Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sound when pv enters alarm state #59

Merged
merged 24 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9657763
ENH: Add cmdline arg for enabling annunciate mode, atm does nothing i…
Sep 8, 2023
ceff361
WIP: Annunciate button disabled/enabled depending on flag
Sep 12, 2023
5cb899b
WIP: Make annunciate flag a switch-like flag, if present annunciate=T…
Sep 12, 2023
ed956cd
WIP: Added noise for when alarm is signaled
Sep 12, 2023
930165e
WIP: Printing out msg when annunciate is enabled in cmd line and for …
Sep 12, 2023
287ef5b
WIP: Clean up some unneeded passing of annunciate variable
Sep 13, 2023
7f2d70b
WIP: Remove annunciate debugging prints
Sep 13, 2023
5826957
BUG: Fix issue where pv with annunciate checked where not getting the…
Sep 13, 2023
cd92885
TST: Fixing tests after annunciate-mode changes
Sep 14, 2023
ad1f959
WIP: Fix issue where cryo sent different status string than lcls, add…
Sep 14, 2023
fb78c33
TST: Update annunciate test to check for bell character in stdout
Oct 2, 2023
6c299c0
STY: Apply pre-commit changes
Oct 2, 2023
4b156d3
BUG: check against STATE_ALARM string to see if alarm is active state
Oct 3, 2023
f1939a0
STY: Apply pre-commit changes
Oct 3, 2023
7240593
BUG: Switch to using alarmitem member function to check if in alarm s…
Oct 5, 2023
53e5237
DEV: Remove debugging prints
Oct 5, 2023
d59e8cb
STY: style fixes for annunicate feature
Oct 18, 2023
70b9aae
WIP: Fix test for annunciate mode
Oct 18, 2023
2e6cd30
STY: minor style fix related to add annunciate mode
Oct 18, 2023
6286bee
STY: Minor cleanup for annunciate mode changes
Oct 18, 2023
cd3deba
STY: minor style fix
Nov 14, 2023
b479fe0
STY: Improve some comments related to annunciate option
Nov 14, 2023
64ae0f9
STY: apply pre-commit changes
Nov 14, 2023
dd831fe
ENH: Add short-hand cmd line option for user-permissions
Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions slam/alarm_configuration_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ class AlarmConfigurationWidget(QDialog):
"""

def __init__(
self, alarm_item: AlarmItem, kafka_producer: KafkaProducer, topic: str, parent: Optional[QObject] = None
self,
alarm_item: AlarmItem,
kafka_producer: KafkaProducer,
topic: str,
annunciate: bool = False,
parent: Optional[QObject] = None,
):
super().__init__(parent=parent)
self.alarm_item = alarm_item
Expand All @@ -60,10 +65,10 @@ def __init__(

self.behavior_label = QLabel("Behavior:")
self.enabled_checkbox = QCheckBox("Enabled")
self.enabled_checkbox.clicked.connect(self.update_enabled_checkbox_pre_disabled_value)

self.latch_checkbox = QCheckBox("Latched")
self.annunciate_checkbox = QCheckBox("Annunciate")
annunciate_checkbox_text = "Annunciate" if annunciate else "Annunciate (disabled)"
self.annunciate_checkbox = QCheckBox(annunciate_checkbox_text)
self.annunciate_checkbox.setEnabled(annunciate)

self.disable_date_label = QLabel("Disable Until:")
self.minimum_datetime = QDateTime.currentDateTime().addDays(-1)
Expand Down
1 change: 1 addition & 0 deletions slam/alarm_table_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(
topic: str,
table_type: AlarmTableType,
plot_slot: Callable,
annunciate: bool = False,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not currently used for tables but maybe could be useful in the future

):
super().__init__()
self.resize(1035, 600)
Expand Down
10 changes: 9 additions & 1 deletion slam/alarm_tree_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class AlarmItemsTreeModel(QAbstractItemModel):
The parent of this model.
"""

