Modbus 应用示例

  • 此应用使用了一个Lua实现的modbus模块(modbus_lua)
  • 此应用使用SocketChannel和SerialChannel作为链路层
  • 更为详细的modbus数据采集可以参考: modbus_example

代码:

  1. local class = require 'middleclass'
  2. --- 导入需要的模块
  3. local modbus = require 'modbus.init'
  4. local sm_client = require 'modbus.skynet_client'
  5. local socketchannel = require 'socketchannel'
  6. local serialchannel = require 'serialchannel'
  7. --- 注册对象(请尽量使用唯一的标识字符串)
  8. local app = class("XXXX_App")
  9. --- 设定应用最小运行接口版本(目前版本为1,为了以后的接口兼容性)
  10. app.API_VER = 1
  11. ---
  12. -- 应用对象初始化函数
  13. -- @param name: 应用本地安装名称。 modbus_com_1
  14. -- @param sys: 系统sys接口对象。参考API文档中的sys接口说明
  15. -- @param conf: 应用配置参数。由安装配置中的json数据转换出来的数据对象
  16. function app:initialize(name, sys, conf)
  17. self._name = name
  18. self._sys = sys
  19. self._conf = conf
  20. --- 获取数据接口
  21. self._api = sys:data_api()
  22. --- 获取日志接口
  23. self._log = sys:logger()
  24. self._log:debug(name.." Application initlized")
  25. end
  26. --- 应用启动函数
  27. function app:start()
  28. --- 设定回调处理函数(目前此应用只做数据采集)
  29. self._api:set_handler({
  30. on_ctrl = function(...)
  31. print(...)
  32. end,
  33. })
  34. ---获取设备序列号和应用配置
  35. local sys_id = self._sys:id()
  36. local config = self._conf or {
  37. channel_type = 'socket'
  38. }
  39. --- 添加10个采集项
  40. local inputs = {}
  41. for i = 1, 10 do
  42. inputs[#inputs + 1] = {
  43. name='tag'..i,
  44. desc='tag'..i..' description',
  45. }
  46. end
  47. --- 生成设备的序列号
  48. local dev_sn = sys_id..".modbus_"..self._name
  49. local meta = self._api:default_meta()
  50. meta.name = "Modbus"
  51. meta.description = "Modbus Device"
  52. meta.series = "XXX"
  53. --- 生成设备对象
  54. local dev = self._api:add_device(dev_sn, meta, inputs)
  55. --- 生成设备通讯口统计对象
  56. local stat = dev:stat('port')
  57. local client = nil
  58. --- 获取配置
  59. if config.channel_type == 'socket' then
  60. opt = {
  61. host = "127.0.0.1",
  62. port = 1503,
  63. nodelay = true,
  64. }
  65. print('socket')
  66. client = sm_client(socketchannel, opt, modbus.apdu_tcp, 1)
  67. else
  68. opt = {
  69. port = "/dev/ttymxc1",
  70. baudrate = 115200
  71. }
  72. print('serial')
  73. client = sm_client(serialchannel, opt, modbus.apdu_rtu, 1)
  74. end
  75. --- 设定通讯口数据回调
  76. client:set_io_cb(function(io, msg)
  77. --- 输出通讯报文
  78. dev:dump_comm(io, msg)
  79. --- 计算统计信息
  80. if io == 'IN' then
  81. stat:inc('bytes_in', string.len(msg))
  82. else
  83. stat:inc('bytes_out', string.len(msg))
  84. end
  85. end)
  86. self._client1 = client
  87. self._dev1 = dev
  88. self._stat1 = stat
  89. return true
  90. end
  91. --- 应用退出函数
  92. function app:close(reason)
  93. print(self._name, reason)
  94. end
  95. --- modbus寄存器数据解析
  96. function decode_registers(raw, count)
  97. local d = modbus.decode
  98. local len = d.uint8(raw, 2)
  99. assert(len >= count * 2)
  100. local regs = {}
  101. --- 按照无符号短整数进行解析
  102. for i = 0, count - 1 do
  103. regs[#regs + 1] = d.uint16(raw, i * 2 + 3)
  104. end
  105. return regs
  106. end
  107. --- 应用运行入口
  108. function app:run(tms)
  109. local client = self._client1
  110. if not client then
  111. return
  112. end
  113. --- 设定读取的起始地址和读取的长度
  114. local base_address = 0x00
  115. local req = {
  116. func = 0x03, -- 03指令
  117. addr = base_address, -- 起始地址
  118. len = 10, -- 长度
  119. }
  120. --- 读取数据
  121. local r, pdu, err = pcall(function(req, timeout)
  122. --- 统计数据
  123. self._stat1:inc('packets_out', 1)
  124. --- 接口调用
  125. return client:request(req, timeout)
  126. end, req, 1000)
  127. if not r then
  128. pdu = tostring(pdu)
  129. if string.find(pdu, 'timeout') then
  130. self._log:debug(pdu, err)
  131. else
  132. self._log:warning(pdu, err)
  133. end
  134. return
  135. end
  136. if not pdu then
  137. self._log:warning("read failed: " .. err)
  138. return
  139. end
  140. --- 统计数据
  141. self._log:trace("read input registers done!")
  142. self._stat1:inc('packets_in', 1)
  143. --- 解析数据
  144. local regs = decode_registers(pdu, 10)
  145. local now = self._sys:time()
  146. --- 将解析好的数据设定到输入项
  147. for r,v in ipairs(regs) do
  148. self._dev1:set_input_prop('tag'..r, "value", math.tointeger(v), now, 0)
  149. end
  150. --- 返回下一次调用run之前的时间间隔
  151. return tms
  152. end
  153. --- 返回应用对象
  154. return app