Browse Source

Generated some unit tests (need to be extended) + worked on the elevator example

Sam Pieters 1 year ago
parent
commit
78156f4c58

+ 1 - 10
README.md

@@ -97,14 +97,5 @@ python3 runner.py
 ### Timer
 
 ## TODO
+1) Is there no < or > in the condition transition
 
-
-## TO ASK
-1) De tests van bouncingballs --> server was down/traag gisteren
-2) Destroy all verwijdert alles maar simulatie stopt niet --> setTerminationCondition(condition)?
-3) FixedTimerThread werkt niet in DEVS, gevalvan threading ipv Eventloop
-4) if earliest_event_time == INFINITY:
-		if self.finished_callback: self.finished_callback() # TODO: This is not necessarily correct (keep_running necessary?)
-        	return
-Dit gebeurd in sccd soms maar als dit gebeurd in DEVS dan betekend dit dat er een event kan zijn 
-6) Hans example --> Bouncing Elevator Balls

+ 32 - 0
examples/ElevatorBalls/PyDEVS/runner.py

@@ -0,0 +1,32 @@
+import tkinter as tk
+import examples.ElevatorBalls.PyDEVS.target as target
+from sccd.runtime.libs.ui_v2 import UI
+from sccd.runtime.DEVS_loop import DEVSSimulator
+
+class OutputListener:
+	def __init__(self, ui):
+		self.ui = ui
+
+	def add(self, events):
+		for event in events:
+			if event.port == "ui":
+				method = getattr(self.ui, event.name)
+				method(*event.parameters)
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	refs = {"ui": model.in_ui, "field_ui": model.atomic0.field_ui, "floor_ui": model.atomic1.floor_ui, "elevator_ui": model.atomic2.elevator_ui, "ball_ui": model.atomic3.ball_ui}
+
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	sim = DEVSSimulator(model, refs)
+
+	sim.setVerbose()
+	sim.setRealTimePlatformTk(tkroot)
+
+	ui = UI(tkroot, sim)
+
+	listener = OutputListener(ui)
+	sim.setListenPorts(model.out_ui, listener.add)
+	sim.simulate()
+	tkroot.mainloop()

+ 783 - 0
examples/ElevatorBalls/PyDEVS/target.py

