datatypes.py 28 KB

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