tracerXML.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at
  2. # McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from pypdevs.tracers.tracerBase import BaseTracer
  16. from pypdevs.util import runTraceAtController, toStr
  17. import sys, re
  18. class TracerXML(BaseTracer):
  19. """
  20. A tracer for XML tracing output
  21. """
  22. def __init__(self, uid, server, filename):
  23. """
  24. Constructor
  25. :param uid: the UID of this tracer
  26. :param server: the server to make remote calls on
  27. :param filename: file to save the trace to
  28. """
  29. super(TracerXML, self).__init__(uid, server)
  30. if server.getName() == 0:
  31. self.filename = filename
  32. else:
  33. self.filename = None
  34. def write_py23(self, string):
  35. try:
  36. self.xml_file.write(string)
  37. except TypeError:
  38. self.xml_file.write(string.encode())
  39. def startTracer(self, recover):
  40. """
  41. Starts up the tracer
  42. :param recover: whether or not this is a recovery call (so whether or not the file should be appended to)
  43. """
  44. if self.filename is None:
  45. # Nothing to do here as we aren't the controller
  46. return
  47. elif recover:
  48. self.xml_file = open(self.filename, 'a+')
  49. else:
  50. self.xml_file = open(self.filename, 'w')
  51. self.write_py23("<?xml version=\"1.0\"?>\n" + "<trace>\n")
  52. def stopTracer(self):
  53. """
  54. Stop the tracer
  55. """
  56. self.write_py23("</trace>")
  57. self.xml_file.flush()
  58. def trace(self, model_name, timestamp, event_kind, port_info, xml_state, str_state):
  59. """
  60. Save an XML entry for the provided parameters, basically wraps it in the necessary tags
  61. :param model_name: name of the model
  62. :param timestamp: timestamp of the transition
  63. :param event_kind: kind of event that happened, e.g. internal, external, ...
  64. :param port_info: actual information about the port
  65. :param xml_state: XML representation of the state
  66. :param str_state: normal string representation of the state
  67. """
  68. self.write_py23("<event>\n"
  69. + "<model>" + model_name + "</model>\n"
  70. + "<time>" + str(timestamp[0]) + "</time>\n"
  71. + "<kind>" + event_kind + "</kind>\n"
  72. + port_info
  73. + "<state>\n"+ xml_state + "<![CDATA[" + str_state + "]]>\n</state>\n"
  74. + "</event>\n")
  75. def traceInternal(self, aDEVS):
  76. """
  77. The trace functionality for XML output at an internal transition
  78. :param aDEVS: the model that transitioned
  79. """
  80. port_info = ""
  81. for I in range(len(aDEVS.OPorts)):
  82. if (aDEVS.OPorts[I] in aDEVS.my_output and
  83. aDEVS.my_output[aDEVS.OPorts[I]] is not None):
  84. port_info += '<port name="' + aDEVS.OPorts[I].getPortName() + '" category="O">\n'
  85. for j in aDEVS.my_output.get(aDEVS.OPorts[I], []):
  86. port_info += "<message>" + str(j) + "</message>\n"
  87. port_info += "</port>\n"
  88. runTraceAtController(self.server,
  89. self.uid,
  90. aDEVS,
  91. [toStr(aDEVS.getModelFullName()),
  92. aDEVS.time_last,
  93. "'IN'",
  94. toStr(port_info),
  95. toStr(TracerXML.toXML(aDEVS.state)),
  96. toStr(aDEVS.state)])
  97. def traceExternal(self, aDEVS):
  98. """
  99. The trace functionality for XML output at an external transition
  100. :param aDEVS: the model that transitioned
  101. """
  102. port_info = ""
  103. for I in range(len(aDEVS.IPorts)):
  104. port_info += '<port name="' + aDEVS.IPorts[I].getPortName() + '" category="I">\n'
  105. for j in aDEVS.my_input.get(aDEVS.IPorts[I], []):
  106. port_info += "<message>" + str(j) + "</message>\n"
  107. port_info += "</port>\n"
  108. runTraceAtController(self.server,
  109. self.uid,
  110. aDEVS,
  111. [toStr(aDEVS.getModelFullName()),
  112. aDEVS.time_last,
  113. "'EX'",
  114. toStr(port_info),
  115. toStr(TracerXML.toXML(aDEVS.state)),
  116. toStr(aDEVS.state)])
  117. def traceConfluent(self, aDEVS):
  118. """
  119. The trace functionality for XML output at a confluent transition
  120. :param aDEVS: the model that transitioned
  121. """
  122. port_info = ""
  123. for I in range(len(aDEVS.IPorts)):
  124. port_info += '<port name="' + aDEVS.IPorts[I].getPortName() + '" category="I">\n'
  125. for j in aDEVS.my_input.get(aDEVS.IPorts[I], []):
  126. port_info += "<message>" + str(j) + "</message>\n"
  127. port_info += "</port>\n"
  128. runTraceAtController(self.server,
  129. self.uid,
  130. aDEVS,
  131. [toStr(aDEVS.getModelFullName()),
  132. aDEVS.time_last,
  133. "'EX'",
  134. toStr(port_info),
  135. toStr(TracerXML.toXML(aDEVS.state)),
  136. toStr(aDEVS.state)])
  137. port_info = ""
  138. for I in range(len(aDEVS.OPorts)):
  139. if aDEVS.OPorts[I] in aDEVS.my_output:
  140. port_info += '<port name="' + aDEVS.OPorts[I].getPortName() + '" category="O">\n'
  141. for j in aDEVS.my_output.get(aDEVS.OPorts[I], []):
  142. port_info += "<message>" + str(j) + "</message>\n"
  143. port_info += "</port>\n"
  144. runTraceAtController(self.server,
  145. self.uid,
  146. aDEVS,
  147. [toStr(aDEVS.getModelFullName()),
  148. aDEVS.time_last,
  149. "'IN'",
  150. toStr(port_info),
  151. toStr(TracerXML.toXML(aDEVS.state)),
  152. toStr(aDEVS.state)])
  153. def traceInit(self, aDEVS, t):
  154. """
  155. The trace functionality for XML output at initialization
  156. :param aDEVS: the model that transitioned
  157. :param t: time at which it should be traced
  158. """
  159. runTraceAtController(self.server,
  160. self.uid,
  161. aDEVS,
  162. [toStr(aDEVS.getModelFullName()),
  163. t,
  164. "'EX'",
  165. "''",
  166. toStr(TracerXML.toXML(aDEVS.state)),
  167. toStr(aDEVS.state)])
  168. @staticmethod
  169. def toXML(state):
  170. primitives = {
  171. int: "Integer",
  172. float: "Float",
  173. str: "String",
  174. bool: "Boolean"
  175. }
  176. def create_multi_attrib(name, elem):
  177. cat = "C"
  178. if type(elem) in primitives:
  179. cat = "P"
  180. type_ = primitives[type(elem)]
  181. return "<attribute category=\"%s\"><name>%s</name><type>%s</type><value>%s</value></attribute>" % (
  182. cat, name, type_, str(elem))
  183. else:
  184. type_ = "Unknown"
  185. value = TracerXML.toXML(elem)
  186. return "<attribute category=\"%s\"><name>%s</name><type>%s</type><value>%s</value></attribute>" % (
  187. cat, name, type_, str(value))
  188. if isinstance(state, (str, int, float, bool)):
  189. return "<attribute category=\"P\"><name>state</name><type>%s</type><value>%s</value></attribute>" % (primitives[type(state)], str(state))
  190. elif isinstance(state, dict):
  191. res = ""
  192. for k, v in state.items():
  193. name = re.sub("[^a-zA-Z0-9_]", "", k)
  194. res += create_multi_attrib(name, v)
  195. return "<attribute category=\"C\"><name>state</name><type>Map</type><value>%s</value></attribute>" % res
  196. elif isinstance(state, (list, tuple)):
  197. res = ""
  198. for ix, item in enumerate(state):
  199. name = "item-%d" % ix
  200. res += create_multi_attrib(name, item)
  201. return "<attribute category=\"C\"><name>state</name><type>List</type><value>%s</value></attribute>" % res
  202. elif hasattr(state, "toXML"):
  203. return state.toXML()
  204. elif hasattr(state, "__str__"):
  205. return TracerXML.toXML(str(state))
  206. return TracerXML.toXML({k: getattr(state, k) for k in dir(state) if not k.startswith("_") and not callable(getattr(state, k))})