From f8b347c0befadbe1269f0f989478888b614c35e4 Mon Sep 17 00:00:00 2001
From: Maxime Perrotin <maxime.perrotin@esa.int>
Date: Mon, 14 Mar 2022 12:32:37 +0100
Subject: [PATCH 1/2] Filter quick events using state information

---
 .../taste-interactive-simulator               | 121 +++++++++++++++---
 1 file changed, 100 insertions(+), 21 deletions(-)

diff --git a/misc/helper-scripts/taste-interactive-simulator b/misc/helper-scripts/taste-interactive-simulator
index 1f7a86f5..e05e6668 100755
--- a/misc/helper-scripts/taste-interactive-simulator
+++ b/misc/helper-scripts/taste-interactive-simulator
@@ -74,6 +74,25 @@ def get_system_state():
     systemState.SetData(get_state())
     return systemState
 
+
+def get_dest(ctypes_evt, instance):
+    ''' Get the PID of the receiver of an event from the GUI
+    Return a string that is the representation of the enumerated value
+    (name of the function) '''
+    pid = ASN1_CTYPES.PID()
+    try:
+        # There is a function in the Ada code that does exactly that:
+        get_dest_pid = getattr(dll, f"{instance.lower()}_event_dest")
+    except AttributeError:
+        # Timer events, ignore for now
+        return
+    pid_val = get_dest_pid(ctypes_evt._ptr)
+    pid.Set(pid_val)
+    dest = pid.GSER()
+    # Return value with have dash, not underscores
+    return dest
+
+
 def get_global_event_queue():
     ''' Return and ctytpes instance of Events_Ty containing the global event
     queue before they have been dispatched in the input queues of functions '''
@@ -313,7 +332,7 @@ class Taste_Simulator(QObject):
             # read the top-level state. It is needed to check that parallel
             # states are part of it. If we are no in a state containing
             # parallel states we can ignore their values
-            main_state = s['s'][f]['state']['Enum']
+            main_state = s['s'][f]['state']['Enum'].replace('-', '_')
             for name in self.states[f].keys():  # for each state
                 # Pass if it does not correspond to the current state
                 if name != 'state' and not name.startswith(main_state):
@@ -545,8 +564,6 @@ def initialize():
     resetButton = simuWidget.findChild(QToolButton, 'reset')
     mscToggle   = simuWidget.findChild(QToolButton, 'msc')
     resetButton.clicked.connect(simulator.reset_simulation)
