Forráskód Böngészése

started the test framework + fixed examples

sampieters 1 éve
szülő
commit
57ab9ffb86
36 módosított fájl, 3879 hozzáadás és 409 törlés
  1. 11 1
      README.md
  2. 2 2
      examples/BouncingBalls/PyDEVS/runner.py
  3. 312 0
      examples/BouncingBalls/PyDEVS/trace.txt
  4. 1 1
      examples/BouncingBalls/Python/runner.py
  5. 0 8
      examples/BouncingBalls/Python/target.py
  6. 43 0
      examples/BouncingBalls/Python/test_runner.py
  7. 128 0
      examples/BouncingBalls/Python/tester.py
  8. 219 0
      examples/BouncingBalls/Python/trace.txt
  9. 4 0
      examples/BouncingBalls/input_trace.txt
  10. 0 0
      examples/ElevatorBalls/Python/runner.py
  11. 587 0
      examples/ElevatorBalls/Python/target.py
  12. 445 0
      examples/ElevatorBalls/sccd.xml
  13. 0 0
      examples/FixedTimerEventloop/PyDEVS/runner.py
  14. 0 0
      examples/FixedTimerEventloop/PyDEVS/target.py
  15. 26 0
      examples/FixedTimerEventloop/Python/runner.py
  16. 0 0
      examples/FixedTimerEventloop/Python/target.py
  17. 0 0
      examples/FixedTimerEventloop/sccd.xml
  18. 61 0
      examples/FixedTimerThread/PyDEVS/runner.py
  19. 131 0
      examples/FixedTimerThread/PyDEVS/target.py
  20. 25 0
      examples/FixedTimerThread/Python/runner.py
  21. 17 29
      examples/Timer/Python/target.py
  22. 47 0
      examples/FixedTimerThread/sccd.xml
  23. 3 3
      examples/Timer/PyDEVS/runner.py
  24. 40 0
      examples/FixedTrafficLight/Python/runner.py
  25. 113 0
      examples/FixedTrafficLight/sccd.xml
  26. 32 0
      examples/SimpleElevatorBalls/PyDEVS/runner.py
  27. 562 0
      examples/SimpleElevatorBalls/PyDEVS/target.py
  28. 26 0
      examples/SimpleElevatorBalls/Python/runner.py
  29. 520 0
      examples/SimpleElevatorBalls/Python/target.py
  30. 390 0
      examples/SimpleElevatorBalls/sccd.xml
  31. 0 259
      examples/Timer/PyDEVS/target.py
  32. 0 12
      examples/Timer/Python/runner.py
  33. 0 57
      examples/Timer/sccd.xml
  34. 6 6
      examples/Train/Python/runner.py
  35. 29 31
      sccd/runtime/tracers/tracerVerbose.py
  36. 99 0
      tracechecker/main.py

+ 11 - 1
README.md

@@ -97,4 +97,14 @@ python3 runner.py
 ### Timer
 
 ## TODO
-1) Fix bug (when deleting and adding and back deleting, assocation error)
+
+
+## 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

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

@@ -20,9 +20,9 @@ if __name__ == '__main__':
 	tkroot.withdraw()
 	sim = DEVSSimulator(model)
 	sim.setRealTime(True)
-	sim.setRealTimeInputFile(None)
+	#sim.setRealTimeInputFile("./examples/BouncingBalls/input_trace.txt")
 	sim.setRealTimePorts(refs)
-	sim.setVerbose(None)
+	sim.setVerbose("./examples/BouncingBalls/PyDEVS/trace.txt")
 	sim.setRealTimePlatformTk(tkroot)
 
 	ui = UI(tkroot, sim)

+ 312 - 0
examples/BouncingBalls/PyDEVS/trace.txt

@@ -0,0 +1,312 @@
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	INITIAL CONDITIONS in model <controller.Ball>
+		Initial State: None
+		Next scheduled internal transition at time inf
+
+
+	INITIAL CONDITIONS in model <controller.Button>
+		Initial State: None
+		Next scheduled internal transition at time inf
+
+
+	INITIAL CONDITIONS in model <controller.Field>
+		Initial State: None
+		Next scheduled internal transition at time inf
+
+
+	INITIAL CONDITIONS in model <controller.MainApp>
+		Initial State: None
+		Next scheduled internal transition at time inf
+
+
+	INITIAL CONDITIONS in model <controller.ObjectManager>
+		Initial State: None
+		Next scheduled internal transition at time 0.000000
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.MainApp>
+		Input Port Configuration:
+			port <obj_manager_in>:
+				('MainApp', 'MainApp', (event name: start_instance; port: None; parameters: ['MainApp[0]']))
+			port <input>:
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+				('MainApp', 'MainApp', (event name: start_instance; port: None; parameters: ['MainApp[0]']))
+			port <port2>:
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.ObjectManager>
+		Input Port Configuration:
+			port <input>:
+				('MainApp', 'MainApp', (event name: instance_started; port: None; parameters: ['MainApp[0]']))
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+				('MainApp', 'MainApp', (event name: instance_started; port: None; parameters: ['MainApp[0]']))
+			port <fields>:
+		Next scheduled internal transition at time 0.000000
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	CONFLUENT TRANSITION in model <controller.MainApp>
+		Input Port Configuration:
+			port <obj_manager_in>: 
+				('MainApp', 'MainApp', (event name: instance_started; port: None; parameters: ['MainApp[0]']))
+			port <input>: 
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+			port <fields>:
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+				('MainApp', 'MainApp', (event name: instance_started; port: None; parameters: ['MainApp[0]']))
+			port <port2>:
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.ObjectManager>
+		Input Port Configuration:
+			port <input>:
+				('MainApp', 'Field', (event name: create_instance; port: None; parameters: ['fields']))
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+				('MainApp', 'Field', (event name: create_instance; port: None; parameters: ['fields']))
+			port <fields>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.Field>
+		Input Port Configuration:
+			port <obj_manager_in>:
+				('MainApp', 'Field', (event name: create_instance; port: None; parameters: ['fields']))
+			port <input>:
+			port <field_ui>:
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.FieldInstance object at 0x10472d370>}
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+			port <port2>:
+				('MainApp', 'Field', (event name: create_instance; port: None; parameters: ['fields']))
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.ObjectManager>
+		Input Port Configuration:
+			port <input>:
+				('Field', 'MainApp', (event name: instance_created; port: None; parameters: ['fields[0]']))
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.Field>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.FieldInstance object at 0x10472d370>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+				('Field', 'MainApp', (event name: instance_created; port: None; parameters: ['fields[0]']))
+			port <balls>:
+			port <buttons>:
+			port <parent>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.MainApp>
+		Input Port Configuration:
+			port <obj_manager_in>:
+				('Field', 'MainApp', (event name: instance_created; port: None; parameters: ['fields[0]']))
+			port <input>:
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+				('Field', 'MainApp', (event name: instance_created; port: None; parameters: ['fields[0]']))
+			port <port2>:
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+			port <fields>:
+		Next scheduled internal transition at time 0.000000
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+			port <fields>:
+		Next scheduled internal transition at time 0.000000
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.ObjectManager>
+		Input Port Configuration:
+			port <input>:
+				('MainApp', 'Field', (event name: start_instance; port: None; parameters: ['fields[0]']))
+				('MainApp', 'Field', (event name: set_association_name; port: None; parameters: ['fields[0]']))
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+				('MainApp', 'Field', (event name: start_instance; port: None; parameters: ['fields[0]']))
+				('MainApp', 'Field', (event name: set_association_name; port: None; parameters: ['fields[0]']))
+			port <fields>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.Field>
+		Input Port Configuration:
+			port <obj_manager_in>:
+				('MainApp', 'Field', (event name: start_instance; port: None; parameters: ['fields[0]']))
+				('MainApp', 'Field', (event name: set_association_name; port: None; parameters: ['fields[0]']))
+			port <input>:
+			port <field_ui>:
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.FieldInstance object at 0x10472d370>}
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+			port <port2>:
+				('MainApp', 'Field', (event name: start_instance; port: None; parameters: ['fields[0]']))
+				('MainApp', 'Field', (event name: set_association_name; port: None; parameters: ['fields[0]']))
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.ObjectManager>
+		Input Port Configuration:
+			port <input>:
+				('Field', 'MainApp', (event name: instance_started; port: None; parameters: ['fields[0]']))
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.Field>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.FieldInstance object at 0x10472d370>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+				('Field', 'MainApp', (event name: instance_started; port: None; parameters: ['fields[0]']))
+			port <balls>:
+			port <buttons>:
+			port <parent>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	EXTERNAL TRANSITION in model <controller.MainApp>
+		Input Port Configuration:
+			port <obj_manager_in>:
+				('Field', 'MainApp', (event name: instance_started; port: None; parameters: ['fields[0]']))
+			port <input>:
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Next scheduled internal transition at time 0.000000
+
+
+	INTERNAL TRANSITION in model <controller.ObjectManager>
+		New State: <examples.BouncingBalls.PyDEVS.target.ObjectManagerState object at 0x104663470>
+		Output Port Configuration:
+			port <port1>:
+				('Field', 'MainApp', (event name: instance_started; port: None; parameters: ['fields[0]']))
+			port <port2>:
+			port <port3>:
+			port <port4>:
+		Next scheduled internal transition at time inf
+
+
+__  Current Time:   0.000000 __________________________________________ 
+
+
+	INTERNAL TRANSITION in model <controller.MainApp>
+		New State: {0: <examples.BouncingBalls.PyDEVS.target.MainAppInstance object at 0x104673ef0>}
+		Output Port Configuration:
+			port <obj_manager_out>:
+			port <fields>:
+		Next scheduled internal transition at time inf
+

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

@@ -24,7 +24,7 @@ if __name__ == '__main__':
 	ui = UI(tkroot, controller)
 	controller.addMyOwnOutputListener(OutputListener(ui))
 
-	controller.setVerbose(None)
+	controller.setVerbose("./examples/BouncingBalls/Python/trace.txt")
 
 	controller.start()
 	tkroot.mainloop()

+ 0 - 8
examples/BouncingBalls/Python/target.py

@@ -562,16 +562,8 @@ class Ball(RuntimeClassBase):
         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
-        
-        # TODO:
-        #circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
-        #ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, self.controller, 'mouse_press', self.inports["ball_ui"]);
-        #ui.bind_event(circle, ui.EVENTS.MOUSE_MOVE, self.controller, 'mouse_move', self.inports['ball_ui']);
-        #ui.bind_event(circle, ui.EVENTS.MOUSE_RELEASE, self.controller, 'mouse_release', self.inports['ball_ui']);
-        #self.element = circle;
     
     def user_defined_destructor(self):
-        #self.canvas.remove_element(self.element);
         pass
     
     

+ 43 - 0
examples/BouncingBalls/Python/test_runner.py

