/bitwrap_io/machine/pnml.py

https://github.com/bitwrap/bitwrap-io · Python · 276 lines · 149 code · 40 blank · 87 comment · 14 complexity · e2357ba65b15b8ee13d574d6a4dfa794 MD5 · raw file

  1. #!/usr/bin/python3
  2. # -*- coding_ utf-8 -*-
  3. """
  4. This program implements a parser and data structure for Petri net files.
  5. Original author: Copyright (c) 2015 Thomas Irgang
  6. see MIT license: https://github.com/irgangla/pntools/blob/master/LICENSE
  7. This program implements an XML parser and a python data structure for
  8. Petri nets/PNML.
  9. """
  10. import sys # argv for test file path
  11. import xml.etree.ElementTree as ET # XML parser
  12. import time # timestamp for id generation
  13. from random import randint # random number for id generation
  14. class PetriNet(object):
  15. """ This class represents a Petri net.
  16. This class represents a Petri net. A Petri net consists of
  17. a set of labelled labelled transitions, labelled places and
  18. arcs from places to transitions or transitions to places.
  19. net.edges: List of all edges of this Petri net
  20. net.transitions: Map of (id, transition) of all transisions of this Petri net
  21. net.places: Map of (id, place) of all places of this Petri net
  22. """
  23. def __init__(self):
  24. self.edges = [] # List or arcs
  25. self.transitions = {} # Map of transitions. Key: transition id, Value: event
  26. self.places = {} # Map of places. Key: place id, Value: place
  27. self.name = None
  28. def __str__(self):
  29. text = '--- Net: ' + self.name + '\nTransitions: '
  30. for transition in self.transitions.values():
  31. text += str(transition) + ' '
  32. text += '\nPlaces: '
  33. for place in self.places.values():
  34. text += str(place) + ' '
  35. text += '\n'
  36. for edge in self.edges:
  37. text += str(edge) + '\n'
  38. text += '---'
  39. return text
  40. class Transition(object):
  41. """ This class represents a labelled transition of a Petri net.
  42. A transition represents an activity.
  43. transition.id: Unique ID of this transition.
  44. transition.label: Label of this transition.
  45. Layout information:
  46. transition.position: Position to display the transition in graphical representations.
  47. Usually a transition is drawn as a square. The position is the center of this square.
  48. transition.offset: Offest of the transition label.
  49. Usually the label of a transition is printed centered below the square which
  50. represents the transition in graphical representations. This offset represents
  51. a vector which defines a translation of the label inscription from its
  52. usual position.
  53. """
  54. def __init__(self):
  55. self.label = "Transition" # default label of event
  56. #generate a unique id
  57. self.id = ("Transition" + str(time.time())) + str(randint(0, 1000))
  58. self.offset = [0, 0]
  59. self.position = [0, 0]
  60. self.net = None
  61. def __str__(self):
  62. return self.label
  63. class Place(object):
  64. """ This class represents a labelled Place of a Petri net.
  65. A place represents a resource.
  66. place.id: Unique ID of this place.
  67. place.label: Label of this place.
  68. place.marking: Current marking of this place.
  69. Usually a marking is the count of tokens contained into this place.
  70. Layout information:
  71. place.position: Position to display the place in graphical representations.
  72. Usually a place is drawn as a circle. The position is the center of this circel.
  73. place.offset: Offest of the place label.
  74. Usually the label of a place is printed centered below the circle which
  75. represents the place in graphical representations. This offset represents
  76. a vector which defines a translation of the label inscription from its
  77. usual position.
  78. """
  79. def __init__(self):
  80. self.label = "Place" # default label of event
  81. #generate a unique id
  82. self.id = ("Place" + str(time.time())) + str(randint(0, 1000))
  83. self.offset = [0, 0]
  84. self.position = [0, 0]
  85. self.marking = 0
  86. def __str__(self):
  87. return self.label
  88. class Edge(object):
  89. """ This class represents an arc of a Petri net.
  90. An edge represents an relation between a place and a transition or a transition
  91. and a place.
  92. edge.id: Unique ID of this edge.
  93. edge.source: ID of the source (start) node of this edge.
  94. edge.target: ID of the target (end) node of this edge.
  95. edge.inscription: Inscription of this edge.
  96. The inscription is usually an integer which is interpreted as weight of this edge.
  97. edge.net: The Petri net which contains this edge.
  98. This reference is used for the label resolution of the source and target events.
  99. See __str__ method.
  100. """
  101. def __init__(self):
  102. #generate a unique id
  103. self.id = ("Arc" + str(time.time())) + str(randint(0, 1000))
  104. self.source = None # id of the source event of this arc
  105. self.target = None # id of the target event of this arc
  106. self.inscription = "1" # inscription of this arc
  107. self.net = None # Reference to net object for label resolution of source an target
  108. self.inhibitor = False
  109. def find_source(self):
  110. """ find source of txn """
  111. if self.source in self.net.transitions:
  112. return self.net.transitions[self.source]
  113. return self.net.places[self.source]
  114. def find_target(self):
  115. """ find txn target """
  116. if self.target in self.net.transitions:
  117. return self.net.transitions[self.target]
  118. return self.net.places[self.target]
  119. def __str__(self):
  120. return str(self.find_source()) + "-->" + str(self.find_target())
  121. def parse_pnml_file(filename):
  122. """ This method parse all Petri nets of the given file.
  123. This method expects a path to a VipTool pnml file which
  124. represent a Petri net (.pnml), parse all Petri nets
  125. from the file and returns the Petri nets as list of PetriNet
  126. objects.
  127. XML format:
  128. <pnml>
  129. <net id="...">
  130. (<page>)
  131. <name>
  132. <text>name of Petri net</text>
  133. </name>
  134. <transition id="...">
  135. <name>
  136. <text>label of transition</text>
  137. <graphics>
  138. <offset x="0" y="0"/>
  139. </graphics>
  140. </name>
  141. <graphics>
  142. <position x="73" y="149"/>
  143. </graphics>
  144. </transition>
  145. ...
  146. <place id="...">
  147. <name>
  148. <text>label of transition</text>
  149. <graphics>
  150. <offset x="0" y="0"/>
  151. </graphics>
  152. </name>
  153. <graphics>
  154. <position x="73" y="149"/>
  155. </graphics>
  156. <initialMarking>
  157. <text>1</text>
  158. </initialMarking>
  159. </place>
  160. ...
  161. <arc id="..." source="id of source event" target="id of target event">
  162. <inscription>
  163. <text>1</text>
  164. </inscription>
  165. </arc>
  166. ...
  167. (</page>)
  168. </net>
  169. ...
  170. </pnml>
  171. """
  172. tree = ET.parse(filename) # parse XML with ElementTree
  173. root = tree.getroot()
  174. nets = [] # list for parsed PetriNet objects
  175. for net_node in root.iter('net'):
  176. # create PetriNet object
  177. net = PetriNet()
  178. nets.append(net)
  179. net.name = net_node.get('id')
  180. # and parse transitions
  181. for transition_node in net_node.iter('transition'):
  182. transition = Transition()
  183. transition.id = transition_node.get('id')
  184. transition.label = transition.id
  185. off_node = transition_node.find('./name/graphics/offset')
  186. transition.offset = [float(off_node.get('x')), float(off_node.get('y'))]
  187. position_node = transition_node.find('./graphics/position')
  188. transition.position = [float(position_node.get('x')), float(position_node.get('y'))]
  189. net.transitions[transition.id] = transition
  190. transition.net = net
  191. # and parse places
  192. for place_node in net_node.iter('place'):
  193. place = Place()
  194. place.id = place_node.get('id')
  195. place.label = place.id
  196. off_node = place_node.find('./name/graphics/offset')
  197. place.offset = [float(off_node.get('x')), float(off_node.get('y'))]
  198. position_node = place_node.find('./graphics/position')
  199. place.position = [float(position_node.get('x')), float(position_node.get('y'))]
  200. marking_str = place_node.find('./initialMarking/value')
  201. if marking_str.text:
  202. place.marking = int(marking_str.text.split(',')[1])
  203. else:
  204. place.marking = 0
  205. net.places[place.id] = place
  206. # and arcs
  207. for arc_node in net_node.iter('arc'):
  208. edge = Edge()
  209. net.edges.append(edge)
  210. edge.id = arc_node.get('id')
  211. edge.source = arc_node.get('source')
  212. edge.target = arc_node.get('target')
  213. inscription_str = arc_node.find('./inscription/value').text
  214. edge.inscription = int(inscription_str.replace('Default,', ''))
  215. arc_type = arc_node.find('type').get('value')
  216. if arc_type == 'inhibitor':
  217. edge.inhibitor = True
  218. edge.net = net
  219. return nets
  220. if __name__ == "__main__":
  221. for pnet in parse_pnml_file(sys.argv[1]):
  222. print(pnet)