Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f449aff
Merge branch 'counter' of github.com:lisazacarias/cavityDisplay into …
Derikka May 13, 2024
1e04ab5
Made a bar chart that works with a single pv
Derikka May 17, 2024
a98f495
Can make a bar chart with more than one pv
Derikka May 17, 2024
0439dea
Pushing just to be safe
Derikka May 20, 2024
c9babba
oops, front end isn't working
Derikka May 20, 2024
6f24277
pulled from production (aka the reorganization to get ready of ui files)
Derikka Jul 10, 2024
bbb65e8
Can launch bar chart without .ui file now
Derikka Jul 19, 2024
de71ce5
Got archive_data.py to launch bar chart GUI from another file
Derikka Jul 22, 2024
467f294
Troubleshooting BarChart failing to launch
Derikka Jul 23, 2024
e25d16b
It works! Made new GUI to select cm and cav to make bar chart
Derikka Jul 23, 2024
97fc10e
Added labels for user readability
Derikka Jul 24, 2024
57e22a9
Removed attempt at scroll bar
Derikka Aug 12, 2024
22715f8
Made bar chart horizontal
Derikka Aug 13, 2024
9a9f9cb
Made a stacked bar chart example (i.e. in an effort to get invalid an…
Derikka Aug 13, 2024
bd5aed8
Set minimum start time to 3 yrs, default 30 min
Derikka Aug 14, 2024
31b4d89
Found a bug where previous bar chart is not being erased
Derikka Aug 14, 2024
ee9f36a
Added calendar to start and end dates
Derikka Aug 19, 2024
d4dd147
Made a checkbox and removed POT, but these are not interconnected tas…
Derikka Sep 3, 2024
b398b35
Can use checkbox to remove POT fault counts from bar chart
Derikka Sep 3, 2024
7c99fe5
Removed extraneous files
Derikka Sep 4, 2024
5e0f2ca
Renamed fault count display file
Derikka Sep 4, 2024
c71c659
Addressed comments in PR
Derikka Sep 4, 2024
1d05d38
Cleaned up comments
Derikka Sep 5, 2024
a0ffd1f
Moved fault_count_display.py in to frontend directory
Derikka Sep 5, 2024
0b4bdde
Copied changes from cavityDisplay repo to sc_linac_physics repo; Adde…
Derikka Sep 16, 2024
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
30 changes: 18 additions & 12 deletions backend/backend_cavity.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from collections import OrderedDict
from datetime import datetime
from typing import Dict
from collections import OrderedDict, defaultdict

from datetime import datetime
from epics import caput
from typing import Dict

from backend.fault import Fault, FaultCounter, PVInvalidError
from lcls_tools.superconducting.sc_linac import Cavity
Expand All @@ -18,9 +18,9 @@

class BackendCavity(Cavity):
def __init__(
self,
cavity_num,
rack_object,
self,
cavity_num,
rack_object,
):
super(BackendCavity, self).__init__(
cavity_num=cavity_num, rack_object=rack_object
Expand Down Expand Up @@ -75,7 +75,7 @@ def create_faults(self):
)

if (cm_type == "1.3" and self.cryomodule.is_harmonic_linearizer) or (
cm_type == "3.9" and not self.cryomodule.is_harmonic_linearizer
cm_type == "3.9" and not self.cryomodule.is_harmonic_linearizer
):
continue
pv = prefix + suffix
Expand Down Expand Up @@ -119,14 +119,20 @@ def create_faults(self):
)

def get_fault_counts(
self, start_time: datetime, end_time: datetime
self, start_time: datetime, end_time: datetime
) -> Dict[str, FaultCounter]:
result: Dict[str, FaultCounter] = {}

result: Dict[str, FaultCounter] = defaultdict(FaultCounter)

"""
Using max function to get the maximum fault or invalid count for duplicate TLCs
i.e. MGT tlc has three PVs associated with it (X, Y, and Q) but we
only want the fault and invalid count for whichever PV had the
greatest number of faults
"""
for fault in self.faults.values():
result[fault.pv.pvname] = fault.get_fault_count_over_time_range(
result[fault.tlc] = max(result[fault.tlc], fault.get_fault_count_over_time_range(
start_time=start_time, end_time=end_time
)
))