@@ -0,0 +1,43 @@
+'''
+Created on 27-jul.-2014
+
+@author: Simon
+'''
+import tkinter as tk
+import target as target
+from sccd.runtime.libs.ui_v2 import UI
+from sccd.runtime.tkinter_eventloop import TkEventLoop
+import time
+
+return_dict = {
+	"create_window": "window_created"
+}
+
+
+
+class OutputListener:
+	def __init__(self, ui, controller):
+		self.ui = ui
+		self.controller = controller
+
+	def add(self, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+			#ret_event = return_dict[event.name]
+		#time.sleep(5)
+		#print("check")
+
+
+if __name__ == '__main__':
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	controller = target.Controller(TkEventLoop(tkroot))
+	ui = UI(tkroot, controller)
+	controller.addMyOwnOutputListener(OutputListener(ui, controller))
+
+	controller.setVerbose("./examples/BouncingBalls/Python/trace.txt")
+
+	controller.start()
+	tkroot.mainloop()

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 128 - 0
examples/BouncingBalls/Python/tester.py


+ 219 - 0
examples/BouncingBalls/Python/trace.txt

@@ -0,0 +1,219 @@
+__  Current Time:   0.000000 __________________________________________ 
+
+EXIT STATE in model <MainApp>
+		State: /running/root/main_behaviour/initializing
+
+ENTER STATE in model <MainApp>
+		State: /running/root/main_behaviour/running
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour/waiting
+
+ENTER STATE in model <MainApp>
+		State: /running/root/cd_behaviour/creating
+
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour/creating
+
+ENTER STATE in model <MainApp>
+		State: /running/root/cd_behaviour/waiting
+
+__  Current Time:   0.009000 __________________________________________ 
+
+
+INPUT EVENT from port <private_1_<narrow_cast>>
+	\Type: <narrow_cast>
+	\Event: (event name: set_association_name; port: private_1_<narrow_cast>; parameters: ['fields[0]'])
+
+EXIT STATE in model <Field>
+		State: /root/waiting
+
+ENTER STATE in model <Field>
+		State: /root/creating_window
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: create_window; port: ui; parameters: [800, 600, 'BouncingBalls', 'private_2_field_ui'])
+
+__  Current Time:   0.029000 __________________________________________ 
+
+INPUT EVENT from port <private_2_field_ui>
+	\Type: field_ui
+	\Event: (event name: window_created; port: private_2_field_ui; parameters: [0])
+
+EXIT STATE in model <Field>
+		State: /root/creating_window
+
+ENTER STATE in model <Field>
+		State: /root/creating_canvas
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [0, 'WM_DELETE_WINDOW', 'window_close', 'private_2_field_ui'])
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [0, '<Key>', 'key_press', 'private_2_field_ui'])
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: create_canvas; port: ui; parameters: [0, 800, 550, {'background': '#eee'}, 'private_2_field_ui'])
+
+__  Current Time:   0.085000 __________________________________________ 
+
+INPUT EVENT from port <private_2_field_ui>
+	\Type: field_ui
+	\Event: (event name: canvas_created; port: private_2_field_ui; parameters: [1])
+
+EXIT STATE in model <Field>
+		State: /root/creating_canvas
+
+ENTER STATE in model <Field>
+		State: /root/creating_button
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [1, '<Button-2>', 'right_click', 'private_2_field_ui'])
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [1, '<Motion>', 'mouse_move', 'private_2_field_ui'])
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [1, '<ButtonRelease>', 'mouse_release', 'private_2_field_ui'])
+
+
+EXIT STATE in model <Field>
+		State: /root/creating_button
+
+ENTER STATE in model <Field>
+		State: /root/running
+
+ENTER STATE in model <Field>
+		State: /root/running/main_behaviour
+
+ENTER STATE in model <Field>
+		State: /root/running/main_behaviour/running
+
+ENTER STATE in model <Field>
+		State: /root/running/deleting_behaviour
+
+ENTER STATE in model <Field>
+		State: /root/running/deleting_behaviour/running
+
+ENTER STATE in model <Field>
+		State: /root/running/child_behaviour
+
+ENTER STATE in model <Field>
+		State: /root/running/child_behaviour/listening
+
+ENTER STATE in model <Field>
+		State: /root/running/deleting_balls_behaviour
+
+ENTER STATE in model <Field>
+		State: /root/running/deleting_balls_behaviour/listening
+
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: create_button; port: ui; parameters: [0, 'create_new_field', 'private_4_button_ui'])
+
+__  Current Time:   0.090000 __________________________________________ 
+
+INPUT EVENT from port <private_4_button_ui>
+	\Type: button_ui
+	\Event: (event name: button_created; port: private_4_button_ui; parameters: [2])
+
+EXIT STATE in model <Button>
+		State: /creating_button
+
+ENTER STATE in model <Button>
+		State: /running
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: bind_event; port: ui; parameters: [2, '<Button>', 'mouse_click', 'private_4_button_ui'])
+
+__  Current Time:   3.570000 __________________________________________ 
+
+INPUT EVENT from port <private_2_field_ui>
+	\Type: field_ui
+	\Event: (event name: window_close; port: private_2_field_ui)
+
+EXIT STATE in model <Field>
+		State: /root/running/deleting_balls_behaviour/listening
+
+EXIT STATE in model <Field>
+		State: /root/running/child_behaviour/listening
+
+EXIT STATE in model <Field>
+		State: /root/running/deleting_behaviour/running
+
+EXIT STATE in model <Field>
+		State: /root/running/main_behaviour/running
+
+EXIT STATE in model <Field>
+		State: /root/running/deleting_balls_behaviour
+
+EXIT STATE in model <Field>
+		State: /root/running/child_behaviour
+
+EXIT STATE in model <Field>
+		State: /root/running/deleting_behaviour
+
+EXIT STATE in model <Field>
+		State: /root/running/main_behaviour
+
+EXIT STATE in model <Field>
+		State: /root/running
+
+ENTER STATE in model <Field>
+		State: /root/deleting
+
+EXIT STATE in model <Field>
+		State: /root/deleting
+
+ENTER STATE in model <Field>
+		State: /root/deleted
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: destroy_window; port: ui; parameters: [0])
+
+__  Current Time:   3.572000 __________________________________________ 
+
+
+INPUT EVENT from port <private_0_<narrow_cast>>
+	\Type: <narrow_cast>
+	\Event: (event name: delete_field; port: private_0_<narrow_cast>; parameters: ['fields[0]'])
+
+__  Current Time:   3.572000 __________________________________________ 
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour/waiting
+
+ENTER STATE in model <MainApp>
+		State: /running/root/cd_behaviour/check_nr_of_fields
+
+
+__  Current Time:   3.622000 __________________________________________ 
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour/check_nr_of_fields
+
+ENTER STATE in model <MainApp>
+		State: /running/root/cd_behaviour/stopped
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour/stopped
+
+EXIT STATE in model <MainApp>
+		State: /running/root/main_behaviour/running
+
+EXIT STATE in model <MainApp>
+		State: /running/root/cd_behaviour
+
+EXIT STATE in model <MainApp>
+		State: /running/root/main_behaviour
+
+EXIT STATE in model <MainApp>
+		State: /running/root
+
+ENTER STATE in model <MainApp>
+		State: /running/stopped
+
+OUTPUT EVENT to port <ui>
+	\Event: (event name: destroy_all; port: ui)
+

+ 4 - 0
examples/BouncingBalls/input_trace.txt

@@ -0,0 +1,4 @@
+0.029 field_ui Event("window_created","private_2_field_ui",[0])
+0.085 field_ui Event("canvas_created","private_2_field_ui",[1])
+0.09 button_ui Event("button_created","private_4_button_ui",[2])
+3.57 field_ui Event("window_close","private_2_field_ui",None)

examples/FixedTimer/Python/runner.py → examples/ElevatorBalls/Python/runner.py


+ 587 - 0
examples/ElevatorBalls/Python/target.py

