From 2771f43cfa1c3eeb5dea2dc1a181ba45cb4fafde Mon Sep 17 00:00:00 2001 From: chrin Date: Mon, 17 Jul 2023 13:55:49 +0200 Subject: [PATCH] domain/section now taken from ElogBooks key --- ambientdata.py | 211 ++++++++++++++++++++++++++++++ enumkind.py | 61 ++++++--- guiheader.py | 58 +++++++++ sendelogsf.py | 340 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 650 insertions(+), 20 deletions(-) create mode 100644 ambientdata.py create mode 100644 sendelogsf.py diff --git a/ambientdata.py b/ambientdata.py new file mode 100644 index 0000000..e654538 --- /dev/null +++ b/ambientdata.py @@ -0,0 +1,211 @@ +def collect_ambient_data(self): + ''' + Collect ambient data such as bunch association and return it + as a dictionary + ''' + # Time in seconds in an integer and can be stored in hdf5 + time_in_seconds = time.time() + time_stamp = datetime.fromtimestamp( + time_in_seconds).strftime('%a %d-%m-%Y %H:%M:%S') + + # Bunch number and destination + handles = self.cafe.getHandles()[0] + status = self.cafe.attachContext(handles[0]) + + if status == self.cyca.ECAFE_NULLCONTEXT: + options = {} + options['statusCode'] = (str(status) + " " + + self.cafe.getStatusCodeAsString(status)) + options['statusInfo'] = self.cafe.getStatusInfo(status) + self.parent.trigger_log_message.emit( + MsgSeverity.ERROR.name, _pymodule, _line(), + ("Cannot attach CA context in thread " + + "Scan will not be initiated!"), options) + + if self.abort: + self.aborting(_line()) + return {} + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_ERROR) + return {} + + pv_value_dict = OrderedDict() + pv_list = [] + for key, inner_key in self.settings.data["PVAmbient"].items(): + pv_value_dict[key] = OrderedDict() + for ikey, value in inner_key.items(): + pv_value_dict[key][ikey] = 0 + pv_list.append(value) + + + self.cafe.openPrepare() + handle_list = self.cafe.open(pv_list) + self.cafe.openNowAndWait(1.0) + + self.cafe.setGetActionWhenMonitorPolicyAllHandles( + self.cyca.GET_FROM_CACHE) + + value_list, stat, status_list = self.cafe.getScalarList(handle_list) + + if self.debug: + for pv, val, stat in zip(pv_list, value_list, status_list): + print(pv, "//", val, "//", stat) + + if stat != self.cyca.ICAFE_NORMAL: + self.check_status_list(pv_list, status_list, _line()) + + + #Put values in dictionary for inspection + i = 0 + for dict_key in pv_value_dict.keys(): + for inner_key in pv_value_dict[dict_key].keys(): + pv_value_dict[dict_key][inner_key] = value_list[i] + i += 1 + + bunch_key_dict = {ARAMIS: None, ATHOS: None, PORTHOS: None} + bunch_no_dict = {ARAMIS: 0, ATHOS: 0, PORTHOS: 0} + + + def get_target_bunch(): + for target in target_list: + target_bunch = None + target_bunch_no = 0 + for key in pv_value_dict.keys(): + if "dest" in pv_value_dict[key].keys(): + if pv_value_dict[key]["dest"] == target: + target_bunch = key + break + if target_bunch is None: + print("Beamline {0} not ready".format(target)) + else: + target_bunch_no = re.findall(r'\d+', target_bunch)[0] + + bunch_key_dict[target] = target_bunch + bunch_no_dict[target] = target_bunch_no + + + get_target_bunch() + + + print(bunch_key_dict, flush=True) + print(bunch_no_dict, flush=True) + + + active_target_dict = {ARAMIS: False, ATHOS: False, PORTHOS: False} + + def is_target_active(): + bar = bunch_key_dict[ARAMIS] + bat = bunch_key_dict[ATHOS] + if None in [bar, bat]: + return + + + + target_found = True + if pv_value_dict[bar]["rep"] > 0.0: + if pv_value_dict[bat]["rep"] <= 0.0: + active_target_dict[ARAMIS] = True + else: + laser_ar = pv_value_dict[bar]["laser"] + lar = pv_value_dict["Las"][laser_ar] + laser_at = pv_value_dict[bat]["laser"] + lat = pv_value_dict["Las"][laser_at] + + if lar > 0.0: + if lat <= 0.0: + active_target_dict[ARAMIS] = True + else: + target_found = False + elif lat > 0.0: + active_target_dict[ATHOS] = True + else: + target_found = False + elif pv_value_dict[bat]["rep"] > 0.0: + active_target_dict[ATHOS] = True + + for key, value in active_target_dict.items(): + if value: + return key + return None + + + active_target = is_target_active() + + if self.debug: + print("active_target_dict:", active_target_dict) + print("active_target: ", active_target) + + + if active_target is not None: + active_bunch = bunch_key_dict[active_target] + beamline = active_target + bunch_no = bunch_no_dict[active_target] + gun_laser = pv_value_dict[active_bunch]["laser"] + rep_rate = pv_value_dict[active_bunch]["rep"] + #beamline_color = "blue" + bunch_label = "B{0}".format(bunch_no) + dest_label = "to {0} \n({1}, {2} Hz)".format( + beamline, gun_laser, rep_rate) + else: + active_bunch = "Bunch1" + beamline = ARAMIS + "/" + ATHOS + bunch_no = 0 + gun1 = pv_value_dict["Bunch1"]["laser"] + gun2 = pv_value_dict["Bunch2"]["laser"] + gun_bunch1 = gun1 if gun1 else "unknown" + gun_bunch2 = gun2 if gun2 else "unknown" + gun_laser = (gun_bunch1 + "/" + gun_bunch2) + rep1 = pv_value_dict["Bunch1"]["rep"] + rep2 = pv_value_dict["Bunch2"]["rep"] + rep_bunch1 = str(rep1) if rep1 is not None else "unknown" + rep_bunch2 = str(rep2) if rep2 is not None else "unknown" + rep_rate = (rep_bunch1 + "/" + rep_bunch2) + #beamline_color = "blue" + bunch_label = "B1/B2" + dest_label = " " + + + mess = "Bunch: {0} {1}".format(bunch_label, dest_label) + self.parent.trigger_log_message.emit( + MsgSeverity.INFO.name, _pymodule, _line(), mess, {}) + + if self.debug: + print(beamline, bunch_no, gun_laser, rep_rate) + print("Active bunch", active_bunch, flush=True) + # Bunch charge and TD voltage + + pv_charge = self.settings.data["PVAmbient"][active_bunch]["charge"] + + pv_charge_voltage = [pv_charge] + + self.cafe.openPrepare() + self.cafe.open(pv_charge_voltage) + self.cafe.openNowAndWait(0.2) + + value_list, stat, status_list = self.cafe.getScalarList( + pv_charge_voltage) + + if stat != self.cyca.ICAFE_NORMAL: + self.check_status_list(pv_charge_voltage, status_list, _line()) + + + bunch_charge = value_list[0] * (10**-12) + if self.debug: + print("\nCharge : ", bunch_charge, " [C]") + + + ambient_data = { + 'Time in seconds': time_in_seconds, + 'Time stamp': time_stamp, + 'Bunch number': bunch_no, + 'Beam line': beamline, + 'Gun laser': gun_laser, + 'Repetition rate': rep_rate, + 'Bunch label': bunch_label, + 'Destination label': dest_label, + 'Bunch charge': bunch_charge + } + + if self.debug: + print(ambient_data) + + return ambient_data diff --git a/enumkind.py b/enumkind.py index f96bb4e..fb35f31 100644 --- a/enumkind.py +++ b/enumkind.py @@ -8,19 +8,32 @@ class ElogSwissFEL: self.system = self.System(0) class Category(IntEnum): - INFO = 0 - MEASUREMENT = 1 + ACCESS = 0 + DCM =1 + INFO = 2 + LASER_GUN = 3 + MEASUREMENT = 4 + PIKETT = 5 + PROBLEM = 6 + SCHICHT_UBERGABE = 7 + SHIFT_SUMMARY = 8 + TIPS_TRICKS = 9 + UBERBRUCKUNG = 10 + SCHICHT_AUFTRAG = 11 + RC = 12 + WEEKLY_REF = 13 class Domain(IntEnum): NONE = 0 - INJECTOR = 1 - LINAC1 = 2 - LINAC2 = 3 - LINAC3 = 4 - ARAMIS = 5 - ARAMIS_BEAMLINES = 6 - ATHOS = 7 - ATHOS_BEAMLINES = 8 + GLOBAL = 1 + INJECTOR = 2 + LINAC1 = 3 + LINAC2 = 4 + LINAC3 = 5 + ARAMIS = 6 + ARAMIS_BEAMLINES = 7 + ATHOS = 8 + ATHOS_BEAMLINES = 9 class System(IntEnum): NONE = 0 @@ -28,13 +41,21 @@ class ElogSwissFEL: CONTROLS = 2 DIAGNOSTICS = 3 ELECTRICSUPPLY = 4 - INSERTIONDEVICES = 5 - LASER = 6 - MAGNETPOWERSUPPLIES = 7 - OPERATION = 8 - RF = 9 - SAFETY = 10 - VACUUM = 11 - WATERCOOLING = 11 - OTHER = 12 - UNKNOWN = 13 + FEEDBACKS = 5 + INSERTIONDEVICES = 6 + LASER = 7 + MAGNETPOWERSUPPLIES = 8 + OPERATION = 9 + PHOTONICS = 10 + PLC = 11 + RF = 12 + SAFETY = 13 + VACUUM = 14 + TIMING = 15 + WATERCOOLING = 16 + OTHER = 17 + UNKNOWN = 18 + + class Eintrag(IntEnum): + PROBLEM = 0 + ANREGUNG = 1 diff --git a/guiheader.py b/guiheader.py index 1ebd408..1ce3bf1 100644 --- a/guiheader.py +++ b/guiheader.py @@ -44,9 +44,14 @@ class GUIHeader(QWidget): self.font_pts10.setPointSize(10) self.widget_height = self.settings.data["StyleGuide"]["widgetHeight"] self.extra_height = self.settings.data["StyleGuide"]["extraGroupHeight"] + self.machine_color = self.settings.data['Machine']['color'] self.aramis_color = self.settings.data["Aramis"]["color"] self.athos_color = self.settings.data["Athos"]["color"] self.porthos_color = self.settings.data["Porthos"]["color"] + self.aramis_grad = self.settings.data['Aramis']['qlingrad'] + self.athos_grad = self.settings.data['Athos']['qlingrad'] + self.porthos_grad = self.settings.data['Porthos']['qlingrad'] + self.machine_grad = self.settings.data['Machine']['qlingrad'] beamline = ["Aramis", "Athos", "Porthos"] pvlist = [] @@ -104,6 +109,7 @@ class GUIHeader(QWidget): title = "SwissFEL {0}".format(self.user_mode.name) if self.title: title += ": {0}".format(self.title) + self.header_wgt.setTitle(title) self.target_beamline = None @@ -328,6 +334,58 @@ class GUIHeader(QWidget): self.machine_status = station return self.machine_status + def get_location_color(self, title: str): + """Determines text color for the various location points + + Args: + title: String indicating location point for which the text + color is to be determined + + Returns: + string in html color + """ + text_color = self.machine_color + aramis_matches = ["AR", "LINAC"] + + if any([x in title for x in aramis_matches]): + text_color = self.aramis_color + # if "AR" in title: + # text_color = self.aramis_color + # elif "LINAC" in title: + # text_color = self.aramis_color + elif "AT" in title: + text_color = self.athos_color + elif "PO" in title: + text_color = self.porthos_color + return text_color + + + def get_location_gradient_color(self, title: str): + """Determines bg gradient color for the various location points + + Args: + title: String indicating location point for which the bg + gradient color is to be determined + + Returns: + string in html color + """ + text_color = self.machine_grad + + aramis_matches = ["AR", "LINAC"] + + if any([x in title for x in aramis_matches]): + text_color = self.aramis_grad + # if "AR" in title: + # text_color = self.aramis_grad + # elif "LINAC" in title: + # text_color = self.aramis_grad + elif "AT" in title: + text_color = self.athos_grad + elif "PO" in title: + text_color = self.porthos_grad + return text_color + def operator_group_header(self): return self.radio_target_beamline() diff --git a/sendelogsf.py b/sendelogsf.py new file mode 100644 index 0000000..a039de3 --- /dev/null +++ b/sendelogsf.py @@ -0,0 +1,340 @@ +import getpass +import inspect +import os +import time + +from qtpy.QtCore import Qt +from qtpy.QtWidgets import (QComboBox, QDialog, QFileDialog, QHBoxLayout, + QLabel, QLineEdit, QPushButton, QTextEdit, + QVBoxLayout) + +import elog # https://github.com/paulscherrerinstitute/py_elog +from pyqtacc.bdbase.enumkind import MsgSeverity + +from pyqtacc.bdbase.sendelogframe import QSendToELOGFrame + +_version = "1.0.0" +_pymodule = os.path.basename(__file__) +_appname, _appext = _pymodule.split(".") + +def _line(): + """Macro to return the current line number. + + The current line number within the file is used when + reporting messages to the message logging window. + + Returns: + int: Current line number. + """ + return inspect.currentframe().f_back.f_lineno + +class QSendToELOG(QSendToELOGFrame): + """ Graphical interface to elog + """ + def __init__(self, parent, logbook=None, categoryIdx=0, domainIdx=0, + systemIdx=0, sectionIdx=0, eintragIdx=0, statusIdx=0, + title=None, message=None, + attachFile=None, destination_folder=None): + + super().__init__(parent, logbook=logbook, title=title, message=message) + + #print ("ELOG SEND", flush=True) + #First check what is the logbook being requested? + #find layout items + self.layout_items = self.get_logbook_specific_items(self.logbook) + self.layout_to_widget_dict = {} + + self.category_idx = categoryIdx + self.domain_idx = domainIdx + self.system_idx = systemIdx + self.section_idx = sectionIdx + self.eintrag_idx = eintragIdx + self.status_idx = statusIdx + + #print("indices", self.category_idx, self.domain_idx, self.system_idx, + # self.section_idx, flush=True) + self.category = None + self.domain = None + self.system = None + self.section = None + self.eintrag = None + self.estatus = None + self.effekt = None + + #self.elog_section = list(self.parent.settings.data["Elog"]['section']) + try: + self.elog_section = list(self.parent.settings.data['ElogBooks'][ + self.logbook]['Optional']['Section']) + except KeyError: + self.elog_section = None + + #print("LAYOUT ITEMS: ", self.layout_items, flush=True) + + self.sim_list = ["Sand", "test"] + + self.initialize_layout(self.logbook) + self.exec() + + def reset_layout(self): + def remove_wgt(wgt): + self.layout.removeItem(wgt) + while wgt.count(): + item = wgt.takeAt(0) + widget = item.widget() + widget.deleteLater() + + print("old logbook==>", self.logbook) + print("remove==>", self.get_logbook_specific_items(self.logbook), + flush=True) + + for layout in self.get_logbook_specific_items(self.logbook): + print(layout, "layout", flush=True) + remove_wgt(self.layout_to_widget_dict[layout]) + print(layout, "layout-removed", flush=True) + + + def create_layout_widgets(self): + + + + if not self.eintrag: + print("EINTRAG", flush=True) + self.eintrag = QHBoxLayout() + self.eintrag.addWidget(QLabel('Eintrag: ')) + self.eintrag_items = QComboBox() + self.eintrag_items.setObjectName("Elog") + self.eintrag.addWidget(self.eintrag_items) + self.layout_to_widget_dict['Eintrag'] = self.eintrag + + if not self.category: + print("CATEGORY", flush=True) + self.category = QHBoxLayout() + self.category.addWidget(QLabel('Category: ')) + self.category_items = QComboBox() + self.category_items.setObjectName("Elog") + self.category.addWidget(self.category_items) + self.layout_to_widget_dict['Category'] = self.category + + if not self.domain: + print("DOMAIN", flush=True) + self.domain = QHBoxLayout() + self.domain.addWidget(QLabel('Domain: ')) + self.domain_items = QComboBox() + self.domain_items.setObjectName("Elog") + self.domain.addWidget(self.domain_items) + self.domain_items.currentIndexChanged.connect(self.on_domain_change) + self.layout_to_widget_dict['Domain'] = self.domain + + if not self.system: + print("SYSTEM", flush=True) + self.system = QHBoxLayout() + self.system.addWidget(QLabel('System: ')) + self.system_items = QComboBox() + self.system_items.setObjectName("Elog") + self.system.addWidget(self.system_items) + self.layout_to_widget_dict['System'] = self.system + + + if not self.section: + print("SECTION", flush=True) + self.section = QHBoxLayout() + self.section.addWidget(QLabel('Section: ')) + self.section_items = QComboBox() + self.section_items.setObjectName("Elog") + self.section.addWidget(self.section_items) + self.layout_to_widget_dict['Section'] = self.section + + + if not self.effekt: + print("EFFEKT", flush=True) + self.effekt = QHBoxLayout() + self.effekt.addWidget(QLabel('Effect: ')) + self.effekt_le = QLineEdit() + self.effekt_le.setObjectName('Elog') + self.effekt_le.setText(str("")) + self.effekt_le.setFixedWidth(300) + self.effekt_le.setAlignment(Qt.AlignCenter) + self.effekt.addWidget(self.effekt_le) + self.layout_to_widget_dict['Effect'] = self.effekt + + if not self.estatus: + print("STATUS", flush=True) + self.estatus = QHBoxLayout() + self.estatus.addWidget(QLabel('Status: ')) + self.estatus_items = QComboBox() + self.estatus_items.setObjectName("Elog") + self.estatus.addWidget(self.estatus_items) + self.layout_to_widget_dict['Status'] = self.estatus + + print("END", flush=True) + + def initialize_layout(self, logbook): + + #Decide on layout + self.create_layout_widgets() + item_no = 2 + + print("HERE===========00>", logbook, flush=True) + + if 'Eintrag' in self.layout_items: + print("EINTRAG", item_no, flush=True) + self.eintrag_items.clear() + self.eintrag_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Required']['Eintrag'])) + self.eintrag_items.setCurrentIndex(self.eintrag_idx) + self.layout.insertLayout(item_no, self.eintrag) + + + + if 'Category' in self.layout_items: + print("Categ", item_no, flush=True) + self.category_items.clear() + key = 'Required' if 'Required' in self.parent.settings.data[ + "ElogBooks"][logbook] else 'Optional' + + self.category_items.addItems(list(self.parent.settings.data[ + "ElogBooks"][logbook][key]['Category'])) + self.category_items.setCurrentIndex(self.category_idx) + self.layout.insertLayout(item_no, self.category) + + if 'Effect' in self.layout_items: + item_no += 1 + print("Effect", item_no, flush=True) + self.layout.insertLayout(item_no, self.effekt) + + if 'Domain' in self.layout_items: + item_no += 1 + print("Domain", item_no, flush=True) + self.domain_items.clear() + self.domain_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Optional']['Domain'])) + self.domain_items.setCurrentIndex(self.domain_idx) + self.layout.insertLayout(item_no, self.domain) + + if 'Section' in self.layout_items: + item_no += 1 + print("Section", item_no, flush=True) + self.section_items.clear() + self.section_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Optional']['Section'][self.domain_idx])) + self.section_items.setCurrentIndex(self.section_idx) + self.layout.insertLayout(item_no, self.section) + + if 'System' in self.layout_items: + item_no += 1 + print("System", item_no, flush=True) + self.system_items.clear() + self.system_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Optional']['System'])) + self.system_items.setCurrentIndex(self.system_idx) + self.layout.insertLayout(item_no, self.system) + + if 'Status' in self.layout_items: + item_no += 1 + print("Status", item_no, flush=True) + self.estatus_items.clear() + self.estatus_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Optional']['Status'])) + self.estatus_items.setCurrentIndex(self.status_idx) + self.layout.insertLayout(item_no, self.estatus) + + + + + print("self.attachFile==>", self.parent.attach_files) + + + self.attachFile = self.parent.attach_files + self.filesE.clear() + self.files_text = '' + + if self.attachFile is not None: + _attachFile = [] + if isinstance(self.attachFile, str): + _attachFile.append(self.attachFile) + elif isinstance(self.attachFile, list): + _attachFile = self.attachFile + for i, file in enumerate(_attachFile): + _attach_base = os.path.basename(file) + if i > 0: + self.files_text += "\n" + self.files_text += str(_attach_base) + self.filesE.setText(self.files_text) + self.fflag = True + + if any(substring.upper() in logbook.upper() \ + for substring in self.sim_list): + _bgcolor = "QComboBox {background: plum; color : black;}" + else: + _bgcolor = "QComboBox {background: lightblue; color : black;}" + self.elog_items.setStyleSheet(_bgcolor) + + #have to remove widgets within layout too! + #https://riverbankcomputing.com/pipermail/pyqt/2009-November/025214.html + def on_elog_change(self, idx): + + print(self.elog_items.currentText(), "new=", idx) + print(self.elog_items.itemText(idx)) + print(self.logbook) + + new_logbook = self.elog_items.itemText(idx) + + print("NEW", new_logbook, flush=True) + print("OLD", self.logbook, flush=True) + + #Meet the new logbook. Same as the old logbook + if new_logbook == self.logbook: + return + + try: + self.elog_section = list(self.parent.settings.data['ElogBooks'][ + new_logbook]['Optional']['Section']) + except KeyError: + pass + + print("self.layout_items START", self.layout_items, flush=True) + self.layout_items = self.get_logbook_specific_items(new_logbook) + print("self.layout_items END", self.layout_items, flush=True) + + + if any(substring.upper() in new_logbook.upper() \ + for substring in self.sim_list): + _bgcolor = "QComboBox {background: plum; color : black;}" + else: + _bgcolor = "QComboBox {background: lightblue; color : black;}" + + self.reset_layout() + self.initialize_layout(new_logbook) + self.elog_items.setStyleSheet(_bgcolor) + self.logbook = new_logbook + + def on_domain_change(self, i): + self.section_items.clear() + self.section_items.addItems(self.elog_section[i]) + + def ok(self): + #proj = self.category_items.currentText() + #if not proj: + # self.messagelbl.setText('Category attribute is required') + # return + #self.attributes['Category'] = proj + el = self.elog_items.currentText() + if 'Eintrag' in self.layout_items: + self.attributes['Eintrag'] = self.eintrag_items.currentText() + if 'Category' in self.layout_items: + self.attributes['Category'] = self.category_items.currentText() + if 'Effect' in self.layout_items: + self.attributes['Effect'] = self.effekt_le.text() + if 'Domain' in self.layout_items: + self.attributes['Domain'] = self.domain_items.currentText() + if 'System' in self.layout_items: + self.attributes['System'] = self.system_items.currentText() + if 'Section' in self.layout_items: + self.attributes['Section'] = self.section_items.currentText() + if 'Status' in self.layout_items: + self.attributes['Status'] = self.estatus_items.currentText() + + + QSendToELOGFrame.ok(self) + +