Parcourir la source

Fixed 'after' triggers + render them in SVG

Joeri Exelmans il y a 5 ans
Parent
commit
52ad9ebcbd

+ 16 - 11
src/sccd/runtime/statechart_instance.py

@@ -34,7 +34,7 @@ class StatechartInstance(Instance):
         self._combo_step = ComboStepState()
         self._small_step = SmallStepState()
 
-        self.ignore_events = Counter() # Mapping from event name to future times to ignore such event. Used for canceling timers.
+        self.next_timer_id = 0 # each time a timer is started, a new unique future event is created for ourselves.
 
     # enter default states, generating a set of output events
     def initialize(self, now: Timestamp) -> Tuple[bool, List[OutputEvent]]:
@@ -45,20 +45,14 @@ class StatechartInstance(Instance):
             print_debug(termcolor.colored('  ENTER %s'%state.name, 'green'))
             self.eventless_states += state.has_eventless_transitions
             self._perform_actions(state.enter)
+            self._start_timers(state.after_triggers)
         stable = not self.eventless_states
-        print_debug(termcolor.colored('completed initialization', 'red'))
+        print_debug(termcolor.colored('completed initialization (time=%d)'%now, 'red'))
         return (stable, self._big_step.output_events)
 
     # perform a big step. generating a set of output events
     def big_step(self, now: Timestamp, input_events: List[Event]) -> Tuple[bool, List[OutputEvent]]:
-        filtered = []
-        for e in input_events:
-            if e.name in self.ignore_events:
-                self.ignore_events[e.name] -= 1
-            else:
-                filtered.append(e)
-
-        self._big_step.next(filtered)
+        self._big_step.next(input_events)
         self._combo_step.reset()
         self._small_step.reset()
 
@@ -69,7 +63,7 @@ class StatechartInstance(Instance):
                 break # Take One -> only one combo step allowed
 
         if self._big_step.has_stepped:
-            print_debug(termcolor.colored('completed big step', 'red'))
+            print_debug(termcolor.colored('completed big step (time=%d)'%now, 'red'))
 
         # can the next big step still contain transitions, even if there are no input events?
         stable = not self.eventless_states or (not filtered and not self._big_step.has_stepped)
@@ -184,6 +178,7 @@ class StatechartInstance(Instance):
             self.configuration_bitmap |= 2**s.state_id
             # execute enter action(s)
             self._perform_actions(s.enter)
+            self._start_timers(s.after_triggers)
         try:
             self.configuration = self.config_mem[self.configuration_bitmap]
         except:
@@ -253,6 +248,16 @@ class StatechartInstance(Instance):
                     OutputPortTarget(a.outport),
                     a.time_offset))
 
+    def _start_timers(self, triggers: List[AfterTrigger]):
+        for after in triggers:
+            event_name = "_after"+str(self.next_timer_id)
+            self.next_timer_id += 1
+            self._big_step.addOutputEvent(OutputEvent(
+                Event(event_name),
+                target=InstancesTarget([self]),
+                time_offset=after.delay))
+            after.name = event_name # update trigger
+
     def _raiseInternalEvent(self, event):
         if self.model.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_SMALL_STEP:
             self._small_step.addNextEvent(event)

+ 12 - 1
src/sccd/runtime/statechart_syntax.py

@@ -17,11 +17,13 @@ class State:
         self.parent = None
         self.children = []
         self.default_state = None
-        self.transitions = []
+        self.transitions: List[Transition] = []
         self.enter: List[Action] = []
         self.exit: List[Action] = []
         self.history = [] # list of history states that are children
 
+        self.after_triggers: List[AfterTrigger] = []
+
         # optimization stuff
         self.state_id = -1
         self.ancestors = []
@@ -71,6 +73,8 @@ class State:
     
     def addTransition(self, transition):
         self.transitions.append(transition)
+        if isinstance(transition.trigger, AfterTrigger):
+            self.after_triggers.append(transition.trigger)
         
     def setEnter(self, enter: List[Action]):
         self.enter = enter
@@ -126,6 +130,13 @@ class Trigger:
     name: str
     port: str
 
+class AfterTrigger(Trigger):
+    def __init__(self, delay: Timestamp):
+        # event name will be generated by the runtime when the timer starts
+        super().__init__(name="", port="")
+        self.delay = delay
+
+
 class Transition:
     def __init__(self, source, targets: List[State]):
         self.guard = None

+ 5 - 2
src/sccd/runtime/xml_loader.py

@@ -127,7 +127,6 @@ def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
   states: Dict[str, State] = {}
   transitions: List[Tuple[Any, State]] = [] # List of (<transition>, State) tuples
 
