Files
mobile_comm/luatos/air780epm/module/Air780EPM/demo/vd960Air/proto_conv.lua
T
wangfq 3ad7ae8f09 feat(vd960Air): 方案 C MVP — 0x7F 帧解析 + 标准 JSON 上报(2026-08-31)
协议依据:《DLD960_IoT_MQTT协议》V1.13(4G 通道适配方案 C)

新增模块:
- frame_parser.lua: 0x7F 帧解析状态机(Lua 版 lup_feed_byte, XOR+SUM 校验)
- proto_conv.lua: 0xC0 传感帧 → loop_data + car_state/loop_state 沿检测(4 类线圈事件)
- evt_queue.lua: event_report 队列 + ACK 状态机(16深/5s×3/跨重连同 msg_id,对齐 DBN)
- clock.lua: report_config 时钟校准(协议 §2.3)
- app_iot.lua: initialize(extra_info imei/iccid + link)/ heartbeat / poll 驱动
- tools/sim_frame_test.py: 帧协议参考验证(56B 帧/4 路解析/沿检测/JSON 格式,全过)

改造:
- uart_app.lua: 0x7D 帧 → 0x7F 帧驱动;loop_data/event_report 组包 + link 注入
- mqtt_receiver.lua: 下行分发(ACK 路由 + 时钟校准)
- mqtt_main.lua: 连接成功 → initialize + 未决事件重发
- config.lua/main.lua: 方案 C 配置与入口

MVP 范围: 上行(initialize/loop_data/event_report/heartbeat)+ report_config;
下行命令转换与 0x7D 配置同步列入 P1。
2026-08-31 15:47:43 +08:00

142 lines
4.9 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--[[
@module proto_conv
@summary 0x7F 帧 ↔ 标准 JSON 协议转换(方案 C 核心)
@version 0.1
@date 2026.08.31
@usage
Air780 解析 vd960DBN 转发的 0x7F 帧,转换为《DLD960_IoT_MQTT协议》标准 JSON 命令:
- 0xC0 传感帧(多线圈) → loop_data JSON
- car_state / loop_state 沿检测 → event_report 事件(car_enter/car_leave/loop_cut/loop_restore)
- 4G 事件面 = 仅线圈事件(不含 DBN 内部网络事件,协议 §6.4)
0xC0 帧 payload(cmd=0xC0 之后):
[SensType(1)][SubPkgFlag(1)][SensData: 4×12B]
每路线圈单元 12B(与《DLD960Loop_串口通信协议》§3.07 一致):
config(1): freq_level(bit6-7) + direction(bit5) + freq_type(bit4) + sens(bit0-3)
eval(1): condition(bit4-7) + loop_state(bit3) + car_state(bit2) + misc_type(bit0-1)
freq(3B LE 无符号) + variation(3B LE 有符号) + misc(4B LE)
]]
local proto_conv = {}
-- 线圈高低频档位 → level 字符串
local LEVEL_NAMES = { "high", "mid_high", "mid_low", "low" }
-- 杂项类型 → type 字符串
local MISC_NAMES = { "time", "cut_count", "flow_count", "relay_count" }
-- 3B 小端无符号
local function u24le(s, off)
return string.byte(s, off)
+ string.byte(s, off + 1) * 256
+ string.byte(s, off + 2) * 65536
end
-- 3B 小端有符号(补码)
local function i24le(s, off)
local v = u24le(s, off)
if v >= 0x800000 then
v = v - 0x1000000
end
return v
end
-- 4B 小端无符号
local function u32le(s, off)
return string.byte(s, off)
+ string.byte(s, off + 1) * 256
+ string.byte(s, off + 2) * 65536
+ string.byte(s, off + 3) * 16777216
end
-- 解析 0xC0 传感帧 payload → 4 路通道表
-- @param payload string: cmd 之后的字节(SensType + SubPkgFlag + SensData)
-- @return channels table or nil: [{ch, level, iscar, loop_ok, freq, diff, sens, cndtn, misc}]
function proto_conv.parse_sensor(payload)
if not payload or payload:len() < 2 then
return nil
end
local sens_type = string.byte(payload, 1)
if sens_type ~= 0x0C then
return nil -- 非多线圈传感类型
end
-- payload: [SensType(1)][SubPkgFlag(1)][SensData...]
local data = payload:sub(3)
local channels = {}
local coil_count = 4 -- 960Loop 固定 4 路
for i = 1, coil_count do
local off = (i - 1) * 12 + 1
if data:len() < off + 11 then
break
end
local cfg = string.byte(data, off)
local evl = string.byte(data, off + 1)
local freq_level = (cfg >> 6) & 0x03
local sens = cfg & 0x0F
local condition = (evl >> 4) & 0x0F
local loop_state = (evl >> 3) & 0x01
local car_state = (evl >> 2) & 0x01
local misc_type = evl & 0x03
local freq = u24le(data, off + 2)
local diff = i24le(data, off + 5)
local misc = u32le(data, off + 8)
channels[i] = {
ch = i,
level = LEVEL_NAMES[freq_level + 1] or "high",
iscar = (car_state == 1),
loop_ok = (loop_state == 0),
freq = freq,
diff = diff,
sens = sens,
cndtn = condition,
misc = { type = MISC_NAMES[misc_type + 1] or "time", value = misc },
}
end
return channels
end
-- ==================== 事件沿检测 ====================
-- 保存上一帧每路 car_state / loop_state
local prev_car = { 0, 0, 0, 0 }
local prev_loop = { 0, 0, 0, 0 }
-- 沿检测: 输入当前通道表,输出事件列表
-- @return events: [{type, ch, value}]
function proto_conv.detect_events(channels)
local events = {}
if not channels then
return events
end
for i = 1, #channels do
local ch = channels[i]
local cur_car = ch.iscar and 1 or 0
local cur_loop = ch.loop_ok and 0 or 1 -- loop_ok=false → 断开=1
if i <= 4 then
-- car 沿(线圈断开期间屏蔽,与 DBN 同策略)
if prev_loop[i] == 0 and cur_loop == 0 then
if prev_car[i] == 0 and cur_car == 1 then
events[#events + 1] = { type = "car_enter", ch = i, value = ch.misc.value }
elseif prev_car[i] == 1 and cur_car == 0 then
events[#events + 1] = { type = "car_leave", ch = i, value = ch.misc.value }
end
end
-- loop 沿
if prev_loop[i] == 0 and cur_loop == 1 then
events[#events + 1] = { type = "loop_cut", ch = i, value = 0 }
elseif prev_loop[i] == 1 and cur_loop == 0 then
events[#events + 1] = { type = "loop_restore", ch = i, value = 0 }
end
prev_car[i] = cur_car
prev_loop[i] = cur_loop
end
end
return events
end
-- 复位沿检测状态(上电/MQTT 重连后避免误报)
function proto_conv.reset_events()
prev_car = { 0, 0, 0, 0 }
prev_loop = { 0, 0, 0, 0 }
end
return proto_conv