@@ -0,0 +1,783 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration) and Sam Pieters (DEVS)
+
+Model author: Sam Pieters
+Model name:   Elevator Balls
+
+"""
+
+from sccd.runtime.DEVS_statecharts_core import *
+import sccd.runtime.libs.ui_v2 as ui
+import random
+import time
+
+CANVAS_DIMS = (800, 550)
+FLOOR_LENGTH = 350
+FLOOR_SPACE = 50
+FLOORS = 4
+
+# package "Elevator Balls"
+
+class MainAppInstance(RuntimeClassBase):
+    def __init__(self, atomdevs):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        self.associations["floor"] = Association("Floor", 2, -1)
+        self.associations["elevator"] = Association("Elevator", 1, 1)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # user defined attributes
+        self.window_id = None
+        self.canvas_id = None
+        self.num_floors = None
+        self.floor_dimensions = None
+        
+        # call user defined constructor
+        MainAppInstance.user_defined_constructor(self)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+        port_name = Ports.addInputPort("field_ui", self)
+        atomdevs.addInPort(port_name)
+        atomdevs.port_mappings[port_name] = atomdevs.next_instance
+        self.inports["field_ui"] = port_name
+    
+    def user_defined_constructor(self):
+        self.num_floors = 0
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /creating_window
+        self.states["/creating_window"] = State(1, "/creating_window", self)
+        self.states["/creating_window"].setEnter(self._creating_window_enter)
+        
+        # state /creating_canvas
+        self.states["/creating_canvas"] = State(2, "/creating_canvas", self)
+        self.states["/creating_canvas"].setEnter(self._creating_canvas_enter)
+        
+        # state /create_floors
+        self.states["/create_floors"] = State(3, "/create_floors", self)
+        
+        # state /wait
+        self.states["/wait"] = State(4, "/wait", self)
+        
+        # state /create_elevator
+        self.states["/create_elevator"] = State(5, "/create_elevator", self)
+        
+        # state /creating
+        self.states["/creating"] = State(6, "/creating", self)
+        
+        # state /waiting
+        self.states["/waiting"] = State(7, "/waiting", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_window"])
+        self.states[""].addChild(self.states["/creating_canvas"])
+        self.states[""].addChild(self.states["/create_floors"])
+        self.states[""].addChild(self.states["/wait"])
+        self.states[""].addChild(self.states["/create_elevator"])
+        self.states[""].addChild(self.states["/creating"])
+        self.states[""].addChild(self.states["/waiting"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/creating_window"]
+        
+        # transition /creating_window
+        _creating_window_0 = Transition(self, self.states["/creating_window"], [self.states["/creating_canvas"]])
+        _creating_window_0.setAction(self._creating_window_0_exec)
+        _creating_window_0.setTrigger(Event("window_created", None))
+        self.states["/creating_window"].addTransition(_creating_window_0)
+        
+        # transition /creating_canvas
+        _creating_canvas_0 = Transition(self, self.states["/creating_canvas"], [self.states["/create_floors"]])
+        _creating_canvas_0.setAction(self._creating_canvas_0_exec)
+        _creating_canvas_0.setTrigger(Event("canvas_created", None))
+        self.states["/creating_canvas"].addTransition(_creating_canvas_0)
+        
+        # transition /create_floors
+        _create_floors_0 = Transition(self, self.states["/create_floors"], [self.states["/create_elevator"]])
+        _create_floors_0.setTrigger(None)
+        _create_floors_0.setGuard(self._create_floors_0_guard)
+        self.states["/create_floors"].addTransition(_create_floors_0)
+        _create_floors_1 = Transition(self, self.states["/create_floors"], [self.states["/wait"]])
+        _create_floors_1.setAction(self._create_floors_1_exec)
+        _create_floors_1.setTrigger(None)
+        _create_floors_1.setGuard(self._create_floors_1_guard)
+        self.states["/create_floors"].addTransition(_create_floors_1)
+        
+        # transition /wait
+        _wait_0 = Transition(self, self.states["/wait"], [self.states["/wait"]])
+        _wait_0.setAction(self._wait_0_exec)
+        _wait_0.setTrigger(Event("instance_created", None))
+        self.states["/wait"].addTransition(_wait_0)
+        _wait_1 = Transition(self, self.states["/wait"], [self.states["/create_floors"]])
+        _wait_1.setTrigger(Event("instance_started", None))
+        self.states["/wait"].addTransition(_wait_1)
+        
+        # transition /create_elevator
+        _create_elevator_0 = Transition(self, self.states["/create_elevator"], [self.states["/creating"]])
+        _create_elevator_0.setAction(self._create_elevator_0_exec)
+        _create_elevator_0.setTrigger(None)
+        self.states["/create_elevator"].addTransition(_create_elevator_0)
+        
+        # transition /creating
+        _creating_0 = Transition(self, self.states["/creating"], [self.states["/waiting"]])
+        _creating_0.setAction(self._creating_0_exec)
+        _creating_0.setTrigger(Event("instance_created", None))
+        self.states["/creating"].addTransition(_creating_0)
+    
+    def _creating_window_enter(self):
+        self.big_step.outputEvent(Event("create_window", self.getOutPortName("ui"), [CANVAS_DIMS[0], CANVAS_DIMS[1], "Bouncing Balls Elevator", self.inports['field_ui']]))
+    
+    def _creating_canvas_enter(self):
+        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, CANVAS_DIMS[0], CANVAS_DIMS[1] - 200, {'background':'#fff'}, self.inports['field_ui']]))
+    
+    def _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 _creating_canvas_0_exec(self, parameters):
+        canvas_id = parameters[0]
+        self.canvas_id = canvas_id
+    
+    def _create_floors_0_guard(self, parameters):
+        return self.num_floors == FLOORS
+    
+    def _create_floors_1_exec(self, parameters):
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "floor", "Floor", self.canvas_id, self.num_floors]))
+        self.num_floors += 1
+    
+    def _create_floors_1_guard(self, parameters):
+        return self.num_floors != FLOORS
+    
+    def _wait_0_exec(self, parameters):
+        association_name = parameters[0]
+        self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
+    
+    def _create_elevator_0_exec(self, parameters):
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "elevator", "Elevator", self.canvas_id]))
+    
+    def _creating_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, self.canvas_id, self.window_id])]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/creating_window"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class MainApp(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.outputs["floor"] = self.addOutPort("floor")
+        self.outputs["elevator"] = self.addOutPort("elevator")
+        self.field_ui = self.addInPort("field_ui")
+        self.instances[self.next_instance] = MainAppInstance(self)
+        self.next_instance = self.next_instance + 1
+    
+    def constructObject(self, parameters):
+        new_instance = MainAppInstance(self)
+        return new_instance
+
+class FloorInstance(RuntimeClassBase):
+    def __init__(self, atomdevs, canvas_id, floor_num):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        self.associations["balls"] = Association("Ball", 0, -1)
+        self.associations["parent"] = Association("MainApp", 1, 1)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # user defined attributes
+        self.window_id = None
+        self.canvas_id = None
+        self.elevator_id = None
+        
+        # call user defined constructor
+        FloorInstance.user_defined_constructor(self, canvas_id, floor_num)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+        port_name = Ports.addInputPort("floor_ui", self)
+        atomdevs.addInPort(port_name)
+        atomdevs.port_mappings[port_name] = atomdevs.next_instance
+        self.inports["floor_ui"] = port_name
+    
+    def user_defined_constructor(self, canvas_id, floor_num):
+        self.canvas_id = canvas_id;
+        
+        y_dim = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+        
+        self.dim = {'x': FLOOR_LENGTH, 'y': y_dim};
+        self.pos = {'x': FLOOR_LENGTH / 2, 'y': (y_dim /2) + (floor_num * (y_dim + FLOOR_SPACE))};
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /creating_floor
+        self.states["/creating_floor"] = State(1, "/creating_floor", self)
+        self.states["/creating_floor"].setEnter(self._creating_floor_enter)
+        
+        # state /running
+        self.states["/running"] = State(2, "/running", self)
+        
+        # state /running/create_random_ball
+        self.states["/running/create_random_ball"] = State(3, "/running/create_random_ball", self)
+        self.states["/running/create_random_ball"].setEnter(self._running_create_random_ball_enter)
+        self.states["/running/create_random_ball"].setExit(self._running_create_random_ball_exit)
+        
+        # state /running/wait
+        self.states["/running/wait"] = State(4, "/running/wait", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_floor"])
+        self.states[""].addChild(self.states["/running"])
+        self.states["/running"].addChild(self.states["/running/create_random_ball"])
+        self.states["/running"].addChild(self.states["/running/wait"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/creating_floor"]
+        self.states["/running"].default_state = self.states["/running/create_random_ball"]
+        
+        # transition /creating_floor
+        _creating_floor_0 = Transition(self, self.states["/creating_floor"], [self.states["/running"]])
+        _creating_floor_0.setTrigger(Event("rectangle_created", None))
+        self.states["/creating_floor"].addTransition(_creating_floor_0)
+        
+        # transition /running/create_random_ball
+        _running_create_random_ball_0 = Transition(self, self.states["/running/create_random_ball"], [self.states["/running/wait"]])
+        _running_create_random_ball_0.setAction(self._running_create_random_ball_0_exec)
+        _running_create_random_ball_0.setTrigger(Event("_0after"))
+        self.states["/running/create_random_ball"].addTransition(_running_create_random_ball_0)
+        
+        # transition /running/wait
+        _running_wait_0 = Transition(self, self.states["/running/wait"], [self.states["/running/create_random_ball"]])
+        _running_wait_0.setAction(self._running_wait_0_exec)
+        _running_wait_0.setTrigger(Event("instance_created", None))
+        self.states["/running/wait"].addTransition(_running_wait_0)
+    
+    def _creating_floor_enter(self):
+        self.big_step.outputEvent(Event("create_rectangle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.dim['x'], self.dim['y'], {'fill':'white', 'outline': 'black'}, self.inports['floor_ui']]))
+    
+    def _running_create_random_ball_enter(self):
+        self.addTimer(0, 1)
+    
+    def _running_create_random_ball_exit(self):
+        self.removeTimer(0)
+    
+    def _running_create_random_ball_0_exec(self, parameters):
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas_id, 100, 100]))
+    
+    def _running_wait_0_exec(self, parameters):
+        association_name = parameters[0]
+        self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/creating_floor"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class Floor(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.outputs["balls"] = self.addOutPort("balls")
+        self.outputs["parent"] = self.addOutPort("parent")
+        self.floor_ui = self.addInPort("floor_ui")
+    
+    def constructObject(self, parameters):
+        new_instance = FloorInstance(self, parameters[2], parameters[3])
+        return new_instance
+
+class ElevatorInstance(RuntimeClassBase):
+    def __init__(self, atomdevs, canvas_id):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        self.associations["balls"] = Association("Ball", 0, -1)
+        self.associations["parent"] = Association("MainApp", 1, 1)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # user defined attributes
+        self.window_id = None
+        self.canvas_id = None
+        self.elevator_id = None
+        
+        # call user defined constructor
+        ElevatorInstance.user_defined_constructor(self, canvas_id)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+        port_name = Ports.addInputPort("elevator_ui", self)
+        atomdevs.addInPort(port_name)
+        atomdevs.port_mappings[port_name] = atomdevs.next_instance
+        self.inports["elevator_ui"] = port_name
+    
+    def user_defined_constructor(self, canvas_id):
+        self.canvas_id = canvas_id;
+        self.button_ids = {
+            'up': None,
+            'down': None,
+            'open': None
+        };
+        self.open_button = None;
+        self.is_open = False;
+        
+        elevator_height = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+        
+        self.dim = {'x': elevator_height, 'y': elevator_height};
+        self.vel = {'x': 0, 'y': -2};
+        self.pos = {'x': FLOOR_LENGTH + (elevator_height / 2), 'y': 75};
+        self.smooth = 0.6; # value between 0 and 1
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /root
+        self.states["/root"] = State(1, "/root", self)
+        
+        # state /root/waiting
+        self.states["/root/waiting"] = State(2, "/root/waiting", self)
+        
+        # state /root/creating_elevator
+        self.states["/root/creating_elevator"] = State(3, "/root/creating_elevator", self)
+        self.states["/root/creating_elevator"].setEnter(self._root_creating_elevator_enter)
+        
+        # state /root/running
+        self.states["/root/running"] = State(4, "/root/running", self)
+        self.states["/root/running"].setEnter(self._root_running_enter)
+        self.states["/root/running"].setExit(self._root_running_exit)
+        
+        # add children
+        self.states[""].addChild(self.states["/root"])
+        self.states["/root"].addChild(self.states["/root/waiting"])
+        self.states["/root"].addChild(self.states["/root/creating_elevator"])
+        self.states["/root"].addChild(self.states["/root/running"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/root"]
+        self.states["/root"].default_state = self.states["/root/waiting"]
+        
+        # transition /root/waiting
+        _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/creating_elevator"]])
+        _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/creating_elevator
+        _root_creating_elevator_0 = Transition(self, self.states["/root/creating_elevator"], [self.states["/root/running"]])
+        _root_creating_elevator_0.setAction(self._root_creating_elevator_0_exec)
+        _root_creating_elevator_0.setTrigger(Event("rectangle_created", None))
+        self.states["/root/creating_elevator"].addTransition(_root_creating_elevator_0)
+        
+        # transition /root/running
+        _root_running_0 = Transition(self, self.states["/root/running"], [self.states["/root/running"]])
+        _root_running_0.setAction(self._root_running_0_exec)
+        _root_running_0.setTrigger(Event("_0after"))
+        self.states["/root/running"].addTransition(_root_running_0)
+        _root_running_1 = Transition(self, self.states["/root/running"], [self.states["/root/running"]])
+        _root_running_1.setAction(self._root_running_1_exec)
+        _root_running_1.setTrigger(Event("right_click", self.getInPortName("elevator_ui")))
+        self.states["/root/running"].addTransition(_root_running_1)
+        _root_running_2 = Transition(self, self.states["/root/running"], [self.states["/root/running"]])
+        _root_running_2.setAction(self._root_running_2_exec)
+        _root_running_2.setTrigger(Event("instance_created", None))
+        self.states["/root/running"].addTransition(_root_running_2)
+    
+    def _root_creating_elevator_enter(self):
+        self.big_step.outputEvent(Event("create_rectangle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.dim['x'], self.dim['y'], {'fill':'white', 'outline': 'black'}, self.inports['elevator_ui']]))
+    
+    def _root_running_enter(self):
+        self.addTimer(0, 0.02)
+    
+    def _root_running_exit(self):
+        self.removeTimer(0)
+    
+    def _root_waiting_0_exec(self, parameters):
+        association_name = parameters[0]
+        canvas_id = parameters[1]
+        window_id = parameters[2]
+        self.association_name = association_name
+        self.canvas_id = canvas_id
+        self.window_id = window_id
+    
+    def _root_creating_elevator_0_exec(self, parameters):
+        canvas_id = parameters[0]
+        rect_id = parameters[1]
+        self.elevator_id = rect_id
+        self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RIGHT_CLICK, 'right_click', self.inports['elevator_ui']]))
+    
+    def _root_running_0_exec(self, parameters):
+        # Invert velocity when colliding with canvas border:
+        if self.pos['y']-(self.dim['y']/2) <= 0 or self.pos['y']+(self.dim['y']/2) >= CANVAS_DIMS[1]:
+            self.vel['y'] = -self.vel['y'];
+        self.big_step.outputEvent(Event("move_element", self.getOutPortName("ui"), [self.canvas_id, self.elevator_id, self.vel['x'], self.vel['y']]))
+        self.pos['x'] += self.vel['x']
+        self.pos['y'] += self.vel['y']
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'balls', Event("update_bounds", None, [self.pos, self.dim, self.vel])]))
+    
+    def _root_running_1_exec(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas_id, x, y]))
+    
+    def _root_running_2_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])]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/root"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class Elevator(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.outputs["balls"] = self.addOutPort("balls")
+        self.outputs["parent"] = self.addOutPort("parent")
+        self.elevator_ui = self.addInPort("elevator_ui")
+    
+    def constructObject(self, parameters):
+        new_instance = ElevatorInstance(self, parameters[2])
+        return new_instance
+
+class BallInstance(RuntimeClassBase):
+    def __init__(self, atomdevs, canvas_id, x, y):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        self.associations["parent"] = Association("MainApp", 1, 1)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # user defined attributes
+        self.canvas_id = None
+        self.pos = None
+        
+        # call user defined constructor
+        BallInstance.user_defined_constructor(self, canvas_id, x, y)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+        port_name = Ports.addInputPort("ball_ui", self)
+        atomdevs.addInPort(port_name)
+        atomdevs.port_mappings[port_name] = atomdevs.next_instance
+        self.inports["ball_ui"] = port_name
+    
+    def user_defined_constructor(self, canvas_id, x, y):
+        self.canvas_id = canvas_id;
+        self.r = 5.0;
+        self.vel = {'x': random.uniform(-5.0, 5.0), 'y': random.uniform(-5.0, 5.0)};
+        self.pos = {'x': x, 'y': y};
+        self.smooth = 0.6; # value between 0 and 1
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /main_behaviour
+        self.states["/main_behaviour"] = State(1, "/main_behaviour", self)
+        
+        # state /main_behaviour/initializing
+        self.states["/main_behaviour/initializing"] = State(2, "/main_behaviour/initializing", self)
+        
+        # state /main_behaviour/creating_circle
+        self.states["/main_behaviour/creating_circle"] = State(3, "/main_behaviour/creating_circle", self)
+        self.states["/main_behaviour/creating_circle"].setEnter(self._main_behaviour_creating_circle_enter)
+        
+        # state /main_behaviour/bouncing
+        self.states["/main_behaviour/bouncing"] = State(4, "/main_behaviour/bouncing", self)
+        self.states["/main_behaviour/bouncing"].setEnter(self._main_behaviour_bouncing_enter)
+        self.states["/main_behaviour/bouncing"].setExit(self._main_behaviour_bouncing_exit)
+        
+        # state /main_behaviour/dragging
+        self.states["/main_behaviour/dragging"] = State(5, "/main_behaviour/dragging", self)
+        
+        # state /main_behaviour/selected
+        self.states["/main_behaviour/selected"] = State(6, "/main_behaviour/selected", self)
+        
+        # state /deleted
+        self.states["/deleted"] = State(7, "/deleted", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/main_behaviour"])
+        self.states[""].addChild(self.states["/deleted"])
+        self.states["/main_behaviour"].addChild(self.states["/main_behaviour/initializing"])
+        self.states["/main_behaviour"].addChild(self.states["/main_behaviour/creating_circle"])
+        self.states["/main_behaviour"].addChild(self.states["/main_behaviour/bouncing"])
+        self.states["/main_behaviour"].addChild(self.states["/main_behaviour/dragging"])
+        self.states["/main_behaviour"].addChild(self.states["/main_behaviour/selected"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/main_behaviour"]
+        self.states["/main_behaviour"].default_state = self.states["/main_behaviour/initializing"]
+        
+        # transition /main_behaviour/initializing
+        _main_behaviour_initializing_0 = Transition(self, self.states["/main_behaviour/initializing"], [self.states["/main_behaviour/creating_circle"]])
+        _main_behaviour_initializing_0.setAction(self._main_behaviour_initializing_0_exec)
+        _main_behaviour_initializing_0.setTrigger(Event("set_association_name", None))
+        self.states["/main_behaviour/initializing"].addTransition(_main_behaviour_initializing_0)
+        
+        # transition /main_behaviour/creating_circle
+        _main_behaviour_creating_circle_0 = Transition(self, self.states["/main_behaviour/creating_circle"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_creating_circle_0.setAction(self._main_behaviour_creating_circle_0_exec)
+        _main_behaviour_creating_circle_0.setTrigger(Event("circle_created", None))
+        self.states["/main_behaviour/creating_circle"].addTransition(_main_behaviour_creating_circle_0)
+        
+        # transition /main_behaviour/bouncing
+        _main_behaviour_bouncing_0 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_bouncing_0.setAction(self._main_behaviour_bouncing_0_exec)
+        _main_behaviour_bouncing_0.setTrigger(Event("_0after"))
+        self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_0)
+        _main_behaviour_bouncing_1 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/selected"]])
+        _main_behaviour_bouncing_1.setAction(self._main_behaviour_bouncing_1_exec)
+        _main_behaviour_bouncing_1.setTrigger(Event("mouse_press", self.getInPortName("ball_ui")))
+        _main_behaviour_bouncing_1.setGuard(self._main_behaviour_bouncing_1_guard)
+        self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_1)
+        _main_behaviour_bouncing_2 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_bouncing_2.setAction(self._main_behaviour_bouncing_2_exec)
+        _main_behaviour_bouncing_2.setTrigger(Event("update_bounds", None))
+        self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_2)
+        
+        # transition /main_behaviour/dragging
+        _main_behaviour_dragging_0 = Transition(self, self.states["/main_behaviour/dragging"], [self.states["/main_behaviour/dragging"]])
+        _main_behaviour_dragging_0.setAction(self._main_behaviour_dragging_0_exec)
+        _main_behaviour_dragging_0.setTrigger(Event("mouse_move", self.getInPortName("ball_ui")))
+        self.states["/main_behaviour/dragging"].addTransition(_main_behaviour_dragging_0)
+        _main_behaviour_dragging_1 = Transition(self, self.states["/main_behaviour/dragging"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_dragging_1.setAction(self._main_behaviour_dragging_1_exec)
+        _main_behaviour_dragging_1.setTrigger(Event("mouse_release", self.getInPortName("ball_ui")))
+        self.states["/main_behaviour/dragging"].addTransition(_main_behaviour_dragging_1)
+        
+        # transition /main_behaviour/selected
+        _main_behaviour_selected_0 = Transition(self, self.states["/main_behaviour/selected"], [self.states["/main_behaviour/dragging"]])
+        _main_behaviour_selected_0.setAction(self._main_behaviour_selected_0_exec)
+        _main_behaviour_selected_0.setTrigger(Event("mouse_press", self.getInPortName("ball_ui")))
+        _main_behaviour_selected_0.setGuard(self._main_behaviour_selected_0_guard)
+        self.states["/main_behaviour/selected"].addTransition(_main_behaviour_selected_0)
+        _main_behaviour_selected_1 = Transition(self, self.states["/main_behaviour/selected"], [self.states["/deleted"]])
+        _main_behaviour_selected_1.setAction(self._main_behaviour_selected_1_exec)
+        _main_behaviour_selected_1.setTrigger(Event("delete_self", None))
+        self.states["/main_behaviour/selected"].addTransition(_main_behaviour_selected_1)
+    
+    def _main_behaviour_creating_circle_enter(self):
+        self.big_step.outputEvent(Event("create_circle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.r, {'fill':'#000'}, self.inports['ball_ui']]))
+    
+    def _main_behaviour_bouncing_enter(self):
+        self.addTimer(0, 0.02)
+    
+    def _main_behaviour_bouncing_exit(self):
+        self.removeTimer(0)
+    
+    def _main_behaviour_initializing_0_exec(self, parameters):
+        association_name = parameters[0]
+        self.association_name = association_name
+    
+    def _main_behaviour_creating_circle_0_exec(self, parameters):
+        canvas_id = parameters[0]
+        circle_id = parameters[1]
+        self.circle_id = circle_id
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_PRESS, 'mouse_press', self.inports['ball_ui']]))
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_MOVE, 'mouse_move', self.inports['ball_ui']]))
+        self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release', self.inports['ball_ui']]))
+    
+    def _main_behaviour_bouncing_0_exec(self, parameters):
+        # Check collision with the left and right borders
+        if self.pos['x'] - self.r < self.rect_pos['x'] - (self.rect_dim['x'] / 2):
+            self.pos['x'] = self.rect_pos['x'] - (self.rect_dim['x'] / 2) + self.r  # Correct position
+            self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+        elif self.pos['x'] + self.r > self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+            self.pos['x'] = self.rect_pos['x'] + (self.rect_dim['x'] / 2) - self.r  # Correct position
+            self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+        
+        # Check collision with the top and bottom borders
+        if self.pos['y'] - self.r < self.rect_pos['y'] - (self.rect_dim['y'] / 2):
+            self.pos['y'] = self.rect_pos['y'] - (self.rect_dim['y'] / 2) + self.r  # Correct position
+            self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+        elif self.pos['y'] + self.r > self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+            self.pos['y'] = self.rect_pos['y'] + (self.rect_dim['y'] / 2) - self.r  # Correct position
+            self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+        self.big_step.outputEvent(Event("move_element", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, self.vel['x'], self.vel['y']]))
+        self.pos['x'] += self.vel['x']
+        self.pos['y'] += self.vel['y']
+    
+    def _main_behaviour_bouncing_1_exec(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        self.big_step.outputEvent(Event("set_element_color", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, '#ff0']))
+    
+    def _main_behaviour_bouncing_1_guard(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        return button == ui.MOUSE_BUTTONS.LEFT
+    
+    def _main_behaviour_bouncing_2_exec(self, parameters):
+        pos = parameters[0]
+        dim = parameters[1]
+        vel = parameters[2]
+        self.rect_pos = pos
+        self.rect_dim = dim
+        self.rect_vel = vel
+    
+    def _main_behaviour_dragging_0_exec(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        # Always keep ball within canvas:
+        x = min(max(0+self.r, x), CANVAS_DIMS[0]-self.r)
+        y = min(max(0+self.r, y), CANVAS_DIMS[1]-self.r)
+        
+        dx = x - self.pos['x']
+        dy = y - self.pos['y']
+        
+        self.vel = {
+            'x': (1-self.smooth)*dx + self.smooth*self.vel['x'],
+            'y': (1-self.smooth)*dy + self.smooth*self.vel['y']
+        }
+        
+        self.pos = {'x': x, 'y': y}
+        self.big_step.outputEvent(Event("set_element_pos", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, x-self.r, y-self.r]))
+    
+    def _main_behaviour_dragging_1_exec(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        self.big_step.outputEvent(Event("set_element_color", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, '#f00']))
+    
+    def _main_behaviour_selected_0_exec(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        self.mouse_pos = {'x':x, 'y':y};
+    
+    def _main_behaviour_selected_0_guard(self, parameters):
+        x = parameters[0]
+        y = parameters[1]
+        button = parameters[2]
+        return button == ui.MOUSE_BUTTONS.LEFT
+    
+    def _main_behaviour_selected_1_exec(self, parameters):
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("delete_ball", None, [self.association_name])]))
+        self.big_step.outputEvent(Event("destroy_element", self.getOutPortName("ui"), [self.canvas_id, self.element_id]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/main_behaviour"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class Ball(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.outputs["parent"] = self.addOutPort("parent")
+        self.ball_ui = self.addInPort("ball_ui")
+    
+    def constructObject(self, parameters):
+        new_instance = BallInstance(self, parameters[2], parameters[3], parameters[4])
+        return new_instance
+
+class ObjectManagerState:
+    def __init__(self):
+        self.to_send = [("MainApp", "MainApp", Event("start_instance", None, ["MainApp[0]"], 0))]
+
+class ObjectManager(TheObjectManager):
+    def __init__(self, name):
+        TheObjectManager.__init__(self, name)
+        self.State = ObjectManagerState()
+        self.input = self.addInPort("input")
+        self.output["MainApp"] = self.addOutPort()
+        self.output["Floor"] = self.addOutPort()
+        self.output["Elevator"] = self.addOutPort()
+        self.output["Ball"] = self.addOutPort()
+
+class Controller(CoupledDEVS):
+    def __init__(self, name):
+        CoupledDEVS.__init__(self, name)
+        self.in_ui = self.addInPort("ui")
+        Ports.addInputPort("ui")
+        self.out_ui = self.addOutPort("ui")
+        Ports.addOutputPort("ui")
+        self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
+        self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        self.atomic1 = self.addSubModel(Floor("Floor"))
+        self.atomic2 = self.addSubModel(Elevator("Elevator"))
+        self.atomic3 = self.addSubModel(Ball("Ball"))
+        self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
+        self.connectPorts(self.atomic0.outputs["floor"], self.atomic1.input)
+        self.connectPorts(self.atomic0.outputs["elevator"], self.atomic2.input)
+        self.connectPorts(self.atomic1.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["Floor"], self.atomic1.obj_manager_in)
+        self.connectPorts(self.atomic1.outputs["balls"], self.atomic3.input)
+        self.connectPorts(self.atomic1.outputs["parent"], self.atomic0.input)
+        self.connectPorts(self.atomic2.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["Elevator"], self.atomic2.obj_manager_in)
+        self.connectPorts(self.atomic2.outputs["balls"], self.atomic3.input)
+        self.connectPorts(self.atomic2.outputs["parent"], self.atomic0.input)
+        self.connectPorts(self.atomic3.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["Ball"], self.atomic3.obj_manager_in)
+        self.connectPorts(self.atomic3.outputs["parent"], self.atomic0.input)
+        self.connectPorts(self.atomic0.output, self.out_ui)
+        self.connectPorts(self.atomic1.output, self.out_ui)
+        self.connectPorts(self.atomic2.output, self.out_ui)
+        self.connectPorts(self.atomic3.output, self.out_ui)

+ 2 - 4
examples/ElevatorBalls/Python/runner.py

@@ -19,8 +19,6 @@ if __name__ == '__main__':
 	ui = UI(tkroot, controller)
 	controller.addMyOwnOutputListener(OutputListener(ui))
 
-	controller.setVerbose(None)
-	
+
 	controller.start()
-	tkroot.mainloop()
-	
+	tkroot.mainloop()

+ 234 - 104
examples/ElevatorBalls/Python/target.py

@@ -7,12 +7,14 @@ Model name:   Elevator Balls
 """
 
 from sccd.runtime.statecharts_core import *
