浏览代码

fixed bug + added new code. The generator's first else if gets converted to if. Need to fix this

sampieters 1 年之前
父节点
当前提交
960b076623

+ 206 - 142
examples/BouncingBalls/PyDEVS/new_tester.py

@@ -11,7 +11,7 @@ from sccd.runtime.DEVS_statecharts_core import *
 from pypdevs.DEVS import *
 from pypdevs.infinity import *
 from pypdevs.simulator import *
-from sccd.runtime.libs.DEVui import ui
+from sccd.runtime.libs import ui_v2 as ui
 from sccd.runtime.libs.utils import utils
 import random
 
@@ -154,7 +154,7 @@ class MainAppInstance(RuntimeClassBase):
         self.removeTimer(0)
     
     def _running_root_0_exec(self, parameters):
-        ui.close_window(ui.window)
+        self.big_step.outputEvent(Event("destroy_all", self.getOutPortName("ui"), []))
     
     def _running_root_main_behaviour_initializing_0_exec(self, parameters):
         self.raiseInternalEvent(Event("create_field", None, []))
@@ -200,19 +200,17 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
-        self.obj_manager_in = self.addInPort("obj_manager_in")
+        self.name = "MainApp"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
-        self.input = self.addInPort("input")
         self.outputs = {}
         self.outputs["fields"] = self.addOutPort("fields")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
         self.instances.add(MainAppInstance(self))
-
-        self.name = "MainApp"
+        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])
@@ -224,22 +222,22 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(MainAppInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("MainApp", TODO, input[2], ev))
+                ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
+                self.to_send.append((self.name, input[1], input[2], ev))
             elif input[3].name == "start_instance":
                 instance = list(self.instances)[input[2]]
                 instance.start()
-                ev = Event("instance_started", None, parameters=[])
-                self.to_send.append(("MainApp", input[1], input[2], ev))
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
+                self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append(("MainApp", TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append(("MainApp", TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append(("MainApp", TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -247,28 +245,35 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
             elif input[3].name == "instance_started":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                #instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_deleted":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_associated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_disassociated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "set_association_name":
                 ev = input[3]
                 self.addInput(ev, force_internal=True)
         return self.instances
     
     def intTransition(self):
+        earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if not (earliest == INFINITY):
+            self.simulated_time = earliest
         self.to_send = []
         self.handleInput()
         self.stepAll()
+        next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        
+        if len(self.to_send) != 0:
+            self.next_time = 0
+        elif next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
@@ -291,9 +296,7 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         return to_dict
     
     def timeAdvance(self):
-        if not (len(self.to_send) == 0):
-            return 0
-        return self.getEarliestEventTime()
+        return self.next_time
 
 class FieldInstance(RuntimeClassBase):
     def __init__(self, atomdevs):
@@ -313,23 +316,27 @@ class FieldInstance(RuntimeClassBase):
         self.build_statechart_structure()
         
         # user defined attributes
-        self.canvas = None
-        self.field_window = None
+        self.window_id = None
+        self.canvas_id = None
         
         # call user defined constructor
         FieldInstance.user_defined_constructor(self)
     
     def user_defined_constructor(self):
-        self.field_window = ui.new_window(800,600,"BouncingBalls");
-        self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
-        ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', 'field_ui');
-        ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', 'field_ui');
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', 'field_ui');
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', 'field_ui');
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', 'field_ui');
+        # All of these TkInter calls have been converted to in/out-events:
+        #self.field_window = ui.new_window(800,600,"BouncingBalls");
+        #self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
+        #ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', self.inports['field_ui']);
+        #ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['field_ui']);
+        print("created field")
+        pass
     
     def user_defined_destructor(self):
-        ui.close_window(self.field_window);
+        # ui.close_window(self.field_window);
+        pass
     
     
     # builds Statechart structure
@@ -344,14 +351,17 @@ class FieldInstance(RuntimeClassBase):
         # state /root/waiting
         self.states["/root/waiting"] = State(2, "/root/waiting", self)
         
-        # state /root/initializing
-        self.states["/root/initializing"] = State(3, "/root/initializing", self)
+        # state /root/creating_window
+        self.states["/root/creating_window"] = State(3, "/root/creating_window", self)
+        self.states["/root/creating_window"].setEnter(self._root_creating_window_enter)
         
-        # state /root/creating
-        self.states["/root/creating"] = State(4, "/root/creating", self)
+        # state /root/creating_canvas
+        self.states["/root/creating_canvas"] = State(4, "/root/creating_canvas", self)
+        self.states["/root/creating_canvas"].setEnter(self._root_creating_canvas_enter)
         
-        # state /root/packing
-        self.states["/root/packing"] = State(5, "/root/packing", self)
+        # state /root/creating_button
+        self.states["/root/creating_button"] = State(5, "/root/creating_button", self)
+        self.states["/root/creating_button"].setEnter(self._root_creating_button_enter)
         
         # state /root/running
         self.states["/root/running"] = ParallelState(6, "/root/running", self)
@@ -362,8 +372,8 @@ class FieldInstance(RuntimeClassBase):
         # state /root/running/main_behaviour/running
         self.states["/root/running/main_behaviour/running"] = State(8, "/root/running/main_behaviour/running", self)
         
-        # state /root/running/main_behaviour/creating
-        self.states["/root/running/main_behaviour/creating"] = State(9, "/root/running/main_behaviour/creating", self)
+        # state /root/running/main_behaviour/creating_ball
+        self.states["/root/running/main_behaviour/creating_ball"] = State(9, "/root/running/main_behaviour/creating_ball", self)
         
         # state /root/running/deleting_behaviour
         self.states["/root/running/deleting_behaviour"] = State(10, "/root/running/deleting_behaviour", self)
@@ -392,9 +402,9 @@ class FieldInstance(RuntimeClassBase):
         # add children
         self.states[""].addChild(self.states["/root"])
         self.states["/root"].addChild(self.states["/root/waiting"])
-        self.states["/root"].addChild(self.states["/root/initializing"])
-        self.states["/root"].addChild(self.states["/root/creating"])
-        self.states["/root"].addChild(self.states["/root/packing"])
+        self.states["/root"].addChild(self.states["/root/creating_window"])
+        self.states["/root"].addChild(self.states["/root/creating_canvas"])
+        self.states["/root"].addChild(self.states["/root/creating_button"])
         self.states["/root"].addChild(self.states["/root/running"])
         self.states["/root"].addChild(self.states["/root/deleting"])
         self.states["/root"].addChild(self.states["/root/deleted"])
@@ -403,7 +413,7 @@ class FieldInstance(RuntimeClassBase):
         self.states["/root/running"].addChild(self.states["/root/running/child_behaviour"])
         self.states["/root/running"].addChild(self.states["/root/running/deleting_balls_behaviour"])
         self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/running"])
-        self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/creating"])
+        self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/creating_ball"])
         self.states["/root/running/deleting_behaviour"].addChild(self.states["/root/running/deleting_behaviour/running"])
         self.states["/root/running/child_behaviour"].addChild(self.states["/root/running/child_behaviour/listening"])
         self.states["/root/running/deleting_balls_behaviour"].addChild(self.states["/root/running/deleting_balls_behaviour/listening"])
@@ -416,39 +426,40 @@ class FieldInstance(RuntimeClassBase):
         self.states["/root/running/deleting_balls_behaviour"].default_state = self.states["/root/running/deleting_balls_behaviour/listening"]
         
         # transition /root/waiting
-        _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/initializing"]])
+        _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/creating_window"]])
         _root_waiting_0.setAction(self._root_waiting_0_exec)
         _root_waiting_0.setTrigger(Event("set_association_name", None))
         self.states["/root/waiting"].addTransition(_root_waiting_0)
         
-        # transition /root/initializing
-        _root_initializing_0 = Transition(self, self.states["/root/initializing"], [self.states["/root/creating"]])
-        _root_initializing_0.setAction(self._root_initializing_0_exec)
-        _root_initializing_0.setTrigger(None)
-        self.states["/root/initializing"].addTransition(_root_initializing_0)
+        # transition /root/creating_window
+        _root_creating_window_0 = Transition(self, self.states["/root/creating_window"], [self.states["/root/creating_canvas"]])
+        _root_creating_window_0.setAction(self._root_creating_window_0_exec)
+        _root_creating_window_0.setTrigger(Event("window_created", None))
+        self.states["/root/creating_window"].addTransition(_root_creating_window_0)
         
-        # transition /root/creating
-        _root_creating_0 = Transition(self, self.states["/root/creating"], [self.states["/root/packing"]])
-        _root_creating_0.setAction(self._root_creating_0_exec)
-        _root_creating_0.setTrigger(Event("instance_created", None))
-        self.states["/root/creating"].addTransition(_root_creating_0)
+        # transition /root/creating_canvas
+        _root_creating_canvas_0 = Transition(self, self.states["/root/creating_canvas"], [self.states["/root/creating_button"]])
+        _root_creating_canvas_0.setAction(self._root_creating_canvas_0_exec)
+        _root_creating_canvas_0.setTrigger(Event("canvas_created", None))
+        self.states["/root/creating_canvas"].addTransition(_root_creating_canvas_0)
         
-        # transition /root/packing
-        _root_packing_0 = Transition(self, self.states["/root/packing"], [self.states["/root/running"]])
-        _root_packing_0.setTrigger(Event("button_created", None))
-        self.states["/root/packing"].addTransition(_root_packing_0)
+        # transition /root/creating_button
+        _root_creating_button_0 = Transition(self, self.states["/root/creating_button"], [self.states["/root/running"]])
+        _root_creating_button_0.setAction(self._root_creating_button_0_exec)
+        _root_creating_button_0.setTrigger(Event("instance_created", None))
+        self.states["/root/creating_button"].addTransition(_root_creating_button_0)
         
         # transition /root/running/main_behaviour/running
-        _root_running_main_behaviour_running_0 = Transition(self, self.states["/root/running/main_behaviour/running"], [self.states["/root/running/main_behaviour/creating"]])
+        _root_running_main_behaviour_running_0 = Transition(self, self.states["/root/running/main_behaviour/running"], [self.states["/root/running/main_behaviour/creating_ball"]])
         _root_running_main_behaviour_running_0.setAction(self._root_running_main_behaviour_running_0_exec)
         _root_running_main_behaviour_running_0.setTrigger(Event("right_click", self.getInPortName("field_ui")))
         self.states["/root/running/main_behaviour/running"].addTransition(_root_running_main_behaviour_running_0)
         
-        # transition /root/running/main_behaviour/creating
-        _root_running_main_behaviour_creating_0 = Transition(self, self.states["/root/running/main_behaviour/creating"], [self.states["/root/running/main_behaviour/running"]])
-        _root_running_main_behaviour_creating_0.setAction(self._root_running_main_behaviour_creating_0_exec)
-        _root_running_main_behaviour_creating_0.setTrigger(Event("instance_created", None))
-        self.states["/root/running/main_behaviour/creating"].addTransition(_root_running_main_behaviour_creating_0)
+        # transition /root/running/main_behaviour/creating_ball
+        _root_running_main_behaviour_creating_ball_0 = Transition(self, self.states["/root/running/main_behaviour/creating_ball"], [self.states["/root/running/main_behaviour/running"]])
+        _root_running_main_behaviour_creating_ball_0.setAction(self._root_running_main_behaviour_creating_ball_0_exec)
+        _root_running_main_behaviour_creating_ball_0.setTrigger(Event("instance_created", None))
+        self.states["/root/running/main_behaviour/creating_ball"].addTransition(_root_running_main_behaviour_creating_ball_0)
         
         # transition /root/running/deleting_behaviour/running
         _root_running_deleting_behaviour_running_0 = Transition(self, self.states["/root/running/deleting_behaviour/running"], [self.states["/root/running/deleting_behaviour/running"]])
@@ -481,6 +492,15 @@ class FieldInstance(RuntimeClassBase):
         _root_running_0.setTrigger(Event("window_close", self.getInPortName("field_ui")))
         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", self.inports['field_ui']]))
+    
+    def _root_creating_canvas_enter(self):
+        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, 800, 550, {'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']))
+    
     def _root_running_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("delete_instance", None, [self, "buttons"]))
         self.big_step.outputEventOM(Event("delete_instance", None, [self, "balls"]))
@@ -489,10 +509,20 @@ class FieldInstance(RuntimeClassBase):
         association_name = parameters[0]
         self.association_name = association_name
     
-    def _root_initializing_0_exec(self, parameters):
-        self.big_step.outputEventOM(Event("create_instance", None, [self, "buttons", "Button", self.field_window, 'create_new_field', 'Spawn New Window']))
+    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', 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', 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_0_exec(self, parameters):
+    def _root_creating_button_0_exec(self, parameters):
         association_name = parameters[0]
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
     
@@ -502,7 +532,7 @@ class FieldInstance(RuntimeClassBase):
         button = parameters[2]
         self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas, x, y]))
     
