Browse Source

fixed the elif in generator and fixed the "lag" in the ball for external transition

sampieters 1 year ago
parent
commit
4d3c534e4e

+ 3 - 3
README.md

@@ -97,6 +97,6 @@ python3 runner.py
 ### Timer
 
 ## TODO
-fill parameters in constructor (e.g. Button, Ball).
-some "if" should be "elif"
-self.inports["elem"] should only be "elem"
+1) fill parameters in constructor (e.g. Button, Ball).
+2) self.inports["elem"] should only be "elem"
+3) Convert DEVui_v2 to ui_v2

+ 67 - 59
examples/BouncingBalls/PyDEVS/best_target.py

@@ -242,9 +242,24 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
                 ev = Event("instance_disassociated", None, parameters=[TODO])
                 self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
+                association = self.processAssociationReference(input[3].parameters[0])[0]
                 instance = list(self.instances)[input[2]]
+                p = instance.associations.get(association[0])
+                if p:
+                    p.addInstance(input[3].parameters[0])
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
+                #instance.associations['fields'].instances[0] = input[3].parameters[0]
+
+
+
+                #association = self.processAssociationReference(input[3].parameters[0])[0]
+                #instance = list(self.instances)[input[2]]
+                #p = instance.associations.get(association[0])
+                #if p:
+                #    p.addInstance(input[3].parameters[0])
+                #instance.addEvent(input[3])
+
+                
             elif input[3].name == "instance_started":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -260,6 +275,9 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
             elif input[3].name == "set_association_name":
                 ev = input[3]
                 self.addInput(ev, force_internal=True)
+            else:
+                ev = input[3]
+                self.addInput(ev)
         return self.instances
     
     def intTransition(self):
@@ -301,6 +319,8 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         return self.next_time
 
 class FieldInstance(RuntimeClassBase):
+    num_instances = 0
+
     def __init__(self, atomdevs):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
@@ -323,6 +343,9 @@ class FieldInstance(RuntimeClassBase):
         
         # call user defined constructor
         FieldInstance.user_defined_constructor(self)
+
+        self.inports['field_ui'] = ('field_ui', FieldInstance.num_instances)
+        FieldInstance.num_instances += 1
     
     def user_defined_constructor(self):
         pass
@@ -485,10 +508,10 @@ class FieldInstance(RuntimeClassBase):
         self.states["/root/running"].addTransition(_root_running_0)
     
     def _root_creating_window_enter(self):
-        self.big_step.outputEvent(Event("create_window", self.getOutPortName("ui"), [800, 600, "BouncingBalls", 'field_ui']))
+        self.big_step.outputEvent(Event("create_window", self.getOutPortName("ui"), [800, 600, "BouncingBalls",  self.inports['field_ui']]))
     
     def _root_creating_canvas_enter(self):
-        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, CANVAS_WIDTH, CANVAS_HEIGHT, {'background':'#eee'}, 'field_ui']))
+        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, CANVAS_WIDTH, CANVAS_HEIGHT, {'background':'#eee'},  self.inports['field_ui']]))
     
     def _root_creating_button_enter(self):
         self.big_step.outputEventOM(Event("create_instance", None, [self, "buttons", "Button", self.window_id, 'create_new_field', 'Spawn New Window']))
@@ -504,15 +527,15 @@ class FieldInstance(RuntimeClassBase):
     def _root_creating_window_0_exec(self, parameters):
         window_id = parameters[0]
         self.window_id = window_id
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [window_id, ui.EVENTS.WINDOW_CLOSE, 'window_close', 'field_ui']))
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [window_id, ui.EVENTS.KEY_PRESS, 'key_press', 'field_ui']))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [window_id, ui.EVENTS.WINDOW_CLOSE, 'window_close',  self.inports['field_ui']]))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [window_id, ui.EVENTS.KEY_PRESS, 'key_press',  self.inports['field_ui']]))
     
     def _root_creating_canvas_0_exec(self, parameters):
         canvas_id = parameters[0]
         self.canvas_id = canvas_id
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RIGHT_CLICK, 'right_click', 'field_ui']))
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_MOVE, 'mouse_move', 'field_ui']))
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release', 'field_ui']))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RIGHT_CLICK, 'right_click',  self.inports['field_ui']]))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_MOVE, 'mouse_move',  self.inports['field_ui']]))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release',  self.inports['field_ui']]))
     
     def _root_creating_button_0_exec(self, parameters):
         association_name = parameters[0]