-from sccd.runtime.libs.ui import ui
+import sccd.runtime.libs.ui_v2 as ui
 import random
 import time
 
-CANVAS_WIDTH = 800
-CANVAS_HEIGHT = 550
+CANVAS_DIMS = (800, 550)
+FLOOR_LENGTH = 350
+FLOOR_SPACE = 50
+FLOORS = 4
 
 # package "Elevator Balls"
 
@@ -34,13 +36,14 @@ class MainApp(RuntimeClassBase):
         # user defined attributes
         self.window_id = None
         self.canvas_id = None
+        self.num_floors = None
         self.floor_dimensions = None
         
         # call user defined constructor
         MainApp.user_defined_constructor(self)
     
     def user_defined_constructor(self):
-        pass
+        self.num_floors = 0
     
     def user_defined_destructor(self):
         pass
@@ -60,18 +63,26 @@ class MainApp(RuntimeClassBase):
         self.states["/creating_canvas"] = State(2, "/creating_canvas", self)
         self.states["/creating_canvas"].setEnter(self._creating_canvas_enter)
         
+        # state /create_floors
+        self.states["/create_floors"] = State(3, "/create_floors", self)
+        
+        # state /wait
+        self.states["/wait"] = State(4, "/wait", self)
+        
         # state /create_elevator
-        self.states["/create_elevator"] = State(3, "/create_elevator", self)
+        self.states["/create_elevator"] = State(5, "/create_elevator", self)
         
         # state /creating
-        self.states["/creating"] = State(4, "/creating", self)
+        self.states["/creating"] = State(6, "/creating", self)
         
         # state /waiting
-        self.states["/waiting"] = State(5, "/waiting", self)
+        self.states["/waiting"] = State(7, "/waiting", self)
         
         # add children
         self.states[""].addChild(self.states["/creating_window"])
         self.states[""].addChild(self.states["/creating_canvas"])
+        self.states[""].addChild(self.states["/create_floors"])
+        self.states[""].addChild(self.states["/wait"])
         self.states[""].addChild(self.states["/create_elevator"])
         self.states[""].addChild(self.states["/creating"])
         self.states[""].addChild(self.states["/waiting"])
@@ -85,11 +96,31 @@ class MainApp(RuntimeClassBase):
         self.states["/creating_window"].addTransition(_creating_window_0)
         
         # transition /creating_canvas
-        _creating_canvas_0 = Transition(self, self.states["/creating_canvas"], [self.states["/create_elevator"]])
+        _creating_canvas_0 = Transition(self, self.states["/creating_canvas"], [self.states["/create_floors"]])
         _creating_canvas_0.setAction(self._creating_canvas_0_exec)
         _creating_canvas_0.setTrigger(Event("canvas_created", None))
         self.states["/creating_canvas"].addTransition(_creating_canvas_0)
         
