• OpcUA 数据采集应用示例

    OpcUA 数据采集应用示例

    • 使用了opcua的Lua模块: open62541-lua
    • 读取Simulation节点下所有数据项

    代码:

    1. --- 导入需求的模块
    2. local class = require 'middleclass'
    3. local opcua = require 'opcua'
    4. --- 注册对象(请尽量使用唯一的标识字符串)
    5. local app = class("IOT_OPCUA_CLIENT_APP")
    6. --- 设定应用最小运行接口版本(目前版本为1,为了以后的接口兼容性)
    7. app.API_VER = 1
    8. ---
    9. -- 应用对象初始化函数
    10. -- @param name 应用本地安装名称。 modbus_com_1
    11. -- @param sys 系统sys接口对象。参考API文档中的sys接口说明
    12. -- @param conf 应用配置参数。由安装配置中的json数据转换出来的数据对象
    13. function app:initialize(name, sys, conf)
    14. self._name = name
    15. self._sys = sys
    16. self._conf = conf
    17. --- 获取数据接口
    18. self._api = sys:data_api()
    19. --- 获取日志接口
    20. self._log = sys:logger()
    21. self._connect_retry = 1000
    22. end
    23. ---
    24. -- 检测连接可用性
    25. function app:is_connected()
    26. if self._client then
    27. return true
    28. end
    29. end
    30. ---
    31. -- 获取设备的OpcUa节点
    32. function app:get_device_node(namespace, obj_name)
    33. if not self:is_connected() then
    34. self._log:warning("Client is not connected!")
    35. return
    36. end
    37. local client = self._client
    38. local nodes = self._nodes
    39. --- 获取Objects节点
    40. local objects = client:getObjectsNode()
    41. --- 获取名字空间的id
    42. local idx, err = client:getNamespaceIndex(namespace)
    43. if not idx then
    44. self._log:warning("Cannot find namespace", err)
    45. return
    46. end
    47. --- 获取设备节点
    48. local devobj, err = objects:getChild(idx..":"..obj_name)
    49. if not devobj then
    50. self._log:error('Device object not found', err)
    51. return
    52. else
    53. self._log:debug("Device object found", devobj)
    54. end
    55. --- 返回节点对象
    56. return {
    57. idx = idx,
    58. name = obj_name,
    59. device = device,
    60. devobj = devobj,
    61. vars = {}
    62. }
    63. end
    64. ---
    65. -- 定义需要获取数据的输入项
    66. local inputs = {
    67. { name = "Counter1", desc = "Counter1"},
    68. { name = "s1", desc = "Simulation 1"},
    69. }
    70. ---
    71. -- 连接成功后的处理函数
    72. function app:on_connected(client)
    73. -- Cleanup nodes buffer
    74. self._nodes = {}
    75. -- Set client object
    76. self._client = client
    77. --- Get opcua object instance by namespace and browse name
    78. -- 根据名字空间和节点名称获取OpcUa对象实体
    79. local namespace = self._conf.namespace or "http://www.prosysopc.com/OPCUA/SimulationNodes"
    80. local obj_name = "Simulation"
    81. local node, err = self:get_device_node(namespace, obj_name)
    82. ---
    83. -- 获取设备对象节点下的变量节点
    84. if node then
    85. for _,v in ipairs(inputs) do
    86. local var, err = node.devobj:getChild(v.name)
    87. --print(_,v.name,var)
    88. if not var then
    89. self._log:error('Variable not found', err)
    90. else
    91. node.vars[v.name] = var
    92. end
    93. end
    94. local sn = namespace..'/'..obj_name
    95. self._nodes[sn] = node
    96. end
    97. end
    98. ---
    99. -- 连接断开后的处理函数
    100. function app:on_disconnect()
    101. self._nodes = {}
    102. self._client = nil
    103. self._sys:timeout(self._connect_retry, function() self:connect_proc() end)
    104. self._connect_retry = self._connect_retry * 2
    105. if self._connect_retry > 2000 * 64 then
    106. self._connect_retry = 2000
    107. end
    108. end
    109. ---
    110. -- 连接处理函数
    111. function app:connect_proc()
    112. self._log:notice("OPC Client start connection!")
    113. local client = self._client_obj
    114. local ep = self._conf.endpoint or "opc.tcp://172.30.1.162:53530/OPCUA/SimulationServer"
    115. local username = self._conf.username or "user1"
    116. local password = self._conf.password or "password"
    117. --local r, err = client:connect_username(ep, username, password)
    118. local r, err = client:connect(ep)
    119. if r then
    120. self._log:notice("OPC Client connect successfully!", self._sys:time())
    121. self._connect_retry = 2000
    122. self:on_connected(client)
    123. else
    124. self._log:error("OPC Client connect failure!", err, self._sys:time())
    125. self:on_disconnect()
    126. end
    127. end
    128. --- 应用启动函数
    129. function app:start()
    130. self._nodes = {}
    131. self._devs = {}
    132. --- 设定OpcUa连接配置
    133. local config = opcua.ConnectionConfig.new()
    134. config.protocolVersion = 0 -- 协议版本
    135. config.sendBufferSize = 65535 -- 发送缓存大小
    136. config.recvBufferSize = 65535 -- 接受缓存大小
    137. config.maxMessageSize = 0 -- 消息大小限制
    138. config.maxChunkCount = 0 --
    139. --- 生成OpcUa客户端对象
    140. local client = opcua.Client.new(5000, 10 * 60 * 1000, config)
    141. self._client_obj = client
    142. --- 发起OpcUa连接
    143. self._sys:fork(function() self:connect_proc() end)
    144. --- 设定接口处理函数
    145. self._api:set_handler({
    146. on_output = function(...)
    147. print(...)
    148. end,
    149. on_ctrl = function(...)
    150. print(...)
    151. end
    152. })
    153. --- 创建设备对象实例
    154. local sys_id = self._sys:id()
    155. local meta = self._api:default_meta()
    156. meta.name = "Unknow OPCUA"
    157. meta.description = "Unknow OPCUA Device"
    158. meta.series = "X1"
    159. local dev = self._api:add_device(sys_id..'.OPCUA_TEST', meta, inputs)
    160. self._devs['Simulation'] = dev
    161. return true
    162. end
    163. --- 应用退出函数
    164. function app:close(reason)
    165. print('close', self._name, reason)
    166. --- 清理OpcUa客户端连接
    167. self._client = nil
    168. if self._client_obj then
    169. self._nodes = {}
    170. self._client_obj:disconnect()
    171. self._client_obj = nil
    172. end
    173. end
    174. --- 应用运行入口
    175. function app:run(tms)
    176. if not self._client then
    177. return 1000
    178. end
    179. --- 获取节点当前值数据
    180. for sn, node in pairs(self._nodes) do
    181. local dev = self._devs[node.name]
    182. assert(dev)
    183. for k, v in pairs(node.vars) do
    184. local dv = v:getValue()
    185. --[[
    186. print(dv, dv:isEmpty(), dv:isScalar())
    187. print(dv:asLong(), dv:asDouble(), dv:asString())
    188. ]]--
    189. local now = self._sys:time()
    190. --- 设定当前值
    191. dev:set_input_prop(k, "value", dv:asDouble(), now, 0)
    192. end
    193. end
    194. --- 返回下一次调用run函数的间隔
    195. return 2000
    196. end
    197. --- 返回应用对象
    198. return app