From 05a72ac7dde6031dc0cefc75641d84cdfdac87ca Mon Sep 17 00:00:00 2001 From: zhangzhen Date: Tue, 2 Jun 2026 09:15:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=BA=E5=91=98=E7=AE=A1=E7=90=86=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- pages/index.vue | 9 ++- pages/modbus/modbus.vue | 4 +- pages/set/personmanage.vue | 132 ++++++++++++++++++++++++++++++++++--- pages/start.vue | 99 ++++++++++------------------ utils/cmd.js | 53 ++++++++++++--- 6 files changed, 212 insertions(+), 87 deletions(-) diff --git a/manifest.json b/manifest.json index 4da148d..92f88a2 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "appid" : "__UNI__5A0A7D6", "description" : "", "versionName" : "1.2.5", - "versionCode" : 114, + "versionCode" : 116, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/pages/index.vue b/pages/index.vue index 57f7636..cf0e49d 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -603,17 +603,19 @@ export default { \ No newline at end of file diff --git a/pages/start.vue b/pages/start.vue index 071cb51..a602d11 100644 --- a/pages/start.vue +++ b/pages/start.vue @@ -55,7 +55,6 @@ export default { RS232: undefined, timer: null, // 定时器读取485接口数据 taskTimer: null, - leftDoorCycleTimer: null, // 左门循环定时器 last485DataTime: null, // 上次收到485数据的时间戳 heartbeatTimer: null, // 心跳检测定时器 reconnectCount: 0, // 重连次数计数器 @@ -119,9 +118,9 @@ export default { uni.$on('closeDoor', async (data) => { try { - cmd.LeftDoor(false) - await delay(100) - cmd.RightDoor(false) + await cmd.LeftDoor(false) + await delay(200) + await cmd.RightDoor(false) this.addLog('门', '触摸屏关门') // 触发关门事件 this.closeDoorEvent() @@ -163,11 +162,6 @@ export default { clearInterval(this.taskTimer) this.taskTimer = null } - // 清理左门循环定时器 - if (this.leftDoorCycleTimer) { - clearInterval(this.leftDoorCycleTimer) - this.leftDoorCycleTimer = null - } }, methods: { init() { @@ -519,10 +513,13 @@ export default { this.timer = null } - // 4. 重置时间戳 + // 4. 清空RS485队列中积压的失效指令 + cmd.clearRS485Queue() + + // 5. 重置时间戳 this.last485DataTime = null - // 5. 重新启动RS485通信 + // 6. 重新启动RS485通信 this.startRS485() } catch (error) { @@ -531,7 +528,23 @@ export default { // 失败后延迟重试 await delay(5000) if (this.reconnectCount < this.maxReconnect) { + // 还有重连机会,继续重连 this.resetRS485() + } else { + // 重连次数耗尽,自动重启应用(彻底恢复native层状态) + this.addLog('485通信', `重连${this.maxReconnect}次均失败,即将自动重启...`) + + uni.showToast({ + title: '通信异常,3秒后重启', + icon: 'none', + duration: 3000 + }) + + setTimeout(() => { + // #ifdef APP-PLUS + plus.runtime.restart() + // #endif + }, 3000) } } }, @@ -592,7 +605,7 @@ export default { if (data.action == 'door' && data.status == 'closed') { try { await cmd.LeftDoor(false) - await delay(100) + await delay(200) await cmd.RightDoor(false) // 触发关门事件 await this.closeDoorEvent() @@ -678,50 +691,7 @@ export default { } } }, - // handle232HexData(hex) { - // // 处理232接口上报的数据 - // let data = cmd.parse232dData(hex) - // if (data == {}) return - // if (data.action == 'leave' && data.type == 'internal') { - // // 更新内镜数据 - // let key = data.address - 1 - // uni.$emit('notice', { title: data.address, content: '内镜取出'}) - // this.updateScope(key, 'leave', '') - // // 添加日志数据 - // this.addLog('位置'+key, '内镜取出') - // } - // if (data.action == 'enter' && data.type == 'internal') { - // // 验证内镜信息 - // this.checkEndoCard(data.number).then(res => { - // if (res) { - // uni.$emit('notice', { title: data.number, content: '内镜存入'}) - // // 更新内镜数据 - // let key = data.address - 1 - // this.updateScope(key, 'enter', data.number) - // // 添加日志数据 - // this.addLog(data.number, '内镜存入') - // } - // }) - // uni.$emit('ic', data.number) - // } - // if (data.action == 'enter' && data.type == 'person') { - // // 验证人员信息 - // this.checkPersonCard(data.number).then(res => { - // if (res) { - // // 开门事件 - // cmd.Door(true) - // // this.openDoorEvent() - // uni.$emit('notice', { title: data.number, content: '刷卡开门'}) - // // 添加日志数据 - // this.addLog(data.number, '刷卡开门') - // } - // }) - // uni.$emit('ic', data.number) - // } - // if (data.action == 'door') { - // cmd.Door(false) - // } - // }, + // 密码框提交 inputModalSubmit(val) { let password = this.$store.state.base.screenPw @@ -803,20 +773,21 @@ export default { // 开门事件 async openDoorEvent() { // 关闭真空泵和消毒,打开照明,打开门锁,打开风机 - cmd.Vacuum(false) - await delay(300) cmd.Light(true) - await delay(300) + await delay(500) + cmd.Vacuum(false) + await delay(500) cmd.Wind(true) }, // 关门事件 async closeDoorEvent() { // 关闭照明,打开真空泵、消毒 - cmd.Light(false) // 照明关闭指令 - await delay(300) - cmd.Vacuum(true) //真空泵开启指令 - await delay(300) - cmd.Wind(true) + await delay(500) + await cmd.Light(false) // 照明关闭指令 + await delay(500) + await cmd.Vacuum(true) //真空泵开启指令 + await delay(500) + await cmd.Wind(true) }, // 验证人员卡是否可以开门 async checkPersonCard(ic) { diff --git a/utils/cmd.js b/utils/cmd.js index f109941..b96ab2e 100644 --- a/utils/cmd.js +++ b/utils/cmd.js @@ -38,7 +38,7 @@ const enqueueTask = (task) => { // 规则1: 去重 - 已存在相同cmd的任务则丢弃 const isDuplicate = rs485Queue.some(t => t.cmd === task.cmd) if (isDuplicate) { - console.warn('[RS485队列] 丢弃重复任务:', task.cmd) + // console.warn('[RS485队列] 丢弃重复任务:', task.cmd) return false } @@ -54,16 +54,16 @@ const enqueueTask = (task) => { } } if (lastLowIndex !== -1) { - console.warn('[RS485队列] 高优挤掉低优:', rs485Queue[lastLowIndex].cmd) + // console.warn('[RS485队列] 高优挤掉低优:', rs485Queue[lastLowIndex].cmd) rs485Queue.splice(lastLowIndex, 1) } else { // 没有低优先级可挤,踢掉队首最老的 - console.warn('[RS485队列] 满,踢掉队首:', rs485Queue[0].cmd) + // console.warn('[RS485队列] 满,踢掉队首:', rs485Queue[0].cmd) rs485Queue.shift() } } else { // 低优先级:直接踢掉队首最老的 - console.warn('[RS485队列] 满,踢掉队首:', rs485Queue[0].cmd) + // console.warn('[RS485队列] 满,踢掉队首:', rs485Queue[0].cmd) rs485Queue.shift() } } @@ -80,10 +80,41 @@ const enqueueTask = (task) => { rs485Queue.push(task) } - console.log(`[RS485队列] 入队成功, 长度:${rs485Queue.length}, cmd:${task.cmd}`) + // console.log(`[RS485队列] 入队成功, 长度:${rs485Queue.length}, cmd:${task.cmd}`) return true } +// ========== RS485 发送超时保护 ========== +const RS485_SEND_TIMEOUT = 2000 // 发送超时2秒 + +/** + * 带超时的RS485发送封装 + * 防止底层串口驱动卡死导致Promise永久pending,造成队列死锁 + */ +const sendWithTimeout = (rs485Instance, cmd) => { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + console.warn(`[RS485] 发送超时(${RS485_SEND_TIMEOUT}ms), cmd: ${cmd}`) + reject(new Error('RS485发送超时')) + }, RS485_SEND_TIMEOUT) + + const result = rs485Instance.sendDataString(cmd) + if (result && typeof result.then === 'function') { + result.then(res => { + clearTimeout(timer) + resolve(res) + }).catch(err => { + clearTimeout(timer) + reject(err) + }) + } else { + clearTimeout(timer) + resolve(result) + } + }) +} +// ========== RS485 发送超时保护 END ========== + const processRS485Queue = async () => { if (isRS485Sending || rs485Queue.length === 0) { return @@ -95,10 +126,8 @@ const processRS485Queue = async () => { if (!RS485) { throw new Error('RS485 not initialized') } - const result = RS485.sendDataString(task.cmd) - if (result && typeof result.then === 'function') { - await result - } + // 使用带超时的发送封装,防止Promise永久pending导致队列死锁 + await sendWithTimeout(RS485, task.cmd) // 发送后延迟,确保总线空闲再发下一条 await new Promise(r => setTimeout(r, RS485_SEND_DELAY)) task.resolve() @@ -132,6 +161,7 @@ const clearRS485Queue = () => { const task = rs485Queue.shift() task.reject(new Error('RS485队列已清空(重连中)')) } + isRS485Sending = false // 强制释放发送锁,防止队列死锁 } /** @@ -223,6 +253,11 @@ export default { clearRS485Queue, getRS485QueueSize, + // 重置RS485队列锁(供外部重连时调用) + resetRS485Lock() { + clearRS485Queue() // 内部已包含清队列 + 释放锁 + }, + // 解析门卡数据,返回卡号 parse232dData: (hexString) => { // IC卡 20 01 00 08 04 00 00 00 0e 26 fe ab 8f 03