@@ -0,0 +1,587 @@
+"""
+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:   Elevator Balls
+
+"""
+
+from sccd.runtime.statecharts_core import *
+from sccd.runtime.libs.ui import ui
+import random
+import time
+
+CANVAS_WIDTH = 800
+CANVAS_HEIGHT = 550
+
+# package "Elevator Balls"
+
+class MainApp(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["field_ui"] = controller.addInputPort("field_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.floor_dimensions = None
+        
+        # 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 /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_elevator
+        self.states["/create_elevator"] = State(3, "/create_elevator", self)
+        
+        # state /creating
+        self.states["/creating"] = State(4, "/creating", self)
+        
+        # state /waiting
+        self.states["/waiting"] = State(5, "/waiting", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_window"])
+        self.states[""].addChild(self.states["/creating_canvas"])
+        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_elevator"]])
+        _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_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_WIDTH, CANVAS_HEIGHT, "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']]))
+    
+    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_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 Elevator(RuntimeClassBase):
+    def __init__(self, controller, canvas_id):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["elevator_ui"] = controller.addInputPort("elevator_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
+        Elevator.user_defined_constructor(self, canvas_id)
+    
+    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;
+        
+        self.dim = {'x': 80, 'y': 150};
+        self.vel = {'x': 0, 'y': 0};
+        self.pos = {'x': 400, '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/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"].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/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"]])
+        _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/create_elevator_controls"]])
+        _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)
+        _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_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)
+    
+    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_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:
+            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 Ball(RuntimeClassBase):
+    def __init__(self, controller, canvas_id, x, y):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["ball_ui"] = controller.addInputPort("ball_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.canvas_id = None
+        self.pos = None
+        
+        # call user defined constructor
+        Ball.user_defined_constructor(self, canvas_id, x, y)
+    
+    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):
+        # Invert velocity when colliding with canvas border:
+        if self.pos['x']-self.r <= self.rect_pos['x'] - (self.rect_dim['x'] / 2) or self.pos['x']+self.r >= self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+            self.vel['x'] = -self.vel['x'] + self.rect_vel['x'];
+        if self.pos['y']-self.r <= self.rect_pos['y'] - (self.rect_dim['y'] / 2) or self.pos['y']+self.r >= self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+            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_WIDTH-self.r)
+        y = min(max(0+self.r, y), CANVAS_HEIGHT-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 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 = {}
+            instance.associations["elevator"] = Association("Elevator", 0, -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.associations = {}
+            instance.associations["parent"] = Association("Field", 1, 1)
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(EventLoopControllerBase):
+    def __init__(self, event_loop_callbacks, finished_callback = None, behind_schedule_callback = None):
+        if finished_callback == None: finished_callback = None
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        EventLoopControllerBase.__init__(self, ObjectManager(self), event_loop_callbacks, finished_callback, behind_schedule_callback)
+        self.addInputPort("ui")
+        self.addOutputPort("ui")
+        self.object_manager.createInstance("MainApp", [])

+ 445 - 0
examples/ElevatorBalls/sccd.xml

@@ -0,0 +1,445 @@
+<?xml version="1.0" ?>
+<diagram author="Sam Pieters" name="Elevator Balls">
+    <top>
+        from sccd.runtime.libs.ui import ui
+        import random
+        import time
+
+        CANVAS_WIDTH = 800
+        CANVAS_HEIGHT = 550
+    </top>
+    <inport name="ui" />
+    <outport name="ui" />
+    <class name="MainApp" default="true">
+        <relationships>
+            <association name="elevator" class="Elevator" />
+        </relationships>
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="floor_dimensions" />
+        <atrribute name="floor_height" />
+        <inport name="field_ui"/>
+        <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='"Bouncing Balls Elevator"'/><!-- title -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="window_created" target="../creating_canvas">
+                    <parameter name="window_id" type="int" />
+                    <script>
+                        self.window_id = window_id
+                    </script>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.WINDOW_CLOSE"/><!-- tk_event -->
+                        <parameter expr="'window_close'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.KEY_PRESS"/><!-- tk_event -->
+                        <parameter expr="'key_press'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </transition>
+            </state>
+            <state id="creating_canvas">
+                <onentry>
+                    <raise port="ui" event="create_canvas">
+                        <parameter expr="self.window_id"/><!-- window_id -->
+                        <parameter expr="CANVAS_WIDTH"/><!-- width -->
+                        <parameter expr="CANVAS_HEIGHT - 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">
+                    <parameter name="canvas_id" type="int"/>
+                    <script>
+                        self.canvas_id = canvas_id
+                    </script>
+                </transition>
+            </state>
+            <state id="create_elevator">
+                <transition target="../creating">
+                    <raise scope="cd" event="create_instance">
+                        <parameter expr='"elevator"' />
+                        <parameter expr='"Elevator"' />
+                        <parameter expr="self.canvas_id" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="creating">
+                <transition event="instance_created" target="../waiting">
+                    <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" />
+                        <parameter expr="self.canvas_id" />
+                        <parameter expr="self.window_id" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="waiting">
+            </state>
+        </scxml>
+    </class>
+
+    <class name="Elevator">
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="elevator_id" />
+        <atrribute name="pos" />
+        <inport name="elevator_ui"/>
+        <relationships>
+            <association name="balls" class="Ball" />
+            <association name="parent" class="MainApp" min="1" max="1" />
+        </relationships>
+        <constructor>
+            <parameter name="canvas_id" />
+            <body>
+                <![CDATA[
+                self.canvas_id = canvas_id;
+                self.button_ids = {
+                    'up': None,
+                    'down': None,
+                    'open': None
+                };
+                self.open_button = None;
+                self.is_open = False;
+
+                self.dim = {'x': 80, 'y': 150};
+                self.vel = {'x': 0, 'y': 0};
+                self.pos = {'x': 400, 'y': 75};
+                self.smooth = 0.6; # value between 0 and 1
+                ]]>
+            </body>
+        </constructor>
+        <scxml initial="root">
+            <state id="root" initial="waiting">
+                <state id="waiting">
+                    <transition event="set_association_name" target="../creating_elevator">
+                        <parameter name="association_name" type="str" />
+                        <parameter name="canvas_id" type="int" />
+                        <parameter name="window_id" type="int" />
+                        <script>
+                            self.association_name = association_name
+                            self.canvas_id = canvas_id
+                            self.window_id = window_id
+                        </script>
+                    </transition>
+                </state>
+                <state id="creating_elevator">
+                    <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['elevator_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="rectangle_created" target="../create_elevator_controls">
+                        <parameter name="canvas_id" type="int" />
+                        <parameter name="rect_id" type="int" />
+                        <script>
+                            self.elevator_id = rect_id
+                        </script>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="canvas_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.MOUSE_RIGHT_CLICK"/><!-- tk_event -->
+                            <parameter expr="'right_click'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['elevator_ui']"/><!-- inport for response -->
+                        </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:
+                                self.vel['y'] = -self.vel['y'];
+                            ]]>
+                        </script>
+                        <raise port="ui" event="move_element">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.elevator_id"/>
+                            <parameter expr="self.vel['x']"/>
+                            <parameter expr="self.vel['y']"/>
+                        </raise>
+                        <script>
+                            self.pos['x'] += self.vel['x']
+                            self.pos['y'] += self.vel['y']
+                        </script>
+                        <raise scope="narrow" event="update_bounds" target="'balls'">
+                            <parameter expr="self.pos" />
+                            <parameter expr="self.dim" />
+                            <parameter expr="self.vel" />
+                        </raise>
+                    </transition>
+                    <transition port="elevator_ui" event="right_click" target=".">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <raise scope="cd" event="create_instance">
+                            <parameter expr='"balls"' />
+                            <parameter expr='"Ball"' />
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="x" />
+                            <parameter expr="y" />
+                        </raise>
+                    </transition>
+                    <transition event="instance_created" target=".">
+                        <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="Ball">
+        <attribute name="canvas_id" />
+        <atrribute name="circle_id" />
+        <attribute name="pos" />
+        <inport name="ball_ui" />
+        <relationships>
+            <association name="parent" class="Field" min="1" max="1" />
+        </relationships>
+        <constructor>
+            <parameter name="canvas_id" />
+            <parameter name="x" />
+            <parameter name="y" />
+            <body>
+                <![CDATA[
+                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
+                ]]>
+            </body>
+        </constructor>
+        <destructor>
+        </destructor>
+        <scxml initial="main_behaviour">
+            <state id="main_behaviour" initial="initializing">
+                <state id="initializing">
+                    <transition event="set_association_name" target="../creating_circle">
+                        <parameter name="association_name" type="str" />
+                        <script>
+                            self.association_name = association_name
+                        </script>
+                    </transition>
+                </state>
+                <state id="creating_circle">
+                    <onentry>
+                        <raise port="ui" event="create_circle">
+                            canvas_id, x, y, r, style, res_port
+                            <parameter expr="self.canvas_id"/><!-- canvas_id -->
+                            <parameter expr="self.pos['x']"/><!-- x -->
+                            <parameter expr="self.pos['y']"/><!-- y -->
+                            <parameter expr="self.r"/><!-- r -->
+                            <parameter expr="{'fill':'#000'}"/><!-- style -->
+                            <parameter expr="self.inports['ball_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="circle_created" target="../bouncing">
+                        <parameter name="canvas_id"/>
+                        <parameter name="circle_id"/>
+                        <script>
+                            self.circle_id = circle_id
+                        </script>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_PRESS"/>
+                            <parameter expr="'mouse_press'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_MOVE"/>
+                            <parameter expr="'mouse_move'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_RELEASE"/>
+                            <parameter expr="'mouse_release'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                    </transition>
+                </state>
+                <state id="bouncing">
+                    <transition after="0.02" target=".">
+                        <script>
+                            <![CDATA[
+                            # Invert velocity when colliding with canvas border:
+                            if self.pos['x']-self.r <= self.rect_pos['x'] - (self.rect_dim['x'] / 2) or self.pos['x']+self.r >= self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+                                self.vel['x'] = -self.vel['x'] + self.rect_vel['x'];
+                            if self.pos['y']-self.r <= self.rect_pos['y'] - (self.rect_dim['y'] / 2) or self.pos['y']+self.r >= self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+                                self.vel['y'] = -self.vel['y'] + self.rect_vel['y'];
+                            ]]>
+                        </script>
+                        <raise port="ui" event="move_element">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="self.vel['x']"/>
+                            <parameter expr="self.vel['y']"/>
+                        </raise>
+                        <script>
+                            self.pos['x'] += self.vel['x']
+                            self.pos['y'] += self.vel['y']
+                        </script>
+                    </transition>
+                    <transition port="ball_ui" event="mouse_press" target="../selected" cond="button == ui.MOUSE_BUTTONS.LEFT">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <raise port="ui" event="set_element_color">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="'#ff0'"/>
+                        </raise>
+                    </transition>
+                    <transition event="update_bounds" target=".">
+                        <parameter name="pos" type="dict" />
+                        <parameter name="dim" type="dict" />
+                        <parameter name="vel" type="dict" />
+                        <script>
+                            self.rect_pos = pos
+                            self.rect_dim = dim
+                            self.rect_vel = vel
+                        </script>
+                    </transition>
+                </state>
+                <state id="dragging">
+                    <transition port="ball_ui" event="mouse_move" target=".">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <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)
+
+                            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}
+                            ]]>
+                        </script>
+                        <raise port="ui" event="set_element_pos">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="x-self.r"/>
+                            <parameter expr="y-self.r"/>
+                        </raise>
+                    </transition>
+                    <transition port="ball_ui" event="mouse_release" target="../bouncing">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <raise port="ui" event="set_element_color">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="'#f00'"/>
+                        </raise>
+                    </transition>
+                </state>
+                <state id='selected'>
+                    <transition port="ball_ui" event="mouse_press" target="../dragging" cond="button == ui.MOUSE_BUTTONS.LEFT">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <script>
+                            <![CDATA[
+                            self.mouse_pos = {'x':x, 'y':y};
+                            ]]>
+                        </script>
+                    </transition>
+                    <transition event="delete_self" target='../../deleted'>                    
+                        <raise event="delete_ball" scope="narrow" target="'parent'">
+                            <parameter expr='self.association_name' />
+                        </raise>
+                        <raise port="ui" event="destroy_element">
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="self.element_id" />
+                        </raise>
+                    </transition>
+                </state>
+            </state>
+            <state id='deleted' />
+        </scxml>
+    </class>
+</diagram>

examples/FixedTimer/PyDEVS/runner.py → examples/FixedTimerEventloop/PyDEVS/runner.py


examples/FixedTimer/PyDEVS/target.py → examples/FixedTimerEventloop/PyDEVS/target.py


+ 26 - 0
examples/FixedTimerEventloop/Python/runner.py