-    def _root_running_main_behaviour_creating_0_exec(self, parameters):
+    def _root_running_main_behaviour_creating_ball_0_exec(self, parameters):
         association_name = parameters[0]
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, association_name, Event("set_association_name", None, [association_name])]))
@@ -525,6 +555,7 @@ class FieldInstance(RuntimeClassBase):
     
     def _root_deleting_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("delete_field", None, [self.association_name])]))
+        self.big_step.outputEvent(Event("destroy_window", self.getOutPortName("ui"), [self.window_id]))
     
     def initializeStatechart(self):
         # enter default state
@@ -536,9 +567,8 @@ class Field(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
-        self.obj_manager_in = self.addInPort("obj_manager_in")
+        self.name = "Field"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
-        self.input = self.addInPort("input")
         self.outputs = {}
         self.outputs["balls"] = self.addOutPort("balls")
         self.outputs["buttons"] = self.addOutPort("buttons")
@@ -546,9 +576,10 @@ 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.next_time = INFINITY
     
     def extTransition(self, inputs):
-        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])
@@ -560,11 +591,10 @@ class Field(AtomicDEVS, ObjectManagerBase):
             if isinstance(input, str):
                 tem = eval(input)
                 self.addInput(tem)
-            elif input[3].name == "create_instance":
+            if 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}]"])
-                # TODO: changed, first elemenet is not "MainApp" anymore but None
-                self.to_send.append((self.name, input[0] , input[2], ev))
+                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()
@@ -572,17 +602,17 @@ class Field(AtomicDEVS, ObjectManagerBase):
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
+                #instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_started":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -601,16 +631,27 @@ class Field(AtomicDEVS, ObjectManagerBase):
         return self.instances
     
     def intTransition(self):
+        earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if not (earliest == INFINITY):
+            self.simulated_time = earliest
         self.to_send = []
         self.handleInput()
         self.stepAll()
+        next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
             if sending[3].port == None:
-                to_dict[self.obj_manager_out] = [sending]
+                if self.obj_manager_out in to_dict:
+                    to_dict[self.obj_manager_out].append(sending)
+                else:
+                    to_dict[self.obj_manager_out] = [sending]
             else:
                 the_port = None
                 for port in self.OPorts:
