ChimeraTK-ControlSystemAdapter-OPCUAAdapter 04.00.05
Loading...
Searching...
No Matches
ua_node_types.py
Go to the documentation of this file.
1#!/usr/bin/env/python
2# -*- coding: utf-8 -*-
3
4# This Source Code Form is subject to the terms of the Mozilla Public
5# License, v. 2.0. If a copy of the MPL was not distributed with this
6# file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
22
23import sys
24import logging
25from ua_builtin_types import *;
26from open62541_MacroHelper import open62541_MacroHelper
27from ua_constants import *
28
29logger = logging.getLogger(__name__)
30
31def getNextElementNode(xmlvalue):
32 if xmlvalue == None:
33 return None
34 xmlvalue = xmlvalue.nextSibling
35 while not xmlvalue == None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
36 xmlvalue = xmlvalue.nextSibling
37 return xmlvalue
38
39
43
45 """ Representation of a pointer.
46
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.
50 """
51 __reference_type__ = None
52 __target__ = None
53 __isForward__ = True
54 __addr__ = 0
55 __parentNode__ = None
56
57 def __init__(self, target, hidden=False, parentNode=None):
58 self.__target__ = target
59 self.__reference_type__ = None
61 self.__isHidden__ = hidden
62 self.__parentNode__ = parentNode
63 self.__addr____addr__ = 0
64
65 def isHidden(self, data=None):
66 if isinstance(data, bool):
67 self.__isHidden__ = data
68 return self.__isHidden__
69
70 def isForward(self, data=None):
71 if isinstance(data, bool):
74
75 def referenceType(self, type=None):
76 if not type == None:
77 self.__reference_type__ = type
78 return self.__reference_type__
79
80 def target(self, data=None):
81 if not data == None:
82 self.__target__ = data
83 return self.__target__
84
85 def address(self, data=None):
86 if data != None:
87 self.__addr____addr__ = data
88 return self.__addr____addr__
89
90 def parent(self):
91 return self.__parentNode__
92
94 src = "None"
95 tgt = "None"
96 type = "Unknown"
97 if self.parent() != None:
98 src = str(self.parent().id())
99 if self.target() != None:
100 tgt = str(self.target().id())
101 if self.referenceType() != None:
102 type = str(self.referenceType().id())
103 tmp = src+"_"+type+"_"+tgt
104 tmp = tmp.lower()
105 refid = ""
106 for i in tmp:
107 if not i in "ABCDEFGHIJKLMOPQRSTUVWXYZ0123456789".lower():
108 refid = refid + ("_")
109 else:
110 refid = refid + i
111 return refid
112
113 def __str__(self):
114 retval=""
115 if isinstance(self.parent(), opcua_node_t):
116 if isinstance(self.parent().id(), opcua_node_id_t):
117 retval=retval + str(self.parent().id()) + "--["
118 else:
119 retval=retval + "(?) --["
120 else:
121 retval=retval + "(?) --["
122
123 if isinstance(self.referenceType(), opcua_node_t):
124 retval=retval + str(self.referenceType().browseName()) + "]-->"
125 else:
126 retval=retval + "?]-->"
127
128 if isinstance(self.target(), opcua_node_t):
129 if isinstance(self.target().id(), opcua_node_id_t):
130 retval=retval + str(self.target().id())
131 else:
132 retval=retval + "(?) "
133 else:
134 retval=retval + "(?) "
135
136 if self.isForward() or self.isHidden():
137 retval = retval + " <"
138 if self.isForward():
139 retval = retval + "F"
140 if self.isHidden():
141 retval = retval + "H"
142 retval = retval + ">"
143 return retval
144
145 def __repr__(self):
146 return self.__str__()
147
148 def __cmp__(self, other):
149 if not isinstance(other, opcua_referencePointer_t):
150 return -1
151 if other.target() == self.target():
152 if other.referenceType() == self.referenceType():
153 if other.isForward() == self.isForward():
154 return 0
155 return 1
156
157
158
161
163 """ Implementation of a node ID.
164
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).
168 """
169 i = -1
170 o = ""
171 g = ""
172 s = ""
173 ns = 0
174 __mystrname__ = ""
175
176 def __init__(self, idstring):
177 idparts = idstring.split(";")
178 self.ii = None
179 self.b = None
180 self.gg = None
181 self.ss = None
182 self.nsns = 0
183 for p in idparts:
184 if p[:2] == "ns":
185 self.nsns = int(p[3:])
186 elif p[:2] == "i=":
187 self.ii = int(p[2:])
188 elif p[:2] == "o=":
189 self.b = p[2:]
190 elif p[:2] == "g=":
191 tmp = []
192 self.gg = p[2:].split("-")
193 for i in self.gg:
194 i = "0x"+i
195 tmp.append(int(i,16))
196 self.gg = tmp
197 elif p[:2] == "s=":
198 self.ss = p[2:]
200 self.toString()
201
202 def toString(self):
204 if self.nsns != 0:
205 self.__mystrname____mystrname__ = "ns="+str(self.nsns)+";"
206 # Order of preference is numeric, guid, bytestring, string
207 if self.ii != None:
209 elif self.gg != None:
211 tmp = []
212 for i in self.gg:
213 tmp.append(hex(i).replace("0x",""))
214 for i in tmp:
216 self.__mystrname____mystrname__ = self.__mystrname____mystrname__.replace("g=-","g=")
217 elif self.b != None:
219 elif self.ss != None:
221
222 def __str__(self):
224
225 def __eq__(self, nodeId2):
226 if not nodeId2:
227 return False
228 return (self.toString() == nodeId2.toString())
229
230 def __repr__(self):
232
233
236
238 __node_id__ = None
239 __node_class__ = 0
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__ = []
248 __binary__ = ""
249 __address__ = 0
250
272 self.nodeClass(0)
273
274 def __str__(self):
275 if isinstance(self.id(), opcua_node_id_t):
276 return self.__class__.__name__ + "(" + str(self.id()) + ")"
277 return self.__class__.__name__ + "( no ID )"
278
279 def __repr__(self):
280 if isinstance(self.id(), opcua_node_id_t):
281 return self.__class__.__name__ + "(" + str(self.id()) + ")"
282 return self.__class__.__name__ + "( no ID )"
283
284
286 CodePrintable="NODE_"
287
288 if isinstance(self.id(), opcua_node_id_t):
289 CodePrintable = self.__class__.__name__ + "_" + str(self.id())
290 else:
291 CodePrintable = self.__class__.__name__ + "_unknown_nid"
292
293 CodePrintable = CodePrintable.lower()
294 cleanPrintable = ""
295 for i in range(0,len(CodePrintable)):
296 if not CodePrintable[i] in "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_".lower():
297 cleanPrintable = cleanPrintable + "_"
298 else:
299 cleanPrintable = cleanPrintable + CodePrintable[i]
300 return cleanPrintable
301
302 def addReference(self, ref):
303 """ Add a opcua_referencePointer_t to the list of
304 references this node carries.
305 """
308
309 def removeReference(self, ref):
312
313 def removeReferenceToNode(self, targetNode):
314 tmp = []
316 if ref.target() != targetNode:
317 tmp.append(ref)
319
321 """ Adds a reference to the inverse reference list of this node.
322
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.
328 """
329 # Only add this target if it is not already referenced
331 if not self.hasReferenceTarget(node):
332 self.__node_referencedBy____node_referencedBy__.append(opcua_referencePointer_t(node, hidden=True, parentNode=self))
333# logger.debug(self.__node_browseName__ + " added reverse reference to " + str(node.__node_browseName__))
334# else:
335# logger.debug(self.__node_browseName__ + " refusing reverse reference to " + str(node.__node_browseName__) + " (referenced normally)")
336# else:
337# logger.debug(self.__node_browseName__ + " refusing reverse reference to " + str(node.__node_browseName__) + " (already reversed referenced)")
338
346 for r in self.getInverseReferences():
347 if node == r.target():
348 return True
349 return False
350
351 def hasReferenceTarget(self, node):
352 for r in self.getReferences():
353 if node == r.target():
354 return True
355 return False
356
358 """ getFirstParentNode
359
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.
363
364 This function requires a linked namespace.
365
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.
370 """
371 parent = None
372 revref = None
373
374 for hiddenstatus in [False, True]:
375 for r in self.getReferences():
376 if r.isHidden() == hiddenstatus and r.isForward() == False:
377 parent = r.target()
378 for r in parent.getReferences():
379 if r.target() == self:
380 revref = r
381 break
382 if revref != None:
383 return (parent, revref)
384
385 return (parent, revref)
386
388 """ Updates inverse references in all nodes referenced by this node.
389
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.
395 """
396 # Update inverse references in all nodes we have referenced
397 for r in self.getReferences():
398 if isinstance(r.target(), opcua_node_t):
399 if not r.target().hasInverseReferenceTarget(self):
400 #logger.debug(self.__node_browseName__ + " req. rev. referencing in" + str(r.target().__node_browseName__))
401 r.target().addInverseReferenceTarget(self)
402 #else:
403 #logger.debug("Cannot register inverse link to " + str(r.target()) + " (not a node)")
404
405 def id(self):
406 return self.__node_id__
407
408 def getNamespace(self):
409 return self.__node_namespace__
410
411 def nodeClass(self, c = 0):
412 """ Sets the node class attribute if c is passed.
413 Returns the current node class.
414 """
415 # Allow overwriting only if it is not set
416 if isinstance(c, int):
417 if self.__node_class____node_class__ == 0 and c < 256:
420
421 def browseName(self, data=0):
422 """ Sets the browse name attribute if data is passed.
423 Returns the current browse name.
424 """
425 if isinstance(data, str):
427 if sys.version_info[0] < 3:
428 return self.__node_browseName____node_browseName__.encode('utf-8')
430
431 def displayName(self, data=None):
432 """ Sets the display name attribute if data is passed.
433 Returns the current display name.
434 """
435 if data != None:
437 return self.__node_displayName____node_displayName__.encode('utf-8')
438
439 def description(self, data=None):
440 """ Sets the description attribute if data is passed.
441 Returns the current description.
442 """
443 if data != None:
445 return self.__node_description____node_description__.encode('utf-8')
446
447 def writeMask(self, data=None):
448 """ Sets the write mask attribute if data is passed.
449 Returns the current write mask.
450 """
451 if data != None:
454
455 def userWriteMask(self, data=None):
456 """ Sets the user write mask attribute if data is passed.
457 Returns the current user write mask.
458 """
459 if data != None:
462
463 def initiateDummyXMLReferences(self, xmlelement):
464 """ Initiates references found in the XML <References> element.
465
466 All references initiated will be registered with this node, but
467 their targets will be strings extracted from the XML description
468 (hence "dummy").
469
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.
473 """
474 if not xmlelement.tagName == "References":
475 logger.error("XMLElement passed is not a reference list")
476 return
477 for ref in xmlelement.childNodes:
478 if ref.nodeType == ref.ELEMENT_NODE:
479 dummy = opcua_referencePointer_t(unicode(ref.firstChild.data), parentNode=self)
480 self.addReference(dummy)
481 self.getNamespace().linkLater(dummy)
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)
488 else:
489 logger.error("Don't know how to process attribute " + at + "(" + av + ") for references.")
490
491 def printDot(self):
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("=","")
497 dot = dot + "\n"
498 if r.isForward() == True:
499 dot = dot + cleanname + " -> " + tgtname + " [label=\"" + str(r.referenceType().browseName()) + "\"]\n"
500 else:
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"
504 return dot
505
506 def sanitize(self):
507 """ Check the health of this node.
508
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.
512 """
513 # Do we have an id?
514 if not isinstance(self.id(), opcua_node_id_t):
515 logger.error("HELP! I'm an id'less node!")
516 return False
517
518 # Remove unlinked references
519 tmp = []
520 for r in self.getReferences():
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.")
527 else:
528 tmp.append(r)
530
531 # Make sure that every inverse referenced node actually does reference us
532 tmp = []
533 for r in self.getInverseReferences():
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.")
536 else:
537 if r.target().hasReferenceTarget(self):
538 tmp.append(r)
539 else:
540 logger.warn("Node " + str(self.id()) + " was falsely under the impression that it is referenced by " + str(r.target().id()))
542
543 # Remove references from inverse list if we can reach this not "the regular way"
544 # over a normal reference
545 tmp=[]
546 for r in self.getInverseReferences():
547 if not self.hasReferenceTarget(r.target()):
548 tmp.append(r)
549 else:
550 logger.debug("Removing unnecessary inverse reference to " + str(r.target.id()))
552
553 return self.sanitizeSubType()
554
556 pass
557
558 def address(self, addr = None):
559 """ If addr is passed, the address of this node within the binary
560 representation will be set.
561
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
564 set.
565 """
566 if addr != None:
567 self.__address__ = addr
568 return self.__address__
569
570 def parseXML(self, xmlelement):
571 """ Extracts base attributes from the XML description of an element.
572 Parsed basetype attributes are:
573 * browseName
574 * displayName
575 * Description
576 * writeMask
577 * userWriteMask
578 * eventNotifier
579
580 ParentNodeIds are ignored.
581
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.
586 """
587 thisxml = xmlelement
588 for (at, av) in thisxml.attributes.items():
589 if at == "NodeId":
590 xmlelement.removeAttribute(at)
591 elif at == "BrowseName":
592 self.browseName(str(av))
593 xmlelement.removeAttribute(at)
594 elif at == "DisplayName":
595 self.displayName(av)
596 xmlelement.removeAttribute(at)
597 elif at == "Description":
598 self.description(av)
599 xmlelement.removeAttribute(at)
600 elif at == "WriteMask":
601 self.writeMask(int(av))
602 xmlelement.removeAttribute(at)
603 elif at == "UserWriteMask":
604 self.userWriteMask(int(av))
605 xmlelement.removeAttribute(at)
606 elif at == "EventNotifier":
607 self.eventNotifier(int(av))
608 xmlelement.removeAttribute(at)
609 elif at == "ParentNodeId":
610 # Silently ignore this one..
611 xmlelement.removeAttribute(at)
612
613 for x in thisxml.childNodes:
614 if x.nodeType == x.ELEMENT_NODE:
615 if x.firstChild:
616 if x.tagName == "BrowseName":
617 self.browseName(unicode(x.firstChild.data))
618 xmlelement.removeChild(x)
619 elif x.tagName == "DisplayName":
620 self.displayName(unicode(x.firstChild.data))
621 xmlelement.removeChild(x)
622 elif x.tagName == "Description":
623 self.description(unicode(x.firstChild.data))
624 xmlelement.removeChild(x)
625 elif x.tagName == "WriteMask":
626 self.writeMask(int(unicode(x.firstChild.data)))
627 xmlelement.removeChild(x)
628 elif x.tagName == "UserWriteMask":
629 self.userWriteMask(int(unicode(x.firstChild.data)))
630 xmlelement.removeChild(x)
631 if x.tagName == "References":
633 xmlelement.removeChild(x)
634 self.parseXMLSubType(xmlelement)
635
636 def parseXMLSubType(self, xmlelement):
637 pass
638
639 def printXML(self):
640 pass
641
642 def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
643 """ printOpen62541CCode_SubtypeEarly
644
645 Initiate code segments for the nodes instantiotion that preceed
646 the actual UA_Server_addNode or UA_NodeStore_insert calls.
647 """
648 return []
649
650 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
651 """ printOpen62541CCode_Subtype
652
653 Appends node type specific information to the nodes UA_Server_addNode
654 or UA_NodeStore_insert calls.
655 """
656 return []
657
658 def printOpen62541CCode(self, unPrintedNodes=[], unPrintedReferences=[], supressGenerationOfAttribute=[]):
659 """ printOpen62541CCode
660
661 Returns a list of strings containing the C-code necessary to intialize
662 this node for the open62541 OPC-UA Stack.
663
664 Note that this function will fail if the nodeid is non-numeric, as
665 there is no UA_EXPANDEDNNODEID_[STRING|GUID|BYTESTRING] macro.
666 """
667 codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
668 code = []
669 code.append("")
670 code.append("do {")
671
672 # Just to be sure...
673 if not (self in unPrintedNodes):
674 logger.warn(str(self) + " attempted to reprint already printed node " + str(self)+ ".")
675 return []
676
677 # If we are being passed a parent node by the namespace, use that for registering ourselves in the namespace
678 # Note: getFirstParentNode will return [parentNode, referenceToChild]
679 (parentNode, parentRef) = self.getFirstParentNode()
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)
685 # Parent to child reference is added by the server, do not reprint that reference
686 if parentRef in unPrintedReferences:
687 unPrintedReferences.remove(parentRef)
688 # the UA_Server_addNode function will use addReference which creates a bidirectional reference; remove any inverse
689 # references to our parent to avoid duplicate refs
690 for ref in self.getReferences():
691 if ref.target() == parentNode and ref.referenceType() == parentRef.referenceType() and ref.isForward() == False:
692 while ref in unPrintedReferences:
693 unPrintedReferences.remove(ref)
694 # Otherwise use the "Bootstrapping" method and we will get registered with other nodes later.
695 else:
696 code = code + self.printOpen62541CCode_SubtypeEarly(bootstrapping = True)
697 code = code + codegen.getCreateNodeBootstrap(self)
698 code = code + self.printOpen62541CCode_Subtype(unPrintedReferences = unPrintedReferences, bootstrapping = True)
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();")
703
704 # Try to print all references to nodes that already exist
705 # Note: we know the reference types exist, because the namespace class made sure they were
706 # the first ones being printed
707 tmprefs = []
708 for r in self.getReferences():
709 #logger.debug("Checking if reference from " + str(r.parent()) + "can be created...")
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)
715 tmprefs.append(r)
716 # Remove printed refs from list
717 for r in tmprefs:
718 unPrintedReferences.remove(r)
719
720 # Again, but this time check if other nodes deffered their node creation because this node did
721 # not exist...
722 tmprefs = []
723 for r in unPrintedReferences:
724 #logger.debug("Checking if another reference " + str(r.target()) + "can be created...")
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!")
730 else:
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)
734 tmprefs.append(r)
735 # Remove printed refs from list
736 for r in tmprefs:
737 unPrintedReferences.remove(r)
738
739 # Again, just to be sure...
740 if self in unPrintedNodes:
741 # This is necessery to make printing work at all!
742 unPrintedNodes.remove(self)
743
744 code.append("} while(0);")
745 return code
746
748 __isAbstract__ = False
749 __symmetric__ = False
750 __reference_inverseName__ = ""
751 __reference_referenceType__ = None
752
754 self.nodeClass(NODE_CLASS_REFERENCETYPE)
755 self.__reference_isAbstract__ = False
756 self.__reference_symmetric__ = False
759
760 def referenceType(self,data=None):
761 if isinstance(data, opcua_node_t):
764
765 def isAbstract(self,data=None):
766 if isinstance(data, bool):
769
770 def symmetric(self,data=None):
771 if isinstance(data, bool):
774
775 def inverseName(self,data=None):
776 if isinstance(data, str):
779
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).")
784 return False
785 return True
786
787 def parseXMLSubType(self, xmlelement):
788 for (at, av) in xmlelement.attributes.items():
789 if at == "Symmetric":
790 if "false" in av.lower():
791 self.symmetric(False)
792 else:
793 self.symmetric(True)
794 xmlelement.removeAttribute(at)
795 elif at == "InverseName":
796 self.inverseName(str(av))
797 xmlelement.removeAttribute(at)
798 elif at == "IsAbstract":
799 if "false" in str(av).lower():
800 self.isAbstract(False)
801 else:
802 self.isAbstract(True)
803 xmlelement.removeAttribute(at)
804 else:
805 logger.warn("Don't know how to process attribute " + at + " (" + av + ")")
806
807 for x in xmlelement.childNodes:
808 if x.nodeType == x.ELEMENT_NODE:
809 if x.tagName == "InverseName" and x.firstChild:
810 self.inverseName(str(unicode(x.firstChild.data)))
811 else:
812 logger.warn( "Unprocessable XML Element: " + x.tagName)
813
814 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
815 code = []
816 codegen = open62541_MacroHelper()
817
818 # Detect if this is bootstrapping or if we are attempting to use userspace...
819 if bootstrapping == False:
820 typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
821 myTypeRef = None
822 for ref in self.getReferences():
823 if ref.referenceType() in typeDefs:
824 myTypeRef = ref
825 break
826 if myTypeRef==None:
827 for ref in self.getReferences():
828 if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
829 myTypeRef = ref
830 break
831 if myTypeRef==None:
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),")
835 else:
836 code.append(" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
837 while myTypeRef in unPrintedReferences:
838 unPrintedReferences.remove(myTypeRef)
839
840 code.append(" UA_LOCALIZEDTEXT(\"\",\"" + str(self.inverseName()) + "\"),");
841 code.append(" // FIXME: Missing, isAbstract")
842 code.append(" // FIXME: Missing, symmetric")
843 return code
844
845 if self.isAbstract():
846 code.append(self.getCodePrintableID() + "->isAbstract = true;")
847 if self.symmetric():
848 code.append(self.getCodePrintableID() + "->symmetric = true;")
850 code.append(self.getCodePrintableID() + "->inverseName = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + self.__reference_inverseName____reference_inverseName__ + "\");")
851 return code;
852
853
855 __object_eventNotifier__ = 0
856
858 self.nodeClass(NODE_CLASS_OBJECT)
860
861 def eventNotifier(self, data=""):
862 if isinstance(data, int):
865
866 def parseXMLSubType(self, xmlelement):
867 for (at, av) in xmlelement.attributes.items():
868 if at == "EventNotifier":
869 self.eventNotifier(int(av))
870 xmlelement.removeAttribute(at)
871 elif at == "SymbolicName":
872 # Silently ignore this one
873 xmlelement.removeAttribute(at)
874 else:
875 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
876
877 for x in xmlelement.childNodes:
878 if x.nodeType == x.ELEMENT_NODE:
879 logger.info( "Unprocessable XML Element: " + x.tagName)
880
881 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
882 code = []
883 codegen = open62541_MacroHelper()
884
885 # Detect if this is bootstrapping or if we are attempting to use userspace...
886 if bootstrapping == False:
887 typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
888 myTypeRef = None
889 for ref in self.getReferences():
890 if ref.referenceType() in typeDefs:
891 myTypeRef = ref
892 break
893 if myTypeRef==None:
894 for ref in self.getReferences():
895 if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
896 myTypeRef = ref
897 break
898 if myTypeRef==None:
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),")
902 else:
903 code.append(" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
904 while myTypeRef in unPrintedReferences:
905 unPrintedReferences.remove(myTypeRef)
906
907 #FIXME: No event notifier in UA_Server_addNode call!
908 return code
909
910 # We are being bootstrapped! Add the raw attributes to the node.
911 code.append(self.getCodePrintableID() + "->eventNotifier = (UA_Byte) " + str(self.eventNotifier()) + ";")
912 return code
913
914if sys.version_info[0] >= 3:
915 # strings are already parsed to unicode
916 def unicode(s):
917 return s
918
920 __value__ = 0
921 __dataType__ = None
922 __valueRank__ = 0
923 __arrayDimensions__ = 0
924 __accessLevel__ = 0
925 __userAccessLevel__ = 0
926 __minimumSamplingInterval__ = 0
927 __historizing__ = False
928
941 def value(self, data=0):
942 if isinstance(data, opcua_value_t):
943 self.__value____value__ = data
944 return self.__value____value__
945
946 def dataType(self, data=None):
947 if data != None:
948 self.__dataType__ = data
949 return self.__dataType__
950
951 def valueRank(self, data=""):
952 if isinstance(data, int):
955
956 def arrayDimensions(self, data=None):
957 if not data==None:
960
961 def accessLevel(self, data=None):
962 if not data==None:
965
966 def userAccessLevel(self, data=None):
967 if not data==None:
970
976 def historizing(self, data=None):
977 if data != None:
980
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).")
984 self.__dataType__ = None
985 return False
986
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).")
989 self.__dataType__ = None
990 return False
991 return True
992
993 def allocateValue(self):
994 if not isinstance(self.dataType(), opcua_referencePointer_t):
995 logger.error("Variable " + self.browseName() + "/" + str(self.id()) + " does not reference a valid dataType.")
996 return False
997
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.")
1000 return False
1001
1002 if not self.dataType().target().isEncodable():
1003 logger.error("DataType for Variable " + self.browseName() + "/" + str(self.id()) + " is not encodable.")
1004 return False
1005
1006 # FIXME: Don't build at all or allocate "defaults"? I'm for not building at all.
1007 if self.__xmlValueDef__ == None:
1008 #logger.warn("Variable " + self.browseName() + "/" + str(self.id()) + " is not initialized. No memory will be allocated.")
1009 return False
1010
1011 self.value(opcua_value_t(self))
1012 self.value().parseXML(self.__xmlValueDef__)
1013
1014 # Array Dimensions must accurately represent the value and will be patched
1015 # reflect the exaxt dimensions attached binary stream.
1016 if not isinstance(self.value(), opcua_value_t) or len(self.value().value) == 0:
1017 self.arrayDimensions([])
1018 else:
1019 # Parser only permits 1-d arrays, which means we do not have to check further dimensions
1020 self.arrayDimensions([len(self.value().value)])
1021 return True
1022
1023 def parseXMLSubType(self, xmlelement):
1024 for (at, av) in xmlelement.attributes.items():
1025 if at == "ValueRank":
1026 self.valueRank(int(av))
1027 xmlelement.removeAttribute(at)
1028 elif at == "AccessLevel":
1029 self.accessLevel(int(av))
1030 xmlelement.removeAttribute(at)
1031 elif at == "UserAccessLevel":
1032 self.userAccessLevel(int(av))
1033 xmlelement.removeAttribute(at)
1034 elif at == "MinimumSamplingInterval":
1035 self.minimumSamplingInterval(float(av))
1036 xmlelement.removeAttribute(at)
1037 elif at == "DataType":
1038 self.dataType(opcua_referencePointer_t(str(av), parentNode=self))
1039 # dataType needs to be linked to a node once the namespace is read
1040 self.getNamespace().linkLater(self.dataType())
1041 xmlelement.removeAttribute(at)
1042 elif at == "SymbolicName":
1043 # Silently ignore this one
1044 xmlelement.removeAttribute(at)
1045 else:
1046 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
1047
1048 for x in xmlelement.childNodes:
1049 if x.nodeType == x.ELEMENT_NODE:
1050 if x.tagName == "Value":
1051 # We need to be able to parse the DataType to build the variable value,
1052 # which can only be done if the namespace is linked.
1053 # Store the Value for later parsing
1054 self.__xmlValueDef__ = x
1055 #logger.debug( "Value description stored for later elaboration.")
1056 elif x.tagName == "DataType":
1057 self.dataType(opcua_referencePointer_t(str(av), parentNode=self))
1058 # dataType needs to be linked to a node once the namespace is read
1059 self.getNamespace().linkLater(self.dataType())
1060 elif x.tagName == "ValueRank":
1061 self.valueRank(int(unicode(x.firstChild.data)))
1062 elif x.tagName == "ArrayDimensions":
1063 self.arrayDimensions(int(unicode(x.firstChild.data)))
1064 elif x.tagName == "AccessLevel":
1065 self.accessLevel(int(unicode(x.firstChild.data)))
1066 elif x.tagName == "UserAccessLevel":
1067 self.userAccessLevel(int(unicode(x.firstChild.data)))
1068 elif x.tagName == "MinimumSamplingInterval":
1069 self.minimumSamplingInterval(float(unicode(x.firstChild.data)))
1070 elif x.tagName == "Historizing":
1071 if "true" in x.firstChild.data.lower():
1072 self.historizing(True)
1073 else:
1074 logger.info( "Unprocessable XML Element: " + x.tagName)
1075
1076 def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
1077 code = []
1078 # If we have an encodable value, try to encode that
1079 if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
1080 # Delegate the encoding of the datavalue to the helper if we have
1081 # determined a valid encoding
1082 if self.dataType().target().isEncodable():
1083 if self.value() != None:
1084 code = code + self.value().printOpen62541CCode(bootstrapping)
1085 return code
1086 if(bootstrapping):
1087 code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_alloca(sizeof(UA_Variant));")
1088 code.append("UA_Variant_init(" + self.getCodePrintableID() + "_variant);")
1089 return code
1090
1091 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1092 code = []
1093 codegen = open62541_MacroHelper()
1094
1095 # Detect if this is bootstrapping or if we are attempting to use userspace...
1096 if bootstrapping == False:
1097 code.append(" " + self.getCodePrintableID() + "_variant, ")
1098 code.append(" // FIXME: missing minimumSamplingInterval")
1099 code.append(" // FIXME: missing accessLevel")
1100 code.append(" // FIXME: missing userAccessLevel")
1101 code.append(" // FIXME: missing valueRank")
1102 return code
1103
1104 if self.historizing():
1105 code.append(self.getCodePrintableID() + "->historizing = true;")
1106
1107 code.append(self.getCodePrintableID() + "->minimumSamplingInterval = (UA_Double) " + str(self.minimumSamplingInterval()) + ";")
1108 code.append(self.getCodePrintableID() + "->userAccessLevel = (UA_Int32) " + str(self.userAccessLevel()) + ";")
1109 code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
1110 code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
1111 # The variant is guaranteed to exist by SubtypeEarly()
1112 code.append("UA_Variant_copy(" + self.getCodePrintableID() + "_variant, &" + self.getCodePrintableID() + "->value.data.value.value );")
1113 code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_DATA;")
1114 return code
1115
1117 __executable__ = True
1118 __userExecutable__ = True
1119 __methodDecalaration__ = None
1120
1122 self.nodeClass(NODE_CLASS_METHOD)
1125 self.__methodDecalaration__ = None
1126
1127 def methodDeclaration(self, data=None):
1128 if not data==None:
1129 self.__methodDecalaration__ = data
1130 return self.__methodDecalaration__
1131
1132 def executable(self, data=None):
1133 if isinstance(data, bool):
1134 self.__executable____executable__ == data
1136
1137 def userExecutable(self, data=None):
1138 if isinstance(data, bool):
1141
1143 if self.methodDeclaration() != None:
1144 if not isinstance(self.methodDeclaration().target(), opcua_node_t):
1145 return False
1146 else:
1147 #FIXME: Is this even permitted!?
1148 pass
1149
1150 def parseXMLSubType(self, xmlelement):
1151 for (at, av) in xmlelement.attributes.items():
1152 if at == "MethodDeclarationId":
1153 self.methodDeclaration(opcua_referencePointer_t(str(av), parentNode=self))
1154 # dataType needs to be linked to a node once the namespace is read
1155 self.getNamespace().linkLater(self.methodDeclaration())
1156 else:
1157 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
1158
1159 for x in xmlelement.childNodes:
1160 if x.nodeType == x.ELEMENT_NODE:
1161 logger.info( "Unprocessable XML Element: " + x.tagName)
1162
1163 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1164 code = []
1165
1166 # Detect if this is bootstrapping or if we are attempting to use userspace...
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")
1176 return code
1177
1178 # UA_False is default for booleans on _init()
1179 if self.executable():
1180 code.append(self.getCodePrintableID() + "->executable = true;")
1181 if self.userExecutable():
1182 code.append(self.getCodePrintableID() + "->userExecutable = true;")
1183
1184 return code
1185
1187 __isAbstract__ = False
1188
1190 self.nodeClass(NODE_CLASS_OBJECTTYPE)
1191 self.__isAbstract____isAbstract__ == False
1192
1193 def isAbstract(self, data=None):
1194 if isinstance(data, bool):
1197
1198 def parseXMLSubType(self, xmlelement):
1199 for (at, av) in xmlelement.attributes.items():
1200 if at == "IsAbstract":
1201 if "false" in av.lower():
1202 self.isAbstract(False)
1203 xmlelement.removeAttribute(at)
1204 else:
1205 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
1206
1207 for x in xmlelement.childNodes:
1208 if x.nodeType == x.ELEMENT_NODE:
1209 logger.info( "Unprocessable XML Element: " + x.tagName)
1210
1211 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1212 code = []
1213 codegen = open62541_MacroHelper();
1214
1215 # Detect if this is bootstrapping or if we are attempting to use userspace...
1216 if bootstrapping == False:
1217 typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
1218 myTypeRef = None
1219 for ref in self.getReferences():
1220 if ref.referenceType() in typeDefs:
1221 myTypeRef = ref
1222 break
1223 if myTypeRef==None:
1224 for ref in self.getReferences():
1225 if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
1226 myTypeRef = ref
1227 break
1228 if myTypeRef==None:
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),")
1232 else:
1233 code.append(" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
1234 while myTypeRef in unPrintedReferences:
1235 code.append(" // removed " + str(myTypeRef))
1236 unPrintedReferences.remove(myTypeRef)
1237
1238 if (self.isAbstract()):
1239 code.append(" true,")
1240 else:
1241 code.append(" false,")
1242
1243 # Fallback mode for bootstrapping
1244 if (self.isAbstract()):
1245 code.append(self.getCodePrintableID() + "->isAbstract = true;")
1246
1247 return code
1248
1250 __value__ = 0
1251 __dataType__ = None
1252 __valueRank__ = 0
1253 __arrayDimensions__ = 0
1254 __isAbstract__ = False
1255 __xmlDefinition__ = None
1256
1258 self.nodeClass(NODE_CLASS_VARIABLETYPE)
1259 self.__value____value__ = 0
1260 self.__dataType__ = None
1263 self.__isAbstract____isAbstract__ = False
1264 self.__xmlDefinition__ = None
1265
1266 def value(self, data=None):
1267 logger.error("Setting data not implemented!")
1268
1269 def dataType(self, data=None):
1270 if data != None:
1271 self.__dataType__ = data
1272 return self.__dataType__
1273
1274 def valueRank(self,data=None):
1275 if isinstance(data, int):
1276 self.__valueRank____valueRank__ = data
1277 return self.__valueRank____valueRank__
1278
1279 def arrayDimensions(self,data=None):
1280 if isinstance(data, int):
1283
1284 def isAbstract(self,data=None):
1285 if isinstance(data, bool):
1288
1290 # DataType fields appear to be optional for VariableTypes
1291 # but if it does have a node set, it must obviously be a valid node
1292 if not self.dataType() != None:
1293 if not isinstance(self.dataType(), opcua_referencePointer_t):
1294 logger.error("DataType attribute of " + str(self.id()) + " is not a pointer")
1295 return False
1296 else:
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")
1299 return False
1300 else:
1301 # FIXME: It's unclear wether this is ok or not.
1302 logger.warn("DataType attribute of variableType " + str(self.id()) + " is not defined.")
1303 return False
1304
1305 def parseXMLSubType(self, xmlelement):
1306 for (at, av) in xmlelement.attributes.items():
1307 if at == "IsAbstract":
1308 if "false" in av.lower():
1309 self.isAbstract(False)
1310 else:
1311 self.isAbstract(True)
1312 xmlelement.removeAttribute(at)
1313 elif at == "ValueRank":
1314 self.valueRank(int(av))
1315 if self.valueRank() != -1:
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":
1319 self.dataType(opcua_referencePointer_t(str(av), parentNode=self))
1320 # dataType needs to be linked to a node once the namespace is read
1321 self.getNamespace().linkLater(self.dataType())
1322 else:
1323 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
1324
1325 for x in xmlelement.childNodes:
1326 if x.nodeType == x.ELEMENT_NODE:
1327 if x.tagName == "Definition":
1328 self.__xmlDefinition__ = x
1329 logger.debug( "Definition stored for future processing")
1330 else:
1331 logger.info( "Unprocessable XML Element: " + x.tagName)
1332
1333 def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
1334 code = []
1335 # If we have an encodable value, try to encode that
1336 if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
1337 # Delegate the encoding of the datavalue to the helper if we have
1338 # determined a valid encoding
1339 if self.dataType().target().isEncodable():
1340 if self.value() != None:
1341 code = code + self.value().printOpen62541CCode(bootstrapping)
1342 return code
1343 if(bootstrapping):
1344 code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_alloca(sizeof(UA_Variant));")
1345 code.append("UA_Variant_init(" + self.getCodePrintableID() + "_variant);")
1346 return code
1347
1348 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1349 code = []
1350 codegen = open62541_MacroHelper()
1351
1352 if bootstrapping == False:
1353 code.append(" " + self.getCodePrintableID() + "_variant, ")
1354 code.append(" " + str(self.valueRank()) + ",")
1355 if self.isAbstract():
1356 code.append(" true,")
1357 else:
1358 code.append(" false,")
1359 return code
1360
1361 if (self.isAbstract()):
1362 code.append(self.getCodePrintableID() + "->isAbstract = true;")
1363 else:
1364 code.append(self.getCodePrintableID() + "->isAbstract = false;")
1365
1366 # The variant is guaranteed to exist by SubtypeEarly()
1367 code.append("UA_Variant_copy(" + self.getCodePrintableID() + "_variant, &" + self.getCodePrintableID() + "->value.data.value.value );")
1368 code.append(self.getCodePrintableID() + "->valueSource = UA_VALUESOURCE_DATA;")
1369 return code
1370
1372 """ opcua_node_dataType_t is a subtype of opcua_note_t describing DataType nodes.
1373
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
1382 value.
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.
1387
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.
1390
1391 A DataType shall be further deemed not encodable if it contains mixed structure/
1392 enumaration definitions.
1393
1394 If encodable, the encoding can be retrieved using getEncoding().
1395 """
1396 __isAbstract__ = False
1397 __isEnum__ = False
1398 __xmlDefinition__ = None
1399 __baseTypeEncoding__ = []
1400 __encodable__ = False
1401 __encodingBuilt__ = False
1402 __definition__ = []
1403
1405 self.nodeClass(NODE_CLASS_DATATYPE)
1406 self.__isAbstract____isAbstract__ == False
1407 self.__xmlDefinition__ = None
1409 self.__encodable____encodable__ = None
1412 self.__isEnum____isEnum__ = False
1413
1414 def isAbstract(self,data=None):
1415 """ Will return True if isAbstract was defined.
1416
1417 Calling this function with an arbitrary data parameter will set
1418 isAbstract = data.
1419 """
1420 if isinstance(data, bool):
1423
1424 def isEncodable(self):
1425 """ Will return True if buildEncoding() was able to determine which builtin
1426 type corresponds to all fields of this DataType.
1427
1428 If no encoding has been build yet, this function will call buildEncoding()
1429 and return True if it succeeds.
1430 """
1431 return self.__encodable____encodable__
1432
1433 def getEncoding(self):
1434 """ If the dataType is encodable, getEncoding() returns a nested list
1435 containing the encoding the structure definition for this type.
1436
1437 If no encoding has been build yet, this function will call buildEncoding()
1438 and return the encoding if buildEncoding() succeeds.
1439
1440 If buildEncoding() fails or has failed, an empty list will be returned.
1441 """
1442 if self.__encodable____encodable__ == False:
1443 if self.__encodingBuilt____encodingBuilt__ == False:
1444 return self.buildEncoding()
1445 return []
1446 else:
1448
1449 def buildEncoding(self, indent=0, force=False):
1450 """ buildEncoding() determines the structure and aliases used for variables
1451 of this DataType.
1452
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:
1456
1457 [['Alias1', ['Alias2', ['BuiltinType']]], [Alias2, ['BuiltinType']], ...]
1458
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.
1463
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
1467 necessary;
1468
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.
1472
1473 DataTypes can be either structures or enumeration - mixed definitions will
1474 be unencodable.
1475
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
1479 reparsed.
1480
1481 After parsing, __definition__ holds the field definition as a list. Note
1482 that this might deviate from the encoding, especially if inheritance was
1483 used.
1484 """
1485
1486 proxy = opcua_value_t(None)
1487 prefix = " " + "|"*indent+ "+"
1488
1489 if force==True:
1491
1492 if self.__encodingBuilt____encodingBuilt__ == True:
1493 if self.isEncodable():
1494 logger.debug(prefix + str(self.__baseTypeEncoding____baseTypeEncoding__) + " (already analyzed)")
1495 else:
1496 logger.debug( prefix + str(self.__baseTypeEncoding____baseTypeEncoding__) + "(already analyzed, not encodable!)")
1498 self.__encodingBuilt____encodingBuilt__ = True # signify that we have attempted to built this type
1499 self.__encodable____encodable__ = True
1500
1501 if indent==0:
1502 logger.debug("Parsing DataType " + self.browseName() + " (" + str(self.id()) + ")")
1503
1504 if proxy.isBuiltinByString(self.browseName()):
1506 self.__encodable____encodable__ = True
1507 logger.debug( prefix + self.browseName() + "*")
1508 logger.debug("Encodable as: " + str(self.__baseTypeEncoding____baseTypeEncoding__))
1509 logger.debug("")
1511
1512 if self.__xmlDefinition__ == None:
1513 # Check if there is a supertype available
1514 for ref in self.getReferences():
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())
1518 subenc = ref.target().buildEncoding(indent=indent+1)
1519 if not ref.target().isEncodable():
1520 self.__encodable____encodable__ = False
1521 break
1522 else:
1525 logger.debug(prefix + "No viable definition for " + self.browseName() + " " + str(self.id()) + " found.")
1526 self.__encodable____encodable__ = False
1527
1528 if indent==0:
1529 if not self.__encodable____encodable__:
1530 logger.debug("Not encodable (partial): " + str(self.__baseTypeEncoding____baseTypeEncoding__))
1531 else:
1532 logger.debug("Encodable as: " + str(self.__baseTypeEncoding____baseTypeEncoding__))
1533 logger.debug( "")
1534
1536
1537 isEnum = True
1538 isSubType = True
1539 hasValueRank = 0
1540
1541 # We need to store the definition as ordered data, but can't use orderedDict
1542 # for backward compatibility with Python 2.6 and 3.4
1543 enumDict = []
1544 typeDict = []
1545
1546 # An XML Definition is provided and will be parsed... now
1547 for x in self.__xmlDefinition__.childNodes:
1548 if x.nodeType == x.ELEMENT_NODE:
1549 fname = ""
1550 fdtype = ""
1551 enumVal = ""
1552 hasValueRank = 0
1553 for at,av in x.attributes.items():
1554 if at == "DataType":
1555 fdtype = str(av)
1556 isEnum = False
1557 elif at == "Name":
1558 fname = str(av)
1559 elif at == "Value":
1560 enumVal = int(av)
1561 isSubType = False
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.")
1565 else:
1566 logger.warn("Unknown Field Attribute " + str(at))
1567 # This can either be an enumeration OR a structure, not both.
1568 # Figure out which of the dictionaries gets the newly read value pair
1569 if isEnum == isSubType:
1570 # This is an error
1571 logger.warn("DataType contains both enumeration and subtype (or neither)")
1572 self.__encodable____encodable__ = False
1573 break
1574 elif isEnum:
1575 # This is an enumeration
1576 enumDict.append((fname, enumVal))
1577 continue
1578 else:
1579 # This might be a subtype... follow the node defined as datatype to find out
1580 # what encoding to use
1581 dtnode = self.getNamespace().getNodeByIDString(fdtype)
1582 if dtnode == None:
1583 # Node found in datatype element is invalid
1584 logger.debug( prefix + fname + " ?? " + av + " ??")
1585 self.__encodable____encodable__ = False
1586 else:
1587 # The node in the datatype element was found. we inherit its encoding,
1588 # but must still ensure that the dtnode is itself validly encodable
1589 typeDict.append([fname, dtnode])
1590 if hasValueRank < 0:
1591 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():
1597 # If we inherit an encoding from an unencodable not, this node is
1598 # also not encodable
1599 self.__encodable____encodable__ = False
1600 break
1601
1602 # If we used inheritance to determine an encoding without alias, there is a
1603 # the possibility that lists got double-nested despite of only one element
1604 # being encoded, such as [['Int32']] or [['alias',['int32']]]. Remove that
1605 # enclosing list.
1606 while len(self.__baseTypeEncoding____baseTypeEncoding__) == 1 and isinstance(self.__baseTypeEncoding____baseTypeEncoding__[0], list):
1608
1609 if isEnum == True:
1611 self.__definition____definition__ = enumDict
1612 self.__isEnum____isEnum__ = True
1613 logger.debug( prefix+"Int32* -> enumeration with dictionary " + str(enumDict) + " encodable " + str(self.__encodable____encodable__))
1615
1616 if indent==0:
1617 if not self.__encodable____encodable__:
1618 logger.debug( "Not encodable (partial): " + str(self.__baseTypeEncoding____baseTypeEncoding__))
1619 else:
1620 logger.debug( "Encodable as: " + str(self.__baseTypeEncoding____baseTypeEncoding__))
1621 self.__isEnum____isEnum__ = False
1622 self.__definition____definition__ = typeDict
1623 logger.debug( "")
1625
1626 def parseXMLSubType(self, xmlelement):
1627 """ Parses all XML data that is not considered part of the base node attributes.
1628
1629 XML attributes fields processed are "isAbstract"
1630 XML elements processed are "Definition"
1631 """
1632 for (at, av) in xmlelement.attributes.items():
1633 if at == "IsAbstract":
1634 if "true" in str(av).lower():
1635 self.isAbstract(True)
1636 else:
1637 self.isAbstract(False)
1638 xmlelement.removeAttribute(at)
1639 else:
1640 logger.warn("Don't know how to process attribute " + at + " (" + av + ")")
1641
1642 for x in xmlelement.childNodes:
1643 if x.nodeType == x.ELEMENT_NODE:
1644 if x.tagName == "Definition":
1645 self.__xmlDefinition__ = x
1646 #logger.debug( "Definition stored for future processing")
1647 else:
1648 logger.warn( "Unprocessable XML Element: " + x.tagName)
1649
1650 def encodedTypeId(self):
1651 """ Returns a number of the builtin Type that should be used
1652 to represent this datatype.
1653 """
1654 if self.isEncodable() != True or len(self.getEncoding()) == 0:
1655 # Encoding is []
1656 return 0
1657 else:
1658 enc = self.getEncoding()
1659 if len(enc) > 1 and isinstance(enc[0], list):
1660 # [ [?], [?], [?] ]
1661 # Encoding is a list representing an extensionobject
1662 return opcua_BuiltinType_extensionObject_t(None).getNumericRepresentation()
1663 else:
1664 if len(enc)==1 and isinstance(enc[0], str):
1665 # [ 'BuiltinType' ]
1666 return opcua_value_t(None).getTypeByString(enc[0]).getNumericRepresentation()
1667 else:
1668 # [ ['Alias', [?]] ]
1669 # Determine if [?] is reducable to a builtin type or if [?] is an aliased
1670 # extensionobject
1671 while len(enc) > 1 and isinstance(enc[0], str):
1672 enc = enc[1]
1673 if len(enc) > 1:
1674 return opcua_BuiltinType_extensionObject_t(None).getNumericRepresentation()
1675 else:
1676 return opcua_value_t(None).getTypeByString(enc[0]).getNumericRepresentation()
1677
1678 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1679 code = []
1680 codegen = open62541_MacroHelper()
1681
1682 # Detect if this is bootstrapping or if we are attempting to use userspace...
1683 if bootstrapping == False:
1684 typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
1685 myTypeRef = None
1686 for ref in self.getReferences():
1687 if ref.referenceType() in typeDefs:
1688 myTypeRef = ref
1689 break
1690 if myTypeRef==None:
1691 for ref in self.getReferences():
1692 if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
1693 myTypeRef = ref
1694 break
1695 if myTypeRef==None:
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),")
1699 else:
1700 code.append(" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
1701 while myTypeRef in unPrintedReferences:
1702 unPrintedReferences.remove(myTypeRef)
1703
1704 if (self.isAbstract()):
1705 code.append(" true,")
1706 else:
1707 code.append(" false,")
1708 return code
1709
1710 if (self.isAbstract()):
1711 code.append(self.getCodePrintableID() + "->isAbstract = true;")
1712 else:
1713 code.append(self.getCodePrintableID() + "->isAbstract = false;")
1714 return code
1715
1717 __containsNoLoops__ = True
1718 __eventNotifier__ = 0
1719
1721 self.nodeClass(NODE_CLASS_VIEW)
1724
1725 def containsNoLoops(self,data=None):
1726 if isinstance(data, bool):
1729
1730 def eventNotifier(self,data=None):
1731 if isinstance(data, int):
1734
1735 def parseXMLSubtype(self, xmlelement):
1736 for (at, av) in xmlelement.attributes.items():
1737 logger.error("Don't know how to process attribute " + at + " (" + av + ")")
1738
1739 for x in xmlelement.childNodes:
1740 if x.nodeType == x.ELEMENT_NODE:
1741 logger.info( "Unprocessable XML Element: " + x.tagName)
1742
1743 def printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping = True):
1744 code = []
1745 codegen = open62541_MacroHelper()
1746
1747 # Detect if this is bootstrapping or if we are attempting to use userspace...
1748 if bootstrapping == False:
1749 typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
1750 myTypeRef = None
1751 for ref in self.getReferences():
1752 if ref.referenceType() in typeDefs:
1753 myTypeRef = ref
1754 break
1755 if myTypeRef==None:
1756 for ref in self.getReferences():
1757 if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
1758 myTypeRef = ref
1759 break
1760 if myTypeRef==None:
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),")
1764 else:
1765 code.append(" " + codegen.getCreateExpandedNodeIDMacro(myTypeRef.target()) + ",")
1766 while myTypeRef in unPrintedReferences:
1767 unPrintedReferences.remove(myTypeRef)
1768
1769 code.append(" // FIXME: Missing eventNotifier")
1770 code.append(" // FIXME: Missing containsNoLoops")
1771 return code
1772
1773 if self.containsNoLoops():
1774 code.append(self.getCodePrintableID() + "->containsNoLoops = true;")
1775 else:
1776 code.append(self.getCodePrintableID() + "->containsNoLoops = false;")
1777
1778 code.append(self.getCodePrintableID() + "->eventNotifier = (UA_Byte) " + str(self.eventNotifier()) + ";")
1779
1780 return code
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
buildEncoding(self, indent=0, force=False)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_SubtypeEarly(self, bootstrapping=True)
hasInverseReferenceTarget(self, node)
removeReferenceToNode(self, targetNode)
addInverseReferenceTarget(self, node)
parseXMLSubType(self, xmlelement)
displayName(self, data=None)
description(self, data=None)
parseXML(self, xmlelement)
printOpen62541CCode(self, unPrintedNodes=[], unPrintedReferences=[], supressGenerationOfAttribute=[])
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
userWriteMask(self, data=None)
initiateDummyXMLReferences(self, xmlelement)
printOpen62541CCode_SubtypeEarly(self, bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
printOpen62541CCode_SubtypeEarly(self, bootstrapping=True)
printOpen62541CCode_Subtype(self, unPrintedReferences=[], bootstrapping=True)
References are not really described by OPC-UA.
__init__(self, target, hidden=False, parentNode=None)
getNextElementNode(xmlvalue)
#define str(a)