Kaynağa Gözat

fixed multiple afters leaving a state: events were not correctly removed in event queue. also: now afters are added at the end of the enter body, instead of at the start, to account for any computation performed in the enter body (which has an effect on sccd_yield)

Simon Van Mierlo 8 yıl önce
ebeveyn
işleme
7e5718ab9b

+ 117 - 0
examples/my-tests/python/multiple_afters_running_behind.py

@@ -0,0 +1,117 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Wed Jun 14 14:49:34 2017
+
+Model name:   multiple-afters-running-behind
+
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "multiple-afters-running-behind"
+
+class Main(RuntimeClassBase):
+    def __init__(self, controller):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        Main.user_defined_constructor(self)
+    
+    def user_defined_constructor(self):
+        pass
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # user defined method
+    def cond(self):
+        return True
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, self)
+        
+        # state /x
+        self.states["/x"] = State(1, self)
+        self.states["/x"].setEnter(self._x_enter)
+        self.states["/x"].setExit(self._x_exit)
+        
+        # add children
+        self.states[""].addChild(self.states["/x"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/x"]
+        
+        # transition /x
+        _x_0 = Transition(self, self.states["/x"], [self.states["/x"]])
+        _x_0.setTrigger(Event("_0after"))
+        _x_0.setGuard(self._x_0_guard)
+        self.states["/x"].addTransition(_x_0)
+        _x_1 = Transition(self, self.states["/x"], [self.states["/x"]])
+        _x_1.setTrigger(Event("_1after"))
+        _x_1.setGuard(self._x_1_guard)
+        self.states["/x"].addTransition(_x_1)
+        _x_2 = Transition(self, self.states["/x"], [self.states["/x"]])
+        _x_2.setTrigger(Event("_2after"))
+        _x_2.setGuard(self._x_2_guard)
+        self.states["/x"].addTransition(_x_2)
+    
+    def _x_enter(self):
+        import time
+        print 'time.time() = %s' % time.time()
+        time.sleep(0.1)
+        print self.sccd_yield()
+        self.addTimer(0, self.sccd_yield() + 0.5)
+        self.addTimer(1, self.sccd_yield() + 0.5)
+        self.addTimer(2, self.sccd_yield() + 0.5)
+    
+    def _x_exit(self):
+        self.removeTimer(0)
+        self.removeTimer(1)
+        self.removeTimer(2)
+    
+    def _x_0_guard(self, parameters):
+        return self.cond()
+    
+    def _x_1_guard(self, parameters):
+        return self.cond()
+    
+    def _x_2_guard(self, parameters):
+        return self.cond()
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/x"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "Main":
+            instance = Main(self.controller)
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.object_manager.createInstance("Main", [])

+ 25 - 0
examples/my-tests/python/multiple_afters_running_behind.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram name="multiple-afters-running-behind">
+    <class name="Main" default="true">
+        <method name="cond">
+            <body>
+                return True
+            </body>
+        </method>
+        <scxml initial="x">
+            <state id="x">
+                <transition after="self.sccd_yield() + 0.5" cond="self.cond()" target="." />
+                <transition after="self.sccd_yield() + 0.5" cond="self.cond()" target="." />
+                <transition after="self.sccd_yield() + 0.5" cond="self.cond()" target="." />
+                <onentry>
+                    <script>
+                        import time
+                        print 'time.time() = %s' % time.time()
+                        time.sleep(0.1)
+                        print self.sccd_yield()
+                    </script>
+                </onentry>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 3 - 0
examples/my-tests/python/runner_multiple_afters_running_behind.py

@@ -0,0 +1,3 @@
+import multiple_afters_running_behind
+controller = multiple_afters_running_behind.Controller(False)
+controller.start()

+ 4 - 3
src/python_sccd/python_sccd_compiler/generic_generator.py

@@ -670,6 +670,9 @@ class GenericGenerator(Visitor):
         self.writer.beginMethod(parent_node.friendly_name + "_enter")
         self.writer.beginMethodBody()
 
+        if enter_method.action:
+            enter_method.action.accept(self)
+
         # take care of any AFTER events
         for transition in parent_node.transitions :
             trigger = transition.getTrigger()
@@ -678,9 +681,7 @@ class GenericGenerator(Visitor):
                 trigger.after.accept(self)
                 after = self.writer.stopRecordingExpression()
                 self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addTimer"), [str(trigger.getAfterIndex()), after]))
-
-        if enter_method.action:
-            enter_method.action.accept(self)
+                
         self.writer.endMethodBody()
         self.writer.endMethod()
          

+ 8 - 4
src/python_sccd/python_sccd_runtime/event_queue.py

@@ -8,18 +8,22 @@ class EventQueue(object):
         self.removed = set()
     
     def __str__(self):
-        return str(self.event_list)
+        return str([entry for entry in self.event_list if entry not in self.removed])
     
     def isEmpty(self):
-        return not self.event_list
+        return not [item for item in self.event_list if not item in self.removed]
     
     def getEarliestTime(self):
+        while not self.isEmpty() and (self.event_list[0] in self.removed):
+            item = heappop(self.event_list)
+            self.removed.remove(item)
         return INFINITY if self.isEmpty() else self.event_list[0][0]
     
     def add(self, event_time, event):
         self.event_time_numbers[event_time] = self.event_time_numbers.setdefault(event_time, 0) + 1
-        heappush(self.event_list, (event_time, self.event_time_numbers[event_time], event))
-        return event
+        def_event = (event_time, self.event_time_numbers[event_time], event)
+        heappush(self.event_list, def_event)
+        return def_event
     
     def remove(self, event):
         self.removed.add(event)

+ 2 - 2
src/python_sccd/python_sccd_runtime/statecharts_core.py

@@ -133,7 +133,6 @@ class ObjectManagerBase(object):
         to_step = set()
         while self.instance_times[0][0] <= simulated_time:
             to_step.add(heappop(self.instance_times)[1])
-        # print simulated_time, len(self.instances), len(to_step | self.eventless)
         for i in to_step | self.eventless:
             if i.active and (i.earliest_event_time <= simulated_time or i.eventless_states):
                 i.step()
@@ -903,6 +902,7 @@ class RuntimeClassBase(object):
         if index in self.timers:
             self.events.remove(self.timers[index])
             del self.timers[index]
+        self.earliest_event_time = self.events.getEarliestTime()
         
     def addEvent(self, event_list, time_offset = 0):
         event_time = self.controller.simulated_time + time_offset
@@ -931,7 +931,7 @@ class RuntimeClassBase(object):
             self.earliest_event_time = self.events.getEarliestTime()
         heappush(self.controller.object_manager.instance_times, (self.earliest_event_time, self))
 
-    def step(self):        
+    def step(self):
         is_stable = False
         while not is_stable:
             due = []