object_manager.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import re
  2. import abc
  3. from typing import List, Tuple
  4. from sccd.statechart.dynamic.statechart_instance import InternalEvent, Instance, StatechartInstance
  5. # TODO: Clean this mess up. Look at all object management operations and see how they can be improved.
  6. class ObjectManager(Instance):
  7. __slots__ = ["cd", "output_callback", "schedule_callback", "instances"]
  8. _regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$")
  9. def __init__(self, cd, output_callback, schedule_callback, cancel_callback):
  10. self.cd = cd
  11. self.output_callback = output_callback
  12. self.schedule_callback = schedule_callback
  13. self.cancel_callback = cancel_callback
  14. i = StatechartInstance(cd.get_default_class(), self, self.output_callback, self.schedule_callback, self.cancel_callback)
  15. # set of all instances in the runtime
  16. # we need to maintain this set in order to do broadcasts
  17. self.instances = [i]
  18. def _create(self, class_name) -> StatechartInstance:
  19. statechart_model = self.cd.classes[class_name]
  20. i = StatechartInstance(statechart_model, self, self.output_callback, self.schedule_callback, self.cancel_callback)
  21. self.instances.append(i)
  22. return i
  23. def initialize(self):
  24. pass
  25. # Implementation of super class: Instance
  26. def big_step(self, input_events: List[InternalEvent]):
  27. pass
  28. # output = []
  29. # for e in input_events:
  30. # try:
  31. # o = ObjectManager._handlers[e.name](self, timestamp, e.parameters)
  32. # if isinstance(o, OutputEvent):
  33. # output.append(o)
  34. # elif isinstance(o, list):
  35. # output.extend(o)
  36. # except KeyError:
  37. # pass
  38. # return output
  39. # def _assoc_ref(self, input_string) -> List[Tuple[str,int]]:
  40. # if len(input_string) == 0:
  41. # raise AssociationReferenceException("Empty association reference.")
  42. # path_string = input_string.split("/")
  43. # result = []
  44. # for piece in path_string:
  45. # match = ObjectManager._regex_pattern.match(piece)
  46. # if match:
  47. # name = match.group(1)
  48. # index = match.group(2)
  49. # if index is None:
  50. # index = -1
  51. # result.append((name,int(index)))
  52. # else:
  53. # raise AssociationReferenceException("Invalid entry in association reference. Input string: " + input_string)
  54. # return result
  55. # def _handle_broadcast(self, timestamp, parameters) -> OutputEvent:
  56. # if len(parameters) != 2:
  57. # raise ParameterException ("The broadcast event needs 2 parameters (source of event and event name).")
  58. # return OutputEvent(parameters[1], InstancesTarget(self.instances))
  59. # def _handle_create(self, timestamp, parameters) -> List[OutputEvent]:
  60. # if len(parameters) < 2:
  61. # raise ParameterException ("The create event needs at least 2 parameters.")
  62. # source = parameters[0]
  63. # association_name = parameters[1]
  64. # traversal_list = self._assoc_ref(association_name)
  65. # instances = self._get_instances(source, traversal_list)
  66. # association = source.associations[association_name]
  67. # # association = self.instances_map[source].getAssociation(association_name)
  68. # if association.allowedToAdd():
  69. # ''' allow subclasses to be instantiated '''
  70. # class_name = association.to_class if len(parameters) == 2 else parameters[2]
  71. # instance = self._create(class_name)
  72. # # new_instance = self.model.classes[class_name](parameters[3:])
  73. # if not instance:
  74. # raise ParameterException("Creating instance: no such class: " + class_name)
  75. # output_events = instance.initialize(timestamp)
  76. # try:
  77. # index = association.addInstance(instance)
  78. # except AssociationException as exception:
  79. # raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception))
  80. # p = instance.associations.get("parent")
  81. # if p:
  82. # p.addInstance(source)
  83. # return output_events.append(OutputEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]), InstancesTarget([source])))
  84. # else:
  85. # return OutputEvent(Event("instance_creation_error", None, [association_name]), InstancesTarget([source]))
  86. # def _handle_delete(self, timestamp, parameters) -> OutputEvent:
  87. # if len(parameters) < 2:
  88. # raise ParameterException ("The delete event needs at least 2 parameters.")
  89. # else:
  90. # source = parameters[0]
  91. # association_name = parameters[1]
  92. # traversal_list = self._assoc_ref(association_name)
  93. # instances = self._get_instances(source, traversal_list)
  94. # # association = self.instances_map[source].getAssociation(traversal_list[0][0])
  95. # association = source.associations[traversal_list[0][0]]
  96. # for i in instances:
  97. # try:
  98. # for assoc_name in i["instance"].associations:
  99. # if assoc_name != 'parent':
  100. # traversal_list = self._assoc_ref(assoc_name)
  101. # instances = self._get_instances(i["instance"], traversal_list)
  102. # if len(instances) > 0:
  103. # raise RuntimeException("Error removing instance from association %s, still %i children left connected with association %s" % (association_name, len(instances), assoc_name))
  104. # # del i["instance"].controller.input_ports[i["instance"].narrow_cast_port]
  105. # association.removeInstance(i["instance"])
  106. # self.instances.remove(i["instance"])
  107. # except AssociationException as exception:
  108. # raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception))
  109. # i["instance"].user_defined_destructor()
  110. # i["instance"].stop()
  111. # return OutputEvent(Event("instance_deleted", parameters = [parameters[1]]), InstancesTarget([source]))
  112. # def _handle_associate(self, timestamp, parameters) -> OutputEvent:
  113. # if len(parameters) != 3:
  114. # raise ParameterException ("The associate_instance event needs 3 parameters.")
  115. # else:
  116. # source = parameters[0]
  117. # to_copy_list = self._get_instances(source, self._assoc_ref(parameters[1]))
  118. # if len(to_copy_list) != 1:
  119. # raise AssociationReferenceException ("Invalid source association reference.")
  120. # wrapped_to_copy_instance = to_copy_list[0]["instance"]
  121. # dest_list = self._assoc_ref(parameters[2])
  122. # if len(dest_list) == 0:
  123. # raise AssociationReferenceException ("Invalid destination association reference.")
  124. # last = dest_list.pop()
  125. # if last[1] != -1:
  126. # raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.")
  127. # added_links = []
  128. # for i in self._get_instances(source, dest_list):
  129. # association = i["instance"].associations[last[0]]
  130. # if association.allowedToAdd():
  131. # index = association.addInstance(wrapped_to_copy_instance)
  132. # added_links.append(i["path"] + ("" if i["path"] == "" else "/") + last[0] + "[" + str(index) + "]")
  133. # return OutputEvent(Event("instance_associated", parameters = [added_links]), InstancesTarget([source]))
  134. # def _handle_disassociate(self, timestamp, parameters) -> OutputEvent:
  135. # if len(parameters) < 2:
  136. # raise ParameterException ("The disassociate_instance event needs at least 2 parameters.")
  137. # else:
  138. # source = parameters[0]
  139. # association_name = parameters[1]
  140. # if not isinstance(association_name, list):
  141. # association_name = [association_name]
  142. # deleted_links = []
  143. # for a_n in association_name:
  144. # traversal_list = self._assoc_ref(a_n)
  145. # instances = self._get_instances(source, traversal_list)
  146. # for i in instances:
  147. # try:
  148. # index = i['ref'].associations[i['assoc_name']].removeInstance(i["instance"])
  149. # deleted_links.append(a_n + "[" + str(index) + "]")
  150. # except AssociationException as exception:
  151. # raise RuntimeException("Error disassociating '" + a_n + "': " + str(exception))
  152. # return OutputEvent(Event("instance_disassociated", parameters = [deleted_links]), InstancesTarget([source]))
  153. # def _handle_narrowcast(self, timestamp, parameters) -> OutputEvent:
  154. # if len(parameters) != 3:
  155. # raise ParameterException ("The narrow_cast event needs 3 parameters.")
  156. # source, targets, cast_event = parameters
  157. # if not isinstance(targets, list):
  158. # targets = [targets]
  159. # all_instances = []
  160. # for target in targets:
  161. # traversal_list = self._assoc_ref(target)
  162. # instances = self._get_instances(source, traversal_list)
  163. # all_instances.extend(instances)
  164. # return OutputEvent(cast_event, instances)
  165. # def _get_instances(self, source, traversal_list):
  166. # print("_get_instances(source=",source,"traversal_list=",traversal_list)
  167. # currents = [{
  168. # "instance": source,
  169. # "ref": None,
  170. # "assoc_name": None,
  171. # "assoc_index": None,
  172. # "path": ""
  173. # }]
  174. # # currents = [source]
  175. # for (name, index) in traversal_list:
  176. # nexts = []
  177. # for current in currents:
  178. # association = current["instance"].associations[name]
  179. # if (index >= 0 ):
  180. # try:
  181. # nexts.append({
  182. # "instance": association.instances[index],
  183. # "ref": current["instance"],
  184. # "assoc_name": name,
  185. # "assoc_index": index,
  186. # "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
  187. # })
  188. # except KeyError:
  189. # # Entry was removed, so ignore this request
  190. # pass
  191. # elif (index == -1):
  192. # for i in association.instances:
  193. # nexts.append({
  194. # "instance": association.instances[i],
  195. # "ref": current["instance"],
  196. # "assoc_name": name,
  197. # "assoc_index": index,
  198. # "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
  199. # })
  200. # #nexts.extend( association.instances.values() )
  201. # else:
  202. # raise AssociationReferenceException("Incorrect index in association reference.")
  203. # currents = nexts
  204. # return currents
  205. # _handlers = {
  206. # # "narrow_cast": _handle_narrowcast,
  207. # "broad_cast": _handle_broadcast,
  208. # # "create_instance": _handle_create,
  209. # # "associate_instance": _handle_associate,
  210. # # "disassociate_instance": _handle_disassociate,
  211. # # "delete_instance": _handle_delete
  212. # }