@@ -623,13 +664,10 @@ class Field(AtomicDEVS, ObjectManagerBase):
         return to_dict
     
     def timeAdvance(self):
-        if not (len(self.to_send) == 0):
-            return 0
-        return self.getEarliestEventTime()
+        return self.next_time
 
 class ButtonInstance(RuntimeClassBase):
-    # TODO: Add parameters to constructor __init__
-    def __init__(self, atomdevs, tkparent, event_name, button_text):
+    def __init__(self, atomdevs, window_id, event_name, button_text):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
         self.associations["parent"] = Association("Field", 1, 1)
@@ -643,13 +681,21 @@ class ButtonInstance(RuntimeClassBase):
         # build Statechart structure
         self.build_statechart_structure()
         
+        # user defined attributes
+        self.window_id = None
+        self.event_name = None
+        self.button_id = None
+        
         # call user defined constructor
-        ButtonInstance.user_defined_constructor(self, tkparent, event_name, button_text)
+        ButtonInstance.user_defined_constructor(self, window_id, event_name, button_text)
     
-    def user_defined_constructor(self, tkparent, event_name, button_text):
+    def user_defined_constructor(self, window_id, event_name, button_text):
+        self.window_id = window_id;
         self.event_name = event_name;
-        button = ui.append_button(tkparent, event_name);
-        ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
+        
+        # Translated to events:
+        #button = ui.append_button(tkparent, event_name);
+        #ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
     
     def user_defined_destructor(self):
         pass
@@ -661,23 +707,24 @@ class ButtonInstance(RuntimeClassBase):
         # state <root>
         self.states[""] = State(0, "", self)
         
-        # state /initializing
-        self.states["/initializing"] = State(1, "/initializing", self)
+        # state /creating_button
+        self.states["/creating_button"] = State(1, "/creating_button", self)
+        self.states["/creating_button"].setEnter(self._creating_button_enter)
         
         # state /running
         self.states["/running"] = State(2, "/running", self)
         
         # add children
-        self.states[""].addChild(self.states["/initializing"])
+        self.states[""].addChild(self.states["/creating_button"])
         self.states[""].addChild(self.states["/running"])
         self.states[""].fixTree()
-        self.states[""].default_state = self.states["/initializing"]
+        self.states[""].default_state = self.states["/creating_button"]
         
-        # transition /initializing
-        _initializing_0 = Transition(self, self.states["/initializing"], [self.states["/running"]])
-        _initializing_0.setAction(self._initializing_0_exec)
-        _initializing_0.setTrigger(None)
-        self.states["/initializing"].addTransition(_initializing_0)
+        # transition /creating_button
+        _creating_button_0 = Transition(self, self.states["/creating_button"], [self.states["/running"]])
+        _creating_button_0.setAction(self._creating_button_0_exec)
+        _creating_button_0.setTrigger(Event("button_created", None))
+        self.states["/creating_button"].addTransition(_creating_button_0)
         
         # transition /running
         _running_0 = Transition(self, self.states["/running"], [self.states["/running"]])
@@ -686,8 +733,13 @@ class ButtonInstance(RuntimeClassBase):
         _running_0.setGuard(self._running_0_guard)
         self.states["/running"].addTransition(_running_0)
     
-    def _initializing_0_exec(self, parameters):
-        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("button_created", None, [])]))
+    def _creating_button_enter(self):
+        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", self.inports['button_ui']]))
     
     def _running_0_exec(self, parameters):
         x = parameters[0]
@@ -703,7 +755,7 @@ class ButtonInstance(RuntimeClassBase):
     
     def initializeStatechart(self):
         # enter default state
-        self.default_targets = self.states["/initializing"].getEffectiveTargetStates()
+        self.default_targets = self.states["/creating_button"].getEffectiveTargetStates()
         RuntimeClassBase.initializeStatechart(self)
 
 class Button(AtomicDEVS, ObjectManagerBase):
@@ -711,17 +763,17 @@ class Button(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
-        self.obj_manager_in = self.addInPort("obj_manager_in")
+        self.name = "Button"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
-        self.input = self.addInPort("input")
         self.outputs = {}
         self.outputs["parent"] = self.addOutPort("parent")
         self.button_ui = self.addInPort("button_ui")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
-        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])
@@ -735,22 +787,22 @@ class Button(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(ButtonInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("Button", TODO, input[2], ev))
+                ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
+                self.to_send.append((self.name, input[1], input[2], ev))
             elif input[3].name == "start_instance":
                 instance = list(self.instances)[input[2]]
                 instance.start()
-                ev = Event("instance_started", None, parameters=[TODO])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -758,35 +810,42 @@ class Button(AtomicDEVS, ObjectManagerBase):
             elif input[3].name == "instance_started":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_deleted":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_associated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_disassociated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "set_association_name":
                 ev = input[3]
                 self.addInput(ev, force_internal=True)
         return self.instances
     
     def intTransition(self):
+        earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if not (earliest == INFINITY):
+            self.simulated_time = earliest
         self.to_send = []
         self.handleInput()
         self.stepAll()
+        next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
-                to_dict[self.obj_manager_out] = [sending]
+            if sending[3].port == None:
+                if self.obj_manager_out in to_dict:
+                    to_dict[self.obj_manager_out].append(sending)
+                else:
+                    to_dict[self.obj_manager_out] = [sending]
             else:
                 the_port = None
                 for port in self.OPorts:
@@ -799,12 +858,10 @@ class Button(AtomicDEVS, ObjectManagerBase):
         return to_dict
     
     def timeAdvance(self):
-        if not (len(self.to_send) == 0):
-            return 0
-        return self.getEarliestEventTime()
+        return self.next_time
 
 class BallInstance(RuntimeClassBase):
-    def __init__(self, atomdevs):
+    def __init__(self, atomdevs, canvas, x, y):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
         self.associations["parent"] = Association("Field", 1, 1)
@@ -831,11 +888,12 @@ class BallInstance(RuntimeClassBase):
         self.mouse_pos = {};
         self.smooth = 0.4; # value between 0 and 1
         
-        circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
-        ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
-        ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
-        ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
-        self.element = circle;
+        # TODO:
+        #circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
+        #self.element = circle;
     
     def user_defined_destructor(self):
         self.canvas.remove_element(self.element);
@@ -1002,17 +1060,17 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
-        self.obj_manager_in = self.addInPort("obj_manager_in")
+        self.name = "Ball"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
-        self.input = self.addInPort("input")
         self.outputs = {}
         self.outputs["parent"] = self.addOutPort("parent")
         self.ball_ui = self.addInPort("ball_ui")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
-        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])
@@ -1026,22 +1084,22 @@ class Ball(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(BallInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("Ball", TODO, input[2], ev))
+                ev = Event("instance_created", None, parameters=[f"{input[3].parameters[0]}[{len(self.instances)-1}]"])
+                self.to_send.append((self.name, input[1], input[2], ev))
             elif input[3].name == "start_instance":
                 instance = list(self.instances)[input[2]]
                 instance.start()
-                ev = Event("instance_started", None, parameters=[TODO])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -1049,35 +1107,42 @@ class Ball(AtomicDEVS, ObjectManagerBase):
             elif input[3].name == "instance_started":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_deleted":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_associated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "instance_disassociated":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
-                instance.associations['fields'].instances[0] = input[3].parameters[0]
             elif input[3].name == "set_association_name":
                 ev = input[3]
                 self.addInput(ev, force_internal=True)
         return self.instances
     
     def intTransition(self):
+        earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if not (earliest == INFINITY):
+            self.simulated_time = earliest
         self.to_send = []
         self.handleInput()
         self.stepAll()
+        next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
-                to_dict[self.obj_manager_out] = [sending]
+            if sending[3].port == None:
+                if self.obj_manager_out in to_dict:
+                    to_dict[self.obj_manager_out].append(sending)
+                else:
+                    to_dict[self.obj_manager_out] = [sending]
             else:
                 the_port = None
                 for port in self.OPorts:
@@ -1090,9 +1155,7 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         return to_dict
     
     def timeAdvance(self):
-        if not (len(self.to_send) == 0):
-            return 0
-        return self.getEarliestEventTime()
+        return self.next_time
 
 class ObjectManagerState:
     def __init__(self):
@@ -1137,6 +1200,7 @@ class Controller(CoupledDEVS):
     def __init__(self, name):
         CoupledDEVS.__init__(self, name)
         self.ui = self.addInPort("ui")
+        self.addOutPort("ui")
         self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
         self.atomic0 = self.addSubModel(MainApp("MainApp"))
         self.atomic1 = self.addSubModel(Field("Field"))

+ 179 - 108
examples/BouncingBalls/PyDEVS/target.py

@@ -11,7 +11,7 @@ from sccd.runtime.DEVS_statecharts_core import *
 from pypdevs.DEVS import *
 from pypdevs.infinity import *
 from pypdevs.simulator import *
-from sccd.runtime.libs.ui import ui
+from sccd.runtime.libs import ui_v2 as ui
 from sccd.runtime.libs.utils import utils
 import random
 
@@ -154,7 +154,7 @@ class MainAppInstance(RuntimeClassBase):
         self.removeTimer(0)
     
     def _running_root_0_exec(self, parameters):
-        ui.close_window(ui.window)
+        self.big_step.outputEvent(Event("destroy_all", self.getOutPortName("ui"), []))
     
     def _running_root_main_behaviour_initializing_0_exec(self, parameters):
         self.raiseInternalEvent(Event("create_field", None, []))
@@ -200,13 +200,14 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
+        self.name = "MainApp"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
         self.outputs = {}
         self.outputs["fields"] = self.addOutPort("fields")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
         self.instances.add(MainAppInstance(self))
-        self.next_time = 0
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
         self.next_time = 0
@@ -221,22 +222,22 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(MainAppInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("MainApp", TODO, input[2], ev))
+                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()
-                ev = Event("instance_started", None, parameters=[])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -266,13 +267,18 @@ class MainApp(AtomicDEVS, ObjectManagerBase):
         self.handleInput()
         self.stepAll()
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
-        self.next_time = next_earliest - earliest
+        if not (len(self.to_send) == 0):
+            self.next_time = 0
+        elif next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
+            if sending[3].port == None:
                 if self.obj_manager_out in to_dict:
                     to_dict[self.obj_manager_out].append(sending)
                 else:
@@ -309,23 +315,27 @@ class FieldInstance(RuntimeClassBase):
         self.build_statechart_structure()
         
         # user defined attributes
-        self.canvas = None
-        self.field_window = None
+        self.window_id = None
+        self.canvas_id = None
         
         # call user defined constructor
         FieldInstance.user_defined_constructor(self)
     
     def user_defined_constructor(self):
-        self.field_window = ui.new_window(800,600,"BouncingBalls");
-        self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
-        ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', self.inports['field_ui']);
-        ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', self.inports['field_ui']);
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', self.inports['field_ui']);
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['field_ui']);
-        ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['field_ui']);
+        # All of these TkInter calls have been converted to in/out-events:
+        #self.field_window = ui.new_window(800,600,"BouncingBalls");
+        #self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
+        #ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', self.inports['field_ui']);
+        #ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['field_ui']);
+        #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['field_ui']);
+        print("created field")
+        pass
     
     def user_defined_destructor(self):
