Browse Source

Fixed 'after' triggers

Joeri Exelmans 5 years ago
parent
commit
87464a8f07

+ 16 - 8
src/sccd/compiler/generic_generator.py

@@ -316,7 +316,7 @@ class GenericGenerator(Visitor):
                 # if any trigger associated with transition: instantiate correct Event instance
                 trigger = None
                 if t.trigger.is_after:
-                    trigger = GLC.NewExpression("Event", [GLC.String("_%iafter" % (t.trigger.getAfterIndex()))])
+                    trigger = GLC.NewExpression("Event", [GLC.String("_after_%i" % (t.trigger.getAfterIndex()))])
                 elif t.trigger.event:
                     trigger = GLC.NewExpression("Event",
                                                     [
@@ -395,12 +395,19 @@ class GenericGenerator(Visitor):
                 self.writer.startRecordingExpression()
                 trigger.after.accept(self)
                 after = self.writer.stopRecordingExpression()
-                print("---------------")
-                print(after)
-                print(trigger)
-                print(trigger.after.expression_parts)
-                # self.writer.add(GLC.FunctionCall(GLC.Property("instance", GLC.Property("_big_step", "addOutputEvent")), [GLC.NewExpression("OutputEvent", [GLC.NewExpression("Event", ["__after_"+str(trigger.getAfterIndex())]), GLC.NewExpression("InstancesTarget", [GLC.Identifier("instance")]), (trigger.after)])]))
-                self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addTimer"), [str(trigger.getAfterIndex()), after]))
+                self.writer.addComment("schedule timer")
+                # self.writer.add(GLC.AssignmentExpression(GLC.Identifier("timer_event"), GLC.NewExpression("Event", [GLC.FunctionCall(GLC.Property("instance", "next_timer_id"))])))
+                # self.writer.add(GLC.FunctionCall())
+                self.writer.add(GLC.FunctionCall(GLC.Property(GLC.Property("instance", "_big_step"), "addOutputEvent"),
+                    # function call parameter:
+                    [
+                        GLC.NewExpression("OutputEvent", [
+                            GLC.NewExpression("Event", [GLC.String("_after_"+str(trigger.getAfterIndex()))]),
+                            GLC.NewExpression("InstancesTarget", [GLC.ArrayExpression([GLC.Identifier("instance")])]),
+                            after
+                        ])
+                    ]))
+                # self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addTimer"), [str(trigger.getAfterIndex()), after]))
                 
         self.writer.endMethodBody()
         self.writer.endMethod()
@@ -415,7 +422,8 @@ class GenericGenerator(Visitor):
         for transition in parent_node.transitions:
             trigger = transition.getTrigger()
             if trigger.isAfter():
-                self.writer.add(GLC.FunctionCall(GLC.SelfProperty("removeTimer"), [str(trigger.getAfterIndex())]))
+                self.writer.add(GLC.IncrementExpression(GLC.MapIndexedExpression(GLC.Property("instance", "ignore_events"), GLC.String("_after_"+str(trigger.getAfterIndex()))), GLC.Literal("1")))
+                # self.writer.add(GLC.FunctionCall(GLC.SelfProperty("removeTimer"), [str(trigger.getAfterIndex())]))
                 
         # execute user-defined exit action if present
         if exit_method.action:

+ 10 - 0
src/sccd/compiler/generic_language_constructs.py

@@ -566,6 +566,9 @@ class EqualsOperator(Operator):
 class AssignmentOperator(Operator):
 	pass
 
+class IncrementOperator(Operator):
+	pass
+
 class ProductOperator(Operator):
 	pass
 
@@ -629,6 +632,10 @@ class AssignmentExpression(BinaryExpression):
 	def __init__(self, lexpr = None, rexpr = None):
 		BinaryExpression.__init__(self, lexpr, AssignmentOperator(), rexpr)
 
+class IncrementExpression(BinaryExpression):
+	def __init__(self, lexpr = None, rexpr = None):
+		BinaryExpression.__init__(self, lexpr, IncrementOperator(), rexpr)
+
 class ProductExpression(BinaryExpression):
 	def __init__(self, lexpr = None, rexpr = None):
 		BinaryExpression.__init__(self, lexpr, ProductOperator(), rexpr)
@@ -854,6 +861,9 @@ class GenericWriterBase(Visitor):
 	def visit_AssignmentOperator(self, assign):
 		self.out.extendWrite(" = ")
 
+	def visit_IncrementOperator(self, inc):
+		self.out.extendWrite(" += ")
+
 	def visit_BinaryExpression(self, b):
 		lhs = b.getLhsExpression()
 		rhs = b.getRhsExpression()

+ 11 - 2
src/sccd/runtime/statecharts_core.py

@@ -8,6 +8,7 @@ from typing import List
 from sccd.runtime.infinity import INFINITY
 from sccd.runtime.event_queue import Timestamp
 from sccd.runtime.event import Event, OutputEvent, Instance, InstancesTarget
+from collections import Counter
 
 DEBUG = False
 ELSE_GUARD = "ELSE_GUARD"
@@ -357,6 +358,8 @@ 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.
+
     # whether the statechart MAY still perform at least one transition, even in the absense of an event
     def is_stable(self) -> bool:
         return self.stable
@@ -374,7 +377,13 @@ class StatechartInstance(Instance):
 
     # perform a big step. returns a set of output events
     def big_step(self, now: Timestamp, input_events: List[Event]) -> List[OutputEvent]:
-        self._big_step.next(input_events)
+        my_input_events = []
+        for e in input_events:
+            if e.name in self.ignore_events:
+                self.ignore_events[e.name] -= 1
+            else:
+                my_input_events.append(e)
+        self._big_step.next(my_input_events)
         self._combo_step.reset()
         self._small_step.reset()
 
@@ -384,7 +393,7 @@ class StatechartInstance(Instance):
                 break # Take One -> only one combo step allowed
 
         # can the next big step still contain transitions, even if there are no input events?
-        self.stable = not self.eventless_states or (not input_events and not self._big_step.has_stepped)
+        self.stable = not self.eventless_states or (not my_input_events and not self._big_step.has_stepped)
         return self._big_step.output_events
 
     def combo_step(self):

+ 21 - 19
test/semantics/original_semantics/after.xml

@@ -6,32 +6,34 @@
     <description>
         Used for testing the AFTER event.
     </description>
-    <inport name="test_input" />
-    <outport name="test_output" />
+    <inport name="in" />
+    <outport name="out" />
     <class name="Class1" default="true">
-        <scxml initial="composite">
-            <state id="composite" initial="state_1">
-                <state id="state_1">
-                    <transition after="0.1" target="../state_2"/>
-                    <transition after="0.2" target="../state_3"/>
-                </state>
-                <state id="state_2">
-                    <onentry>
-                       <raise event="in_state_2" port="test_output" />
-                    </onentry>
-                </state>
-                <state id="state_3">
-                    <onentry>
-                       <raise event="in_state_3" port="test_output"/>
-                    </onentry>
-                </state>
+        <scxml initial="s1">
+            <state id="s1">
+                <transition after="100" target="../s2"/>
+                <transition after="200" target="../s3"/>
+            </state>
+            <state id="s2">
+                <onentry>
+                   <raise event="in_state_2" port="out" />
+                </onentry>
+                <transition after="150" target="../s3"/>
+            </state>
+            <state id="s3">
+                <onentry>
+                   <raise event="in_state_3" port="out"/>
+                </onentry>
             </state>
         </scxml>
     </class>
     <test>
         <expected>
             <slot>
-                <event name="in_state_2" port="test_output"/>
+                <event name="in_state_2" port="out"/>
+            </slot>
+            <slot>
+                <event name="in_state_3" port="out"/>
             </slot>
         </expected>
     </test>