+        # transition /create_floors
+        _create_floors_0 = Transition(self, self.states["/create_floors"], [self.states["/create_elevator"]])
+        _create_floors_0.setTrigger(None)
+        _create_floors_0.setGuard(self._create_floors_0_guard)
+        self.states["/create_floors"].addTransition(_create_floors_0)
+        _create_floors_1 = Transition(self, self.states["/create_floors"], [self.states["/wait"]])
+        _create_floors_1.setAction(self._create_floors_1_exec)
+        _create_floors_1.setTrigger(None)
+        _create_floors_1.setGuard(self._create_floors_1_guard)
+        self.states["/create_floors"].addTransition(_create_floors_1)
+        
+        # transition /wait
+        _wait_0 = Transition(self, self.states["/wait"], [self.states["/wait"]])
+        _wait_0.setAction(self._wait_0_exec)
+        _wait_0.setTrigger(Event("instance_created", None))
+        self.states["/wait"].addTransition(_wait_0)
+        _wait_1 = Transition(self, self.states["/wait"], [self.states["/create_floors"]])
+        _wait_1.setTrigger(Event("instance_started", None))
+        self.states["/wait"].addTransition(_wait_1)
+        
         # transition /create_elevator
         _create_elevator_0 = Transition(self, self.states["/create_elevator"], [self.states["/creating"]])
         _create_elevator_0.setAction(self._create_elevator_0_exec)
@@ -103,10 +134,10 @@ class MainApp(RuntimeClassBase):
         self.states["/creating"].addTransition(_creating_0)
     
     def _creating_window_enter(self):
-        self.big_step.outputEvent(Event("create_window", self.getOutPortName("ui"), [CANVAS_WIDTH, CANVAS_HEIGHT, "Bouncing Balls Elevator", self.inports['field_ui']]))
+        self.big_step.outputEvent(Event("create_window", self.getOutPortName("ui"), [CANVAS_DIMS[0], CANVAS_DIMS[1], "Bouncing Balls Elevator", self.inports['field_ui']]))
     
     def _creating_canvas_enter(self):
-        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, CANVAS_WIDTH, CANVAS_HEIGHT - 200, {'background':'#fff'}, self.inports['field_ui']]))
+        self.big_step.outputEvent(Event("create_canvas", self.getOutPortName("ui"), [self.window_id, CANVAS_DIMS[0], CANVAS_DIMS[1] - 200, {'background':'#fff'}, self.inports['field_ui']]))
     
     def _creating_window_0_exec(self, parameters):
         window_id = parameters[0]
@@ -118,6 +149,20 @@ class MainApp(RuntimeClassBase):
         canvas_id = parameters[0]
         self.canvas_id = canvas_id
     
+    def _create_floors_0_guard(self, parameters):
+        return self.num_floors == FLOORS
+    
+    def _create_floors_1_exec(self, parameters):
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "floor", "Floor", self.canvas_id, self.num_floors]))
+        self.num_floors += 1
+    
+    def _create_floors_1_guard(self, parameters):
+        return self.num_floors != FLOORS
+    
+    def _wait_0_exec(self, parameters):
+        association_name = parameters[0]
+        self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
+    
     def _create_elevator_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("create_instance", None, [self, "elevator", "Elevator", self.canvas_id]))
     
@@ -131,6 +176,111 @@ class MainApp(RuntimeClassBase):
         self.default_targets = self.states["/creating_window"].getEffectiveTargetStates()
         RuntimeClassBase.initializeStatechart(self)
 
+class Floor(RuntimeClassBase):
+    def __init__(self, controller, canvas_id, floor_num):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["floor_ui"] = controller.addInputPort("floor_ui", self)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # user defined attributes
+        self.window_id = None
+        self.canvas_id = None
+        self.elevator_id = None
+        
+        # call user defined constructor
+        Floor.user_defined_constructor(self, canvas_id, floor_num)
+    
+    def user_defined_constructor(self, canvas_id, floor_num):
+        self.canvas_id = canvas_id;
+        self.floor_num = floor_num;
+        
+        y_dim = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+        
+        self.dim = {'x': FLOOR_LENGTH, 'y': y_dim};
+        self.pos = {'x': FLOOR_LENGTH / 2, 'y': (y_dim /2) + (self.floor_num * (y_dim + FLOOR_SPACE))};
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /creating_floor
+        self.states["/creating_floor"] = State(1, "/creating_floor", self)
+        self.states["/creating_floor"].setEnter(self._creating_floor_enter)
+        
+        # state /running
+        self.states["/running"] = State(2, "/running", self)
+        
+        # state /running/create_random_ball
+        self.states["/running/create_random_ball"] = State(3, "/running/create_random_ball", self)
+        self.states["/running/create_random_ball"].setEnter(self._running_create_random_ball_enter)
+        self.states["/running/create_random_ball"].setExit(self._running_create_random_ball_exit)
+        
+        # state /running/wait
+        self.states["/running/wait"] = State(4, "/running/wait", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_floor"])
+        self.states[""].addChild(self.states["/running"])
+        self.states["/running"].addChild(self.states["/running/create_random_ball"])
+        self.states["/running"].addChild(self.states["/running/wait"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/creating_floor"]
+        self.states["/running"].default_state = self.states["/running/create_random_ball"]
+        
+        # transition /creating_floor
+        _creating_floor_0 = Transition(self, self.states["/creating_floor"], [self.states["/running"]])
+        _creating_floor_0.setTrigger(Event("rectangle_created", None))
+        self.states["/creating_floor"].addTransition(_creating_floor_0)
+        
+        # transition /running/create_random_ball
+        _running_create_random_ball_0 = Transition(self, self.states["/running/create_random_ball"], [self.states["/running/wait"]])
+        _running_create_random_ball_0.setAction(self._running_create_random_ball_0_exec)
+        _running_create_random_ball_0.setTrigger(Event("_0after"))
+        self.states["/running/create_random_ball"].addTransition(_running_create_random_ball_0)
+        
+        # transition /running/wait
+        _running_wait_0 = Transition(self, self.states["/running/wait"], [self.states["/running/create_random_ball"]])
+        _running_wait_0.setAction(self._running_wait_0_exec)
+        _running_wait_0.setTrigger(Event("instance_created", None))
+        self.states["/running/wait"].addTransition(_running_wait_0)
+    
+    def _creating_floor_enter(self):
+        self.big_step.outputEvent(Event("create_rectangle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.dim['x'], self.dim['y'], {'fill':'white', 'outline': 'black'}, self.inports['floor_ui']]))
+    
+    def _running_create_random_ball_enter(self):
+        self.addTimer(0, random.randint(2, 10))
+    
+    def _running_create_random_ball_exit(self):
+        self.removeTimer(0)
+    
+    def _running_create_random_ball_0_exec(self, parameters):
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas_id, self.floor_num, 10, self.pos['y']]))
+    
+    def _running_wait_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])]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/creating_floor"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
 class Elevator(RuntimeClassBase):
     def __init__(self, controller, canvas_id):
         RuntimeClassBase.__init__(self, controller)
@@ -164,9 +314,11 @@ class Elevator(RuntimeClassBase):
         self.open_button = None;
         self.is_open = False;
         
-        self.dim = {'x': 80, 'y': 150};
+        elevator_height = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+        
+        self.dim = {'x': elevator_height, 'y': elevator_height};
         self.vel = {'x': 0, 'y': -2};
-        self.pos = {'x': 400, 'y': 75};
+        self.pos = {'x': FLOOR_LENGTH + (elevator_height / 2), 'y': 75};
         self.smooth = 0.6; # value between 0 and 1
     
     def user_defined_destructor(self):
@@ -189,23 +341,8 @@ class Elevator(RuntimeClassBase):
         self.states["/root/creating_elevator"] = State(3, "/root/creating_elevator", self)
         self.states["/root/creating_elevator"].setEnter(self._root_creating_elevator_enter)
         
-        # state /root/create_elevator_controls
-        self.states["/root/create_elevator_controls"] = State(4, "/root/create_elevator_controls", self)
-        
-        # state /root/create_elevator_controls/create_up
-        self.states["/root/create_elevator_controls/create_up"] = State(5, "/root/create_elevator_controls/create_up", self)
-        self.states["/root/create_elevator_controls/create_up"].setEnter(self._root_create_elevator_controls_create_up_enter)
-        
-        # state /root/create_elevator_controls/create_down
-        self.states["/root/create_elevator_controls/create_down"] = State(6, "/root/create_elevator_controls/create_down", self)
-        self.states["/root/create_elevator_controls/create_down"].setEnter(self._root_create_elevator_controls_create_down_enter)
-        
-        # state /root/create_elevator_controls/create_open
-        self.states["/root/create_elevator_controls/create_open"] = State(7, "/root/create_elevator_controls/create_open", self)
-        self.states["/root/create_elevator_controls/create_open"].setEnter(self._root_create_elevator_controls_create_open_enter)
-        
         # state /root/running
-        self.states["/root/running"] = State(8, "/root/running", self)
+        self.states["/root/running"] = State(4, "/root/running", self)
         self.states["/root/running"].setEnter(self._root_running_enter)
         self.states["/root/running"].setExit(self._root_running_exit)
         
@@ -213,15 +350,10 @@ class Elevator(RuntimeClassBase):
         self.states[""].addChild(self.states["/root"])
         self.states["/root"].addChild(self.states["/root/waiting"])
         self.states["/root"].addChild(self.states["/root/creating_elevator"])
-        self.states["/root"].addChild(self.states["/root/create_elevator_controls"])
         self.states["/root"].addChild(self.states["/root/running"])
-        self.states["/root/create_elevator_controls"].addChild(self.states["/root/create_elevator_controls/create_up"])
-        self.states["/root/create_elevator_controls"].addChild(self.states["/root/create_elevator_controls/create_down"])
-        self.states["/root/create_elevator_controls"].addChild(self.states["/root/create_elevator_controls/create_open"])
         self.states[""].fixTree()
         self.states[""].default_state = self.states["/root"]
         self.states["/root"].default_state = self.states["/root/waiting"]
-        self.states["/root/create_elevator_controls"].default_state = self.states["/root/create_elevator_controls/create_up"]
         
         # transition /root/waiting
         _root_waiting_0 = Transition(self, self.states["/root/waiting"], [self.states["/root/creating_elevator"]])
@@ -230,29 +362,11 @@ class Elevator(RuntimeClassBase):
         self.states["/root/waiting"].addTransition(_root_waiting_0)
         
         # transition /root/creating_elevator
-        _root_creating_elevator_0 = Transition(self, self.states["/root/creating_elevator"], [self.states["/root/create_elevator_controls"]])
+        _root_creating_elevator_0 = Transition(self, self.states["/root/creating_elevator"], [self.states["/root/running"]])
         _root_creating_elevator_0.setAction(self._root_creating_elevator_0_exec)
         _root_creating_elevator_0.setTrigger(Event("rectangle_created", None))
         self.states["/root/creating_elevator"].addTransition(_root_creating_elevator_0)
         
