--[[ @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