@@ -568,9 +591,6 @@ class Field(AtomicDEVS, ObjectManagerBase):
         self.field_ui = self.addInPort("field_ui")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
-
-        self.inports["field_ui"] = self.addInputPort("field_ui", self)
-
         self.next_time = INFINITY
     
     def extTransition(self, inputs):
@@ -587,9 +607,17 @@ class Field(AtomicDEVS, ObjectManagerBase):
                 tem = eval(input)
                 self.addInput(tem)
             elif input[3].name == "create_instance":
-                self.instances.add(FieldInstance(self))
+                new_instance = FieldInstance(self)
+                self.instances.add(new_instance)
+
+                #TODO: hardcoded but needs to be fixed
+                p = new_instance.associations.get("parent")
+                if p:
+                    p.addInstance(f"mainapp[0]")
+
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((input[1], input[0], input[2], ev))
+
             elif input[3].name == "start_instance":
                 instance = list(self.instances)[input[2]]
                 instance.start()
@@ -671,6 +699,8 @@ class Field(AtomicDEVS, ObjectManagerBase):
         return self.next_time
 
 class ButtonInstance(RuntimeClassBase):
+    num_instances = 0
+
     def __init__(self, atomdevs, window_id, event_name, button_text):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
@@ -692,6 +722,9 @@ class ButtonInstance(RuntimeClassBase):
         
         # call user defined constructor
         ButtonInstance.user_defined_constructor(self, window_id, event_name, button_text)
+
+        self.inports['button_ui'] = ('button_ui', ButtonInstance.num_instances)
+        ButtonInstance.num_instances += 1
     
     def user_defined_constructor(self, window_id, event_name, button_text):
         self.window_id = window_id;
@@ -734,12 +767,12 @@ class ButtonInstance(RuntimeClassBase):
         self.states["/running"].addTransition(_running_0)
     
     def _creating_button_enter(self):
-        self.big_step.outputEvent(Event("create_button", self.getOutPortName("ui"), [self.window_id, self.event_name, 'button_ui']))
+        self.big_step.outputEvent(Event("create_button", self.getOutPortName("ui"), [self.window_id, self.event_name,  self.inports['button_ui']]))
     
     def _creating_button_0_exec(self, parameters):
         button_id = parameters[0]
         self.button_id = button_id
-        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [button_id, ui.EVENTS.MOUSE_CLICK, "mouse_click", 'button_ui']))
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [button_id, ui.EVENTS.MOUSE_CLICK, "mouse_click",  self.inports['button_ui']]))
     
     def _running_0_exec(self, parameters):
         x = parameters[0]
@@ -786,44 +819,13 @@ class Button(AtomicDEVS, ObjectManagerBase):
                 tem = eval(input)
                 self.addInput(tem)
             elif input[3].name == "create_instance":