-        # transition /root/create_elevator_controls/create_up
-        _root_create_elevator_controls_create_up_0 = Transition(self, self.states["/root/create_elevator_controls/create_up"], [self.states["/root/create_elevator_controls/create_down"]])
-        _root_create_elevator_controls_create_up_0.setAction(self._root_create_elevator_controls_create_up_0_exec)
-        _root_create_elevator_controls_create_up_0.setTrigger(Event("button_created", None))
-        self.states["/root/create_elevator_controls/create_up"].addTransition(_root_create_elevator_controls_create_up_0)
-        
-        # transition /root/create_elevator_controls/create_down
-        _root_create_elevator_controls_create_down_0 = Transition(self, self.states["/root/create_elevator_controls/create_down"], [self.states["/root/create_elevator_controls/create_open"]])
-        _root_create_elevator_controls_create_down_0.setAction(self._root_create_elevator_controls_create_down_0_exec)
-        _root_create_elevator_controls_create_down_0.setTrigger(Event("button_created", None))
-        self.states["/root/create_elevator_controls/create_down"].addTransition(_root_create_elevator_controls_create_down_0)
-        
-        # transition /root/create_elevator_controls/create_open
-        _root_create_elevator_controls_create_open_0 = Transition(self, self.states["/root/create_elevator_controls/create_open"], [self.states["/root/running"]])
-        _root_create_elevator_controls_create_open_0.setAction(self._root_create_elevator_controls_create_open_0_exec)
-        _root_create_elevator_controls_create_open_0.setTrigger(Event("button_created", None))
-        self.states["/root/create_elevator_controls/create_open"].addTransition(_root_create_elevator_controls_create_open_0)
-        
         # transition /root/running
         _root_running_0 = Transition(self, self.states["/root/running"], [self.states["/root/running"]])
         _root_running_0.setAction(self._root_running_0_exec)
@@ -270,15 +384,6 @@ class Elevator(RuntimeClassBase):
     def _root_creating_elevator_enter(self):
         self.big_step.outputEvent(Event("create_rectangle", self.getOutPortName("ui"), [self.canvas_id, self.pos['x'], self.pos['y'], self.dim['x'], self.dim['y'], {'fill':'white', 'outline': 'black'}, self.inports['elevator_ui']]))
     
-    def _root_create_elevator_controls_create_up_enter(self):
-        self.big_step.outputEvent(Event("create_button", self.getOutPortName("ui"), [self.window_id, 'START', self.inports['elevator_ui']]))
-    
-    def _root_create_elevator_controls_create_down_enter(self):
-        self.big_step.outputEvent(Event("create_button", self.getOutPortName("ui"), [self.window_id, 'STOP', self.inports['elevator_ui']]))
-    
-    def _root_create_elevator_controls_create_open_enter(self):
-        self.big_step.outputEvent(Event("create_button", self.getOutPortName("ui"), [self.window_id, 'OPEN', self.inports['elevator_ui']]))
-    
     def _root_running_enter(self):
         self.addTimer(0, 0.02)
     
@@ -299,21 +404,9 @@ class Elevator(RuntimeClassBase):
         self.elevator_id = rect_id
         self.big_step.outputEvent(Event("bind_event", self.getOutPortName("ui"), [canvas_id, ui.EVENTS.MOUSE_RIGHT_CLICK, 'right_click', self.inports['elevator_ui']]))
     
-    def _root_create_elevator_controls_create_up_0_exec(self, parameters):
-        button_id = parameters[0]
-        self.button_ids['up'] = button_id
-    
-    def _root_create_elevator_controls_create_down_0_exec(self, parameters):
-        button_id = parameters[0]
-        self.button_ids['down'] = button_id
-    
-    def _root_create_elevator_controls_create_open_0_exec(self, parameters):
-        button_id = parameters[0]
-        self.button_ids['open'] = button_id
-    
     def _root_running_0_exec(self, parameters):
         # Invert velocity when colliding with canvas border:
-        if self.pos['y']-(self.dim['y']/2) <= 0 or self.pos['y']+(self.dim['y']/2) >= CANVAS_HEIGHT:
+        if self.pos['y']-(self.dim['y']/2) <= 0 or self.pos['y']+(self.dim['y']/2) >= CANVAS_DIMS[1]:
             self.vel['y'] = -self.vel['y'];
         self.big_step.outputEvent(Event("move_element", self.getOutPortName("ui"), [self.canvas_id, self.elevator_id, self.vel['x'], self.vel['y']]))
         self.pos['x'] += self.vel['x']
@@ -324,7 +417,7 @@ class Elevator(RuntimeClassBase):
         x = parameters[0]
         y = parameters[1]
         button = parameters[2]
-        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas_id, x, y]))
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", self.canvas_id, -1, x, y]))
     
     def _root_running_2_exec(self, parameters):
         association_name = parameters[0]
@@ -337,7 +430,7 @@ class Elevator(RuntimeClassBase):
         RuntimeClassBase.initializeStatechart(self)
 
 class Ball(RuntimeClassBase):
-    def __init__(self, controller, canvas_id, x, y):
+    def __init__(self, controller, canvas_id, floor_num, x, y):
         RuntimeClassBase.__init__(self, controller)
         
         self.inports["ball_ui"] = controller.addInputPort("ball_ui", self)
@@ -356,10 +449,11 @@ class Ball(RuntimeClassBase):
         self.pos = None
         
         # call user defined constructor
-        Ball.user_defined_constructor(self, canvas_id, x, y)
+        Ball.user_defined_constructor(self, canvas_id, floor_num, x, y)
     
-    def user_defined_constructor(self, canvas_id, x, y):
+    def user_defined_constructor(self, canvas_id, floor_num, x, y):
         self.canvas_id = canvas_id;
+        self.floor_num = floor_num;
         self.r = 5.0;
         self.vel = {'x': random.uniform(-5.0, 5.0), 'y': random.uniform(-5.0, 5.0)};
         self.pos = {'x': x, 'y': y};
@@ -428,15 +522,20 @@ class Ball(RuntimeClassBase):
         _main_behaviour_bouncing_0.setAction(self._main_behaviour_bouncing_0_exec)
         _main_behaviour_bouncing_0.setTrigger(Event("_0after"))
         self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_0)
-        _main_behaviour_bouncing_1 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/selected"]])
+        _main_behaviour_bouncing_1 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/bouncing"]])
         _main_behaviour_bouncing_1.setAction(self._main_behaviour_bouncing_1_exec)
-        _main_behaviour_bouncing_1.setTrigger(Event("mouse_press", self.getInPortName("ball_ui")))
+        _main_behaviour_bouncing_1.setTrigger(None)
         _main_behaviour_bouncing_1.setGuard(self._main_behaviour_bouncing_1_guard)
         self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_1)
-        _main_behaviour_bouncing_2 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_bouncing_2 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/selected"]])
         _main_behaviour_bouncing_2.setAction(self._main_behaviour_bouncing_2_exec)
-        _main_behaviour_bouncing_2.setTrigger(Event("update_bounds", None))
+        _main_behaviour_bouncing_2.setTrigger(Event("mouse_press", self.getInPortName("ball_ui")))
+        _main_behaviour_bouncing_2.setGuard(self._main_behaviour_bouncing_2_guard)
         self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_2)
+        _main_behaviour_bouncing_3 = Transition(self, self.states["/main_behaviour/bouncing"], [self.states["/main_behaviour/bouncing"]])
+        _main_behaviour_bouncing_3.setAction(self._main_behaviour_bouncing_3_exec)
+        _main_behaviour_bouncing_3.setTrigger(Event("update_bounds", None))
+        self.states["/main_behaviour/bouncing"].addTransition(_main_behaviour_bouncing_3)
         
         # transition /main_behaviour/dragging
         _main_behaviour_dragging_0 = Transition(self, self.states["/main_behaviour/dragging"], [self.states["/main_behaviour/dragging"]])
@@ -481,38 +580,63 @@ class Ball(RuntimeClassBase):
         self.big_step.outputEvent(Event("bind_canvas_event", self.getOutPortName("ui"), [self.canvas_id, circle_id, ui.EVENTS.MOUSE_RELEASE, 'mouse_release', self.inports['ball_ui']]))
     
     def _main_behaviour_bouncing_0_exec(self, parameters):
-        # Check collision with the left and right borders
-        if self.pos['x'] - self.r < self.rect_pos['x'] - (self.rect_dim['x'] / 2):
-            self.pos['x'] = self.rect_pos['x'] - (self.rect_dim['x'] / 2) + self.r  # Correct position
-            self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
-        elif self.pos['x'] + self.r > self.rect_pos['x'] + (self.rect_dim['x'] / 2):
-            self.pos['x'] = self.rect_pos['x'] + (self.rect_dim['x'] / 2) - self.r  # Correct position
-            self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
-        
-        # Check collision with the top and bottom borders
-        if self.pos['y'] - self.r < self.rect_pos['y'] - (self.rect_dim['y'] / 2):
-            self.pos['y'] = self.rect_pos['y'] - (self.rect_dim['y'] / 2) + self.r  # Correct position
-            self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
-        elif self.pos['y'] + self.r > self.rect_pos['y'] + (self.rect_dim['y'] / 2):
-            self.pos['y'] = self.rect_pos['y'] + (self.rect_dim['y'] / 2) - self.r  # Correct position
-            self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+        if self.floor_num == -1:
+            if self.pos['x'] - self.r < self.rect_pos['x'] - (self.rect_dim['x'] / 2):
+                self.pos['x'] = self.rect_pos['x'] - (self.rect_dim['x'] / 2) + self.r  # Correct position
+                self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+            elif self.pos['x'] + self.r > self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+                self.pos['x'] = self.rect_pos['x'] + (self.rect_dim['x'] / 2) - self.r  # Correct position
+                self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+        
+            # Check collision with the top and bottom borders
+            if self.pos['y'] - self.r < self.rect_pos['y'] - (self.rect_dim['y'] / 2):
+                self.pos['y'] = self.rect_pos['y'] - (self.rect_dim['y'] / 2) + self.r  # Correct position
+                self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+            elif self.pos['y'] + self.r > self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+                self.pos['y'] = self.rect_pos['y'] + (self.rect_dim['y'] / 2) - self.r  # Correct position
+                self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+        else:
+            floor_height = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+            floor_dim = {'x': FLOOR_LENGTH, 'y': floor_height};
+            floor_pos = {'x': FLOOR_LENGTH / 2, 'y': (floor_height /2) + (self.floor_num * (floor_height + FLOOR_SPACE))};
+        
+            if self.pos['x'] - self.r < floor_pos['x'] - (floor_dim['x'] / 2):
+                self.pos['x'] = floor_pos['x'] - (floor_dim['x'] / 2) + self.r  # Correct position
+                self.vel['x'] = -self.vel['x']
+            elif self.pos['x'] + self.r > floor_pos['x'] + (floor_dim['x'] / 2):
+                self.pos['x'] = floor_pos['x'] + (floor_dim['x'] / 2) - self.r  # Correct position
+                self.vel['x'] = -self.vel['x']
+        
+            # Check collision with the top and bottom borders
+            if self.pos['y'] - self.r < floor_pos['y'] - (floor_dim['y'] / 2):
+                self.pos['y'] = floor_pos['y'] - (floor_dim['y'] / 2) + self.r  # Correct position
+                self.vel['y'] = -self.vel['y']
+            elif self.pos['y'] + self.r > floor_pos['y'] + (floor_dim['y'] / 2):
+                self.pos['y'] = floor_pos['y'] + (floor_dim['y'] / 2) - self.r  # Correct position
+                self.vel['y'] = -self.vel['y']
         self.big_step.outputEvent(Event("move_element", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, self.vel['x'], self.vel['y']]))
         self.pos['x'] += self.vel['x']
         self.pos['y'] += self.vel['y']
     
     def _main_behaviour_bouncing_1_exec(self, parameters):
+        self.big_step.outputEvent(Event("destroy_element", self.getOutPortName("ui"), [self.canvas_id, self.element_id]))
+    
+    def _main_behaviour_bouncing_1_guard(self, parameters):
+        return self.pos['x'] - self.r == 6
+    
+    def _main_behaviour_bouncing_2_exec(self, parameters):
         x = parameters[0]
         y = parameters[1]
         button = parameters[2]
         self.big_step.outputEvent(Event("set_element_color", self.getOutPortName("ui"), [self.canvas_id, self.circle_id, '#ff0']))
     
