|
|
@@ -5,13 +5,15 @@ from sccd.controller.event_queue import *
|
|
|
from sccd.statechart.dynamic.event import *
|
|
|
from sccd.controller.object_manager import *
|
|
|
from sccd.util.debug import print_debug
|
|
|
-from sccd.cd.cd import *
|
|
|
+from sccd.cd.static.cd import *
|
|
|
|
|
|
def _dummy_output_callback(output_event):
|
|
|
pass
|
|
|
|
|
|
-# The Controller class is a primitive that can be used to build backends of any kind:
|
|
|
+# The Controller class' sole responsibility is running a model.
|
|
|
+# Its interface is a primitive that can be used to build backends of any kind:
|
|
|
# Threads, integration with existing event loop, game loop, test framework, ...
|
|
|
+# All methods take control of the current thread and are synchronous (blocking).
|
|
|
# The Controller class itself is NOT thread-safe.
|
|
|
class Controller:
|
|
|
__slots__ = ["cd", "object_manager", "queue", "simulated_time", "run_until"]
|
|
|
@@ -25,7 +27,6 @@ class Controller:
|
|
|
def __repr__(self):
|
|
|
return "QueueEntry("+str(self.event)+")"
|
|
|
|
|
|
-
|
|
|
def __init__(self, cd: AbstractCD, output_callback: Callable[[OutputEvent],None] = _dummy_output_callback):
|
|
|
cd.globals.assert_ready()
|
|
|
self.cd = cd
|
|
|
@@ -45,18 +46,18 @@ class Controller:
|
|
|
|
|
|
if DEBUG:
|
|
|
self.cd.print()
|
|
|
- print("Model delta is %s" % str(self.cd.globals.delta))
|
|
|
+ print("Model delta is %s" % str(self.cd.get_delta()))
|
|
|
|
|
|
- # First call to 'run_until' method initializes
|
|
|
+ # This is a 'hack', the attribute run_until First call to 'run_until' method initializes
|
|
|
self.run_until = self._run_until_w_initialize
|
|
|
|
|
|
-
|
|
|
- def get_model_delta(self) -> Duration:
|
|
|
- return self.cd.globals.delta
|
|
|
-
|
|
|
+ # Lower-level way of adding an event to the queue
|
|
|
+ # See also method 'add_input'
|
|
|
def schedule(self, timestamp: int, event: InternalEvent, instances: List[Instance]):
|
|
|
self.queue.add(timestamp, Controller.EventQueueEntry(event, instances))
|
|
|
|
|
|
+ # Low-level utility function, intended to map a port name to a list of instances
|
|
|
+ # For now, all known ports map to all instances (i.e. all ports are broadcast ports)
|
|
|
def inport_to_instances(self, port: str) -> List[Instance]:
|
|
|
try:
|
|
|
self.cd.globals.inports.get_id(port)
|
|
|
@@ -68,6 +69,8 @@ class Controller:
|
|
|
# TODO: multicast event only to instances that subscribe to this port.
|
|
|
return self.object_manager.instances
|
|
|
|
|
|
+ # Higher-level way of adding an event to the queue.
|
|
|
+ # See also method 'schedule'
|
|
|
def add_input(self, timestamp: int, port: str, event_name: str, params = []):
|
|
|
try:
|
|
|
event_id = self.cd.globals.events.get_id(event_name)
|
|
|
@@ -79,10 +82,11 @@ class Controller:
|
|
|
|
|
|
self.schedule(timestamp, event, instances)
|
|
|
|
|
|
- # Get timestamp of next entry in event queue
|
|
|
+ # Get timestamp of earliest entry in event queue
|
|
|
def next_wakeup(self) -> Optional[int]:
|
|
|
return self.queue.earliest_timestamp()
|
|
|
|
|
|
+ # Before calling _run_until, this method should be called instead, exactly once.
|
|
|
def _run_until_w_initialize(self, now: Optional[int]):
|
|
|
# first run...
|
|
|
# initialize the object manager, in turn initializing our default class
|
|
|
@@ -98,8 +102,10 @@ class Controller:
|
|
|
# Let's try it out :)
|
|
|
self.run_until(now)
|
|
|
|
|
|
- # Run until the event queue has no more due events wrt given timestamp and until all instances are stable.
|
|
|
- # If no timestamp is given (now = None), run until event queue is empty.
|
|
|
+ # Run until the event queue has no more events smaller than given timestamp.
|
|
|
+ # If no timestamp is given (now = None), the "given timestamp" is considered to be +infinity,
|
|
|
+ # meaning the controller will run until event queue is empty.
|
|
|
+ # This method is blocking.
|
|
|
def _run_until(self, now: Optional[int]):
|
|
|
# Actual "event loop"
|
|
|
for timestamp, entry in self.queue.due(now):
|