Просмотр исходного кода

A fix for a major bug (when deleting an instance, can't make a new instance). Push this for my own sanity + starting to tune the target file (less code)

sampieters 1 год назад
Родитель
Сommit
8111793390

Разница между файлами не показана из-за своего большого размера
+ 1308 - 0
examples/BouncingBalls/PyDEVS/fixed_target.py


+ 1 - 1
examples/BouncingBalls/PyDEVS/runner.py

@@ -1,5 +1,5 @@
 import tkinter as tk
-import examples.BouncingBalls.PyDEVS.target as target
+import examples.BouncingBalls.PyDEVS.tuned_target as target
 from sccd.runtime.libs.ui_v2 import UI
 from sccd.runtime.DEVS_loop import DEVSSimulator
 

+ 110 - 99
examples/BouncingBalls/PyDEVS/bestbest_target.py

@@ -209,17 +209,18 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         self.outputs["fields"] = self.addOutPort("fields")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
-        self.instances.append(MainAppInstance(self))
+        self.instances[self.next_instance] = MainAppInstance(self)
+        self.next_instance += 1
+
         self.next_time = INFINITY
     
+
     def extTransition(self, inputs):
         self.simulated_time = (self.simulated_time + self.elapsed)
         self.next_time = 0
         all_inputs = []
-        if self.obj_manager_in in inputs:
-            all_inputs.extend(inputs[self.obj_manager_in])
-        if self.input in inputs:
-            all_inputs.extend(inputs[self.input])
+        for input_list in inputs.values():
+            all_inputs.extend(input_list)
         for input in all_inputs:
             if isinstance(input, str):
                 tem = eval(input)
@@ -236,13 +237,21 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
                 instance = self.instances[input[2].instance]
                 instance.start()
                 ev = Event("instance_started", None, [f"{input[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[0], input[1], ev))
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "delete_instance":
-                pass
-            elif input[2].name == "associate_instance":
-                pass
-            elif input[2].name == "disassociate_instance":
-                pass
+                for index in input[2].parameters[1]:
+                    i = self.instances[index]
+                    for assoc_name in i.associations:
+                        if not (assoc_name == "parent"):
+                            traversal_list = self.processAssociationReference(assoc_name)
+                            instances = self.getInstances(i["instance"], traversal_list)
+                            if len(instances) > 0:
+                                pass
+                    i.user_defined_destructor()
+                    i.stop()
+                self.instances = {key: value for key, value in self.instances.items() if key not in input[2].parameters[1]}
+                ev = Event("instance_deleted", None, input[2].parameters[1], input[2].instance)
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "instance_created":
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
@@ -250,11 +259,12 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
             elif input[2].name == "instance_deleted":
-                pass
-            elif input[2].name == "instance_associated":
-                pass
-            elif input[2].name == "instance_disassociated":
-                pass
+                instance = self.instances[input[2].instance]
+                for association in instance.associations.items():
+                    if association[1].to_class == input[0]:
+                        for index in input[2].parameters:
+                            association[1].removeInstance(index)
+                instance.addEvent(input[2])
             else:
                 ev = input[2]
                 self.addInput(ev)
@@ -321,7 +331,7 @@ class FieldInstance(RuntimeClassBase):
         
         # call user defined constructor
         FieldInstance.user_defined_constructor(self)
-        self.inports["field_ui"] = ('field_ui', len(atomdevs.instances))
+        self.inports["field_ui"] = ('field_ui', atomdevs.next_instance)
     
     def user_defined_constructor(self):
         pass
@@ -573,35 +583,43 @@ class Field(AtomicDEVS, ObjectManagerBase):
         self.simulated_time = (self.simulated_time + self.elapsed)
         self.next_time = 0
         all_inputs = []
-        if self.field_ui in inputs:
-            all_inputs.extend(inputs[self.field_ui])
-        if self.obj_manager_in in inputs:
-            all_inputs.extend(inputs[self.obj_manager_in])
-        if self.input in inputs:
-            all_inputs.extend(inputs[self.input])
+        for input_list in inputs.values():
+            all_inputs.extend(input_list)
         for input in all_inputs:
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
             elif input[2].name == "create_instance":
                 new_instance = FieldInstance(self)
-                self.instances.append(new_instance)
+                self.instances[self.next_instance] = new_instance
                 p = new_instance.associations.get("parent")
                 if p:
                     p.addInstance(input[2].instance)
-                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{len(self.instances)-1}]"], input[2].instance)
+                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{self.next_instance}]"], input[2].instance)
                 self.to_send.append((input[1], input[0], ev))
+                self.next_instance += 1
             elif input[2].name == "start_instance":
-                instance = self.instances[input[2].instance]
+                test = self.processAssociationReference(input[2].parameters[0])
+                index = test[0][1]
+                instance = self.instances[index]
                 instance.start()
-                ev = Event("instance_started", None, [f"{input[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[0], input[1], ev))
+                ev = Event("instance_started", None, [input[2].parameters[0]], input[2].instance)
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "delete_instance":
-                pass
-            elif input[2].name == "associate_instance":
-                pass
-            elif input[2].name == "disassociate_instance":
-                pass
+                for index in input[2].parameters[1]:
+                    i = self.instances[index]
+                    for assoc_name in i.associations:
+                        if not (assoc_name == "parent"):
+                            traversal_list = self.processAssociationReference(assoc_name)
+                            # TODO: Still need to check
+                            #instances = self.getInstances(i["instance"], traversal_list)
+                            #if len(instances) > 0:
+                            #    pass
+                    i.user_defined_destructor()
+                    i.stop()
+                self.instances = {key: value for key, value in self.instances.items() if key not in input[2].parameters[1]}
+                ev = Event("instance_deleted", None, input[2].parameters[1], input[2].instance)
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "instance_created":
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
@@ -610,15 +628,11 @@ class Field(AtomicDEVS, ObjectManagerBase):
                 instance.addEvent(input[2])
             elif input[2].name == "instance_deleted":
                 instance = self.instances[input[2].instance]
-                for assoc in instance.associations.items():
-                    if assoc[1].to_class == input[0]:
+                for association in instance.associations.items():
+                    if association[1].to_class == input[0]:
                         for index in input[2].parameters:
-                            assoc[1].removeInstance(index)
-                        break
-            elif input[2].name == "instance_associated":
-                pass
-            elif input[2].name == "instance_disassociated":
-                pass
+                            association[1].removeInstance(index)
+                instance.addEvent(input[2])
             else:
                 ev = input[2]
                 self.addInput(ev)
@@ -684,7 +698,7 @@ class ButtonInstance(RuntimeClassBase):
         
         # call user defined constructor
         ButtonInstance.user_defined_constructor(self, window_id, event_name, button_text)
-        self.inports["button_ui"] = ('button_ui', len(atomdevs.instances))
+        self.inports["button_ui"] = ('button_ui', atomdevs.next_instance)
     
     def user_defined_constructor(self, window_id, event_name, button_text):
         self.window_id = window_id;
@@ -769,35 +783,41 @@ class Button(AtomicDEVS, ObjectManagerBase):
         self.simulated_time = (self.simulated_time + self.elapsed)
         self.next_time = 0
         all_inputs = []
-        if self.button_ui in inputs:
-            all_inputs.extend(inputs[self.button_ui])
-        if self.obj_manager_in in inputs:
-            all_inputs.extend(inputs[self.obj_manager_in])
-        if self.input in inputs:
-            all_inputs.extend(inputs[self.input])
+        for input_list in inputs.values():
+            all_inputs.extend(input_list)
         for input in all_inputs:
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
             elif input[2].name == "create_instance":
                 new_instance = ButtonInstance(self, input[2].parameters[2], input[2].parameters[3], input[2].parameters[4])
-                self.instances.append(new_instance)
+                #self.instances.append(new_instance)
+                self.instances[self.next_instance] = new_instance
                 p = new_instance.associations.get("parent")
                 if p:
                     p.addInstance(input[2].instance)
-                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{len(self.instances)-1}]"], input[2].instance)
+                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{self.next_instance}]"], input[2].instance)
                 self.to_send.append((input[1], input[0], ev))
+                self.next_instance += 1
             elif input[2].name == "start_instance":
                 instance = self.instances[input[2].instance]
                 instance.start()
                 ev = Event("instance_started", None, [f"{input[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[0], input[1], ev))
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "delete_instance":
-                pass
-            elif input[2].name == "associate_instance":
-                pass
-            elif input[2].name == "disassociate_instance":
-                pass
+                for index in input[2].parameters[1]:
+                    i = self.instances[index]
+                    for assoc_name in i.associations:
+                        if not (assoc_name == "parent"):
+                            traversal_list = self.processAssociationReference(assoc_name)
+                            instances = self.getInstances(i["instance"], traversal_list)
+                            if len(instances) > 0:
+                                pass
+                    i.user_defined_destructor()
+                    i.stop()
+                self.instances = {key: value for key, value in self.instances.items() if key not in input[2].parameters[1]}
+                ev = Event("instance_deleted", None, input[2].parameters[1], input[2].instance)
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "instance_created":
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
@@ -805,11 +825,12 @@ class Button(AtomicDEVS, ObjectManagerBase):
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
             elif input[2].name == "instance_deleted":
-                pass
-            elif input[2].name == "instance_associated":
-                pass
-            elif input[2].name == "instance_disassociated":
-                pass
+                instance = self.instances[input[2].instance]
+                for association in instance.associations.items():
+                    if association[1].to_class == input[0]:
+                        for index in input[2].parameters:
+                            association[1].removeInstance(index)
+                instance.addEvent(input[2])
             else:
                 ev = input[2]
                 self.addInput(ev)
@@ -874,7 +895,7 @@ class BallInstance(RuntimeClassBase):
         
         # call user defined constructor
         BallInstance.user_defined_constructor(self, canvas_id, x, y)
-        self.inports["ball_ui"] = ('ball_ui', len(atomdevs.instances))
+        self.inports["ball_ui"] = ('ball_ui', atomdevs.next_instance)
     
     def user_defined_constructor(self, canvas_id, x, y):
         self.canvas_id = canvas_id;
@@ -1087,56 +1108,42 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         self.simulated_time = (self.simulated_time + self.elapsed)
         self.next_time = 0
         all_inputs = []
-        if self.ball_ui in inputs:
-            all_inputs.extend(inputs[self.ball_ui])
-        if self.obj_manager_in in inputs:
-            all_inputs.extend(inputs[self.obj_manager_in])
-        if self.input in inputs:
-            all_inputs.extend(inputs[self.input])
+        for input_list in inputs.values():
+            all_inputs.extend(input_list)
         for input in all_inputs:
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
             elif input[2].name == "create_instance":
                 new_instance = BallInstance(self, input[2].parameters[2], input[2].parameters[3], input[2].parameters[4])
-                self.instances.append(new_instance)
+                self.instances[self.next_instance] = new_instance
                 p = new_instance.associations.get("parent")
                 if p:
                     p.addInstance(input[2].instance)
-                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{len(self.instances)-1}]"], input[2].instance)
+                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{self.next_instance}]"], input[2].instance)
                 self.to_send.append((input[1], input[0], ev))
+                self.next_instance += 1
             elif input[2].name == "start_instance":
-                instance = self.instances[input[2].instance]
+                test = self.processAssociationReference(input[2].parameters[0])
+                index = test[0][1]
+                instance = self.instances[index]
                 instance.start()
-                ev = Event("instance_started", None, [f"{input[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[0], input[1], ev))
+                ev = Event("instance_started", None, [input[2].parameters[0]], input[2].instance)
+                self.to_send.append((input[1], input[0], ev))
             elif input[2].name == "delete_instance":
                 for index in input[2].parameters[1]:
                     i = self.instances[index]
-                    try:
-                        for assoc_name in i.associations:
-                            if assoc_name != 'parent':
-                                traversal_list = self.processAssociationReference(assoc_name)
-                                instances = self.getInstances(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))
-                        # TODO: These still do need to be implemented
-                        #association.removeInstance(i["instance"])
-                        #self.eventless.discard(i["instance"])
-                    except AssociationException as exception:
-                        raise RuntimeException("Error removing instance from association '" + association_name + "': " + str(exception))
+                    for assoc_name in i.associations:
+                        if not (assoc_name == "parent"):
+                            traversal_list = self.processAssociationReference(assoc_name)
+                            instances = self.getInstances(i["instance"], traversal_list)
+                            if len(instances) > 0:
+                                pass
                     i.user_defined_destructor()
                     i.stop()
-                self.instances = [self.instances[i] for i in range(len(self.instances)) if i not in input[2].parameters[1]]
-                #source.addEvent(Event("instance_deleted", parameters = [parameters[1]]))
-
+                self.instances = {key: value for key, value in self.instances.items() if key not in input[2].parameters[1]}
                 ev = Event("instance_deleted", None, input[2].parameters[1], input[2].instance)
                 self.to_send.append((input[1], input[0], ev))
-
-            elif input[2].name == "associate_instance":
-                pass
-            elif input[2].name == "disassociate_instance":
-                pass
             elif input[2].name == "instance_created":
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
@@ -1144,11 +1151,12 @@ class Ball(AtomicDEVS, ObjectManagerBase):
                 instance = self.instances[input[2].instance]
                 instance.addEvent(input[2])
             elif input[2].name == "instance_deleted":
-                pass
-            elif input[2].name == "instance_associated":
-                pass
-            elif input[2].name == "instance_disassociated":
-                pass
+                instance = self.instances[input[2].instance]
+                for association in instance.associations.items():
+                    if association[1].to_class == input[0]:
+                        for index in input[2].parameters:
+                            association[1].removeInstance(index)
+                instance.addEvent(input[2])
             else:
                 ev = input[2]
                 self.addInput(ev)
@@ -1220,7 +1228,10 @@ class ObjectManager(AtomicDEVS):
     def outputFnc(self):
         out_dict = {}
         for (source, target, message) in self.State.to_send:
-            if self.output[target] in out_dict:
+            # Special case for the first
+            if target is None:
+                pass
+            elif self.output[target] in out_dict:
                 out_dict[self.output[target]].append((source, target, message))
             else:
                 out_dict[self.output[target]] = [(source, target, message)]

+ 37 - 17
sccd/runtime/DEVS_statecharts_core.py

@@ -68,7 +68,7 @@ class Association(object):
             index = self.instances_to_ids[instance]
             del self.instances[index]
             del self.instances_to_ids[instance]
-            self.size -= 1
+            #self.size -= 1
             return index
         else:
             raise AssociationException("Not allowed to remove the instance from the association.")
@@ -682,7 +682,8 @@ class ObjectManagerBase(object):
         self.to_send = []
 
         self.events = EventQueue()
-        self.instances = []
+        self.instances = {}
+        self.next_instance = 0
         self.instance_times = []
         self.eventless = set()
         self.regex_pattern = re.compile("^([a-zA-Z_]\w*)(?:\[(\d+)\])?$")
@@ -755,12 +756,13 @@ class ObjectManagerBase(object):
             #TODO: get the first field, should be dynamically
             #temp = None
 
+            # TODO: instance is now given in parameters
             temp = e.instance
             if temp is None:
                 temp = self.processAssociationReference(e.parameters[0])[0]
                 temp = temp[1]
-            target_instance = list(self.instances)[temp]
-            #target_instance = self.State[0]
+            #target_instance = list(self.instances)[temp]
+            target_instance = self.instances[temp]
             if target_instance == None:
                 self.broadcast(e, event_time - self.simulated_time)
             else:
@@ -815,13 +817,22 @@ class ObjectManagerBase(object):
             raise ParameterException ("The start instance event needs 2 parameters.")  
         else:
             source = parameters[0]
+            source_index = None
+            try:
+                source_traversal_list = self.processAssociationReference(source.association_name)
+                source_index = source_traversal_list[0][1]
+            except:
+                source_index = 0 
+                
             traversal_list = self.processAssociationReference(parameters[1])
 
             # TODO: This does not work as the mainapp should start the field instance now, but this is not working yet
             for i in self.getInstances(source, traversal_list):
                 #i["instance"].start()
                 # TODO: start instance over a link from mainapp to field
-                self.to_send.append((i['assoc_name'], i['to_class'], Event("start_instance", None, [], i['instance'])))
+                #ev = Event("start_instance", None, [], i['instance'])
+                ev = Event("start_instance", None, [i['path']], source_index)
+                self.to_send.append((self.name, i['to_class'], ev))
 
 
 
@@ -850,7 +861,7 @@ class ObjectManagerBase(object):
         association_name = parameters[1]
 
         index = 0
-        for inst in self.instances:
+        for (key, inst) in self.instances.items():
             index += len(inst.associations[association_name].instances)
 
 
@@ -858,13 +869,11 @@ class ObjectManagerBase(object):
         instances = self.getInstances(source, traversal_list)
         
         association = source.associations[association_name]
-
+        # TODO: eveything inside the assocation is still wrong, len(instances) can't be 
         if association.allowedToAdd():
             ''' allow subclasses to be instantiated '''
             class_name = association.to_class if len(parameters) == 2 else parameters[2]
 
-            new_instance = association_name + f"[{len(instances)}]"
-
             try:
                 new_index = association.addInstance(index)
             except AssociationException as exception:
@@ -911,12 +920,17 @@ class ObjectManagerBase(object):
             #    i["instance"].stop()
                 
             #source.addEvent(Event("instance_deleted", parameters = [parameters[1]]))
-
-            index = self.processAssociationReference(source.association_name)
-            index = index[0][1]
-
-
-            params = list(association.instances.values())
+            index = None
+            try:
+                index = self.processAssociationReference(source.association_name)
+                index = index[0][1]
+                params = list(association.instances.values())
+            except:
+                index = self.processAssociationReference(association_name)
+                params = [index[0][1]]
+                # TODO: This is only for the default, don't know if it will work always --> beter check source with instances 
+                index = 0
+            
 
             self.to_send.append((self.name, association.to_class, Event("delete_instance", None, [parameters[1], params], index)))
 
@@ -1011,7 +1025,7 @@ class ObjectManagerBase(object):
             for i in self.getInstances(source, traversal_list):
                 # TODO: port cannot be none but don't know yet how to do port 
                 ev = Event(cast_event.name, None, cast_event.parameters, i["instance"])
-                self.to_send.append((i["assoc_name"], i['to_class'], ev))
+                self.to_send.append((self.name, i['to_class'], ev))
 
                 #to_send_event = Event(cast_event.name, i["instance"].narrow_cast_port, cast_event.parameters)
                 #i["instance"].controller.addInput(to_send_event, force_internal=True)
@@ -1072,4 +1086,10 @@ class ObjectManagerBase(object):
         return instance
 
     def addMyOwnOutputListener(self, listener):
-        self.output_listeners.append(listener)
+        self.output_listeners.append(listener)
+
+    def my_extTransition(self, input):
+        pass
+
+    def my_intTransition(self):
+        pass