-                '''
-                if len(parameters) < 2:
-                    raise ParameterException ("The create event needs at least 2 parameters.")
-
-                source = parameters[0]
-                association_name = parameters[1]
-        
-                traversal_list = self.processAssociationReference(association_name)
-                instances = self.getInstances(source, traversal_list)
-                
-                association = source.associations[association_name]
-                # association = self.instances_map[source].getAssociation(association_name)
-                if association.allowedToAdd():
-                    class_name = association.to_class if len(parameters) == 2 else parameters[2]
-                    new_instance = self.createInstance(class_name, parameters[3:])
-                    if not new_instance:
-                        raise ParameterException("Creating instance: no such class: " + class_name)
-                    # index = association.addInstance(new_instance)
-                    try:
-                        index = association.addInstance(new_instance)
-                    except AssociationException as exception:
-                        raise RuntimeException("Error adding instance to association '" + association_name + "': " + str(exception))
-                    p = new_instance.associations.get("parent")
-                    if p:
-                        p.addInstance(source)
-                    source.addEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]))
-                    return [source, association_name+"["+str(index)+"]"]
-                else:
-                    source.addEvent(Event("instance_creation_error", None, [association_name]))
-                    return []
-                '''
-
                 new_instance = ButtonInstance(self, input[3].parameters[1], input[3].parameters[2], input[3].parameters[3])
                 self.instances.add(new_instance)
 
-                #p = new_instance.associations.get("parent")
-                #if p:
-                #    p.addInstance(f"{input[3].parameters[0]}[{input[3].parameters[1]}]")
+                #TODO: hardcoded but needs to be fixed
+                p = new_instance.associations.get("parent")
+                if p:
+                    p.addInstance(f"fields[0]")
 
 
 
@@ -906,6 +908,8 @@ class Button(AtomicDEVS, ObjectManagerBase):
         return self.next_time
 
 class BallInstance(RuntimeClassBase):
+    num_instances = 0
+
     def __init__(self, atomdevs, canvas_id, x, y):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
@@ -926,6 +930,9 @@ class BallInstance(RuntimeClassBase):
         
         # call user defined constructor
         BallInstance.user_defined_constructor(self, canvas_id, x, y)
+
+        self.inports['ball_ui'] = ('ball_ui', BallInstance.num_instances)
+        BallInstance.num_instances += 1
     
     def user_defined_constructor(self, canvas_id, x, y):
         self.canvas_id = canvas_id;
@@ -1033,7 +1040,7 @@ class BallInstance(RuntimeClassBase):
         self.states["/main_behaviour/selected"].addTransition(_main_behaviour_selected_1)
     
     def _main_behaviour_creating_circle_enter(self):
-        self.big_step.outputEvent(Event("create_circle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.r, {'fill':'#000'}, 'ball_ui']))
+        self.big_step.outputEvent(Event("create_circle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.r, {'fill':'#000'},  self.inports['ball_ui']]))
     
     def _main_behaviour_bouncing_enter(self):
         self.addTimer(0, 0.02)
@@ -1049,9 +1056,9 @@ class BallInstance(RuntimeClassBase):
         canvas_id = parameters[0]
         circle_id = parameters[1]
         self.circle_id = circle_id
-        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_PRESS, 'mouse_press', 'ball_ui']))
-        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_MOVE, 'mouse_move', 'ball_ui']))
-        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release', 'ball_ui']))
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_PRESS, 'mouse_press',  self.inports['ball_ui']]))
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_MOVE, 'mouse_move',  self.inports['ball_ui']]))
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release',  self.inports['ball_ui']]))
     
     def _main_behaviour_bouncing_0_exec(self, parameters):
         # Invert velocity when colliding with canvas border:
@@ -1135,6 +1142,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         self.next_time = INFINITY
     
     def extTransition(self, inputs):
+        self.simulated_time += self.elapsed
         self.next_time = 0
         all_inputs = []
         if self.ball_ui in inputs:
@@ -1152,11 +1160,10 @@ class Ball(AtomicDEVS, ObjectManagerBase):
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((self.name, input[0], input[2], ev))
             elif input[3].name == "start_instance":
-                association = self.processAssociationReference(input[3].parameters[0])[0]
-                instance = list(self.instances)[association[1]]
+                instance = list(self.instances)[input[3].parameters[0]]
                 instance.start()
                 ev = Event("instance_started", None, parameters=[input[3].parameters[0]])