-        ui.close_window(self.field_window);
+        # ui.close_window(self.field_window);
+        pass
     
     
     # builds Statechart structure
@@ -340,14 +350,17 @@ class FieldInstance(RuntimeClassBase):
         # state /root/waiting
         self.states["/root/waiting"] = State(2, "/root/waiting", self)
         
-        # state /root/initializing
-        self.states["/root/initializing"] = State(3, "/root/initializing", self)
+        # state /root/creating_window
+        self.states["/root/creating_window"] = State(3, "/root/creating_window", self)
+        self.states["/root/creating_window"].setEnter(self._root_creating_window_enter)
         
-        # state /root/creating
-        self.states["/root/creating"] = State(4, "/root/creating", self)
+        # state /root/creating_canvas
+        self.states["/root/creating_canvas"] = State(4, "/root/creating_canvas", self)
+        self.states["/root/creating_canvas"].setEnter(self._root_creating_canvas_enter)
         
-        # state /root/packing
-        self.states["/root/packing"] = State(5, "/root/packing", self)
+        # state /root/creating_button
+        self.states["/root/creating_button"] = State(5, "/root/creating_button", self)
+        self.states["/root/creating_button"].setEnter(self._root_creating_button_enter)
         
         # state /root/running
         self.states["/root/running"] = ParallelState(6, "/root/running", self)
@@ -358,8 +371,8 @@ class FieldInstance(RuntimeClassBase):
         # state /root/running/main_behaviour/running
         self.states["/root/running/main_behaviour/running"] = State(8, "/root/running/main_behaviour/running", self)
         
-        # state /root/running/main_behaviour/creating
-        self.states["/root/running/main_behaviour/creating"] = State(9, "/root/running/main_behaviour/creating", self)
+        # state /root/running/main_behaviour/creating_ball
+        self.states["/root/running/main_behaviour/creating_ball"] = State(9, "/root/running/main_behaviour/creating_ball", self)
         
         # state /root/running/deleting_behaviour
         self.states["/root/running/deleting_behaviour"] = State(10, "/root/running/deleting_behaviour", self)
@@ -388,9 +401,9 @@ class FieldInstance(RuntimeClassBase):
         # add children
         self.states[""].addChild(self.states["/root"])
         self.states["/root"].addChild(self.states["/root/waiting"])
-        self.states["/root"].addChild(self.states["/root/initializing"])
-        self.states["/root"].addChild(self.states["/root/creating"])
-        self.states["/root"].addChild(self.states["/root/packing"])
+        self.states["/root"].addChild(self.states["/root/creating_window"])
+        self.states["/root"].addChild(self.states["/root/creating_canvas"])
+        self.states["/root"].addChild(self.states["/root/creating_button"])
         self.states["/root"].addChild(self.states["/root/running"])
         self.states["/root"].addChild(self.states["/root/deleting"])
         self.states["/root"].addChild(self.states["/root/deleted"])
@@ -399,7 +412,7 @@ class FieldInstance(RuntimeClassBase):
         self.states["/root/running"].addChild(self.states["/root/running/child_behaviour"])
         self.states["/root/running"].addChild(self.states["/root/running/deleting_balls_behaviour"])
         self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/running"])
-        self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/creating"])
+        self.states["/root/running/main_behaviour"].addChild(self.states["/root/running/main_behaviour/creating_ball"])
         self.states["/root/running/deleting_behaviour"].addChild(self.states["/root/running/deleting_behaviour/running"])
         self.states["/root/running/child_behaviour"].addChild(self.states["/root/running/child_behaviour/listening"])
         self.states["/root/running/deleting_balls_behaviour"].addChild(self.states["/root/running/deleting_balls_behaviour/listening"])
@@ -412,39 +425,40 @@ class FieldInstance(RuntimeClassBase):
         self.states["/root/running/deleting_balls_behaviour"].default_state = self.states["/root/running/deleting_balls_behaviour/listening"]
         
         # transition /root/waiting
-        _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/initializing"]])
+        _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/creating_window"]])
         _root_waiting_0.setAction(self._root_waiting_0_exec)
         _root_waiting_0.setTrigger(Event("set_association_name", None))
         self.states["/root/waiting"].addTransition(_root_waiting_0)
         
