datatypes.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  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 datetime import datetime
  20. import xml.dom.minidom as dom
  21. from base64 import *
  22. __all__ = ['valueIsInternalType', 'Value', 'Boolean', 'Number', 'Integer',
  23. 'UInteger', 'Byte', 'SByte',
  24. 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64', 'Float', 'Double',
  25. 'String', 'XmlElement', 'ByteString', 'ExtensionObject', 'LocalizedText',
  26. 'NodeId', 'ExpandedNodeId', 'DateTime', 'QualifiedName', 'StatusCode',
  27. 'DiagnosticInfo', 'Guid']
  28. logger = logging.getLogger(__name__)
  29. if sys.version_info[0] >= 3:
  30. # strings are already parsed to unicode
  31. def unicode(s):
  32. return s
  33. string_types = str
  34. else:
  35. string_types = basestring
  36. def getNextElementNode(xmlvalue):
  37. if xmlvalue is None:
  38. return None
  39. xmlvalue = xmlvalue.nextSibling
  40. while not xmlvalue is None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
  41. xmlvalue = xmlvalue.nextSibling
  42. return xmlvalue
  43. def valueIsInternalType(valueTypeString):
  44. return valueTypeString.lower() in ['boolean', 'number', 'int32', 'uint32', 'int16', 'uint16',
  45. 'int64', 'uint64', 'byte', 'sbyte', 'float', 'double',
  46. 'string', 'bytestring', 'localizedtext', 'statuscode',
  47. 'diagnosticinfo', 'nodeid', 'guid', 'datetime',
  48. 'qualifiedname', 'expandednodeid', 'xmlelement', 'integer', 'uinteger']
  49. class Value(object):
  50. def __init__(self):
  51. self.value = None
  52. self.alias = None
  53. self.dataType = None
  54. self.encodingRule = []
  55. self.isInternal = False
  56. self.valueRank = None
  57. def getValueFieldByAlias(self, fieldname):
  58. if not isinstance(self.value, list):
  59. return None
  60. if not isinstance(self.value[0], Value):
  61. return None
  62. for val in self.value:
  63. if val.alias() == fieldname:
  64. return val.value
  65. return None
  66. def getTypeByString(self, stringName, encodingRule):
  67. stringName = str(stringName.lower())
  68. if stringName == 'boolean':
  69. t = Boolean()
  70. elif stringName == 'number':
  71. t = Number()
  72. elif stringName == 'integer':
  73. t = Integer()
  74. elif stringName == 'uinteger':
  75. t = UInteger()
  76. elif stringName == 'int32':
  77. t = Int32()
  78. elif stringName == 'uint32':
  79. t = UInt32()
  80. elif stringName == 'int16':
  81. t = Int16()
  82. elif stringName == 'uint16':
  83. t = UInt16()
  84. elif stringName == 'int64':
  85. t = Int64()
  86. elif stringName == 'uint64':
  87. t = UInt64()
  88. elif stringName == 'byte':
  89. t = Byte()
  90. elif stringName == 'sbyte':
  91. t = SByte()
  92. elif stringName == 'float':
  93. t = Float()
  94. elif stringName == 'double':
  95. t = Double()
  96. elif stringName == 'string':
  97. t = String()
  98. elif stringName == 'bytestring':
  99. t = ByteString()
  100. elif stringName == 'localizedtext':
  101. t = LocalizedText()
  102. elif stringName == 'statuscode':
  103. t = StatusCode()
  104. elif stringName == 'diagnosticinfo':
  105. t = DiagnosticInfo()
  106. elif stringName == 'nodeid':
  107. t = NodeId()
  108. elif stringName == 'guid':
  109. t = Guid()
  110. elif stringName == 'datetime':
  111. t = DateTime()
  112. elif stringName == 'qualifiedname':
  113. t = QualifiedName()
  114. elif stringName == 'expandednodeid':
  115. t = ExpandedNodeId()
  116. elif stringName == 'xmlelement':
  117. t = XmlElement()
  118. else:
  119. logger.debug("No class representing stringName " + stringName + " was found. Cannot create builtinType.")
  120. return None
  121. t.encodingRule = encodingRule
  122. return t
  123. def checkXML(self, xmlvalue):
  124. if xmlvalue is None or xmlvalue.nodeType != xmlvalue.ELEMENT_NODE:
  125. logger.error("Expected XML Element, but got junk...")
  126. return
  127. def parseXMLEncoding(self, xmlvalue, parentDataTypeNode, parent, namespaceMapping):
  128. self.checkXML(xmlvalue)
  129. if not "value" in xmlvalue.localName.lower():
  130. logger.error("Expected <Value> , but found " + xmlvalue.localName + \
  131. " instead. Value will not be parsed.")
  132. return
  133. if len(xmlvalue.childNodes) == 0:
  134. logger.error("Expected childnodes for value, but none were found...")
  135. return
  136. for n in xmlvalue.childNodes:
  137. if n.nodeType == n.ELEMENT_NODE:
  138. xmlvalue = n
  139. break
  140. if "ListOf" in xmlvalue.localName:
  141. self.value = []
  142. for el in xmlvalue.childNodes:
  143. if not el.nodeType == el.ELEMENT_NODE:
  144. continue
  145. val = self.__parseXMLSingleValue(el, parentDataTypeNode, parent, namespaceMapping=namespaceMapping)
  146. if val is None:
  147. self.value = []
  148. return
  149. self.value.append(val)
  150. else:
  151. self.value = [self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent, namespaceMapping=namespaceMapping)]
  152. def __parseXMLSingleValue(self, xmlvalue, parentDataTypeNode, parent, namespaceMapping, alias=None, encodingPart=None, valueRank=None):
  153. # Parse an encoding list such as enc = [[Int32], ['Duration', ['DateTime']]],
  154. # returning a possibly aliased variable or list of variables.
  155. # Keep track of aliases, as ['Duration', ['Hawaii', ['UtcTime', ['DateTime']]]]
  156. # will be of type DateTime, but tagged as <Duration>2013-04-10 12:00 UTC</Duration>,
  157. # and not as <Duration><Hawaii><UtcTime><String>2013-04-10 12:00 UTC</String>...
  158. # Encoding may be partially handed down (iterative call). Only resort to
  159. # type definition if we are not given a specific encoding to match
  160. if encodingPart is None:
  161. enc = parentDataTypeNode.getEncoding()
  162. else:
  163. enc = encodingPart
  164. # Check the structure of the encoding list to determine if a type is to be
  165. # returned or we need to descend further checking aliases or multipart types
  166. # such as extension Objects.
  167. if len(enc) == 1:
  168. # 0: ['BuiltinType'] either builtin type
  169. # 1: [ [ 'Alias', [...], n] ] or single alias for possible multipart
  170. if isinstance(enc[0], string_types):
  171. # 0: 'BuiltinType'
  172. if alias is not None:
  173. if not xmlvalue.localName == alias and not xmlvalue.localName == enc[0]:
  174. logger.error(str(parent.id) + ": Expected XML element with tag " + alias + " but found " + xmlvalue.localName + " instead")
  175. return None
  176. else:
  177. t = self.getTypeByString(enc[0], enc)
  178. t.alias = alias
  179. t.valueRank = valueRank
  180. if valueRank == 1:
  181. values = []
  182. for el in xmlvalue.childNodes:
  183. if not el.nodeType == el.ELEMENT_NODE:
  184. continue
  185. val = self.getTypeByString(enc[0], enc)
  186. val.parseXML(el, namespaceMapping=namespaceMapping)
  187. values.append(val)
  188. return values
  189. else:
  190. t.parseXML(xmlvalue, namespaceMapping=namespaceMapping)
  191. return t
  192. else:
  193. if not valueIsInternalType(xmlvalue.localName):
  194. logger.error(str(parent.id) + ": Expected XML describing builtin type " + enc[0] + " but found " + xmlvalue.localName + " instead")
  195. else:
  196. t = self.getTypeByString(enc[0], enc)
  197. t.parseXML(xmlvalue, namespaceMapping=namespaceMapping)
  198. t.isInternal = True
  199. return t
  200. else:
  201. # 1: ['Alias', [...], n]
  202. # Let the next elif handle this
  203. return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent,
  204. namespaceMapping=namespaceMapping,
  205. alias=alias, encodingPart=enc[0], valueRank=enc[2] if len(enc)>2 else None)
  206. elif len(enc) == 3 and isinstance(enc[0], string_types):
  207. # [ 'Alias', [...], 0 ] aliased multipart
  208. if alias is None:
  209. alias = enc[0]
  210. # if we have an alias and the next field is multipart, keep the alias
  211. elif alias is not None and len(enc[1]) > 1:
  212. alias = enc[0]
  213. # otherwise drop the alias
  214. return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent,
  215. namespaceMapping=namespaceMapping,
  216. alias=alias, encodingPart=enc[1], valueRank=enc[2] if len(enc)>2 else None)
  217. else:
  218. # [ [...], [...], [...]] multifield of unknowns (analyse separately)
  219. # create an extension object to hold multipart type
  220. # FIXME: This implementation expects an extensionobject to be manditory for
  221. # multipart variables. Variants/Structures are not included in the
  222. # OPCUA Namespace 0 nodeset.
  223. # Consider moving this ExtensionObject specific parsing into the
  224. # builtin type and only determining the multipart type at this stage.
  225. if not xmlvalue.localName == "ExtensionObject":
  226. logger.error(str(parent.id) + ": Expected XML tag <ExtensionObject> for multipart type, but found " + xmlvalue.localName + " instead.")
  227. return None
  228. extobj = ExtensionObject()
  229. extobj.encodingRule = enc
  230. etype = xmlvalue.getElementsByTagName("TypeId")
  231. if len(etype) == 0:
  232. logger.error(str(parent.id) + ": Did not find <TypeId> for ExtensionObject")
  233. return None
  234. etype = etype[0].getElementsByTagName("Identifier")
  235. if len(etype) == 0:
  236. logger.error(str(parent.id) + ": Did not find <Identifier> for ExtensionObject")
  237. return None
  238. etype = NodeId(etype[0].firstChild.data.strip(' \t\n\r'))
  239. extobj.typeId = etype
  240. ebody = xmlvalue.getElementsByTagName("Body")
  241. if len(ebody) == 0:
  242. logger.error(str(parent.id) + ": Did not find <Body> for ExtensionObject")
  243. return None
  244. ebody = ebody[0]
  245. # Body must contain an Object of type 'DataType' as defined in Variable
  246. ebodypart = ebody.firstChild
  247. if not ebodypart.nodeType == ebodypart.ELEMENT_NODE:
  248. ebodypart = getNextElementNode(ebodypart)
  249. if ebodypart is None:
  250. logger.error(str(parent.id) + ": Expected ExtensionObject to hold a variable of type " + str(parentDataTypeNode.browseName) + " but found nothing.")
  251. return None
  252. if not ebodypart.localName == "OptionSet" and not ebodypart.localName == parentDataTypeNode.browseName.name:
  253. logger.error(str(parent.id) + ": Expected ExtensionObject to hold a variable of type " + str(parentDataTypeNode.browseName) + " but found " +
  254. str(ebodypart.localName) + " instead.")
  255. return None
  256. extobj.alias = ebodypart.localName
  257. ebodypart = ebodypart.firstChild
  258. if not ebodypart.nodeType == ebodypart.ELEMENT_NODE:
  259. ebodypart = getNextElementNode(ebodypart)
  260. if ebodypart is None:
  261. logger.error(str(parent.id) + ": Description of dataType " + str(parentDataTypeNode.browseName) + " in ExtensionObject is empty/invalid.")
  262. return None
  263. extobj.value = []
  264. for e in enc:
  265. if not ebodypart is None:
  266. extobj.value.append(extobj.__parseXMLSingleValue(ebodypart, parentDataTypeNode, parent, namespaceMapping=namespaceMapping, alias=None, encodingPart=e))
  267. else:
  268. logger.error(str(parent.id) + ": Expected encoding " + str(e) + " but found none in body.")
  269. ebodypart = getNextElementNode(ebodypart)
  270. return extobj
  271. def __str__(self):
  272. return self.__class__.__name__ + "(" + str(self.value) + ")"
  273. def __repr__(self):
  274. return self.__str__()
  275. #################
  276. # Builtin Types #
  277. #################
  278. class Boolean(Value):
  279. def __init__(self, xmlelement=None):
  280. Value.__init__(self)
  281. if xmlelement:
  282. self.parseXML(xmlelement)
  283. def parseXML(self, xmlvalue, namespaceMapping=None):
  284. # Expect <Boolean>value</Boolean> or
  285. # <Aliasname>value</Aliasname>
  286. self.checkXML(xmlvalue)
  287. if xmlvalue.firstChild is None:
  288. self.value = "false" # Catch XML <Boolean /> by setting the value to a default
  289. else:
  290. if "false" in unicode(xmlvalue.firstChild.data).lower():
  291. self.value = "false"
  292. else:
  293. self.value = "true"
  294. class Number(Value):
  295. def __init__(self, xmlelement=None):
  296. Value.__init__(self)
  297. if xmlelement:
  298. self.parseXML(xmlelement)
  299. def parseXML(self, xmlvalue, namespaceMapping=None):
  300. # Expect <Int16>value</Int16> or any other valid number type, or
  301. # <Aliasname>value</Aliasname>
  302. self.checkXML(xmlvalue)
  303. if xmlvalue.firstChild is None:
  304. self.value = 0 # Catch XML <Int16 /> by setting the value to a default
  305. else:
  306. self.value = int(unicode(xmlvalue.firstChild.data))
  307. class Integer(Number):
  308. def __init__(self, xmlelement=None):
  309. Number.__init__(self)
  310. if xmlelement:
  311. self.parseXML(xmlelement)
  312. class UInteger(Number):
  313. def __init__(self, xmlelement=None):
  314. Number.__init__(self)
  315. if xmlelement:
  316. self.parseXML(xmlelement)
  317. class Byte(UInteger):
  318. def __init__(self, xmlelement=None):
  319. UInteger.__init__(self)
  320. if xmlelement:
  321. self.parseXML(xmlelement)
  322. class SByte(Integer):
  323. def __init__(self, xmlelement=None):
  324. Integer.__init__(self)
  325. if xmlelement:
  326. self.parseXML(xmlelement)
  327. class Int16(Integer):
  328. def __init__(self, xmlelement=None):
  329. Integer.__init__(self)
  330. if xmlelement:
  331. self.parseXML(xmlelement)
  332. class UInt16(UInteger):
  333. def __init__(self, xmlelement=None):
  334. UInteger.__init__(self)
  335. if xmlelement:
  336. self.parseXML(xmlelement)
  337. class Int32(Integer):
  338. def __init__(self, xmlelement=None):
  339. Integer.__init__(self)
  340. if xmlelement:
  341. self.parseXML(xmlelement)
  342. class UInt32(UInteger):
  343. def __init__(self, xmlelement=None):
  344. UInteger.__init__(self)
  345. if xmlelement:
  346. self.parseXML(xmlelement)
  347. class Int64(Integer):
  348. def __init__(self, xmlelement=None):
  349. Integer.__init__(self)
  350. if xmlelement:
  351. self.parseXML(xmlelement)
  352. class UInt64(UInteger):
  353. def __init__(self, xmlelement=None):
  354. UInteger.__init__(self)
  355. if xmlelement:
  356. self.parseXML(xmlelement)
  357. class Float(Number):
  358. def __init__(self, xmlelement=None):
  359. Number.__init__(self)
  360. if xmlelement:
  361. Float.parseXML(self, xmlelement)
  362. def parseXML(self, xmlvalue, namespaceMapping=None):
  363. # Expect <Float>value</Float> or
  364. # <Aliasname>value</Aliasname>
  365. self.checkXML(xmlvalue)
  366. if xmlvalue.firstChild is None:
  367. self.value = 0.0 # Catch XML <Float /> by setting the value to a default
  368. else:
  369. self.value = float(unicode(xmlvalue.firstChild.data))
  370. class Double(Float):
  371. def __init__(self, xmlelement=None):
  372. Float.__init__(self)
  373. if xmlelement:
  374. self.parseXML(xmlelement)
  375. class String(Value):
  376. def __init__(self, xmlelement=None):
  377. Value.__init__(self)
  378. if xmlelement:
  379. self.parseXML(xmlelement)
  380. def pack(self):
  381. bin = structpack("I", len(unicode(self.value)))
  382. bin = bin + str(self.value)
  383. return bin
  384. def parseXML(self, xmlvalue, namespaceMapping=None):
  385. # Expect <String>value</String> or
  386. # <Aliasname>value</Aliasname>
  387. if not isinstance(xmlvalue, dom.Element):
  388. self.value = xmlvalue
  389. return
  390. self.checkXML(xmlvalue)
  391. if xmlvalue.firstChild is None:
  392. self.value = "" # Catch XML <String /> by setting the value to a default
  393. else:
  394. self.value = unicode(xmlvalue.firstChild.data)
  395. class XmlElement(String):
  396. def __init__(self, xmlelement=None):
  397. String.__init__(self, xmlelement)
  398. class ByteString(Value):
  399. def __init__(self, xmlelement=None):
  400. Value.__init__(self)
  401. def parseXML(self, xmlvalue, namespaceMapping=None):
  402. # Expect <ByteString>value</ByteString>
  403. if not isinstance(xmlvalue, dom.Element):
  404. self.value = xmlvalue
  405. return
  406. self.checkXML(xmlvalue)
  407. if xmlvalue.firstChild is None:
  408. self.value = [] # Catch XML <ByteString /> by setting the value to a default
  409. else:
  410. self.value = b64decode(xmlvalue.firstChild.data)
  411. class ExtensionObject(Value):
  412. def __init__(self, xmlelement=None):
  413. Value.__init__(self)
  414. if xmlelement:
  415. self.parseXML(xmlelement)
  416. def parseXML(self, xmlelement, namespaceMapping=None):
  417. pass
  418. def __str__(self):
  419. return "'ExtensionObject'"
  420. class LocalizedText(Value):
  421. def __init__(self, xmlvalue=None):
  422. Value.__init__(self)
  423. self.locale = ''
  424. self.text = ''
  425. if xmlvalue:
  426. self.parseXML(xmlvalue)
  427. def parseXML(self, xmlvalue, namespaceMapping=None):
  428. # Expect <LocalizedText> or <AliasName>
  429. # <Locale>xx_XX</Locale>
  430. # <Text>TextText</Text>
  431. # <LocalizedText> or </AliasName>
  432. if not isinstance(xmlvalue, dom.Element):
  433. self.text = xmlvalue
  434. return
  435. self.checkXML(xmlvalue)
  436. tmp = xmlvalue.getElementsByTagName("Locale")
  437. if len(tmp) > 0 and tmp[0].firstChild != None:
  438. self.locale = tmp[0].firstChild.data.strip(' \t\n\r')
  439. tmp = xmlvalue.getElementsByTagName("Text")
  440. if len(tmp) > 0 and tmp[0].firstChild != None:
  441. self.text = tmp[0].firstChild.data.strip(' \t\n\r')
  442. def __str__(self):
  443. if self.locale is not None and len(self.locale) > 0:
  444. return "(" + self.locale + ":" + self.text + ")"
  445. else:
  446. return self.text
  447. class NodeId(Value):
  448. def __init__(self, idstring=None):
  449. Value.__init__(self)
  450. self.i = None
  451. self.b = None
  452. self.g = None
  453. self.s = None
  454. self.ns = 0
  455. self.setFromIdString(idstring)
  456. def setFromIdString(self, idstring):
  457. if not idstring:
  458. self.i = 0
  459. return
  460. # The ID will encoding itself appropriatly as string. If multiple ID's
  461. # (numeric, string, guid) are defined, the order of preference for the ID
  462. # string is always numeric, guid, bytestring, string. Binary encoding only
  463. # applies to numeric values (UInt16).
  464. idparts = idstring.strip().split(";")
  465. for p in idparts:
  466. if p[:2] == "ns":
  467. self.ns = int(p[3:])
  468. elif p[:2] == "i=":
  469. self.i = int(p[2:])
  470. elif p[:2] == "o=":
  471. self.b = p[2:]
  472. elif p[:2] == "g=":
  473. tmp = []
  474. self.g = p[2:].split("-")
  475. for i in self.g:
  476. i = "0x" + i
  477. tmp.append(int(i, 16))
  478. self.g = tmp
  479. elif p[:2] == "s=":
  480. self.s = p[2:]
  481. else:
  482. raise Exception("no valid nodeid: " + idstring)
  483. def parseXML(self, xmlvalue, namespaceMapping=None):
  484. # Expect <NodeId> or <Alias>
  485. # <Identifier> # It is unclear whether or not this is manditory. Identifier tags are used in Namespace 0.
  486. # ns=x;i=y or similar string representation of id()
  487. # </Identifier>
  488. # </NodeId> or </Alias>
  489. if not isinstance(xmlvalue, dom.Element):
  490. self.text = xmlvalue # Alias
  491. return
  492. self.checkXML(xmlvalue)
  493. # Catch XML <NodeId />
  494. if xmlvalue.firstChild is None:
  495. logger.error("No value is given, which is illegal for Node Types...")
  496. self.value = None
  497. else:
  498. # Check if there is an <Identifier> tag
  499. if len(xmlvalue.getElementsByTagName("Identifier")) != 0:
  500. xmlvalue = xmlvalue.getElementsByTagName("Identifier")[0]
  501. self.setFromIdString(unicode(xmlvalue.firstChild.data))
  502. if namespaceMapping is not None:
  503. self.ns = namespaceMapping[self.ns]
  504. def __str__(self):
  505. s = "ns=" + str(self.ns) + ";"
  506. # Order of preference is numeric, guid, bytestring, string
  507. if self.i != None:
  508. return s + "i=" + str(self.i)
  509. elif self.g != None:
  510. s = s + "g="
  511. tmp = []
  512. for i in self.g:
  513. tmp.append(hex(i).replace("0x", ""))
  514. for i in tmp:
  515. s = s + "-" + i
  516. return s.replace("g=-", "g=")
  517. elif self.b != None:
  518. return s + "b=" + str(self.b)
  519. elif self.s != None:
  520. return s + "s=" + str(self.s)
  521. def __eq__(self, nodeId2):
  522. return (str(self) == str(nodeId2))
  523. def __ne__(self, other):
  524. return not self.__eq__(other)
  525. def __repr__(self):
  526. return str(self)
  527. def __hash__(self):
  528. return hash(str(self))
  529. class ExpandedNodeId(Value):
  530. def __init__(self, xmlelement=None):
  531. Value.__init__(self)
  532. if xmlelement:
  533. self.parseXML(xmlelement)
  534. def parseXML(self, xmlvalue, namespaceMapping=None):
  535. self.checkXML(xmlvalue)
  536. logger.debug("Not implemented", LOG_LEVEL_ERR)
  537. class DateTime(Value):
  538. def __init__(self, xmlelement=None):
  539. Value.__init__(self)
  540. if xmlelement:
  541. self.parseXML(xmlelement)
  542. def parseXML(self, xmlvalue, namespaceMapping=None):
  543. # Expect <DateTime> or <AliasName>
  544. # 2013-08-13T21:00:05.0000L
  545. # </DateTime> or </AliasName>
  546. self.checkXML(xmlvalue)
  547. if xmlvalue.firstChild is None:
  548. # Catch XML <DateTime /> by setting the value to a default
  549. self.value = datetime(2001, 1, 1)
  550. else:
  551. timestr = unicode(xmlvalue.firstChild.data)
  552. # .NET tends to create this garbage %Y-%m-%dT%H:%M:%S.0000z
  553. # strip everything after the "." away for a posix time_struct
  554. if "." in timestr:
  555. timestr = timestr[:timestr.index(".")]
  556. # If the last character is not numeric, remove it
  557. while len(timestr) > 0 and not timestr[-1] in "0123456789":
  558. timestr = timestr[:-1]
  559. try:
  560. self.value = datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
  561. except Exception:
  562. try:
  563. self.value = datetime.strptime(timestr, "%Y-%m-%d")
  564. except Exception:
  565. logger.error("Timestring format is illegible. Expected 2001-01-30T21:22:23 or 2001-01-30, but got " + \
  566. timestr + " instead. Time will be defaultet to now()")
  567. self.value = datetime(2001, 1, 1)
  568. class QualifiedName(Value):
  569. def __init__(self, xmlelement=None):
  570. Value.__init__(self)
  571. self.ns = 0
  572. self.name = ''
  573. if xmlelement:
  574. self.parseXML(xmlelement)
  575. def parseXML(self, xmlvalue, namespaceMapping=None):
  576. # Expect <QualifiedName> or <AliasName>
  577. # <NamespaceIndex>Int16<NamespaceIndex>
  578. # <Name>SomeString<Name>
  579. # </QualifiedName> or </AliasName>
  580. if not isinstance(xmlvalue, dom.Element):
  581. colonindex = xmlvalue.find(":")
  582. if colonindex == -1:
  583. self.name = xmlvalue
  584. else:
  585. self.name = xmlvalue[colonindex + 1:]
  586. self.ns = int(xmlvalue[:colonindex])
  587. if namespaceMapping is not None:
  588. self.ns = namespaceMapping[self.ns]
  589. return
  590. self.checkXML(xmlvalue)
  591. # Is a namespace index passed?
  592. if len(xmlvalue.getElementsByTagName("NamespaceIndex")) != 0:
  593. self.ns = int(xmlvalue.getElementsByTagName("NamespaceIndex")[0].firstChild.data)
  594. if namespaceMapping is not None:
  595. self.ns = namespaceMapping[self.ns]
  596. if len(xmlvalue.getElementsByTagName("Name")) != 0:
  597. self.name = xmlvalue.getElementsByTagName("Name")[0].firstChild.data
  598. def __str__(self):
  599. return "ns=" + str(self.ns) + ";" + str(self.name)
  600. class StatusCode(UInt32):
  601. def __init__(self, xmlelement=None):
  602. UInt32.__init__(self, xmlelement)
  603. class DiagnosticInfo(Value):
  604. def __init__(self, xmlelement=None):
  605. Value.__init__(self)
  606. if xmlelement:
  607. self.parseXML(xmlelement)
  608. def parseXML(self, xmlvalue, namespaceMapping=None):
  609. self.checkXML(xmlvalue)
  610. logger.warn("Not implemented")
  611. class Guid(Value):
  612. def __init__(self, xmlelement=None):
  613. Value.__init__(self)
  614. if xmlelement:
  615. self.parseXML(xmlelement)
  616. def parseXML(self, xmlvalue, namespaceMapping=None):
  617. self.checkXML(xmlvalue)
  618. if xmlvalue.firstChild is None:
  619. self.value = [0, 0, 0, 0] # Catch XML <Guid /> by setting the value to a default
  620. else:
  621. self.value = unicode(xmlvalue.firstChild.data)
  622. self.value = self.value.replace("{", "")
  623. self.value = self.value.replace("}", "")
  624. self.value = self.value.split("-")
  625. tmp = []
  626. for g in self.value:
  627. try:
  628. tmp.append(int("0x" + g, 16))
  629. except Exception:
  630. logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
  631. unicode(xmlvalue.firstChild.data))
  632. tmp = [0, 0, 0, 0, 0]
  633. if len(tmp) != 5:
  634. logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
  635. unicode(xmlvalue.firstChild.data))
  636. tmp = [0, 0, 0, 0]
  637. self.value = tmp