Skip to content

Commit e92abd5

Browse files
committed
finish client implementation for custom structures
1 parent 3d927e9 commit e92abd5

File tree

6 files changed

+279
-240
lines changed

6 files changed

+279
-240
lines changed

opcua/client/client.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -534,21 +534,38 @@ def register_namespace(self, uri):
534534
ns_node.set_value(uries)
535535
return len(uries) - 1
536536

537-
def import_structures(self, nodes=None):
537+
def import_and_register_structures(self, nodes=None):
538538
"""
539539
Download xml from given variable node defining custom structures.
540-
If no node is given, attemps to import variables from
540+
If no no node is given, attemps to import variables from all nodes under
541+
"0:OPC Binary"
542+
the code is generated and imported on the fly. If you know the structures
543+
are not going to be modified it is safer to copy the generated files
544+
and include them in you code
541545
"""
542-
if not nodes:
546+
if nodes is None:
543547
nodes = []
544-
opc_bin = self.nodes.base_data_type.get_child("0:OPC Binary")
545-
for desc in opc_bin.get_children_descriptions():
546-
if desc.BrowseName != ua.QualifiedName("opc.Ua"):
548+
for desc in self.nodes.opc_binary.get_children_descriptions():
549+
if desc.BrowseName != ua.QualifiedName("Opc.Ua"):
547550
nodes.append(self.get_node(desc.NodeId))
551+
self.logger.info("Importing structures from nodes: %s", nodes)
548552

549553
for node in nodes:
550554
xml = node.get_value()
551-
gen = StructGenerator(xml, name)
555+
xml = xml.decode("utf-8")
556+
#with open("titi.xml", "w") as f:
557+
#f.write(xml)
558+
name = "structures_" + node.get_browse_name().Name
559+
gen = StructGenerator()
560+
gen.make_model_from_string(xml)
561+
structs_dict = gen.save_and_import(name + ".py")
562+
# register classes
563+
for desc in node.get_children_descriptions():
564+
if desc.BrowseName.Name in structs_dict:
565+
self.logger.info("registring new structure: %: %s", desc.NodeId, desc.BrowseName.Name)
566+
ua.extension_object_classes[desc.NodeId] = structs_dict[desc.BrowseName.Name]
567+
ua.extension_object_ids[desc.BrowseName.Name] = desc.NodeId
568+
552569

553570

554571

opcua/common/shortcuts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ def __init__(self, server):
2424
self.variable_types = Node(server, ObjectIds.VariableTypesFolder)
2525
self.object_types = Node(server, ObjectIds.ObjectTypesFolder)
2626
self.namespace_array = Node(server, ObjectIds.Server_NamespaceArray)
27+
self.opc_binary = Node(server, ObjectIds.OPCBinarySchema_TypeSystem)

opcua/common/structures_generator.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
for custom structures
55
"""
66

7+
import os
8+
import importlib
9+
710
from lxml import objectify
811

912

@@ -123,15 +126,19 @@ def __init__(self, name):
123126

124127

125128
class StructGenerator(object):
126-
def __init__(self, path, output):
127-
self.path = path
128-
self.output = output
129+
def __init__(self):
129130
self.model = []
130-
self._file = None
131131

132-
def _make_model(self):
133-
obj = objectify.parse(self.path)
132+
def make_model_from_string(self, xml):
133+
obj = objectify.fromstring(xml)
134+
self._make_model(obj)
135+
136+
def make_model_from_file(self, path):
137+
obj = objectify.parse(path)
134138
root = obj.getroot()
139+
self._make_model(root)
140+
141+
def _make_model(self, root):
135142
for child in root.iter("{*}StructuredType"):
136143
struct = Struct(child.get("Name"))
137144
array = False
@@ -152,23 +159,29 @@ def _make_model(self):
152159
struct.fields.append(field)
153160
self.model.append(struct)
154161

155-
def run(self):
156-
self._make_model()
157-
self._file = open(self.output, "wt")
158-
self._make_header()
162+
def save_to_file(self, path):
163+
_file = open(path, "wt")
164+
self._make_header(_file)
159165
for struct in self.model:
160-
self._file.write(struct.get_code())
161-
self._file.close()
166+
_file.write(struct.get_code())
167+
_file.close()
168+
169+
def save_and_import(self, path):
170+
self.save_to_file(path)
171+
name = os.path.basename(path)
172+
name = os.path.splitext(name)[0]
173+
mymodule = importlib.import_module(name)
174+
mydict = {struct.name: getattr(mymodule, struct.name) for struct in self.model}
175+
return mydict
162176

163177
def get_structures(self):
164-
self._make_model()
165178
ld = {}
166179
for struct in self.model:
167180
exec(struct.get_code(), ld)
168181
return ld
169182

170-
def _make_header(self):
171-
self._file.write("""
183+
def _make_header(self, _file):
184+
_file.write("""
172185
'''
173186
THIS FILE IS AUTOGENERATED, DO NOT EDIT!!!
174187
'''

0 commit comments

Comments
 (0)