local RECV_TOPIC = "D_RECV_3" -- 接收DI数据通道 local SEND_TOPIC = "D_SEND_3" local dtu = io.getDtu() local db = require "db" local docfg = db.new("/data/do.cfg") -- 继电器状态配置文件有掉电记忆的功能 local cfg = db.new("/data/config4.cfg") local doInfo = docfg and docfg:export() or { do1_timestamp = 0, do2_timestamp = 0, do1_timeout = 30, -- DO1超时时间30秒 do2_timeout = 30 -- DO2超时时间30秒 } --[[ 循环定时器处理函数 功能:定期检查DO1和DO2的超时状态,并在超时后发布关闭指令 ]] local function relayTimerHandler() -- 检查DO1是否超时,若超时则发布关闭指令并重置时间戳 if doInfo.do1_timestamp > 0 and (os.time() - doInfo.do1_timestamp) >= doInfo.do1_timeout then sys.publish("D_SEND_21",{DO1 = 0}) --关闭继电器1 doInfo.do1_timestamp = 0 docfg:import(doInfo) end -- 检查DO2是否超时,若超时则发布关闭指令并重置时间戳 if doInfo.do2_timestamp > 0 and (os.time() - doInfo.do2_timestamp) >= doInfo.do2_timeout then sys.publish("D_SEND_21",{DO2 = 0}) doInfo.do2_timestamp = 0 docfg:import(doInfo) end end local function NecvSubHook(msg) local data log.info("msgSubHook", msg) log.info("msgSubHook11111", json.encode(msg)) if type(msg) == "table" and msg.payload then data = json.decode(msg.payload) end -- 若消息中包含DO1_Relay字段,则记录DO1开启时间和超时时间 if data.DO1_Relay or data.DO1 then doInfo.do1_timestamp = os.time() doInfo.do1_timeout = data.DO1_Relay or doInfo.do1_timeout docfg:import(doInfo) end -- 若消息中包含DO2_Relay字段,则记录DO2开启时间和超时时间 if data.DO2_Relay or data.DO2 then doInfo.do2_timestamp = os.time() doInfo.do2_timeout = data.DO2_Relay or doInfo.do2_timeout docfg:import(doInfo) end if data.DO1_Relay or data.DO2_Relay then log.info("DO1超时时间:", doInfo.do1_timeout, "DO2超时时间:", doInfo.do2_timeout) sys.publish("D_RECV_27",{ b = {DO1_Relay = doInfo.do1_timeout, DO2_Relay = doInfo.do2_timeout}, from = "RULE" }) end end -- 接触注释可以控制继电器点动延时时间 -- 订阅网络通道1下发的数据,触发msgSubHook回调函数 -- sys.subscribe("N_RECV_1", NecvSubHook) -- -- 启动循环定时器,每秒调用一次relayTimerHandler函数检查继电器状态 -- sys.timerLoopStart(relayTimerHandler, 1000) -- 订阅DI主动上报的数据,触发msgSubHook回调函数 local function msgSubHook(modbusMsg) if #modbusMsg < 4 then log.error("msgSubHook", "报文长度不足") return end local dataPart = modbusMsg:sub(1, -3) -- 除了最后2个字节的所有数据 local receivedCrc = string.unpack("H", modbusMsg, 3) -- 地址码(2字节,大端) local dataLen = string.byte(modbusMsg, 5) -- 数据长度 local diValue = string.unpack(">H", modbusMsg, 6) -- DI数据值(2字节,大端) -- 根据地址码确定是哪个DI local diName = string.format("DI%d", addrCode) -- 构建DI数据对象 local diData = { [diName] = diValue -- 根据地址码确定DI编号 } log.info("DI Data:", json.encode(diData)) -- 推送数据到自定义7,可选N_SEND_1 ,推送到网络通道1 sys.publish("D_RECV_27",{ b = diData, from = "RULE" }) elseif firstByte == 0xFE then -- DO解析方式:按照Modbus协议格式解析 -- modbus地址码(1字节),modbus功能码(1字节),地址码(2字节),长度(1字节),数据(2字节),校验码(2字节) if #modbusMsg < 9 then log.error("msgSubHook", "DO报文长度不足") return end local modbusAddr = string.byte(modbusMsg, 1) -- 设备地址码 (0xFE) local funcCode = string.byte(modbusMsg, 2) -- 功能码 local addrCode = string.unpack(">H", modbusMsg, 3) -- 地址码(2字节,大端) local dataLen = string.byte(modbusMsg, 5) -- 数据长度 local doValue = string.unpack(">H", modbusMsg, 6) -- DO数据值(2字节,大端) -- 根据地址码确定是哪个DO local doName = string.format("DO%d", addrCode) -- 构建DO数据对象 local doData = { [doName] = doValue -- 根据地址码确定DO编号 } log.info("DO Data:", json.encode(doData)) -- 推送数据到自定义7,可选N_SEND_1 ,推送到网络通道1 sys.publish("D_RECV_27",{ b = doData, from = "RULE" }) else log.error("msgSubHook", "未知的报文类型") return end end sys.subscribe(RECV_TOPIC, msgSubHook)