feat: treating page monitors fitting/run_state and handles safety events

- Listen for 'fitting' event: detect skin detachment, pause on loss,
  resume on re-attach
- Listen for 'run_state' event: detect device fault (0xFF) and IDLE
  completion (0x00)
- Low battery warnings: toast at <=10%, force stop at <=5%
- Guard finishAsComplete against double-completion
- Proper cleanup of all new event listeners on unload
这个提交包含在:
Guoguo
2026-06-22 06:21:59 -07:00
父节点 f4e74e9460
当前提交 c5a9e89bd9
+64 -4
查看文件
@@ -17,6 +17,8 @@ Page({
completed: false, completed: false,
startedAt: 0, startedAt: 0,
regionText: '全区域', regionText: '全区域',
fittingLost: false,
deviceFault: false
}, },
_getRegionText: function (regionMask) { _getRegionText: function (regionMask) {
@@ -44,15 +46,18 @@ Page({
regionText: this._getRegionText(regions) regionText: this._getRegionText(regions)
}) })
this._sessionId = 'SESS_' + Date.now()
this._onStatus = this.onStatus.bind(this) this._onStatus = this.onStatus.bind(this)
this._onBattery = this.onBattery.bind(this)
this._onComplete = this.onComplete.bind(this) this._onComplete = this.onComplete.bind(this)
this._onException = this.onException.bind(this) this._onException = this.onException.bind(this)
this._onDisconnect = this.onDisconnect.bind(this) this._onDisconnect = this.onDisconnect.bind(this)
this._onReconnect = this.onReconnect.bind(this) this._onReconnect = this.onReconnect.bind(this)
this._onReconnectFailed = this.onReconnectFailed.bind(this) this._onReconnectFailed = this.onReconnectFailed.bind(this)
this._sessionId = 'SESS_' + Date.now() this._onFitting = this.onFitting.bind(this)
this._onRunState = this.onRunState.bind(this)
this._onBattery = this.onBattery.bind(this)
ble.on('status', this._onStatus) ble.on('status', this._onStatus)
ble.on('battery', this._onBattery) ble.on('battery', this._onBattery)
ble.on('treatment_complete', this._onComplete) ble.on('treatment_complete', this._onComplete)
@@ -60,6 +65,9 @@ Page({
ble.on('disconnected', this._onDisconnect) ble.on('disconnected', this._onDisconnect)
ble.on('reconnected', this._onReconnect) ble.on('reconnected', this._onReconnect)
ble.on('reconnect_failed', this._onReconnectFailed) ble.on('reconnect_failed', this._onReconnectFailed)
ble.on('fitting', this._onFitting)
ble.on('run_state', this._onRunState)
this.startLocalTimer() this.startLocalTimer()
this.syncCommands() this.syncCommands()
this.syncTreatmentStart() this.syncTreatmentStart()
@@ -73,7 +81,42 @@ Page({
if (this._onDisconnect) ble.off('disconnected', this._onDisconnect) if (this._onDisconnect) ble.off('disconnected', this._onDisconnect)
if (this._onReconnect) ble.off('reconnected', this._onReconnect) if (this._onReconnect) ble.off('reconnected', this._onReconnect)
if (this._onReconnectFailed) ble.off('reconnect_failed', this._onReconnectFailed) if (this._onReconnectFailed) ble.off('reconnect_failed', this._onReconnectFailed)
if (this._onFitting) ble.off('fitting', this._onFitting)
if (this._onRunState) ble.off('run_state', this._onRunState)
if (this._localTimer) clearInterval(this._localTimer) if (this._localTimer) clearInterval(this._localTimer)
if (this._fittingTimer) clearTimeout(this._fittingTimer)
},
onFitting: function (data) {
if (this.data.completed) return
if (data.fitting === 0 && !this.data.fittingLost) {
this.setData({ fittingLost: true, paused: true })
wx.showToast({ title: '检测到离肤,请重新佩戴设备', icon: 'none', duration: 3000 })
} else if (data.fitting === 1 && this.data.fittingLost) {
this.setData({ fittingLost: false, paused: false })
wx.showToast({ title: '已重新贴合,继续使用', icon: 'success', duration: 2000 })
}
},
onRunState: function (data) {
if (this.data.completed) return
console.log('[TREAT] run_state:', data.run_state)
if (data.run_state === 0xFF) {
this.setData({ deviceFault: true })
var self = this
wx.showModal({
title: '设备故障',
content: '设备上报故障,已自动停止。',
showCancel: false,
complete: function () { self.finishAsComplete() }
})
return
}
if (data.run_state === 0x00 && !this.data.paused && !this.data.fittingLost) {
this.finishAsComplete()
}
}, },
startLocalTimer: function () { startLocalTimer: function () {
@@ -83,6 +126,23 @@ Page({
var elapsed = Date.now() - self.data.startedAt var elapsed = Date.now() - self.data.startedAt
var remaining = Math.max(0, self.data.duration - elapsed) var remaining = Math.max(0, self.data.duration - elapsed)
self.updateProgress(remaining) self.updateProgress(remaining)
if (self.data.battery > 0 && self.data.battery <= 5) {
clearInterval(timer)
wx.showModal({
title: '电量过低',
content: '设备电量不足5%,已自动停止。请及时充电。',
showCancel: false,
complete: function () { self.finishAsComplete() }
})
return
}
if (self.data.battery > 0 && self.data.battery <= 10 && !self._lowBatteryWarned) {
self._lowBatteryWarned = true
wx.showToast({ title: '电量低,请及时充电', icon: 'none', duration: 3000 })
}
if (remaining <= 0) { if (remaining <= 0) {
clearInterval(timer) clearInterval(timer)
self.finishAsComplete() self.finishAsComplete()
@@ -179,6 +239,7 @@ Page({
}, },
finishAsComplete: function () { finishAsComplete: function () {
if (this.data.completed) return
var elapsed = Math.min(this.data.duration, Date.now() - this.data.startedAt) var elapsed = Math.min(this.data.duration, Date.now() - this.data.startedAt)
this.syncTreatmentEnd(elapsed) this.syncTreatmentEnd(elapsed)
this.onComplete({ this.onComplete({
@@ -254,6 +315,5 @@ Page({
} }
} }
}) })
}, }
}) })