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