-    def _main_behaviour_bouncing_1_guard(self, parameters):
+    def _main_behaviour_bouncing_2_guard(self, parameters):
         x = parameters[0]
         y = parameters[1]
         button = parameters[2]
         return button == ui.MOUSE_BUTTONS.LEFT
     
-    def _main_behaviour_bouncing_2_exec(self, parameters):
+    def _main_behaviour_bouncing_3_exec(self, parameters):
         pos = parameters[0]
         dim = parameters[1]
         vel = parameters[2]
@@ -525,8 +649,8 @@ class Ball(RuntimeClassBase):
         y = parameters[1]
         button = parameters[2]
         # Always keep ball within canvas:
-        x = min(max(0+self.r, x), CANVAS_WIDTH-self.r)
-        y = min(max(0+self.r, y), CANVAS_HEIGHT-self.r)
+        x = min(max(0+self.r, x), CANVAS_DIMS[0]-self.r)
+        y = min(max(0+self.r, y), CANVAS_DIMS[1]-self.r)
         
         dx = x - self.pos['x']
         dy = y - self.pos['y']
@@ -573,16 +697,22 @@ class ObjectManager(ObjectManagerBase):
         if class_name == "MainApp":
             instance = MainApp(self.controller)
             instance.associations = {}
-            instance.associations["elevator"] = Association("Elevator", 0, -1)
+            instance.associations["floor"] = Association("Floor", 2, -1)
+            instance.associations["elevator"] = Association("Elevator", 1, 1)
+        elif class_name == "Floor":
+            instance = Floor(self.controller, construct_params[0], construct_params[1])
+            instance.associations = {}
+            instance.associations["balls"] = Association("Ball", 0, -1)
+            instance.associations["parent"] = Association("MainApp", 1, 1)
         elif class_name == "Elevator":
             instance = Elevator(self.controller, construct_params[0])
             instance.associations = {}
             instance.associations["balls"] = Association("Ball", 0, -1)
             instance.associations["parent"] = Association("MainApp", 1, 1)
         elif class_name == "Ball":
-            instance = Ball(self.controller, construct_params[0], construct_params[1], construct_params[2])
+            instance = Ball(self.controller, construct_params[0], construct_params[1], construct_params[2], construct_params[3])
             instance.associations = {}
-            instance.associations["parent"] = Association("Field", 1, 1)
+            instance.associations["parent"] = Association("MainApp", 1, 1)
         else:
             raise Exception("Cannot instantiate class " + class_name)
         return instance

+ 164 - 77
examples/ElevatorBalls/sccd.xml

@@ -1,30 +1,39 @@
 <?xml version="1.0" ?>
 <diagram author="Sam Pieters" name="Elevator Balls">
     <top>
-        from sccd.runtime.libs.ui import ui
+        import sccd.runtime.libs.ui_v2 as ui
         import random
         import time
 
-        CANVAS_WIDTH = 800
-        CANVAS_HEIGHT = 550
+        CANVAS_DIMS = (800, 550)
+        FLOOR_LENGTH = 350
+        FLOOR_SPACE = 50
+        FLOORS = 4
     </top>
     <inport name="ui" />
     <outport name="ui" />
     <class name="MainApp" default="true">
         <relationships>
-            <association name="elevator" class="Elevator" />
+            <association name="floor" class="Floor" min="2"/>
+            <association name="elevator" class="Elevator" min="1" max="1"/>
         </relationships>
         <attribute name="window_id" />
         <attribute name="canvas_id" />
+        <attribute name="num_floors" />
         <attribute name="floor_dimensions" />
         <atrribute name="floor_height" />
         <inport name="field_ui"/>
+        <constructor>
+            <body>
+                self.num_floors = 0
+            </body>
+        </constructor>
         <scxml initial="creating_window">
             <state id="creating_window">
                 <onentry>
                     <raise port="ui" event="create_window">
-                        <parameter expr="CANVAS_WIDTH"/><!-- width -->
-                        <parameter expr="CANVAS_HEIGHT"/><!-- height -->
+                        <parameter expr="CANVAS_DIMS[0]"/><!-- width -->
+                        <parameter expr="CANVAS_DIMS[1]"/><!-- height -->
                         <parameter expr='"Bouncing Balls Elevator"'/><!-- title -->
                         <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
                     </raise>
@@ -52,19 +61,43 @@
                 <onentry>
                     <raise port="ui" event="create_canvas">
                         <parameter expr="self.window_id"/><!-- window_id -->
-                        <parameter expr="CANVAS_WIDTH"/><!-- width -->
-                        <parameter expr="CANVAS_HEIGHT - 200"/><!-- height -->
+                        <parameter expr="CANVAS_DIMS[0]"/><!-- width -->
+                        <parameter expr="CANVAS_DIMS[1] - 200"/><!-- height -->
                         <parameter expr="{'background':'#fff'}"/><!-- style -->
                         <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
                     </raise>
                 </onentry>
-                <transition event="canvas_created" target="../create_elevator">
+                <transition event="canvas_created" target="../create_floors">
                     <parameter name="canvas_id" type="int"/>
                     <script>
                         self.canvas_id = canvas_id
                     </script>
                 </transition>
             </state>
+            <state id="create_floors">
+                <transition target="../create_elevator" cond="self.num_floors == FLOORS">
+                </transition>
+                <transition target="../wait" cond="self.num_floors != FLOORS">
+                    <raise scope="cd" event="create_instance">
+                        <parameter expr='"floor"' />
+                        <parameter expr='"Floor"' />
+                        <parameter expr="self.canvas_id" />
+                        <parameter expr="self.num_floors" />
+                    </raise>
+                    <script>
+                        self.num_floors += 1
+                    </script>
+                </transition>
+            </state>
+            <state id="wait">
+                <transition event='instance_created' target='.'>
+                    <parameter name="association_name" type="string"/>
+                    <raise scope="cd" event="start_instance">
+                        <parameter expr="association_name" />
+                    </raise>
+                </transition>
+                <transition event="instance_started" target="../create_floors" />
+            </state>
             <state id="create_elevator">
                 <transition target="../creating">
                     <raise scope="cd" event="create_instance">
@@ -92,6 +125,75 @@
         </scxml>
     </class>
 
+    <class name="Floor">
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="elevator_id" />
+        <atrribute name="pos" />
+        <inport name="floor_ui"/>
+        <relationships>
+            <association name="balls" class="Ball" />
+            <association name="parent" class="MainApp" min="1" max="1" />
+        </relationships>
+        <constructor>
+            <parameter name="canvas_id" />
+            <parameter name="floor_num" />
+            <body>
+                <![CDATA[
+                self.canvas_id = canvas_id;
+                self.floor_num = floor_num;
+
+                y_dim = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+
+                self.dim = {'x': FLOOR_LENGTH, 'y': y_dim};
+                self.pos = {'x': FLOOR_LENGTH / 2, 'y': (y_dim /2) + (self.floor_num * (y_dim + FLOOR_SPACE))};
+                ]]>
+            </body>
+        </constructor>
+        <scxml initial="creating_floor">
+            <state id="creating_floor">
+                <onentry>
+                    <raise port="ui" event="create_rectangle">
+                        <parameter expr="self.canvas_id" />
+                        <parameter expr="self.pos['x']"/>
+                        <parameter expr="self.pos['y']"/>
+                        <parameter expr="self.dim['x']" />
+                        <parameter expr="self.dim['y']"/>
+                        <parameter expr="{'fill':'white', 'outline': 'black'}"/><!-- style -->
+                        <parameter expr="self.inports['floor_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="rectangle_created" target="../running">
+                </transition>
+            </state>
+            <state id="running" initial="create_random_ball">
+                <state id="create_random_ball" >
+                    <transition after="random.randint(2, 10)" target="../wait">
+                        <raise scope="cd" event="create_instance">
+                            <parameter expr='"balls"' />
+                            <parameter expr='"Ball"' />
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="self.floor_num" />
+                            <parameter expr="10" />
+                            <parameter expr="self.pos['y']" />
+                        </raise>
+                    </transition>
+                </state>
+                <state id="wait">
+                    <transition event="instance_created" target="../create_random_ball">
+                        <parameter name="association_name" type="string"/>
+                        <raise scope="cd" event="start_instance">
+                            <parameter expr="association_name" />
+                        </raise>
+                        <raise scope="narrow" event="set_association_name" target="association_name">
+                            <parameter expr="association_name" />
+                        </raise>
+                    </transition>
+                </state>
+            </state>
+        </scxml>
+    </class>
+
     <class name="Elevator">
         <attribute name="window_id" />
         <attribute name="canvas_id" />
@@ -115,9 +217,11 @@
                 self.open_button = None;
                 self.is_open = False;
 
-                self.dim = {'x': 80, 'y': 150};
+                elevator_height = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+
+                self.dim = {'x': elevator_height, 'y': elevator_height};
                 self.vel = {'x': 0, 'y': -2};
-                self.pos = {'x': 400, 'y': 75};
+                self.pos = {'x': FLOOR_LENGTH + (elevator_height / 2), 'y': 75};
                 self.smooth = 0.6; # value between 0 and 1
                 ]]>
             </body>
@@ -148,7 +252,7 @@
                             <parameter expr="self.inports['elevator_ui']"/><!-- inport for response -->
                         </raise>
                     </onentry>
-                    <transition event="rectangle_created" target="../create_elevator_controls">
+                    <transition event="rectangle_created" target="../running">
                         <parameter name="canvas_id" type="int" />
                         <parameter name="rect_id" type="int" />
                         <script>
@@ -162,59 +266,12 @@
                         </raise>
                     </transition>
                 </state>
-                <state id="create_elevator_controls" initial="create_up">
-                    <state id="create_up">
-                        <onentry>
-                            <raise port="ui" event="create_button">
-                                <parameter expr="self.window_id"/>
-                                <parameter expr="'START'"/>
-                                <parameter expr="self.inports['elevator_ui']"/>
-                            </raise>
-                        </onentry>
-                        <transition event="button_created" target="../create_down">
-                            <parameter name="button_id" type="int"/>
-                            <script>
-                                self.button_ids['up'] = button_id
-                            </script>
-                        </transition>
-                    </state>
-                    <state id="create_down">
-                        <onentry>
-                            <raise port="ui" event="create_button">
-                                <parameter expr="self.window_id"/>
-                                <parameter expr="'STOP'"/>
-                                <parameter expr="self.inports['elevator_ui']"/>
-                            </raise>
-                        </onentry>
-                        <transition event="button_created" target="../create_open">
-                            <parameter name="button_id" type="int"/>
-                            <script>
-                                self.button_ids['down'] = button_id
-                            </script>
-                        </transition>
-                    </state>
-                    <state id="create_open">
-                        <onentry>
-                            <raise port="ui" event="create_button">
-                                <parameter expr="self.window_id"/>
-                                <parameter expr="'OPEN'"/>
-                                <parameter expr="self.inports['elevator_ui']"/>
-                            </raise>
-                        </onentry>
-                        <transition event="button_created" target="../../running">
-                            <parameter name="button_id" type="int"/>
-                            <script>
-                                self.button_ids['open'] = button_id
-                            </script>
-                        </transition>
-                    </state>
-                </state>
                 <state id="running">
                     <transition after="0.02" target=".">
                         <script>
                             <![CDATA[
                             # Invert velocity when colliding with canvas border:
-                            if self.pos['y']-(self.dim['y']/2) <= 0 or self.pos['y']+(self.dim['y']/2) >= CANVAS_HEIGHT:
+                            if self.pos['y']-(self.dim['y']/2) <= 0 or self.pos['y']+(self.dim['y']/2) >= CANVAS_DIMS[1]:
                                 self.vel['y'] = -self.vel['y'];
                             ]]>
                         </script>
