Files
2026-05-24 19:03:37 +08:00

12 KiB
Raw Permalink Blame History

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调试"页面查看。


七、测试验证

  1. 正常情况:启动后每5秒通过 readData 主动查询,传感器定时上报,last485DataTime 持续更新,心跳检测通过
  2. 模拟中断:断开485传感器线缆,确认30秒后触发重连提示,3次后弹窗告警
  3. 模拟恢复:在重连过程中重新接入传感器,确认 startRS485 成功后将计数器归零
  4. 页面切换:离开 start.vue 时确认所有定时器已清除
  5. 日志验证:检查 SQLite 数据库中的 log 表,确认所有关键操作都有记录

八、注意事项

  1. 不需要控制台输出:所有日志都记录到数据库,方便在生产环境排查问题
  2. 重连失败不需要手动恢复:超过最大重连次数后,只能重启应用或检查硬件
  3. 参数调整:如果硬件响应较慢,可以适当增加超时阈值和等待时间
  4. 左门循环定时器:这是独立的调试功能,不受485中断处理影响