Improve heart rate device reconnect flow
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { getTileSizePx, screenToWorld, worldToScreen, type CameraState } from '../camera/camera'
|
||||
import { CompassHeadingController } from '../sensor/compassHeadingController'
|
||||
import { HeartRateController } from '../sensor/heartRateController'
|
||||
import { HeartRateController, type HeartRateDiscoveredDevice } from '../sensor/heartRateController'
|
||||
import { LocationController } from '../sensor/locationController'
|
||||
import { WebGLMapRenderer } from '../renderer/webglMapRenderer'
|
||||
import { type MapRendererStats } from '../renderer/mapRenderer'
|
||||
@@ -132,6 +132,14 @@ export interface MapEngineViewState {
|
||||
heartRateConnected: boolean
|
||||
heartRateStatusText: string
|
||||
heartRateDeviceText: string
|
||||
heartRateScanText: string
|
||||
heartRateDiscoveredDevices: Array<{
|
||||
deviceId: string
|
||||
name: string
|
||||
rssiText: string
|
||||
preferred: boolean
|
||||
connected: boolean
|
||||
}>
|
||||
gameSessionStatus: 'idle' | 'running' | 'finished' | 'failed'
|
||||
gameModeText: string
|
||||
panelTimerText: string
|
||||
@@ -227,6 +235,8 @@ const VIEW_SYNC_KEYS: Array<keyof MapEngineViewState> = [
|
||||
'heartRateConnected',
|
||||
'heartRateStatusText',
|
||||
'heartRateDeviceText',
|
||||
'heartRateScanText',
|
||||
'heartRateDiscoveredDevices',
|
||||
'gameSessionStatus',
|
||||
'gameModeText',
|
||||
'panelTimerText',
|
||||
@@ -612,37 +622,64 @@ export class MapEngine {
|
||||
this.setState(this.getLocationControllerViewPatch(), true)
|
||||
},
|
||||
})
|
||||
this.heartRateController = new HeartRateController({
|
||||
onHeartRate: (bpm) => {
|
||||
this.telemetryRuntime.dispatch({
|
||||
type: 'heart_rate_updated',
|
||||
at: Date.now(),
|
||||
this.heartRateController = new HeartRateController({
|
||||
onHeartRate: (bpm) => {
|
||||
this.telemetryRuntime.dispatch({
|
||||
type: 'heart_rate_updated',
|
||||
at: Date.now(),
|
||||
bpm,
|
||||
})
|
||||
this.syncSessionTimerText()
|
||||
},
|
||||
onStatus: (message) => {
|
||||
this.setState({
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: this.heartRateController.currentDeviceName || '--',
|
||||
}, true)
|
||||
},
|
||||
onError: (message) => {
|
||||
this.setState({
|
||||
heartRateConnected: false,
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: '--',
|
||||
statusText: `${message} (${this.buildVersion})`,
|
||||
}, true)
|
||||
},
|
||||
onConnectionChange: (connected, deviceName) => {
|
||||
this.setState({
|
||||
heartRateConnected: connected,
|
||||
heartRateDeviceText: deviceName || '--',
|
||||
heartRateStatusText: connected ? '心率带已连接' : '心率带未连接',
|
||||
}, true)
|
||||
},
|
||||
})
|
||||
},
|
||||
onStatus: (message) => {
|
||||
const deviceName = this.heartRateController.currentDeviceName
|
||||
|| (this.heartRateController.reconnecting ? this.heartRateController.lastDeviceName : null)
|
||||
|| '--'
|
||||
this.setState({
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: deviceName,
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
}, true)
|
||||
},
|
||||
onError: (message) => {
|
||||
this.clearHeartRateSignal()
|
||||
const deviceName = this.heartRateController.reconnecting
|
||||
? (this.heartRateController.lastDeviceName || '--')
|
||||
: '--'
|
||||
this.setState({
|
||||
heartRateConnected: false,
|
||||
heartRateStatusText: message,
|
||||
heartRateDeviceText: deviceName,
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
statusText: `${message} (${this.buildVersion})`,
|
||||
}, true)
|
||||
},
|
||||
onConnectionChange: (connected, deviceName) => {
|
||||
if (!connected) {
|
||||
this.clearHeartRateSignal()
|
||||
}
|
||||
const resolvedDeviceName = connected
|
||||
? (deviceName || '--')
|
||||
: (this.heartRateController.reconnecting
|
||||
? (this.heartRateController.lastDeviceName || '--')
|
||||
: '--')
|
||||
this.setState({
|
||||
heartRateConnected: connected,
|
||||
heartRateDeviceText: resolvedDeviceName,
|
||||
heartRateStatusText: connected
|
||||
? '心率带已连接'
|
||||
: (this.heartRateController.reconnecting ? '心率带自动重连中' : '心率带未连接'),
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
heartRateDiscoveredDevices: this.formatHeartRateDevices(this.heartRateController.discoveredDevices),
|
||||
}, true)
|
||||
},
|
||||
onDeviceListChange: (devices) => {
|
||||
this.setState({
|
||||
heartRateDiscoveredDevices: this.formatHeartRateDevices(devices),
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
}, true)
|
||||
},
|
||||
})
|
||||
this.feedbackDirector = new FeedbackDirector({
|
||||
showPunchFeedback: (text, tone, motionClass) => {
|
||||
this.showPunchFeedback(text, tone, motionClass)
|
||||
@@ -745,9 +782,11 @@ export class MapEngine {
|
||||
mockCoordText: '--',
|
||||
mockSpeedText: '--',
|
||||
gpsCoordText: '--',
|
||||
heartRateConnected: false,
|
||||
heartRateStatusText: '心率带未连接',
|
||||
heartRateDeviceText: '--',
|
||||
heartRateConnected: false,
|
||||
heartRateStatusText: '心率带未连接',
|
||||
heartRateDeviceText: '--',
|
||||
heartRateScanText: '未扫描',
|
||||
heartRateDiscoveredDevices: [],
|
||||
panelTimerText: '00:00:00',
|
||||
panelMileageText: '0m',
|
||||
panelActionTagText: '目标',
|
||||
@@ -848,6 +887,14 @@ export class MapEngine {
|
||||
this.mounted = false
|
||||
}
|
||||
|
||||
handleAppShow(): void {
|
||||
this.feedbackDirector.setAppAudioMode('foreground')
|
||||
}
|
||||
|
||||
handleAppHide(): void {
|
||||
this.feedbackDirector.setAppAudioMode('foreground')
|
||||
}
|
||||
|
||||
|
||||
clearGameRuntime(): void {
|
||||
this.gameRuntime.clear()
|
||||
@@ -858,6 +905,15 @@ export class MapEngine {
|
||||
this.setCourseHeading(null)
|
||||
}
|
||||
|
||||
clearHeartRateSignal(): void {
|
||||
this.telemetryRuntime.dispatch({
|
||||
type: 'heart_rate_updated',
|
||||
at: Date.now(),
|
||||
bpm: null,
|
||||
})
|
||||
this.syncSessionTimerText()
|
||||
}
|
||||
|
||||
clearFinishedTestOverlay(): void {
|
||||
this.currentGpsPoint = null
|
||||
this.currentGpsTrack = []
|
||||
@@ -1386,6 +1442,18 @@ export class MapEngine {
|
||||
this.heartRateController.disconnect()
|
||||
}
|
||||
|
||||
handleConnectHeartRateDevice(deviceId: string): void {
|
||||
this.heartRateController.connectToDiscoveredDevice(deviceId)
|
||||
}
|
||||
|
||||
handleClearPreferredHeartRateDevice(): void {
|
||||
this.heartRateController.clearPreferredDevice()
|
||||
this.setState({
|
||||
heartRateDeviceText: this.heartRateController.currentDeviceName || '--',
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
}, true)
|
||||
}
|
||||
|
||||
handleDebugHeartRateTone(tone: HeartRateTone): void {
|
||||
const sampleBpm = getHeartRateToneSampleBpm(tone, this.telemetryRuntime.config)
|
||||
this.telemetryRuntime.dispatch({
|
||||
@@ -1407,9 +1475,42 @@ export class MapEngine {
|
||||
})
|
||||
this.setState({
|
||||
heartRateStatusText: this.heartRateController.connected ? '心率带已连接' : '心率带未连接',
|
||||
heartRateScanText: this.getHeartRateScanText(),
|
||||
}, true)
|
||||
this.syncSessionTimerText()
|
||||
}
|
||||
|
||||
formatHeartRateDevices(devices: HeartRateDiscoveredDevice[]): Array<{ deviceId: string; name: string; rssiText: string; preferred: boolean; connected: boolean }> {
|
||||
return devices.map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
name: device.name,
|
||||
rssiText: device.rssi === null ? '--' : `${device.rssi} dBm`,
|
||||
preferred: device.isPreferred,
|
||||
connected: !!this.heartRateController.currentDeviceId && this.heartRateController.currentDeviceId === device.deviceId && this.heartRateController.connected,
|
||||
}))
|
||||
}
|
||||
|
||||
getHeartRateScanText(): string {
|
||||
if (this.heartRateController.connected) {
|
||||
return '已连接'
|
||||
}
|
||||
|
||||
if (this.heartRateController.connecting) {
|
||||
return '连接中'
|
||||
}
|
||||
|
||||
if (this.heartRateController.disconnecting) {
|
||||
return '断开中'
|
||||
}
|
||||
|
||||
if (this.heartRateController.scanning) {
|
||||
return this.heartRateController.lastDeviceId ? '扫描中(优先首选)' : '扫描中(等待选择)'
|
||||
}
|
||||
|
||||
return this.heartRateController.discoveredDevices.length
|
||||
? `已发现 ${this.heartRateController.discoveredDevices.length} 个设备`
|
||||
: '未扫描'
|
||||
}
|
||||
setStage(rect: MapEngineStageRect): void {
|
||||
this.previewScale = 1
|
||||
this.previewOriginX = rect.width / 2
|
||||
|
||||
Reference in New Issue
Block a user