-                self.to_send.append((input[0], input[1], association[1], ev))
+                self.to_send.append((input[0], input[1], input[3].parameters[0], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[])
                 self.to_send.append((self.name, input[0], input[2], ev))
@@ -1172,8 +1179,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
                 instance.addEvent(input[3])
                 instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_started":
-                association = self.processAssociationReference(input[3].parameters[0])[0]
-                instance = list(self.instances)[association[1]]
+                instance = list(self.instances)[input[3].parameters[0]]
                 instance.addEvent(input[3])
             elif input[3].name == "instance_deleted":
                 instance = list(self.instances)[input[2]]
@@ -1190,7 +1196,8 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         return self.instances
     
     def intTransition(self):
-        earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        # TODO: self.simulated_time added here, do this everywhere
+        earliest = min(self.getEarliestEventTime(), self.simulated_time + self.input_queue.getEarliestTime())
         if not (earliest == INFINITY):
             self.simulated_time = earliest
         self.to_send = []
@@ -1203,6 +1210,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
             self.next_time = INFINITY
         else:
             self.next_time = next_earliest - earliest
+            #helping = self.time_next[0] - self.time_last[0]
         return self.instances
     
     def outputFnc(self):

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

@@ -1,5 +1,5 @@
 from pypdevs.simulator import Simulator
-import examples.BouncingBalls.PyDEVS.best_target as best_target
+import examples.BouncingBalls.PyDEVS.best_target as target
 
 from tkinter import *
 from sccd.runtime.libs.DEVui_v2 import UI
@@ -14,7 +14,7 @@ class OutputListener:
 			method(*event.parameters)
 
 if __name__ == '__main__':
-	model = best_target.Controller(name="controller")
+	model = target.Controller(name="controller")
 	refs = {"ui": model.ui, "field_ui": model.atomic1.field_ui, "button_ui": model.atomic2.button_ui, "ball_ui": model.atomic3.ball_ui}
 
 	tkroot = Tk()

+ 8 - 8
examples/BouncingBalls/PyDEVS/target.py

@@ -223,7 +223,7 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
-            if input[3].name == "create_instance":
+            elif input[3].name == "create_instance":
                 self.instances.add(MainAppInstance(self))
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((input[1], input[0], input[2], ev))
@@ -272,7 +272,7 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
         if not (len(self.to_send) == 0):
             self.next_time = 0
-        if next_earliest == INFINITY:
+        elif next_earliest == INFINITY:
             self.next_time = INFINITY
         else:
             self.next_time = next_earliest - earliest
@@ -583,7 +583,7 @@ class Field(AtomicDEVS, ObjectManagerBase):
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
-            if input[3].name == "create_instance":
+            elif input[3].name == "create_instance":
                 self.instances.add(FieldInstance(self))
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((input[1], input[0], input[2], ev))
@@ -632,7 +632,7 @@ class Field(AtomicDEVS, ObjectManagerBase):
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
         if not (len(self.to_send) == 0):
             self.next_time = 0
-        if next_earliest == INFINITY:
+        elif next_earliest == INFINITY:
             self.next_time = INFINITY
         else:
             self.next_time = next_earliest - earliest
@@ -775,7 +775,7 @@ class Button(AtomicDEVS, ObjectManagerBase):
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
-            if input[3].name == "create_instance":
+            elif input[3].name == "create_instance":
                 self.instances.add(ButtonInstance(self))
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((input[1], input[0], input[2], ev))
@@ -824,7 +824,7 @@ class Button(AtomicDEVS, ObjectManagerBase):
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
         if not (len(self.to_send) == 0):
             self.next_time = 0
-        if next_earliest == INFINITY:
+        elif next_earliest == INFINITY:
             self.next_time = INFINITY
         else:
             self.next_time = next_earliest - earliest
@@ -1094,7 +1094,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
-            if input[3].name == "create_instance":
+            elif input[3].name == "create_instance":
                 self.instances.add(BallInstance(self))
                 ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
                 self.to_send.append((input[1], input[0], input[2], ev))
@@ -1143,7 +1143,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
         if not (len(self.to_send) == 0):
             self.next_time = 0
-        if next_earliest == INFINITY:
+        elif next_earliest == INFINITY:
             self.next_time = INFINITY
         else:
             self.next_time = next_earliest - earliest

File diff suppressed because it is too large
+ 1255 - 0
examples/BouncingBalls/PyDEVS/testtarget.py


+ 1 - 1
examples/Timer/PyDEVS/target.py

@@ -47,7 +47,7 @@ class MainAppInstance(RuntimeClassBase):
     
     # user defined method
     def update_timers(self):
-        self.canvas.element.itemconfigure(self.clock_text, text=str('%.2f' % (self.getSimulatedTime() / 1000.0)))
+        self.canvas.element.itemconfigure(self.clock_text, text=str('%.2f' % (self.getSimulatedTime())))
         #self.canvas.element.itemconfigure(self.actual_clock_text, text='%.2f' % (time() / 1000.0))
     
     

+ 5 - 10
sccd/compiler/DEVS_generator.py

@@ -378,10 +378,10 @@ class DEVSGenerator(Visitor):
 
         self.writer.beginForLoopIterateArray("all_inputs", "input")
        
-        self.writer.beginIf(GLC.FunctionCall("isinstance", ["input", "str"]))
+        self.writer.beginElseIf(GLC.FunctionCall("isinstance", ["input", "str"]))
         self.writer.addAssignment("tem", GLC.FunctionCall("eval", ["input"]))
         self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addInput"), ["tem"]))
-        self.writer.endIf()
+        self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("create_instance")))
         self.writer.add(GLC.FunctionCall(GLC.SelfProperty("instances.add"), [GLC.FunctionCall(f"{class_node.name}Instance", ["self"])]))
