Add mock heart rate simulator flow
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { getTileSizePx, screenToWorld, worldToScreen, type CameraState } from '../camera/camera'
|
||||
import { CompassHeadingController } from '../sensor/compassHeadingController'
|
||||
import { HeartRateController, type HeartRateDiscoveredDevice } from '../sensor/heartRateController'
|
||||
import { type HeartRateDiscoveredDevice } from '../sensor/heartRateController'
|
||||
import { HeartRateInputController } from '../sensor/heartRateInputController'
|
||||
import { LocationController } from '../sensor/locationController'
|
||||
import { WebGLMapRenderer } from '../renderer/webglMapRenderer'
|
||||
import { type MapRendererStats } from '../renderer/mapRenderer'
|
||||
@@ -129,6 +130,8 @@ export interface MapEngineViewState {
|
||||
mockCoordText: string
|
||||
mockSpeedText: string
|
||||
gpsCoordText: string
|
||||
heartRateSourceMode: 'real' | 'mock'
|
||||
heartRateSourceText: string
|
||||
heartRateConnected: boolean
|
||||
heartRateStatusText: string
|
||||
heartRateDeviceText: string
|
||||
@@ -140,6 +143,10 @@ export interface MapEngineViewState {
|
||||
preferred: boolean
|
||||
connected: boolean
|
||||
}>
|
||||
mockHeartRateBridgeConnected: boolean
|
||||
mockHeartRateBridgeStatusText: string
|
||||
mockHeartRateBridgeUrlText: string
|
||||
mockHeartRateText: string
|
||||
gameSessionStatus: 'idle' | 'running' | 'finished' | 'failed'
|
||||
gameModeText: string
|
||||
panelTimerText: string
|
||||
@@ -232,11 +239,17 @@ const VIEW_SYNC_KEYS: Array<keyof MapEngineViewState> = [
|
||||
'mockCoordText',
|
||||
'mockSpeedText',
|
||||
'gpsCoordText',
|
||||
'heartRateSourceMode',
|
||||
'heartRateSourceText',
|
||||
'heartRateConnected',
|
||||
'heartRateStatusText',
|
||||
'heartRateDeviceText',
|
||||
'heartRateScanText',
|
||||
'heartRateDiscoveredDevices',
|
||||
'mockHeartRateBridgeConnected',
|
||||
'mockHeartRateBridgeStatusText',
|
||||
'mockHeartRateBridgeUrlText',
|
||||
'mockHeartRateText',
|
||||
'gameSessionStatus',
|
||||
'gameModeText',
|
||||
'panelTimerText',
|
||||
@@ -514,7 +527,7 @@ export class MapEngine {
|
||||
renderer: WebGLMapRenderer
|
||||
compassController: CompassHeadingController
|
||||
locationController: LocationController
|
||||
heartRateController: HeartRateController
|
||||
heartRateController: HeartRateInputController
|
||||
feedbackDirector: FeedbackDirector
|
||||
onData: (patch: Partial<MapEngineViewState>) => void
|
||||
state: MapEngineViewState
|
||||
@@ -622,7 +635,7 @@ export class MapEngine {
|
||||
this.setState(this.getLocationControllerViewPatch(), true)
|
||||
},
|
||||
})
|
||||
this.heartRateController = new HeartRateController({
|
||||
this.heartRateController = new HeartRateInputController({
|
||||
onHeartRate: (bpm) => {
|
||||
this.telemetryRuntime.dispatch({
|
||||
type: 'heart_rate_updated',
|
||||
@@ -639,6 +652,7 @@ export class MapEngine {
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: deviceName,
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
...this.getHeartRateControllerViewPatch(),
|
||||
}, true)
|
||||
},
|
||||
onError: (message) => {
|
||||
@@ -651,6 +665,7 @@ export class MapEngine {
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: deviceName,
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
...this.getHeartRateControllerViewPatch(),
|
||||
statusText: `${message} (${this.buildVersion})`,
|
||||
}, true)
|
||||
},
|
||||
@@ -667,18 +682,23 @@ export class MapEngine {
|
||||
heartRateConnected: connected,
|
||||
heartRateDeviceText: resolvedDeviceName,
|
||||
heartRateStatusText: connected
|
||||
? '心率带已连接'
|
||||
: (this.heartRateController.reconnecting ? '心率带自动重连中' : '心率带未连接'),
|
||||
? (this.heartRateController.sourceMode === 'mock' ? '模拟心率源已连接' : '心率带已连接')
|
||||
: (this.heartRateController.reconnecting ? '心率带自动重连中' : (this.heartRateController.sourceMode === 'mock' ? '模拟心率源未连接' : '心率带未连接')),
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
heartRateDiscoveredDevices: this.formatHeartRateDevices(this.heartRateController.discoveredDevices),
|
||||
...this.getHeartRateControllerViewPatch(),
|
||||
}, true)
|
||||
},
|
||||
onDeviceListChange: (devices) => {
|
||||
this.setState({
|
||||
heartRateDiscoveredDevices: this.formatHeartRateDevices(devices),
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
...this.getHeartRateControllerViewPatch(),
|
||||
}, true)
|
||||
},
|
||||
onDebugStateChange: () => {
|
||||
this.setState(this.getHeartRateControllerViewPatch(), true)
|
||||
},
|
||||
})
|
||||
this.feedbackDirector = new FeedbackDirector({
|
||||
showPunchFeedback: (text, tone, motionClass) => {
|
||||
@@ -782,11 +802,17 @@ export class MapEngine {
|
||||
mockCoordText: '--',
|
||||
mockSpeedText: '--',
|
||||
gpsCoordText: '--',
|
||||
heartRateSourceMode: 'real',
|
||||
heartRateSourceText: '真实心率',
|
||||
heartRateConnected: false,
|
||||
heartRateStatusText: '心率带未连接',
|
||||
heartRateDeviceText: '--',
|
||||
heartRateScanText: '未扫描',
|
||||
heartRateDiscoveredDevices: [],
|
||||
mockHeartRateBridgeConnected: false,
|
||||
mockHeartRateBridgeStatusText: '未连接',
|
||||
mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
|
||||
mockHeartRateText: '--',
|
||||
panelTimerText: '00:00:00',
|
||||
panelMileageText: '0m',
|
||||
panelActionTagText: '目标',
|
||||
@@ -951,6 +977,18 @@ export class MapEngine {
|
||||
}
|
||||
}
|
||||
|
||||
getHeartRateControllerViewPatch(): Partial<MapEngineViewState> {
|
||||
const debugState = this.heartRateController.getDebugState()
|
||||
return {
|
||||
heartRateSourceMode: debugState.sourceMode,
|
||||
heartRateSourceText: debugState.sourceModeText,
|
||||
mockHeartRateBridgeConnected: debugState.mockBridgeConnected,
|
||||
mockHeartRateBridgeStatusText: debugState.mockBridgeStatusText,
|
||||
mockHeartRateBridgeUrlText: debugState.mockBridgeUrlText,
|
||||
mockHeartRateText: debugState.mockHeartRateText,
|
||||
}
|
||||
}
|
||||
|
||||
getGameModeText(): string {
|
||||
return this.gameMode === 'score-o' ? '积分赛' : '顺序赛'
|
||||
}
|
||||
@@ -1442,6 +1480,26 @@ export class MapEngine {
|
||||
this.heartRateController.disconnect()
|
||||
}
|
||||
|
||||
handleSetRealHeartRateMode(): void {
|
||||
this.heartRateController.setSourceMode('real')
|
||||
}
|
||||
|
||||
handleSetMockHeartRateMode(): void {
|
||||
this.heartRateController.setSourceMode('mock')
|
||||
}
|
||||
|
||||
handleConnectMockHeartRateBridge(): void {
|
||||
this.heartRateController.connectMockBridge()
|
||||
}
|
||||
|
||||
handleDisconnectMockHeartRateBridge(): void {
|
||||
this.heartRateController.disconnectMockBridge()
|
||||
}
|
||||
|
||||
handleSetMockHeartRateBridgeUrl(url: string): void {
|
||||
this.heartRateController.setMockBridgeUrl(url)
|
||||
}
|
||||
|
||||
handleConnectHeartRateDevice(deviceId: string): void {
|
||||
this.heartRateController.connectToDiscoveredDevice(deviceId)
|
||||
}
|
||||
@@ -1474,8 +1532,11 @@ export class MapEngine {
|
||||
bpm: null,
|
||||
})
|
||||
this.setState({
|
||||
heartRateStatusText: this.heartRateController.connected ? '心率带已连接' : '心率带未连接',
|
||||
heartRateStatusText: this.heartRateController.connected
|
||||
? (this.heartRateController.sourceMode === 'mock' ? '模拟心率源已连接' : '心率带已连接')
|
||||
: (this.heartRateController.sourceMode === 'mock' ? '模拟心率源未连接' : '心率带未连接'),
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
...this.getHeartRateControllerViewPatch(),
|
||||
}, true)
|
||||
this.syncSessionTimerText()
|
||||
}
|
||||
@@ -1491,6 +1552,18 @@ export class MapEngine {
|
||||
}
|
||||
|
||||
getHeartRateScanText(): string {
|
||||
if (this.heartRateController.sourceMode === 'mock') {
|
||||
if (this.heartRateController.connected) {
|
||||
return '模拟源已连接'
|
||||
}
|
||||
|
||||
if (this.heartRateController.connecting) {
|
||||
return '模拟源连接中'
|
||||
}
|
||||
|
||||
return '模拟模式'
|
||||
}
|
||||
|
||||
if (this.heartRateController.connected) {
|
||||
return '已连接'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user