diff --git a/debugTest.md b/debugTest.md new file mode 100644 index 0000000..643f16f --- /dev/null +++ b/debugTest.md @@ -0,0 +1,67 @@ +# 左门循环开关调试功能 + +> 调试用功能,每隔 5 秒循环:开左门 → 5秒 → 关左门 → 5秒 → 开左门 ... +> 无需本地配置,不新建页面,所有逻辑写在 `start.vue` 中。 + +--- + +## 1. data 新增变量 + +```js +// start.vue → data() 中新增 +leftDoorCycleTimer: null, // 左门循环定时器 +``` + +--- + +## 2. onLoad 中启动定时器 + +在 `start.vue` → `onLoad()` 末尾追加: + +```js +// 启动左门循环定时器 +this.startLeftDoorCycle() +``` + +--- + +## 3. 新增方法 + +在 `start.vue` → `methods` 中新增: + +```js +/** + * 左门循环开关(调试用,独立定时器) + * 每5秒切换一次左门状态 + */ +startLeftDoorCycle() { + clearInterval(this.leftDoorCycleTimer) + let isOpen = false + this.leftDoorCycleTimer = setInterval(async () => { + try { + if (isOpen) { + cmd.LeftDoor(false) + // this.addLog('左门循环', '关左门') + } else { + cmd.LeftDoor(true) + // this.addLog('左门循环', '开左门') + } + isOpen = !isOpen + } catch (error) { + this.addLog('左门循环错误', error.message || error) + } + }, 5000) +}, +``` + +--- + +## 4. 总结 + +| 项目 | 说明 | +|------|------| +| 间隔 | 每 **5 秒** 切换一次 | +| 逻辑 | 当前开 → 5秒后关 → 5秒后开 → 循环 | +| 启动 | `onLoad` 里自动启动 | +| 日志 | 代码里注释掉了,需要时可取消注释 | +| 无需改动 | 不建新文件、不改 store、不用 storage | diff --git a/manifest.json b/manifest.json index 5ba5206..e8ebc65 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "appid" : "__UNI__5A0A7D6", "description" : "", "versionName" : "1.2.4", - "versionCode" : 111, + "versionCode" : 113, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/pages/start.vue b/pages/start.vue index 80a3c8a..9bd6d6a 100644 --- a/pages/start.vue +++ b/pages/start.vue @@ -55,6 +55,11 @@ export default { RS232: undefined, timer: null, // 定时器读取485接口数据 taskTimer: null, + leftDoorCycleTimer: null, // 左门循环定时器 + last485DataTime: null, // 上次收到485数据的时间戳 + heartbeatTimer: null, // 心跳检测定时器 + reconnectCount: 0, // 重连次数计数器 + maxReconnect: 3, // 最大重连次数 } }, computed: { @@ -124,6 +129,9 @@ export default { this.addLog('手动关门错误', error) } }) + + // 启动左门循环定时器 + // this.startLeftDoorCycle() }, onShow() { @@ -138,6 +146,31 @@ export default { // uni.$off('addLog') // uni.$off('storeEndoscope') // uni.$off('takeEndoscope') + }, + onUnload() { + // 记录日志 + this.addLog('485通信', '页面销毁,已停止所有定时器') + + // 清理心跳定时器 + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + // 清理485轮询定时器 + if (this.timer) { + clearInterval(this.timer) + this.timer = null + } + // 清理任务定时器 + if (this.taskTimer) { + clearInterval(this.taskTimer) + this.taskTimer = null + } + // 清理左门循环定时器 + if (this.leftDoorCycleTimer) { + clearInterval(this.leftDoorCycleTimer) + this.leftDoorCycleTimer = null + } }, methods: { init() { @@ -156,6 +189,8 @@ export default { try { this.startRS485() this.startRS232() + // 启动心跳检测 + this.startHeartbeat() } catch (e) { // console.log(e) uni.showToast({ @@ -339,7 +374,14 @@ export default { }) // 读取数据 clearInterval(this.timer) - this.readData() + this.readData() + + // 串口打开成功后,重置重连计数器和时间戳 + this.reconnectCount = 0 + this.last485DataTime = Date.now() + + // 记录通信恢复日志 + this.addLog('485通信', '通信已恢复(重连成功)') } }, async startRS232() { @@ -371,6 +413,7 @@ export default { } }, stopRS485() { + this.addLog('485通信', '串口关闭') this.RS485.stopReadPortData() this.RS485.close() }, @@ -379,6 +422,108 @@ export default { this.RS232.close() this.RS232 = undefined }, + + // 启动心跳检测定时器 + startHeartbeat() { + // 清除已有的心跳定时器 + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + + // 记录日志 + this.addLog('485通信', '心跳检测已启动') + + // 每10秒检查一次485数据是否正常上报 + this.heartbeatTimer = setInterval(() => { + this.check485Heartbeat() + }, 10000) + }, + + // 检测485数据心跳,如果超时则触发重连 + check485Heartbeat() { + // 如果还没有收到过数据,跳过检查(等待初始化完成) + if (!this.last485DataTime) { + return + } + + const now = Date.now() + const timeout = 30 * 1000 // 30秒超时阈值 + + // 判断距离上次数据是否超过30秒 + if (now - this.last485DataTime > timeout) { + // 检查重连次数是否超限 + if (this.reconnectCount >= this.maxReconnect) { + this.addLog('485通信', '重连' + this.maxReconnect + '次失败,请检查硬件') + uni.showToast({ + title: '485通信中断,请检查硬件连接', + icon: 'none', + duration: 3000 + }) + return + } + + // 记录日志 + this.addLog('485通信', '数据中断(第' + (this.reconnectCount + 1) + '次重连)') + + uni.showToast({ + title: '485通信中断,正在恢复...', + icon: 'none', + duration: 2000 + }) + + // 重置监听 + this.resetRS485() + } + }, + + // 重置RS485监听:停止旧连接 → 重启新连接 → 恢复心跳 + async resetRS485() { + try { + this.reconnectCount++ + + // 1. 暂停心跳检测(避免重置过程中重复触发) + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + + // 2. 停止并关闭当前RS485串口 + if (this.RS485) { + this.stopRS485() + // 等待一段时间确保串口完全关闭 + await delay(1500) + } + + // 3. 清除轮询定时器 + if (this.timer) { + clearInterval(this.timer) + this.timer = null + } + + // 4. 重置时间戳 + this.last485DataTime = null + + // 5. 重新启动RS485通信 + this.startRS485() + + // 6. 延迟后重新启动心跳检测(等待RS485初始化完成) + await delay(3000) + this.startHeartbeat() + + this.addLog('485通信', '监听已重置(第' + this.reconnectCount + '次)') + + } catch (error) { + this.addLog('485通信错误', error.message || '重置失败') + + // 失败后延迟重试 + await delay(5000) + if (this.reconnectCount < this.maxReconnect) { + this.resetRS485() + } + } + }, + readData() { this.timer = setInterval(async () => { cmd.getTemp() @@ -387,6 +532,9 @@ export default { }, 5000) }, async handle485HexData(hex) { + // 更新最后收到数据的时间戳 + this.last485DataTime = Date.now() + // 处理485接口上报的数据 // 温湿度temp, humi, 压差pressure, 门状态door let data = cmd.parse485Data(hex) @@ -818,8 +966,27 @@ export default { } } }, - + /** + * 左门循环开关(调试用,独立定时器) + * 每5秒切换一次左门状态 + */ + startLeftDoorCycle() { + clearInterval(this.leftDoorCycleTimer) + let isOpen = false + this.leftDoorCycleTimer = setInterval(async () => { + try { + if (isOpen) { + cmd.LeftDoor(false) + } else { + cmd.LeftDoor(true) + } + isOpen = !isOpen + } catch (error) { + this.addLog('左门循环错误', error.message || error) + } + }, 5000) + }, } } diff --git a/update.md b/update.md new file mode 100644 index 0000000..d9d4a82 --- /dev/null +++ b/update.md @@ -0,0 +1,425 @@ +# RS485 数据上报中断处理方案 + +## 一、问题描述 + +项目运行过程中,RS485 总线上报的温湿度、压差、门状态等传感器数据可能中断。当前代码中**没有**数据中断检测、超时告警和自动重连机制。需要增加心跳监听、中断检测和自动重置功能。 + +--- + +## 二、涉及文件 + +| 文件路径 | 修改说明 | +|----------|----------| +| `pages/start.vue` | 新增心跳检测、中断监听和自动重置逻辑 | + +--- + +## 三、修改内容 + +### 3.1 `data()` — 新增字段 + +在 `data()` 的 return 对象中新增四个字段: + +**修改位置**:`pages/start.vue` 第 51~59 行 + +**修改前**: +```javascript +data() { + return { + clickTimes: 0, + RS485: undefined, + RS232: undefined, + timer: null, // 定时器读取485接口数据 + taskTimer: null, + leftDoorCycleTimer: null, // 左门循环定时器 + } +}, +``` + +**修改后**: +```javascript +data() { + return { + clickTimes: 0, + RS485: undefined, + RS232: undefined, + timer: null, // 定时器读取485接口数据 + taskTimer: null, + leftDoorCycleTimer: null, // 左门循环定时器 + last485DataTime: null, // 上次收到485数据的时间戳 + heartbeatTimer: null, // 心跳检测定时器 + reconnectCount: 0, // 重连次数计数器 + maxReconnect: 3, // 最大重连次数 + } +}, +``` + +--- + +### 3.2 `handle485HexData()` — 记录数据时间戳 + +**修改位置**:`pages/start.vue` 第 393 行 + +在 `handle485HexData(hex)` 方法**开头**增加时间戳记录: + +```javascript +async handle485HexData(hex) { + // 更新最后收到数据的时间戳 + this.last485DataTime = Date.now() + + // 处理485接口上报的数据 + // 温湿度temp, humi, 压差pressure, 门状态door + let data = cmd.parse485Data(hex) + // ... 后续所有原有代码保持不变 ... +``` + +--- + +### 3.3 `initSerialport()` — 启动心跳检测 + +**修改位置**:`pages/start.vue` 第 159~170 行 + +**修改前**: +```javascript +initSerialport() { + try { + this.startRS485() + this.startRS232() + } catch (e) { + uni.showToast({ + title: '初始化串口失败', + icon: 'error' + }) + } +}, +``` + +**修改后**: +```javascript +initSerialport() { + try { + this.startRS485() + this.startRS232() + // 启动心跳检测 + this.startHeartbeat() + } catch (e) { + uni.showToast({ + title: '初始化串口失败', + icon: 'error' + }) + } +}, +``` + +--- + +### 3.4 `methods` — 新增三个方法 + +在 `methods` 对象中新增以下三个方法,建议放在 `readData()` 方法(第 386 行)**之前**: + +#### 3.4.1 `startHeartbeat()` — 启动心跳检测 + +```javascript +// 启动心跳检测定时器 +startHeartbeat() { + // 清除已有的心跳定时器 + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + + // 记录日志 + this.addLog('485通信', '心跳检测已启动') + + // 每10秒检查一次485数据是否正常上报 + this.heartbeatTimer = setInterval(() => { + this.check485Heartbeat() + }, 10000) +}, +``` + +#### 3.4.2 `check485Heartbeat()` — 心跳检测逻辑 + +```javascript +// 检测485数据心跳,如果超时则触发重连 +check485Heartbeat() { + // 如果还没有收到过数据,跳过检查(等待初始化完成) + if (!this.last485DataTime) { + return + } + + const now = Date.now() + const timeout = 30 * 1000 // 30秒超时阈值 + + // 判断距离上次数据是否超过30秒 + if (now - this.last485DataTime > timeout) { + // 检查重连次数是否超限 + if (this.reconnectCount >= this.maxReconnect) { + this.addLog('485通信', '重连' + this.maxReconnect + '次失败,请检查硬件') + uni.showToast({ + title: '485通信中断,请检查硬件连接', + icon: 'none', + duration: 3000 + }) + return + } + + // 记录日志 + this.addLog('485通信', '数据中断(第' + (this.reconnectCount + 1) + '次重连)') + + uni.showToast({ + title: '485通信中断,正在恢复...', + icon: 'none', + duration: 2000 + }) + + // 重置监听 + this.resetRS485() + } +}, +``` + +#### 3.4.3 `resetRS485()` — 重置RS485监听 + +```javascript +// 重置RS485监听:停止旧连接 → 重启新连接 → 恢复心跳 +async resetRS485() { + try { + this.reconnectCount++ + + // 1. 暂停心跳检测(避免重置过程中重复触发) + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + + // 2. 停止并关闭当前RS485串口 + if (this.RS485) { + this.stopRS485() + // 等待一段时间确保串口完全关闭 + await delay(1500) + } + + // 3. 清除轮询定时器 + if (this.timer) { + clearInterval(this.timer) + this.timer = null + } + + // 4. 重置时间戳 + this.last485DataTime = null + + // 5. 重新启动RS485通信 + this.startRS485() + + // 6. 延迟后重新启动心跳检测(等待RS485初始化完成) + await delay(3000) + this.startHeartbeat() + + this.addLog('485通信', '监听已重置(第' + this.reconnectCount + '次)') + + } catch (error) { + this.addLog('485通信错误', error.message || '重置失败') + + // 失败后延迟重试 + await delay(5000) + if (this.reconnectCount < this.maxReconnect) { + this.resetRS485() + } + } +}, +``` + +--- + +### 3.5 `startRS485()` — 成功后重置计数器和记录日志 + +**修改位置**:`pages/start.vue` 第 318~348 行 + +在 `startRS485()` 方法的 `state` 判断块中,新增计数器重置和通信恢复日志: + +```javascript +startRS485() { + // 获取本地串口存储端口 + let rs485Conf = storage.get('rs485') + if (rs485Conf) { + // 设置串口和波特率 + } else { + rs485Conf = { + path: '/dev/ttyUSB2', + baudrate: 9600 + } + } + this.RS485.setPath(rs485Conf.path) + this.RS485.setBaudrate(rs485Conf.baudrate) + // 打开串口 + const state = this.RS485.open(); + if (state) { + uni.showToast({ + title: '485已打开', + icon: 'none' + }) + this.RS485.startAutoReadData((res) =>{ + let hex = this.RS485.byte2HexString(res) + this.handle485HexData(hex) + }) + // 读取数据 + clearInterval(this.timer) + this.readData() + + // 【新增】串口打开成功后,重置重连计数器和时间戳 + this.reconnectCount = 0 + this.last485DataTime = Date.now() + + // 【新增】记录通信恢复日志 + this.addLog('485通信', '通信已恢复(重连成功)') + } +}, +``` + +--- + +### 3.6 `stopRS485()` — 添加日志 + +**修改位置**:`pages/start.vue` 第 377~380 行 + +**修改前**: +```javascript +stopRS485() { + this.RS485.stopReadPortData() + this.RS485.close() +}, +``` + +**修改后**: +```javascript +stopRS485() { + this.addLog('485通信', '串口关闭') + this.RS485.stopReadPortData() + this.RS485.close() +}, +``` + +--- + +### 3.7 页面销毁时清理定时器 + +**修改位置**:`pages/start.vue`,在 `onHide()` 生命周期之后、`methods` 之前添加 `onUnload`: + +```javascript +onUnload() { + // 记录日志 + this.addLog('485通信', '页面销毁,已停止所有定时器') + + // 清理心跳定时器 + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = null + } + // 清理485轮询定时器 + if (this.timer) { + clearInterval(this.timer) + this.timer = null + } + // 清理任务定时器 + if (this.taskTimer) { + clearInterval(this.taskTimer) + this.taskTimer = null + } + // 清理左门循环定时器 + if (this.leftDoorCycleTimer) { + clearInterval(this.leftDoorCycleTimer) + this.leftDoorCycleTimer = null + } +}, +``` + +--- + +## 四、工作流程图 + +``` +启动RS485 ──▶ startHeartbeat() ──▶ 每10秒检查 + │ + ┌───────────────────▼────────────────────┐ + │ last485DataTime 距现在超过 30 秒? │ + └───┬───────────────────────┬────────────┘ + │ 否 │ 是 + ▼ ▼ + 继续等待 检查 reconnectCount + │ + ┌───────────────┴───────────────┐ + │ < 3 │ >= 3 + ▼ ▼ + resetRS485() 弹窗告警,停止重连 + │ "请检查硬件连接" + ┌───────────┼───────────┐ + ▼ ▼ ▼ + stopRS485 重置时间戳 startRS485 + │ │ + ▼ ▼ + 关闭串口 重新打开串口监听 + │ + ▼ + reconnectCount=0 + last485DataTime重置 + │ + ▼ + startHeartbeat() +``` + +--- + +## 五、关键参数说明 + +| 参数 | 值 | 说明 | +|------|-----|------| +| 心跳检测间隔 | 10 秒 | `setInterval` 的执行间隔 | +| 数据超时阈值 | 30 秒 | 超过此时间未收到数据触发重连 | +| 最大重连次数 | 3 次 | 超过后停止自动重连,需手动干预 | +| 重置等待时间 | 1500ms | 关闭串口后等待的时间 | +| 重连初始化等待 | 3000ms | 重新打开串口后等待稳定 | + +> 这些参数可根据实际硬件响应速度调整。 + +--- + +## 六、日志记录说明 + +所有关键操作都会记录到 SQLite 数据库的 `log` 表中,方便后续排查问题。 + +### 6.1 日志表示例 + +| 时间 | name | action | +|------|------|--------| +| 14:30:00 | 485通信 | 心跳检测已启动 | +| 14:35:30 | 485通信 | 数据中断(第1次重连) | +| 14:35:35 | 485通信 | 监听已重置(第1次) | +| 14:36:00 | 485通信 | 数据中断(第2次重连) | +| 14:36:05 | 485通信 | 监听已重置(第2次) | +| 14:36:35 | 485通信 | 重连3次失败,请检查硬件 | +| 15:00:00 | 485通信 | 通信已恢复(重连成功) | +| 15:30:00 | 485通信 | 页面销毁,已停止所有定时器 | + +### 6.2 日志查看方式 + +日志存储在本地 SQLite 数据库的 `log` 表中,可以通过"sqlite调试"页面查看。 + +--- + +## 七、测试验证 + +1. **正常情况**:启动后每5秒通过 `readData` 主动查询,传感器定时上报,`last485DataTime` 持续更新,心跳检测通过 +2. **模拟中断**:断开485传感器线缆,确认30秒后触发重连提示,3次后弹窗告警 +3. **模拟恢复**:在重连过程中重新接入传感器,确认 `startRS485` 成功后将计数器归零 +4. **页面切换**:离开 `start.vue` 时确认所有定时器已清除 +5. **日志验证**:检查 SQLite 数据库中的 `log` 表,确认所有关键操作都有记录 + +--- + +## 八、注意事项 + +1. **不需要控制台输出**:所有日志都记录到数据库,方便在生产环境排查问题 +2. **重连失败不需要手动恢复**:超过最大重连次数后,只能重启应用或检查硬件 +3. **参数调整**:如果硬件响应较慢,可以适当增加超时阈值和等待时间 +4. **左门循环定时器**:这是独立的调试功能,不受485中断处理影响