def __init__(self, enable_all_topic: bool = False, parent: Optional[QObject] = None):
def __init__(self, annunciate: bool = False, enable_all_topic: bool = False, parent: Optional[QObject] = None):
super().__init__(parent)
self.root_item = AlarmItem("")
self.nodes = []
Expand All @@ -27,6 +27,7 @@ def __init__(self, enable_all_topic: bool = False, parent: Optional[QObject] = N
if self.enable_all_topic:
self.nodes.insert(0, self.root_item)
self.added_paths = dict() # Mapping from PV name to all associated paths in the tree (will be just 1 for most)
self.annunciate = annunciate

def clear(self) -> None:
"""Clear out all the nodes in this tree and set the root to an empty item"""
Expand Down Expand Up @@ -165,6 +166,13 @@ def update_item(
item_to_update.filtered = True
elif item_to_update.filtered:
item_to_update.filtered = False
# also ensure annunciate is enabled on application level (self.annunciate) and also for the current item.
if item_to_update.is_in_active_alarm_state() and (self.annunciate and item_to_update.annunciating):
# prints bell character, cross platform way to generate "beep" noise
# (assuming the user has the bell-sound option enabled for their terminal),
# could be replaced with call to audio library for more sound options
print("\a")

self.layoutChanged.emit()

def update_model(self, item_path: str, values: dict) -> None:
Expand Down
11 changes: 9 additions & 2 deletions slam/alarm_tree_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(
topic: str,
plot_slot: Callable,
enable_all_topic: bool = False,
annunciate: bool = False,
):
super().__init__()

Expand All @@ -45,10 +46,12 @@ def __init__(
self.plot_slot = plot_slot
self.plot_signal.connect(self.plot_slot)
self.clipboard = QApplication.clipboard()
self.annunciate = annunciate

self.setFont(QFont("Arial", 12))
self.layout = QVBoxLayout(self)
self.treeModel = AlarmItemsTreeModel(enable_all_topic)

self.treeModel = AlarmItemsTreeModel(annunciate, enable_all_topic)
self.tree_view = QTreeView(self)
self.tree_view.setProperty("showDropIndicator", False)
self.tree_view.setDragDropOverwriteMode(False)
Expand Down Expand Up @@ -179,7 +182,11 @@ def create_alarm_configuration_widget(self, index: QModelIndex) -> None:
"""Create and display the alarm configuration widget for the alarm item with the input index"""
alarm_item = self.treeModel.getItem(index)
alarm_config_window = AlarmConfigurationWidget(
alarm_item=alarm_item, kafka_producer=self.kafka_producer, topic=self.topic, parent=self
alarm_item=alarm_item,
kafka_producer=self.kafka_producer,
topic=self.topic,
parent=self,
annunciate=self.annunciate,
)
alarm_config_window.show()

Expand Down
19 changes: 15 additions & 4 deletions slam/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AlarmHandlerMainWindow(QMainWindow):

alarm_update_signal = Signal(str, str, str, AlarmSeverity, str, datetime, str, AlarmSeverity, str)

def __init__(self, topics: List[str], bootstrap_servers: List[str]):
def __init__(self, topics: List[str], bootstrap_servers: List[str], annunciate: bool = False):
super().__init__()

self.kafka_producer = None
Expand Down Expand Up @@ -104,12 +104,23 @@ def __init__(self, topics: List[str], bootstrap_servers: List[str]):
for topic in topics:
self.last_received_update_time[topic] = datetime.now()
self.alarm_select_combo_box.addItem(topic)
self.alarm_trees[topic] = AlarmTreeViewWidget(self.kafka_producer, topic, self.plot_pv, False)

self.alarm_trees[topic] = AlarmTreeViewWidget(self.kafka_producer, topic, self.plot_pv, False, annunciate)
self.active_alarm_tables[topic] = AlarmTableViewWidget(
self.alarm_trees[topic].treeModel, self.kafka_producer, topic, AlarmTableType.ACTIVE, self.plot_pv
self.alarm_trees[topic].treeModel,
self.kafka_producer,
topic,
AlarmTableType.ACTIVE,
self.plot_pv,
annunciate,
)
self.acknowledged_alarm_tables[topic] = AlarmTableViewWidget(
self.alarm_trees[topic].treeModel, self.kafka_producer, topic, AlarmTableType.ACKNOWLEDGED, self.plot_pv
self.alarm_trees[topic].treeModel,
self.kafka_producer,
topic,
AlarmTableType.ACKNOWLEDGED,
self.plot_pv,
annunciate,
)

# Sync the column widths in the active and acknowledged tables, resizing a column will effect both tables.
Expand Down
7 changes: 5 additions & 2 deletions slam/tests/test_alarm_configuration_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ def test_create_and_show(qtbot, alarm_item, mock_kafka_producer):
def test_save_configuration(qtbot, alarm_item, mock_kafka_producer, enabled, latching, annunciating):
"""Verify that the information saved in the configuration widget is sent to the kafka cluster correctly"""
alarm_config_widget = AlarmConfigurationWidget(
alarm_item=alarm_item, kafka_producer=mock_kafka_producer, topic="TEST"
alarm_item=alarm_item, kafka_producer=mock_kafka_producer, topic="TEST", annunciate=annunciating
)
qtbot.addWidget(alarm_config_widget)

# Simulate the user typing in several suggestions for how to handle this particular alarm
alarm_config_widget.enabled_checkbox.setChecked(enabled)
alarm_config_widget.latch_checkbox.setChecked(latching)
alarm_config_widget.annunciate_checkbox.setChecked(annunciating)
if annunciating:
alarm_config_widget.annunciate_checkbox.setChecked(True)
else:
assert alarm_config_widget.annunciate_checkbox.isEnabled() is False

alarm_config_widget.guidance_table.cellWidget(0, 0).setText("Call")
alarm_config_widget.guidance_table.cellWidget(0, 1).setText("Somebody")
Expand Down
44 changes: 44 additions & 0 deletions slam/tests/test_alarm_tree_model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from ..alarm_item import AlarmItem, AlarmSeverity
from operator import attrgetter
import sys
from io import StringIO


def test_clear(tree_model, alarm_item):
Expand Down Expand Up @@ -145,3 +147,45 @@ def test_remove_item(tree_model):

assert len(tree_model.nodes) == 0
assert len(tree_model.added_paths) == 0


def test_annunciation(tree_model):
"""Test that a beep noise is made when an alarm-item enters an active alarm state"""

tree_model.annunciate = True
alarm_item = AlarmItem(
"TEST:PV",
path="/path/to/TEST:PV",
alarm_severity=AlarmSeverity.OK,
alarm_status="OK",
pv_severity=AlarmSeverity.OK,
annunciating=True,
)
tree_model.nodes.append(alarm_item)
tree_model.added_paths["TEST:PV"] = ["/path/to/TEST:PV"]

# To verify the beep happened we check that bell character was printed to stdout,
# which when printed to stdout makes beep sound
# (assuming the user has the bell-sound option enabled for their terminal).
# Could later replace with audio library if more sound options are wanted.
stdout_buffer = StringIO()
# redirect stdout to buffer
sys.stdout = stdout_buffer

tree_model.update_item(
"TEST:PV",
"/path/to/TEST:PV",
AlarmSeverity.MINOR,
"STATE_ALARM",
None,
"FAULT",
AlarmSeverity.MINOR,
"alarm_status",
)

# restore original stdout stream
sys.stdout = sys.__stdout__

captured_output = stdout_buffer.getvalue()
# checking for bell character
assert captured_output == "\x07\n"
12 changes: 8 additions & 4 deletions slam_launcher/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@

def main():
parser = argparse.ArgumentParser(description="SLAC Alarm Manager")
parser.add_argument("--topics", help="Comma separated list of kafka alarm topics to listen to")
parser.add_argument("-t", "--topics", help="Comma separated list of kafka alarm topics to listen to")
parser.add_argument(
"-b",
"--bootstrap-servers",
default="localhost:9092",
help="Comma separated list of urls for one or more kafka boostrap servers",
)
parser.add_argument("--user-permissions", default="admin", help="One of read-only, operator, admin")
parser.add_argument("--log", default="warning", help="Logging level. debug, info, warning, error, critical")
parser.add_argument("-u", "--user-permissions", default="admin", help="One of read-only, operator, admin")
parser.add_argument("-l", "--log", default="warning", help="Logging level. debug, info, warning, error, critical")
parser.add_argument(
"-a", "--annunciate", action="store_true", help="Enable beep from alarms that have annunciate setting enabled"
) # default=False
Comment on lines -11 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the easier command line options


app_args = parser.parse_args()

Expand All @@ -36,7 +40,7 @@ def main():
topics = app_args.topics.split(",")

app = QApplication([])
main_window = AlarmHandlerMainWindow(topics, kafka_boostrap_servers)
main_window = AlarmHandlerMainWindow(topics, kafka_boostrap_servers, app_args.annunciate)
main_window.resize(1536, 864)
main_window.setWindowTitle("SLAC Alarm Manager")
main_window.show()
Expand Down