From 0693a18a84c52b17d76f01a65d1bcea22aae43cc Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Fri, 27 Oct 2023 15:57:19 -0700 Subject: [PATCH 01/12] WIP: Initial debugging for adding pyepics to grab alarm thresholds --- slam/alarm_table_view.py | 23 +++++++++++++++++++++++ slam/main_window.py | 1 + 2 files changed, 24 insertions(+) diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index b8fba16..86dfd10 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -2,6 +2,7 @@ import getpass import socket from functools import partial +from epics import cainfo, PV from kafka.producer import KafkaProducer from qtpy.QtCore import QEvent, QModelIndex, QSortFilterProxyModel, Qt, Signal from qtpy.QtGui import QCursor @@ -165,8 +166,30 @@ def update_counter_label(self) -> None: else: self.alarm_count_label.setText(f"Acknowledged Alarms: {len(self.alarmModel.alarm_items)}") + def getThresholdValues(self, alarm_item_path) -> List[str]: + + result = [] + result.append(cainfo(alarm_item_path)) + + pv = PV(alarm_item_path) + print ("!!pv: ", pv) + ''' + thresholds = ["LOW", "LOLO", "HIGH", "HIHI"] + for currThreshold in thresholds: + currPVName = alarm_item_path + "." + currThreshold + val = caget(currPVName) + result.append(val) + ''' + return result + def alarm_context_menu_event(self, ev: QEvent) -> None: """Display the right-click context menu for items in the active alarms table""" + indices = self.get_selected_indices() + if len(indices) > 0: + alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] + #print ("!!alarm_item path: ", alarm_item.name) + vals = self.getThresholdValues(alarm_item.name) + #print ("!!!vals: ", vals) self.alarm_context_menu.popup(QCursor.pos()) def get_selected_indices(self) -> List[QModelIndex]: diff --git a/slam/main_window.py b/slam/main_window.py index 7095568..d32e058 100644 --- a/slam/main_window.py +++ b/slam/main_window.py @@ -209,6 +209,7 @@ def update_table( """ A slot for updating an alarm table """ + #print ('!!! path: ', path, ",n ame: ", name) if status == "Disabled": self.active_alarm_tables[alarm_config_name].alarmModel.remove_row(name) self.acknowledged_alarm_tables[alarm_config_name].alarmModel.remove_row(name) From 3e046b63715f86b3543c3b2dc875374a2ff272c7 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 20 Nov 2023 20:20:07 -0800 Subject: [PATCH 02/12] WIP: Threshold display functionality working --- slam/alarm_item.py | 15 ++++++++ slam/alarm_table_view.py | 82 +++++++++++++++++++++++++++++++--------- slam/alarm_tree_view.py | 65 ++++++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 19 deletions(-) diff --git a/slam/alarm_item.py b/slam/alarm_item.py index e2afa9a..27a9981 100644 --- a/slam/alarm_item.py +++ b/slam/alarm_item.py @@ -158,6 +158,20 @@ def is_acknowledged(self) -> bool: AlarmSeverity.UNDEFINED_ACK, ) + def is_undefined_or_invalid(self) -> bool: + """ + A convenience method for returning whether or not this item is undefiend or invalid. + (Basically returns if the alarm-item is colored some shade of purple) + This function is useful to check before calling some PyEpics calls like "cainfo", + which takes long time to return when called on undefined/invalid alarms. + """ + return self.alarm_severity in ( + AlarmSeverity.UNDEFINED, + AlarmSeverity.UNDEFINED_ACK, + AlarmSeverity.INVALID, + AlarmSeverity.INVALID_ACK, + ) + def is_in_active_alarm_state(self) -> bool: """A convenience method for returning whether or not this item is actively in an alarm state""" return self.alarm_severity in ( @@ -167,6 +181,7 @@ def is_in_active_alarm_state(self) -> bool: AlarmSeverity.UNDEFINED, ) + def display_color(self, severity) -> QBrush: """ Return a QBrush with the appropriate color for drawing this alarm based on severity diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index 86dfd10..27854ce 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -24,7 +24,7 @@ from .alarm_table_model import AlarmItemsTableModel from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action - +import re class AlarmTableType(str, enum.Enum): """ @@ -108,6 +108,8 @@ def __init__( self.unacknowledge_action = QAction("Unacknowledge") self.copy_action = QAction("Copy PV To Clipboard") self.plot_action = QAction("Draw Plot") + self.display_threshholds_menu = QMenu("Display Alarm Thresholds") + self.acknowledge_action.triggered.connect(partial(self.send_acknowledge_action, True)) self.unacknowledge_action.triggered.connect(partial(self.send_acknowledge_action, False)) self.plot_action.triggered.connect(self.plot_pv) @@ -120,6 +122,8 @@ def __init__( self.alarm_context_menu.addAction(self.copy_action) self.alarm_context_menu.addAction(self.plot_action) + self.alarm_context_menu.addMenu(self.display_threshholds_menu) + self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) self.alarmView.contextMenuEvent = self.alarm_context_menu_event @@ -144,6 +148,65 @@ def __init__( self.layout.addWidget(self.alarmView) self.alarmModel.layoutChanged.connect(self.update_counter_label) + self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) + + def handleThresholdDisplay(self): + indices = self.get_selected_indices() + index = indices[0] + alarm_item = None + if len(indices) > 0: + alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] + + info = None + hihi = high = low = lolo = "None" + + # Avoid calling "cainfo" on undefined alarm since causes the call to stall for a bit. + # Also we don't want thresholds from an undefined alarm anyway. + if alarm_item.is_undefined_or_invalid(): + self.display_threshholds_menu.clear() + return + + info = cainfo(alarm_item.name, False) # False arg is so call returns string + print (info) + + if info != None: + """ + "cainfo" just returns string, so need regex to extract values, + the following is example of values in the string: + + upper_alarm_limit = 130.0 + lower_alarm_limit = 90.0 + upper_warning_limit = 125.0 + lower_warning_limit = 90.0 + + """ + upper_alarm_limit_pattern = re.compile(r'upper_alarm_limit\s*=\s*([\d.]+)') + lower_alarm_limit_pattern = re.compile(r'lower_alarm_limit\s*=\s*([\d.]+)') + upper_warning_limit_pattern = re.compile(r'upper_warning_limit\s*=\s*([\d.]+)') + lower_warning_limit_pattern = re.compile(r'lower_warning_limit\s*=\s*([\d.]+)') + + hihi_search_result = upper_alarm_limit_pattern.search(info) + # threshold values are not always set + hihi = hihi_search_result.group(1) if hihi_search_result else "None" + + high_search_result = lower_alarm_limit_pattern.search(info) + high = high_search_result.group(1) if high_search_result else "None" + + low_search_result = upper_warning_limit_pattern.search(info) + low = low_search_result.group(1) if low_search_result else "None" + + lolo_search_result = lower_warning_limit_pattern.search(info) + lolo = lolo_search_result.group(1) if lolo_search_result else "None" + + + self.hihi_action = QAction("HIHI: " + hihi) + self.high_action = QAction("HIGH: " + high) + self.low_action = QAction("LOW: " + low) + self.lolo_action = QAction("LOLO: " + lolo) + self.display_threshholds_menu.addAction(self.hihi_action) + self.display_threshholds_menu.addAction(self.high_action) + self.display_threshholds_menu.addAction(self.low_action) + self.display_threshholds_menu.addAction(self.lolo_action) def filter_table(self) -> None: """Filter the table based on the text typed into the filter bar""" @@ -166,29 +229,12 @@ def update_counter_label(self) -> None: else: self.alarm_count_label.setText(f"Acknowledged Alarms: {len(self.alarmModel.alarm_items)}") - def getThresholdValues(self, alarm_item_path) -> List[str]: - - result = [] - result.append(cainfo(alarm_item_path)) - - pv = PV(alarm_item_path) - print ("!!pv: ", pv) - ''' - thresholds = ["LOW", "LOLO", "HIGH", "HIHI"] - for currThreshold in thresholds: - currPVName = alarm_item_path + "." + currThreshold - val = caget(currPVName) - result.append(val) - ''' - return result - def alarm_context_menu_event(self, ev: QEvent) -> None: """Display the right-click context menu for items in the active alarms table""" indices = self.get_selected_indices() if len(indices) > 0: alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] #print ("!!alarm_item path: ", alarm_item.name) - vals = self.getThresholdValues(alarm_item.name) #print ("!!!vals: ", vals) self.alarm_context_menu.popup(QCursor.pos()) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index e12ee3b..f104c74 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -11,7 +11,9 @@ from .alarm_item import AlarmItem, AlarmSeverity from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action - +from epics import cainfo, PV, caget +from typing import List +import re class AlarmTreeViewWidget(QWidget): """ @@ -70,6 +72,7 @@ def __init__( self.enable_action = QAction("Enable") self.disable_action = QAction("Disable") self.guidance_menu = QMenu("Guidance") + self.display_threshholds_menu = QMenu("Display Alarm Thresholds") self.display_actions = [] self.guidance_objects = [] @@ -84,9 +87,66 @@ def __init__( self.layout.addWidget(self.tree_view) + def handleThresholdDisplay(self): + indices = self.tree_view.selectedIndexes() + index = indices[0] + alarm_item = self.treeModel.getItem(index) + + info = None + hihi = high = low = lolo = "None" + + # Avoid calling "cainfo" on undefined alarm since causes the call to stall for a bit. + # Also we don't want thresholds from an undefined alarm anyway. + if alarm_item.is_undefined_or_invalid(): + self.display_threshholds_menu.clear() + return + + info = cainfo(alarm_item.name, False) # False arg is so call returns string + if info != None: + """ + "cainfo" just returns string, so need regex to extract values, + the following is example of values in the string: + + upper_alarm_limit = 130.0 + lower_alarm_limit = 90.0 + upper_warning_limit = 125.0 + lower_warning_limit = 90.0 + + """ + upper_alarm_limit_pattern = re.compile(r'upper_alarm_limit\s*=\s*([\d.]+)') + lower_alarm_limit_pattern = re.compile(r'lower_alarm_limit\s*=\s*([\d.]+)') + upper_warning_limit_pattern = re.compile(r'upper_warning_limit\s*=\s*([\d.]+)') + lower_warning_limit_pattern = re.compile(r'lower_warning_limit\s*=\s*([\d.]+)') + + hihi_search_result = upper_alarm_limit_pattern.search(info) + # threshold values are not always set, just display "None" if so + hihi = hihi_search_result.group(1) if hihi_search_result else "None" + + high_search_result = lower_alarm_limit_pattern.search(info) + high = high_search_result.group(1) if high_search_result else "None" + + low_search_result = upper_warning_limit_pattern.search(info) + low = low_search_result.group(1) if low_search_result else "None" + + lolo_search_result = lower_warning_limit_pattern.search(info) + lolo = lolo_search_result.group(1) if lolo_search_result else "None" + + + self.hihi_action = QAction("HIHI: " + hihi) + self.high_action = QAction("HIGH: " + high) + self.low_action = QAction("LOW: " + low) + self.lolo_action = QAction("LOLO: " + lolo) + self.display_threshholds_menu.addAction(self.hihi_action) + self.display_threshholds_menu.addAction(self.high_action) + self.display_threshholds_menu.addAction(self.low_action) + self.display_threshholds_menu.addAction(self.lolo_action) + def tree_menu(self, pos: QPoint) -> None: """Creates and displays the context menu to be displayed upon right clicking on an alarm item""" indices = self.tree_view.selectedIndexes() + index = indices[0] + name = self.treeModel.getItem(index).name + if len(indices) > 0: index = indices[0] alarm_item = self.treeModel.getItem(index) @@ -108,6 +168,7 @@ def tree_menu(self, pos: QPoint) -> None: self.context_menu.addAction(self.unacknowledge_action) self.context_menu.addAction(self.copy_action) self.context_menu.addAction(self.plot_action) + else: # Parent Node leaf_nodes = self.treeModel.get_all_leaf_nodes(alarm_item) add_acknowledge_action = False @@ -137,6 +198,8 @@ def tree_menu(self, pos: QPoint) -> None: self.context_menu.addAction(self.enable_action) self.context_menu.addAction(self.disable_action) self.context_menu.addMenu(self.guidance_menu) + self.context_menu.addMenu(self.display_threshholds_menu) + self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) # Make the entires from the config-page appear when alarm in tree is right-clicked indices = self.tree_view.selectedIndexes() From 3bf7224bba33c0f763f7842a33cfb1662bdcded7 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 20 Nov 2023 20:28:58 -0800 Subject: [PATCH 03/12] STY: Some cleanup for adding threshold-display feature --- slam/alarm_item.py | 7 +++--- slam/alarm_table_view.py | 54 +++++++++++++++++++--------------------- slam/alarm_tree_view.py | 31 +++++++++++------------ slam/main_window.py | 1 - 4 files changed, 43 insertions(+), 50 deletions(-) diff --git a/slam/alarm_item.py b/slam/alarm_item.py index 27a9981..e4d6077 100644 --- a/slam/alarm_item.py +++ b/slam/alarm_item.py @@ -162,15 +162,15 @@ def is_undefined_or_invalid(self) -> bool: """ A convenience method for returning whether or not this item is undefiend or invalid. (Basically returns if the alarm-item is colored some shade of purple) - This function is useful to check before calling some PyEpics calls like "cainfo", - which takes long time to return when called on undefined/invalid alarms. + This function is useful to check before using some PyEpics calls like "cainfo", + which takes long time to return when called on undefined/invalid alarms. """ return self.alarm_severity in ( AlarmSeverity.UNDEFINED, AlarmSeverity.UNDEFINED_ACK, AlarmSeverity.INVALID, AlarmSeverity.INVALID_ACK, - ) + ) def is_in_active_alarm_state(self) -> bool: """A convenience method for returning whether or not this item is actively in an alarm state""" @@ -181,7 +181,6 @@ def is_in_active_alarm_state(self) -> bool: AlarmSeverity.UNDEFINED, ) - def display_color(self, severity) -> QBrush: """ Return a QBrush with the appropriate color for drawing this alarm based on severity diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index 27854ce..f971f0a 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -1,8 +1,8 @@ import enum import getpass import socket +import re from functools import partial -from epics import cainfo, PV from kafka.producer import KafkaProducer from qtpy.QtCore import QEvent, QModelIndex, QSortFilterProxyModel, Qt, Signal from qtpy.QtGui import QCursor @@ -20,11 +20,12 @@ QVBoxLayout, QWidget, ) +from epics import cainfo from typing import Callable, List from .alarm_table_model import AlarmItemsTableModel from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action -import re + class AlarmTableType(str, enum.Enum): """ @@ -108,8 +109,7 @@ def __init__( self.unacknowledge_action = QAction("Unacknowledge") self.copy_action = QAction("Copy PV To Clipboard") self.plot_action = QAction("Draw Plot") - self.display_threshholds_menu = QMenu("Display Alarm Thresholds") - + self.display_thresholds_menu = QMenu("Display Alarm Thresholds") self.acknowledge_action.triggered.connect(partial(self.send_acknowledge_action, True)) self.unacknowledge_action.triggered.connect(partial(self.send_acknowledge_action, False)) self.plot_action.triggered.connect(self.plot_pv) @@ -122,8 +122,8 @@ def __init__( self.alarm_context_menu.addAction(self.copy_action) self.alarm_context_menu.addAction(self.plot_action) - self.alarm_context_menu.addMenu(self.display_threshholds_menu) - self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) + self.alarm_context_menu.addMenu(self.display_thresholds_menu) + self.display_thresholds_menu.aboutToShow.connect(self.handleThresholdDisplay) self.alarmView.contextMenuEvent = self.alarm_context_menu_event @@ -148,30 +148,29 @@ def __init__( self.layout.addWidget(self.alarmView) self.alarmModel.layoutChanged.connect(self.update_counter_label) - self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) def handleThresholdDisplay(self): indices = self.get_selected_indices() - index = indices[0] - alarm_item = None + indices[0] + alarm_item = None if len(indices) > 0: alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] info = None hihi = high = low = lolo = "None" - # Avoid calling "cainfo" on undefined alarm since causes the call to stall for a bit. + # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. # Also we don't want thresholds from an undefined alarm anyway. if alarm_item.is_undefined_or_invalid(): - self.display_threshholds_menu.clear() + self.display_thresholds_menu.clear() return - info = cainfo(alarm_item.name, False) # False arg is so call returns string - print (info) + info = cainfo(alarm_item.name, False) # False arg is so call returns string + print(info) - if info != None: + if info is not None: """ - "cainfo" just returns string, so need regex to extract values, + 'cainfo' just returns a string, so need regex to extract values, the following is example of values in the string: upper_alarm_limit = 130.0 @@ -180,15 +179,15 @@ def handleThresholdDisplay(self): lower_warning_limit = 90.0 """ - upper_alarm_limit_pattern = re.compile(r'upper_alarm_limit\s*=\s*([\d.]+)') - lower_alarm_limit_pattern = re.compile(r'lower_alarm_limit\s*=\s*([\d.]+)') - upper_warning_limit_pattern = re.compile(r'upper_warning_limit\s*=\s*([\d.]+)') - lower_warning_limit_pattern = re.compile(r'lower_warning_limit\s*=\s*([\d.]+)') + upper_alarm_limit_pattern = re.compile(r"upper_alarm_limit\s*=\s*([\d.]+)") + lower_alarm_limit_pattern = re.compile(r"lower_alarm_limit\s*=\s*([\d.]+)") + upper_warning_limit_pattern = re.compile(r"upper_warning_limit\s*=\s*([\d.]+)") + lower_warning_limit_pattern = re.compile(r"lower_warning_limit\s*=\s*([\d.]+)") hihi_search_result = upper_alarm_limit_pattern.search(info) - # threshold values are not always set + # threshold values are not always set hihi = hihi_search_result.group(1) if hihi_search_result else "None" - + high_search_result = lower_alarm_limit_pattern.search(info) high = high_search_result.group(1) if high_search_result else "None" @@ -198,15 +197,14 @@ def handleThresholdDisplay(self): lolo_search_result = lower_warning_limit_pattern.search(info) lolo = lolo_search_result.group(1) if lolo_search_result else "None" - self.hihi_action = QAction("HIHI: " + hihi) self.high_action = QAction("HIGH: " + high) self.low_action = QAction("LOW: " + low) self.lolo_action = QAction("LOLO: " + lolo) - self.display_threshholds_menu.addAction(self.hihi_action) - self.display_threshholds_menu.addAction(self.high_action) - self.display_threshholds_menu.addAction(self.low_action) - self.display_threshholds_menu.addAction(self.lolo_action) + self.display_thresholds_menu.addAction(self.hihi_action) + self.display_thresholds_menu.addAction(self.high_action) + self.display_thresholds_menu.addAction(self.low_action) + self.display_thresholds_menu.addAction(self.lolo_action) def filter_table(self) -> None: """Filter the table based on the text typed into the filter bar""" @@ -233,9 +231,7 @@ def alarm_context_menu_event(self, ev: QEvent) -> None: """Display the right-click context menu for items in the active alarms table""" indices = self.get_selected_indices() if len(indices) > 0: - alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] - #print ("!!alarm_item path: ", alarm_item.name) - #print ("!!!vals: ", vals) + list(self.alarmModel.alarm_items.items())[indices[0].row()][1] self.alarm_context_menu.popup(QCursor.pos()) def get_selected_indices(self) -> List[QModelIndex]: diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index f104c74..955ad7a 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -1,5 +1,6 @@ import getpass import socket +import re from functools import partial from kafka.producer import KafkaProducer from pydm.display import load_file @@ -11,9 +12,8 @@ from .alarm_item import AlarmItem, AlarmSeverity from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action -from epics import cainfo, PV, caget -from typing import List -import re +from epics import cainfo + class AlarmTreeViewWidget(QWidget): """ @@ -95,16 +95,16 @@ def handleThresholdDisplay(self): info = None hihi = high = low = lolo = "None" - # Avoid calling "cainfo" on undefined alarm since causes the call to stall for a bit. + # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. # Also we don't want thresholds from an undefined alarm anyway. if alarm_item.is_undefined_or_invalid(): self.display_threshholds_menu.clear() return - info = cainfo(alarm_item.name, False) # False arg is so call returns string - if info != None: + info = cainfo(alarm_item.name, False) # False arg is so call returns string + if info is not None: """ - "cainfo" just returns string, so need regex to extract values, + 'cainfo' just returns a string so need regex to extract values, the following is example of values in the string: upper_alarm_limit = 130.0 @@ -113,15 +113,15 @@ def handleThresholdDisplay(self): lower_warning_limit = 90.0 """ - upper_alarm_limit_pattern = re.compile(r'upper_alarm_limit\s*=\s*([\d.]+)') - lower_alarm_limit_pattern = re.compile(r'lower_alarm_limit\s*=\s*([\d.]+)') - upper_warning_limit_pattern = re.compile(r'upper_warning_limit\s*=\s*([\d.]+)') - lower_warning_limit_pattern = re.compile(r'lower_warning_limit\s*=\s*([\d.]+)') + upper_alarm_limit_pattern = re.compile(r"upper_alarm_limit\s*=\s*([\d.]+)") + lower_alarm_limit_pattern = re.compile(r"lower_alarm_limit\s*=\s*([\d.]+)") + upper_warning_limit_pattern = re.compile(r"upper_warning_limit\s*=\s*([\d.]+)") + lower_warning_limit_pattern = re.compile(r"lower_warning_limit\s*=\s*([\d.]+)") hihi_search_result = upper_alarm_limit_pattern.search(info) - # threshold values are not always set, just display "None" if so + # threshold values are not always set, just display "None" if so hihi = hihi_search_result.group(1) if hihi_search_result else "None" - + high_search_result = lower_alarm_limit_pattern.search(info) high = high_search_result.group(1) if high_search_result else "None" @@ -131,7 +131,6 @@ def handleThresholdDisplay(self): lolo_search_result = lower_warning_limit_pattern.search(info) lolo = lolo_search_result.group(1) if lolo_search_result else "None" - self.hihi_action = QAction("HIHI: " + hihi) self.high_action = QAction("HIGH: " + high) self.low_action = QAction("LOW: " + low) @@ -140,12 +139,12 @@ def handleThresholdDisplay(self): self.display_threshholds_menu.addAction(self.high_action) self.display_threshholds_menu.addAction(self.low_action) self.display_threshholds_menu.addAction(self.lolo_action) - + def tree_menu(self, pos: QPoint) -> None: """Creates and displays the context menu to be displayed upon right clicking on an alarm item""" indices = self.tree_view.selectedIndexes() index = indices[0] - name = self.treeModel.getItem(index).name + self.treeModel.getItem(index).name if len(indices) > 0: index = indices[0] diff --git a/slam/main_window.py b/slam/main_window.py index d32e058..7095568 100644 --- a/slam/main_window.py +++ b/slam/main_window.py @@ -209,7 +209,6 @@ def update_table( """ A slot for updating an alarm table """ - #print ('!!! path: ', path, ",n ame: ", name) if status == "Disabled": self.active_alarm_tables[alarm_config_name].alarmModel.remove_row(name) self.acknowledged_alarm_tables[alarm_config_name].alarmModel.remove_row(name) From 9070df0a09d4e0947483d27d99edc2668fcf1d40 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 20 Nov 2023 20:54:08 -0800 Subject: [PATCH 04/12] ENH: Add option for viewing alarm-thresholds for an alarm-item in right-click context menu --- slam/alarm_table_view.py | 18 ++++++++++++------ slam/alarm_tree_view.py | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index f971f0a..db85621 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -150,11 +150,20 @@ def __init__( self.alarmModel.layoutChanged.connect(self.update_counter_label) def handleThresholdDisplay(self): + # If multiple alarm-items selected, just display thresholds for 1st item. + # (or don't display anything if 1st item is undefined/invalid). + # This follows how the "Draw Plot" option handles multiple selected items. indices = self.get_selected_indices() - indices[0] alarm_item = None if len(indices) > 0: - alarm_item = list(self.alarmModel.alarm_items.items())[indices[0].row()][1] + index = indices[0] + alarm_item = list(self.alarmModel.alarm_items.items())[index.row()][1] + else: + return + + # If not a leaf its an nvalid 'cainfo' call which could stall things for a while. + if not alarm_item.is_leaf(): + return info = None hihi = high = low = lolo = "None" @@ -166,7 +175,6 @@ def handleThresholdDisplay(self): return info = cainfo(alarm_item.name, False) # False arg is so call returns string - print(info) if info is not None: """ @@ -197,6 +205,7 @@ def handleThresholdDisplay(self): lolo_search_result = lower_warning_limit_pattern.search(info) lolo = lolo_search_result.group(1) if lolo_search_result else "None" + # we display threshold values as 4 items in a drop-down menu self.hihi_action = QAction("HIHI: " + hihi) self.high_action = QAction("HIGH: " + high) self.low_action = QAction("LOW: " + low) @@ -229,9 +238,6 @@ def update_counter_label(self) -> None: def alarm_context_menu_event(self, ev: QEvent) -> None: """Display the right-click context menu for items in the active alarms table""" - indices = self.get_selected_indices() - if len(indices) > 0: - list(self.alarmModel.alarm_items.items())[indices[0].row()][1] self.alarm_context_menu.popup(QCursor.pos()) def get_selected_indices(self) -> List[QModelIndex]: diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index 955ad7a..7b23b12 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -92,6 +92,10 @@ def handleThresholdDisplay(self): index = indices[0] alarm_item = self.treeModel.getItem(index) + # If not a leaf its an nvalid 'cainfo' call which could stall things for a while. + if not alarm_item.is_leaf(): + return + info = None hihi = high = low = lolo = "None" @@ -131,6 +135,7 @@ def handleThresholdDisplay(self): lolo_search_result = lower_warning_limit_pattern.search(info) lolo = lolo_search_result.group(1) if lolo_search_result else "None" + # we display threshold values as 4 items in a drop-down menu self.hihi_action = QAction("HIHI: " + hihi) self.high_action = QAction("HIGH: " + high) self.low_action = QAction("LOW: " + low) From 5b56ddbf71d1d0f98acc3f09deeb8f64f45a21ce Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 20 Nov 2023 20:58:38 -0800 Subject: [PATCH 05/12] STY: Minor style fixes for viewing thresholds feature --- slam/alarm_table_view.py | 7 ++++--- slam/alarm_tree_view.py | 10 +++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index db85621..a03f970 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -155,6 +155,9 @@ def handleThresholdDisplay(self): # This follows how the "Draw Plot" option handles multiple selected items. indices = self.get_selected_indices() alarm_item = None + info = None + hihi = high = low = lolo = "None" + if len(indices) > 0: index = indices[0] alarm_item = list(self.alarmModel.alarm_items.items())[index.row()][1] @@ -165,12 +168,10 @@ def handleThresholdDisplay(self): if not alarm_item.is_leaf(): return - info = None - hihi = high = low = lolo = "None" - # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. # Also we don't want thresholds from an undefined alarm anyway. if alarm_item.is_undefined_or_invalid(): + # Don't display any of the threshold-display actions if alarm-item undefined self.display_thresholds_menu.clear() return diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index 7b23b12..e8a42dc 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -91,17 +91,17 @@ def handleThresholdDisplay(self): indices = self.tree_view.selectedIndexes() index = indices[0] alarm_item = self.treeModel.getItem(index) + info = None + hihi = high = low = lolo = "None" # If not a leaf its an nvalid 'cainfo' call which could stall things for a while. if not alarm_item.is_leaf(): return - info = None - hihi = high = low = lolo = "None" - # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. # Also we don't want thresholds from an undefined alarm anyway. if alarm_item.is_undefined_or_invalid(): + # Don't display any of the threshold-display actions if alarm-item undefined self.display_threshholds_menu.clear() return @@ -148,9 +148,6 @@ def handleThresholdDisplay(self): def tree_menu(self, pos: QPoint) -> None: """Creates and displays the context menu to be displayed upon right clicking on an alarm item""" indices = self.tree_view.selectedIndexes() - index = indices[0] - self.treeModel.getItem(index).name - if len(indices) > 0: index = indices[0] alarm_item = self.treeModel.getItem(index) @@ -172,7 +169,6 @@ def tree_menu(self, pos: QPoint) -> None: self.context_menu.addAction(self.unacknowledge_action) self.context_menu.addAction(self.copy_action) self.context_menu.addAction(self.plot_action) - else: # Parent Node leaf_nodes = self.treeModel.get_all_leaf_nodes(alarm_item) add_acknowledge_action = False From 918ab57384310099f55685e129575d5ee09708c4 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 20 Nov 2023 21:54:02 -0800 Subject: [PATCH 06/12] STY: very minor, move comment to better spot --- slam/alarm_table_view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index a03f970..2002c0d 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -150,14 +150,14 @@ def __init__( self.alarmModel.layoutChanged.connect(self.update_counter_label) def handleThresholdDisplay(self): - # If multiple alarm-items selected, just display thresholds for 1st item. - # (or don't display anything if 1st item is undefined/invalid). - # This follows how the "Draw Plot" option handles multiple selected items. indices = self.get_selected_indices() alarm_item = None info = None hihi = high = low = lolo = "None" + # If multiple alarm-items selected, just display thresholds for 1st item. + # (or don't display anything if 1st item is undefined/invalid). + # This follows how the "Draw Plot" option handles multiple selected items. if len(indices) > 0: index = indices[0] alarm_item = list(self.alarmModel.alarm_items.items())[index.row()][1] From e6c07a61e06fc146563dabce0e440aec06b15f23 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Tue, 21 Nov 2023 17:04:44 -0800 Subject: [PATCH 07/12] MNT: For displaying threshold-info, switch from using cainfo to PV objects PV object lets use access threshold-values directly without regex parsing the result-string of cainfo --- slam/alarm_item.py | 1 + slam/alarm_table_view.py | 71 ++++++++++++++++----------------- slam/alarm_tree_view.py | 85 +++++++++++++++++++--------------------- 3 files changed, 74 insertions(+), 83 deletions(-) diff --git a/slam/alarm_item.py b/slam/alarm_item.py index e4d6077..18b0dee 100644 --- a/slam/alarm_item.py +++ b/slam/alarm_item.py @@ -130,6 +130,7 @@ def __init__( self.annunciating = annunciating self.delay = delay self.alarm_filter = alarm_filter + self.pv_object = None def is_leaf(self) -> bool: """Return whether or not this alarm is associated with a leaf node in its configured hierarchy""" diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index 2002c0d..ea6b8ef 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -1,7 +1,6 @@ import enum import getpass import socket -import re from functools import partial from kafka.producer import KafkaProducer from qtpy.QtCore import QEvent, QModelIndex, QSortFilterProxyModel, Qt, Signal @@ -20,11 +19,12 @@ QVBoxLayout, QWidget, ) -from epics import cainfo +from epics import PV from typing import Callable, List from .alarm_table_model import AlarmItemsTableModel from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action +from math import isnan class AlarmTableType(str, enum.Enum): @@ -152,8 +152,7 @@ def __init__( def handleThresholdDisplay(self): indices = self.get_selected_indices() alarm_item = None - info = None - hihi = high = low = lolo = "None" + hihi = high = low = lolo = -1 # If multiple alarm-items selected, just display thresholds for 1st item. # (or don't display anything if 1st item is undefined/invalid). @@ -164,8 +163,10 @@ def handleThresholdDisplay(self): else: return - # If not a leaf its an nvalid 'cainfo' call which could stall things for a while. + # If not a leaf its an invalid 'cainfo' call which could stall things for a while. if not alarm_item.is_leaf(): + # Don't display any of the threshold-display actions if selected non-leaf + self.display_thresholds_menu.clear() return # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. @@ -175,42 +176,36 @@ def handleThresholdDisplay(self): self.display_thresholds_menu.clear() return - info = cainfo(alarm_item.name, False) # False arg is so call returns string - - if info is not None: - """ - 'cainfo' just returns a string, so need regex to extract values, - the following is example of values in the string: - - upper_alarm_limit = 130.0 - lower_alarm_limit = 90.0 - upper_warning_limit = 125.0 - lower_warning_limit = 90.0 - - """ - upper_alarm_limit_pattern = re.compile(r"upper_alarm_limit\s*=\s*([\d.]+)") - lower_alarm_limit_pattern = re.compile(r"lower_alarm_limit\s*=\s*([\d.]+)") - upper_warning_limit_pattern = re.compile(r"upper_warning_limit\s*=\s*([\d.]+)") - lower_warning_limit_pattern = re.compile(r"lower_warning_limit\s*=\s*([\d.]+)") - - hihi_search_result = upper_alarm_limit_pattern.search(info) - # threshold values are not always set - hihi = hihi_search_result.group(1) if hihi_search_result else "None" - - high_search_result = lower_alarm_limit_pattern.search(info) - high = high_search_result.group(1) if high_search_result else "None" - - low_search_result = upper_warning_limit_pattern.search(info) - low = low_search_result.group(1) if low_search_result else "None" + if alarm_item.pv_object is None: + alarm_item.pv_object = PV(alarm_item.name) + # Update the values only when user requests them in right-click menu + alarm_item.pv_object.clear_auto_monitor() + + alarm_item_metadata = alarm_item.pv_object.get_ctrlvars() + print(alarm_item_metadata) + + # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), + # in this case don't display any threshold sub-menus + if ( + alarm_item_metadata is not None + and len(alarm_item_metadata) > 0 + and "upper_alarm_limit" not in alarm_item_metadata + ): + self.display_thresholds_menu.clear() + return - lolo_search_result = lower_warning_limit_pattern.search(info) - lolo = lolo_search_result.group(1) if lolo_search_result else "None" + # threshold values are not always set, just display "None" if so + # upper_alarm_limit here is same as calling caget for pv's '.HIHI' + hihi = alarm_item_metadata["upper_alarm_limit"] + lolo = alarm_item_metadata["lower_alarm_limit"] + high = alarm_item_metadata["upper_warning_limit"] + low = alarm_item_metadata["lower_warning_limit"] # we display threshold values as 4 items in a drop-down menu - self.hihi_action = QAction("HIHI: " + hihi) - self.high_action = QAction("HIGH: " + high) - self.low_action = QAction("LOW: " + low) - self.lolo_action = QAction("LOLO: " + lolo) + self.hihi_action = QAction("HIHI: " + str(hihi)) if not isnan(hihi) else QAction("HIHI: Not set") + self.high_action = QAction("HIGH: " + str(high)) if not isnan(high) else QAction("HIGH: Not set") + self.low_action = QAction("LOW: " + str(low)) if not isnan(low) else QAction("LOW: Not set") + self.lolo_action = QAction("LOLO: " + str(lolo)) if not isnan(lolo) else QAction("LOLO: Not set") self.display_thresholds_menu.addAction(self.hihi_action) self.display_thresholds_menu.addAction(self.high_action) self.display_thresholds_menu.addAction(self.low_action) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index e8a42dc..5835653 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -1,6 +1,5 @@ import getpass import socket -import re from functools import partial from kafka.producer import KafkaProducer from pydm.display import load_file @@ -12,7 +11,8 @@ from .alarm_item import AlarmItem, AlarmSeverity from .alarm_tree_model import AlarmItemsTreeModel from .permissions import UserAction, can_take_action -from epics import cainfo +from math import isnan +from epics import PV class AlarmTreeViewWidget(QWidget): @@ -72,7 +72,7 @@ def __init__( self.enable_action = QAction("Enable") self.disable_action = QAction("Disable") self.guidance_menu = QMenu("Guidance") - self.display_threshholds_menu = QMenu("Display Alarm Thresholds") + self.display_thresholds_menu = QMenu("Display Alarm Thresholds") self.display_actions = [] self.guidance_objects = [] @@ -91,59 +91,54 @@ def handleThresholdDisplay(self): indices = self.tree_view.selectedIndexes() index = indices[0] alarm_item = self.treeModel.getItem(index) - info = None - hihi = high = low = lolo = "None" + hihi = high = low = lolo = -1 - # If not a leaf its an nvalid 'cainfo' call which could stall things for a while. + # If not a leaf its an invalid 'cainfo' call which could stall things for a while. if not alarm_item.is_leaf(): + # Don't display any of the threshold-display actions if selected non-leaf + self.display_thresholds_menu.clear() return # Avoid calling 'cainfo' on undefined alarm since causes the call to stall for a bit. # Also we don't want thresholds from an undefined alarm anyway. if alarm_item.is_undefined_or_invalid(): # Don't display any of the threshold-display actions if alarm-item undefined - self.display_threshholds_menu.clear() + self.display_thresholds_menu.clear() return - info = cainfo(alarm_item.name, False) # False arg is so call returns string - if info is not None: - """ - 'cainfo' just returns a string so need regex to extract values, - the following is example of values in the string: - - upper_alarm_limit = 130.0 - lower_alarm_limit = 90.0 - upper_warning_limit = 125.0 - lower_warning_limit = 90.0 - - """ - upper_alarm_limit_pattern = re.compile(r"upper_alarm_limit\s*=\s*([\d.]+)") - lower_alarm_limit_pattern = re.compile(r"lower_alarm_limit\s*=\s*([\d.]+)") - upper_warning_limit_pattern = re.compile(r"upper_warning_limit\s*=\s*([\d.]+)") - lower_warning_limit_pattern = re.compile(r"lower_warning_limit\s*=\s*([\d.]+)") - - hihi_search_result = upper_alarm_limit_pattern.search(info) - # threshold values are not always set, just display "None" if so - hihi = hihi_search_result.group(1) if hihi_search_result else "None" - - high_search_result = lower_alarm_limit_pattern.search(info) - high = high_search_result.group(1) if high_search_result else "None" - - low_search_result = upper_warning_limit_pattern.search(info) - low = low_search_result.group(1) if low_search_result else "None" + if alarm_item.pv_object is None: + alarm_item.pv_object = PV(alarm_item.name) + # Update the values only when user requests them in right-click menu + alarm_item.pv_object.clear_auto_monitor() + + alarm_item_metadata = alarm_item.pv_object.get_ctrlvars() + + # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), + # in this case don't display any threshold sub-menus + if ( + alarm_item_metadata is not None + and len(alarm_item_metadata) > 0 + and "upper_alarm_limit" not in alarm_item_metadata + ): + self.display_thresholds_menu.clear() + return - lolo_search_result = lower_warning_limit_pattern.search(info) - lolo = lolo_search_result.group(1) if lolo_search_result else "None" + # threshold values are not always set, just display "None" if so + # upper_alarm_limit here is same as calling caget for pv's '.HIHI' + hihi = alarm_item_metadata["upper_alarm_limit"] + lolo = alarm_item_metadata["lower_alarm_limit"] + high = alarm_item_metadata["upper_warning_limit"] + low = alarm_item_metadata["lower_warning_limit"] # we display threshold values as 4 items in a drop-down menu - self.hihi_action = QAction("HIHI: " + hihi) - self.high_action = QAction("HIGH: " + high) - self.low_action = QAction("LOW: " + low) - self.lolo_action = QAction("LOLO: " + lolo) - self.display_threshholds_menu.addAction(self.hihi_action) - self.display_threshholds_menu.addAction(self.high_action) - self.display_threshholds_menu.addAction(self.low_action) - self.display_threshholds_menu.addAction(self.lolo_action) + self.hihi_action = QAction("HIHI: " + str(hihi)) if not isnan(hihi) else QAction("HIHI: Not set") + self.high_action = QAction("HIGH: " + str(high)) if not isnan(high) else QAction("HIGH: Not set") + self.low_action = QAction("LOW: " + str(low)) if not isnan(low) else QAction("LOW: Not set") + self.lolo_action = QAction("LOLO: " + str(lolo)) if not isnan(lolo) else QAction("LOLO: Not set") + self.display_thresholds_menu.addAction(self.hihi_action) + self.display_thresholds_menu.addAction(self.high_action) + self.display_thresholds_menu.addAction(self.low_action) + self.display_thresholds_menu.addAction(self.lolo_action) def tree_menu(self, pos: QPoint) -> None: """Creates and displays the context menu to be displayed upon right clicking on an alarm item""" @@ -198,8 +193,8 @@ def tree_menu(self, pos: QPoint) -> None: self.context_menu.addAction(self.enable_action) self.context_menu.addAction(self.disable_action) self.context_menu.addMenu(self.guidance_menu) - self.context_menu.addMenu(self.display_threshholds_menu) - self.display_threshholds_menu.aboutToShow.connect(self.handleThresholdDisplay) + self.context_menu.addMenu(self.display_thresholds_menu) + self.display_thresholds_menu.aboutToShow.connect(self.handleThresholdDisplay) # Make the entires from the config-page appear when alarm in tree is right-clicked indices = self.tree_view.selectedIndexes() From 9f35ac6ab40ffe65e8a46dc84a1603a77c670cb1 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Mon, 27 Nov 2023 18:58:09 -0800 Subject: [PATCH 08/12] STY: remove debugging print --- slam/alarm_table_view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/slam/alarm_table_view.py b/slam/alarm_table_view.py index ea6b8ef..b138c5c 100644 --- a/slam/alarm_table_view.py +++ b/slam/alarm_table_view.py @@ -182,7 +182,6 @@ def handleThresholdDisplay(self): alarm_item.pv_object.clear_auto_monitor() alarm_item_metadata = alarm_item.pv_object.get_ctrlvars() - print(alarm_item_metadata) # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), # in this case don't display any threshold sub-menus From 6bde0160489e0eb405527f05d355c7453159118c Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Thu, 14 Dec 2023 01:58:27 -0800 Subject: [PATCH 09/12] BUG: when displaying thresholds, log error msg if metadata not recieved --- slam/alarm_tree_view.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index 5835653..fb58e76 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -1,5 +1,6 @@ import getpass import socket +import logging from functools import partial from kafka.producer import KafkaProducer from pydm.display import load_file @@ -14,6 +15,7 @@ from math import isnan from epics import PV +logger = logging.getLogger(__name__) class AlarmTreeViewWidget(QWidget): """ @@ -105,21 +107,22 @@ def handleThresholdDisplay(self): # Don't display any of the threshold-display actions if alarm-item undefined self.display_thresholds_menu.clear() return - + + # Make pv_object if first time item's threshold is requested if alarm_item.pv_object is None: - alarm_item.pv_object = PV(alarm_item.name) # Update the values only when user requests them in right-click menu - alarm_item.pv_object.clear_auto_monitor() + alarm_item.pv_object = PV(alarm_item.name, auto_monitor=False) alarm_item_metadata = alarm_item.pv_object.get_ctrlvars() # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), # in this case don't display any threshold sub-menus - if ( - alarm_item_metadata is not None - and len(alarm_item_metadata) > 0 - and "upper_alarm_limit" not in alarm_item_metadata - ): + if alarm_item_metadata is None: + logger.debug(f"Can't connect to PV: {alarm_item.name}") + self.display_thresholds_menu.clear() + return + elif len(alarm_item_metadata) > 0 and "upper_alarm_limit" not in alarm_item_metadata: + logger.debug(f"No threshold data for PV: {alarm_item.name}") self.display_thresholds_menu.clear() return From 3a186dd54fb01d3d6dcaa79894c6f52a6b91a904 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Thu, 14 Dec 2023 02:10:25 -0800 Subject: [PATCH 10/12] BUG: do quick check if pv is connected before getting threshold info when requested --- slam/alarm_tree_view.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index fb58e76..abc9727 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -113,6 +113,12 @@ def handleThresholdDisplay(self): # Update the values only when user requests them in right-click menu alarm_item.pv_object = PV(alarm_item.name, auto_monitor=False) + # Do a get call we can quickly timeout, so if PV not-connected don't + # need to wait for slower get_ctrlvars() call. + # 0.1 is small arbitrary value, can be made larger if timing-out for + # actually connected PVs. + if alarm_item.pv_object.get(timeout=0.1) is None: + return alarm_item_metadata = alarm_item.pv_object.get_ctrlvars() # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), From bd56e71d7f9afff500447e804467be4fb7f67392 Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Thu, 14 Dec 2023 09:08:23 -0800 Subject: [PATCH 11/12] STY: fix pre-commit issues --- slam/alarm_tree_view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index abc9727..f891f90 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -17,6 +17,7 @@ logger = logging.getLogger(__name__) + class AlarmTreeViewWidget(QWidget): """ The TreeViewWidget is a collection of everything needed to display and interact with the alarm tree. @@ -107,7 +108,7 @@ def handleThresholdDisplay(self): # Don't display any of the threshold-display actions if alarm-item undefined self.display_thresholds_menu.clear() return - + # Make pv_object if first time item's threshold is requested if alarm_item.pv_object is None: # Update the values only when user requests them in right-click menu From af3ebf848e2e12e3d68d41555c058acbf637299a Mon Sep 17 00:00:00 2001 From: Nolan Stelter Date: Tue, 19 Dec 2023 11:29:02 -0800 Subject: [PATCH 12/12] DEV: set threshold can't connect log to warn mode --- slam/alarm_tree_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slam/alarm_tree_view.py b/slam/alarm_tree_view.py index f891f90..e4e64b1 100644 --- a/slam/alarm_tree_view.py +++ b/slam/alarm_tree_view.py @@ -125,11 +125,11 @@ def handleThresholdDisplay(self): # Getting data can fail for some PV's, good metadata will always have a key for all 4 limits (nan if not set), # in this case don't display any threshold sub-menus if alarm_item_metadata is None: - logger.debug(f"Can't connect to PV: {alarm_item.name}") + logger.warn(f"Can't connect to PV: {alarm_item.name}") self.display_thresholds_menu.clear() return elif len(alarm_item_metadata) > 0 and "upper_alarm_limit" not in alarm_item_metadata: - logger.debug(f"No threshold data for PV: {alarm_item.name}") + logger.warn(f"No threshold data for PV: {alarm_item.name}") self.display_thresholds_menu.clear() return