瀏覽代碼

changed so that narrow cast events are sent to a specific private port. somehow works for javascript although nothing was changed, otherwise we get exceptions on an undefined controller variable

Simon Van Mierlo 9 年之前
父節點
當前提交
cd3531d69f

+ 1 - 0
src/python_sccd/python_sccd_compiler/sccd_constructs.py

@@ -640,6 +640,7 @@ class StateChart(Visitable):
         self.big_step_maximality = getSemanticOption("big_step_maximality", ["take_one", "take_many"], "take_many")
         self.internal_event_lifeline = getSemanticOption("internal_event_lifeline", ["next_small_step", "next_combo_step", "queue"], "queue")
         self.input_event_lifeline = getSemanticOption("input_event_lifeline", ["first_small_step", "first_combo_step", "whole"], "first_combo_step")
+        print self.input_event_lifeline
         self.priority = getSemanticOption("priority", ["source_parent", "source_child"], "source_parent")
         self.concurrency = getSemanticOption("concurrency", ["single", "many"], "single")
 

+ 61 - 58
src/python_sccd/python_sccd_runtime/statecharts_core.py

@@ -55,9 +55,9 @@ class Association(object):
         """
         Constructor
         
-        :param to_class: the name of the target class
-        :param min_card: the minimal cardinality
-        :param max_card: the maximal cardinality
+       :param to_class: the name of the target class
+       :param min_card: the minimal cardinality
+       :param max_card: the maximal cardinality
         """
         self.to_class = to_class
         self.min_card = min_card
@@ -74,28 +74,28 @@ class Association(object):
         return self.min_card == -1 or self.size > self.min_card
         
     def addInstance(self, instance):
-        if self.allowedToAdd() :
+        if self.allowedToAdd():
             new_id = self.next_id
             self.next_id += 1
             self.instances[new_id] = instance
             self.instances_to_ids[instance] = new_id
             self.size += 1
             return new_id
-        else :
+        else:
             raise AssociationException("Not allowed to add the instance to the association.")
         
     def removeInstance(self, instance):
-        if self.allowedToRemove() :
+        if self.allowedToRemove():
             del self.instances[self.instances_to_ids[instance]]
             del self.instances_to_ids[instance]
             self.size -= 1
-        else :
+        else:
             raise AssociationException("Not allowed to remove the instance from the association.")
         
     def getInstance(self, index):
-        try :
+        try:
             return self.instances[index]
-        except IndexError :
+        except IndexError:
             raise AssociationException("Invalid index for fetching instance(s) from association.")
 
 # TODO: Clean this mess up. Look at all object management operations and see how they can be improved.
@@ -150,39 +150,39 @@ class ObjectManagerBase(object):
         self.handlers[e.getName()](e.getParameters())
             
     def processAssociationReference(self, input_string):
-        if len(input_string) == 0 :
+        if len(input_string) == 0:
             raise AssociationReferenceException("Empty association reference.")
         path_string =  input_string.split("/")
         result = []
-        for piece in path_string :
+        for piece in path_string:
             match = self.regex_pattern.match(piece)
-            if match :
+            if match:
                 name = match.group(1)
                 index = match.group(2)
-                if index is None :
+                if index is None:
                     index = -1
                 result.append((name,int(index)))
-            else :
+            else:
                 raise AssociationReferenceException("Invalid entry in association reference. Input string: " + input_string)
         return result
     
     def handleStartInstanceEvent(self, parameters):
-        if len(parameters) != 2 :
+        if len(parameters) != 2:
             raise ParameterException ("The start instance event needs 2 parameters.")  
-        else :
+        else:
             source = parameters[0]
             traversal_list = self.processAssociationReference(parameters[1])
-            for i in self.getInstances(source, traversal_list) :
+            for i in self.getInstances(source, traversal_list):
                 i["instance"].start()
             source.addEvent(Event("instance_started", parameters = [parameters[1]]))
         
     def handleBroadCastEvent(self, parameters):
-        if len(parameters) != 1 :
+        if len(parameters) != 1:
             raise ParameterException ("The broadcast event needs 1 parameter.")
         self.broadcast(parameters[0])
 
     def handleCreateEvent(self, parameters):
-        if len(parameters) < 2 :
+        if len(parameters) < 2:
             raise ParameterException ("The create event needs at least 2 parameters.")
 
         source = parameters[0]
@@ -190,7 +190,7 @@ class ObjectManagerBase(object):
         
         association = source.associations[association_name]
         # association = self.instances_map[source].getAssociation(association_name)
-        if association.allowedToAdd() :
+        if association.allowedToAdd():
             ''' allow subclasses to be instantiated '''
             class_name = association.to_class if len(parameters) == 2 else parameters[2]
             new_instance = self.createInstance(class_name, parameters[3:])
