Commit 9c60ba32 authored by Marcel's avatar Marcel Committed by ORD

support multiple depths of inheritance for instantiate (#271)

* support multiple depths of inheritance for instantiate

* move common methods to common/ua_utils.py

* add method is_child_present to ua_utils.py

* cleanup empty newlines

* Improve implementation of is_child_present

* [is_child_present] avoid break
parent 9d00d60c
......@@ -3,6 +3,7 @@ import copy
from opcua import ua
import opcua
from opcua.common.uaerrors import UaError
from opcua.common import ua_utils
class Event(object):
......@@ -148,7 +149,7 @@ def where_clause_from_evtype(evtypes):
# now create a list of all subtypes we want to accept
subtypes = []
for evtype in evtypes:
subtypes += [st.nodeid for st in get_node_subtypes(evtype)]
subtypes += [st.nodeid for st in ua_utils.get_node_subtypes(evtype)]
subtypes = list(set(subtypes)) # remove duplicates
for subtypeid in subtypes:
op = ua.LiteralOperand()
......@@ -161,15 +162,6 @@ def where_clause_from_evtype(evtypes):
return cf
def get_node_subtypes(node, nodes=None):
if nodes is None:
nodes = [node]
for child in node.get_children(refs=ua.ObjectIds.HasSubtype):
nodes.append(child)
get_node_subtypes(child, nodes)
return nodes
def get_event_properties_from_type_node(node):
properties = []
curr_node = node
......
......@@ -5,6 +5,7 @@ Instantiate a new node and its child nodes from a node type.
from opcua import Node
from opcua import ua
from opcua.common import ua_utils
def instantiate(parent, node_type, nodeid=None, bname=None, idx=0):
......@@ -33,7 +34,7 @@ def instantiate(parent, node_type, nodeid=None, bname=None, idx=0):
if bname is None:
bname = rdesc.BrowseName
elif isinstance(bname, str):
bname = ua.QualifiedName.from_string(bname)
bname = ua.QualifiedName.from_string(bname)
return _instantiate_node(parent.server, parent.nodeid, rdesc, nodeid, bname)
......@@ -42,7 +43,7 @@ def _instantiate_node(server, parentid, rdesc, nodeid, bname, recursive=True):
"""
instantiate a node type under parent
"""
addnode = ua.AddNodesItem()
addnode.RequestedNewNodeId = nodeid
addnode.BrowseName = bname
......@@ -51,32 +52,46 @@ def _instantiate_node(server, parentid, rdesc, nodeid, bname, recursive=True):
addnode.TypeDefinition = rdesc.TypeDefinition
node_type = Node(server, rdesc.NodeId)
if rdesc.NodeClass in (ua.NodeClass.Object, ua.NodeClass.ObjectType):
addnode.NodeClass = ua.NodeClass.Object
_read_and_copy_attrs(node_type, ua.ObjectAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Variable, ua.NodeClass.VariableType):
addnode.NodeClass = ua.NodeClass.Variable
_read_and_copy_attrs(node_type, ua.VariableAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Method,):
addnode.NodeClass = ua.NodeClass.Method
_read_and_copy_attrs(node_type, ua.MethodAttributes(), addnode)
refs = node_type.get_referenced_nodes(refs=ua.ObjectIds.HasModellingRule)
# skip optional elements
if not(len(refs) == 1 and refs[0].nodeid == ua.NodeId(ua.ObjectIds.ModellingRule_Optional) ):
if rdesc.NodeClass in (ua.NodeClass.Object, ua.NodeClass.ObjectType):
addnode.NodeClass = ua.NodeClass.Object
_read_and_copy_attrs(node_type, ua.ObjectAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Variable, ua.NodeClass.VariableType):
addnode.NodeClass = ua.NodeClass.Variable
_read_and_copy_attrs(node_type, ua.VariableAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Method,):
addnode.NodeClass = ua.NodeClass.Method
_read_and_copy_attrs(node_type, ua.MethodAttributes(), addnode)
else:
print("Instantiate: Node class not supported: ", rdesc.NodeClass)
return
res = server.add_nodes([addnode])[0]
if recursive:
parents = ua_utils.get_node_supertypes(node_type, includeitself = True)
node = Node(server, res.AddedNodeId)
for parent in parents:
descs = parent.get_children_descriptions(includesubtypes=False)
for c_rdesc in descs:
# skip items that already exists, prefer the 'lowest' one in object hierarchy
if not ua_utils.is_child_present(node, c_rdesc.BrowseName):
_instantiate_node(server, res.AddedNodeId, c_rdesc, nodeid=ua.NodeId(namespaceidx=res.AddedNodeId.NamespaceIndex), bname=c_rdesc.BrowseName)
return Node(server, res.AddedNodeId)
else:
print("Instantiate: Node class not supported: ", rdesc.NodeClass)
return
res = server.add_nodes([addnode])[0]
if recursive:
descs = node_type.get_children_descriptions(includesubtypes=False)
for c_rdesc in descs:
_instantiate_node(server, res.AddedNodeId, c_rdesc, nodeid=ua.NodeId(namespaceidx=res.AddedNodeId.NamespaceIndex), bname=c_rdesc.BrowseName)
return Node(server, res.AddedNodeId)
return None
def _read_and_copy_attrs(node_type, struct, addnode):
names = [name for name in struct.__dict__.keys() if not name.startswith("_") and name not in ("BodyLength", "TypeId", "SpecifiedAttributes", "Encoding", "IsAbstract", "EventNotifier")]
attrs = [getattr(ua.AttributeIds, name) for name in names]
attrs = [getattr(ua.AttributeIds, name) for name in names]
for name in names:
results = node_type.get_attributes(attrs)
for idx, name in enumerate(names):
......@@ -86,5 +101,5 @@ def _read_and_copy_attrs(node_type, struct, addnode):
else:
setattr(struct, name, results[idx].Value.Value)
else:
print("Instantiate: while copying attributes from node type %s, attribute %s, statuscode is %s" % (node_type, name, results[idx].StatusCode))
print("Instantiate: while copying attributes from node type %s, attribute %s, statuscode is %s" % (node_type, name, results[idx].StatusCode))
addnode.NodeAttributes = struct
......@@ -7,6 +7,7 @@ from datetime import datetime
from enum import Enum, IntEnum
from opcua import ua
from opcua.common.uaerrors import UaError
def val_to_string(val):
......@@ -103,3 +104,57 @@ def string_to_variant(string, vtype):
return ua.Variant(string_to_val(string, vtype), vtype)
def get_node_subtypes(node, nodes=None):
if nodes is None:
nodes = [node]
for child in node.get_children(refs=ua.ObjectIds.HasSubtype):
nodes.append(child)
get_node_subtypes(child, nodes)
return nodes
def get_node_supertypes(node, includeitself = False, skipbase = True):
"""
return get all subtype parents of node recursive
:param server: used in case node is nodeid
:param node: can be a ua.Node or ua.NodeId
:param includeitself: include also node to the list
:param skipbase don't include the toplevel one
:returns list of ua.Node, top parent first
"""
parents =[]
if includeitself:
parents.append(node)
parents.extend(_get_node_supertypes(node))
if skipbase and len(parents) > 1:
parents = parents [:-1]
return parents
def _get_node_supertypes(node):
"""
recursive implementation of get_node_derived_from_types
"""
basetypes = []
parents = node.get_referenced_nodes(refs=ua.ObjectIds.HasSubtype, direction=ua.BrowseDirection.Inverse, includesubtypes=True)
if len(parents) != 0:
#TODO: Is it possible to have multiple subtypes ? If so extended support for it
basetypes.append(parents[0])
basetypes.extend( _get_node_supertypes(parents[0]) )
return basetypes
def is_child_present(node, browsename):
"""
return if a browsename is present a child from the provide node
:param node: node wherein to find the browsename
:param browsename: browsename to search
:returns returne True if the browsename is present else False
"""
child_descs = node.get_children_descriptions()
for child_desc in child_descs:
if child_desc.BrowseName == browsename:
return True
return False
\ No newline at end of file
......@@ -559,6 +559,17 @@ class CommonTests(object):
self.assertEqual(prop.get_type_definition().Identifier, ua.ObjectIds.PropertyType)
self.assertEqual(prop.get_value(), "Running")
self.assertNotEqual(prop.nodeid, prop_t.nodeid)
# also test if all of all of parent type is instantiated
devd_t = dev_t.add_object_type(0, "MyDeviceDervived")
v_t = devd_t.add_variable(0, "childparam", 1.0)
p_t = devd_t.add_property(0, "sensorx_id", "0340")
mydevicederived = instantiate(self.opc.nodes.objects, devd_t, bname="2:Device0002")
prop1 = mydevicederived.get_child(["0:sensorx_id"])
var1 = mydevicederived.get_child(["0:childparam"])
var_parent = mydevicederived.get_child(["0:sensor"])
prop_parent = mydevicederived.get_child(["0:sensor_id"])
def test_variable_with_datatype(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment