import { type LocationSample } from './locationSource' export const DEFAULT_MOCK_LOCATION_BRIDGE_URL = 'wss://gs.gotomars.xyz/mock-gps' export interface MockLocationBridgeCallbacks { onOpen: () => void onClose: (message: string) => void onError: (message: string) => void onSample: (sample: LocationSample) => void } type RawMockGpsMessage = { type?: string timestamp?: number lat?: number lon?: number accuracyMeters?: number speedMps?: number headingDeg?: number } function safeParseMessage(data: string): RawMockGpsMessage | null { try { return JSON.parse(data) as RawMockGpsMessage } catch (_error) { return null } } function toLocationSample(message: RawMockGpsMessage): LocationSample | null { if (message.type !== 'mock_gps') { return null } if (!Number.isFinite(message.lat) || !Number.isFinite(message.lon)) { return null } return { latitude: Number(message.lat), longitude: Number(message.lon), accuracy: Number.isFinite(message.accuracyMeters) ? Number(message.accuracyMeters) : undefined, speed: Number.isFinite(message.speedMps) ? Number(message.speedMps) : null, headingDeg: Number.isFinite(message.headingDeg) ? Number(message.headingDeg) : null, timestamp: Number.isFinite(message.timestamp) ? Number(message.timestamp) : Date.now(), sourceMode: 'mock', } } export class MockLocationBridge { callbacks: MockLocationBridgeCallbacks socketTask: WechatMiniprogram.SocketTask | null connected: boolean connecting: boolean url: string constructor(callbacks: MockLocationBridgeCallbacks) { this.callbacks = callbacks this.socketTask = null this.connected = false this.connecting = false this.url = DEFAULT_MOCK_LOCATION_BRIDGE_URL } connect(url = DEFAULT_MOCK_LOCATION_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 sample = toLocationSample(parsed) if (!sample) { return } this.callbacks.onSample(sample) }) } 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() } }