export const DEFAULT_MOCK_HEART_RATE_BRIDGE_URL = 'wss://gs.gotomars.xyz/mock-gps' export interface MockHeartRateBridgeCallbacks { onOpen: () => void onClose: (message: string) => void onError: (message: string) => void onBpm: (bpm: number) => void } type RawMockHeartRateMessage = { type?: string timestamp?: number bpm?: number } function safeParseMessage(data: string): RawMockHeartRateMessage | null { try { return JSON.parse(data) as RawMockHeartRateMessage } catch (_error) { return null } } function toHeartRateValue(message: RawMockHeartRateMessage): number | null { if (message.type !== 'mock_heart_rate' || !Number.isFinite(message.bpm)) { return null } const bpm = Math.round(Number(message.bpm)) if (bpm <= 0) { return null } return bpm } export class MockHeartRateBridge { callbacks: MockHeartRateBridgeCallbacks socketTask: WechatMiniprogram.SocketTask | null connected: boolean connecting: boolean url: string constructor(callbacks: MockHeartRateBridgeCallbacks) { this.callbacks = callbacks this.socketTask = null this.connected = false this.connecting = false this.url = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL } connect(url = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL): void { if (this.connected || this.connecting) { return } this.url = url this.connecting = true try { const socketTask = wx.connectSocket({ url, }) this.socketTask = socketTask socketTask.onOpen(() => { this.connected = true this.connecting = false this.callbacks.onOpen() }) socketTask.onClose((result) => { const reason = result && result.reason ? result.reason : '模拟心率源连接已关闭' this.connected = false this.connecting = false this.socketTask = null this.callbacks.onClose(reason) }) socketTask.onError((result) => { const message = result && result.errMsg ? result.errMsg : '模拟心率源连接失败' this.connected = false this.connecting = false this.socketTask = null this.callbacks.onError(message) }) socketTask.onMessage((result) => { if (!result || typeof result.data !== 'string') { return } const parsed = safeParseMessage(result.data) if (!parsed) { this.callbacks.onError('模拟心率消息不是合法 JSON') return } const bpm = toHeartRateValue(parsed) if (bpm === null) { return } this.callbacks.onBpm(bpm) }) } catch (error) { this.connected = false this.connecting = false this.socketTask = null const message = error && (error as Error).message ? (error as Error).message : '模拟心率源连接创建失败' this.callbacks.onError(message) } } disconnect(): void { if (!this.socketTask) { if (this.connected || this.connecting) { this.connected = false this.connecting = false } return } const socketTask = this.socketTask this.socketTask = null this.connected = false this.connecting = false socketTask.close({}) } destroy(): void { this.disconnect() } }