@@ -205,13 +205,13 @@ class ObjectManagerBase(object):
             if p:
                 p.addInstance(source)
             source.addEvent(Event("instance_created", None, [association_name+"["+str(index)+"]"]))
-        else :
+        else:
             source.addEvent(Event("instance_creation_error", None, [association_name]))
 
     def handleDeleteInstanceEvent(self, parameters):
-        if len(parameters) < 2 :
+        if len(parameters) < 2:
             raise ParameterException ("The delete event needs at least 2 parameters.")
-        else :
+        else:
             source = parameters[0]
             association_name = parameters[1]
             traversal_list = self.processAssociationReference(association_name)
@@ -229,62 +229,63 @@ class ObjectManagerBase(object):
             source.addEvent(Event("instance_deleted", parameters = [parameters[1]]))
                 
     def handleAssociateEvent(self, parameters):
-        if len(parameters) != 3 :
+        if len(parameters) != 3:
             raise ParameterException ("The associate_instance event needs 3 parameters.")
-        else :
+        else:
             source = parameters[0]
             to_copy_list = self.getInstances(source,self.processAssociationReference(parameters[1]))
-            if len(to_copy_list) != 1 :
+            if len(to_copy_list) != 1:
                 raise AssociationReferenceException ("Invalid source association reference.")
             wrapped_to_copy_instance = to_copy_list[0]["instance"]
             dest_list = self.processAssociationReference(parameters[2])
-            if len(dest_list) == 0 :
+            if len(dest_list) == 0:
                 raise AssociationReferenceException ("Invalid destination association reference.")
             last = dest_list.pop()
-            if last[1] != -1 :
+            if last[1] != -1:
                 raise AssociationReferenceException ("Last association name in association reference should not be accompanied by an index.")
                 
-            for i in self.getInstances(source, dest_list) :
+            for i in self.getInstances(source, dest_list):
                 i["instance"].associations[last[0]].addInstance(wrapped_to_copy_instance)
         
     def handleNarrowCastEvent(self, parameters):
-        if len(parameters) != 3 :
-            raise ParameterException ("The associate_instance event needs 3 parameters.")
+        if len(parameters) != 3:
+            raise ParameterException ("The narrow_cast event needs 3 parameters.")
         source = parameters[0]
         traversal_list = self.processAssociationReference(parameters[1])
         cast_event = parameters[2]
-        for i in self.getInstances(source, traversal_list) :
-            i["instance"].addEvent(cast_event)
+        for i in self.getInstances(source, traversal_list):
+            to_send_event = Event(cast_event.name, i["instance"].narrow_cast_port, cast_event.parameters)
+            i["instance"].controller.addInput(to_send_event)
         
     def getInstances(self, source, traversal_list):
         currents = [{
-            "instance" : source,
-            "ref" : None,
-            "assoc_name" : None,
-            "assoc_index" : None
+            "instance": source,
+            "ref": None,
+            "assoc_name": None,
+            "assoc_index": None
         }]
         # currents = [source]
-        for (name, index) in traversal_list :
+        for (name, index) in traversal_list:
             nexts = []
-            for current in currents :
+            for current in currents:
                 association = current["instance"].associations[name]
-                if (index >= 0 ) :
+                if (index >= 0 ):
                     nexts.append({
-                        "instance" : association.instances[index],
-                        "ref" : current["instance"],
-                        "assoc_name" : name,
-                        "assoc_index" : index
+                        "instance": association.instances[index],
+                        "ref": current["instance"],
+                        "assoc_name": name,
+                        "assoc_index": index
                     })
-                elif (index == -1) :
+                elif (index == -1):
                     for i in association.instances:
                         nexts.append({
-                            "instance" : association.instances[i],
-                            "ref" : current["instance"],
-                            "assoc_name" : name,
-                            "assoc_index" : index
+                            "instance": association.instances[i],
+                            "ref": current["instance"],
+                            "assoc_name": name,
+                            "assoc_index": index
                         })
                     #nexts.extend( association.instances.values() )
-                else :
+                else:
                     raise AssociationReferenceException("Incorrect index in association reference.")
             currents = nexts
         return currents
@@ -314,9 +315,9 @@ class Event(object):
         return self.parameters
     
     def __repr__(self):
-        representation = "(event name : " + str(self.name) + "; port : " + str(self.port)
-        if self.parameters :
-            representation += "; parameters : " + str(self.parameters)
+        representation = "(event name: " + str(self.name) + "; port: " + str(self.port)
+        if self.parameters:
+            representation += "; parameters: " + str(self.parameters)
         representation += ")"
         return representation
     
@@ -326,7 +327,7 @@ class OutputListener(object):
         self.queue = Queue()
 
     def add(self, event):