-
   # Recursively create state hierarchy from XML node
   # Adding <transition> elements to the 'transitions' list as a side effect
   def _get_state_tree(xml_node) -> State:
@@ -204,7 +203,11 @@ def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
     # Trigger
     event = xml_t.get("event")
     port = xml_t.get("port")
-    trigger = None if event == None else Trigger(event, port)
+    after = xml_t.get("after")
+    if after is not None:
+      trigger = AfterTrigger(Timestamp(after))
+    else:
+      trigger = None if event == None else Trigger(event, port)
     transition.setTrigger(trigger)
     # Actions
     actions = load_actions(xml_t)

+ 4 - 1
test/render.py

@@ -115,8 +115,11 @@ if __name__ == '__main__':
         ctr = 0
         for t in transitions:
           label = ""
-          if t.trigger and t.trigger.name:
+          if t.trigger:
+            if t.trigger.name:
               label = (t.trigger.port + '.' if t.trigger.port else '') + t.trigger.name
+            elif isinstance(t.trigger, AfterTrigger):
+              label = "after("+str(t.trigger.delay)+")"
           if t.actions:
             raises = [a for a in t.actions if isinstance(a, RaiseEvent)]
             label += ','.join([r.render() for r in raises])

+ 8 - 8
test/test_files/semantics/original_semantics/after+Class1.svg

@@ -4,11 +4,11 @@
 <!-- Generated by graphviz version 2.40.1 (20161225.0304)
  -->
 <!-- Title: state transitions Pages: 1 -->
-<svg width="204pt" height="231pt"
- viewBox="0.00 0.00 204.00 231.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg width="242pt" height="231pt"
+ viewBox="0.00 0.00 242.18 231.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 227)">
 <title>state transitions</title>
-<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-227 200,-227 200,4 -4,4"/>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-227 238.177,-227 238.177,4 -4,4"/>
 <!-- __initial -->
 <g id="node1" class="node">
 <title>__initial</title>
@@ -42,7 +42,7 @@
 <title>_s1&#45;&gt;_s2</title>
 <path fill="none" stroke="#000000" d="M111.5239,-147.8711C106.4979,-141.7869 100.7414,-134.8185 95.1343,-128.031"/>
 <polygon fill="#000000" stroke="#000000" points="97.7407,-125.6905 88.6735,-120.21 92.3439,-130.1487 97.7407,-125.6905"/>
-<text text-anchor="middle" x="106.8895" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+<text text-anchor="start" x="105.5" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(100) &#160;&#160;</text>
 </g>
 <!-- _s3 -->
 <g id="node4" class="node">
@@ -56,16 +56,16 @@
 <!-- _s1&#45;&gt;_s3 -->
 <g id="edge3" class="edge">
 <title>_s1&#45;&gt;_s3</title>
-<path fill="none" stroke="#000000" d="M137.9641,-147.9484C142.5129,-139.7539 147.2024,-129.7327 149.5,-120 154.1972,-100.1025 153.7985,-93.9874 149.5,-74 148.1598,-67.7684 145.9953,-61.3803 143.5172,-55.3254"/>
-<polygon fill="#000000" stroke="#000000" points="146.7004,-53.8697 139.4382,-46.1553 140.3046,-56.7146 146.7004,-53.8697"/>
-<text text-anchor="middle" x="154.8895" y="-94" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+<path fill="none" stroke="#000000" d="M154.8302,-149.9062C157.9072,-147.0066 160.6047,-143.7067 162.5,-140 170.4672,-124.4181 201.648,-151.2752 158.2297,-55.6537"/>
+<polygon fill="#000000" stroke="#000000" points="161.3017,-53.9568 153.9422,-46.3351 154.9425,-56.8826 161.3017,-53.9568"/>
+<text text-anchor="start" x="182.5" y="-94" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(200) &#160;&#160;</text>
 </g>
 <!-- _s2&#45;&gt;_s3 -->
 <g id="edge4" class="edge">
 <title>_s2&#45;&gt;_s3</title>
 <path fill="none" stroke="#000000" d="M87.2227,-73.9916C92.0022,-67.7866 97.256,-60.9659 102.3232,-54.3875"/>
 <polygon fill="#000000" stroke="#000000" points="105.2624,-56.3072 108.5919,-46.2491 99.7168,-52.0355 105.2624,-56.3072"/>
-<text text-anchor="middle" x="102.8895" y="-57" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+<text text-anchor="start" x="101.5" y="-57" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(150) &#160;&#160;</text>
 </g>
 </g>
 </svg>