任何一项新标准如果不能充分应用是不可能推广的,最近看了一些国外网站,发现类似OPC UA 的应用以及比较广泛了,而且有许多课程。相比之下,我国OPCUA 标准的普及工作仍然停留在概述的阶段,为此,我将逐步介绍一些编写OPC UA 应用程序实例,同时也介绍一些OPCUA 的应用场合。为了避免程序设计的细节,我们采用Python Opcua。
Python已经成为非常流行的程序设计语言,它在网络编程,数值运算和人工智能领域应用广泛。并且在这些领域的应用程序也非常多,使人们能够很块地进入人工智能,数值运算,图像处理等领域的程序设计。而OPCUA 是一个正在普及的工业自动化协议和信息模型系统,opcua 是进入OT领域的接口。
在工业软件中,使用Python并不多,人们更倾向C#,C++和Matlab、labview 等程序设计语言编程。不过,Python 在以数值技术为中心的应用中具备优势,例如,复杂的控制算法,视觉识别,数据分析,工业AI应用中使用python 更加适合,同时,如果你有一些想法,希望快速成型,验证。使用python 无疑是高效率的。
OPC UA 是一个在工业领域应用越来越广泛的通信协议。它主要采取服务器/客户端通信方式,也支持发布/订阅方式。OPC UA 协议中包含了构建信息模型的方法,并且不断地制定行业规范。工业4.0 和MTP等主要的协议都是基于OPCUA的。因此,采用OPC UA 作为产品和服务的接口协议能够实现兼容性,目前各种主流的PLC 已经支持OPCUA。
使用Python 编程,OPC UA 协议作为IT 连接OT 领域的PLC ,传感器,执行器的接口。这种方式为IT 行业的最新技术导入OT 是十分有效的方式,例如视觉识别,AI,数据分析等技术发展速度惊人。通过Python-Opcua 组合在算法研究,快速原型设计和新产品开发验证中带来的效率。
下面是一个应用实例
在这个例子中,使用python+OpenCV 完成视觉识别。它们可以在一个小型的Linux 控制器上运行。PLC 和视觉控制器之间通过网线连接,并且使用OPC UA 交互,PLC 是服务器端,视觉控制器是Client 端。
Python 的OPCUA 模块是,python-opcua。使用它来编写OPCUA 的服务器和客户端程序要比C#的OPC Foundation .Net 容易的多。如果编写一些OPC UA 的小程序,正巧你有会使用python的话,强烈建议使用python。
所有编程语言的文档都是十分枯燥的,最好的方式是通过实例来学习。在这一系列博文中,我们从实例的方式来介绍Python opcua的编程技巧。
import sys
sys.path.insert(0, "..")
import time
from opcua import Server
if __name__ == "__main__":
# setup our server
server = Server()
server.set_endpoint("opc.tcp://127.0.0.1:48400/freeopcua/server/")
# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)
# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()
# populating our address space
myobj = objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
myvar.set_writable() # Set MyVariable to be writable by clients
# starting!
server.start()
try:
count = 0
while True:
time.sleep(1)
count += 0.1
myvar.set_value(count)
finally:
#close connection, remove subcsriptions, etc
server.stop()
import sys
sys.path.insert(0, "..")
from opcua import Client
if __name__ == "__main__":
client = Client("opc.tcp://localhost:48400/freeopcua/server/")
# client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
try:
client.connect()
# Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
root = client.get_root_node()
print("Objects node is: ", root)
# Node objects have methods to read and write node attributes as well as browse or populate address space
print("Children of root are: ", root.get_children())
# get a specific node knowing its node id
#var = client.get_node(ua.NodeId(1002, 2))
#var = client.get_node("ns=3;i=2002")
#print(var)
#var.get_data_value() # get value of node as a DataValue object
#var.get_value() # get value of node as a python builtin
#var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type
#var.set_value(3.9) # set node value using implicit data type
# Now getting a variable node using its browse path
myvar = root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"])
obj = root.get_child(["0:Objects", "2:MyObject"])
print("myvar is: ", myvar)
print("myobj is: ", obj)
# Stacked myvar access
# print("myvar is: ", root.get_children()[0].get_children()[1].get_variables()[0].get_value())
finally:
client.disconnect()
OPC UA 的信息模型采用标准化模板的xml 来表达,它被称为NodeSet。NodeSet是使用OPC UA 的建模工具来生成的,例如、luaModeler,SiOME等基于图像界面的建模工具,也可以使用UA ModelCompile 编译工具。python-opcua 的服务器能够导入外部生成的NodeSet2信息模型。
python-opcua server 通过 import_xml 导入NodeSet2 文档。
import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server
if __name__ == "__main__":
# setup our server
server = Server()
server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/")
server.import_xml("../schemas/UA-Nodeset/DI/Opc.Ua.Di.NodeSet2.xml")
server.import_xml("../schemas/UA-Nodeset/Robotics/Opc.Ua.Robotics.NodeSet2.xml")
# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)
# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()
# populating our address space
myobj = objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
myvar.set_writable() # Set MyVariable to be writable by clients
# starting!
server.start()
try:
count = 0
while True:
time.sleep(1)
count += 0.1
myvar.set_value(count)
finally:
#close connection, remove subcsriptions, etc
server.stop()
可以使用多个xml 文档来描述信息模型,比如最基本的命名模型的URI 是http://www.opcfoundation.org/UA。
在该基础上可以导入其它的命名空间,例如
http://www.opcfoundation.org/UA/DI
http://www.opcfoundation.org/PLCOpen
你也可以导入自定义的信息模型,URI 可以为
http://www.maxim.com/App
在Python-opcua 中,可以注册python 程序的命名空间,例如
uri = "http://www.maxim.org/app/"
idx = server.register_namespace(uri)
注意,这里注册的命名空间并不是你装入的命名空间,而是你Python 程序的命名空间,它返回一个idx。在程序中新建一个变量的话,可以使用该idx
在Python-opcua 中,命名空间是存放在namespaceArray中的,可以定义下面的子程序来读取namespaceArray
def get_namespace_array(self):
ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
return ns_node.get_value()
打印返回的namespace
['http://opcfoundation.org/UA/', 'urn:freeopcua:python:server', 'http://www.maxim.org/pac/', 'http://www.maxim.org/fbs/', 'http://www.maxim.org/app/']
从上可见,在python-opcua 中,增加了一个URI 为'urn:freeopcua:python:server' 的命名空间。
一个完整的代码
import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server
def get_namespace_array(self):
ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
return ns_node.get_value()
if __name__ == "__main__":
# setup our server
server = Server()
server.set_endpoint("opc.tcp://localhost:48400/freeopcua/server/")
server.application_uri = "urn:freeopcua:python:server"
nodes=server.import_xml("E:/yao2023/Model1/pac.NodeSet2.xml")
nodes=server.import_xml("E:/yao2023/Model1/OpcUaLib.NodeSet2.xml")
nodes=server.import_xml("E:/yao2023/Model1/app.NodeSet2.xml")
print(get_namespace_array(server))
uri = "http://www.maxim.org/app/"
idx = server.register_namespace(uri)
# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()
# populating our address space
myobj = objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
myvar.set_writable() # Set MyVariable to be writable by clients
# starting!
server.start()
try:
count = 0
while True:
time.sleep(1)
count += 0.1
myvar.set_value(count)
finally:
#close connection, remove subcsriptions, etc
server.stop()