exmodbus_rtu_ascii.lua 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. -- 定义类结构;
  2. local modbus = {} -- 定义 modbus 实例的元表;
  3. modbus.__index = modbus -- 定义 modbus 实例的索引元方法,用于访问实例的属性;
  4. modbus.__metatable = "instance is protected" -- 定义 modbus 实例的元表,防止外部修改;
  5. -- 模块级变量:依赖注入的引用;
  6. local exmodbus_ref -- 主模块引用,用于访问enqueue_request等核心功能;
  7. local gen_id_func -- ID生成函数引用,用于生成唯一请求ID;
  8. -- 创建 modbus 实例的构造函数;
  9. function modbus:new(config)
  10. local obj = {
  11. mode = config.mode, -- 通信模式
  12. uart_id = config.uart_id, -- 串口 ID
  13. baud_rate = config.baud_rate, -- 波特率
  14. data_bits = config.data_bits, -- 数据位
  15. stop_bits = config.stop_bits, -- 停止位
  16. parity_bits = config.parity_bits, -- 校验位
  17. byte_order = config.byte_order, -- 字节序
  18. rs485_dir_gpio = config.rs485_dir_gpio, -- RS485 方向控制 GPIO 引脚
  19. rs485_dir_rx_level = config.rs485_dir_rx_level, -- RS485 方向控制接收电平
  20. }
  21. -- 串口是否已初始化;
  22. obj.uart_initialized = false
  23. -- 当前等待的主题;
  24. obj.current_wait_request_id = nil
  25. -- 从站请求处理回调函数;
  26. obj.slaveHandler = nil
  27. -- 设置原表;
  28. setmetatable(obj, modbus)
  29. -- 返回实例;
  30. return obj
  31. end
  32. -- 解析 Modbus RTU 请求帧(从站使用);
  33. local function parse_rtu_request(frame)
  34. -- 校验请求帧长度是否至少为 4 字节(包含从站地址、功能码和 CRC);
  35. local frame_len = #frame
  36. if frame_len < 4 then
  37. return nil
  38. end
  39. -- 仅校验 CRC(格式基础校验);
  40. local calc_crc = crypto.crc16_modbus(frame:sub(1, -3))
  41. local recv_crc = string.byte(frame, -2) + bit.lshift(string.byte(frame, -1), 8)
  42. if calc_crc ~= recv_crc then
  43. -- log.warn("exmodbus", "请求帧 CRC 校验失败")
  44. return nil
  45. end
  46. -- 提取从站地址和功能码;
  47. local slave_id = string.byte(frame, 1)
  48. local func_code = string.byte(frame, 2)
  49. -- 所有字段尽可能提取,即使值可能非法;
  50. local request_data = {
  51. slave_id = slave_id,
  52. func_code = func_code,
  53. reg_type = nil,
  54. start_addr = nil,
  55. reg_count = nil,
  56. data = {},
  57. }
  58. -- 读请求和单写请求;
  59. -- 校验请求帧长度是否为 8 字节(包含从站地址、功能码、起始地址、寄存器数量/寄存器值和 CRC);
  60. if frame_len == 8 then
  61. request_data.start_addr = bit.lshift(string.byte(frame, 3), 8) + string.byte(frame, 4)
  62. request_data.reg_count = bit.lshift(string.byte(frame, 5), 8) + string.byte(frame, 6)
  63. -- 写单个线圈;
  64. if func_code == exmodbus_ref.WRITE_SINGLE_COIL then
  65. local coil_val = bit.lshift(string.byte(frame, 5), 8) + string.byte(frame, 6)
  66. request_data.reg_count = 1
  67. request_data.data[request_data.start_addr] = (coil_val == 0xFF00) and 1 or 0
  68. -- 写单个保持寄存器;
  69. elseif func_code == exmodbus_ref.WRITE_SINGLE_HOLDING_REGISTER then
  70. local reg_val = bit.lshift(string.byte(frame, 5), 8) + string.byte(frame, 6)
  71. request_data.reg_count = 1
  72. request_data.data[request_data.start_addr] = reg_val
  73. end
  74. -- 多写请求;
  75. -- 校验请求帧长度是否至少为 9 字节(包含从站地址、功能码、起始地址、寄存器数量、字节数量、数据和 CRC);
  76. elseif frame_len >= 9 then
  77. request_data.start_addr = bit.lshift(string.byte(frame, 3), 8) + string.byte(frame, 4)
  78. request_data.reg_count = bit.lshift(string.byte(frame, 5), 8) + string.byte(frame, 6)
  79. -- 写多个线圈;
  80. if func_code == exmodbus_ref.WRITE_MULTIPLE_COILS then
  81. for i = 0, request_data.reg_count - 1 do
  82. local byte_idx = 8 + math.floor(i / 8)
  83. if byte_idx > frame_len - 2 then break end -- 防止越界;
  84. local bit_idx = i % 8
  85. local byte_val = string.byte(frame, byte_idx)
  86. local bit_val = bit.band(byte_val, bit.lshift(1, bit_idx)) ~= 0 and 1 or 0
  87. request_data.data[request_data.start_addr + i] = bit_val
  88. end
  89. -- 写多个保持寄存器;
  90. elseif func_code == exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS then
  91. for i = 0, request_data.reg_count - 1 do
  92. local pos = 8 + i * 2
  93. if pos + 1 > frame_len - 2 then break end -- 防止越界;
  94. local val = bit.lshift(string.byte(frame, pos), 8) + string.byte(frame, pos + 1)
  95. request_data.data[request_data.start_addr + i] = val
  96. end
  97. end
  98. end
  99. -- 对于读请求,data 为 nil,由用户处理读逻辑;
  100. if not request_data.data and (
  101. func_code == exmodbus_ref.READ_COILS or
  102. func_code == exmodbus_ref.READ_DISCRETE_INPUTS or
  103. func_code == exmodbus_ref.READ_HOLDING_REGISTERS or
  104. func_code == exmodbus_ref.READ_INPUT_REGISTERS
  105. ) then
  106. request_data.data = nil -- request_data.data 保持 nil,由用户处理读逻辑;
  107. end
  108. return request_data
  109. end
  110. -- 构建 Modbus RTU 响应帧(从站使用);
  111. local function build_rtu_response(request, user_return)
  112. local slave_id = request.slave_id
  113. local func_code = request.func_code
  114. -- 用户返回异常码 -> 异常响应;
  115. if type(user_return) == "number" then
  116. local exception_code = user_return
  117. local frame = string.char(slave_id, bit.bor(func_code, 0x80), exception_code)
  118. local crc = crypto.crc16_modbus(frame)
  119. return frame .. string.char(crc & 0xFF, (crc >> 8) & 0xFF)
  120. end
  121. -- 用户返回表 -> 正常响应;
  122. if type(user_return) ~= "table" then
  123. log.error("exmodbus", "从站回调必须返回 table 或 number,实际类型: ", type(user_return))
  124. return nil
  125. end
  126. local data_bytes = ""
  127. -- 处理读线圈和读离散输入响应;
  128. if func_code == exmodbus_ref.READ_COILS or func_code == exmodbus_ref.READ_DISCRETE_INPUTS then
  129. local reg_count = request.reg_count
  130. -- 校验 reg_count 是否有效;
  131. if not reg_count or reg_count <= 0 then
  132. log.error("exmodbus", "请求中 reg_count 无效")
  133. return nil
  134. end
  135. local byte_count = math.ceil(reg_count / 8)
  136. local values = {}
  137. for i = 0, reg_count - 1 do
  138. local addr = request.start_addr + i
  139. local bit_val = user_return[addr]
  140. if bit_val == nil then
  141. log.error("exmodbus", "读线圈/离散输入回调未返回地址 ", addr, " 的数据")
  142. return nil
  143. end
  144. if bit_val ~= 0 and bit_val ~= 1 then
  145. log.error("exmodbus", "地址 ", addr, " 的值必须为 0 或 1,实际: ", bit_val)
  146. return nil
  147. end
  148. local byte_idx = math.floor(i / 8)
  149. if not values[byte_idx] then values[byte_idx] = 0 end
  150. if bit_val == 1 then
  151. values[byte_idx] = bit.bor(values[byte_idx], bit.lshift(1, i % 8))
  152. end
  153. end
  154. data_bytes = string.char(byte_count)
  155. for i = 0, byte_count - 1 do
  156. data_bytes = data_bytes .. string.char(values[i] or 0)
  157. end
  158. -- 处理读保持寄存器和读输入寄存器响应;
  159. elseif func_code == exmodbus_ref.READ_HOLDING_REGISTERS or func_code == exmodbus_ref.READ_INPUT_REGISTERS then
  160. local reg_count = request.reg_count
  161. -- 校验 reg_count 是否有效;
  162. if not reg_count or reg_count <= 0 then
  163. log.error("exmodbus", "请求中 reg_count 无效")
  164. return nil
  165. end
  166. local values = ""
  167. for i = 0, reg_count - 1 do
  168. local addr = request.start_addr + i
  169. local val = user_return[addr]
  170. if val == nil then
  171. log.error("exmodbus", "读保持寄存器/输入寄存器回调未返回地址 ", addr, " 的数据")
  172. return nil
  173. end
  174. if type(val) ~= "number" or val ~= math.floor(val) or val < 0 or val > 65535 then
  175. log.error("exmodbus", "地址 ", addr, " 的值必须为 0~65535 的整数,实际: ", val)
  176. return nil
  177. end
  178. values = values .. string.char((val >> 8) & 0xFF, val & 0xFF)
  179. end
  180. data_bytes = string.char(#values) .. values
  181. -- 处理写单个线圈响应;
  182. elseif func_code == exmodbus_ref.WRITE_SINGLE_COIL then
  183. local addr = request.start_addr
  184. -- 校验 start_addr 是否有效;
  185. if addr == nil then
  186. log.error("exmodbus", "请求中 start_addr 无效")
  187. return nil
  188. end
  189. local coil_val = (request.data and request.data[addr]) or 0
  190. local resp_val = (coil_val ~= 0) and 0xFF00 or 0x0000
  191. data_bytes = string.char(
  192. (addr >> 8) & 0xFF, addr & 0xFF,
  193. (resp_val >> 8) & 0xFF, resp_val & 0xFF
  194. )
  195. -- 处理写单个保持寄存器响应;
  196. elseif func_code == exmodbus_ref.WRITE_SINGLE_HOLDING_REGISTER then
  197. local addr = request.start_addr
  198. -- 校验 start_addr 是否有效;
  199. if addr == nil then
  200. log.error("exmodbus", "请求中 start_addr 无效")
  201. return nil
  202. end
  203. local reg_val = (request.data and request.data[addr]) or 0
  204. -- 校验 reg_val 是否有效;
  205. if type(reg_val) ~= "number" or reg_val ~= math.floor(reg_val) or reg_val < 0 or reg_val > 65535 then
  206. log.error("exmodbus", "地址 ", addr, " 的值必须为 0~65535 的整数,实际: ", reg_val)
  207. return nil
  208. end
  209. data_bytes = string.char(
  210. (addr >> 8) & 0xFF, addr & 0xFF,
  211. (reg_val >> 8) & 0xFF, reg_val & 0xFF
  212. )
  213. -- 处理写多个线圈/保持寄存器响应;
  214. elseif func_code == exmodbus_ref.WRITE_MULTIPLE_COILS or func_code == exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS then
  215. local start_addr = request.start_addr
  216. local reg_count = request.reg_count
  217. -- 校验 start_addr 和 reg_count 是否有效;
  218. if not start_addr or not reg_count or reg_count <= 0 then
  219. log.error("exmodbus", "请求中 start_addr 或 reg_count 无效")
  220. return nil
  221. end
  222. data_bytes = string.char(
  223. (start_addr >> 8) & 0xFF, start_addr & 0xFF,
  224. (reg_count >> 8) & 0xFF, reg_count & 0xFF
  225. )
  226. -- 处理未知功能码,视为错误;
  227. else
  228. log.error("exmodbus", "不支持的功能码,且未返回异常码: ", func_code)
  229. return nil
  230. end
  231. local frame = string.char(slave_id, func_code) .. data_bytes
  232. local crc = crypto.crc16_modbus(frame)
  233. return frame .. string.char(crc & 0xFF, (crc >> 8) & 0xFF)
  234. end
  235. -- 初始化串口;
  236. local function init_uart(instance)
  237. -- 检查串口是否已被初始化;
  238. if instance.uart_initialized then
  239. log.warn("exmodbus", "串口 ", instance.uart_id, " 已经初始化,无需重复初始化")
  240. return true
  241. end
  242. -- 配置串口参数,并开启串口功能;
  243. local result = uart.setup(
  244. instance.uart_id, -- 串口 ID
  245. instance.baud_rate, -- 波特率
  246. instance.data_bits, -- 数据位
  247. instance.stop_bits, -- 停止位
  248. instance.parity_bits, -- 校验位
  249. instance.byte_order, -- 字节序
  250. nil, -- 缓冲区大小
  251. instance.rs485_dir_gpio, -- RS485 方向控制 GPIO 引脚
  252. instance.rs485_dir_rx_level -- RS485 方向控制接收电平
  253. )
  254. -- 检查串口是否初始化成功;
  255. -- 成功时返回 0,其他返回值表示失败;
  256. if result ~= 0 then
  257. log.error("exmodbus", "串口 ", instance.uart_id, " 初始化失败")
  258. return false
  259. end
  260. -- 定义发送完成回调函数;
  261. -- 当串口发送完成时,发布一个主题,通知其他任务;
  262. local function on_sent(uart_id)
  263. sys.publish("exmodbus/sent/" .. uart_id, true)
  264. end
  265. -- 定义接收完成回调函数;
  266. -- 当串口接收完成时,对接收数据进行处理;
  267. -- 处理成功时,发布一个主题,通知其他任务;
  268. -- 处理失败时,不做任何处理;
  269. local function on_receive(uart_id, data_len)
  270. local data = uart.read(uart_id, data_len)
  271. if not data or #data == 0 then return end
  272. -- 处理 RTU 主站模式下的接收数据;
  273. if instance.mode == exmodbus_ref.RTU_MASTER then
  274. -- 校验等待主题是否存在;
  275. if instance.current_wait_request_id then
  276. -- 发布主题,通知其他任务;
  277. sys.publish("exmodbus/rtu_resp/" .. instance.current_wait_request_id, data)
  278. -- 发布后,清除等待主题;
  279. instance.current_wait_request_id = nil
  280. return
  281. end
  282. -- 处理 RTU 从站模式下的接收数据;
  283. elseif instance.mode == exmodbus_ref.RTU_SLAVE then
  284. -- 解析 RTU 请求帧;
  285. local request = parse_rtu_request(data)
  286. if request then
  287. -- 广播地址(0)不响应;
  288. if request.slave_id == 0 then
  289. -- 调用回调以允许用户记录或处理广播命令(如写寄存器);
  290. if instance.slaveHandler then
  291. instance.slaveHandler(request)
  292. -- 注意:即使回调返回数据,也不发送响应;
  293. end
  294. -- 广播请求处理完毕,不回复;
  295. return
  296. end
  297. if instance.slaveHandler then
  298. local user_return = instance.slaveHandler(request)
  299. local response_frame = build_rtu_response(request, user_return)
  300. if response_frame then
  301. uart.write(uart_id, response_frame)
  302. else
  303. log.error("exmodbus", "构建响应帧失败,从站地址:", request.slave_id)
  304. end
  305. else
  306. log.warn("exmodbus", "收到主站请求,但未注册回调函数")
  307. end
  308. else
  309. log.debug("exmodbus", "无效 RTU 请求帧(CRC 或格式错误)")
  310. end
  311. return
  312. end
  313. end
  314. -- 注册发送完成和接收完成回调函数;
  315. uart.on(instance.uart_id, "sent", on_sent)
  316. uart.on(instance.uart_id, "receive", on_receive)
  317. -- 初始化成功,设置标志位为 true;
  318. instance.uart_initialized = true
  319. log.info("exmodbus", "串口 " .. instance.uart_id .. " 初始化成功,波特率 " .. instance.baud_rate)
  320. return true
  321. end
  322. -- 创建一个新的实例;
  323. local function create(config, exmodbus, gen_request_id)
  324. exmodbus_ref = exmodbus
  325. gen_id_func = gen_request_id
  326. -- 创建一个新的实例;
  327. local instance = modbus:new(config)
  328. -- 检查实例是否创建成功;
  329. if not instance then
  330. log.error("exmodbus", "创建 Modbus 实例失败")
  331. return false
  332. end
  333. -- 初始化串口;
  334. local result = init_uart(instance)
  335. -- 检查串口初始化结果;
  336. if not result then
  337. -- 销毁已创建的实例,释放资源;
  338. instance:destroy()
  339. return false
  340. end
  341. -- 返回实例;
  342. return instance
  343. end
  344. -- 销毁已创建的实例,释放资源;
  345. function modbus:destroy()
  346. -- 检查实例是否已被销毁;
  347. if not self then
  348. log.error("exmodbus", "实例对象已被销毁,无需重复销毁")
  349. return
  350. end
  351. -- 关闭串口;
  352. if self.uart_initialized then
  353. uart.close(self.uart_id)
  354. uart.on(self.uart_id, "sent", nil)
  355. uart.on(self.uart_id, "receive", nil)
  356. end
  357. -- 释放GPIO资源;
  358. if self.rs485_dir_gpio then
  359. gpio.close(self.rs485_dir_gpio)
  360. end
  361. -- 销毁已创建的实例;
  362. setmetatable(self, nil)
  363. log.info("exmodbus", "实例对象已销毁")
  364. end
  365. -- 构建 Modbus RTU 帧的函数,支持读取和写入操作;(主站使用)
  366. local function build_rtu_frame(opt_type, config)
  367. -- 参数验证;
  368. if not config or type(config) ~= "table" then
  369. log.error("exmodbus", "配置必须是表格类型")
  370. return false
  371. end
  372. -- 验证必要参数;
  373. if not config.slave_id then
  374. log.error("exmodbus", "缺少必要参数: slave_id")
  375. return false
  376. end
  377. if not config.reg_type then
  378. log.error("exmodbus", "缺少必要参数: reg_type")
  379. return false
  380. end
  381. if not config.start_addr then
  382. log.error("exmodbus", "缺少必要参数: start_addr")
  383. return false
  384. end
  385. if not config.reg_count then
  386. log.error("exmodbus", "缺少必要参数: reg_count")
  387. return false
  388. end
  389. if opt_type == "write" then
  390. if not config.data then
  391. log.error("exmodbus", "缺少写入请求必要参数: data")
  392. return false
  393. end
  394. end
  395. -- 参数范围验证;
  396. if type(config.slave_id) ~= "number" or config.slave_id < 1 or config.slave_id > 247 then
  397. log.error("exmodbus", "从站地址必须在 1-247 范围内")
  398. return false
  399. end
  400. if type(config.start_addr) ~= "number" or config.start_addr < 0 or config.start_addr > 65535 then
  401. log.error("exmodbus", "起始地址必须在 0-65535 范围内")
  402. return false
  403. end
  404. if config.reg_type ~= exmodbus_ref.COIL_STATUS and config.reg_type ~= exmodbus_ref.INPUT_STATUS and
  405. config.reg_type ~= exmodbus_ref.HOLDING_REGISTER and config.reg_type ~= exmodbus_ref.INPUT_REGISTER then
  406. log.error("exmodbus", "无效的寄存器类型: " .. tostring(config.reg_type))
  407. return false
  408. end
  409. -- 根据操作类型和寄存器类型确定功能码;
  410. local function_code
  411. if opt_type == "write" then
  412. -- 校验每一个地址是否有数据,且数据是否为数字类型;
  413. for i = 0, config.reg_count - 1 do
  414. local addr = config.start_addr + i
  415. if config.data[addr] == nil then
  416. log.error("exmodbus", "缺少寄存器数据", "address:", addr)
  417. return false
  418. end
  419. if type(config.data[addr]) ~= "number" then
  420. log.error("exmodbus", "寄存器数据必须是数字类型", "address:", addr)
  421. return false
  422. end
  423. end
  424. -- 判断是否强制使用写多个功能码;
  425. local use_multiple = config.force_multiple
  426. if config.reg_count == 1 then
  427. -- 写入单个线圈或单个保持寄存器;
  428. if not use_multiple then -- 使用写单个功能码;
  429. if config.reg_type == exmodbus_ref.COIL_STATUS then
  430. function_code = exmodbus_ref.WRITE_SINGLE_COIL
  431. elseif config.reg_type == exmodbus_ref.HOLDING_REGISTER then
  432. function_code = exmodbus_ref.WRITE_SINGLE_HOLDING_REGISTER
  433. end
  434. else -- 使用写多个功能码;
  435. if config.reg_type == exmodbus_ref.COIL_STATUS then
  436. function_code = exmodbus_ref.WRITE_MULTIPLE_COILS
  437. elseif config.reg_type == exmodbus_ref.HOLDING_REGISTER then
  438. function_code = exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS
  439. end
  440. end
  441. elseif config.reg_count > 1 then
  442. -- 写入多个线圈或寄存器;
  443. if config.reg_type == exmodbus_ref.COIL_STATUS then
  444. function_code = exmodbus_ref.WRITE_MULTIPLE_COILS
  445. elseif config.reg_type == exmodbus_ref.HOLDING_REGISTER then
  446. function_code = exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS
  447. end
  448. end
  449. elseif opt_type == "read" then
  450. -- 读线圈状态;
  451. if config.reg_type == exmodbus_ref.COIL_STATUS then
  452. function_code = exmodbus_ref.READ_COILS
  453. -- 读离散输入状态;
  454. elseif config.reg_type == exmodbus_ref.INPUT_STATUS then
  455. function_code = exmodbus_ref.READ_DISCRETE_INPUTS
  456. -- 读保持寄存器;
  457. elseif config.reg_type == exmodbus_ref.HOLDING_REGISTER then
  458. function_code = exmodbus_ref.READ_HOLDING_REGISTERS
  459. -- 读输入寄存器;
  460. elseif config.reg_type == exmodbus_ref.INPUT_REGISTER then
  461. function_code = exmodbus_ref.READ_INPUT_REGISTERS
  462. end
  463. end
  464. local data_bytes
  465. -- 功能码 0x01 和 0x02:读取线圈状态和离散输入状态;
  466. if function_code == exmodbus_ref.READ_COILS or function_code == exmodbus_ref.READ_DISCRETE_INPUTS then
  467. -- 验证数量范围;
  468. if config.reg_count < 1 or config.reg_count > 2000 then
  469. log.error("exmodbus", "线圈/离散输入读取数量超出范围: " .. config.reg_count .. " (范围: 1-2000)")
  470. return false
  471. end
  472. -- 构建数据部分(起始地址 + 数量)(大端序);
  473. data_bytes = string.char(
  474. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  475. (config.reg_count >> 8) & 0xFF, config.reg_count & 0xFF
  476. )
  477. -- 功能码 0x03 和 0x04:读取保持寄存器和输入寄存器;
  478. elseif function_code == exmodbus_ref.READ_HOLDING_REGISTERS or function_code == exmodbus_ref.READ_INPUT_REGISTERS then
  479. -- 验证数量范围;
  480. if config.reg_count < 1 or config.reg_count > 125 then
  481. log.error("exmodbus", "寄存器读取数量超出范围: " .. config.reg_count .. " (范围: 1-125)")
  482. return false
  483. end
  484. -- 构建数据部分(起始地址 + 数量)(字节序为大端序);
  485. data_bytes = string.char(
  486. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  487. (config.reg_count >> 8) & 0xFF, config.reg_count & 0xFF
  488. )
  489. -- 功能码 0x05:写入单个线圈;
  490. elseif function_code == exmodbus_ref.WRITE_SINGLE_COIL then
  491. -- 写入单个线圈,值必须是 0xFF00 (ON) 或 0x0000 (OFF);
  492. local value = config.data[config.start_addr] ~= 0 and 0xFF00 or 0x0000
  493. -- 构建数据部分(起始地址 + 值)(字节序为大端序);
  494. data_bytes = string.char(
  495. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  496. (value >> 8) & 0xFF, value & 0xFF
  497. )
  498. -- 功能码 0x06:写入单个保持寄存器;
  499. elseif function_code == exmodbus_ref.WRITE_SINGLE_HOLDING_REGISTER then
  500. -- 写入单个保持寄存器;
  501. local value = config.data[config.start_addr]
  502. -- 验证寄存器值范围(16 位无符号整数);
  503. if value < 0 or value > 65535 or value ~= math.floor(value) then
  504. log.error("exmodbus", "寄存器值必须是 0~65535 范围内的整数,实际值: ", value)
  505. return false
  506. end
  507. -- 构建数据部分(起始地址 + 值)(字节序为大端序);
  508. data_bytes = string.char(
  509. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  510. (value >> 8) & 0xFF, value & 0xFF
  511. )
  512. -- 功能码 0x0F:写入多个线圈;
  513. elseif function_code == exmodbus_ref.WRITE_MULTIPLE_COILS then
  514. -- 验证数量范围;
  515. if config.reg_count < 1 or config.reg_count > 1968 then
  516. log.error("exmodbus", "线圈写入数量超出范围: " .. config.reg_count .. " (范围: 1-1968)")
  517. return false
  518. end
  519. -- 计算字节数;
  520. local byte_count = math.ceil(config.reg_count / 8)
  521. local values_bytes = ""
  522. -- 构建线圈数据(字节序为大端序);
  523. for i = 0, byte_count - 1 do
  524. local byte_value = 0
  525. -- 遍历当前字节的 8 个位;
  526. for j = 0, 7 do
  527. local bit_index = i * 8 + j -- 计算当前比特在整个线圈序列中的全局索引(从 0 开始);
  528. -- 检查当前比特是否在有效范围内;
  529. if bit_index < config.reg_count then
  530. local addr = config.start_addr + bit_index -- 根据起始地址和全局索引计算实际的线圈地址;
  531. local bit_val = config.data[addr] -- 获取当前线圈的状态值(0 或 1);
  532. if bit_val ~= nil and bit_val ~= 0 then
  533. byte_value = byte_value | (1 << j) -- 如果状态为 1,则将当前位设置为 1;
  534. end
  535. end
  536. end
  537. values_bytes = values_bytes .. string.char(byte_value)
  538. end
  539. -- 构建数据部分(起始地址 + 数量 + 字节数 + 线圈数据)(字节序为大端序);
  540. data_bytes = string.char(
  541. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  542. (config.reg_count >> 8) & 0xFF, config.reg_count & 0xFF,
  543. byte_count
  544. ) .. values_bytes
  545. -- 功能码 0x10:写入多个保持寄存器;
  546. elseif function_code == exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS then
  547. -- 验证数量范围;
  548. if config.reg_count < 1 or config.reg_count > 123 then
  549. log.error("exmodbus", "寄存器写入数量超出范围: " .. config.reg_count .. " (范围: 1-123)")
  550. return false
  551. end
  552. -- 计算字节数;
  553. local byte_count = config.reg_count * 2
  554. local values_bytes = ""
  555. -- 构建寄存器数据(字节序为大端序);
  556. for i = 0, config.reg_count - 1 do
  557. local addr = config.start_addr + i
  558. local value = config.data[addr]
  559. values_bytes = values_bytes .. string.char(
  560. (value >> 8) & 0xFF, value & 0xFF
  561. )
  562. end
  563. -- 构建数据部分(起始地址 + 数量 + 字节数 + 寄存器数据)(字节序为大端序);
  564. data_bytes = string.char(
  565. (config.start_addr >> 8) & 0xFF, config.start_addr & 0xFF,
  566. (config.reg_count >> 8) & 0xFF, config.reg_count & 0xFF,
  567. byte_count
  568. ) .. values_bytes
  569. -- 未知功能码;
  570. else
  571. log.error("exmodbus", "不支持的功能码构建: " .. function_code)
  572. return false
  573. end
  574. -- 构建 Modbus RTU 帧(从站地址 + 功能码 + 数据);
  575. local frame = string.char(config.slave_id, function_code) .. data_bytes
  576. -- 计算 CRC16 校验并添加到帧末尾(小端序);
  577. local crc = crypto.crc16_modbus(frame)
  578. frame = frame .. string.char(crc & 0xFF, (crc >> 8) & 0xFF)
  579. return frame, function_code
  580. end
  581. -- 发送 Modbus 请求并等待响应;
  582. local function sendRequest_waitResponse(instance, request_frame, config)
  583. -- 生成唯一请求ID;
  584. local req_id = gen_id_func()
  585. instance.current_wait_request_id = req_id
  586. -- 执行发送请求;
  587. uart.write(instance.uart_id, request_frame)
  588. -- 等待发送完成;
  589. local sent_ok = sys.waitUntil("exmodbus/sent/" .. instance.uart_id, 200)
  590. if not sent_ok then
  591. log.error("exmodbus", "数据发送失败")
  592. instance.current_wait_request_id = nil
  593. return false, nil
  594. end
  595. -- -- 显示发送的HEX数据;
  596. -- local hex_str = ""
  597. -- for i = 1, #request_frame do
  598. -- hex_str = hex_str .. string.format("%02X ", string.byte(request_frame, i))
  599. -- end
  600. -- log.info("exmodbus", "发送请求命令成功, HEX: " .. hex_str:sub(1, -2))
  601. -- 等待接收响应;
  602. local ok, response = sys.waitUntil("exmodbus/rtu_resp/" .. req_id, config.timeout or 1000)
  603. -- 清除当前等待的请求ID;
  604. instance.current_wait_request_id = nil
  605. -- 显示接收的HEX数据;
  606. if ok then
  607. -- hex_str = ""
  608. -- for i = 1, #response do
  609. -- hex_str = hex_str .. string.format("%02X ", string.byte(response, i))
  610. -- end
  611. -- log.info("exmodbus", "接收响应成功, HEX: " .. hex_str:sub(1, -2))
  612. return true, response
  613. else
  614. -- log.error("exmodbus", "接收响应失败或超时")
  615. return false, nil
  616. end
  617. end
  618. -- 解析 Modbus RTU 响应报文(主站使用);
  619. local function parse_rtu_response(response, config, function_code)
  620. -- 定义返回数据结构;
  621. local return_data = {
  622. status = false,
  623. execption_code = nil,
  624. data = {},
  625. }
  626. -- 验证响应是否为空;
  627. if not response or #response == 0 then
  628. log.error("exmodbus", "响应报文为空")
  629. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  630. return return_data
  631. end
  632. -- 验证响应长度(最小长度:从站地址 + 功能码 + CRC = 4 字节);
  633. if not response or #response < 4 then
  634. log.error("exmodbus", "响应报文长度不足")
  635. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  636. return return_data
  637. end
  638. -- 提取响应中的字段;
  639. local actual_slave_id = string.byte(response, 1)
  640. local actual_function_code = string.byte(response, 2)
  641. local response_length = #response
  642. -- 验证从站地址是否匹配;
  643. if actual_slave_id ~= config.slave_id then
  644. log.error("exmodbus", "从站地址不匹配,期望:", config.slave_id, "实际:", actual_slave_id)
  645. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  646. return return_data
  647. end
  648. -- 检查是否为异常响应(功能码最高位为 1);
  649. if bit.band(actual_function_code, 0x80) ~= 0 then
  650. -- 异常响应格式:从站地址(1 字节) + 功能码(1 字节) + 异常码(1 字节) + CRC(2 字节);
  651. if response_length ~= 5 then
  652. log.error("exmodbus", "异常响应报文长度不正确,期望: 5 字节,实际:", response_length, "字节")
  653. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  654. return return_data
  655. end
  656. -- 提取异常码(第 3 字节);
  657. local exception_code = string.byte(response, 3)
  658. log.error("exmodbus", "接收到 Modbus 异常响应,功能码:", actual_function_code, "异常码:", exception_code)
  659. return_data.status = exmodbus_ref.STATUS_EXCEPTION
  660. return_data.execption_code = exception_code
  661. return return_data
  662. end
  663. -- 验证功能码是否匹配;
  664. if actual_function_code ~= function_code then
  665. log.error("exmodbus", "功能码不匹配,期望:", function_code, "实际:", actual_function_code)
  666. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  667. return return_data
  668. end
  669. -- 根据不同的功能码解析数据;
  670. local parsed_data = {}
  671. -- 功能码 0x01 和 0x02:读取线圈状态和离散输入状态;
  672. if function_code == exmodbus_ref.READ_COILS or function_code == exmodbus_ref.READ_DISCRETE_INPUTS then
  673. -- 提取数据部分(不包括CRC);
  674. local data_length = string.byte(response, 3)
  675. local data_start_pos = 4
  676. local data_end_pos = response_length - 2 -- 减去CRC长度
  677. -- 验证数据长度是否正确;
  678. -- 注意:这里只验证响应报文中声明的数据长度与实际数据长度是否一致;
  679. if data_end_pos - data_start_pos + 1 ~= data_length then
  680. log.error("exmodbus", "数据长度不匹配,期望:", data_length, "实际:", data_end_pos - data_start_pos + 1)
  681. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  682. return return_data
  683. end
  684. -- 验证字节数是否足够表示指定数量的位;
  685. local expected_bytes = math.ceil(config.reg_count / 8)
  686. if data_length < expected_bytes then
  687. log.error("exmodbus", "数据字节数不足,无法表示所有位")
  688. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  689. return return_data
  690. end
  691. -- 解析位数据;
  692. for i = 0, config.reg_count - 1 do
  693. local modbus_addr = config.start_addr + i -- 计算当前位对应的 Modbus 地址;
  694. local byte_pos = data_start_pos + math.floor(i / 8) -- 计算当前位对应的字节位置;
  695. local bit_pos = i % 8 -- 计算当前位对应的位位置;
  696. local byte_value = string.byte(response, byte_pos)
  697. parsed_data[modbus_addr] = bit.band(byte_value, bit.lshift(1, bit_pos)) ~= 0 and 1 or 0
  698. end
  699. -- 功能码 0x03 和 0x04:读取保持寄存器和输入寄存器;
  700. elseif function_code == exmodbus_ref.READ_HOLDING_REGISTERS or function_code == exmodbus_ref.READ_INPUT_REGISTERS then
  701. -- 提取数据部分(不包括CRC);
  702. local data_length = string.byte(response, 3)
  703. local data_start_pos = 4
  704. local data_end_pos = response_length - 2 -- 减去CRC长度
  705. -- 验证数据长度是否正确;
  706. if data_end_pos - data_start_pos + 1 ~= data_length then
  707. log.error("exmodbus", "数据长度不匹配,期望:", data_length, "实际:", data_end_pos - data_start_pos + 1)
  708. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  709. return return_data
  710. end
  711. -- 验证字节数是否足够表示指定数量的寄存器;
  712. local expected_bytes = config.reg_count * 2
  713. if data_length < expected_bytes then
  714. log.error("exmodbus", "数据字节数不足,无法表示所有寄存器")
  715. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  716. return return_data
  717. end
  718. -- 解析寄存器数据(大端序);
  719. for i = 0, config.reg_count - 1 do
  720. local modbus_addr = config.start_addr + i -- 计算当前寄存器对应的 Modbus 地址;
  721. local reg_pos = data_start_pos + i * 2 -- 计算当前寄存器对应的字节位置;
  722. parsed_data[modbus_addr] = bit.lshift(string.byte(response, reg_pos), 8) + string.byte(response, reg_pos + 1)
  723. end
  724. -- 功能码 0x05:写入单个线圈;
  725. elseif function_code == exmodbus_ref.WRITE_SINGLE_COIL then
  726. -- 写入单个线圈响应格式:从站地址(1 字节) + 功能码(1 字节) + 线圈地址(2 字节) + 线圈值(2 字节) + CRC(2 字节);
  727. if response_length ~= 8 then
  728. log.error("exmodbus", "写入单个线圈响应报文长度不正确")
  729. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  730. return return_data
  731. end
  732. -- 解析线圈地址和值;
  733. local coil_addr = bit.lshift(string.byte(response, 3), 8) + string.byte(response, 4)
  734. local coil_value = bit.lshift(string.byte(response, 5), 8) + string.byte(response, 6)
  735. -- 验证地址是否匹配请求;
  736. if config.start_addr and coil_addr ~= config.start_addr then
  737. log.error("exmodbus", "线圈地址不匹配,期望:", config.start_addr, "实际:", coil_addr)
  738. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  739. return return_data
  740. end
  741. -- 线圈值应该是 0x0000(OFF) 或 0xFF00(ON);
  742. local normalized_value = (coil_value == 0x0000) and 0 or 1
  743. parsed_data[coil_addr] = normalized_value
  744. -- 功能码 0x06:写入单个保持寄存器;
  745. elseif function_code == exmodbus_ref.WRITE_SINGLE_HOLDING_REGISTER then
  746. -- 写入单个保持寄存器响应格式:从站地址(1 字节) + 功能码(1 字节) + 寄存器地址(2 字节) + 寄存器值(2 字节) + CRC(2 字节);
  747. if response_length ~= 8 then
  748. log.error("exmodbus", "写入单个保持寄存器响应报文长度不正确")
  749. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  750. return return_data
  751. end
  752. -- 解析寄存器地址和值;
  753. local reg_addr = bit.lshift(string.byte(response, 3), 8) + string.byte(response, 4)
  754. local reg_value = bit.lshift(string.byte(response, 5), 8) + string.byte(response, 6)
  755. -- 验证地址是否匹配请求;
  756. if config.start_addr and reg_addr ~= config.start_addr then
  757. log.error("exmodbus", "单个保持寄存器地址不匹配,期望:", config.start_addr, "实际:", reg_addr)
  758. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  759. return return_data
  760. end
  761. parsed_data[reg_addr] = reg_value
  762. -- 功能码 0x0F:写入多个线圈;
  763. elseif function_code == exmodbus_ref.WRITE_MULTIPLE_COILS then
  764. -- 写入多个线圈响应格式:从站地址(1 字节) + 功能码(1 字节) + 起始地址(2 字节) + 线圈数量(2 字节) + CRC(2 字节);
  765. if response_length ~= 8 then
  766. log.error("exmodbus", "写入多个线圈响应报文长度不正确")
  767. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  768. return return_data
  769. end
  770. -- 解析起始地址和线圈数量;
  771. local start_addr = bit.lshift(string.byte(response, 3), 8) + string.byte(response, 4)
  772. local coil_count = bit.lshift(string.byte(response, 5), 8) + string.byte(response, 6)
  773. -- 验证地址和数量是否匹配请求;
  774. if config.start_addr and start_addr ~= config.start_addr then
  775. log.error("exmodbus", "线圈起始地址不匹配,期望:", config.start_addr, "实际:", start_addr)
  776. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  777. return return_data
  778. end
  779. if config.reg_count and coil_count ~= config.reg_count then
  780. log.error("exmodbus", "线圈数量不匹配,期望:", config.reg_count, "实际:", coil_count)
  781. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  782. return return_data
  783. end
  784. -- 在返回数据中记录操作成功的起始地址和数量;
  785. parsed_data.start_addr = start_addr
  786. parsed_data.count = coil_count
  787. -- 功能码 0x10:写入多个保持寄存器;
  788. elseif function_code == exmodbus_ref.WRITE_MULTIPLE_HOLDING_REGISTERS then
  789. -- 写入多个保持寄存器响应格式:从站地址(1 字节) + 功能码(1 字节) + 起始地址(2 字节) + 寄存器数量(2 字节) + CRC(2 字节);
  790. if response_length ~= 8 then
  791. log.error("exmodbus", "写入多个保持寄存器响应报文长度不正确")
  792. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  793. return return_data
  794. end
  795. -- 解析起始地址和寄存器数量;
  796. local start_addr = bit.lshift(string.byte(response, 3), 8) + string.byte(response, 4)
  797. local reg_count = bit.lshift(string.byte(response, 5), 8) + string.byte(response, 6)
  798. -- 验证地址和数量是否匹配请求;
  799. if config.start_addr and start_addr ~= config.start_addr then
  800. log.error("exmodbus", "寄存器起始地址不匹配,期望:", config.start_addr, "实际:", start_addr)
  801. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  802. return return_data
  803. end
  804. if config.reg_count and reg_count ~= config.reg_count then
  805. log.error("exmodbus", "寄存器数量不匹配,期望:", config.reg_count, "实际:", reg_count)
  806. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  807. return return_data
  808. end
  809. -- 在返回数据中记录操作成功的起始地址和数量;
  810. parsed_data.start_addr = start_addr
  811. parsed_data.count = reg_count
  812. -- 未知功能码;
  813. else
  814. log.error("exmodbus", "不支持的功能码解析:", function_code)
  815. return_data.status = exmodbus_ref.STATUS_DATA_INVALID
  816. return return_data
  817. end
  818. -- 成功解析响应数据;
  819. -- log.info("exmodbus", "响应解析成功,功能码:", function_code, "数据:", json.encode(parsed_data))
  820. return_data.status = exmodbus_ref.STATUS_SUCCESS
  821. return_data.data = parsed_data
  822. return return_data
  823. end
  824. -- 主站读取请求函数;(内部使用)
  825. function modbus:read_internal(config)
  826. -- 处理响应结果;
  827. local parsed_data = {}
  828. -- 检查通信模式是否有效;
  829. if self.mode == exmodbus_ref.RTU_MASTER then
  830. -- 检查是否同时指定了 slave_id 和 raw_request;
  831. if config.slave_id and config.raw_request then
  832. log.error("exmodbus", "禁止同时指定 slave_id 和 raw_request")
  833. return false
  834. end
  835. -- 用户传入字段式请求帧;
  836. if config.slave_id then
  837. -- 构建 Modbus RTU 帧;
  838. local request_frame, function_code = build_rtu_frame("read", config)
  839. if not request_frame then
  840. parsed_data.status = exmodbus_ref.STATUS_DATA_INVALID
  841. return parsed_data
  842. end
  843. -- 发送请求并等待响应;
  844. local result, response = sendRequest_waitResponse(self, request_frame, config)
  845. if not result then
  846. parsed_data.status = exmodbus_ref.STATUS_TIMEOUT
  847. else
  848. -- 解析响应数据;
  849. parsed_data = parse_rtu_response(response, config, function_code)
  850. end
  851. -- 用户传入原始请求帧;
  852. elseif config.raw_request then
  853. -- 发送请求并等待响应;
  854. local result, response = sendRequest_waitResponse(self, config.raw_request, config)
  855. if not result then
  856. parsed_data.status = exmodbus_ref.STATUS_TIMEOUT
  857. else
  858. -- 直接返回响应结果和原始响应数据;
  859. parsed_data.status = exmodbus_ref.STATUS_SUCCESS
  860. parsed_data.raw_response = response
  861. end
  862. end
  863. return parsed_data
  864. else
  865. log.error("exmodbus", "通信模式不支持")
  866. return false
  867. end
  868. end
  869. -- 主站写入请求的函数;
  870. function modbus:write_internal(config)
  871. -- 处理响应结果;
  872. local parsed_data = {}
  873. -- 检查通信模式是否有效;
  874. if self.mode == exmodbus_ref.RTU_MASTER then
  875. -- 检查是否同时指定了 slave_id 和 raw_request;
  876. if config.slave_id and config.raw_request then
  877. log.error("exmodbus", "禁止同时指定 slave_id 和 raw_request")
  878. return false
  879. end
  880. -- 用户传入字段式请求帧;
  881. if config.slave_id then
  882. -- 构建 Modbus RTU 帧;
  883. local request_frame, function_code = build_rtu_frame("write", config)
  884. if not request_frame then
  885. parsed_data.status = exmodbus_ref.STATUS_DATA_INVALID
  886. return parsed_data
  887. end
  888. -- 发送请求并等待响应;
  889. local result, response = sendRequest_waitResponse(self, request_frame, config)
  890. if not result then
  891. -- log.error("exmodbus", "接收响应失败或超时")
  892. parsed_data.status = exmodbus_ref.STATUS_TIMEOUT
  893. else
  894. -- 解析响应数据;
  895. parsed_data = parse_rtu_response(response, config, function_code)
  896. end
  897. -- 用户传入原始请求帧;
  898. elseif config.raw_request then
  899. -- 发送请求并等待响应;
  900. local result, response = sendRequest_waitResponse(self, config.raw_request, config)
  901. if not result then
  902. parsed_data.status = exmodbus_ref.STATUS_TIMEOUT
  903. else
  904. -- 直接返回响应结果和原始响应数据;
  905. parsed_data.status = exmodbus_ref.STATUS_SUCCESS
  906. parsed_data.raw_response = response
  907. end
  908. end
  909. return parsed_data
  910. else
  911. log.error("exmodbus", "通信模式不支持")
  912. return false
  913. end
  914. end
  915. -- 主站读取请求的函数;
  916. function modbus:read(config)
  917. return exmodbus_ref.enqueue_request(self, config, true)
  918. end
  919. -- 主站写入请求的函数;
  920. function modbus:write(config)
  921. return exmodbus_ref.enqueue_request(self, config, false)
  922. end
  923. -- 注册从站请求处理回调函数;
  924. function modbus:on(callback)
  925. if type(callback) ~= "function" then
  926. log.error("exmodbus", "on(callback) 的参数必须是一个函数")
  927. return false
  928. end
  929. self.slaveHandler = callback
  930. log.info("exmodbus", "已注册从站请求处理回调函数")
  931. return true
  932. end
  933. return { create = create }