Jelajahi Sumber

Input port taken into account when assigning globally unique ID to input event.

Joeri Exelmans 5 tahun lalu
induk
melakukan
3e825ba583

+ 1 - 1
src/sccd/controller/controller.py

@@ -50,7 +50,7 @@ class Controller:
                 raise Exception("No such port: '%s'" % input.port) from e
 
             try:
-                event_id = self.model.globals.events.get_id(input.name)
+                event_id = self.model.globals.events.get_id(input.port + '.' + input.name)
             except KeyError as e:
                 raise Exception("No such event: '%s'" % input.name) from e
 

+ 2 - 1
src/sccd/execution/round.py

@@ -11,6 +11,7 @@ class CandidatesGenerator:
 
 class CandidatesGeneratorCurrentConfigBased(CandidatesGenerator):
     def generate(self, state, enabled_events: List[Event], arenas_changed: Bitmap) -> Iterable[Transition]:
+        # events_bitmap = Bitmap.from_list(e.id for e in enabled_events)
         key = (state.configuration_bitmap, arenas_changed)
 
         try:
@@ -28,7 +29,7 @@ class CandidatesGeneratorCurrentConfigBased(CandidatesGenerator):
             if not t.trigger:
                 return True
             for e in enabled_events:
-                if t.trigger.id == e.id and (not t.trigger.port or t.trigger.port == e.port):
+                if t.trigger.id == e.id:
                     return True
             return False
 

+ 13 - 2
src/sccd/parser/statechart_parser.py

@@ -134,9 +134,11 @@ class ActionParser(XmlParser):
     name = el.get("event")
     port = el.get("port")
     if not port:
+      # internal event
       event_id = globals.events.assign_id(name)
       a = RaiseInternalEvent(name=name, parameters=[], event_id=event_id)
     else:
+      # output event - no ID in global namespace
       globals.outports.assign_id(port)
       a = RaiseOutputEvent(name=name, parameters=[], outport=port, time_offset=0)
     actions.append(a)
@@ -282,12 +284,15 @@ class TreeParser(StateParser):
     next_after_id = 0
     transitions = self.transitions.pop()
     for t_el, source, actions in transitions:
-      target_string = t_el.get("target", "")
+      target_string = t_el.get("target")
       event = t_el.get("event")
       port = t_el.get("port")
       after = t_el.get("after")
       cond = t_el.get("cond")
 
+      if target_string is None:
+        self._raise(t_el, "Missing mandatory attribute: 'target'.", None)
+
       try:
         # Parse and find target state
         parse_tree = parse_state_ref(target_string)
@@ -317,6 +322,8 @@ class TreeParser(StateParser):
 
       # Trigger
       if after is not None:
+        if event is not None:
+          self._raise(t_el, "Can only specify one of attributes 'after', 'event'.", None)
         try:
           after_expr = parse_expression(globals, expr=after)
           after_type = after_expr.init_rvalue(scope)
@@ -331,7 +338,11 @@ class TreeParser(StateParser):
         except Exception as e:
           self._raise(t_el, "after=\"%s\": %s" % (after, str(e)), e)
       elif event is not None:
-        trigger = EventTrigger(globals.events.assign_id(event), event, port)
+        if port is None:
+          event_id = globals.events.assign_id(event)
+        else:
+          event_id = globals.events.assign_id(port + '.' + event)
+        trigger = EventTrigger(event_id, event, port)
         globals.inports.assign_id(port)
       else:
         trigger = None

+ 6 - 5
src/sccd/syntax/tree.py

@@ -86,6 +86,11 @@ class EventTrigger:
     name: str # event name
     port: str
 
+    bitmap: Bitmap = None
+
+    def __post_init__(self):
+        self.bitmap = bit(self.id)
+
     def render(self) -> str:
         if self.port:
             return self.port+'.'+self.name
@@ -103,7 +108,6 @@ class AfterTrigger(EventTrigger):
     def render(self) -> str:
         return "after("+self.delay.render()+")"
 
-
 @dataclass
 class Transition:
     source: State
@@ -191,7 +195,7 @@ class StateTree:
         init_state(root, "", [])
         self.root = root
 
-        def init_transition(t: Transition):
+        for t in self.transition_list:
             # the least-common ancestor can be computed statically
             if t.source in t.targets[0].gen.ancestors:
                 lca = t.source
@@ -207,6 +211,3 @@ class StateTree:
             t.gen = TransitionOptimization(
                 lca=lca,
                 arena_bitmap=lca.gen.descendant_bitmap.set(lca.gen.state_id))
-
-        for t in self.transition_list:
-            init_transition(t)

+ 18 - 0
test/test_files/syntax/fail_after_and_event.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart>
+    <semantics/>
+    <datamodel/>
+
+    <tree>
+      <state>
+        <state id="a">
+          <transition after="1 s" event="e" target="."/>
+        </state>
+      </state>
+    </tree>
+  </statechart>
+
+  <output>
+  </output>
+</test>

+ 18 - 0
test/test_files/syntax/fail_missing_target.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart>
+    <semantics/>
+    <datamodel/>
+
+    <tree>
+      <state>
+        <state id="a">
+          <transition event="e"/>
+        </state>
+      </state>
+    </tree>
+  </statechart>
+
+  <output>
+  </output>
+</test>