datatypes.py 27 KB

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