-        # transition /root/initializing
-        _root_initializing_0 = Transition(self, self.states["/root/initializing"], [self.states["/root/creating"]])
-        _root_initializing_0.setAction(self._root_initializing_0_exec)
-        _root_initializing_0.setTrigger(None)
-        self.states["/root/initializing"].addTransition(_root_initializing_0)
+        # transition /root/creating_window
+        _root_creating_window_0 = Transition(self, self.states["/root/creating_window"], [self.states["/root/creating_canvas"]])
+        _root_creating_window_0.setAction(self._root_creating_window_0_exec)
+        _root_creating_window_0.setTrigger(Event("window_created", None))
+        self.states["/root/creating_window"].addTransition(_root_creating_window_0)
         
-        # transition /root/creating
-        _root_creating_0 = Transition(self, self.states["/root/creating"], [self.states["/root/packing"]])
-        _root_creating_0.setAction(self._root_creating_0_exec)
-        _root_creating_0.setTrigger(Event("instance_created", None))
-        self.states["/root/creating"].addTransition(_root_creating_0)
+        # transition /root/creating_canvas
+        _root_creating_canvas_0 = Transition(self, self.states["/root/creating_canvas"], [self.states["/root/creating_button"]])
+        _root_creating_canvas_0.setAction(self._root_creating_canvas_0_exec)
+        _root_creating_canvas_0.setTrigger(Event("canvas_created", None))
+        self.states["/root/creating_canvas"].addTransition(_root_creating_canvas_0)
         
-        # transition /root/packing
-        _root_packing_0 = Transition(self, self.states["/root/packing"], [self.states["/root/running"]])
-        _root_packing_0.setTrigger(Event("button_created", None))
-        self.states["/root/packing"].addTransition(_root_packing_0)
+        # transition /root/creating_button
+        _root_creating_button_0 = Transition(self, self.states["/root/creating_button"], [self.states["/root/running"]])
+        _root_creating_button_0.setAction(self._root_creating_button_0_exec)
+        _root_creating_button_0.setTrigger(Event("instance_created", None))
+        self.states["/root/creating_button"].addTransition(_root_creating_button_0)
         
         # transition /root/running/main_behaviour/running
-        _root_running_main_behaviour_running_0 = Transition(self, self.states["/root/running/main_behaviour/running"], [self.states["/root/running/main_behaviour/creating"]])
+        _root_running_main_behaviour_running_0 = Transition(self, self.states["/root/running/main_behaviour/running"], [self.states["/root/running/main_behaviour/creating_ball"]])
         _root_running_main_behaviour_running_0.setAction(self._root_running_main_behaviour_running_0_exec)
         _root_running_main_behaviour_running_0.setTrigger(Event("right_click", self.getInPortName("field_ui")))
         self.states["/root/running/main_behaviour/running"].addTransition(_root_running_main_behaviour_running_0)
         
-        # transition /root/running/main_behaviour/creating
-        _root_running_main_behaviour_creating_0 = Transition(self, self.states["/root/running/main_behaviour/creating"], [self.states["/root/running/main_behaviour/running"]])
-        _root_running_main_behaviour_creating_0.setAction(self._root_running_main_behaviour_creating_0_exec)
-        _root_running_main_behaviour_creating_0.setTrigger(Event("instance_created", None))
-        self.states["/root/running/main_behaviour/creating"].addTransition(_root_running_main_behaviour_creating_0)
+        # transition /root/running/main_behaviour/creating_ball
+        _root_running_main_behaviour_creating_ball_0 = Transition(self, self.states["/root/running/main_behaviour/creating_ball"], [self.states["/root/running/main_behaviour/running"]])
+        _root_running_main_behaviour_creating_ball_0.setAction(self._root_running_main_behaviour_creating_ball_0_exec)
+        _root_running_main_behaviour_creating_ball_0.setTrigger(Event("instance_created", None))
+        self.states["/root/running/main_behaviour/creating_ball"].addTransition(_root_running_main_behaviour_creating_ball_0)
         
         # transition /root/running/deleting_behaviour/running
         _root_running_deleting_behaviour_running_0 = Transition(self, self.states["/root/running/deleting_behaviour/running"], [self.states["/root/running/deleting_behaviour/running"]])
@@ -477,6 +491,15 @@ class FieldInstance(RuntimeClassBase):
         _root_running_0.setTrigger(Event("window_close", self.getInPortName("field_ui")))
         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", self.inports['field_ui']]))
+    
+    def _root_creating_canvas_enter(self):
+        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, 800, 550, {'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']))
+    
     def _root_running_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("delete_instance", None, [self, "buttons"]))
         self.big_step.outputEventOM(Event("delete_instance", None, [self, "balls"]))
@@ -485,10 +508,20 @@ class FieldInstance(RuntimeClassBase):
         association_name = parameters[0]
         self.association_name = association_name
     
-    def _root_initializing_0_exec(self, parameters):
-        self.big_step.outputEventOM(Event("create_instance", None, [self, "buttons", "Button", self.field_window, 'create_new_field', 'Spawn New Window']))
+    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', 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', 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_0_exec(self, parameters):
+    def _root_creating_button_0_exec(self, parameters):
         association_name = parameters[0]
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
     
@@ -498,7 +531,7 @@ class FieldInstance(RuntimeClassBase):
         button = parameters[2]
         self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas, x, y]))
     
-    def _root_running_main_behaviour_creating_0_exec(self, parameters):
+    def _root_running_main_behaviour_creating_ball_0_exec(self, parameters):
         association_name = parameters[0]
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, association_name, Event("set_association_name", None, [association_name])]))
@@ -521,6 +554,7 @@ class FieldInstance(RuntimeClassBase):
     
     def _root_deleting_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("delete_field", None, [self.association_name])]))
+        self.big_step.outputEvent(Event("destroy_window", self.getOutPortName("ui"), [self.window_id]))
     
     def initializeStatechart(self):
         # enter default state
