25 from ua_builtin_types
import *;
26 from open62541_MacroHelper
import open62541_MacroHelper
27 from ua_constants
import *
29 logger = logging.getLogger(__name__)
34 xmlvalue = xmlvalue.nextSibling
35 while not xmlvalue ==
None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
36 xmlvalue = xmlvalue.nextSibling
45 """ Representation of a pointer.
47 A pointer consists of a target (which should be a node class),
48 an optional reference type (which should be an instance of
49 opcua_node_referenceType_t) and an optional isForward flag.
51 __reference_type__ =
None
57 def __init__(self, target, hidden=False, parentNode=None):
66 if isinstance(data, bool):
71 if isinstance(data, bool):
103 tmp = src+
"_"+type+
"_"+tgt
107 if not i
in "ABCDEFGHIJKLMOPQRSTUVWXYZ0123456789".lower():
108 refid = refid + (
"_")
115 if isinstance(self.
parent(), opcua_node_t):
116 if isinstance(self.
parent().
id(), opcua_node_id_t):
119 retval=retval +
"(?) --["
121 retval=retval +
"(?) --["
126 retval=retval +
"?]-->"
128 if isinstance(self.
target(), opcua_node_t):
129 if isinstance(self.
target().
id(), opcua_node_id_t):
132 retval=retval +
"(?) "
134 retval=retval +
"(?) "
137 retval = retval +
" <"
139 retval = retval +
"F"
141 retval = retval +
"H"
142 retval = retval +
">"
149 if not isinstance(other, opcua_referencePointer_t):
151 if other.target() == self.
target():
153 if other.isForward() == self.
isForward():
163 """ Implementation of a node ID.
165 The ID will encoding itself appropriatly as string. If multiple ID's (numeric, string, guid)
166 are defined, the order of preference for the ID string is always numeric, guid,
167 bytestring, string. Binary encoding only applies to numeric values (UInt16).
177 idparts = idstring.split(
";")
192 self.
g = p[2:].split(
"-")
195 tmp.append(int(i,16))
213 tmp.append(hex(i).replace(
"0x",
""))
228 return (self.
toString() == nodeId2.toString())
240 __node_browseName__ =
""
241 __node_displayName__ =
""
242 __node_description__ =
""
243 __node_writeMask__ = 0
244 __node_userWriteMask__ = 0
245 __node_namespace__ =
None
246 __node_references__ = []
247 __node_referencedBy__ = []
275 if isinstance(self.
id(), opcua_node_id_t):
276 return self.__class__.__name__ +
"(" +
str(self.
id()) +
")"
277 return self.__class__.__name__ +
"( no ID )"
280 if isinstance(self.
id(), opcua_node_id_t):
281 return self.__class__.__name__ +
"(" +
str(self.
id()) +
")"
282 return self.__class__.__name__ +
"( no ID )"
286 CodePrintable=
"NODE_"
288 if isinstance(self.
id(), opcua_node_id_t):
289 CodePrintable = self.__class__.__name__ +
"_" +
str(self.
id())
291 CodePrintable = self.__class__.__name__ +
"_unknown_nid"
293 CodePrintable = CodePrintable.lower()
295 for i
in range(0,len(CodePrintable)):
296 if not CodePrintable[i]
in "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_".lower():
297 cleanPrintable = cleanPrintable +
"_"
299 cleanPrintable = cleanPrintable + CodePrintable[i]
300 return cleanPrintable
303 """ Add a opcua_referencePointer_t to the list of
304 references this node carries.
316 if ref.target() != targetNode:
321 """ Adds a reference to the inverse reference list of this node.
323 Inverse references are considered as "this node is referenced by"
324 and facilitate lookups when between nodes that reference this node,
325 but are not referenced by this node. These references would
326 require the namespace to be traversed by references to be found
327 if this node was not aware of them.
347 if node == r.target():
353 if node == r.target():
358 """ getFirstParentNode
360 return a tuple of (opcua_node_t, opcua_referencePointer_t) indicating
361 the first node found that references this node. If this node is not
362 referenced at all, None will be returned.
364 This function requires a linked namespace.
366 Note that there may be more than one nodes that reference this node.
367 The parent returned will be determined by the first isInverse()
368 Reference of this node found. If none exists, the first hidden
369 reference will be returned.
374 for hiddenstatus
in [
False,
True]:
376 if r.isHidden() == hiddenstatus
and r.isForward() ==
False:
378 for r
in parent.getReferences():
379 if r.target() == self:
383 return (parent, revref)
385 return (parent, revref)
388 """ Updates inverse references in all nodes referenced by this node.
390 The function will look up all referenced nodes and check if they
391 have a reference that points back at this node. If none is found,
392 that means that the target is not aware that this node references
393 it. In that case an inverse reference will be registered with
394 the target node to point back to this node instance.
398 if isinstance(r.target(), opcua_node_t):
412 """ Sets the node class attribute if c is passed.
413 Returns the current node class.
416 if isinstance(c, int):
422 """ Sets the browse name attribute if data is passed.
423 Returns the current browse name.
425 if isinstance(data, str):
427 if sys.version_info[0] < 3:
432 """ Sets the display name attribute if data is passed.
433 Returns the current display name.
440 """ Sets the description attribute if data is passed.
441 Returns the current description.
448 """ Sets the write mask attribute if data is passed.
449 Returns the current write mask.
456 """ Sets the user write mask attribute if data is passed.
457 Returns the current user write mask.
464 """ Initiates references found in the XML <References> element.
466 All references initiated will be registered with this node, but
467 their targets will be strings extracted from the XML description
470 References created will however be registered with the namespace
471 for linkLater(), which will eventually replace the string target
472 with an actual instance of an opcua_node_t.
474 if not xmlelement.tagName ==
"References":
475 logger.error(
"XMLElement passed is not a reference list")
477 for ref
in xmlelement.childNodes:
478 if ref.nodeType == ref.ELEMENT_NODE:
482 for (at, av)
in ref.attributes.items():
483 if at ==
"ReferenceType":
484 dummy.referenceType(av)
485 elif at ==
"IsForward":
486 if "false" in av.lower():
487 dummy.isForward(
False)
489 logger.error(
"Don't know how to process attribute " + at +
"(" + av +
") for references.")
492 cleanname =
"node_" +
str(self.
id()).replace(
";",
"").replace(
"=",
"")
493 dot = cleanname +
" [label = \"{" +
str(self.
id()) +
"|" +
str(self.
browseName()) +
"}\", shape=\"record\"]"
495 if isinstance(r.target(), opcua_node_t):
496 tgtname =
"node_" +
str(r.target().
id()).replace(
";",
"").replace(
"=",
"")
498 if r.isForward() ==
True:
499 dot = dot + cleanname +
" -> " + tgtname +
" [label=\"" +
str(r.referenceType().
browseName()) +
"\"]\n"
501 if len(r.referenceType().inverseName()) == 0:
502 logger.warn(
"Inverse name of reference is null " +
str(r.referenceType().
id()))
503 dot = dot + cleanname +
" -> " + tgtname +
" [label=\"" +
str(r.referenceType().inverseName()) +
"\"]\n"
507 """ Check the health of this node.
509 Return True if all mandatory attributes are valid and all references have been
510 correclty linked to nodes. Returns False on failure, which should indicate
511 that this node needs to be removed from the namespace.
514 if not isinstance(self.
id(), opcua_node_id_t):
515 logger.error(
"HELP! I'm an id'less node!")
521 if not isinstance(r, opcua_referencePointer_t):
522 logger.error(
"Reference is not a reference!?.")
523 elif not isinstance(r.referenceType(), opcua_node_t):
524 logger.error(
"Reference has no valid reference type and will be removed.")
525 elif not isinstance(r.target(), opcua_node_t):
526 logger.warn(
"Reference to " +
str(r.target()) +
" is not a node. It has been removed.")
534 if not isinstance(r.target(), opcua_node_t):
535 logger.warn(
"Invers reference to " +
str(r.target()) +
" does not reference a real node. It has been removed.")
540 logger.warn(
"Node " +
str(self.
id()) +
" was falsely under the impression that it is referenced by " +
str(r.target().
id()))
550 logger.debug(
"Removing unnecessary inverse reference to " +
str(r.target.id()))
559 """ If addr is passed, the address of this node within the binary
560 representation will be set.
562 If an address within the binary representation is known/set, this
563 function will return it. NoneType is returned if no address has been
567 self.__address__ = addr
568 return self.__address__
571 """ Extracts base attributes from the XML description of an element.
572 Parsed basetype attributes are:
580 ParentNodeIds are ignored.
582 If recognized, attributes and elements found will be removed from
583 the XML Element passed. Type-specific attributes and child elements
584 are handled by the parseXMLSubType() functions of all classes deriving
585 from this base type and will be called automatically.
588 for (at, av)
in thisxml.attributes.items():
590 xmlelement.removeAttribute(at)
591 elif at ==
"BrowseName":
593 xmlelement.removeAttribute(at)
594 elif at ==
"DisplayName":
596 xmlelement.removeAttribute(at)
597 elif at ==
"Description":
599 xmlelement.removeAttribute(at)
600 elif at ==
"WriteMask":
602 xmlelement.removeAttribute(at)
603 elif at ==
"UserWriteMask":
605 xmlelement.removeAttribute(at)
606 elif at ==
"EventNotifier":
607 self.eventNotifier(int(av))
608 xmlelement.removeAttribute(at)
609 elif at ==
"ParentNodeId":
611 xmlelement.removeAttribute(at)
613 for x
in thisxml.childNodes:
614 if x.nodeType == x.ELEMENT_NODE:
616 if x.tagName ==
"BrowseName":
618 xmlelement.removeChild(x)
619 elif x.tagName ==
"DisplayName":
621 xmlelement.removeChild(x)
622 elif x.tagName ==
"Description":
624 xmlelement.removeChild(x)
625 elif x.tagName ==
"WriteMask":
627 xmlelement.removeChild(x)
628 elif x.tagName ==
"UserWriteMask":
630 xmlelement.removeChild(x)
631 if x.tagName ==
"References":
633 xmlelement.removeChild(x)
643 """ printOpen62541CCode_SubtypeEarly
645 Initiate code segments for the nodes instantiotion that preceed
646 the actual UA_Server_addNode or UA_NodeStore_insert calls.
651 """ printOpen62541CCode_Subtype
653 Appends node type specific information to the nodes UA_Server_addNode
654 or UA_NodeStore_insert calls.
658 def printOpen62541CCode(self, unPrintedNodes=[], unPrintedReferences=[], supressGenerationOfAttribute=[]):
659 """ printOpen62541CCode
661 Returns a list of strings containing the C-code necessary to intialize
662 this node for the open62541 OPC-UA Stack.
664 Note that this function will fail if the nodeid is non-numeric, as
665 there is no UA_EXPANDEDNNODEID_[STRING|GUID|BYTESTRING] macro.
673 if not (self
in unPrintedNodes):
674 logger.warn(
str(self) +
" attempted to reprint already printed node " +
str(self)+
".")
680 if not (parentNode
in unPrintedNodes)
and (parentNode !=
None)
and (parentRef.referenceType() !=
None):
681 code.append(
"// Referencing node found and declared as parent: " +
str(parentNode .
id()) +
"/" +
682 str(parentNode .__node_browseName__) +
" using " +
str(parentRef.referenceType().
id()) +
683 "/" +
str(parentRef.referenceType().__node_browseName__))
684 code = code + codegen.getCreateNodeNoBootstrap(self, parentNode, parentRef, unPrintedNodes)
686 if parentRef
in unPrintedReferences:
687 unPrintedReferences.remove(parentRef)
691 if ref.target() == parentNode
and ref.referenceType() == parentRef.referenceType()
and ref.isForward() ==
False:
692 while ref
in unPrintedReferences:
693 unPrintedReferences.remove(ref)
697 code = code + codegen.getCreateNodeBootstrap(self)
699 code.append(
"// Parent node does not exist yet. This node will be bootstrapped and linked later.")
700 code.append(
"UA_RCU_LOCK();")
701 code.append(
"UA_NodeStore_insert(server->nodestore, (UA_Node*) " + self.
getCodePrintableID() +
");")
702 code.append(
"UA_RCU_UNLOCK();")
710 if not (r.target()
in unPrintedNodes):
711 if r
in unPrintedReferences:
712 if (len(tmprefs) == 0):
713 code.append(
"// This node has the following references that can be created:")
714 code = code + codegen.getCreateStandaloneReference(self, r)
718 unPrintedReferences.remove(r)
723 for r
in unPrintedReferences:
725 if (r.target() == self)
and not (r.parent()
in unPrintedNodes):
726 if not isinstance(r.parent(), opcua_node_t):
727 logger.debug(
"Reference has no parent!")
728 elif not isinstance(r.parent().
id(), opcua_node_id_t):
729 logger.debug(
"Parents nodeid is not a nodeID!")
731 if (len(tmprefs) == 0):
732 code.append(
"// Creating this node has resolved the following open references:")
733 code = code + codegen.getCreateStandaloneReference(r.parent(), r)
737 unPrintedReferences.remove(r)
740 if self
in unPrintedNodes:
742 unPrintedNodes.remove(self)
744 code.append(
"} while(0);")
748 __isAbstract__ =
False
749 __symmetric__ =
False
750 __reference_inverseName__ =
""
751 __reference_referenceType__ =
None
761 if isinstance(data, opcua_node_t):
766 if isinstance(data, bool):
771 if isinstance(data, bool):
776 if isinstance(data, str):
781 if not isinstance(self.
referenceType(), opcua_referencePointer_t):
782 logger.error(
"ReferenceType " +
str(self.
referenceType()) +
" of " +
str(self.
id()) +
" is not a pointer (ReferenceType is mandatory for references).")
788 for (at, av)
in xmlelement.attributes.items():
789 if at ==
"Symmetric":
790 if "false" in av.lower():
794 xmlelement.removeAttribute(at)
795 elif at ==
"InverseName":
797 xmlelement.removeAttribute(at)
798 elif at ==
"IsAbstract":
799 if "false" in str(av).lower():
803 xmlelement.removeAttribute(at)
805 logger.warn(
"Don't know how to process attribute " + at +
" (" + av +
")")
807 for x
in xmlelement.childNodes:
808 if x.nodeType == x.ELEMENT_NODE:
809 if x.tagName ==
"InverseName" and x.firstChild:
812 logger.warn(
"Unprocessable XML Element: " + x.tagName)
819 if bootstrapping ==
False:
823 if ref.referenceType()
in typeDefs:
828 if ref.referenceType().
browseName() ==
"HasSubtype" and ref.isForward() ==
False:
832 logger.warn(
str(self) +
" failed to locate a type definition, assuming BaseDataType.")
833 code.append(
" // No valid typeDefinition found; assuming BaseDataType")
834 code.append(
" UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
836 code.append(
" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) +
",")
837 while myTypeRef
in unPrintedReferences:
838 unPrintedReferences.remove(myTypeRef)
840 code.append(
" UA_LOCALIZEDTEXT(\"\",\"" +
str(self.
inverseName()) +
"\"),");
841 code.append(
" // FIXME: Missing, isAbstract")
842 code.append(
" // FIXME: Missing, symmetric")
855 __object_eventNotifier__ = 0
862 if isinstance(data, int):
867 for (at, av)
in xmlelement.attributes.items():
868 if at ==
"EventNotifier":
870 xmlelement.removeAttribute(at)
871 elif at ==
"SymbolicName":
873 xmlelement.removeAttribute(at)
875 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
877 for x
in xmlelement.childNodes:
878 if x.nodeType == x.ELEMENT_NODE:
879 logger.info(
"Unprocessable XML Element: " + x.tagName)
886 if bootstrapping ==
False:
890 if ref.referenceType()
in typeDefs:
895 if ref.referenceType().
browseName() ==
"HasSubtype" and ref.isForward() ==
False:
899 logger.warn(
str(self) +
" failed to locate a type definition, assuming BaseObjectType.")
900 code.append(
" // No valid typeDefinition found; assuming BaseObjectType")
901 code.append(
" UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
903 code.append(
" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) +
",")
904 while myTypeRef
in unPrintedReferences:
905 unPrintedReferences.remove(myTypeRef)
914 if sys.version_info[0] >= 3:
923 __arrayDimensions__ = 0
925 __userAccessLevel__ = 0
926 __minimumSamplingInterval__ = 0
927 __historizing__ =
False
942 if isinstance(data, opcua_value_t):
952 if isinstance(data, int):
982 if not isinstance(self.
dataType(), opcua_referencePointer_t):
983 logger.error(
"DataType " +
str(self.
dataType()) +
" of " +
str(self.
id()) +
" is not a pointer (DataType is mandatory for variables).")
987 if not isinstance(self.
dataType().target(), opcua_node_t):
988 logger.error(
"DataType " +
str(self.
dataType().target()) +
" of " +
str(self.
id()) +
" does not point to a node (DataType is mandatory for variables).")
994 if not isinstance(self.
dataType(), opcua_referencePointer_t):
995 logger.error(
"Variable " + self.
browseName() +
"/" +
str(self.
id()) +
" does not reference a valid dataType.")
998 if not isinstance(self.
dataType().target(), opcua_node_dataType_t):
999 logger.error(
"Variable " + self.
browseName() +
"/" +
str(self.
id()) +
" does not have a valid dataType reference.")
1002 if not self.
dataType().target().isEncodable():
1003 logger.error(
"DataType for Variable " + self.
browseName() +
"/" +
str(self.
id()) +
" is not encodable.")
1016 if not isinstance(self.
value(), opcua_value_t)
or len(self.
value().value) == 0:
1024 for (at, av)
in xmlelement.attributes.items():
1025 if at ==
"ValueRank":
1027 xmlelement.removeAttribute(at)
1028 elif at ==
"AccessLevel":
1030 xmlelement.removeAttribute(at)
1031 elif at ==
"UserAccessLevel":
1033 xmlelement.removeAttribute(at)
1034 elif at ==
"MinimumSamplingInterval":
1036 xmlelement.removeAttribute(at)
1037 elif at ==
"DataType":
1041 xmlelement.removeAttribute(at)
1042 elif at ==
"SymbolicName":
1044 xmlelement.removeAttribute(at)
1046 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
1048 for x
in xmlelement.childNodes:
1049 if x.nodeType == x.ELEMENT_NODE:
1050 if x.tagName ==
"Value":
1056 elif x.tagName ==
"DataType":
1060 elif x.tagName ==
"ValueRank":
1062 elif x.tagName ==
"ArrayDimensions":
1064 elif x.tagName ==
"AccessLevel":
1066 elif x.tagName ==
"UserAccessLevel":
1068 elif x.tagName ==
"MinimumSamplingInterval":
1070 elif x.tagName ==
"Historizing":
1071 if "true" in x.firstChild.data.lower():
1074 logger.info(
"Unprocessable XML Element: " + x.tagName)
1079 if self.
dataType() !=
None and isinstance(self.
dataType().target(), opcua_node_dataType_t):
1082 if self.
dataType().target().isEncodable():
1083 if self.
value() !=
None:
1087 code.append(
"UA_Variant *" + self.
getCodePrintableID() +
"_variant = UA_alloca(sizeof(UA_Variant));")
1096 if bootstrapping ==
False:
1098 code.append(
" // FIXME: missing minimumSamplingInterval")
1099 code.append(
" // FIXME: missing accessLevel")
1100 code.append(
" // FIXME: missing userAccessLevel")
1101 code.append(
" // FIXME: missing valueRank")
1117 __executable__ =
True
1118 __userExecutable__ =
True
1119 __methodDecalaration__ =
None
1133 if isinstance(data, bool):
1138 if isinstance(data, bool):
1151 for (at, av)
in xmlelement.attributes.items():
1152 if at ==
"MethodDeclarationId":
1157 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
1159 for x
in xmlelement.childNodes:
1160 if x.nodeType == x.ELEMENT_NODE:
1161 logger.info(
"Unprocessable XML Element: " + x.tagName)
1167 if bootstrapping ==
False:
1168 code.append(
" // Note: in/outputArguments are added by attaching the variable nodes,")
1169 code.append(
" // not by including the in the addMethodNode() call.")
1170 code.append(
" NULL,")
1171 code.append(
" NULL,")
1172 code.append(
" 0, NULL,")
1173 code.append(
" 0, NULL,")
1174 code.append(
" // FIXME: Missing executable")
1175 code.append(
" // FIXME: Missing userExecutable")
1187 __isAbstract__ =
False
1194 if isinstance(data, bool):
1199 for (at, av)
in xmlelement.attributes.items():
1200 if at ==
"IsAbstract":
1201 if "false" in av.lower():
1203 xmlelement.removeAttribute(at)
1205 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
1207 for x
in xmlelement.childNodes:
1208 if x.nodeType == x.ELEMENT_NODE:
1209 logger.info(
"Unprocessable XML Element: " + x.tagName)
1216 if bootstrapping ==
False:
1220 if ref.referenceType()
in typeDefs:
1225 if ref.referenceType().
browseName() ==
"HasSubtype" and ref.isForward() ==
False:
1229 logger.warn(
str(self) +
" failed to locate a type definition, assuming BaseObjectType.")
1230 code.append(
" // No valid typeDefinition found; assuming BaseObjectType")
1231 code.append(
" UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
1233 code.append(
" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) +
",")
1234 while myTypeRef
in unPrintedReferences:
1235 code.append(
" // removed " +
str(myTypeRef))
1236 unPrintedReferences.remove(myTypeRef)
1239 code.append(
" true,")
1241 code.append(
" false,")
1253 __arrayDimensions__ = 0
1254 __isAbstract__ =
False
1255 __xmlDefinition__ =
None
1267 logger.error(
"Setting data not implemented!")
1275 if isinstance(data, int):
1280 if isinstance(data, int):
1285 if isinstance(data, bool):
1293 if not isinstance(self.
dataType(), opcua_referencePointer_t):
1294 logger.error(
"DataType attribute of " +
str(self.
id()) +
" is not a pointer")
1297 if not isinstance(self.
dataType().target(), opcua_node_t):
1298 logger.error(
"DataType attribute of " +
str(self.
id()) +
" does not point to a node")
1302 logger.warn(
"DataType attribute of variableType " +
str(self.
id()) +
" is not defined.")
1306 for (at, av)
in xmlelement.attributes.items():
1307 if at ==
"IsAbstract":
1308 if "false" in av.lower():
1312 xmlelement.removeAttribute(at)
1313 elif at ==
"ValueRank":
1316 logger.warn(
"Array's or matrices are only permitted in variables and not supported for variableTypes. This attribute (" + at +
"=" + av +
") will effectively be ignored.")
1317 xmlelement.removeAttribute(at)
1318 elif at ==
"DataType":
1323 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
1325 for x
in xmlelement.childNodes:
1326 if x.nodeType == x.ELEMENT_NODE:
1327 if x.tagName ==
"Definition":
1329 logger.debug(
"Definition stored for future processing")
1331 logger.info(
"Unprocessable XML Element: " + x.tagName)
1336 if self.
dataType() !=
None and isinstance(self.
dataType().target(), opcua_node_dataType_t):
1339 if self.
dataType().target().isEncodable():
1340 if self.
value() !=
None:
1344 code.append(
"UA_Variant *" + self.
getCodePrintableID() +
"_variant = UA_alloca(sizeof(UA_Variant));")
1352 if bootstrapping ==
False:
1356 code.append(
" true,")
1358 code.append(
" false,")
1372 """ opcua_node_dataType_t is a subtype of opcua_note_t describing DataType nodes.
1374 DataType contain definitions and structure information usable for Variables.
1375 The format of this structure is determined by buildEncoding()
1376 Two definition styles are distinguished in XML:
1377 1) A DataType can be a structure of fields, each field having a name and a type.
1378 The type must be either an encodable builtin node (ex. UInt32) or point to
1379 another DataType node that inherits its encoding from a builtin type using
1380 a inverse "hasSubtype" (hasSuperType) reference.
1381 2) A DataType may be an enumeration, in which each field has a name and a numeric
1383 The definition is stored as an ordered list of tuples. Depending on which
1384 definition style was used, the __definition__ will hold
1385 1) A list of ("Fieldname", opcua_node_t) tuples.
1386 2) A list of ("Fieldname", int) tuples.
1388 A DataType (and in consequence all Variables using it) shall be deemed not
1389 encodable if any of its fields cannot be traced to an encodable builtin type.
1391 A DataType shall be further deemed not encodable if it contains mixed structure/
1392 enumaration definitions.
1394 If encodable, the encoding can be retrieved using getEncoding().
1396 __isAbstract__ =
False
1398 __xmlDefinition__ =
None
1399 __baseTypeEncoding__ = []
1400 __encodable__ =
False
1401 __encodingBuilt__ =
False
1415 """ Will return True if isAbstract was defined.
1417 Calling this function with an arbitrary data parameter will set
1420 if isinstance(data, bool):
1425 """ Will return True if buildEncoding() was able to determine which builtin
1426 type corresponds to all fields of this DataType.
1428 If no encoding has been build yet, this function will call buildEncoding()
1429 and return True if it succeeds.
1434 """ If the dataType is encodable, getEncoding() returns a nested list
1435 containing the encoding the structure definition for this type.
1437 If no encoding has been build yet, this function will call buildEncoding()
1438 and return the encoding if buildEncoding() succeeds.
1440 If buildEncoding() fails or has failed, an empty list will be returned.
1450 """ buildEncoding() determines the structure and aliases used for variables
1453 The function will parse the XML <Definition> of the dataType and extract
1454 "Name"-"Type" tuples. If successfull, buildEncoding will return a nested
1455 list of the following format:
1457 [['Alias1', ['Alias2', ['BuiltinType']]], [Alias2, ['BuiltinType']], ...]
1459 Aliases are fieldnames defined by this DataType or DataTypes referenced. A
1460 list such as ['DataPoint', ['Int32']] indicates that a value will encode
1461 an Int32 with the alias 'DataPoint' such as <DataPoint>12827</DataPoint>.
1462 Only the first Alias of a nested list is considered valid for the BuiltinType.
1464 Single-Elemented lists are always BuiltinTypes. Every nested list must
1465 converge in a builtin type to be encodable. buildEncoding will follow
1466 the first type inheritance reference (hasSupertype) of the dataType if
1469 If instead to "DataType" a numeric "Value" attribute is encountered,
1470 the DataType will be considered an enumeration and all Variables using
1471 it will be encoded as Int32.
1473 DataTypes can be either structures or enumeration - mixed definitions will
1476 Calls to getEncoding() will be iterative. buildEncoding() can be called
1477 only once per dataType, with all following calls returning the predetermined
1478 value. Use of the 'force=True' parameter will force the Definition to be
1481 After parsing, __definition__ holds the field definition as a list. Note
1482 that this might deviate from the encoding, especially if inheritance was
1487 prefix =
" " +
"|"*indent+
"+"
1502 logger.debug(
"Parsing DataType " + self.
browseName() +
" (" +
str(self.
id()) +
")")
1504 if proxy.isBuiltinByString(self.
browseName()):
1507 logger.debug( prefix + self.
browseName() +
"*")
1515 if "hassubtype" in ref.referenceType().
browseName().lower()
and ref.isForward() ==
False:
1516 if isinstance(ref.target(), opcua_node_dataType_t):
1517 logger.debug( prefix +
"Attempting definition using supertype " + ref.target().
browseName() +
" for DataType " +
" " + self.
browseName())
1525 logger.debug(prefix +
"No viable definition for " + self.
browseName() +
" " +
str(self.
id()) +
" found.")
1548 if x.nodeType == x.ELEMENT_NODE:
1553 for at,av
in x.attributes.items():
1554 if at ==
"DataType":
1562 elif at ==
"ValueRank":
1563 hasValueRank = int(av)
1564 logger.warn(
"Arrays or matrices (ValueRank) are not supported for datatypes. This DT will become scalar.")
1566 logger.warn(
"Unknown Field Attribute " +
str(at))
1569 if isEnum == isSubType:
1571 logger.warn(
"DataType contains both enumeration and subtype (or neither)")
1576 enumDict.append((fname, enumVal))
1584 logger.debug( prefix + fname +
" ?? " + av +
" ??")
1589 typeDict.append([fname, dtnode])
1590 if hasValueRank < 0:
1592 fdtype =
str(dtnode.browseName()) +
"+"*hasValueRank
1593 logger.debug( prefix + fname +
" : " + fdtype +
" -> " +
str(dtnode.id()))
1594 subenc = dtnode.buildEncoding(indent=indent+1)
1596 if not dtnode.isEncodable():
1613 logger.debug( prefix+
"Int32* -> enumeration with dictionary " +
str(enumDict) +
" encodable " +
str(self.
__encodable__))
1627 """ Parses all XML data that is not considered part of the base node attributes.
1629 XML attributes fields processed are "isAbstract"
1630 XML elements processed are "Definition"
1632 for (at, av)
in xmlelement.attributes.items():
1633 if at ==
"IsAbstract":
1634 if "true" in str(av).lower():
1638 xmlelement.removeAttribute(at)
1640 logger.warn(
"Don't know how to process attribute " + at +
" (" + av +
")")
1642 for x
in xmlelement.childNodes:
1643 if x.nodeType == x.ELEMENT_NODE:
1644 if x.tagName ==
"Definition":
1648 logger.warn(
"Unprocessable XML Element: " + x.tagName)
1651 """ Returns a number of the builtin Type that should be used
1652 to represent this datatype.
1659 if len(enc) > 1
and isinstance(enc[0], list):
1664 if len(enc)==1
and isinstance(enc[0], str):
1666 return opcua_value_t(
None).getTypeByString(enc[0]).getNumericRepresentation()
1671 while len(enc) > 1
and isinstance(enc[0], str):
1676 return opcua_value_t(
None).getTypeByString(enc[0]).getNumericRepresentation()
1683 if bootstrapping ==
False:
1687 if ref.referenceType()
in typeDefs:
1692 if ref.referenceType().
browseName() ==
"HasSubtype" and ref.isForward() ==
False:
1696 logger.warn(
str(self) +
" failed to locate a type definition, assuming BaseDataType.")
1697 code.append(
" // No valid typeDefinition found; assuming BaseDataType")
1698 code.append(
" UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
1700 code.append(
" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) +
",")
1701 while myTypeRef
in unPrintedReferences:
1702 unPrintedReferences.remove(myTypeRef)
1705 code.append(
" true,")
1707 code.append(
" false,")
1717 __containsNoLoops__ =
True
1718 __eventNotifier__ = 0
1726 if isinstance(data, bool):
1731 if isinstance(data, int):
1736 for (at, av)
in xmlelement.attributes.items():
1737 logger.error(
"Don't know how to process attribute " + at +
" (" + av +
")")
1739 for x
in xmlelement.childNodes:
1740 if x.nodeType == x.ELEMENT_NODE:
1741 logger.info(
"Unprocessable XML Element: " + x.tagName)
1748 if bootstrapping ==
False:
1752 if ref.referenceType()
in typeDefs:
1757 if ref.referenceType().
browseName() ==
"HasSubtype" and ref.isForward() ==
False:
1761 logger.warn(
str(self) +
" failed to locate a type definition, assuming BaseViewType.")
1762 code.append(
" // No valid typeDefinition found; assuming BaseViewType")
1763 code.append(
" UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEViewTYPE),")
1765 code.append(
" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) +
",")
1766 while myTypeRef
in unPrintedReferences:
1767 unPrintedReferences.remove(myTypeRef)
1769 code.append(
" // FIXME: Missing eventNotifier")
1770 code.append(
" // FIXME: Missing containsNoLoops")