nodes.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ###
  4. ### Author: Chris Iatrou (ichrispa@core-vector.net)
  5. ### Version: rev 13
  6. ###
  7. ### This program was created for educational purposes and has been
  8. ### contributed to the open62541 project by the author. All licensing
  9. ### terms for this source is inherited by the terms and conditions
  10. ### specified for by the open62541 project (see the projects readme
  11. ### file for more information on the LGPL terms and restrictions).
  12. ###
  13. ### This program is not meant to be used in a production environment. The
  14. ### author is not liable for any complications arising due to the use of
  15. ### this program.
  16. ###
  17. import sys
  18. import logging
  19. from datatypes import *
  20. __all__ = ['Reference', 'RefOrAlias', 'Node', 'ReferenceTypeNode',
  21. 'ObjectNode', 'VariableNode', 'VariableTypeNode',
  22. 'MethodNode', 'ObjectTypeNode', 'DataTypeNode', 'ViewNode']
  23. logger = logging.getLogger(__name__)
  24. if sys.version_info[0] >= 3:
  25. # strings are already parsed to unicode
  26. def unicode(s):
  27. return s
  28. class Reference(object):
  29. # all either nodeids or strings with an alias
  30. def __init__(self, source, referenceType, target, isForward):
  31. self.source = source
  32. self.referenceType = referenceType
  33. self.target = target
  34. self.isForward = isForward
  35. def __str__(self):
  36. retval = str(self.source)
  37. if not self.isForward:
  38. retval = retval + "<"
  39. retval = retval + "--[" + str(self.referenceType) + "]--"
  40. if self.isForward:
  41. retval = retval + ">"
  42. return retval + str(self.target)
  43. def __repr__(self):
  44. return str(self)
  45. def __eq__(self, other):
  46. return str(self) == str(other)
  47. def __ne__(self, other):
  48. return not self.__eq__(other)
  49. def __hash__(self):
  50. return hash(str(self))
  51. def RefOrAlias(s):
  52. try:
  53. return NodeId(s)
  54. except Exception:
  55. return s
  56. class Node(object):
  57. def __init__(self):
  58. self.id = None
  59. self.browseName = None
  60. self.displayName = None
  61. self.description = None
  62. self.symbolicName = None
  63. self.writeMask = None
  64. self.userWriteMask = None
  65. self.references = set()
  66. self.hidden = False
  67. self.modelUri = None
  68. self.parent = None
  69. self.parentReference = None
  70. def __str__(self):
  71. return self.__class__.__name__ + "(" + str(self.id) + ")"
  72. def __repr__(self):
  73. return str(self)
  74. def sanitize(self):
  75. pass
  76. def parseXML(self, xmlelement):
  77. for idname in ['NodeId', 'NodeID', 'nodeid']:
  78. if xmlelement.hasAttribute(idname):
  79. self.id = RefOrAlias(xmlelement.getAttribute(idname))
  80. for (at, av) in xmlelement.attributes.items():
  81. if at == "BrowseName":
  82. self.browseName = QualifiedName(av)
  83. elif at == "DisplayName":
  84. self.displayName = LocalizedText(av)
  85. elif at == "Description":
  86. self.description = LocalizedText(av)
  87. elif at == "WriteMask":
  88. self.writeMask = int(av)
  89. elif at == "UserWriteMask":
  90. self.userWriteMask = int(av)
  91. elif at == "EventNotifier":
  92. self.eventNotifier = int(av)
  93. elif at == "SymbolicName":
  94. self.symbolicName = String(av)
  95. for x in xmlelement.childNodes:
  96. if x.nodeType != x.ELEMENT_NODE:
  97. continue
  98. if x.firstChild:
  99. if x.localName == "BrowseName":
  100. self.browseName = QualifiedName(x.firstChild.data)
  101. elif x.localName == "DisplayName":
  102. self.displayName = LocalizedText(x.firstChild.data)
  103. elif x.localName == "Description":
  104. self.description = LocalizedText(x.firstChild.data)
  105. elif x.localName == "WriteMask":
  106. self.writeMask = int(unicode(x.firstChild.data))
  107. elif x.localName == "UserWriteMask":
  108. self.userWriteMask = int(unicode(x.firstChild.data))
  109. if x.localName == "References":
  110. self.parseXMLReferences(x)
  111. def parseXMLReferences(self, xmlelement):
  112. for ref in xmlelement.childNodes:
  113. if ref.nodeType != ref.ELEMENT_NODE:
  114. continue
  115. source = RefOrAlias(str(self.id)) # deep-copy of the nodeid
  116. target = RefOrAlias(ref.firstChild.data)
  117. reftype = None
  118. forward = True
  119. for (at, av) in ref.attributes.items():
  120. if at == "ReferenceType":
  121. reftype = RefOrAlias(av)
  122. elif at == "IsForward":
  123. forward = not "false" in av.lower()
  124. self.references.add(Reference(source, reftype, target, forward))
  125. def getParentReference(self, parentreftypes):
  126. # HasSubtype has precedence
  127. for ref in self.references:
  128. if ref.referenceType == NodeId("ns=0;i=45") and not ref.isForward:
  129. return ref
  130. for ref in self.references:
  131. if ref.referenceType in parentreftypes and not ref.isForward:
  132. return ref
  133. return None
  134. def popTypeDef(self):
  135. for ref in self.references:
  136. if ref.referenceType.i == 40 and ref.isForward:
  137. self.references.remove(ref)
  138. return ref
  139. return Reference(NodeId(), NodeId(), NodeId(), False)
  140. def replaceAliases(self, aliases):
  141. if str(self.id) in aliases:
  142. self.id = NodeId(aliases[self.id])
  143. if isinstance(self, VariableNode) or isinstance(self, VariableTypeNode):
  144. if str(self.dataType) in aliases:
  145. self.dataType = NodeId(aliases[self.dataType])
  146. new_refs = set()
  147. for ref in self.references:
  148. if str(ref.source) in aliases:
  149. ref.source = NodeId(aliases[ref.source])
  150. if str(ref.target) in aliases:
  151. ref.target = NodeId(aliases[ref.target])
  152. if str(ref.referenceType) in aliases:
  153. ref.referenceType = NodeId(aliases[ref.referenceType])
  154. new_refs.add(ref)
  155. self.references = new_refs
  156. def replaceNamespaces(self, nsMapping):
  157. self.id.ns = nsMapping[self.id.ns]
  158. self.browseName.ns = nsMapping[self.browseName.ns]
  159. if hasattr(self, 'dataType') and isinstance(self.dataType, NodeId):
  160. self.dataType.ns = nsMapping[self.dataType.ns]
  161. new_refs = set()
  162. for ref in self.references:
  163. ref.source.ns = nsMapping[ref.source.ns]
  164. ref.target.ns = nsMapping[ref.target.ns]
  165. ref.referenceType.ns = nsMapping[ref.referenceType.ns]
  166. new_refs.add(ref)
  167. self.references = new_refs
  168. class ReferenceTypeNode(Node):
  169. def __init__(self, xmlelement=None):
  170. Node.__init__(self)
  171. self.isAbstract = False
  172. self.symmetric = False
  173. self.inverseName = ""
  174. if xmlelement:
  175. ReferenceTypeNode.parseXML(self, xmlelement)
  176. def parseXML(self, xmlelement):
  177. Node.parseXML(self, xmlelement)
  178. for (at, av) in xmlelement.attributes.items():
  179. if at == "Symmetric":
  180. self.symmetric = "false" not in av.lower()
  181. elif at == "InverseName":
  182. self.inverseName = str(av)
  183. elif at == "IsAbstract":
  184. self.isAbstract = "false" not in av.lower()
  185. for x in xmlelement.childNodes:
  186. if x.nodeType == x.ELEMENT_NODE:
  187. if x.localName == "InverseName" and x.firstChild:
  188. self.inverseName = str(unicode(x.firstChild.data))
  189. class ObjectNode(Node):
  190. def __init__(self, xmlelement=None):
  191. Node.__init__(self)
  192. self.eventNotifier = 0
  193. if xmlelement:
  194. ObjectNode.parseXML(self, xmlelement)
  195. def parseXML(self, xmlelement):
  196. Node.parseXML(self, xmlelement)
  197. for (at, av) in xmlelement.attributes.items():
  198. if at == "EventNotifier":
  199. self.eventNotifier = int(av)
  200. class VariableNode(Node):
  201. def __init__(self, xmlelement=None):
  202. Node.__init__(self)
  203. self.dataType = None
  204. self.valueRank = None
  205. self.arrayDimensions = []
  206. # Set access levels to read by default
  207. self.accessLevel = 1
  208. self.userAccessLevel = 1
  209. self.minimumSamplingInterval = 0.0
  210. self.historizing = False
  211. self.value = None
  212. self.xmlValueDef = None
  213. if xmlelement:
  214. VariableNode.parseXML(self, xmlelement)
  215. def parseXML(self, xmlelement):
  216. Node.parseXML(self, xmlelement)
  217. for (at, av) in xmlelement.attributes.items():
  218. if at == "ValueRank":
  219. self.valueRank = int(av)
  220. elif at == "AccessLevel":
  221. self.accessLevel = int(av)
  222. elif at == "UserAccessLevel":
  223. self.userAccessLevel = int(av)
  224. elif at == "MinimumSamplingInterval":
  225. self.minimumSamplingInterval = float(av)
  226. elif at == "DataType":
  227. self.dataType = RefOrAlias(av)
  228. elif at == "ArrayDimensions":
  229. self.arrayDimensions = av.split(",")
  230. for x in xmlelement.childNodes:
  231. if x.nodeType != x.ELEMENT_NODE:
  232. continue
  233. if x.localName == "Value":
  234. self.xmlValueDef = x
  235. elif x.localName == "DataType":
  236. self.dataType = RefOrAlias(av)
  237. elif x.localName == "ValueRank":
  238. self.valueRank = int(unicode(x.firstChild.data))
  239. elif x.localName == "ArrayDimensions" and len(self.arrayDimensions) == 0:
  240. elements = x.getElementsByTagName("ListOfUInt32");
  241. if len(elements):
  242. for idx, v in enumerate(elements[0].getElementsByTagName("UInt32")):
  243. self.arrayDimensions.append(v.firstChild.data)
  244. elif x.localName == "AccessLevel":
  245. self.accessLevel = int(unicode(x.firstChild.data))
  246. elif x.localName == "UserAccessLevel":
  247. self.userAccessLevel = int(unicode(x.firstChild.data))
  248. elif x.localName == "MinimumSamplingInterval":
  249. self.minimumSamplingInterval = float(unicode(x.firstChild.data))
  250. elif x.localName == "Historizing":
  251. self.historizing = "false" not in x.lower()
  252. def allocateValue(self, nodeset):
  253. dataTypeNode = nodeset.getDataTypeNode(self.dataType)
  254. if dataTypeNode is None:
  255. return False
  256. # FIXME: Don't build at all or allocate "defaults"? I'm for not building at all.
  257. if self.xmlValueDef is None:
  258. #logger.warn("Variable " + self.browseName() + "/" + str(self.id()) + " is not initialized. No memory will be allocated.")
  259. return False
  260. self.value = Value()
  261. self.value.parseXMLEncoding(self.xmlValueDef, dataTypeNode, self, nodeset.namespaceMapping[self.modelUri])
  262. return True
  263. class VariableTypeNode(VariableNode):
  264. def __init__(self, xmlelement=None):
  265. VariableNode.__init__(self)
  266. self.isAbstract = False
  267. if xmlelement:
  268. VariableTypeNode.parseXML(self, xmlelement)
  269. def parseXML(self, xmlelement):
  270. VariableNode.parseXML(self, xmlelement)
  271. for (at, av) in xmlelement.attributes.items():
  272. if at == "IsAbstract":
  273. self.isAbstract = "false" not in av.lower()
  274. for x in xmlelement.childNodes:
  275. if x.nodeType != x.ELEMENT_NODE:
  276. continue
  277. if x.localName == "IsAbstract":
  278. self.isAbstract = "false" not in av.lower()
  279. class MethodNode(Node):
  280. def __init__(self, xmlelement=None):
  281. Node.__init__(self)
  282. self.executable = True
  283. self.userExecutable = True
  284. self.methodDecalaration = None
  285. if xmlelement:
  286. MethodNode.parseXML(self, xmlelement)
  287. def parseXML(self, xmlelement):
  288. Node.parseXML(self, xmlelement)
  289. for (at, av) in xmlelement.attributes.items():
  290. if at == "Executable":
  291. self.executable = "false" not in av.lower()
  292. if at == "UserExecutable":
  293. self.userExecutable = "false" not in av.lower()
  294. if at == "MethodDeclarationId":
  295. self.methodDeclaration = str(av)
  296. class ObjectTypeNode(Node):
  297. def __init__(self, xmlelement=None):
  298. Node.__init__(self)
  299. self.isAbstract = False
  300. if xmlelement:
  301. ObjectTypeNode.parseXML(self, xmlelement)
  302. def parseXML(self, xmlelement):
  303. Node.parseXML(self, xmlelement)
  304. for (at, av) in xmlelement.attributes.items():
  305. if at == "IsAbstract":
  306. self.isAbstract = "false" not in av.lower()
  307. class DataTypeNode(Node):
  308. """ DataTypeNode is a subtype of Node describing DataType nodes.
  309. DataType contain definitions and structure information usable for Variables.
  310. The format of this structure is determined by buildEncoding()
  311. Two definition styles are distinguished in XML:
  312. 1) A DataType can be a structure of fields, each field having a name and a type.
  313. The type must be either an encodable builtin node (ex. UInt32) or point to
  314. another DataType node that inherits its encoding from a builtin type using
  315. a inverse "hasSubtype" (hasSuperType) reference.
  316. 2) A DataType may be an enumeration, in which each field has a name and a numeric
  317. value.
  318. The definition is stored as an ordered list of tuples. Depending on which
  319. definition style was used, the __definition__ will hold
  320. 1) A list of ("Fieldname", Node) tuples.
  321. 2) A list of ("Fieldname", int) tuples.
  322. A DataType (and in consequence all Variables using it) shall be deemed not
  323. encodable if any of its fields cannot be traced to an encodable builtin type.
  324. A DataType shall be further deemed not encodable if it contains mixed structure/
  325. enumaration definitions.
  326. If encodable, the encoding can be retrieved using getEncoding().
  327. """
  328. def __init__(self, xmlelement=None):
  329. Node.__init__(self)
  330. self.isAbstract = False
  331. self.__xmlDefinition__ = None
  332. self.__baseTypeEncoding__ = []
  333. self.__encodable__ = None
  334. self.__definition__ = []
  335. self.__isEnum__ = False
  336. self.__isOptionSet__ = False
  337. if xmlelement:
  338. DataTypeNode.parseXML(self, xmlelement)
  339. def parseXML(self, xmlelement):
  340. Node.parseXML(self, xmlelement)
  341. for (at, av) in xmlelement.attributes.items():
  342. if at == "IsAbstract":
  343. self.isAbstract = "false" not in av.lower()
  344. for x in xmlelement.childNodes:
  345. if x.nodeType == x.ELEMENT_NODE:
  346. if x.localName == "Definition":
  347. self.__xmlDefinition__ = x
  348. def isEncodable(self):
  349. """ Will return True if buildEncoding() was able to determine which builtin
  350. type corresponds to all fields of this DataType.
  351. If no encoding has been build yet an exception will be thrown.
  352. Make sure to call buildEncoding() first.
  353. """
  354. if self.__encodable__ is None:
  355. raise Exception("Encoding needs to be built first using buildEncoding()")
  356. return self.__encodable__
  357. def getEncoding(self):
  358. """ If the dataType is encodable, getEncoding() returns a nested list
  359. containing the encoding the structure definition for this type.
  360. If no encoding has been build yet an exception will be thrown.
  361. Make sure to call buildEncoding() first.
  362. If buildEncoding() has failed, an empty list will be returned.
  363. """
  364. if self.__encodable__ is None:
  365. raise Exception("Encoding needs to be built first using buildEncoding()")
  366. if not self.__encodable__:
  367. return []
  368. else:
  369. return self.__baseTypeEncoding__
  370. def buildEncoding(self, nodeset, indent=0, force=False):
  371. """ buildEncoding() determines the structure and aliases used for variables
  372. of this DataType.
  373. The function will parse the XML <Definition> of the dataType and extract
  374. "Name"-"Type" tuples. If successful, buildEncoding will return a nested
  375. list of the following format:
  376. [['Alias1', ['Alias2', ['BuiltinType']]], [Alias2, ['BuiltinType']], ...]
  377. Aliases are fieldnames defined by this DataType or DataTypes referenced. A
  378. list such as ['DataPoint', ['Int32']] indicates that a value will encode
  379. an Int32 with the alias 'DataPoint' such as <DataPoint>12827</DataPoint>.
  380. Only the first Alias of a nested list is considered valid for the BuiltinType.
  381. Single-Elemented lists are always BuiltinTypes. Every nested list must
  382. converge in a builtin type to be encodable. buildEncoding will follow
  383. the first type inheritance reference (hasSupertype) of the dataType if
  384. necessary;
  385. If instead to "DataType" a numeric "Value" attribute is encountered,
  386. the DataType will be considered an enumeration and all Variables using
  387. it will be encoded as Int32.
  388. DataTypes can be either structures or enumeration - mixed definitions will
  389. be unencodable.
  390. Calls to getEncoding() will be iterative. buildEncoding() can be called
  391. only once per dataType, with all following calls returning the predetermined
  392. value. Use of the 'force=True' parameter will force the Definition to be
  393. reparsed.
  394. After parsing, __definition__ holds the field definition as a list. Note
  395. that this might deviate from the encoding, especially if inheritance was
  396. used.
  397. """
  398. prefix = " " + "|" * indent + "+"
  399. if force==True:
  400. self.__encodable__ = None
  401. if self.__encodable__ is not None and self.__encodable__:
  402. if self.isEncodable():
  403. logger.debug(prefix + str(self.__baseTypeEncoding__) + " (already analyzed)")
  404. else:
  405. logger.debug( prefix + str(self.__baseTypeEncoding__) + "(already analyzed, not encodable!)")
  406. return self.__baseTypeEncoding__
  407. self.__encodable__ = True
  408. if indent==0:
  409. logger.debug("Parsing DataType " + str(self.browseName) + " (" + str(self.id) + ")")
  410. if valueIsInternalType(self.browseName.name):
  411. self.__baseTypeEncoding__ = [self.browseName.name]
  412. self.__encodable__ = True
  413. logger.debug( prefix + str(self.browseName) + "*")
  414. logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
  415. logger.debug("")
  416. return self.__baseTypeEncoding__
  417. # Check if there is a supertype available
  418. parentType = None
  419. for ref in self.references:
  420. if ref.isForward:
  421. continue
  422. # hasSubtype
  423. if ref.referenceType.i == 45:
  424. targetNode = nodeset.nodes[ref.target]
  425. if targetNode is not None and isinstance(targetNode, DataTypeNode):
  426. parentType = targetNode
  427. break
  428. if self.__xmlDefinition__ is None:
  429. if parentType is not None:
  430. logger.debug( prefix + "Attempting definition using supertype " + str(targetNode.browseName) + " for DataType " + " " + str(self.browseName))
  431. subenc = targetNode.buildEncoding(nodeset=nodeset, indent=indent+1)
  432. if not targetNode.isEncodable():
  433. self.__encodable__ = False
  434. else:
  435. self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [self.browseName.name, subenc, None]
  436. if len(self.__baseTypeEncoding__) == 0:
  437. logger.debug(prefix + "No viable definition for " + str(self.browseName) + " " + str(self.id) + " found.")
  438. self.__encodable__ = False
  439. if indent==0:
  440. if not self.__encodable__:
  441. logger.debug("Not encodable (partial): " + str(self.__baseTypeEncoding__))
  442. else:
  443. logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
  444. logger.debug( "")
  445. return self.__baseTypeEncoding__
  446. isEnum = False
  447. # An option set is at the same time also an enum, at least for the encoding below
  448. isOptionSet = parentType is not None and parentType.id.ns == 0 and parentType.id.i==12755
  449. # We need to store the definition as ordered data, but can't use orderedDict
  450. # for backward compatibility with Python 2.6 and 3.4
  451. enumDict = []
  452. typeDict = []
  453. # An XML Definition is provided and will be parsed... now
  454. for x in self.__xmlDefinition__.childNodes:
  455. if x.nodeType == x.ELEMENT_NODE:
  456. fname = ""
  457. fdtype = ""
  458. enumVal = ""
  459. valueRank = None
  460. #symbolicName = None
  461. for at,av in x.attributes.items():
  462. if at == "DataType":
  463. fdtype = str(av)
  464. if fdtype in nodeset.aliases:
  465. fdtype = nodeset.aliases[fdtype]
  466. elif at == "Name":
  467. fname = str(av)
  468. elif at == "SymbolicName":
  469. # ignore
  470. continue
  471. # symbolicName = str(av)
  472. elif at == "Value":
  473. enumVal = int(av)
  474. isEnum = True
  475. elif at == "ValueRank":
  476. valueRank = int(av)
  477. else:
  478. logger.warn("Unknown Field Attribute " + str(at))
  479. # This can either be an enumeration OR a structure, not both.
  480. # Figure out which of the dictionaries gets the newly read value pair
  481. if isEnum:
  482. # This is an enumeration
  483. enumDict.append((fname, enumVal))
  484. continue
  485. else:
  486. if fdtype == "":
  487. # If no datatype given use base datatype
  488. fdtype = "i=24"
  489. # This might be a subtype... follow the node defined as datatype to find out
  490. # what encoding to use
  491. fdTypeNodeId = NodeId(fdtype)
  492. fdTypeNodeId.ns = nodeset.namespaceMapping[self.modelUri][fdTypeNodeId.ns]
  493. if not fdTypeNodeId in nodeset.nodes:
  494. raise Exception("Node {} not found in nodeset".format(fdTypeNodeId))
  495. dtnode = nodeset.nodes[fdTypeNodeId]
  496. # The node in the datatype element was found. we inherit its encoding,
  497. # but must still ensure that the dtnode is itself validly encodable
  498. typeDict.append([fname, dtnode])
  499. fdtype = str(dtnode.browseName.name)
  500. logger.debug( prefix + fname + " : " + fdtype + " -> " + str(dtnode.id))
  501. subenc = dtnode.buildEncoding(nodeset=nodeset, indent=indent+1)
  502. self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc, valueRank]]
  503. if not dtnode.isEncodable():
  504. # If we inherit an encoding from an unencodable node, this node is
  505. # also not encodable
  506. self.__encodable__ = False
  507. break
  508. # If we used inheritance to determine an encoding without alias, there is a
  509. # the possibility that lists got double-nested despite of only one element
  510. # being encoded, such as [['Int32']] or [['alias',['int32']]]. Remove that
  511. # enclosing list.
  512. while len(self.__baseTypeEncoding__) == 1 and isinstance(self.__baseTypeEncoding__[0], list):
  513. self.__baseTypeEncoding__ = self.__baseTypeEncoding__[0]
  514. if isOptionSet == True:
  515. self.__isOptionSet__ = True
  516. subenc = parentType.buildEncoding(nodeset=nodeset)
  517. if not parentType.isEncodable():
  518. self.__encodable__ = False
  519. else:
  520. self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [self.browseName.name, subenc, None]
  521. self.__definition__ = enumDict
  522. return self.__baseTypeEncoding__
  523. if isEnum == True:
  524. self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + ['Int32']
  525. self.__definition__ = enumDict
  526. self.__isEnum__ = True
  527. logger.debug( prefix+"Int32* -> enumeration with dictionary " + str(enumDict) + " encodable " + str(self.__encodable__))
  528. return self.__baseTypeEncoding__
  529. if indent==0:
  530. if not self.__encodable__:
  531. logger.debug( "Not encodable (partial): " + str(self.__baseTypeEncoding__))
  532. else:
  533. logger.debug( "Encodable as: " + str(self.__baseTypeEncoding__))
  534. self.__isEnum__ = False
  535. self.__definition__ = typeDict
  536. logger.debug( "")
  537. return self.__baseTypeEncoding__
  538. class ViewNode(Node):
  539. def __init__(self, xmlelement=None):
  540. Node.__init__(self)
  541. self.containsNoLoops = False
  542. self.eventNotifier = False
  543. if xmlelement:
  544. ViewNode.parseXML(self, xmlelement)
  545. def parseXML(self, xmlelement):
  546. Node.parseXML(self, xmlelement)
  547. for (at, av) in xmlelement.attributes.items():
  548. if at == "ContainsNoLoops":
  549. self.containsNoLoops = "false" not in av.lower()
  550. if at == "EventNotifier":
  551. self.eventNotifier = "false" not in av.lower()