@@ -532,6 +566,7 @@ class Field(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
+        self.name = "Field"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
         self.outputs = {}
         self.outputs["balls"] = self.addOutPort("balls")
@@ -540,7 +575,7 @@ 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.next_time = 0
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
         self.next_time = 0
@@ -557,22 +592,22 @@ class Field(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(FieldInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("Field", TODO, input[2], ev))
+                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()
-                ev = Event("instance_started", None, parameters=[TODO])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -602,13 +637,18 @@ class Field(AtomicDEVS, ObjectManagerBase):
         self.handleInput()
         self.stepAll()
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
-        self.next_time = next_earliest - earliest
+        if not (len(self.to_send) == 0):
+            self.next_time = 0
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
+            if sending[3].port == None:
                 if self.obj_manager_out in to_dict:
                     to_dict[self.obj_manager_out].append(sending)
                 else:
@@ -628,7 +668,7 @@ class Field(AtomicDEVS, ObjectManagerBase):
         return self.next_time
 
 class ButtonInstance(RuntimeClassBase):
-    def __init__(self, atomdevs, tkparent, event_name, button_text):
+    def __init__(self, atomdevs, window_id, event_name, button_text):
         RuntimeClassBase.__init__(self, atomdevs)
         self.associations = {}
         self.associations["parent"] = Association("Field", 1, 1)
@@ -642,13 +682,21 @@ class ButtonInstance(RuntimeClassBase):
         # build Statechart structure
         self.build_statechart_structure()
         
+        # user defined attributes
+        self.window_id = None
+        self.event_name = None
+        self.button_id = None
+        
         # call user defined constructor
-        ButtonInstance.user_defined_constructor(self, tkparent, event_name, button_text)
+        ButtonInstance.user_defined_constructor(self, window_id, event_name, button_text)
     
-    def user_defined_constructor(self, tkparent, event_name, button_text):
+    def user_defined_constructor(self, window_id, event_name, button_text):
+        self.window_id = window_id;
         self.event_name = event_name;
-        button = ui.append_button(tkparent, event_name);
-        ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
+        
+        # Translated to events:
+        #button = ui.append_button(tkparent, event_name);
+        #ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
     
     def user_defined_destructor(self):
         pass
@@ -660,23 +708,24 @@ class ButtonInstance(RuntimeClassBase):
         # state <root>
         self.states[""] = State(0, "", self)
         
-        # state /initializing
-        self.states["/initializing"] = State(1, "/initializing", self)
+        # state /creating_button
+        self.states["/creating_button"] = State(1, "/creating_button", self)
+        self.states["/creating_button"].setEnter(self._creating_button_enter)
         
         # state /running
         self.states["/running"] = State(2, "/running", self)
         
         # add children
-        self.states[""].addChild(self.states["/initializing"])
+        self.states[""].addChild(self.states["/creating_button"])
         self.states[""].addChild(self.states["/running"])
         self.states[""].fixTree()
-        self.states[""].default_state = self.states["/initializing"]
+        self.states[""].default_state = self.states["/creating_button"]
         
-        # transition /initializing
-        _initializing_0 = Transition(self, self.states["/initializing"], [self.states["/running"]])
-        _initializing_0.setAction(self._initializing_0_exec)
-        _initializing_0.setTrigger(None)
-        self.states["/initializing"].addTransition(_initializing_0)
+        # transition /creating_button
+        _creating_button_0 = Transition(self, self.states["/creating_button"], [self.states["/running"]])
+        _creating_button_0.setAction(self._creating_button_0_exec)
+        _creating_button_0.setTrigger(Event("button_created", None))
+        self.states["/creating_button"].addTransition(_creating_button_0)
         
         # transition /running
         _running_0 = Transition(self, self.states["/running"], [self.states["/running"]])
@@ -685,8 +734,13 @@ class ButtonInstance(RuntimeClassBase):
         _running_0.setGuard(self._running_0_guard)
         self.states["/running"].addTransition(_running_0)
     
-    def _initializing_0_exec(self, parameters):
-        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("button_created", None, [])]))
+    def _creating_button_enter(self):
+        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", self.inports['button_ui']]))
     
     def _running_0_exec(self, parameters):
         x = parameters[0]
@@ -702,7 +756,7 @@ class ButtonInstance(RuntimeClassBase):
     
     def initializeStatechart(self):
         # enter default state
-        self.default_targets = self.states["/initializing"].getEffectiveTargetStates()
+        self.default_targets = self.states["/creating_button"].getEffectiveTargetStates()
         RuntimeClassBase.initializeStatechart(self)
 
 class Button(AtomicDEVS, ObjectManagerBase):