-        if len(self.port_names) == 0 or event.getPort() in self.port_names :
+        if len(self.port_names) == 0 or event.getPort() in self.port_names:
             self.queue.put_nowait(event)
             
     """ Tries for timeout seconds to fetch an event, returns None if failed.
@@ -370,7 +371,7 @@ class ControllerBase(object):
         return self.simulated_time
             
     def addInputPort(self, virtual_name, instance = None):
-        if instance == None :
+        if instance == None:
             port_name = virtual_name
         else:
             port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
@@ -397,10 +398,10 @@ class ControllerBase(object):
             input_event_list = [input_event_list]
 
         for e in input_event_list:
-            if e.getName() == ""  :
+            if e.getName() == "":
                 raise InputException("Input event can't have an empty name.")
         
-            if e.getPort() not in self.input_ports :
+            if e.getPort() not in self.input_ports:
                 raise InputException("Input port mismatch, no such port: " + e.getPort() + ".")
             
             self.input_queue.add((0 if self.simulated_time is None else accurate_time.time()) + time_offset, e)
@@ -421,7 +422,7 @@ class ControllerBase(object):
                 target_instance.addEvent(e, event_time - self.simulated_time)
 
     def outputEvent(self, event):
-        for listener in self.output_listeners :
+        for listener in self.output_listeners:
             listener.add(event)
 
     def addOutputListener(self, ports):
@@ -852,6 +853,8 @@ class RuntimeClassBase(object):
         self.configuration_bitmap = 0
         self.transition_mem = {}
         self.config_mem = {}
+        
+        self.narrow_cast_port = self.controller.addInputPort("<narrow_cast>", self)
 
         self.semantics = StatechartSemantics()
 

+ 6 - 0
test/run_tests.html

@@ -34,6 +34,7 @@
 <script src="target_js/event_lifeline/30_orthogonal_take_many_next_small_step.js"></script>
 <script src="target_js/event_lifeline/31_orthogonal_take_many_next_combo_step.js"></script>
 <script src="target_js/event_lifeline/32_orthogonal_take_many_queue.js"></script>
+<script src="target_js/event_lifeline/event_consuming_whole.js"></script>
 
 <script src="target_js/priority/00_source_parent.js"></script>
 <script src="target_js/priority/01_source_child.js"></script>
@@ -46,6 +47,8 @@
 <script src="target_js/original_semantics/associate_event.js"></script>
 <script src="target_js/original_semantics/correct_duplicate_state_id.js"></script>
 <script src="target_js/original_semantics/enter_exit_hierarchy.js"></script>
+<script src="target_js/original_semantics/event_consuming.js"></script>
+<script src="target_js/original_semantics/event_consuming_2.js"></script>
 <script src="target_js/original_semantics/guard.js"></script>
 <script src="target_js/original_semantics/history.js"></script>
 <script src="target_js/original_semantics/history_deep.js"></script>
@@ -77,6 +80,7 @@ var tests = [
 "orthogonal_take_many_next_small_step",
 "orthogonal_take_many_next_combo_step",
 "orthogonal_take_many_queue",
+"TestEventConsumingWhole",
 // Original Semantics
 "TestAfter",
 "TestAssociateEvent",
@@ -94,6 +98,8 @@ var tests = [
 "TestParallel",
 "TestParallelHistory",
 "TestParallelHistory2",
+"TestEventConsuming",
+"TestEventConsuming2",
 // Priority
 "source_parent",
 "source_child",

+ 88 - 0
test/src/event_lifeline/event_consuming_whole.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" ?>
+<diagram author="Simon Van Mierlo" name="TestEventConsumingWhole">
+    <description>
+        Testing event consuming.
+    </description>
+    <inport name="test_input" />
+    <outport name="test_output" />
+    <class name="Class1" default="true">
+        <relationships>
+            <association class="Class2" name="to_Class2" />
+        </relationships>
+        <scxml initial="initial" input_event_lifeline="whole">
+            <state id="initial">
+                <onentry>
+                    <raise event="create_instance" scope="cd">
+                        <parameter expr="'to_Class2'" />
+                    </raise>
+                </onentry>
+                <transition event="instance_created" target="../one">
+                    <parameter name="link_name" />
+                    <raise event="start_instance" scope="cd">
+                        <parameter expr="link_name" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="one">
+                <onentry>
+                    <raise event="enter_one" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_one" port="test_output" />
+                </onexit>
+                <transition event="A" target="../two" cond="0" />
+                <transition event="B" target="../two" />
+            </state>
+            <state id="two">
+                <onentry>
+                    <raise event="enter_two" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_two" port="test_output" />
+                </onexit>
+                <transition event="B" target="../three" />
+            </state>
+            <state id="three">
+                <onentry>
+                    <raise event="enter_three" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_three" port="test_output" />
+                </onexit>
+            </state>
+        </scxml>
+    </class>
+    <class name="Class2">
+        <relationships>
+            <association class="Class1" name="parent" />
+        </relationships>
+        <scxml initial="initial">
+            <state id="initial">
+                <transition target="../final">
+                    <raise event="A" target="'parent'" />
+                    <raise event="B" target="'parent'" />
+                </transition>
+            </state>
+            <state id="final" />
+        </scxml>
+    </class>
+    <test>
+       <expected>
+           <slot>
+               <event name="enter_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="exit_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="enter_two" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="exit_two" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="enter_three" port="test_output"/>
+           </slot>
+        </expected>
+    </test>
+</diagram>

+ 88 - 0
test/src/original_semantics/event_consuming.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" ?>
+<diagram author="Simon Van Mierlo" name="TestEventConsuming">
+    <description>
+        Testing event consuming.
+    </description>
+    <inport name="test_input" />
+    <outport name="test_output" />
+    <class name="Class1" default="true">
+        <relationships>
+            <association class="Class2" name="to_Class2" />
+        </relationships>
+        <scxml initial="initial">
+            <state id="initial">
+                <onentry>
+                    <raise event="create_instance" scope="cd">
+                        <parameter expr="'to_Class2'" />
+                    </raise>
+                </onentry>
+                <transition event="instance_created" target="../one">
+                    <parameter name="link_name" />
+                    <raise event="start_instance" scope="cd">
+                        <parameter expr="link_name" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="one">
+                <onentry>
+                    <raise event="enter_one" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_one" port="test_output" />
+                </onexit>
+                <transition event="A" target="../two" cond="0" />
+                <transition event="B" target="../two" />
+            </state>
+            <state id="two">
+                <onentry>
+                    <raise event="enter_two" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_two" port="test_output" />
+                </onexit>
+                <transition event="A" target="../three" />
+            </state>
+            <state id="three">
+                <onentry>
+                    <raise event="enter_three" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_three" port="test_output" />
+                </onexit>
+            </state>
+        </scxml>
+    </class>
+    <class name="Class2">
+        <relationships>
+            <association class="Class1" name="parent" />
+        </relationships>
+        <scxml initial="initial">
+            <state id="initial">
+                <transition target="../final">
+                    <raise event="B" target="'parent'" />
+                    <raise event="A" target="'parent'" />
+                </transition>
+            </state>
+            <state id="final" />
+        </scxml>
+    </class>
+    <test>
+       <expected>
+           <slot>
+               <event name="enter_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="exit_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="enter_two" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="exit_two" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="enter_three" port="test_output"/>
+           </slot>
+        </expected>
+    </test>
+</diagram>

+ 82 - 0
test/src/original_semantics/event_consuming_2.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" ?>
+<diagram author="Simon Van Mierlo" name="TestEventConsuming2">
+    <description>
+        Testing event consuming.
+    </description>
+    <inport name="test_input" />
+    <outport name="test_output" />
+    <class name="Class1" default="true">
+        <relationships>
+            <association class="Class2" name="to_Class2" />
+        </relationships>
+        <scxml initial="initial">
+            <state id="initial">
+                <onentry>
+                    <raise event="create_instance" scope="cd">
+                        <parameter expr="'to_Class2'" />
+                    </raise>
+                </onentry>
+                <transition event="instance_created" target="../one">
+                    <parameter name="link_name" />
+                    <raise event="start_instance" scope="cd">
+                        <parameter expr="link_name" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="one">
+                <onentry>
+                    <raise event="enter_one" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_one" port="test_output" />
+                </onexit>
+                <transition event="A" target="../two" cond="0" />
+                <transition event="B" target="../two" />
+            </state>
+            <state id="two">
+                <onentry>
+                    <raise event="enter_two" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_two" port="test_output" />
+                </onexit>
+                <transition event="A" target="../three" />
+            </state>
+            <state id="three">
+                <onentry>
+                    <raise event="enter_three" port="test_output" />
+                </onentry>
+                <onexit>
+                    <raise event="exit_three" port="test_output" />
+                </onexit>
+            </state>
+        </scxml>
+    </class>
+    <class name="Class2">
+        <relationships>
+            <association class="Class1" name="parent" />
+        </relationships>
+        <scxml initial="initial">
+            <state id="initial">
+                <transition target="../final">
+                    <raise event="A" target="'parent'" />
+                    <raise event="B" target="'parent'" />
+                </transition>
+            </state>
+            <state id="final" />
+        </scxml>
+    </class>
+    <test>
+       <expected>
+           <slot>
+               <event name="enter_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="exit_one" port="test_output"/>
+           </slot>
+           <slot>
+               <event name="enter_two" port="test_output"/>
+           </slot>
+        </expected>
+    </test>
+</diagram>