@@ -0,0 +1,26 @@
+import tkinter as tk
+import target as target
+from sccd.runtime.libs.ui_v2 import UI
+from sccd.runtime.tkinter_eventloop import TkEventLoop
+
+class OutputListener:
+	def __init__(self, ui):
+		self.ui = ui
+
+	def add(self, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+if __name__ == '__main__':
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	controller = target.Controller(TkEventLoop(tkroot))
+	ui = UI(tkroot, controller)
+	controller.addMyOwnOutputListener(OutputListener(ui))
+
+	controller.setVerbose(None)
+	
+	controller.start()
+	tkroot.mainloop()
+	

examples/FixedTimer/Python/target.py → examples/FixedTimerEventloop/Python/target.py


examples/FixedTimer/sccd.xml → examples/FixedTimerEventloop/sccd.xml


+ 61 - 0
examples/FixedTimerThread/PyDEVS/runner.py

@@ -0,0 +1,61 @@
+import target as target
+from sccd.runtime.DEVS_statecharts_core import Event
+import threading
+
+
+if __name__ == '__main__':
+    controller = target.Controller() 
+    
+    def raw_inputter():
+        while 1:
+            controller.addInput(Event(input(), "input", []))
+    input_thread = threading.Thread(target=raw_inputter)
+    input_thread.daemon = True
+    input_thread.start()
+    
+    output_listener = controller.addOutputListener(["output"])
+    def outputter():
+        while 1:
+            event = output_listener.fetch(-1)
+            print("SIMTIME: %.2fs" % (event.getParameters()[0] / 1000.0))
+            print("ACTTIME: %.2fs" % (event.getParameters()[1]))
+    output_thread = threading.Thread(target=outputter)
+    output_thread.daemon = True
+    output_thread.start()
+    
+    controller.start()
+
+
+
+
+import tkinter as tk
+import examples.BouncingBalls.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, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	refs = {"ui": model.ui, "field_ui": model.atomic1.field_ui, "button_ui": model.atomic2.button_ui, "ball_ui": model.atomic3.ball_ui}
+
+
+	sim = DEVSSimulator(model)
+	sim.setRealTime(True)
+	sim.setRealTimeInputFile(None)
+	sim.setRealTimePorts(refs)
+	sim.setVerbose(None)
+	#sim.setRealTimePlatformTk(tkroot)
+
+	model.atomic1.addMyOwnOutputListener(OutputListener(ui))
+	model.atomic2.addMyOwnOutputListener(OutputListener(ui))
+	model.atomic3.addMyOwnOutputListener(OutputListener(ui))
+	sim.simulate()
+	#tkroot.mainloop()

+ 131 - 0
examples/FixedTimerThread/PyDEVS/target.py

@@ -0,0 +1,131 @@
+"""
+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: Simon Van Mierlo
+Model name:   Timer (Threaded Version)
+
+"""
+
+from sccd.runtime.DEVS_statecharts_core import *
+import time
+
+# package "Timer (Threaded Version)"
+
+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)
+    
+    def user_defined_constructor(self):
+        self.starting_time = time.time()
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /running
+        self.states["/running"] = State(1, "/running", self)
+        self.states["/running"].setEnter(self._running_enter)
+        self.states["/running"].setExit(self._running_exit)
+        
+        # state /interrupted
+        self.states["/interrupted"] = State(2, "/interrupted", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/running"])
+        self.states[""].addChild(self.states["/interrupted"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/running"]
+        
+        # transition /running
+        _running_0 = Transition(self, self.states["/running"], [self.states["/running"]])
+        _running_0.setAction(self._running_0_exec)
+        _running_0.setTrigger(Event("_0after"))
+        self.states["/running"].addTransition(_running_0)
+        _running_1 = Transition(self, self.states["/running"], [self.states["/interrupted"]])
+        _running_1.setAction(self._running_1_exec)
+        _running_1.setTrigger(Event("interrupt", self.getInPortName("input")))
+        self.states["/running"].addTransition(_running_1)
+        
+        # transition /interrupted
+        _interrupted_0 = Transition(self, self.states["/interrupted"], [self.states["/interrupted"]])
+        _interrupted_0.setAction(self._interrupted_0_exec)
+        _interrupted_0.setTrigger(Event("interrupt", self.getInPortName("input")))
+        self.states["/interrupted"].addTransition(_interrupted_0)
+        _interrupted_1 = Transition(self, self.states["/interrupted"], [self.states["/running"]])
+        _interrupted_1.setAction(self._interrupted_1_exec)
+        _interrupted_1.setTrigger(Event("continue", self.getInPortName("input")))
+        self.states["/interrupted"].addTransition(_interrupted_1)
+    
+    def _running_enter(self):
+        self.addTimer(0, 0.05)
+    
+    def _running_exit(self):
+        self.removeTimer(0)
+    
+    def _running_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
+    
+    def _running_1_exec(self, parameters):
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
+    
+    def _interrupted_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
+    
+    def _interrupted_1_exec(self, parameters):
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/running"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class MainApp(ObjectManagerBase):
+    def __init__(self, name):
+        ObjectManagerBase.__init__(self, name)
+        self.input = self.addInPort("input")
+        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.input = self.addInPort("input")
+        self.addOutPort("output")
+        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)

+ 25 - 0
examples/FixedTimerThread/Python/runner.py

@@ -0,0 +1,25 @@
+import target as target
+from sccd.runtime.statecharts_core import Event
+import threading
+
+if __name__ == '__main__':
+    controller = target.Controller() 
+    
+    def raw_inputter():
+        while 1:
+            controller.addInput(Event(input(), "input", []))
+    input_thread = threading.Thread(target=raw_inputter)
+    input_thread.daemon = True
+    input_thread.start()
+    
+    output_listener = controller.addOutputListener(["output"])
+    def outputter():
+        while 1:
+            event = output_listener.fetch(-1)
+            print("SIMTIME: %.2fs" % (event.getParameters()[0] / 1000.0))
+            print("ACTTIME: %.2fs" % (event.getParameters()[1]))
+    output_thread = threading.Thread(target=outputter)
+    output_thread.daemon = True
+    output_thread.start()
+    
+    controller.start()

+ 17 - 29
examples/Timer/Python/target.py

@@ -2,15 +2,14 @@
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
 Model author: Simon Van Mierlo
-Model name:   Timer (Eventloop Version)
+Model name:   Timer (Threaded Version)
 
 """
 
 from sccd.runtime.statecharts_core import *
-from sccd.runtime.libs.ui import ui
-from time import time
+import time
 
-# package "Timer (Eventloop Version)"
+# package "Timer (Threaded Version)"
 
 class MainApp(RuntimeClassBase):
     def __init__(self, controller):
@@ -30,24 +29,12 @@ class MainApp(RuntimeClassBase):
         MainApp.user_defined_constructor(self)
     
     def user_defined_constructor(self):
-        self.canvas = ui.append_canvas(ui.window,100,100,{'background':'#222222'})
-        self.clock_text = self.canvas.element.create_text(25,25,{'text':'0.0'})
-        self.actual_clock_text = self.canvas.element.create_text(25,50,{'text':'0.0'})
-        interrupt_button = ui.append_button(ui.window, 'INTERRUPT');
-        continue_button = ui.append_button(ui.window, 'CONTINUE');
-        ui.bind_event(interrupt_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'interrupt_clicked');
-        ui.bind_event(continue_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'continue_clicked');
+        self.starting_time = time.time()
     
     def user_defined_destructor(self):
         pass
     
     
-    # user defined method
-    def update_timers(self):
-        self.canvas.element.itemconfigure(self.clock_text, text=str('%.2f' % (self.getSimulatedTime() / 1000.0)))
-        self.canvas.element.itemconfigure(self.actual_clock_text, text='%.2f' % (time() / 1000.0))
-    
-    
     # builds Statechart structure
     def build_statechart_structure(self):
         
@@ -75,17 +62,17 @@ class MainApp(RuntimeClassBase):
         self.states["/running"].addTransition(_running_0)
         _running_1 = Transition(self, self.states["/running"], [self.states["/interrupted"]])
         _running_1.setAction(self._running_1_exec)
-        _running_1.setTrigger(Event("interrupt_clicked", self.getInPortName("ui")))
+        _running_1.setTrigger(Event("interrupt", self.getInPortName("input")))
         self.states["/running"].addTransition(_running_1)
         
         # transition /interrupted
         _interrupted_0 = Transition(self, self.states["/interrupted"], [self.states["/interrupted"]])
         _interrupted_0.setAction(self._interrupted_0_exec)
-        _interrupted_0.setTrigger(Event("interrupt_clicked", self.getInPortName("ui")))
+        _interrupted_0.setTrigger(Event("interrupt", self.getInPortName("input")))
         self.states["/interrupted"].addTransition(_interrupted_0)
         _interrupted_1 = Transition(self, self.states["/interrupted"], [self.states["/running"]])
         _interrupted_1.setAction(self._interrupted_1_exec)
-        _interrupted_1.setTrigger(Event("continue_clicked", self.getInPortName("ui")))
+        _interrupted_1.setTrigger(Event("continue", self.getInPortName("input")))
         self.states["/interrupted"].addTransition(_interrupted_1)
     
     def _running_enter(self):
@@ -95,16 +82,16 @@ class MainApp(RuntimeClassBase):
         self.removeTimer(0)
     
     def _running_0_exec(self, parameters):
-        self.update_timers()
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
     
     def _running_1_exec(self, parameters):
-        self.update_timers()
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
     
     def _interrupted_0_exec(self, parameters):
-        self.update_timers()
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
     
     def _interrupted_1_exec(self, parameters):
-        self.update_timers()
+        self.big_step.outputEvent(Event("time_update", self.getOutPortName("output"), [self.getSimulatedTime(), time.time() - self.starting_time]))
     
     def initializeStatechart(self):
         # enter default state
@@ -123,10 +110,11 @@ class ObjectManager(ObjectManagerBase):
             raise Exception("Cannot instantiate class " + class_name)
         return instance
 
-class Controller(EventLoopControllerBase):
-    def __init__(self, event_loop_callbacks, finished_callback = None, behind_schedule_callback = None):
-        if finished_callback == None: finished_callback = None
+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
-        EventLoopControllerBase.__init__(self, ObjectManager(self), event_loop_callbacks, finished_callback, behind_schedule_callback)
-        self.addInputPort("ui")
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("input")
+        self.addOutputPort("output")
         self.object_manager.createInstance("MainApp", [])

+ 47 - 0
examples/FixedTimerThread/sccd.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" ?>
+<diagram author="Simon Van Mierlo" name="Timer (Threaded Version)">
+    <top>
+        import time
+    </top>
+    
+    <inport name="input" />        
+    <outport name="output" />
+
+    <class name="MainApp" default="true">
+        <constructor>
+            <body>
+                self.starting_time = time.time()
+            </body>
+        </constructor>
+        <scxml initial="running">
+            <state id="running">
+                <transition target="." after="0.05">
+                    <raise event="time_update" port="output">
+                        <parameter expr="self.getSimulatedTime()" />
+                        <parameter expr="time.time() - self.starting_time" />
+                    </raise>
+                </transition>
+                <transition target="../interrupted" event="interrupt" port="input">
+                    <raise event="time_update" port="output">
+                        <parameter expr="self.getSimulatedTime()" />
+                        <parameter expr="time.time() - self.starting_time" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="interrupted">
+                <transition target="." event="interrupt" port="input">
+                    <raise event="time_update" port="output">
+                        <parameter expr="self.getSimulatedTime()" />
+                        <parameter expr="time.time() - self.starting_time" />
+                    </raise>
+                </transition>
+                <transition target="../running" event="continue" port="input">
+                    <raise event="time_update" port="output">
+                        <parameter expr="self.getSimulatedTime()" />
+                        <parameter expr="time.time() - self.starting_time" />
+                    </raise>
+                </transition>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 3 - 3
examples/Timer/PyDEVS/runner.py

@@ -2,19 +2,19 @@ from pypdevs.simulator import Simulator
 import target as target
 
 from tkinter import *
-from sccd.runtime.libs.ui import ui
+from sccd.runtime.libs.DEVui import ui
 
 
 
 model = target.Controller(name="controller")
-#refs = {"ui": model.ui, "field_ui": model.atomic1.field_ui}
+refs = {"ui": model.ui}
 ui.window = Tk()
 ui.window.withdraw()
 
 sim = Simulator(model)
 sim.setRealTime(True)
 sim.setRealTimeInputFile(None)
-#sim.setRealTimePorts(refs)
+sim.setRealTimePorts(refs)
 sim.setVerbose(None)
 sim.setRealTimePlatformTk(ui.window)
 

+ 40 - 0
examples/FixedTrafficLight/Python/runner.py

@@ -0,0 +1,40 @@
+import tkinter as tk
+import target as target
+from sccd.runtime.libs.ui import ui
+from sccd.runtime.statecharts_core import Event
+from sccd.runtime.tkinter_eventloop import *
+
+if __name__ == '__main__':
+	ui.window = tk.Tk()
+
+	controller = target.Controller(TkEventLoop(ui.window))
+	controller.start()
+	ui.window.mainloop()
+
+
+import tkinter as tk
+import target as target
+from sccd.runtime.libs.ui_v2 import UI
+from sccd.runtime.tkinter_eventloop import TkEventLoop
+
+class OutputListener:
+	def __init__(self, ui):
+		self.ui = ui
+
+	def add(self, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+if __name__ == '__main__':
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	controller = target.Controller(TkEventLoop(tkroot))
+	ui = UI(tkroot, controller)
+	controller.addMyOwnOutputListener(OutputListener(ui))
+
+	controller.setVerbose(None)
+	
+	controller.start()
+	tkroot.mainloop()
+	

+ 113 - 0
examples/FixedTrafficLight/sccd.xml

@@ -0,0 +1,113 @@
+<?xml version="1.0" ?>
+<diagram author="Sam Pieters" name="Timer (Eventloop Version)">
+    <top>
+        from sccd.runtime.libs.ui import ui
+        import time
+
+        CANVAS_WIDTH = 350
+        CANVAS_HEIGHT = 300
+    </top>
+    
+    <inport name="ui" />    
+    <class name="MainApp" default="true">
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="clock_id" />
+        <attribute name="actual_clock_id" />
+        <attribute name="button_id" />
+        <attribute name="starting_time" />
+        <inport name="field_ui"/>
+        <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='"Fixed Timer"'/><!-- title -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="window_created" target="../creating_canvas">
+                    <parameter name="window_id" type="int" />
+                    <script>
+                        <![CDATA[
+                        self.window_id = window_id
+                        self.starting_time = time.time()
+                        ]]>
+                    </script>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.WINDOW_CLOSE"/><!-- tk_event -->
+                        <parameter expr="'window_close'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.KEY_PRESS"/><!-- tk_event -->
+                        <parameter expr="'key_press'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </transition>
+            </state>
+            <state id="creating_canvas">
+                <onentry>
+                    <raise port="ui" event="create_canvas">
+                        <parameter expr="self.window_id"/><!-- window_id -->
+                        <parameter expr="CANVAS_WIDTH"/><!-- width -->
+                        <parameter expr="CANVAS_HEIGHT - 200"/><!-- height -->
+                        <parameter expr="{'background':'#222222'}"/><!-- style -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="canvas_created" target="../creating_clock_text">
+                    <parameter name="canvas_id" type="int"/>
+                    <script>
+                        <![CDATA[
+                        self.canvas_id = canvas_id
+                        ]]>
+                    </script>
+                    <raise scope="narrow" event="set_association_name" target="association_name">
+                        <parameter expr="association_name" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="running">
+            </state>
+        </scxml>
+    </class>
+
+    <class name="Elevator">
+        <attribute name="canvas_id" />
+        <atrribute name="circle_id" />
+        <attribute name="pos" />
+        <inport name="elevator_ui"/>
+        <relationships>
+            <association name="parent" class="MainApp" min="1" max="1" />
+        </relationships>
+        <scxml initial="root">
+            <state id="root" initial="waiting">
+                <state id="waiting">
+                    <transition event="set_association_name" target="../creating_elevator">
+                        <parameter name="association_name" type="str" />
+                        <script>
+                            self.association_name = association_name
+                        </script>
+                    </transition>
+                </state>
+                <state id="creating_elevator">
+                    <onentry>
+                        <raise port="ui" event="create_rectangle">
+                            canvas_id, x, y, r, style, res_port
+                            <parameter expr="self.canvas_id"/><!-- canvas_id -->
+                            <parameter expr="self.pos['x']"/><!-- x -->
+                            <parameter expr="self.pos['y']"/><!-- y -->
+                            <parameter expr="self.r"/><!-- r -->
+                            <parameter expr="{'fill':'#000'}"/><!-- style -->
+                            <parameter expr="self.inports['ball_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                </state>
+            </state>
+        </scxml>
+    </class>
+</diagram>

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

@@ -0,0 +1,32 @@
+import tkinter as tk
+import examples.SimpleElevatorBalls.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, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+if __name__ == '__main__':
+	model = target.Controller(name="controller")
+	refs = {"ui": model.ui, "elevator_ui": model.atomic1.elevator_ui, "ball_ui": model.atomic2.ball_ui}
+
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	sim = DEVSSimulator(model)
+	sim.setRealTime(True)
+	sim.setRealTimeInputFile(None)
+	sim.setRealTimePorts(refs)
+	sim.setVerbose(None)
+	sim.setRealTimePlatformTk(tkroot)
+
+	ui = UI(tkroot, sim)
+	model.atomic1.addMyOwnOutputListener(OutputListener(ui))
+	model.atomic2.addMyOwnOutputListener(OutputListener(ui))
+	sim.simulate()
+	tkroot.mainloop()

+ 562 - 0
examples/SimpleElevatorBalls/PyDEVS/target.py

@@ -0,0 +1,562 @@
+"""
+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 *
+from sccd.runtime.libs.ui import ui
+import random
+import time
+
+CANVAS_WIDTH = 800
+CANVAS_HEIGHT = 550
+
+# package "Elevator Balls"
+
+class MainAppInstance(RuntimeClassBase):
+    def __init__(self, atomdevs):
+        RuntimeClassBase.__init__(self, atomdevs)
+        self.associations = {}
+        self.associations["elevator"] = Association("Elevator", 0, -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.floor_dimensions = None
+        
+        # call user defined constructor
+        MainAppInstance.user_defined_constructor(self)
+        self.inports["field_ui"] = ('field_ui', atomdevs.next_instance)
+    
+    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 /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_elevator
+        self.states["/create_elevator"] = State(3, "/create_elevator", self)
+        
+        # state /creating
+        self.states["/creating"] = State(4, "/creating", self)
+        
+        # state /waiting
+        self.states["/waiting"] = State(5, "/waiting", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_window"])
+        self.states[""].addChild(self.states["/creating_canvas"])
+        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_elevator"]])
+        _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_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_WIDTH, CANVAS_HEIGHT, "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']]))
+    
+    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_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.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 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)
+        self.inports["elevator_ui"] = ('elevator_ui', atomdevs.next_instance)
+    
+    def user_defined_constructor(self, canvas_id):
+        self.canvas_id = canvas_id;
+        self.dim = {'x': 80, 'y': 150};
+        self.vel = {'x': 0, 'y': -2};
+        self.pos = {'x': 400, '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.03)
+    
+    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_HEIGHT:
+            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.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("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.canvas_id = None
+        self.pos = None
+        
+        # call user defined constructor
+        BallInstance.user_defined_constructor(self, canvas_id, x, y)
+        self.inports["ball_ui"] = ('ball_ui', atomdevs.next_instance)
+    
+    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):
+        # Invert velocity when colliding with canvas border:
+        if self.pos['x']-self.r <= self.rect_pos['x'] - (self.rect_dim['x'] / 2) or self.pos['x']+self.r >= self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+            self.vel['x'] = -self.vel['x'] + self.rect_vel['x'];
+        if self.pos['y']-self.r <= self.rect_pos['y'] - (self.rect_dim['y'] / 2) or self.pos['y']+self.r >= self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+            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_WIDTH-self.r)
+        y = min(max(0+self.r, y), CANVAS_HEIGHT-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.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["Elevator"] = self.addOutPort()
+        self.output["Ball"] = self.addOutPort()
+
+class Controller(CoupledDEVS):
+    def __init__(self, name):
+        CoupledDEVS.__init__(self, name)
+        self.ui = self.addInPort("ui")
+        self.addOutPort("ui")
+        self.objectmanager = self.addSubModel(ObjectManager("ObjectManager"))
+        self.atomic0 = self.addSubModel(MainApp("MainApp"))
+        self.atomic1 = self.addSubModel(Elevator("Elevator"))
+        self.atomic2 = 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["elevator"], self.atomic1.input)
+        self.connectPorts(self.atomic1.obj_manager_out, self.objectmanager.input)
+        self.connectPorts(self.objectmanager.output["Elevator"], self.atomic1.obj_manager_in)
+        self.connectPorts(self.atomic1.outputs["balls"], self.atomic2.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["Ball"], self.atomic2.obj_manager_in)
+        self.connectPorts(self.atomic2.outputs["parent"], self.atomic1.input)

+ 26 - 0
examples/SimpleElevatorBalls/Python/runner.py

@@ -0,0 +1,26 @@
+import tkinter as tk
+import target as target
+from sccd.runtime.libs.ui_v2 import UI
+from sccd.runtime.tkinter_eventloop import TkEventLoop
+
+class OutputListener:
+	def __init__(self, ui):
+		self.ui = ui
+
+	def add(self, event):
+		if event.port == "ui":
+			method = getattr(self.ui, event.name)
+			method(*event.parameters)
+
+if __name__ == '__main__':
+	tkroot = tk.Tk()
+	tkroot.withdraw()
+	controller = target.Controller(TkEventLoop(tkroot))
+	ui = UI(tkroot, controller)
+	controller.addMyOwnOutputListener(OutputListener(ui))
+
+	controller.setVerbose(None)
+	
+	controller.start()
+	tkroot.mainloop()
+	

+ 520 - 0
examples/SimpleElevatorBalls/Python/target.py

@@ -0,0 +1,520 @@
+"""
+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:   Elevator Balls
+
+"""
+
+from sccd.runtime.statecharts_core import *
+from sccd.runtime.libs.ui import ui
+import random
+import time
+
+CANVAS_WIDTH = 800
+CANVAS_HEIGHT = 550
+
+# package "Elevator Balls"
+
+class MainApp(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["field_ui"] = controller.addInputPort("field_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.floor_dimensions = None
+        
+        # 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 /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_elevator
+        self.states["/create_elevator"] = State(3, "/create_elevator", self)
+        
+        # state /creating
+        self.states["/creating"] = State(4, "/creating", self)
+        
+        # state /waiting
+        self.states["/waiting"] = State(5, "/waiting", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/creating_window"])
+        self.states[""].addChild(self.states["/creating_canvas"])
+        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_elevator"]])
+        _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_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_WIDTH, CANVAS_HEIGHT, "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']]))
+    
+    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_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 Elevator(RuntimeClassBase):
+    def __init__(self, controller, canvas_id):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["elevator_ui"] = controller.addInputPort("elevator_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
+        Elevator.user_defined_constructor(self, canvas_id)
+    
+    def user_defined_constructor(self, canvas_id):
+        self.canvas_id = canvas_id;
+        self.dim = {'x': 80, 'y': 150};
+        self.vel = {'x': 0, 'y': -2};
+        self.pos = {'x': 400, '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.03)
+    
+    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_HEIGHT:
+            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 Ball(RuntimeClassBase):
+    def __init__(self, controller, canvas_id, x, y):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.inports["ball_ui"] = controller.addInputPort("ball_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.canvas_id = None
+        self.pos = None
+        
+        # call user defined constructor
+        Ball.user_defined_constructor(self, canvas_id, x, y)
+    
+    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):
+        # Invert velocity when colliding with canvas border:
+        if self.pos['x']-self.r <= self.rect_pos['x'] - (self.rect_dim['x'] / 2) or self.pos['x']+self.r >= self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+            self.vel['x'] = -self.vel['x'] + self.rect_vel['x'];
+        if self.pos['y']-self.r <= self.rect_pos['y'] - (self.rect_dim['y'] / 2) or self.pos['y']+self.r >= self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+            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_WIDTH-self.r)
+        y = min(max(0+self.r, y), CANVAS_HEIGHT-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 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 = {}
+            instance.associations["elevator"] = Association("Elevator", 0, -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.associations = {}
+            instance.associations["parent"] = Association("Field", 1, 1)
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(EventLoopControllerBase):
+    def __init__(self, event_loop_callbacks, finished_callback = None, behind_schedule_callback = None):
+        if finished_callback == None: finished_callback = None
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        EventLoopControllerBase.__init__(self, ObjectManager(self), event_loop_callbacks, finished_callback, behind_schedule_callback)
+        self.addInputPort("ui")
+        self.addOutputPort("ui")
+        self.object_manager.createInstance("MainApp", [])

+ 390 - 0
examples/SimpleElevatorBalls/sccd.xml

@@ -0,0 +1,390 @@
+<?xml version="1.0" ?>
+<diagram author="Sam Pieters" name="Elevator Balls">
+    <top>
+        from sccd.runtime.libs.ui import ui
+        import random
+        import time
+
+        CANVAS_WIDTH = 800
+        CANVAS_HEIGHT = 550
+    </top>
+    <inport name="ui" />
+    <outport name="ui" />
+    <class name="MainApp" default="true">
+        <relationships>
+            <association name="elevator" class="Elevator" />
+        </relationships>
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="floor_dimensions" />
+        <atrribute name="floor_height" />
+        <inport name="field_ui"/>
+        <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='"Bouncing Balls Elevator"'/><!-- title -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </onentry>
+                <transition event="window_created" target="../creating_canvas">
+                    <parameter name="window_id" type="int" />
+                    <script>
+                        self.window_id = window_id
+                    </script>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.WINDOW_CLOSE"/><!-- tk_event -->
+                        <parameter expr="'window_close'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                    <raise port="ui" event="bind_event">
+                        <parameter expr="window_id"/><!-- widget_id -->
+                        <parameter expr="ui.EVENTS.KEY_PRESS"/><!-- tk_event -->
+                        <parameter expr="'key_press'"/><!-- sccd_event_name -->
+                        <parameter expr="self.inports['field_ui']"/><!-- inport for response -->
+                    </raise>
+                </transition>
+            </state>
+            <state id="creating_canvas">
+                <onentry>
+                    <raise port="ui" event="create_canvas">
+                        <parameter expr="self.window_id"/><!-- window_id -->
+                        <parameter expr="CANVAS_WIDTH"/><!-- width -->
+                        <parameter expr="CANVAS_HEIGHT - 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">
+                    <parameter name="canvas_id" type="int"/>
+                    <script>
+                        self.canvas_id = canvas_id
+                    </script>
+                </transition>
+            </state>
+            <state id="create_elevator">
+                <transition target="../creating">
+                    <raise scope="cd" event="create_instance">
+                        <parameter expr='"elevator"' />
+                        <parameter expr='"Elevator"' />
+                        <parameter expr="self.canvas_id" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="creating">
+                <transition event="instance_created" target="../waiting">
+                    <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" />
+                        <parameter expr="self.canvas_id" />
+                        <parameter expr="self.window_id" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="waiting">
+            </state>
+        </scxml>
+    </class>
+
+    <class name="Elevator">
+        <attribute name="window_id" />
+        <attribute name="canvas_id" />
+        <attribute name="elevator_id" />
+        <atrribute name="pos" />
+        <inport name="elevator_ui"/>
+        <relationships>
+            <association name="balls" class="Ball" />
+            <association name="parent" class="MainApp" min="1" max="1" />
+        </relationships>
+        <constructor>
+            <parameter name="canvas_id" />
+            <body>
+                <![CDATA[
+                self.canvas_id = canvas_id;
+                self.dim = {'x': 80, 'y': 150};
+                self.vel = {'x': 0, 'y': -2};
+                self.pos = {'x': 400, 'y': 75};
+                self.smooth = 0.6; # value between 0 and 1
+                ]]>
+            </body>
+        </constructor>
+        <scxml initial="root">
+            <state id="root" initial="waiting">
+                <state id="waiting">
+                    <transition event="set_association_name" target="../creating_elevator">
+                        <parameter name="association_name" type="str" />
+                        <parameter name="canvas_id" type="int" />
+                        <parameter name="window_id" type="int" />
+                        <script>
+                            self.association_name = association_name
+                            self.canvas_id = canvas_id
+                            self.window_id = window_id
+                        </script>
+                    </transition>
+                </state>
+                <state id="creating_elevator">
+                    <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['elevator_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="rectangle_created" target="../running">
+                        <parameter name="canvas_id" type="int" />
+                        <parameter name="rect_id" type="int" />
+                        <script>
+                            self.elevator_id = rect_id
+                        </script>
+                        <raise port="ui" event="bind_event">
+                            <parameter expr="canvas_id"/><!-- widget_id -->
+                            <parameter expr="ui.EVENTS.MOUSE_RIGHT_CLICK"/><!-- tk_event -->
+                            <parameter expr="'right_click'"/><!-- sccd_event_name -->
+                            <parameter expr="self.inports['elevator_ui']"/><!-- inport for response -->
+                        </raise>
+                    </transition>
+                </state>
+                <state id="running">
+                    <transition after="0.03" 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:
+                                self.vel['y'] = -self.vel['y'];
+                            ]]>
+                        </script>
+                        <raise port="ui" event="move_element">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.elevator_id"/>
+                            <parameter expr="self.vel['x']"/>
+                            <parameter expr="self.vel['y']"/>
+                        </raise>
+                        <script>
+                            self.pos['x'] += self.vel['x']
+                            self.pos['y'] += self.vel['y']
+                        </script>
+                        <raise scope="narrow" event="update_bounds" target="'balls'">
+                            <parameter expr="self.pos" />
+                            <parameter expr="self.dim" />
+                            <parameter expr="self.vel" />
+                        </raise>
+                    </transition>
+                    <transition port="elevator_ui" event="right_click" target=".">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <raise scope="cd" event="create_instance">
+                            <parameter expr='"balls"' />
+                            <parameter expr='"Ball"' />
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="x" />
+                            <parameter expr="y" />
+                        </raise>
+                    </transition>
+                    <transition event="instance_created" target=".">
+                        <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="Ball">
+        <attribute name="canvas_id" />
+        <atrribute name="circle_id" />
+        <attribute name="pos" />
+        <inport name="ball_ui" />
+        <relationships>
+            <association name="parent" class="Elevator" min="1" max="1" />
+        </relationships>
+        <constructor>
+            <parameter name="canvas_id" />
+            <parameter name="x" />
+            <parameter name="y" />
+            <body>
+                <![CDATA[
+                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
+                ]]>
+            </body>
+        </constructor>
+        <destructor>
+        </destructor>
+        <scxml initial="main_behaviour">
+            <state id="main_behaviour" initial="initializing">
+                <state id="initializing">
+                    <transition event="set_association_name" target="../creating_circle">
+                        <parameter name="association_name" type="str" />
+                        <script>
+                            self.association_name = association_name
+                        </script>
+                    </transition>
+                </state>
+                <state id="creating_circle">
+                    <onentry>
+                        <raise port="ui" event="create_circle">
+                            canvas_id, x, y, r, style, res_port
+                            <parameter expr="self.canvas_id"/><!-- canvas_id -->
+                            <parameter expr="self.pos['x']"/><!-- x -->
+                            <parameter expr="self.pos['y']"/><!-- y -->
+                            <parameter expr="self.r"/><!-- r -->
+                            <parameter expr="{'fill':'#000'}"/><!-- style -->
+                            <parameter expr="self.inports['ball_ui']"/><!-- inport for response -->
+                        </raise>
+                    </onentry>
+                    <transition event="circle_created" target="../bouncing">
+                        <parameter name="canvas_id"/>
+                        <parameter name="circle_id"/>
+                        <script>
+                            self.circle_id = circle_id
+                        </script>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_PRESS"/>
+                            <parameter expr="'mouse_press'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_MOVE"/>
+                            <parameter expr="'mouse_move'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                        <raise port="ui" event="bind_canvas_event">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="circle_id"/>
+                            <parameter expr="ui.EVENTS.MOUSE_RELEASE"/>
+                            <parameter expr="'mouse_release'"/>
+                            <parameter expr="self.inports['ball_ui']"/>
+                        </raise>
+                    </transition>
+                </state>
+                <state id="bouncing">
+                    <transition after="0.02" target=".">
+                        <script>
+                            <![CDATA[
+                            # Invert velocity when colliding with canvas border:
+                            if self.pos['x']-self.r <= self.rect_pos['x'] - (self.rect_dim['x'] / 2) or self.pos['x']+self.r >= self.rect_pos['x'] + (self.rect_dim['x'] / 2):
+                                self.vel['x'] = -self.vel['x'] + self.rect_vel['x'];
+                            if self.pos['y']-self.r <= self.rect_pos['y'] - (self.rect_dim['y'] / 2) or self.pos['y']+self.r >= self.rect_pos['y'] + (self.rect_dim['y'] / 2):
+                                self.vel['y'] = -self.vel['y'] + self.rect_vel['y'];
+                            ]]>
+                        </script>
+                        <raise port="ui" event="move_element">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="self.vel['x']"/>
+                            <parameter expr="self.vel['y']"/>
+                        </raise>
+                        <script>
+                            self.pos['x'] += self.vel['x']
+                            self.pos['y'] += self.vel['y']
+                        </script>
+                    </transition>
+                    <transition port="ball_ui" event="mouse_press" target="../selected" cond="button == ui.MOUSE_BUTTONS.LEFT">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <raise port="ui" event="set_element_color">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="'#ff0'"/>
+                        </raise>
+                    </transition>
+                    <transition event="update_bounds" target=".">
+                        <parameter name="pos" type="dict" />
+                        <parameter name="dim" type="dict" />
+                        <parameter name="vel" type="dict" />
+                        <script>
+                            self.rect_pos = pos
+                            self.rect_dim = dim
+                            self.rect_vel = vel
+                        </script>
+                    </transition>
+                </state>
+                <state id="dragging">
+                    <transition port="ball_ui" event="mouse_move" target=".">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <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)
+
+                            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}
+                            ]]>
+                        </script>
+                        <raise port="ui" event="set_element_pos">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="x-self.r"/>
+                            <parameter expr="y-self.r"/>
+                        </raise>
+                    </transition>
+                    <transition port="ball_ui" event="mouse_release" target="../bouncing">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <raise port="ui" event="set_element_color">
+                            <parameter expr="self.canvas_id"/>
+                            <parameter expr="self.circle_id"/>
+                            <parameter expr="'#f00'"/>
+                        </raise>
+                    </transition>
+                </state>
+                <state id='selected'>
+                    <transition port="ball_ui" event="mouse_press" target="../dragging" cond="button == ui.MOUSE_BUTTONS.LEFT">
+                        <parameter name="x" />
+                        <parameter name="y" />
+                        <parameter name="button" />
+                        <script>
+                            <![CDATA[
+                            self.mouse_pos = {'x':x, 'y':y};
+                            ]]>
+                        </script>
+                    </transition>
+                    <transition event="delete_self" target='../../deleted'>                    
+                        <raise event="delete_ball" scope="narrow" target="'parent'">
+                            <parameter expr='self.association_name' />
+                        </raise>
+                        <raise port="ui" event="destroy_element">
+                            <parameter expr="self.canvas_id" />
+                            <parameter expr="self.element_id" />
+                        </raise>
+                    </transition>
+                </state>
+            </state>
+            <state id='deleted' />
+        </scxml>
+    </class>
+</diagram>

+ 0 - 259
examples/Timer/PyDEVS/target.py

@@ -1,259 +0,0 @@
-"""
-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: Simon Van Mierlo
-Model name:   Timer (Eventloop Version)
-
-"""
-
-from sccd.runtime.DEVS_statecharts_core import *
-from pypdevs.DEVS import *
-from pypdevs.infinity import *
-from pypdevs.simulator import *
-from sccd.runtime.libs.ui import ui
-from time import time
-
-# package "Timer (Eventloop Version)"
-
-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)
-    
-    def user_defined_constructor(self):
-        self.canvas = ui.append_canvas(ui.window,100,100,{'background':'#222222'})
-        self.clock_text = self.canvas.element.create_text(25,25,{'text':'0.0'})
-        self.actual_clock_text = self.canvas.element.create_text(25,50,{'text':'0.0'})
-        interrupt_button = ui.append_button(ui.window, 'INTERRUPT');
-        continue_button = ui.append_button(ui.window, 'CONTINUE');
-        ui.bind_event(interrupt_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'interrupt_clicked');
-        ui.bind_event(continue_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'continue_clicked');
-    
-    def user_defined_destructor(self):
-        pass
-    
-    
-    # user defined method
-    def update_timers(self):
-        self.canvas.element.itemconfigure(self.clock_text, text=str('%.2f' % (self.getSimulatedTime() / 1000.0)))
-        self.canvas.element.itemconfigure(self.actual_clock_text, text='%.2f' % (time() / 1000.0))
-    
-    
-    # builds Statechart structure
-    def build_statechart_structure(self):
-        
-        # state <root>
-        self.states[""] = State(0, "", self)
-        
-        # state /running
-        self.states["/running"] = State(1, "/running", self)
-        self.states["/running"].setEnter(self._running_enter)
-        self.states["/running"].setExit(self._running_exit)
-        
-        # state /interrupted
-        self.states["/interrupted"] = State(2, "/interrupted", self)
-        
-        # add children
-        self.states[""].addChild(self.states["/running"])
-        self.states[""].addChild(self.states["/interrupted"])
-        self.states[""].fixTree()
-        self.states[""].default_state = self.states["/running"]
-        
-        # transition /running
-        _running_0 = Transition(self, self.states["/running"], [self.states["/running"]])
-        _running_0.setAction(self._running_0_exec)
-        _running_0.setTrigger(Event("_0after"))
-        self.states["/running"].addTransition(_running_0)
-        _running_1 = Transition(self, self.states["/running"], [self.states["/interrupted"]])
-        _running_1.setAction(self._running_1_exec)
-        _running_1.setTrigger(Event("interrupt_clicked", self.getInPortName("ui")))
-        self.states["/running"].addTransition(_running_1)
-        
-        # transition /interrupted
-        _interrupted_0 = Transition(self, self.states["/interrupted"], [self.states["/interrupted"]])
-        _interrupted_0.setAction(self._interrupted_0_exec)
-        _interrupted_0.setTrigger(Event("interrupt_clicked", self.getInPortName("ui")))
-        self.states["/interrupted"].addTransition(_interrupted_0)
-        _interrupted_1 = Transition(self, self.states["/interrupted"], [self.states["/running"]])
-        _interrupted_1.setAction(self._interrupted_1_exec)
-        _interrupted_1.setTrigger(Event("continue_clicked", self.getInPortName("ui")))
-        self.states["/interrupted"].addTransition(_interrupted_1)
-    
-    def _running_enter(self):
-        self.addTimer(0, 0.05)
-    
-    def _running_exit(self):
-        self.removeTimer(0)
-    
-    def _running_0_exec(self, parameters):
-        self.update_timers()
-    
-    def _running_1_exec(self, parameters):
-        self.update_timers()
-    
-    def _interrupted_0_exec(self, parameters):
-        self.update_timers()
-    
-    def _interrupted_1_exec(self, parameters):
-        self.update_timers()
-    
-    def initializeStatechart(self):
-        # enter default state
-        self.default_targets = self.states["/running"].getEffectiveTargetStates()
-        RuntimeClassBase.initializeStatechart(self)
-
-class MainApp(AtomicDEVS, ObjectManagerBase):
-    def __init__(self, name):
-        AtomicDEVS.__init__(self, name)
-        ObjectManagerBase.__init__(self)
-        self.elapsed = 0
-        self.name = "MainApp"
-        self.obj_manager_out = self.addOutPort("obj_manager_out")
-        self.outputs = {}
-        self.obj_manager_in = self.addInPort("obj_manager_in")
-        self.input = self.addInPort("input")
-        self.instances.append(MainAppInstance(self))
-        self.next_time = INFINITY
-    
-    def extTransition(self, inputs):
-        self.simulated_time = (self.simulated_time + self.elapsed)
-        self.next_time = 0
-        all_inputs = []
-        if self.obj_manager_in in inputs:
-            all_inputs.extend(inputs[self.obj_manager_in])
-        if self.input in inputs:
-            all_inputs.extend(inputs[self.input])
-        for input in all_inputs:
-            if isinstance(input, str):
-                tem = eval(input)
-                self.addInput(tem)
-            elif input[2].name == "create_instance":
-                new_instance = MainAppInstance(self)
-                self.instances.append(new_instance)
-                p = new_instance.associations.get("parent")
-                if p:
-                    p.addInstance(input[2].instance)
-                ev = Event("instance_created", None, [f"{input[2].parameters[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[1], input[0], ev))
-            elif input[2].name == "start_instance":
-                instance = self.instances[input[2].instance]
-                instance.start()
-                ev = Event("instance_started", None, [f"{input[0]}[{len(self.instances)-1}]"], input[2].instance)
-                self.to_send.append((input[0], input[1], ev))
-            elif input[2].name == "delete_instance":
-                pass
-            elif input[2].name == "associate_instance":
-                pass
-            elif input[2].name == "disassociate_instance":
-                pass
-            elif input[2].name == "instance_created":
-                instance = self.instances[input[2].instance]
-                instance.addEvent(input[2])
-            elif input[2].name == "instance_started":
-                instance = self.instances[input[2].instance]
-                instance.addEvent(input[2])
-            elif input[2].name == "instance_deleted":
-                pass
-            elif input[2].name == "instance_associated":
-                pass
-            elif input[2].name == "instance_disassociated":
-                pass
-            else:
-                ev = input[2]
-                self.addInput(ev)
-        return self.instances
-    
-    def intTransition(self):
-        earliest = min(self.getEarliestEventTime(), self.simulated_time + self.input_queue.getEarliestTime())
-        if not (earliest == INFINITY):
-            self.simulated_time = earliest
-        self.to_send = []
-        self.handleInput()
-        self.stepAll()
-        next_earliest = min(self.getEarliestEventTime(), self.input_queue.getEarliestTime())
-        if not (len(self.to_send) == 0):
-            self.next_time = 0
-        elif next_earliest == INFINITY:
-            self.next_time = INFINITY
-        else:
-            self.next_time = next_earliest - earliest
-        return self.instances
-    
-    def outputFnc(self):
-        to_dict = {}
-        for sending in self.to_send:
-            if sending[2].port == None:
-                if self.obj_manager_out in to_dict:
-                    to_dict[self.obj_manager_out].append(sending)
-                else:
-                    to_dict[self.obj_manager_out] = [sending]
-            else:
-                the_port = None
-                for port in self.OPorts:
-                    if port.name == sending[0]:
-                        the_port = port
-                if the_port in to_dict:
-                    to_dict[the_port].append(sending)
-                else:
-                    to_dict[the_port] = [sending]
-        return to_dict
-    
-    def timeAdvance(self):
-        return self.next_time
-
-class ObjectManagerState:
-    def __init__(self):
-        self.to_send = [(None, "MainApp", Event("start_instance", None, [0], 0))]
-
-class ObjectManager(AtomicDEVS):
-    def __init__(self, name):
-        AtomicDEVS.__init__(self, name)
-        self.State = ObjectManagerState()
-        self.input = self.addInPort("input")
-        self.output = {}
-        self.output["MainApp"] = self.addOutPort()
-    
-    def extTransition(self, inputs):
-        all_inputs = inputs[self.input]
-        for input in all_inputs:
-            self.State.to_send.append(input)
-        return self.State
-    
-    def intTransition(self):
-        self.State.to_send = []
-        return self.State
-    
-    def outputFnc(self):
-        out_dict = {}
-        for (source, target, message) in self.State.to_send:
-            if self.output[target] in out_dict:
-                out_dict[self.output[target]].append((source, target, message))
-            else:
-                out_dict[self.output[target]] = [(source, target, message)]
-        return out_dict
-    
-    def timeAdvance(self):
-        if self.State.to_send:
-            return 0
-        return INFINITY
-
-class Controller(CoupledDEVS):
-    def __init__(self, name):
-        CoupledDEVS.__init__(self, name)
-        self.ui = self.addInPort("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)

+ 0 - 12
examples/Timer/Python/runner.py

@@ -1,12 +0,0 @@
-import tkinter as tk
-import target as target
-from sccd.runtime.libs.ui import ui
-from sccd.runtime.statecharts_core import Event
-from sccd.runtime.tkinter_eventloop import *
-
-if __name__ == '__main__':
-	ui.window = tk.Tk()
-
-	controller = target.Controller(TkEventLoop(ui.window))
-	controller.start()
-	ui.window.mainloop()

+ 0 - 57
examples/Timer/sccd.xml

@@ -1,57 +0,0 @@
-<?xml version="1.0" ?>
-<diagram author="Simon Van Mierlo" name="Timer (Eventloop Version)">
-    <top>
-        from sccd.runtime.libs.ui import ui
-        from time import time
-    </top>
-    
-    <inport name="ui" />
-
-    <class name="MainApp" default="true">
-        <method name="MainApp">
-            <body>
-                <![CDATA[
-                self.canvas = ui.append_canvas(ui.window,100,100,{'background':'#222222'})
-                self.clock_text = self.canvas.element.create_text(25,25,{'text':'0.0'})
-                self.actual_clock_text = self.canvas.element.create_text(25,50,{'text':'0.0'})
-                interrupt_button = ui.append_button(ui.window, 'INTERRUPT');
-                continue_button = ui.append_button(ui.window, 'CONTINUE');
-                ui.bind_event(interrupt_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'interrupt_clicked');
-                ui.bind_event(continue_button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'continue_clicked');
-                ]]>
-            </body>        
-        </method>
-        <method name="update_timers">
-            <body>
-                self.canvas.element.itemconfigure(self.clock_text, text=str('%.2f' % (self.getSimulatedTime() / 1000.0)))
-                self.canvas.element.itemconfigure(self.actual_clock_text, text='%.2f' % (time() / 1000.0))
-            </body>
-        </method>
-        <scxml initial="running">
-            <state id="running">
-                <transition target="." after="0.05">
-                    <script>
-                        self.update_timers()
-                    </script>
-                </transition>
-                <transition target="../interrupted" event="interrupt_clicked" port="ui">
-                    <script>
-                        self.update_timers()
-                    </script>
-                </transition>
-            </state>
-            <state id="interrupted">
-                <transition target="." event="interrupt_clicked" port="ui">
-                    <script>
-                        self.update_timers()
-                    </script>
-                </transition>
-                <transition target="../running" event="continue_clicked" port="ui">
-                    <script>
-                        self.update_timers()
-                    </script>
-                </transition>
-            </state>
-        </scxml>
-    </class>
-</diagram>

+ 6 - 6
examples/Train/Python/runner.py

@@ -40,12 +40,12 @@ class SimulationGUI(Tk):
         self.frame.focus_set()
         self.resizable(0, 0)
         self.canvas = Canvas(self.frame, height=150, width=1000, bg="white")
-        self.img_train = PhotoImage(file="imgs/train.gif")
-        self.img_red = PhotoImage(file="imgs/red.gif")
-        self.img_yellow = PhotoImage(file="imgs/yellow.gif")
-        self.img_green = PhotoImage(file="imgs/green.gif")
-        self.img_station = PhotoImage(file="imgs/station.gif")
-        self.img_railway = PhotoImage(file="imgs/rail.gif")
+        self.img_train = PhotoImage(file="./examples/Train/imgs/train.gif")
+        self.img_red = PhotoImage(file="./examples/Train/imgs/red.gif")
+        self.img_yellow = PhotoImage(file="./examples/Train/imgs/yellow.gif")
+        self.img_green = PhotoImage(file="./examples/Train/imgs/green.gif")
+        self.img_station = PhotoImage(file="./examples/Train/imgs/station.gif")
+        self.img_railway = PhotoImage(file="./examples/Train/imgs/rail.gif")
 
         self.label_error = Label(self.frame, text="")
         self.slider_acceleration = Scale(self.frame, command=update_scale, orient=HORIZONTAL, resolution=0.01, from_=-1, to=1)

+ 29 - 31
sccd/runtime/tracers/tracerVerbose.py

@@ -15,11 +15,10 @@ class TracerVerbose(BaseTracer):
         :param filename: file to save the trace to, can be None for output to stdout
         """
         super(TracerVerbose, self).__init__()
-        #if server.getName() == 0:
-        #    self.filename = filename
-        #else:
-        self.filename = None
-        self.prevtime = (-1, -1)
+        self.filename = filename
+        self.prevtime = -1
+        self.text = ""
+
 
     def startTracer(self, recover):
         """
@@ -48,42 +47,41 @@ class TracerVerbose(BaseTracer):
         :param text: the text that was traced
         """
         string = ""
-        #if time > self.prevtime:
-        string = ("\n__  Current Time: %10.6f " + "_"*42 + " \n") % (time / 1000)
+        if time > self.prevtime:
+            string = ("__  Current Time: %10.6f " + "_"*42 + " \n") % (time / 1000)
         self.prevtime = time
-
-        print(string)
-        #string += "%s\n" % text
-        #try:
-        #    self.verb_file.write(string)
-        #except TypeError:
-        #    self.verb_file.write(string.encode())
+        string += self.text + "\n"
+        try:
+            self.verb_file.write(string)
+        except TypeError:
+            self.verb_file.write(string.encode())
+        self.text = ""
     
     def traceExitState(self, StateChart, State):
-        text = "\n"
-        text += "\EXIT STATE in model <%s>\n" % StateChart
-        text += "\t\tState: %s\n" % str(State)
+        self.text += "\n"
+        self.text += "EXIT STATE in model <%s>\n" % StateChart
+        self.text += "\t\tState: %s\n" % str(State)
         # TODO: This should be different because can also be written to a file, the DEVS implementation uses a server which i'm not going to do
-        print(text)
+        #print(text)
 
     def traceEnterState(self, StateChart, State):
-        text = "\n"
-        text += "\ENTER STATE in model <%s>\n" % StateChart
-        text += "\t\tState: %s\n" % str(State)
-        print(text)
+        self.text += "\n"
+        self.text += "ENTER STATE in model <%s>\n" % StateChart
+        self.text += "\t\tState: %s\n" % str(State)
+        #print(text)
     
     def traceOutput(self, listener, event):
-        text = "\n"
-        text += "OUTPUT EVENT to port <%s>\n" % event.port
-        text += "\t\Event: %s\n" % str(event)
-        print(text)
+        self.text += "\n"
+        self.text += "OUTPUT EVENT to port <%s>\n" % event.port
+        self.text += "\t\Event: %s\n" % str(event)
+        #print(text)
 
     def traceInput(self, listener, event):
-        text = "\n"
-        text += "INPUT EVENT from port <%s>\n" % event.port
-        text += "\t\Type: %s\n" % listener.virtual_name
-        text += "\t\Event: %s\n" % str(event)
-        print(text)
+        self.text += "\n"
+        self.text += "INPUT EVENT from port <%s>\n" % event.port
+        self.text += "\t\Type: %s\n" % listener.virtual_name
+        self.text += "\t\Event: %s\n" % str(event)
+        #print(text)
 
     def traceTransition(self, aStateChart):
         """

+ 99 - 0
tracechecker/main.py

@@ -0,0 +1,99 @@
+import re
+
+def extract_float(text):
+    # Define a regex pattern to match the float value
+    pattern = r"Current Time:\s+([-+]?\d*\.\d+|\d+)"
+    
+    # Search for the pattern in the text
+    match = re.search(pattern, text)
+    
+    if match:
+        # Extract the matched float value
+        float_value = float(match.group(1))
+        return float_value
+    else:
+        # Handle the case where no match is found
+        return None
+
+def extract_type(text):
+    # Define a regex pattern to match the type value after 'Type:'
+    pattern = r"Type:\s*(\w+)"
+    
+    # Search for the pattern in the text
+    match = re.search(pattern, text)
+    
+    if match:
+        # Extract the matched type value
+        type_value = match.group(1)
+        return type_value
+    else:
+        # Handle the case where no match is found
+        return None
+    
+def extract_event_details(text):
+    #pattern = r"event name:\s*([^;]+);\s*port:\s*([^;]+)(?:;\s*parameters:\s*(\[.*\]))?"
+    pattern = r"event name:\s*([^;]+);\s*port:\s*([^\);]+)(?:;\s*parameters:\s*(\[.*\]))?"
+    match = re.search(pattern, text, re.IGNORECASE)
+    if match:
+        event_name = match.group(1).strip()
+        port = match.group(2).strip()
+        parameters = match.group(3).strip() if match.group(3) else 'None'
+        return event_name, port, parameters
+    else:
+        return None, None, None
+
+
+def generate_input_trace(input_trace, output_file):
+    # Open the file in read mode
+    with open(input_trace, 'r') as file:
+        # Read all lines into a list
+        lines = file.readlines()
+
+    # Open the file in write mode ('w')
+    with open(output_file, 'w') as file:
+        # Traverse each line in the file
+        i = 0
+        current_time = None
+        while i < len(lines):
+            if lines[i].startswith('__'):
+                current_time = extract_float(lines[i].strip())
+
+            # Check if the line starts with 'INPUT'
+            if lines[i].startswith('INPUT'):
+                # Print the line that starts with 'INPUT'
+                print(lines[i].strip())
+                # Check if the next two lines exist and print them
+                the_type = extract_type(lines[i + 1].strip())
+                name, port, parameters = extract_event_details(lines[i + 2].strip())
+
+                if name == None:
+                    name = 'None'
+                if port == None:
+                    port = 'None'
+                if parameters == None:
+                    parameters = 'None'
+
+
+                if the_type is not None:
+                    to_write = str(current_time) + " " + the_type + " Event(\"" + name + "\",\"" + port + "\"," + parameters + ")\n"
+                    file.write(to_write)            
+
+                # Skip the next two lines to avoid reprocessing them
+                i += 2
+            # Move to the next line
+            i += 1
+
+def compare_traces(SCCDTrace, DEVSTrace):
+    pass
+
+if __name__ == '__main__':
+    SCCDFile = "./examples/BouncingBalls/Python/trace.txt"
+    DEVSFile = "./examples/BouncingBalls/PyDEVS/trace.txt"
+
+    inputTrace = "./examples/BouncingBalls/input_trace.txt"
+
+    option = 1
+    if option == 1:
+        generate_input_trace(SCCDFile, inputTrace)
+    if option == 2:
+        compare_traces(SCCDFile, DEVSFile)