@@ -710,13 +764,14 @@ class Button(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
+        self.name = "Button"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
         self.outputs = {}
         self.outputs["parent"] = self.addOutPort("parent")
         self.button_ui = self.addInPort("button_ui")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
-        self.next_time = 0
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
         self.next_time = 0
@@ -733,22 +788,22 @@ class Button(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(ButtonInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("Button", TODO, input[2], ev))
+                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()
-                ev = Event("instance_started", None, parameters=[TODO])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -778,13 +833,18 @@ class Button(AtomicDEVS, ObjectManagerBase):
         self.handleInput()
         self.stepAll()
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
-        self.next_time = next_earliest - earliest
+        if not (len(self.to_send) == 0):
+            self.next_time = 0
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
+            if sending[3].port == None:
                 if self.obj_manager_out in to_dict:
                     to_dict[self.obj_manager_out].append(sending)
                 else:
@@ -831,11 +891,12 @@ class BallInstance(RuntimeClassBase):
         self.mouse_pos = {};
         self.smooth = 0.4; # value between 0 and 1
         
-        circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
-        ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
-        ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
-        ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
-        self.element = circle;
+        # TODO:
+        #circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
+        #ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
+        #self.element = circle;
     
     def user_defined_destructor(self):
         self.canvas.remove_element(self.element);
@@ -1002,13 +1063,14 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         AtomicDEVS.__init__(self, name)
         ObjectManagerBase.__init__(self)
         self.elapsed = 0
+        self.name = "Ball"
         self.obj_manager_out = self.addOutPort("obj_manager_out")
         self.outputs = {}
         self.outputs["parent"] = self.addOutPort("parent")
         self.ball_ui = self.addInPort("ball_ui")
         self.obj_manager_in = self.addInPort("obj_manager_in")
         self.input = self.addInPort("input")
-        self.next_time = 0
+        self.next_time = INFINITY
     
     def extTransition(self, inputs):
         self.next_time = 0
@@ -1025,22 +1087,22 @@ class Ball(AtomicDEVS, ObjectManagerBase):
                 self.addInput(tem)
             if input[3].name == "create_instance":
                 self.instances.add(BallInstance(self))
-                ev = Event("instance_created", None, parameters=[f"{input[0]}[{len(self.instances)-1}]"])
-                self.to_send.append(("Ball", TODO, input[2], ev))
+                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()
-                ev = Event("instance_started", None, parameters=[TODO])
+                ev = Event("instance_started", None, parameters=[f"{input[0]}[{input[2]}]"])
                 self.to_send.append((input[0], input[1], input[2], ev))
             elif input[3].name == "delete_instance":
                 ev = Event("instance_deleted", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "associate_instance":
                 ev = Event("instance_associated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "disassociate_instance":
                 ev = Event("instance_disassociated", None, parameters=[TODO])
-                self.to_send.append((TODO, TODO, TODO, ev))
+                self.to_send.append((self.name, TODO, TODO, ev))
             elif input[3].name == "instance_created":
                 instance = list(self.instances)[input[2]]
                 instance.addEvent(input[3])
@@ -1070,13 +1132,18 @@ class Ball(AtomicDEVS, ObjectManagerBase):
         self.handleInput()
         self.stepAll()
         next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
-        self.next_time = next_earliest - earliest
+        if not (len(self.to_send) == 0):
+            self.next_time = 0
+        if next_earliest == INFINITY:
+            self.next_time = INFINITY
+        else:
+            self.next_time = next_earliest - earliest
         return self.instances
     
     def outputFnc(self):
         to_dict = {}
         for sending in self.to_send:
-            if sending[0] == None:
+            if sending[3].port == None:
                 if self.obj_manager_out in to_dict:
                     to_dict[self.obj_manager_out].append(sending)
                 else:
@@ -1123,7 +1190,10 @@ class ObjectManager(AtomicDEVS):
     def outputFnc(self):
         out_dict = {}
         for (source, target, id, message) in self.State.to_send:
-            out_dict[self.output[target]] = [(source, target, id, message)]
+            if self.output[target] in out_dict:
+                out_dict[self.output[target]].append((source, target, id, message))
+            else:
+                out_dict[self.output[target]] = [(source, target, id, message)]
         return out_dict
     
     def timeAdvance(self):
@@ -1135,6 +1205,7 @@ class Controller(CoupledDEVS):
     def __init__(self, name):
         CoupledDEVS.__init__(self, name)
         self.ui = self.addInPort("ui")
+        self.addOutPort("ui")
         self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
         self.atomic0 = self.addSubModel(MainApp("MainApp"))
         self.atomic1 = self.addSubModel(Field("Field"))

+ 140 - 39
examples/BouncingBalls/sccd.xml

@@ -4,11 +4,12 @@
         Tkinter frame with bouncing balls in it.
     </description>
     <top>
-        from sccd.runtime.libs.ui import ui
+        from sccd.runtime.libs import ui_v2 as ui
         from sccd.runtime.libs.utils import utils
         import random
     </top>
     <inport name="ui"/>
+    <outport name="ui"/>
     <class name="MainApp" default="true">
         <relationships>
             <association name="fields" class="Field" />
@@ -80,11 +81,7 @@
                         <state id="stopped" />
                     </state>
                     <transition target="../stopped" event="stop">
-                        <script>
-                            <![CDATA[
-                            ui.close_window(ui.window)
-                            ]]>
-                        </script>
+                        <raise port="ui" event="destroy_all"/>
                     </transition>
                 </parallel>
                 <state id="stopped" />
@@ -93,8 +90,8 @@
     </class>
 
     <class name="Field">
-        <attribute name="canvas" />
-        <attribute name="field_window" />
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
         <inport name="field_ui"/>
         <relationships>
             <association name="balls" class="Ball" />
@@ -104,27 +101,31 @@
         <constructor>
             <body>
                 <![CDATA[
-                self.field_window = ui.new_window(800,600,"BouncingBalls");
-                self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
-                ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', self.inports['field_ui']);
-                ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', self.inports['field_ui']);
-                ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', self.inports['field_ui']);
-                ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['field_ui']);
-                ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['field_ui']);
+                # All of these TkInter calls have been converted to in/out-events:
+                #self.field_window = ui.new_window(800,600,"BouncingBalls");
+                #self.canvas = ui.append_canvas(self.field_window,800,550,{'background':'#eee'});
+                #ui.bind_event(self.field_window, ui.EVENTS.WINDOW_CLOSE, self.controller, 'window_close', self.inports['field_ui']);
+                #ui.bind_event(self.field_window, ui.EVENTS.KEY_PRESS, self.controller, 'key_press', self.inports['field_ui']);
+                #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK,    self.controller, 'right_click', self.inports['field_ui']);
+                #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['field_ui']);
+                #ui.bind_event(self.canvas.element, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['field_ui']);
+                print("created field")
+                pass
                 ]]>
             </body>
         </constructor>
         <destructor>
             <body>
                 <![CDATA[
-                ui.close_window(self.field_window);
+                # ui.close_window(self.field_window);
+                pass
                 ]]>
             </body>
         </destructor>
         <scxml initial="root">
             <state id="root" initial="waiting">
                 <state id="waiting">
-                    <transition event="set_association_name" target="../initializing">
+                    <transition event="set_association_name" target="../creating_window">
                         <parameter name="association_name" type="str" />
                         <script>
                             <![CDATA[
@@ -133,30 +134,96 @@
                         </script>
                     </transition>
                 </state>
-                <state id="initializing">
-                    <transition target="../creating">
+                <state id="creating_window">
+                    <onentry>
+                        <raise port="ui" event="create_window">
+                            <parameter expr="800"/><!-- width -->
+                            <parameter expr="600"/><!-- height -->
+                            <parameter expr='"BouncingBalls"'/><!-- title -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="window_created" target="../creating_canvas">
+                        <parameter name="window_id" type="int" />
+                        <script>
+                            <![CDATA[
+                            self.window_id = window_id
+                            ]]>
+                        </script>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="window_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.WINDOW_CLOSE"/><!-- tk_event -->
+                            <parameter expr="'window_close'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="window_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.KEY_PRESS"/><!-- tk_event -->
+                            <parameter expr="'key_press'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                    </transition>
+                </state>
+                <state id="creating_canvas">
+                    <onentry>
+                        <raise port="ui" event="create_canvas">
+                            <parameter expr="self.window_id"/><!-- window_id -->
+                            <parameter expr="800"/><!-- width -->
+                            <parameter expr="550"/><!-- height -->
+                            <parameter expr="{'background':'#eee'}"/><!-- style -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="canvas_created" target="../creating_button">
+                        <parameter name="canvas_id" type="int"/>
+                        <script>
+                            <![CDATA[
+                            self.canvas_id = canvas_id
+                            ]]>
+                        </script>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="canvas_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.MOUSE_RIGHT_CLICK"/><!-- tk_event -->
+                            <parameter expr="'right_click'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="canvas_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.MOUSE_MOVE"/><!-- tk_event -->
+                            <parameter expr="'mouse_move'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="canvas_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.MOUSE_RELEASE"/><!-- tk_event -->
+                            <parameter expr="'mouse_release'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                        </raise>
+                    </transition>
+                </state>
+                <state id="creating_button">
+                    <onentry>
                         <raise scope="cd" event="create_instance">
                             <parameter expr='"buttons"' />
                             <parameter expr='"Button"' />
-                            <parameter expr="self.field_window" />
+                            <parameter expr="self.window_id" />
                             <parameter expr="'create_new_field'" />
                             <parameter expr="'Spawn New Window'" />
                         </raise>
-                    </transition>
-                </state>
-                <state id="creating">
-                    <transition event='instance_created' target='../packing'>
+                    </onentry>
+                    <transition event='instance_created' target='../running'>
                         <parameter name="association_name" type="string"/>
                         <raise scope="cd" event="start_instance">
                             <parameter expr="association_name" />
                         </raise>
                     </transition>
                 </state>
-                <state id="packing">
+<!--                 <state id="packing">
                     <transition event="button_created" target='../running'>
                     </transition>
                 </state>
-                <parallel id="running">
+ -->
+                 <parallel id="running">
                     <transition port="field_ui" event="window_close" target="../deleting">
                         <raise event="delete_instance" scope="cd">
                             <parameter expr='"buttons"' />
@@ -167,7 +234,7 @@
                     </transition>
                     <state id="main_behaviour" initial="running">
                         <state id="running">
-                            <transition port="field_ui" event="right_click" target="../creating">
+                            <transition port="field_ui" event="right_click" target="../creating_ball">
                                 <parameter name="x" />
                                 <parameter name="y" />
                                 <parameter name="button" />
@@ -180,7 +247,7 @@
                                 </raise>
                             </transition>
                         </state>
-                        <state id="creating">
+                        <state id="creating_ball">
                             <transition event="instance_created" target="../running">
                                 <parameter name="association_name" type="string"/>
                                 <raise scope="cd" event="start_instance">
@@ -226,6 +293,9 @@
                         <raise event="delete_field" scope="narrow" target="'parent'">
                             <parameter expr='self.association_name' />
                         </raise>
+                        <raise port="ui" event="destroy_window">
+                            <parameter expr="self.window_id"/><!-- window to destroy -->
+                        </raise>
                     </transition>
                 </state>
                 <state id="deleted" />
@@ -234,30 +304,60 @@
     </class>
     
     <class name="Button">
+        <attribute name="window_id" />
+        <attribute name="event_name" />
+        <attribute name="button_id" />
         <relationships>
             <association name="parent" class="Field" min="1" max="1" />
         </relationships>
         <inport name="button_ui"/>
         <constructor>
-            <parameter name="tkparent" type="object" />
+            <parameter name="window_id" type="int" />
             <parameter name="event_name" type="str" />
             <parameter name="button_text" type="str" />
             <body>
                 <![CDATA[
+                self.window_id = window_id;
                 self.event_name = event_name;
-                button = ui.append_button(tkparent, event_name);
-                ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
+
+                # Translated to events:
+                #button = ui.append_button(tkparent, event_name);
+                #ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click', self.inports['button_ui']);
                 ]]>
             </body>
         </constructor>
-        <scxml initial="initializing">
-            <state id="initializing">
+        <scxml initial="creating_button">
+            <state id="creating_button">
+                <onentry>
+                    <raise port="ui" event="create_button">
+                        <parameter expr="self.window_id"/><!-- window_id -->
+                        <parameter expr="self.event_name"/><!-- text -->
+                        <parameter expr="self.inports['button_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="button_created" target="../running">
+                    <parameter name="button_id" type="int"/>
+                    <script>
+                        <![CDATA[
+                        self.button_id = button_id
+                        ]]>
+                    </script>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="button_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.MOUSE_CLICK"/><!-- tk_event -->
+                        <parameter expr='"mouse_click"'/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['button_ui']"/><!-- inport for response -->
+                    </raise>
+                </transition>
+            </state>
+<!--             <state id="initializing">
                 <transition target="../running">
                     <raise event="button_created" scope="narrow" target="'parent'">
                     </raise>
                 </transition>
             </state>
-            <state id="running">
+ -->
+             <state id="running">
                 <transition port='button_ui' event="mouse_click" target='.' cond="button == ui.MOUSE_BUTTONS.LEFT">
                     <parameter name="x" />
                     <parameter name="y" />
@@ -289,11 +389,12 @@
                 self.mouse_pos = {};
                 self.smooth = 0.4; # value between 0 and 1
 
-                circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
-                ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
-                ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
-                ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
-                self.element = circle;
+                # TODO:
+                #circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
+                #ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
+                #ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
+                #ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
+                #self.element = circle;
                 ]]>
             </body>
         </constructor>

+ 25 - 8
sccd/compiler/DEVS_generator.py

@@ -113,7 +113,12 @@ class DEVSGenerator(Visitor):
         self.writer.addAssignment("out_dict", "{}")
 
         self.writer.beginForLoopIterateArray(GLC.SelfProperty("State.to_send"), "(source, target, id, message)")
+        self.writer.beginIf(GLC.ArrayContains("out_dict", "self.output[target]"))
+        self.writer.add(GLC.FunctionCall("out_dict[self.output[target]].append", ["(source, target, id, message)"]))
+        self.writer.endIf()
+        self.writer.beginElse()
         self.writer.addAssignment(f"out_dict[self.output[target]]", "[(source, target, id, message)]")
+        self.writer.endElse()
         self.writer.endForLoopIterateArray()
 
         self.writer.add(GLC.ReturnStatement("out_dict"))
@@ -380,8 +385,8 @@ class DEVSGenerator(Visitor):
 
         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"])]))
-        self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_created\"", "None", "parameters=[f\"{input[0]}[{len(self.instances)-1}]\"]"]))
-        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), [f"(\"{class_node.name}\", TODO, input[2], ev)"]))
+        self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_created\"", "None", "parameters=[f\"{input[3].parameters[0]}[{len(self.instances)-1}]\"]"]))
+        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(input[1], input[0], input[2], ev)"]))
         self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("start_instance")))
