12 KiB
12 KiB
RS485 数据上报中断处理方案
一、问题描述
项目运行过程中,RS485 总线上报的温湿度、压差、门状态等传感器数据可能中断。当前代码中没有数据中断检测、超时告警和自动重连机制。需要增加心跳监听、中断检测和自动重置功能。
二、涉及文件
| 文件路径 | 修改说明 |
|---|---|
pages/start.vue |
新增心跳检测、中断监听和自动重置逻辑 |
三、修改内容
3.1 data() — 新增字段
在 data() 的 return 对象中新增四个字段:
修改位置:pages/start.vue 第 51~59 行
修改前:
data() {
return {
clickTimes: 0,
RS485: undefined,
RS232: undefined,
timer: null, // 定时器读取485接口数据
taskTimer: null,
leftDoorCycleTimer: null, // 左门循环定时器
}
},
修改后:
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) 方法开头增加时间戳记录:
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 行
修改前:
initSerialport() {
try {
this.startRS485()
this.startRS232()
} catch (e) {
uni.showToast({
title: '初始化串口失败',
icon: 'error'
})
}
},
修改后:
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() — 启动心跳检测
// 启动心跳检测定时器
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() — 心跳检测逻辑
// 检测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监听
// 重置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 判断块中,新增计数器重置和通信恢复日志:
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 行
修改前:
stopRS485() {
this.RS485.stopReadPortData()
this.RS485.close()
},
修改后:
stopRS485() {
this.addLog('485通信', '串口关闭')
this.RS485.stopReadPortData()
this.RS485.close()
},
3.7 页面销毁时清理定时器
修改位置:pages/start.vue,在 onHide() 生命周期之后、methods 之前添加 onUnload:
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调试"页面查看。
七、测试验证
- 正常情况:启动后每5秒通过
readData主动查询,传感器定时上报,last485DataTime持续更新,心跳检测通过 - 模拟中断:断开485传感器线缆,确认30秒后触发重连提示,3次后弹窗告警
- 模拟恢复:在重连过程中重新接入传感器,确认
startRS485成功后将计数器归零 - 页面切换:离开
start.vue时确认所有定时器已清除 - 日志验证:检查 SQLite 数据库中的
log表,确认所有关键操作都有记录
八、注意事项
- 不需要控制台输出:所有日志都记录到数据库,方便在生产环境排查问题
- 重连失败不需要手动恢复:超过最大重连次数后,只能重启应用或检查硬件
- 参数调整:如果硬件响应较慢,可以适当增加超时阈值和等待时间
- 左门循环定时器:这是独立的调试功能,不受485中断处理影响