--[[ @module uart_app @summary vd960Air 串口应用: UART1 对接 vd960DBN(0x7F 帧) → 标准 JSON 上报(方案 C) @version 0.3 @date 2026.08.31 @usage 上行链路(方案 C): vd960Loop --0x7F帧--> vd960DBN --原样转发(UART1)--> Air780 → frame_parser 解析 0x7F 帧 → 0xC0 传感帧: proto_conv 解析 → loop_data JSON(注入 link)发布 → car_state/loop_state 沿检测: evt_queue 入队 → event_report 发布(ACK 机制) 下行链路: MQTT 标准 JSON 命令 → (P1: 转 0x7F 帧) → UART1 → vd960DBN → vd960Loop MVP 范围: 上行(initialize/loop_data/event_report/heartbeat)+ report_config 时钟校准; 下行命令转换与 0x7D 配置同步列入 P1。 ]] -- 依赖 local CFG = require "config" local link_info = require "link_info" local frame_parser = require "frame_parser" local proto_conv = require "proto_conv" local evt_queue = require "evt_queue" local clock = require "clock" local uart_app = {} local UART_ID = CFG.CFG_UART_ID -- ==================== 公共工具 ==================== -- 上行 JSON 注入 link 对象(4G 特有字段) local function inject_link(payload) if CFG.CFG_LINK_ENABLE then local ok, obj = pcall(json.decode, payload) if ok and type(obj) == "table" then obj.link = link_info.get() return json.encode(obj) end end return payload end -- 组 loop_data JSON(协议 §5.2) local function build_loop_data(channels, ts, msg_id) local parts = {} for i = 1, #channels do local c = channels[i] parts[i] = string.format( '{"ch":%d,"level":"%s","iscar":%s,"loop_ok":%s,"freq":%d,"diff":%d,' .. '"sens":%d,"cndtn":%d,"misc":{"type":"%s","value":%d}}', c.ch, c.level, tostring(c.iscar), tostring(c.loop_ok), c.freq, c.diff, c.sens, c.cndtn, c.misc.type, c.misc.value) end return string.format('{"msg_id":%d,"cmd":"loop_data","ts":%d,"data":{"channels":[%s]}}', msg_id, ts, table.concat(parts, ",")) end -- 发布 event_report(新发或重发) local function publish_event() local pend_id, pend_n, pend_ts = evt_queue.pending_info() local ts if pend_n and pend_n > 0 then ts = pend_ts -- 重发: 原始 ts,msg_id 不变 else ts = clock.now() -- 新发 end local payload = evt_queue.build_report(ts) if not payload then return end payload = inject_link(payload) sys.publish("SEND_DATA_REQ", "vd960DBN", CFG.CFG_TOPIC_UP, payload, 1) local id = evt_queue.last_msg_id() evt_queue.mark_sent(id, evt_queue.count(), ts, mstick()) log.info("uart_app.evt", "pub id", id, "n", evt_queue.count(), "ts", ts) end -- ==================== 0x7F 帧回调 ==================== -- 独立 loop_data msg_id 计数 local g_ld_msg_id = 0 local function next_ld_msg_id() g_ld_msg_id = g_ld_msg_id + 1 return g_ld_msg_id end -- 最近通道状态缓存(heartbeat 的 loop_status 用) local last_loop_ok = { true, true, true, true } local function on_frame(cmd, payload) if cmd == 0xC0 then -- 多线圈传感帧 → loop_data + 沿检测事件 local channels = proto_conv.parse_sensor(payload) if not channels then return end -- 刷新 loop_status 缓存 for i = 1, #channels do last_loop_ok[i] = channels[i].loop_ok end -- 沿检测 → 事件入队(返回是否需要立即发布) local need_evt = false local events = proto_conv.detect_events(channels) for i = 1, #events do need_evt = evt_queue.enqueue(events[i]) or need_evt end -- loop_data 上报(带 link) local ts = clock.now() local ld = inject_link(build_loop_data(channels, ts, next_ld_msg_id())) sys.publish("SEND_DATA_REQ", "vd960DBN", CFG.CFG_TOPIC_UP, ld, 0) -- 事件发布 if need_evt then publish_event() end else -- 其他 0x7F 帧(命令响应等): P1 下行命令转换实现后处理 log.info("uart_app.frame", "cmd", string.format("0x%02X", cmd), "len", payload:len()) end end -- ==================== UART 接收 ==================== local function read() local s while true do s = uart.read(UART_ID, 1024) if not s or s:len() == 0 then break end for i = 1, s:len() do frame_parser.feed(string.byte(s, i)) end end end -- ==================== 对外接口 ==================== -- MQTT 重连后: 未决事件包立即重发 function uart_app.on_mqtt_reconnect() local action = evt_queue.on_reconnect(mstick()) if action == "resend" then publish_event() end end -- 平台 ACK(事件确认)路由 function uart_app.handle_event_ack(msg_id, code) evt_queue.handle_ack(msg_id, code) end -- 主循环驱动: 事件重发超时检查 function uart_app.poll() local action = evt_queue.poll(mstick()) if action == "resend" then publish_event() end end -- 上报当前传感缓存? MVP: 心跳带 loop_status 由 mqtt_main 维护,此处暂不提供 -- heartbeat 用: 最近 4 路线圈状态(true=正常) function uart_app.get_loop_status() return last_loop_ok end -- ==================== 初始化 ==================== frame_parser.init(on_frame) uart.setup(UART_ID, CFG.CFG_UART_BAUD, 8, 1) uart.on(UART_ID, "receive", read) log.info("uart_app", "init ok", "baud", CFG.CFG_UART_BAUD, "topic_up", CFG.CFG_TOPIC_UP) return uart_app