@@ -389,23 +394,23 @@ class DEVSGenerator(Visitor):
         self.writer.addAssignment("instance", "list(self.instances)[input[2]]")
         self.writer.add(GLC.FunctionCall("instance.start"))
 
-        self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_started\"", "None", "parameters=[TODO]"]))
+        self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_started\"", "None", "parameters=[f\"{input[0]}[{input[2]}]\"]"]))
         self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(input[0], input[1], input[2], ev)"]))
         self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("delete_instance")))
         self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_deleted\"", "None", "parameters=[TODO]"]))
-        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(TODO, TODO, TODO, ev)"]))
+        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(self.name, TODO, TODO, ev)"]))
         self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("associate_instance")))
         self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_associated\"", "None", "parameters=[TODO]"]))
-        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(TODO, TODO, TODO, ev)"]))
+        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(self.name, TODO, TODO, ev)"]))
         self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("disassociate_instance")))
         self.writer.addAssignment("ev", GLC.FunctionCall("Event", ["\"instance_disassociated\"", "None", "parameters=[TODO]"]))
-        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(TODO, TODO, TODO, ev)"]))
+        self.writer.add(GLC.FunctionCall(GLC.SelfProperty("to_send.append"), ["(self.name, TODO, TODO, ev)"]))
         self.writer.endElseIf()
 
         self.writer.beginElseIf(GLC.EqualsExpression("input[3].name", GLC.String("instance_created")))
@@ -461,7 +466,17 @@ class DEVSGenerator(Visitor):
         self.writer.add(GLC.FunctionCall(GLC.SelfProperty("stepAll")))
 
         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.addAssignment(GLC.SelfProperty("next_time"), "0")
+        self.writer.endIf()
+        self.writer.beginElseIf(GLC.EqualsExpression("next_earliest", "INFINITY"))
+        self.writer.addAssignment(GLC.SelfProperty("next_time"), "INFINITY")
+        self.writer.endElseIf()
+        self.writer.beginElse()
         self.writer.addAssignment(GLC.SelfProperty("next_time"), "next_earliest - earliest")
+        self.writer.endElse()
 
         self.writer.add(GLC.ReturnStatement(GLC.SelfProperty("instances")))
         self.writer.endMethodBody()
@@ -471,7 +486,7 @@ class DEVSGenerator(Visitor):
         self.writer.beginMethodBody()
         self.writer.addAssignment("to_dict", "{}")
         self.writer.beginForLoopIterateArray(GLC.SelfProperty("to_send"), "sending")
-        self.writer.beginIf(GLC.EqualsExpression("sending[0]", "None"))
+        self.writer.beginIf(GLC.EqualsExpression("sending[3].port", "None"))
 
         self.writer.beginIf(GLC.ArrayContains("to_dict", "self.obj_manager_out"))
         self.writer.add(GLC.FunctionCall("to_dict[self.obj_manager_out].append", ["sending"]))
@@ -513,6 +528,7 @@ class DEVSGenerator(Visitor):
         self.writer.endMethod()
 
         # visit constructor
+        class_node.constructors[0].name = class_node.name
         class_node.constructors[0].accept(self)
 
         self.writer.endClass()
@@ -533,6 +549,7 @@ class DEVSGenerator(Visitor):
         self.writer.endSuperClassConstructorCall()
 
         self.writer.addAssignment(GLC.SelfProperty("elapsed"), "0")
+        self.writer.addAssignment(GLC.SelfProperty("name"), GLC.String(constructor.name))
 
         #self.writer.addAssignment(GLC.SelfProperty("obj_manager_in"),
          #                         GLC.FunctionCall(GLC.SelfProperty("addInPort"), [GLC.String("obj_manager_in")]))
@@ -558,7 +575,7 @@ class DEVSGenerator(Visitor):
         if constructor.parent_class.name == constructor.parent_class.class_diagram.default_class.name:
             self.writer.add(GLC.FunctionCall(GLC.SelfProperty("instances.add"), [f"{constructor.parent_class.name}Instance(self)"]))
 
-        self.writer.addAssignment(GLC.SelfProperty("next_time"), "0")
+        self.writer.addAssignment(GLC.SelfProperty("next_time"), "INFINITY")
 
         self.writer.endMethodBody()
         self.writer.endConstructor()

+ 2 - 2
sccd/runtime/DEVS_statecharts_core.py

@@ -854,8 +854,8 @@ class ObjectManagerBase(object):
 
             hulp = [association_name]
             hulp.extend(parameters[3:])
-            #self.to_send.append((self.name, class_name, id, Event('create_instance', None, hulp)))
-            self.to_send.append((None, class_name, id, Event('create_instance', None, hulp)))
+            self.to_send.append((self.name, class_name, id, Event('create_instance', None, hulp)))
+            #self.to_send.append((None, class_name, id, Event('create_instance', None, hulp)))
 
             #if not new_instance:
             #    raise ParameterException("Creating instance: no such class: " + class_name)