@@ -242,6 +299,7 @@
                             <parameter expr='"balls"' />
                             <parameter expr='"Ball"' />
                             <parameter expr="self.canvas_id" />
+                            <parameter expr="-1" />
                             <parameter expr="x" />
                             <parameter expr="y" />
                         </raise>
@@ -266,15 +324,17 @@
         <attribute name="pos" />
         <inport name="ball_ui" />
         <relationships>
-            <association name="parent" class="Field" min="1" max="1" />
+            <association name="parent" class="MainApp" min="1" max="1" />
         </relationships>
         <constructor>
             <parameter name="canvas_id" />
+            <parameter name="floor_num" />
             <parameter name="x" />
             <parameter name="y" />
             <body>
                 <![CDATA[
                 self.canvas_id = canvas_id;
+                self.floor_num = floor_num;
                 self.r = 5.0;
                 self.vel = {'x': random.uniform(-5.0, 5.0), 'y': random.uniform(-5.0, 5.0)};
                 self.pos = {'x': x, 'y': y};
@@ -339,21 +399,41 @@
                     <transition after="0.02" target=".">
                         <script>
                             <![CDATA[
-                            # Check collision with the left and right borders
-                            if self.pos['x'] - self.r < self.rect_pos['x'] - (self.rect_dim['x'] / 2):
-                                self.pos['x'] = self.rect_pos['x'] - (self.rect_dim['x'] / 2) + self.r  # Correct position
-                                self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
-                            elif self.pos['x'] + self.r > self.rect_pos['x'] + (self.rect_dim['x'] / 2):
-                                self.pos['x'] = self.rect_pos['x'] + (self.rect_dim['x'] / 2) - self.r  # Correct position
-                                self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+                            if self.floor_num == -1:
+                                if self.pos['x'] - self.r < self.rect_pos['x'] - (self.rect_dim['x'] / 2):
+                                    self.pos['x'] = self.rect_pos['x'] - (self.rect_dim['x'] / 2) + self.r  # Correct position
+                                    self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+                                elif self.pos['x'] + self.r > self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+                                    self.pos['x'] = self.rect_pos['x'] + (self.rect_dim['x'] / 2) - self.r  # Correct position
+                                    self.vel['x'] = -self.vel['x'] + self.rect_vel['x']
+
+                                # Check collision with the top and bottom borders
+                                if self.pos['y'] - self.r < self.rect_pos['y'] - (self.rect_dim['y'] / 2):
+                                    self.pos['y'] = self.rect_pos['y'] - (self.rect_dim['y'] / 2) + self.r  # Correct position
+                                    self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+                                elif self.pos['y'] + self.r > self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+                                    self.pos['y'] = self.rect_pos['y'] + (self.rect_dim['y'] / 2) - self.r  # Correct position
+                                    self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
+                            else:
+                                floor_height = (CANVAS_DIMS[1] - ((FLOORS - 1) * FLOOR_SPACE)) / FLOORS
+                                floor_dim = {'x': FLOOR_LENGTH, 'y': floor_height};
+                                floor_pos = {'x': FLOOR_LENGTH / 2, 'y': (floor_height /2) + (self.floor_num * (floor_height + FLOOR_SPACE))};
+
+                                if self.pos['x'] - self.r < floor_pos['x'] - (floor_dim['x'] / 2):
+                                    self.pos['x'] = floor_pos['x'] - (floor_dim['x'] / 2) + self.r  # Correct position
+                                    self.vel['x'] = -self.vel['x']
+                                elif self.pos['x'] + self.r > floor_pos['x'] + (floor_dim['x'] / 2):
+                                    self.pos['x'] = floor_pos['x'] + (floor_dim['x'] / 2) - self.r  # Correct position
+                                    self.vel['x'] = -self.vel['x']
+
+                                # Check collision with the top and bottom borders
+                                if self.pos['y'] - self.r < floor_pos['y'] - (floor_dim['y'] / 2):
+                                    self.pos['y'] = floor_pos['y'] - (floor_dim['y'] / 2) + self.r  # Correct position
+                                    self.vel['y'] = -self.vel['y']
+                                elif self.pos['y'] + self.r > floor_pos['y'] + (floor_dim['y'] / 2):
+                                    self.pos['y'] = floor_pos['y'] + (floor_dim['y'] / 2) - self.r  # Correct position
+                                    self.vel['y'] = -self.vel['y']
 
-                            # Check collision with the top and bottom borders
-                            if self.pos['y'] - self.r < self.rect_pos['y'] - (self.rect_dim['y'] / 2):
-                                self.pos['y'] = self.rect_pos['y'] - (self.rect_dim['y'] / 2) + self.r  # Correct position
-                                self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
-                            elif self.pos['y'] + self.r > self.rect_pos['y'] + (self.rect_dim['y'] / 2):
-                                self.pos['y'] = self.rect_pos['y'] + (self.rect_dim['y'] / 2) - self.r  # Correct position
-                                self.vel['y'] = -self.vel['y'] + self.rect_vel['y']
                             ]]>
                         </script>
                         <raise port="ui" event="move_element">
@@ -367,6 +447,12 @@
                             self.pos['y'] += self.vel['y']
                         </script>
                     </transition>
+                    <transition target="." cond="self.pos['x'] - self.r == 6">
+                        <raise port="ui" event="destroy_element">
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="self.element_id" />
+                        </raise>
+                    </transition>
                     <transition port="ball_ui" event="mouse_press" target="../selected" cond="button == ui.MOUSE_BUTTONS.LEFT">
                         <parameter name="x" />
                         <parameter name="y" />
@@ -396,8 +482,8 @@
                         <script>
                             <![CDATA[
                             # Always keep ball within canvas:
-                            x = min(max(0+self.r, x), CANVAS_WIDTH-self.r)
-                            y = min(max(0+self.r, y), CANVAS_HEIGHT-self.r)
+                            x = min(max(0+self.r, x), CANVAS_DIMS[0]-self.r)
+                            y = min(max(0+self.r, y), CANVAS_DIMS[1]-self.r)
 
                             dx = x - self.pos['x']
                             dy = y - self.pos['y']
@@ -452,4 +538,5 @@
             <state id='deleted' />
         </scxml>
     </class>
+
 </diagram>

+ 1 - 1
sccd/runtime/DEVS_loop.py

@@ -12,7 +12,7 @@ def get_port(text):
 
 
 class DEVSSimulator(Simulator):
-	def __init__(self, model, inputs):
+	def __init__(self, model, inputs={}):
 		super().__init__(model)
 		self.setRealTimePorts(inputs)	
 	

+ 9 - 0
tests/Test1/PyDEVS/runner.py

@@ -0,0 +1,9 @@
+import tests.Test1.PyDEVS.target as target
+from sccd.runtime.DEVS_loop import DEVSSimulator
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	sim = DEVSSimulator(model)
+	sim.setVerbose()
+	sim.simulate()
+

+ 110 - 0
tests/Test1/PyDEVS/target.py

@@ -0,0 +1,110 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration) and Sam Pieters (DEVS)
+
+Model author: Sam Pieters
+Model name:   EndingTest
+Model description:
+Test 1: Check if the program ends on time.
+"""
+
+from sccd.runtime.DEVS_statecharts_core import *
+
+# package "EndingTest"
+
+class MainAppInstance(RuntimeClassBase):
+    def __init__(self, atomdevs):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainAppInstance.user_defined_constructor(self)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /end
+        self.states["/end"] = State(2, "/end", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/end"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class MainApp(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.instances[self.next_instance] = MainAppInstance(self)
+        self.next_instance = self.next_instance + 1
+    
+    def constructObject(self, parameters):
+        new_instance = MainAppInstance(self)
+        return new_instance
+
+class ObjectManagerState:
+    def __init__(self):
+        self.to_send = [("MainApp", "MainApp", Event("start_instance", None, ["MainApp[0]"], 0))]
+
+class ObjectManager(TheObjectManager):
+    def __init__(self, name):
+        TheObjectManager.__init__(self, name)
+        self.State = ObjectManagerState()
+        self.input = self.addInPort("input")
+        self.output["MainApp"] = self.addOutPort()
+
+class Controller(CoupledDEVS):
+    def __init__(self, name):
+        CoupledDEVS.__init__(self, name)
+        self.in_ui = self.addInPort("ui")
+        Ports.addInputPort("ui")
+        self.out_ui = self.addOutPort("ui")
+        Ports.addOutputPort("ui")
+        self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
+        self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
+        self.connectPorts(self.atomic0.output, self.out_ui)

+ 9 - 0
tests/Test1/Python/runner.py

@@ -0,0 +1,9 @@
+import tests.Test1.Python.target as target
+
+if __name__ == '__main__':
+	controller = target.Controller()
+	controller.keep_running = False
+	controller.setVerbose(None)
+	controller.start()
+
+	

+ 93 - 0
tests/Test1/Python/target.py

@@ -0,0 +1,93 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Model author: Sam Pieters
+Model name:   EndingTest
+Model description:
+Test 1: Check if the program ends on time.
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "EndingTest"
+
+class MainApp(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainApp.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /end
+        self.states["/end"] = State(2, "/end", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/end"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "MainApp":
+            instance = MainApp(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("ui")
+        self.addOutputPort("ui")
+        self.object_manager.createInstance("MainApp", [])

+ 17 - 0
tests/Test1/sccd.xml

@@ -0,0 +1,17 @@
+<?xml version="1.1" ?>
+<diagram author="Sam Pieters" name="EndingTest">
+    <description>
+        Test 1: Check if the model ends on time.
+    </description>
+    <inport name="ui"/>
+    <outport name="ui"/>
+    <class name="MainApp" default="true">
+        <scxml initial="state1">
+            <state id="state1">
+                <transition after="1" target="../end" />
+            </state>
+            <state id="end">
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 20 - 0
tests/Test2/PyDEVS/runner.py

@@ -0,0 +1,20 @@
+import tests.Test2.PyDEVS.target as target
+from sccd.runtime.DEVS_loop import DEVSSimulator
+
+class OutputListener:
+	def add(self, events):
+		for event in events:
+			if event.port == "ui":
+				print(event.name, ":", event.parameters[0], "seconds")
+				
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	refs = {"ui": model.in_ui}
+	sim = DEVSSimulator(model, refs)
+	sim.setRealTime(True)
+
+	listener = OutputListener()
+	sim.setListenPorts(model.out_ui, listener.add)
+	sim.simulate()
+

+ 114 - 0
tests/Test2/PyDEVS/target.py

@@ -0,0 +1,114 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration) and Sam Pieters (DEVS)
+
+Model author: Sam Pieters
+Model name:   Output Test
+Model description:
+Test 2: Check if the model outputs one event on the right time.
+"""
+
+from sccd.runtime.DEVS_statecharts_core import *
+
+# package "Output Test"
+
+class MainAppInstance(RuntimeClassBase):
+    def __init__(self, atomdevs):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainAppInstance.user_defined_constructor(self)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /end
+        self.states["/end"] = State(2, "/end", self)
+        self.states["/end"].setEnter(self._end_enter)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/end"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def _end_enter(self):
+        self.big_step.outputEvent(Event("test_event", self.getOutPortName("ui"), [str('%.2f' % (self.getSimulatedTime() / 1000.0))]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class MainApp(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.instances[self.next_instance] = MainAppInstance(self)
+        self.next_instance = self.next_instance + 1
+    
+    def constructObject(self, parameters):
+        new_instance = MainAppInstance(self)
+        return new_instance
+
+class ObjectManagerState:
+    def __init__(self):
+        self.to_send = [("MainApp", "MainApp", Event("start_instance", None, ["MainApp[0]"], 0))]
+
+class ObjectManager(TheObjectManager):
+    def __init__(self, name):
+        TheObjectManager.__init__(self, name)
+        self.State = ObjectManagerState()
+        self.input = self.addInPort("input")
+        self.output["MainApp"] = self.addOutPort()
+
+class Controller(CoupledDEVS):
+    def __init__(self, name):
+        CoupledDEVS.__init__(self, name)
+        self.in_ui = self.addInPort("ui")
+        Ports.addInputPort("ui")
+        self.out_ui = self.addOutPort("ui")
+        Ports.addOutputPort("ui")
+        self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
+        self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
+        self.connectPorts(self.atomic0.output, self.out_ui)

+ 13 - 0
tests/Test2/Python/runner.py

@@ -0,0 +1,13 @@
+import tests.Test2.Python.target as target
+
+class OutputListener:
+	def add(self, event):
+		if event.port == "ui":
+			print(event.name, ":", event.parameters[0], "seconds")
+
+if __name__ == '__main__':
+	controller = target.Controller()
+	controller.keep_running = False
+	controller.addMyOwnOutputListener(OutputListener())
+	controller.setVerbose(None)
+	controller.start()

+ 97 - 0
tests/Test2/Python/target.py

@@ -0,0 +1,97 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Model author: Sam Pieters
+Model name:   Output Test
+Model description:
+Test 2: Check if the model outputs one event on the right time.
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "Output Test"
+
+class MainApp(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainApp.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /end
+        self.states["/end"] = State(2, "/end", self)
+        self.states["/end"].setEnter(self._end_enter)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/end"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def _end_enter(self):
+        self.big_step.outputEvent(Event("test_event", self.getOutPortName("ui"), [str('%.2f' % (self.getSimulatedTime() / 1000.0))]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "MainApp":
+            instance = MainApp(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("ui")
+        self.addOutputPort("ui")
+        self.object_manager.createInstance("MainApp", [])

+ 22 - 0
tests/Test2/sccd.xml

@@ -0,0 +1,22 @@
+<?xml version="1.1" ?>
+<diagram author="Sam Pieters" name="Output Test">
+    <description>
+        Test 2: Check if the model outputs one event on the right time.
+    </description>
+    <inport name="ui"/>
+    <outport name="ui"/>
+    <class name="MainApp" default="true">
+        <scxml initial="state1">
+            <state id="state1">
+                <transition after="1" target="../end" />
+            </state>
+            <state id="end">
+                <onentry>
+                    <raise port="ui" event="test_event">
+                        <parameter expr="str('%.2f' % (self.getSimulatedTime() / 1000.0))" />
+                    </raise>
+                </onentry>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 19 - 0
tests/Test3/PyDEVS/runner.py

@@ -0,0 +1,19 @@
+import tests.Test3.PyDEVS.target as target
+from sccd.runtime.DEVS_loop import DEVSSimulator
+
+class OutputListener:
+	def add(self, events):
+		for event in events:
+			if event.port == "ui":
+				print(event.name, event.parameters[0], ":", event.parameters[1], "seconds")
+				
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	refs = {"ui": model.in_ui}
+	sim = DEVSSimulator(model, refs)
+
+	listener = OutputListener()
+	sim.setListenPorts(model.out_ui, listener.add)
+	sim.simulate()
+

+ 138 - 0
tests/Test3/PyDEVS/target.py

@@ -0,0 +1,138 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration) and Sam Pieters (DEVS)
+
+Model author: Sam Pieters
+Model name:   Output Test
+Model description:
+Test 3: Check if the model outputs multiple events with an interval on the right time.
+"""
+
+from sccd.runtime.DEVS_statecharts_core import *
+
+# package "Output Test"
+
+class MainAppInstance(RuntimeClassBase):
+    def __init__(self, atomdevs):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainAppInstance.user_defined_constructor(self)
+        port_name = Ports.addInputPort("<narrow_cast>", self)
+        atomdevs.addInPort(port_name)
+    
+    def user_defined_constructor(self):
+        self.amount = 1
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /state2
+        self.states["/state2"] = State(2, "/state2", self)
+        self.states["/state2"].setEnter(self._state2_enter)
+        
+        # state /end
+        self.states["/end"] = State(3, "/end", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/state2"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/state2"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+        
+        # transition /state2
+        _state2_0 = Transition(self, self.states["/state2"], [self.states["/state1"]])
+        _state2_0.setAction(self._state2_0_exec)
+        _state2_0.setTrigger(None)
+        _state2_0.setGuard(self._state2_0_guard)
+        self.states["/state2"].addTransition(_state2_0)
+        _state2_1 = Transition(self, self.states["/state2"], [self.states["/end"]])
+        _state2_1.setTrigger(None)
+        _state2_1.setGuard(self._state2_1_guard)
+        self.states["/state2"].addTransition(_state2_1)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def _state2_enter(self):
+        self.big_step.outputEvent(Event("test_event", self.getOutPortName("ui"), [str(self.amount), str('%.2f' % (self.getSimulatedTime() / 1000.0))]))
+    
+    def _state2_0_exec(self, parameters):
+        self.amount += 1
+    
+    def _state2_0_guard(self, parameters):
+        return self.amount != 5
+    
+    def _state2_1_guard(self, parameters):
+        return self.amount == 5
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class MainApp(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        self.output = self.addOutPort("ui")
+        self.instances[self.next_instance] = MainAppInstance(self)
+        self.next_instance = self.next_instance + 1
+    
+    def constructObject(self, parameters):
+        new_instance = MainAppInstance(self)
+        return new_instance
+
+class ObjectManagerState:
+    def __init__(self):
+        self.to_send = [("MainApp", "MainApp", Event("start_instance", None, ["MainApp[0]"], 0))]
+
+class ObjectManager(TheObjectManager):
+    def __init__(self, name):
+        TheObjectManager.__init__(self, name)
+        self.State = ObjectManagerState()
+        self.input = self.addInPort("input")
+        self.output["MainApp"] = self.addOutPort()
+
+class Controller(CoupledDEVS):
+    def __init__(self, name):
+        CoupledDEVS.__init__(self, name)
+        self.in_ui = self.addInPort("ui")
+        Ports.addInputPort("ui")
+        self.out_ui = self.addOutPort("ui")
+        Ports.addOutputPort("ui")
+        self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
+        self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        self.connectPorts(self.atomic0.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["MainApp"], self.atomic0.obj_manager_in)
+        self.connectPorts(self.atomic0.output, self.out_ui)

+ 13 - 0
tests/Test3/Python/runner.py

@@ -0,0 +1,13 @@
+import tests.Test3.Python.target as target
+
+class OutputListener:
+	def add(self, event):
+		if event.port == "ui":
+			print(event.name, event.parameters[0], ":", event.parameters[1], "seconds")
+
+if __name__ == '__main__':
+	controller = target.Controller()
+	controller.keep_running = False
+	controller.addMyOwnOutputListener(OutputListener())
+	controller.setVerbose(None)
+	controller.start()

+ 121 - 0
tests/Test3/Python/target.py

@@ -0,0 +1,121 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Model author: Sam Pieters
+Model name:   Output Test
+Model description:
+Test 3: Check if the model outputs multiple events with an interval on the right time.
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "Output Test"
+
+class MainApp(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        MainApp.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        self.amount = 1
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /state1
+        self.states["/state1"] = State(1, "/state1", self)
+        self.states["/state1"].setEnter(self._state1_enter)
+        self.states["/state1"].setExit(self._state1_exit)
+        
+        # state /state2
+        self.states["/state2"] = State(2, "/state2", self)
+        self.states["/state2"].setEnter(self._state2_enter)
+        
+        # state /end
+        self.states["/end"] = State(3, "/end", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/state1"])
+        self.states[""].addChild(self.states["/state2"])
+        self.states[""].addChild(self.states["/end"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/state1"]
+        
+        # transition /state1
+        _state1_0 = Transition(self, self.states["/state1"], [self.states["/state2"]])
+        _state1_0.setTrigger(Event("_0after"))
+        self.states["/state1"].addTransition(_state1_0)
+        
+        # transition /state2
+        _state2_0 = Transition(self, self.states["/state2"], [self.states["/state1"]])
+        _state2_0.setAction(self._state2_0_exec)
+        _state2_0.setTrigger(None)
+        _state2_0.setGuard(self._state2_0_guard)
+        self.states["/state2"].addTransition(_state2_0)
+        _state2_1 = Transition(self, self.states["/state2"], [self.states["/end"]])
+        _state2_1.setTrigger(None)
+        _state2_1.setGuard(self._state2_1_guard)
+        self.states["/state2"].addTransition(_state2_1)
+    
+    def _state1_enter(self):
+        self.addTimer(0, 1)
+    
+    def _state1_exit(self):
+        self.removeTimer(0)
+    
+    def _state2_enter(self):
+        self.big_step.outputEvent(Event("test_event", self.getOutPortName("ui"), [str(self.amount), str('%.2f' % (self.getSimulatedTime() / 1000.0))]))
+    
+    def _state2_0_exec(self, parameters):
+        self.amount += 1
+    
+    def _state2_0_guard(self, parameters):
+        return self.amount != 5
+    
+    def _state2_1_guard(self, parameters):
+        return self.amount == 5
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/state1"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "MainApp":
+            instance = MainApp(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("ui")
+        self.addOutputPort("ui")
+        self.object_manager.createInstance("MainApp", [])

+ 37 - 0
tests/Test3/sccd.xml

@@ -0,0 +1,37 @@
+<?xml version="1.1" ?>
+<diagram author="Sam Pieters" name="Multiple Output Test">
+    <description>
+        Test 3: Check if the model outputs multiple events with an interval on the right time.
+    </description>
+    <inport name="ui"/>
+    <outport name="ui"/>
+    <class name="MainApp" default="true">
+        <constructor>
+            <body>
+                self.amount = 1
+            </body>
+        </constructor>
+        <scxml initial="state1">
+            <state id="state1">
+                <transition after="1" target="../state2" />
+            </state>
+            <state id="state2">
+                <onentry>
+                    <raise port="ui" event="test_event">
+                        <parameter expr="str(self.amount)" />
+                        <parameter expr="str('%.2f' % (self.getSimulatedTime() / 1000.0))" />
+                    </raise>
+                </onentry>
+                <transition target="../state1" cond="self.amount != 5">
+                    <script>
+                        self.amount += 1
+                    </script>
+                </transition>
+                <transition target="../end" cond="self.amount == 5">
+                </transition>
+            </state>
+            <state id="end">
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 23 - 0
tests/Test4/sccd.xml

@@ -0,0 +1,23 @@
+<?xml version="1.1" ?>
+<diagram author="Sam Pieters" name="Input Test">
+    <description>
+        Test 4: Check if the model receives input on the right rime and sends and output event after 0.5 seconds.
+    </description>
+    <inport name="ui"/>
+    <outport name="ui"/>
+    <class name="MainApp" default="true">
+        <scxml initial="state1">
+            <state id="state1">
+                <transition event="input_test_event" target="../state2" />
+            </state>
+            <state id="state2">
+                <onentry>
+                    <raise port="ui" event="test_event">
+                        <parameter expr="str(self.amount)" />
+                        <parameter expr="str('%.2f' % (self.getSimulatedTime() / 1000.0))" />
+                    </raise>
+                </onentry>
+            </state>
+        </scxml>
+    </class>
+</diagram>