|
@@ -1,10 +1,37 @@
|
|
|
import re
|
|
|
import abc
|
|
|
from typing import List, Tuple
|
|
|
-from sccd.runtime.event import Instance, Event, OutputEvent
|
|
|
+from sccd.runtime.event import Instance, Event, OutputEvent, InstancesTarget
|
|
|
from sccd.runtime.event_queue import Timestamp
|
|
|
from sccd.runtime.statecharts_core import StatechartInstance
|
|
|
|
|
|
+class RuntimeException(Exception):
|
|
|
+ """
|
|
|
+ Base class for runtime exceptions.
|
|
|
+ """
|
|
|
+ def __init__(self, message):
|
|
|
+ self.message = message
|
|
|
+ def __str__(self):
|
|
|
+ return repr(self.message)
|
|
|
+
|
|
|
+class AssociationException(RuntimeException):
|
|
|
+ """
|
|
|
+ Exception class thrown when an error occurs in a CRUD operation on associations.
|
|
|
+ """
|
|
|
+ pass
|
|
|
+
|
|
|
+class AssociationReferenceException(RuntimeException):
|
|
|
+ """
|
|
|
+ Exception class thrown when an error occurs when resolving an association reference.
|
|
|
+ """
|
|
|
+ pass
|
|
|
+
|
|
|
+class ParameterException(RuntimeException):
|
|
|
+ """
|
|
|
+ Exception class thrown when an error occurs when passing parameters.
|
|
|
+ """
|
|
|
+ pass
|
|
|
+
|
|
|
# TODO: Clean this mess up. Look at all object management operations and see how they can be improved.
|
|
|
class ObjectManager(Instance):
|
|
|
_regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$")
|
|
@@ -68,174 +95,176 @@ class ObjectManager(Instance):
|
|
|
raise ParameterException ("The broadcast event needs 2 parameters (source of event and event name).")
|
|
|
return OutputEvent(parameters[1], InstancesTarget(self.instances))
|
|
|
|
|
|
- def _handle_create(self, timestamp, parameters) -> List[OutputEvent]:
|
|
|
- if len(parameters) < 2:
|
|
|
- raise ParameterException ("The create event needs at least 2 parameters.")
|
|
|
+ # def _handle_create(self, timestamp, parameters) -> List[OutputEvent]:
|
|
|
+ # if len(parameters) < 2:
|
|
|
+ # raise ParameterException ("The create event needs at least 2 parameters.")
|
|
|
|
|
|
- source = parameters[0]
|
|
|
- association_name = parameters[1]
|
|
|
+ # source = parameters[0]
|
|
|
+ # association_name = parameters[1]
|
|
|
|
|
|
- traversal_list = self._assoc_ref(association_name)
|
|
|
- instances = self._get_instances(source, traversal_list)
|
|
|
+ # traversal_list = self._assoc_ref(association_name)
|
|
|
+ # instances = self._get_instances(source, traversal_list)
|
|
|
|
|
|
- association = source.associations[association_name]
|
|
|
- # association = self.instances_map[source].getAssociation(association_name)
|
|
|
- if association.allowedToAdd():
|
|
|
- ''' allow subclasses to be instantiated '''
|
|
|
- class_name = association.to_class if len(parameters) == 2 else parameters[2]
|
|
|
- instance = self._create(class_name)
|
|
|
- # new_instance = self.model.classes[class_name](parameters[3:])
|
|
|
- if not instance:
|
|
|
- raise ParameterException("Creating instance: no such class: " + class_name)
|
|
|
- output_events = instance.initialize(timestamp)
|
|
|
- try:
|
|
|
- index = association.addInstance(instance)
|
|
|
- except AssociationException as exception:
|
|
|
- raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception))
|
|
|
- p = instance.associations.get("parent")
|
|
|
- if p:
|
|
|
- p.addInstance(source)
|
|
|
- return output_events.append(OutputEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]), InstancesTarget([source])))
|
|
|
- else:
|
|
|
- return OutputEvent(Event("instance_creation_error", None, [association_name]), InstancesTarget([source]))
|
|
|
-
|
|
|
- def _handle_delete(self, timestamp, parameters) -> OutputEvent:
|
|
|
- if len(parameters) < 2:
|
|
|
- raise ParameterException ("The delete event needs at least 2 parameters.")
|
|
|
- else:
|
|
|
- source = parameters[0]
|
|
|
- association_name = parameters[1]
|
|
|
+ # association = source.associations[association_name]
|
|
|
+ # # association = self.instances_map[source].getAssociation(association_name)
|
|
|
+ # if association.allowedToAdd():
|
|
|
+ # ''' allow subclasses to be instantiated '''
|
|
|
+ # class_name = association.to_class if len(parameters) == 2 else parameters[2]
|
|
|
+ # instance = self._create(class_name)
|
|
|
+ # # new_instance = self.model.classes[class_name](parameters[3:])
|
|
|
+ # if not instance:
|
|
|
+ # raise ParameterException("Creating instance: no such class: " + class_name)
|
|
|
+ # output_events = instance.initialize(timestamp)
|
|
|
+ # try:
|
|
|
+ # index = association.addInstance(instance)
|
|
|
+ # except AssociationException as exception:
|
|
|
+ # raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception))
|
|
|
+ # p = instance.associations.get("parent")
|
|
|
+ # if p:
|
|
|
+ # p.addInstance(source)
|
|
|
+ # return output_events.append(OutputEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]), InstancesTarget([source])))
|
|
|
+ # else:
|
|
|
+ # return OutputEvent(Event("instance_creation_error", None, [association_name]), InstancesTarget([source]))
|
|
|
+
|
|
|
+ # def _handle_delete(self, timestamp, parameters) -> OutputEvent:
|
|
|
+ # if len(parameters) < 2:
|
|
|
+ # raise ParameterException ("The delete event needs at least 2 parameters.")
|
|
|
+ # else:
|
|
|
+ # source = parameters[0]
|
|
|
+ # association_name = parameters[1]
|
|
|
|
|
|
- traversal_list = self._assoc_ref(association_name)
|
|
|
- instances = self._get_instances(source, traversal_list)
|
|
|
- # association = self.instances_map[source].getAssociation(traversal_list[0][0])
|
|
|
- association = source.associations[traversal_list[0][0]]
|
|
|
+ # traversal_list = self._assoc_ref(association_name)
|
|
|
+ # instances = self._get_instances(source, traversal_list)
|
|
|
+ # # association = self.instances_map[source].getAssociation(traversal_list[0][0])
|
|
|
+ # association = source.associations[traversal_list[0][0]]
|
|
|
|
|
|
- for i in instances:
|
|
|
- try:
|
|
|
- for assoc_name in i["instance"].associations:
|
|
|
- if assoc_name != 'parent':
|
|
|
- traversal_list = self._assoc_ref(assoc_name)
|
|
|
- instances = self._get_instances(i["instance"], traversal_list)
|
|
|
- if len(instances) > 0:
|
|
|
- raise RuntimeException("Error removing instance from association %s, still %i children left connected with association %s" % (association_name, len(instances), assoc_name))
|
|
|
- # del i["instance"].controller.input_ports[i["instance"].narrow_cast_port]
|
|
|
- association.removeInstance(i["instance"])
|
|
|
- self.instances.remove(i["instance"])
|
|
|
- except AssociationException as exception:
|
|
|
- raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception))
|
|
|
- i["instance"].user_defined_destructor()
|
|
|
- i["instance"].stop()
|
|
|
+ # for i in instances:
|
|
|
+ # try:
|
|
|
+ # for assoc_name in i["instance"].associations:
|
|
|
+ # if assoc_name != 'parent':
|
|
|
+ # traversal_list = self._assoc_ref(assoc_name)
|
|
|
+ # instances = self._get_instances(i["instance"], traversal_list)
|
|
|
+ # if len(instances) > 0:
|
|
|
+ # raise RuntimeException("Error removing instance from association %s, still %i children left connected with association %s" % (association_name, len(instances), assoc_name))
|
|
|
+ # # del i["instance"].controller.input_ports[i["instance"].narrow_cast_port]
|
|
|
+ # association.removeInstance(i["instance"])
|
|
|
+ # self.instances.remove(i["instance"])
|
|
|
+ # except AssociationException as exception:
|
|
|
+ # raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception))
|
|
|
+ # i["instance"].user_defined_destructor()
|
|
|
+ # i["instance"].stop()
|
|
|
|
|
|
- return OutputEvent(Event("instance_deleted", parameters = [parameters[1]]), InstancesTarget([source]))
|
|
|
+ # return OutputEvent(Event("instance_deleted", parameters = [parameters[1]]), InstancesTarget([source]))
|
|
|
|
|
|
- def _handle_associate(self, timestamp, parameters) -> OutputEvent:
|
|
|
- if len(parameters) != 3:
|
|
|
- raise ParameterException ("The associate_instance event needs 3 parameters.")
|
|
|
- else:
|
|
|
- source = parameters[0]
|
|
|
- to_copy_list = self._get_instances(source, self._assoc_ref(parameters[1]))
|
|
|
- if len(to_copy_list) != 1:
|
|
|
- raise AssociationReferenceException ("Invalid source association reference.")
|
|
|
- wrapped_to_copy_instance = to_copy_list[0]["instance"]
|
|
|
- dest_list = self._assoc_ref(parameters[2])
|
|
|
- if len(dest_list) == 0:
|
|
|
- raise AssociationReferenceException ("Invalid destination association reference.")
|
|
|
- last = dest_list.pop()
|
|
|
- if last[1] != -1:
|
|
|
- raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.")
|
|
|
+ # def _handle_associate(self, timestamp, parameters) -> OutputEvent:
|
|
|
+ # if len(parameters) != 3:
|
|
|
+ # raise ParameterException ("The associate_instance event needs 3 parameters.")
|
|
|
+ # else:
|
|
|
+ # source = parameters[0]
|
|
|
+ # to_copy_list = self._get_instances(source, self._assoc_ref(parameters[1]))
|
|
|
+ # if len(to_copy_list) != 1:
|
|
|
+ # raise AssociationReferenceException ("Invalid source association reference.")
|
|
|
+ # wrapped_to_copy_instance = to_copy_list[0]["instance"]
|
|
|
+ # dest_list = self._assoc_ref(parameters[2])
|
|
|
+ # if len(dest_list) == 0:
|
|
|
+ # raise AssociationReferenceException ("Invalid destination association reference.")
|
|
|
+ # last = dest_list.pop()
|
|
|
+ # if last[1] != -1:
|
|
|
+ # raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.")
|
|
|
|
|
|
- added_links = []
|
|
|
- for i in self._get_instances(source, dest_list):
|
|
|
- association = i["instance"].associations[last[0]]
|
|
|
- if association.allowedToAdd():
|
|
|
- index = association.addInstance(wrapped_to_copy_instance)
|
|
|
- added_links.append(i["path"] + ("" if i["path"] == "" else "/") + last[0] + "[" + str(index) + "]")
|
|
|
+ # added_links = []
|
|
|
+ # for i in self._get_instances(source, dest_list):
|
|
|
+ # association = i["instance"].associations[last[0]]
|
|
|
+ # if association.allowedToAdd():
|
|
|
+ # index = association.addInstance(wrapped_to_copy_instance)
|
|
|
+ # added_links.append(i["path"] + ("" if i["path"] == "" else "/") + last[0] + "[" + str(index) + "]")
|
|
|
|
|
|
- return OutputEvent(Event("instance_associated", parameters = [added_links]), InstancesTarget([source]))
|
|
|
+ # return OutputEvent(Event("instance_associated", parameters = [added_links]), InstancesTarget([source]))
|
|
|
|
|
|
- def _handle_disassociate(self, timestamp, parameters) -> OutputEvent:
|
|
|
- if len(parameters) < 2:
|
|
|
- raise ParameterException ("The disassociate_instance event needs at least 2 parameters.")
|
|
|
- else:
|
|
|
- source = parameters[0]
|
|
|
- association_name = parameters[1]
|
|
|
- if not isinstance(association_name, list):
|
|
|
- association_name = [association_name]
|
|
|
- deleted_links = []
|
|
|
+ # def _handle_disassociate(self, timestamp, parameters) -> OutputEvent:
|
|
|
+ # if len(parameters) < 2:
|
|
|
+ # raise ParameterException ("The disassociate_instance event needs at least 2 parameters.")
|
|
|
+ # else:
|
|
|
+ # source = parameters[0]
|
|
|
+ # association_name = parameters[1]
|
|
|
+ # if not isinstance(association_name, list):
|
|
|
+ # association_name = [association_name]
|
|
|
+ # deleted_links = []
|
|
|
|
|
|
- for a_n in association_name:
|
|
|
- traversal_list = self._assoc_ref(a_n)
|
|
|
- instances = self._get_instances(source, traversal_list)
|
|
|
+ # for a_n in association_name:
|
|
|
+ # traversal_list = self._assoc_ref(a_n)
|
|
|
+ # instances = self._get_instances(source, traversal_list)
|
|
|
|
|
|
- for i in instances:
|
|
|
- try:
|
|
|
- index = i['ref'].associations[i['assoc_name']].removeInstance(i["instance"])
|
|
|
- deleted_links.append(a_n + "[" + str(index) + "]")
|
|
|
- except AssociationException as exception:
|
|
|
- raise RuntimeException("Error disassociating '" + a_n + "': " + str(exception))
|
|
|
+ # for i in instances:
|
|
|
+ # try:
|
|
|
+ # index = i['ref'].associations[i['assoc_name']].removeInstance(i["instance"])
|
|
|
+ # deleted_links.append(a_n + "[" + str(index) + "]")
|
|
|
+ # except AssociationException as exception:
|
|
|
+ # raise RuntimeException("Error disassociating '" + a_n + "': " + str(exception))
|
|
|
|
|
|
- return OutputEvent(Event("instance_disassociated", parameters = [deleted_links]), InstancesTarget([source]))
|
|
|
+ # return OutputEvent(Event("instance_disassociated", parameters = [deleted_links]), InstancesTarget([source]))
|
|
|
|
|
|
- def _handle_narrowcast(self, timestamp, parameters) -> OutputEvent:
|
|
|
- if len(parameters) != 3:
|
|
|
- raise ParameterException ("The narrow_cast event needs 3 parameters.")
|
|
|
- source, targets, cast_event = parameters
|
|
|
+ # def _handle_narrowcast(self, timestamp, parameters) -> OutputEvent:
|
|
|
+ # if len(parameters) != 3:
|
|
|
+ # raise ParameterException ("The narrow_cast event needs 3 parameters.")
|
|
|
+ # source, targets, cast_event = parameters
|
|
|
|
|
|
- if not isinstance(targets, list):
|
|
|
- targets = [targets]
|
|
|
-
|
|
|
- all_instances = []
|
|
|
- for target in targets:
|
|
|
- traversal_list = self._assoc_ref(target)
|
|
|
- instances = self._get_instances(source, traversal_list)
|
|
|
- all_instances.extend(instances)
|
|
|
- return OutputEvent(cast_event, instances)
|
|
|
+ # if not isinstance(targets, list):
|
|
|
+ # targets = [targets]
|
|
|
+
|
|
|
+ # all_instances = []
|
|
|
+ # for target in targets:
|
|
|
+ # traversal_list = self._assoc_ref(target)
|
|
|
+ # instances = self._get_instances(source, traversal_list)
|
|
|
+ # all_instances.extend(instances)
|
|
|
+ # return OutputEvent(cast_event, instances)
|
|
|
|
|
|
- def _get_instances(self, source, traversal_list):
|
|
|
- print("_get_instances(source=",source,"traversal_list=",traversal_list)
|
|
|
- currents = [{
|
|
|
- "instance": source,
|
|
|
- "ref": None,
|
|
|
- "assoc_name": None,
|
|
|
- "assoc_index": None,
|
|
|
- "path": ""
|
|
|
- }]
|
|
|
- # currents = [source]
|
|
|
- for (name, index) in traversal_list:
|
|
|
- nexts = []
|
|
|
- for current in currents:
|
|
|
- association = current["instance"].associations[name]
|
|
|
- if (index >= 0 ):
|
|
|
- try:
|
|
|
- nexts.append({
|
|
|
- "instance": association.instances[index],
|
|
|
- "ref": current["instance"],
|
|
|
- "assoc_name": name,
|
|
|
- "assoc_index": index,
|
|
|
- "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
|
|
|
- })
|
|
|
- except KeyError:
|
|
|
- # Entry was removed, so ignore this request
|
|
|
- pass
|
|
|
- elif (index == -1):
|
|
|
- for i in association.instances:
|
|
|
- nexts.append({
|
|
|
- "instance": association.instances[i],
|
|
|
- "ref": current["instance"],
|
|
|
- "assoc_name": name,
|
|
|
- "assoc_index": index,
|
|
|
- "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
|
|
|
- })
|
|
|
- #nexts.extend( association.instances.values() )
|
|
|
- else:
|
|
|
- raise AssociationReferenceException("Incorrect index in association reference.")
|
|
|
- currents = nexts
|
|
|
- return currents
|
|
|
-
|
|
|
- _handlers = {"narrow_cast": _handle_narrowcast,
|
|
|
- "broad_cast": _handle_broadcast,
|
|
|
- "create_instance": _handle_create,
|
|
|
- "associate_instance": _handle_associate,
|
|
|
- "disassociate_instance": _handle_disassociate,
|
|
|
- "delete_instance": _handle_delete}
|
|
|
+ # def _get_instances(self, source, traversal_list):
|
|
|
+ # print("_get_instances(source=",source,"traversal_list=",traversal_list)
|
|
|
+ # currents = [{
|
|
|
+ # "instance": source,
|
|
|
+ # "ref": None,
|
|
|
+ # "assoc_name": None,
|
|
|
+ # "assoc_index": None,
|
|
|
+ # "path": ""
|
|
|
+ # }]
|
|
|
+ # # currents = [source]
|
|
|
+ # for (name, index) in traversal_list:
|
|
|
+ # nexts = []
|
|
|
+ # for current in currents:
|
|
|
+ # association = current["instance"].associations[name]
|
|
|
+ # if (index >= 0 ):
|
|
|
+ # try:
|
|
|
+ # nexts.append({
|
|
|
+ # "instance": association.instances[index],
|
|
|
+ # "ref": current["instance"],
|
|
|
+ # "assoc_name": name,
|
|
|
+ # "assoc_index": index,
|
|
|
+ # "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
|
|
|
+ # })
|
|
|
+ # except KeyError:
|
|
|
+ # # Entry was removed, so ignore this request
|
|
|
+ # pass
|
|
|
+ # elif (index == -1):
|
|
|
+ # for i in association.instances:
|
|
|
+ # nexts.append({
|
|
|
+ # "instance": association.instances[i],
|
|
|
+ # "ref": current["instance"],
|
|
|
+ # "assoc_name": name,
|
|
|
+ # "assoc_index": index,
|
|
|
+ # "path": current["path"] + ("" if current["path"] == "" else "/") + name + "[" + str(index) + "]"
|
|
|
+ # })
|
|
|
+ # #nexts.extend( association.instances.values() )
|
|
|
+ # else:
|
|
|
+ # raise AssociationReferenceException("Incorrect index in association reference.")
|
|
|
+ # currents = nexts
|
|
|
+ # return currents
|
|
|
+
|
|
|
+ _handlers = {
|
|
|
+ # "narrow_cast": _handle_narrowcast,
|
|
|
+ "broad_cast": _handle_broadcast,
|
|
|
+ # "create_instance": _handle_create,
|
|
|
+ # "associate_instance": _handle_associate,
|
|
|
+ # "disassociate_instance": _handle_disassociate,
|
|
|
+ # "delete_instance": _handle_delete
|
|
|
+ }
|