Skip to content

Commit 7e6c7fe

Browse files
zerox1212oroulet
authored andcommitted
Add ua to python mirroring example (#434)
* Add ua to python mirroring example * Change publish to write
1 parent 167cde3 commit 7e6c7fe

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import sys
2+
sys.path.insert(0, "..")
3+
import time
4+
5+
6+
from opcua import ua, Server
7+
8+
# INFO: The concept in this example is that the software model is first built in OPC UA via XML. After that, matching
9+
# python objects are created based on the UA address space design. Do not use this example to build a UA address space
10+
# from the python design.
11+
12+
# The advantage of this is that the software can be designed in a tool like UA Modeler. Then with minimal setup, a
13+
# python program will import the XML and mirror all the objects in the software design. After this mirroring is achieved
14+
# the user can focus on programming in python knowing that all all data from UA clients will reach the python object,
15+
# and all data that needs to be output to the server can be published from the python object.
16+
#
17+
# Be aware that subscription calls are asynchronous.
18+
19+
20+
class SubHandler(object):
21+
"""
22+
Subscription Handler. To receive events from server for a subscription.
23+
The handler forwards updates to it's referenced python object
24+
"""
25+
26+
def __init__(self, obj):
27+
self.obj = obj
28+
29+
def datachange_notification(self, node, val, data):
30+
# print("Python: New data change event", node, val, data)
31+
32+
_node_name = node.get_browse_name()
33+
setattr(self.obj, _node_name.Name, data.monitored_item.Value.Value.Value)
34+
35+
36+
class UaObject(object):
37+
"""
38+
Python object which mirrors an OPC UA object
39+
Child UA variables/properties are auto subscribed to to synchronize python with UA server
40+
Python can write to children via write method, which will trigger an update for UA clients
41+
"""
42+
def __init__(self, opcua_server, ua_node):
43+
self.opcua_server = opcua_server
44+
self.nodes = {}
45+
self.b_name = ua_node.get_browse_name().Name
46+
47+
# keep track of the children of this object (in case python needs to write, or get more info from UA server)
48+
for _child in ua_node.get_children():
49+
_child_name = _child.get_browse_name()
50+
self.nodes[_child_name.Name] = _child
51+
52+
# find all children which can be subscribed to (python object is kept up to date via subscription)
53+
sub_children = ua_node.get_properties()
54+
sub_children.extend(ua_node.get_variables())
55+
56+
# subscribe to properties/variables
57+
handler = SubHandler(self)
58+
sub = opcua_server.create_subscription(500, handler)
59+
handle = sub.subscribe_data_change(sub_children)
60+
61+
def write(self, attr=None):
62+
# if a specific attr isn't passed to write, write all OPC UA children
63+
if attr is None:
64+
for k, node in self.nodes.items():
65+
node_class = node.get_node_class()
66+
if node_class == ua.NodeClass.Variable:
67+
node.set_value(getattr(self, k))
68+
# only update a specific attr
69+
else:
70+
self.nodes[attr].set_value(getattr(self, attr))
71+
72+
73+
class MyObj(UaObject):
74+
"""
75+
Definition of OPC UA object which represents a object to be mirrored in python
76+
This class mirrors it's UA counterpart and semi-configures itself according to the UA model (generally from XML)
77+
"""
78+
def __init__(self, opcua_server, ua_node):
79+
80+
# properties and variables; must mirror UA model (based on browsename!)
81+
self.MyVariable = 0
82+
self.MyProperty = 0
83+
self.MyClientWrite = 0
84+
85+
# init the UaObject super class to connect the python object to the UA object
86+
super().__init__(opcua_server, ua_node)
87+
88+
# local values only for use inside python
89+
self.testval = 'python only'
90+
91+
# If the object has other objects as children it is best to search by type and instantiate more
92+
# mirrored python classes so that your UA object tree matches your python object tree
93+
94+
# ADD CUSTOM OBJECT INITIALIZATION BELOW
95+
# find children by type and instantiate them as sub-objects of this class
96+
# NOT PART OF THIS EXAMPLE
97+
98+
99+
if __name__ == "__main__":
100+
101+
# setup our server
102+
server = Server()
103+
server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
104+
105+
# setup our own namespace, not really necessary but should as spec
106+
uri = "http://examples.freeopcua.github.io"
107+
idx = server.register_namespace(uri)
108+
109+
# get Objects node, this is where we should put our nodes
110+
objects = server.get_objects_node()
111+
112+
# populating our address space; in most real use cases this should be imported from UA spec XML
113+
myobj = objects.add_object(idx, "MyObject")
114+
myvar = myobj.add_variable(idx, "MyVariable", 0.0)
115+
myprop = myobj.add_property(idx, "MyProperty", 0)
116+
mywritevar = myobj.add_variable(idx, "MyClientWrite", 0)
117+
mywritevar.set_writable() # Set MyVariable to be writable by clients
118+
119+
# starting!
120+
server.start()
121+
122+
# after the UA server is started initialize the mirrored object
123+
my_python_obj = MyObj(server, myobj)
124+
125+
try:
126+
while True:
127+
# from an OPC UA client write a value to this node to see it show up in the python object
128+
print('Python mirror of MyClientWrite is: ' + str(my_python_obj.MyClientWrite))
129+
130+
# write a single attr to OPC UA
131+
my_python_obj.MyVariable = 12.3
132+
my_python_obj.MyProperty = 55 # this value will not be visible to clients because write is not called
133+
my_python_obj.write('MyVariable')
134+
135+
time.sleep(3)
136+
137+
# write all attr of the object to OPC UA
138+
my_python_obj.MyVariable = 98.1
139+
my_python_obj.MyProperty = 99
140+
my_python_obj.write()
141+
142+
time.sleep(3)
143+
144+
# write directly to the OPC UA node of the object
145+
dv = ua.DataValue(ua.Variant(5.5, ua.VariantType.Double))
146+
my_python_obj.nodes['MyVariable'].set_value(dv)
147+
dv = ua.DataValue(ua.Variant(4, ua.VariantType.UInt64))
148+
my_python_obj.nodes['MyVariable'].set_value(dv)
149+
150+
time.sleep(3)
151+
152+
finally:
153+
# close connection, remove subscriptions, etc
154+
server.stop()

0 commit comments

Comments
 (0)