@@ -443,11 +443,7 @@ class DEVSGenerator(Visitor):
         self.writer.addAssignment("ev", "input[3]")
         self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addInput"), ["ev", "force_internal=True"]))
         self.writer.endElseIf()
-
-
-
-
-
+        
         self.writer.endForLoopIterateArray()
 
         self.writer.add(GLC.ReturnStatement(GLC.SelfProperty("instances")))
@@ -467,10 +463,9 @@ class DEVSGenerator(Visitor):
 
         self.writer.addAssignment("next_earliest", GLC.FunctionCall("min", [GLC.SelfProperty("getEarliestEventTime()"), GLC.SelfProperty("input_queue.getEarliestTime()")]))
         
-
-        self.writer.beginIf(GLC.NotExpression(GLC.EqualsExpression("len(self.to_send)", "0")))
+        self.writer.beginElseIf(GLC.NotExpression(GLC.EqualsExpression("len(self.to_send)", "0")))
         self.writer.addAssignment(GLC.SelfProperty("next_time"), "0")
-        self.writer.endIf()
+        self.writer.endElseIf()
         self.writer.beginElseIf(GLC.EqualsExpression("next_earliest", "INFINITY"))
         self.writer.addAssignment(GLC.SelfProperty("next_time"), "INFINITY")
         self.writer.endElseIf()

+ 27 - 38
sccd/runtime/DEVS_statecharts_core.py

@@ -4,14 +4,10 @@ from sccd.runtime.event_queue import EventQueue
 from pypdevs.infinity import INFINITY
 
 from heapq import heappush, heappop, heapify
+import threading
 
-DEBUG = False
 ELSE_GUARD = "ELSE_GUARD"
 
