Browse Source

Support fixed model delta.

Joeri Exelmans 5 years ago
parent
commit
a66d3b03f8

+ 4 - 3
src/sccd/controller/controller.py

@@ -33,6 +33,7 @@ class Controller:
         self.initialized = False
 
         self.model.context.assert_ready()
+        print_debug("model delta is %s" % str(self.model.context.delta))
 
     # time_offset: the offset relative to the current simulated time
     # (the timestamp given in the last call to run_until)
@@ -62,7 +63,7 @@ class Controller:
 
     # Returns duration since start
     def get_simulated_duration(self) -> Duration:
-        return self.model.context.delta * self.simulated_time
+        return (self.model.context.delta * self.simulated_time).normalize()
 
     # 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.
@@ -108,7 +109,7 @@ class Controller:
         if not self.initialized:
             self.initialized = True
 
-            print_debug('time is now %s' % str(self.get_simulated_duration()))
+            print_debug("time is now %s" % str(self.get_simulated_duration()))
             # first run...
             # initialize the object manager, in turn initializing our default class
             # and adding the generated events to the queue
@@ -135,7 +136,7 @@ class Controller:
                         return
                     # make time leap
                     self.simulated_time = timestamp
-                    print_debug('\ntime is now %s' % str(self.get_simulated_duration()))
+                    print_debug("\ntime is now %s" % str(self.get_simulated_duration()))
                 # run all instances for whom there are events
                 for instance in entry.targets:
                     stable, output = instance.big_step(timestamp, [entry.event])

+ 20 - 10
src/sccd/model/context.py

@@ -6,29 +6,39 @@ from sccd.util.duration import *
 
 # @dataclass
 class Context:
-  def __init__(self):
+  # max_delta: upper bound on model delta
+  def __init__(self, fixed_delta: Optional[Duration] = Duration(100, Microsecond)):
+    self.fixed_delta = fixed_delta
+
     self.events = Namespace()
     self.inports = Namespace()
     self.outports = Namespace()
     self.durations: List[DurationLiteral] = []
 
-    # The smallest unit for all durations in the model
+    # The smallest unit for all durations in the model.
+    # Calculated after all expressions have been parsed, based on all DurationLiterals.
     self.delta: Optional[Duration] = None
 
+  # Convert all DurationLiterals to model delta
   def _conv(self):
     for d in self.durations:
-      if d.original % self.delta != Duration(0):
-        print("Warning: Duration %s cannot be represented by delta %s" % (str(d.original), str(self.delta)))
+      # The following error is impossible: (i think)
+      # if d.original % self.delta != Duration(0):
+      #   raise Exception("Duration %s cannot be represented by delta %s" % (str(d.original), str(self.delta)))
       d.converted = d.original // self.delta
 
-  def convert_durations_fixed_delta(self, delta: Duration):
-    self.delta = delta
-    self._conv()
-    
-  def convert_durations_auto_delta(self):
+  def process_durations(self):
     self.delta = gcd(*(d.original for d in self.durations))
+
+    # Ensure delta not too big
+    if self.fixed_delta:
+      if self.delta < self.fixed_delta:
+        raise Exception("Model contains duration deltas (smallest = %s) than representable with delta given (%s)." % (str(self.delta), str(self.fixed_delta)))
+      else:
+        self.delta = self.fixed_delta
+
     self._conv()
 
   def assert_ready(self):
     if self.delta is None:
-      raise Exception("Context not ready: durations not yet converted.")
+      raise Exception("Context not ready: durations not yet processed.")

+ 2 - 2
src/sccd/test/xml_loader.py

@@ -36,7 +36,7 @@ class PseudoFailedTest(unittest.TestCase):
 def load_test(src_file) -> List[Test]:
   should_fail = os.path.basename(src_file).startswith("fail_")
 
-  context = Context()
+  context = Context(fixed_delta = None)
 
   test_node = etree.parse(src_file).getroot()
 
@@ -58,7 +58,7 @@ def load_test(src_file) -> List[Test]:
     input = load_input(input_node)
     output = load_output(output_node)
 
-    context.convert_durations_auto_delta()
+    context.process_durations()
 
     def variant_description(i, variant) -> str:
       if not variant:

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

@@ -121,6 +121,10 @@ class Duration:
     # else:
       # return Duration(self.val%other, self.unit)
 
+  def __lt__(self, other):
+    self_conv, other_conv = same_unit(self, other)
+    return self_conv.val < other_conv.val
+
 def same_unit(x: Duration, y: Duration) -> Tuple[Duration, Duration]:
   if x.unit.relative_size >= y.unit.relative_size:
     x_conv = x.convert(y.unit)