Files
cmr-mini/miniprogram/engine/sensor/mockLocationBridge.ts

148 lines
3.8 KiB
TypeScript

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()
}
}