mockHeartRateBridge.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. export const DEFAULT_MOCK_HEART_RATE_BRIDGE_URL = 'wss://gs.gotomars.xyz/mock-gps'
  2. export interface MockHeartRateBridgeCallbacks {
  3. onOpen: () => void
  4. onClose: (message: string) => void
  5. onError: (message: string) => void
  6. onBpm: (bpm: number) => void
  7. }
  8. type RawMockHeartRateMessage = {
  9. type?: string
  10. timestamp?: number
  11. bpm?: number
  12. }
  13. function safeParseMessage(data: string): RawMockHeartRateMessage | null {
  14. try {
  15. return JSON.parse(data) as RawMockHeartRateMessage
  16. } catch (_error) {
  17. return null
  18. }
  19. }
  20. function toHeartRateValue(message: RawMockHeartRateMessage): number | null {
  21. if (message.type !== 'mock_heart_rate' || !Number.isFinite(message.bpm)) {
  22. return null
  23. }
  24. const bpm = Math.round(Number(message.bpm))
  25. if (bpm <= 0) {
  26. return null
  27. }
  28. return bpm
  29. }
  30. export class MockHeartRateBridge {
  31. callbacks: MockHeartRateBridgeCallbacks
  32. socketTask: WechatMiniprogram.SocketTask | null
  33. connected: boolean
  34. connecting: boolean
  35. url: string
  36. constructor(callbacks: MockHeartRateBridgeCallbacks) {
  37. this.callbacks = callbacks
  38. this.socketTask = null
  39. this.connected = false
  40. this.connecting = false
  41. this.url = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL
  42. }
  43. connect(url = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL): void {
  44. if (this.connected || this.connecting) {
  45. return
  46. }
  47. this.url = url
  48. this.connecting = true
  49. try {
  50. const socketTask = wx.connectSocket({
  51. url,
  52. })
  53. this.socketTask = socketTask
  54. socketTask.onOpen(() => {
  55. this.connected = true
  56. this.connecting = false
  57. this.callbacks.onOpen()
  58. })
  59. socketTask.onClose((result) => {
  60. const reason = result && result.reason ? result.reason : '模拟心率源连接已关闭'
  61. this.connected = false
  62. this.connecting = false
  63. this.socketTask = null
  64. this.callbacks.onClose(reason)
  65. })
  66. socketTask.onError((result) => {
  67. const message = result && result.errMsg ? result.errMsg : '模拟心率源连接失败'
  68. this.connected = false
  69. this.connecting = false
  70. this.socketTask = null
  71. this.callbacks.onError(message)
  72. })
  73. socketTask.onMessage((result) => {
  74. if (!result || typeof result.data !== 'string') {
  75. return
  76. }
  77. const parsed = safeParseMessage(result.data)
  78. if (!parsed) {
  79. this.callbacks.onError('模拟心率消息不是合法 JSON')
  80. return
  81. }
  82. const bpm = toHeartRateValue(parsed)
  83. if (bpm === null) {
  84. return
  85. }
  86. this.callbacks.onBpm(bpm)
  87. })
  88. } catch (error) {
  89. this.connected = false
  90. this.connecting = false
  91. this.socketTask = null
  92. const message = error && (error as Error).message ? (error as Error).message : '模拟心率源连接创建失败'
  93. this.callbacks.onError(message)
  94. }
  95. }
  96. disconnect(): void {
  97. if (!this.socketTask) {
  98. if (this.connected || this.connecting) {
  99. this.connected = false
  100. this.connecting = false
  101. }
  102. return
  103. }
  104. const socketTask = this.socketTask
  105. this.socketTask = null
  106. this.connected = false
  107. this.connecting = false
  108. socketTask.close({})
  109. }
  110. destroy(): void {
  111. this.disconnect()
  112. }
  113. }