• Modbus 应用示例

    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