Browse Source

Added 'threads' platform

Joeri Exelmans 5 years ago
parent
commit
34b18167a0
3 changed files with 65 additions and 2 deletions
  1. 2 2
      src/sccd/realtime/eventloop.py
  2. 52 0
      src/sccd/realtime/threads_platform.py
  3. 11 0
      src/sccd/util/duration.py

+ 2 - 2
src/sccd/realtime/eventloop.py

@@ -19,7 +19,7 @@ class EventLoopImplementation(ABC):
     def cancel(self, id: ScheduledID):
         pass
 
-
+# Event loop "platform"
 class EventLoop:
     def __init__(self, controller: Controller, event_loop: EventLoopImplementation, time_impl: TimeImplementation = DefaultTimeImplementation):
         delta = controller.cd.get_delta()
@@ -43,7 +43,7 @@ class EventLoop:
         # back to sleep
         now = self.timer.now()
         next_wakeup = self.controller.next_wakeup()
-        if next_wakeup:
+        if next_wakeup is not None:
             sleep_duration = self.to_event_loop_unit(next_wakeup - now)
             if sleep_duration < 0:
                 self.purposefully_behind = now - next_wakeup

+ 52 - 0
src/sccd/realtime/threads_platform.py

@@ -0,0 +1,52 @@
+from sccd.realtime.time import *
+from sccd.controller.controller import *
+from sccd.util.duration import *
+import threading
+import queue
+
+# Thread-safe real-time Controller execution in a thread
+class ThreadsPlatform:
+
+    def __init__(self, controller: Controller):
+        self.controller = controller
+        self.timer = Timer(impl=DefaultTimeImplementation, unit=self.controller.cd.get_delta())
+        self.lock = threading.Lock()
+        self.condition = threading.Condition()
+
+    def create_controller_thread(self) -> threading.Thread:
+        def thread():
+            # condition object expects fractions of seconds
+            to_condition_wait_time = get_conversion_f_float(self.controller.cd.get_delta(), duration(1, Second))
+
+            # keep simulation responsive even if computer cannot keep up
+            purposefully_behind = 0
+
+            # simulation starts "now"
+            self.timer.start()
+
+            while True:
+                # simulate, then sleep:
+
+                with self.lock:
+                    self.controller.run_until(self.timer.now() + purposefully_behind) # may take a while
+                    next_wakeup = self.controller.next_wakeup()
+
+                if next_wakeup is not None:
+                    sleep_duration = next_wakeup - self.timer.now()
+                    if sleep_duration < 0:
+                        purposefully_behind = sleep_duration
+                    else:
+                        purposefully_behind = 0
+                    with self.condition:
+                        self.condition.wait(to_condition_wait_time(sleep_duration))
+                else:
+                    with self.condition:
+                        self.condition.wait()
+
+        return threading.Thread(target=thread)
+
+    def add_input(self, event_name, port, params=[]):
+        with self.lock:
+            self.controller.add_input(timestamp=self.timer.now(), event_name=event_name, port=port, params=params)
+        with self.condition:
+            self.condition.notify() # wake up controller thread if sleeping

+ 11 - 0
src/sccd/util/duration.py

@@ -255,3 +255,14 @@ def get_conversion_f(from_unit: Duration, to_unit: Duration) -> Callable[[int],
   else:
     factor = to_unit // from_unit
     return lambda x: x // factor
+
+def get_conversion_f_float(from_unit: Duration, to_unit: Duration) -> Callable[[Union[int,float]], Union[int,float]]:
+  if from_unit is _zero or to_unit is _zero:
+    raise Exception("Cannot convert between zero-duration units")
+    
+  if from_unit > to_unit:
+    factor = from_unit // to_unit
+    return lambda x: x * factor
+  else:
+    factor = to_unit // from_unit
+    return lambda x: x / factor