-def print_debug(msg):
-    if DEBUG:
-        print(msg)
-
 class RuntimeException(Exception):
     """
     Base class for runtime exceptions.
@@ -251,7 +247,6 @@ class Transition:
                     f = lambda s0: not s0.descendants and s0 in s.descendants
                 self.obj.history_values[h.state_id] = list(filter(f, self.obj.configuration))
         for s in exit_set:
-            print_debug('EXIT: %s::%s' % (self.obj.__class__.__name__, s.name))
             self.obj.eventless_states -= s.has_eventless_transitions
             # execute exit action(s)
             if s.exit:
@@ -270,7 +265,6 @@ class Transition:
         targets = self.__getEffectiveTargetStates()
         enter_set = self.__enterSet(targets)
         for s in enter_set:
-            print_debug('ENTER: %s::%s' % (self.obj.__class__.__name__, s.name))
             self.obj.eventless_states += s.has_eventless_transitions
             self.obj.configuration_bitmap |= 2 ** s.state_id
             # execute enter action(s)
@@ -340,10 +334,11 @@ class Transition:
 
 
 class Event(object):
-    def __init__(self, event_name, port="", parameters=[]):
+    def __init__(self, event_name, port="", parameters=[], instance=None):
         self.name = event_name
         self.parameters = parameters
         self.port = port
+        self.instance = instance
 
     # for comparisons in heaps
     def __lt__(self, other):
@@ -700,6 +695,10 @@ class ObjectManagerBase(object):
                          "create_and_start_instance": self.handleCreateAndStartEvent}
         
         self.output_listeners = []
+
+        self.inports = {}
+
+        self.lock = threading.Condition()
     
     #def getEarliestEventTime(self):
         #while self.instance_times and self.instance_times[0][0] < self.instance_times[0][1].earliest_event_time:
@@ -708,10 +707,16 @@ class ObjectManagerBase(object):
 
         #return  min(earliest_event_time, self.input_queue.getEarliestTime())
     
+    #def getEarliestEventTime(self):
+    #    while self.instance_times and self.instance_times[0][0] < self.instance_times[0][1].earliest_event_time:
+    #        heappop(self.instance_times)
+    #    return min(INFINITY if not self.instance_times else self.instance_times[0][0], self.events.getEarliestTime())
+    
     def getEarliestEventTime(self):
-        while self.instance_times and self.instance_times[0][0] < self.instance_times[0][1].earliest_event_time:
-            heappop(self.instance_times)
-        return min(INFINITY if not self.instance_times else self.instance_times[0][0], self.events.getEarliestTime())
+        with self.lock:
+            while self.instance_times and self.instance_times[0][0] < self.instance_times[0][1].earliest_event_time:
+                heappop(self.instance_times)
+            return min(INFINITY if not self.instance_times else self.instance_times[0][0], self.events.getEarliestTime())
         
     def addEvent(self, event, time_offset = 0):
         self.events.add(self.simulated_time + time_offset, event)
@@ -760,12 +765,12 @@ class ObjectManagerBase(object):
             #target_instance = input_port.instance
             #TODO: get the first field, should be dynamically
             #temp = None
-            #try:
-            #    temp = self.processAssociationReference(e.parameters[0])[0]
-            #    temp = temp[1]
-            #except:
-            #    temp = 0
-            target_instance = list(self.instances)[0]
+
+            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]
             if target_instance == None:
                 self.broadcast(e, event_time - self.simulated_time)
@@ -788,26 +793,9 @@ class ObjectManagerBase(object):
                 self.input_queue.add((0 if self.simulated_time is None else self.simulated_time) + time_offset, e)
             else:
                 # TODO; changed this from self.accurate_time.get_wct() to self.simulated_time
+                #self.input_queue.add((0 if self.simulated_time is None else 0) + time_offset, e)
                 self.input_queue.add((0 if self.simulated_time is None else 0) + time_offset, e)
 
-    def addInputPort(self, virtual_name, instance = None):
-        if instance == None:
-            port_name = virtual_name
-        else:
-            port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
-            self.private_port_counter += 1
-        self.input_ports[port_name] = InputPortEntry(virtual_name, instance)
-        return port_name
-        
-    def addOutputPort(self, virtual_name, instance = None):
-        if instance == None:
-            port_name = virtual_name
-        else:
-            port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
-            self.private_port_counter += 1
-        self.output_ports[port_name] = OutputPortEntry(port_name, virtual_name, instance)
-        return port_name
-
 
     def handleEvent(self, e):
         self.handlers[e.getName()](e.getParameters())
@@ -998,7 +986,7 @@ class ObjectManagerBase(object):
             cast_event = parameters[2]
             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)
+                ev = Event(cast_event.name, None, cast_event.parameters, i["instance"])
                 self.to_send.append((i["assoc_name"], i['to_class'], i["assoc_index"], ev))
 
                 #to_send_event = Event(cast_event.name, i["instance"].narrow_cast_port, cast_event.parameters)
@@ -1023,7 +1011,7 @@ class ObjectManagerBase(object):
                         # TODO: instance in nexts was the object but now a reference, can introduce bugs
                         nexts.append({
                             "to_class": association.to_class,
-                            "instance": association.instances[index],
+                            "instance": index,
                             "ref": current["instance"],
                             "assoc_name": name,
                             "assoc_index": index,
@@ -1034,9 +1022,10 @@ class ObjectManagerBase(object):
                         pass
                 elif (index == -1):
                     for i in association.instances:
+                        parent = self.processAssociationReference(association.instances[i])[0]
                         nexts.append({
                             "to_class": association.to_class,
-                            "instance": association.instances[i],
+                            "instance": parent[1],
                             "ref": current["instance"],
                             "assoc_name": name,
                             "assoc_index": index,

+ 13 - 19
sccd/runtime/libs/DEVui_v2.py

@@ -64,8 +64,7 @@ class UI:
             button.pack(fill=tk.BOTH, expand=1)
             button_id = self._gen_id(button)
 
-            #self.controller.addInput(Event("button_created", res_port, [button_id]))
-            self.controller.realtime_interrupt(f"{res_port} Event(\"button_created\",\"{res_port}\",[{button_id}])")
+            self.controller.realtime_interrupt(f"{res_port[0]} Event(\"button_created\",\"{res_port[0]}\",[{button_id}],{res_port[1]})")
         # schedule in mainloop:
         self.tk.after(0, callback)
 
@@ -77,8 +76,7 @@ class UI:
             canvas.pack(fill=tk.BOTH, expand=1)
             canvas_id = self._gen_id(canvas)
             
-            #self.controller.addInput(Event("canvas_created", res_port, [canvas_id]))
-            self.controller.realtime_interrupt(f"{res_port} Event(\"canvas_created\",\"{res_port}\",[{canvas_id}])")
+            self.controller.realtime_interrupt(f"{res_port[0]} Event(\"canvas_created\",\"{res_port[0]}\",[{canvas_id}],{res_port[1]})")
         # schedule in mainloop:
         self.tk.after(0, callback)
 
@@ -88,10 +86,8 @@ class UI:
             window.title(title)
             window.geometry(str(width)+"x"+str(height)+"+300+300")
             window_id = self._gen_id(window)
-            #self.controller.addInput(Event("window_created", res_port, [window_id]))
 
-            #source = f"{res_port} Event(\"window_created\", {res_port},[{window_id}])"
-            self.controller.realtime_interrupt(f"{res_port} Event(\"window_created\",\"{res_port}\",[{window_id}])")
+            self.controller.realtime_interrupt(f"{res_port[0]} Event(\"window_created\",\"{res_port[0]}\",[{window_id}],{res_port[1]})")
         # schedule in mainloop:
         self.tk.after(0, callback)
 
@@ -100,7 +96,7 @@ class UI:
             window = self.mapping[window_id]
             window.destroy()
             if res_port != None:
-                self.controller.addInput(Event("window_destroyed", res_port, [window_id]))
+                self.controller.realtime_interrupt(f"{res_port[0]} Event(\"window_destroyed\",\"{res_port[0]}\",[{window_id}],{res_port[1]})")
         # schedule in mainloop:
         self.tk.after(0, callback)
 
@@ -113,8 +109,7 @@ class UI:
         def callback():
             canvas = self.mapping[canvas_id]
             circle_id = canvas.create_oval(x-r, y-r, x+r, y+r, **style)
-            #self.controller.addInput(Event("circle_created", res_port, [canvas_id, circle_id]))
-            self.controller.realtime_interrupt(f"{res_port} Event(\"circle_created\",\"{res_port}\",[{canvas_id},{circle_id}])")
+            self.controller.realtime_interrupt(f"{res_port[0]} Event(\"circle_created\",\"{res_port[0]}\",[{canvas_id},{circle_id}],{res_port[1]})")
         # schedule in mainloop:
         self.tk.after(0, callback)
 
@@ -122,7 +117,7 @@ class UI:
         def callback():
             canvas = self.mapping[canvas_id]
             rect_id = canvas.create_rectangle(x-w/2.0, y-h/2.0, x+w/2.0, y+h/2.0, **style)
-            self.controller.addInput(Event("rectangle_created", res_port, [canvas_id, rect_id]))
+            self.controller.realtime_interrupt(f"{res_port[0]} Event(\"rectangle_created\",\"{res_port[0]}\",[{canvas_id},{rect_id}],{res_port[1]})")
         # schedule in mainloop
         self.tk.after(0, callback)
 
@@ -153,30 +148,29 @@ class UI:
             canvas = self.mapping[canvas_id]
             canvas.delete(element.element_id)
             if res_port != None:
-                self.controller.addInput(Event("element_destroyed", res_port, [canvas_id, element_id]))
+                self.controller.realtime_interrupt(f"{res_port[0]} Event(\"element_destroyed\",\"{res_port[0]}\",[{canvas_id},{element_id}],{res_port[1]})")
         # schedule in mainloop
         self.tk.after(0, callback)
 
     def _handle_event(self, event, raise_name, port, ev=None):
         if event == EVENTS.KEY_PRESS :
-            self.controller.addInput(Event(raise_name, port, [ev.keycode]))
+            self.controller.realtime_interrupt(f"{port[0]} Event(\"{raise_name}\",\"{port[0]}\",[{ev.keycode}],{port[1]})")
+
         elif event == EVENTS.MOUSE_CLICK or \
              event == EVENTS.MOUSE_MOVE or \
              event == EVENTS.MOUSE_PRESS or \
              event == EVENTS.MOUSE_RELEASE or \
              event == EVENTS.MOUSE_RIGHT_CLICK :
-            #self.controller.addInput(Event(raise_name, port, [ev.x, ev.y, ev.num]))
 
             match ev.num:
                 case int():
-                    hel = f"{port} Event(\"{raise_name}\",\"{port}\",[{ev.x},{ev.y},{ev.num}])"
+                    hel = f"{port[0]} Event(\"{raise_name}\",\"{port[0]}\",[{ev.x},{ev.y},{ev.num}],{port[1]})"
                 case _:
-                    hel = f"{port} Event(\"{raise_name}\",\"{port}\",[{ev.x},{ev.y},\"{ev.num}\"])"
-                    
+                    hel = f"{port[0]} Event(\"{raise_name}\",\"{port[0]}\",[{ev.x},{ev.y},\"{ev.num}\"],{port[1]})"
+
             self.controller.realtime_interrupt(hel)
         elif event == EVENTS.WINDOW_CLOSE :
-            #self.controller.addInput(Event(raise_name, port, []))
-            self.controller.realtime_interrupt(f"{port} Event(\"{raise_name}\",\"{port}\",[])")
+            self.controller.realtime_interrupt(f"{port[0]} Event(\"{raise_name}\",\"{port[0]}\",[],{port[1]})")
             
         else:
             raise Exception('Unsupported event: ' + str(event))