-    undoButton.clicked.connect(simulator.undo)
-    redoButton.clicked.connect(simulator.redo)
 
     # setup the MSC streamer
     msc_handler = mscHandler.mscHandler (
@@ -576,9 +593,9 @@ def initialize():
     # fields of the complete Observable-Event structure
     quickEventList = QListWidget(mainWidget)
 
-    # When an event is selected (double-clicked), send it
-    # If there are parameters display a popup window to fill them in
     def quickEvent(item : QListWidgetItem):
+        ''' When an event is selected (double-clicked), send it
+            If there are parameters display a popup window to fill them in  '''
         instance  = item.data(INSTANCE_NAME).replace('-', '_')
         name      = item.data(MESSAGE_NAME).replace('-', '_') # can be "elapse time"
         direction = item.data(DIRECTION) # in or out
@@ -721,6 +738,51 @@ def initialize():
         # send the message
         process_event()
 
+    def update_quick_events():
+        ''' Filter out messages in the quick event list that aer not relevant
+        depending on the current context.
+        Continuous signals can be removed based on the state
+        Lost messages could also be filtered in theory. However it is not done
+        yet because we only know the dest function of messages sent from the
+        guis. We don't know the PI messsage name if it was renamed in the IV.
+        This will be added later. (TODO)
+        '''
+        LOG.debug("Updating quick events list")
+        for row in range(quickEventList.count()):
+            item = quickEventList.item(row)
+            instance  = item.data(INSTANCE_NAME)
+            name      = item.data(MESSAGE_NAME)
+            if name != "input-none":
+                continue
+            _, _, ast = simulator.models[instance]
+            state_ctypes = System_State['editor'].asn1Instance
+            state_gser = state_ctypes.GSER()
+            state_py   = vn.fromValueNotationToPySide('s', state_gser)
+            main_state = state_py['s'][instance]['state']['Enum'].replace('-', '_')
+            if ast.cs_mapping[CleanName(main_state)]:
+                item.setHidden(False)
+                continue
+            else:
+                item.setHidden(True)
+            # In there are inner states, check them
+            for name in simulator.states[instance].keys():  # for each state
+                # Pass if it does not correspond to the current state
+                if name == 'state' or not name.startswith(main_state):
+                    continue
+                # Find the entry in the global state
+                as_key = AdaName(name)
+                if SEPARATOR in as_key:
+                    as_key += f'{SEPARATOR}state'
+                as_key = as_key.replace('_', '-')
+                state_val = state_py['s'][instance][as_key]['Enum']
+                no_dash = state_val.replace('-', '_')
+                state_name = CleanName(no_dash)  # stateA.stateB
+                if ast.cs_mapping[state_name]:
+                    item.setHidden(False)
+                    break
+                else:
+                    item.setHidden(True)
+
 
     def selectStatechartEdge(item : QListWidgetItem):
         ''' When an item is selected on the quick event list,
@@ -743,7 +805,7 @@ def initialize():
                 except:
                     # Only edge will not raise exceptions,..
                     pass
-        if direction == "out":  # out message = from GUI
+        if direction == 'out':  # out message = from GUI
             # We need to create a ctype event to get find its destination
             evt = ASN1_CTYPES.Observable_Event()
             innerType = f'{instance.title()}_Event_msg_out_{name.lower()}'
@@ -760,15 +822,7 @@ def initialize():
                     ASN1_CTYPES,
                     ASN1_AST)
             evt.Reset()
-            pid = ASN1_CTYPES.PID()
-            try:
-                get_dest = getattr(dll, f"{instance.lower()}_event_dest")
-            except AttributeError:
-                # Timer events, ignore for now
-                return
-            pid_val = get_dest(evt._ptr)
-            pid.Set(pid_val)
-            dest = pid.GSER()
+            dest = get_dest(evt, instance)
             LOG.debug(f"dest of {name} = {dest}")
         else:    # direction = in: cyclic and continuous signals
             # Continuous signals =- when name == input_none
@@ -780,13 +834,16 @@ def initialize():
                 if not edge.text_label:
                     continue
                 label = edge.text_label.toPlainText().lower().replace('\n','')
-                
+
                 if edge.edge['source'].name.lower() == str(item).lower() and (
                         name.lower() in label.split(',') or (name == "input_none"
                         and label.startswith("["))):
                     edge.select(True)
                     edge.ensureVisible()
 
+        if not dest:
+            # Timer manager events results in no dest
+            return
         # We have the pid, now we can look for the connection on the statechart
         _, scene, _ = simulator.models[dest.replace('_', '-')]
         for item in scene.highlighted.keys():
@@ -816,6 +873,18 @@ def initialize():
     quickEventList.itemClicked.connect(selectStatechartEdge)
     quickEventList.itemDoubleClicked.connect(quickEvent)
 
+    def undo():
+        ''' Upon undo, also update Quick event list '''
+        simulator.undo()
+        update_quick_events()
+
+    def redo():
+        ''' Upon redo, also update Quick event list '''
+        simulator.redo()
+        update_quick_events()
+
+    undoButton.clicked.connect(undo)
+    redoButton.clicked.connect(redo)
 
     dock = QDockWidget('Send messages', parent=mainWidget)
     dock.setObjectName("Send messages")
@@ -833,7 +902,10 @@ def initialize():
                 # Filter out GUI functions: they have no internal state
                 simulator.states[instance] = {'state': '_unset'}
                 # Create a quick event for Continuous signals, if any
-                # Here TODO filter functions that have no CS using SDL ast
+                # At this point the SDL models have not been parsed yet
+                # so we cannot filter those that have no CS at all. We create
+                # an entry in the Quick Event list in any case, and it will
+                # be updated on the fly by the "update_quick_events" function
                 evt = f"input-event: {{source env, dest {instance.lower()},"\
                       f" event {instance.lower()}: msg-in: input-none: {{}}}}"
                 nameItem = QListWidgetItem(f'{instance.title()} Continuous signals')
@@ -860,7 +932,7 @@ def initialize():
                         nameItem = QListWidgetItem(f'{instance.title()}.{name.title()}')
                         nameItem.setData (INSTANCE_NAME, instance)
                         nameItem.setData (MESSAGE_NAME,  name)
-                        nameItem.setData (DIRECTION,     "out")
+                        nameItem.setData (DIRECTION,     'out')
                         nameItem.setData (GSER_EVENT,    "")  # no GSER pre-fill
                         nameItem.setData (PERIOD,        0)
                         quickEventList.addItem(nameItem)
@@ -871,6 +943,7 @@ def initialize():
 
     def process_event(ctypes_event=None):
         ''' This function is called when the send button is pressed
+        or in general when an event is double clicked (QuickEvent list)
         Optionally an event can be passed (eg to send events from a different
         source such as a recorded scenario '''
         # get the ctypes event from the asn1 value editor
@@ -935,6 +1008,9 @@ def initialize():
             System_State['editor'].asn1Instance = state_ctypes
             System_State['editor'].updateVariable()
 
+            # update the quick event list (filter signals)
+            update_quick_events()
+
             simulator.on_event(events, state_ctypes)
 
     def find_new_input_event(old_state, new_state):
@@ -1092,7 +1168,7 @@ def initialize():
     # then place them in an MDI area
 
     mdiArea = QMdiArea()
-    
+
     sys.argv.append('--toC')
     for function in simulator.states.keys():
         f = function.replace('-', '_')
@@ -1150,7 +1226,7 @@ def initialize():
         sdl_view = opengeode.SDL_View(sdl_scene)
         opengeode.Statechart.render_statechart(sdl_scene, graph)
         sdl_view.refresh()
-        
+
 #       dock = QDockWidget(f, mainWidget)
 #       dock.setFloating(True)
 #       dock.resize(400, 400)
@@ -1243,7 +1319,7 @@ def initialize():
 
     qtwidget_System_State.show()
     qtwidget_Observable_Event.show()
-    
+
     # Connect the MSC buttons (Run/Edit/Load)
     editMSC.pressed.connect(msc_handler.edit)
     runMSC.pressed.connect(run_scenario)
@@ -1255,6 +1331,9 @@ def initialize():
     System_State['editor'].asn1Instance = state_ctypes
     System_State['editor'].updateVariable()
 
+    # update the quick event list (filter signals)
+    update_quick_events()
+
     # Set initial state hash in the simulator
     simulator.change_internal_state(state_ctypes)
 
-- 
GitLab


From d3f08ef6e8edf5e8a84426012d378d35c65b0afe Mon Sep 17 00:00:00 2001
From: Maxime Perrotin <maxime.perrotin@esa.int>
Date: Mon, 14 Mar 2022 14:52:42 +0100
Subject: [PATCH 2/2] Fix typos

---
 misc/helper-scripts/taste-interactive-simulator | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/misc/helper-scripts/taste-interactive-simulator b/misc/helper-scripts/taste-interactive-simulator
index e05e6668..3cb81b65 100755
--- a/misc/helper-scripts/taste-interactive-simulator
+++ b/misc/helper-scripts/taste-interactive-simulator
@@ -89,7 +89,7 @@ def get_dest(ctypes_evt, instance):
     pid_val = get_dest_pid(ctypes_evt._ptr)
     pid.Set(pid_val)
     dest = pid.GSER()
-    # Return value with have dash, not underscores
+    # Return value has dashes, not underscores
     return dest
 
 
@@ -739,7 +739,7 @@ def initialize():
         process_event()
 
     def update_quick_events():
-        ''' Filter out messages in the quick event list that aer not relevant
+        ''' Filter out messages in the quick event list that are not relevant
         depending on the current context.
         Continuous signals can be removed based on the state
         Lost messages could also be filtered in theory. However it is not done
-- 
GitLab