return result

Expand Down
48 changes: 33 additions & 15 deletions backend/fault.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ class FaultCounter:
ok_count: int = 0
invalid_count: int = 0

@property
def sum_fault_count(self):
return self.fault_count + self.invalid_count

@property
def ratio_ok(self):
try:
return self.ok_count / (self.fault_count + self.invalid_count)
except ZeroDivisionError:
return 1

def __gt__(self, other):
return self.sum_fault_count > other.sum_fault_count

def __eq__(self, other):
return self.sum_fault_count == other.sum_fault_count


class PVInvalidError(Exception):
def __init__(self, message):
Expand All @@ -34,20 +44,20 @@ def __init__(self, message):

class Fault:
def __init__(
self,
tlc,
severity,
pv,
ok_value,
fault_value,
long_description,
short_description,
button_level,
button_command,
macros,
button_text,
button_macro,
action,
self,
tlc,
severity,
pv,
ok_value,
fault_value,
long_description,
short_description,
button_level,
button_command,
macros,
button_text,
button_macro,
action,
):
self.tlc = tlc
self.severity = int(severity)
Expand All @@ -65,6 +75,8 @@ def __init__(
self.pv: PV = PV(pv, connection_timeout=PV_TIMEOUT)

def is_currently_faulted(self):
# returns "TRUE" if faulted
# returns "FALSE" if not faulted
return self.is_faulted(self.pv)

def is_faulted(self, obj: Union[PV, ArchiverValue]):
Expand All @@ -79,10 +91,16 @@ class AlarmSeverity(DefaultIntEnum):
if obj.severity == 3 or obj.status is None:
raise PVInvalidError(self.pv.pvname)

# self.ok_value is the value stated in spreadsheet
# obj.value is the actual reading value from pv
if self.ok_value is not None:
# return "TRUE" means they do NOT match
# return "FALSE" means is_okay, not faulted
return obj.val != self.ok_value

elif self.fault_value is not None:
# return "TRUE" means faulted
# return "FALSE" means not faulted
return obj.val == self.fault_value

else:
Expand All @@ -99,7 +117,7 @@ def was_faulted(self, time: datetime):
return self.is_faulted(archiver_value)

def get_fault_count_over_time_range(
self, start_time: datetime, end_time: datetime
self, start_time: datetime, end_time: datetime
) -> FaultCounter:
result = get_values_over_time_range(
pv_list=[self.pv.pvname], start_time=start_time, end_time=end_time
Expand Down
18 changes: 9 additions & 9 deletions backend/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@

from backend.backend_cavity import BackendCavity
from lcls_tools.common.controls.pyepics.utils import PV
from lcls_tools.superconducting.sc_linac import Machine, Cryomodule
from lcls_tools.superconducting.sc_linac import Cryomodule, Machine
from lcls_tools.superconducting.sc_linac_utils import ALL_CRYOMODULES
from utils.utils import DEBUG, BACKEND_SLEEP_TIME

WATCHER_PV: PV = PV("PHYS:SYS0:1:SC_CAV_FAULT_HEARTBEAT")
WATCHER_PV.put(0)

DISPLAY_MACHINE = Machine(cavity_class=BackendCavity)

while True:
start = datetime.now()
for cryomoduleName in ALL_CRYOMODULES:
cryomodule: Cryomodule = DISPLAY_MACHINE.cryomodules[cryomoduleName]
for cavity in cryomodule.cavities.values():
cavity.run_through_faults()
if DEBUG:
delta = (datetime.now() - start).total_seconds()
sleep(BACKEND_SLEEP_TIME - delta if delta < BACKEND_SLEEP_TIME else 0)

try:
WATCHER_PV.put(WATCHER_PV.get() + 1)
except TypeError as e:
print(f"Write to watcher PV failed with error: {e}")
if DEBUG:
delta = (datetime.now() - start).total_seconds()
sleep(BACKEND_SLEEP_TIME - delta if delta < BACKEND_SLEEP_TIME else 0)

try:
WATCHER_PV.put(WATCHER_PV.get() + 1)
except TypeError as e:
print(f"Write to watcher PV failed with error: {e}")
54 changes: 35 additions & 19 deletions frontend/decoder.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import sys
from collections import OrderedDict
from dataclasses import dataclass

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QHBoxLayout,
Expand All @@ -13,6 +12,7 @@
QApplication,
QAbstractScrollArea,
)
from dataclasses import dataclass
from pydm import Display

from utils.utils import parse_csv
Expand All @@ -23,8 +23,9 @@
@dataclass
class Row:
tlc: str
longDesc: str
genShortDesc: str
long_desc: str
gen_short_desc: str
corrective_action: str


class DecoderDisplay(Display):
Expand All @@ -35,8 +36,9 @@ def __init__(self, parent=None, args=None, macros=None):
tlc = faultRowDict["Three Letter Code"]
rows[tlc] = Row(
tlc=tlc,
longDesc=faultRowDict["Long Description"],
genShortDesc=faultRowDict["Generic Short Description for Decoder"],
long_desc=faultRowDict["Long Description"],
gen_short_desc=faultRowDict["Generic Short Description for Decoder"],
corrective_action=faultRowDict["Recommended Corrective Actions"]
)

sorted_fault_rows = OrderedDict(
Expand All @@ -62,52 +64,66 @@ def __init__(self, parent=None, args=None, macros=None):
# Long description header
header_layout = QHBoxLayout()
description_header_label = QLabel("Description")
description_header_label.setMinimumSize(200, 30)
description_header_label.setMinimumSize(100, 30)
description_header_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
description_header_label.setStyleSheet("text-decoration: underline")

# Name (aka short description) header
name_header_label = QLabel("Name")
name_header_label.setMinimumSize(200, 30)
name_header_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
name_header_label.setMinimumSize(100, 30)
name_header_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
name_header_label.setStyleSheet("text-decoration: underline")

# Three-Letter Code header
code_header_label = QLabel("Code")
code_header_label.setMinimumSize(30, 30)
code_header_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
code_header_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
code_header_label.setStyleSheet("text-decoration: underline")

# Corrective Action header
action_header_label = QLabel("Corrective Action")
action_header_label.setMinimumSize(100, 30)
action_header_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
action_header_label.setStyleSheet("text-decoration: underline")

header_layout.setAlignment(Qt.AlignVCenter | Qt.AlignLeft)

header_layout.addWidget(code_header_label)
header_layout.addWidget(name_header_label)
header_layout.addWidget(description_header_label)
header_layout.addWidget(description_header_label, 2)
header_layout.addWidget(action_header_label, 2)
header_layout.setSpacing(50)

scroll_area_layout.addLayout(header_layout)

for row in sorted_fault_rows.values():
horizontal_layout = QHBoxLayout()
description_label = QLabel(row.longDesc)
description_label.setMinimumSize(300, 50)
description_label = QLabel(row.long_desc)
description_label.setMinimumSize(100, 50)
description_label.setSizePolicy(
QSizePolicy.MinimumExpanding, QSizePolicy.Minimum
QSizePolicy.Minimum, QSizePolicy.Minimum
)
description_label.setWordWrap(True)

code_label = QLabel(row.tlc)
code_label.setMinimumSize(30, 30)
code_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
code_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

name_label = QLabel()
name_label.setText(row.genShortDesc)
name_label.setMinimumSize(200, 50)
name_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
name_label.setText(row.gen_short_desc)
name_label.setMinimumSize(100, 50)
name_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
name_label.setWordWrap(True)

action_label = QLabel(row.corrective_action)
action_label.setMinimumSize(100, 50)
action_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
action_label.setWordWrap(True)

horizontal_layout.addWidget(code_label)
horizontal_layout.addWidget(name_label)
horizontal_layout.addWidget(description_label)
horizontal_layout.addWidget(description_label, 2)
horizontal_layout.addWidget(action_label, 2)

horizontal_layout.setSpacing(50)
horizontal_layout.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
Expand Down
Loading