From 0eb187ffd5684638a4b414d5c47c5c6730dc999a Mon Sep 17 00:00:00 2001 From: chrin Date: Mon, 1 May 2023 09:59:31 +0200 Subject: [PATCH] Initial commit --- .gitignore | 5 + enumkind.py | 107 ++++++++++++++++ guiheader.py | 129 +++++++++++++++++++ sendeloghipa.py | 325 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 566 insertions(+) create mode 100644 .gitignore create mode 100644 enumkind.py create mode 100644 guiheader.py create mode 100644 sendeloghipa.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70ca11c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.*~ +*.*-* +*.*+* +__pycache__ +__pycache__/*.* diff --git a/enumkind.py b/enumkind.py new file mode 100644 index 0000000..6e8de2c --- /dev/null +++ b/enumkind.py @@ -0,0 +1,107 @@ + +from enum import IntEnum + +class ElogHIPA: + def __init__(self): + self.eintrag = self.Eintrag(0) + self.projekt = self.Projekt(0) + self.system = self.System(0) + self.effekt = self.Effekt(0) + self.status = self.Status(0) + self.ort = self.Ort(0) + + class Eintrag(IntEnum): + PROBLEM = 0 + INFO = 1 + TUNING = 2 + OPERATION_CHANGE = 3 + BRIDGE = 4 + PIKETT = 5 + STATUS = 6 + INVENTORY = 7 + AK3 = 8 + TIPS = 9 + SHIFT_CHANGE = 10 + SHIFT_REPORT = 11 + + class Projekt(IntEnum): + NONE = 0 + SUPERBUNCHER = 1 + RINGPHASE = 2 + RIE2= 3 + OPAL = 4 + BW860 = 5 + BW860_OPTICS = 6 + MD = 7 + WIKI = 8 + RINGPHASE_PROBES = 9 + NEW_RINGPHASE_PROBES = 10 + BEAM_DUMP = 11 + FOCUS_MONITORS = 12 + INJECTOR2 = 13 + AXIAL_MOVEMENTS = 14 + EHT_SPLITTER = 15 + MINT= 16 + OPTIMIZER = 17 + + class System(IntEnum): + NONE = 0 + UNDEFINED = 1 + BEAMDYNAMICS = 2 + CONTROLS = 3 + DIAGNOSTICS = 4 + ELECTROSTATIC_ELEMENTS = 5 + ELECTROSTATIC_POWER_SUPPLY = 6 + ELECTRICAL_SUPPLY = 7 + SCHEDULED_INTERUPT = 8 + HV_PROBLEM = 9 + HF = 10 + INFRASTRUCTURE = 11 + INTERLOCK = 12 + ION_SOURCE = 13 + WATER_COOLING = 14 + MAGNET = 15 + MAGNET_POWER_SUPPLIES = 16 + OPERATOR_ERROR = 17 + START_DELAYED = 18 + PSYS = 19 + SINQ_NOT_READY = 20 + SERVICE = 21 + SETUP = 22 + SU = 23 + MD = 24 + TARGET_E = 25 + TARGET_M = 26 + TUNING = 27 + UNKNOWN = 28 + VACUUM = 29 + OTHER = 30 + + class Ort(IntEnum): + NONE = 0 + UNDEFINED = 1 + ION_SOURCE = 2 + COCKCROFT = 3 + BEAM_870MEV = 4 + INJECTOR2 = 5 + BEAM_72MEV = 6 + ISOTOPE = 7 + RING_CYCLOTRON = 8 + PROTON_CANAL = 9 + SECONDARY_BEAMLINES = 10 + SINQ = 11 + UCN = 12 + GLOBAL = 13 + OTHER = 14 + + class Effekt(IntEnum): + NONE = 0 + BEAM_LOSS = 1 + BEAM_REDUCED = 2 + OTHER = 3 + NO =4 + + class Status(IntEnum): + NONE = 0 + OFFEN = 1 + GESCHLOSSEN = 2 diff --git a/guiheader.py b/guiheader.py new file mode 100644 index 0000000..034ef89 --- /dev/null +++ b/guiheader.py @@ -0,0 +1,129 @@ +""" Gui header for HIPA +""" +from qtpy.QtCore import Qt +from qtpy.QtGui import QFont +from qtpy.QtWidgets import ( + QGridLayout, QGroupBox, QHBoxLayout, QLabel, QProxyStyle, QVBoxLayout, QWidget) + +from caqtwidgets.pvwidgets import CAQLabel +from pyqtacc.bdbase.enumkind import UserMode + +class GUIHeader(QWidget): + """ GUI Header Class + """ + def __init__(self, parent, user_mode=UserMode.OPERATION, extended=True): + super(GUIHeader, self).__init__() + self.parent = parent + self.appname = parent.appname + self.title = parent.title + self.settings = parent.settings + self.user_mode = user_mode + self.current_user_mode_name = user_mode.name + + self.extended = extended + self.cafe = parent.cafe + self.cyca = parent.cyca + + self.input_parameters = parent.input_parameters + self.input_labels = parent.input_labels + self.expert_parameters = parent.expert_parameters + self.expert_labels = parent.expert_labels + + self.header_groups = self.settings.data['header'] + #self.header_colors = ['Machine', 'Machine', 'Athos', 'Athos', 'Aramis', + # 'Aramis', 'Porthos'] + + self.header_status = [None] * len(self.header_groups) + + + self.font_gui = QFont("sans serif") + self.font_gui.setPointSize(11) + self.font_pts10 = QFont("sans serif") + self.font_pts10.setPointSize(10) + self.widget_height = self.settings.data["StyleGuide"]["widgetHeight"] + self.extra_height = self.settings.data["StyleGuide"]["extraGroupHeight"] + + pvlist = [] + header_colors = [] + for sector in self.header_groups: + pvlist.append(self.settings.data[sector]["current"]) + header_colors.append(self.settings.data[sector]["colorObj"]) + + self.cafe.openPrepare() + self.cafe.open(pvlist) + self.cafe.openNowAndWait(0.4) + self.cafe.printDisconnected() + + self.station_width = 200 #default + self.station_height = 100 + self.hor_layout = QHBoxLayout() + self.beam_current_wgt_dict = {} + + + for sector, pv, color in zip(self.header_groups, pvlist, header_colors): + self.hor_layout.addWidget(self.beam_current_widget( + header=sector, pv= pv, color_obj=color)) + self.hor_layout.setSpacing(6) + self.hor_layout.setAlignment(Qt.AlignLeft) + self.hor_layout.setContentsMargins(5, 0, 5, 0) + + self.header_wgt = QGroupBox() + self.header_wgt.setObjectName(self.user_mode.name) + self.header_wgt.setLayout(self.hor_layout) + self.header_wgt.setFixedHeight(110) + #self.header_wgt.setStyle(QProxyStyle()) + title = "HIPA {0}".format(self.user_mode.name) + if self.title: + title += ": {0}".format(self.title) + self.header_wgt.setTitle(title) + + + def reset_operation_mode(self): + """ Reset header colors to application operation mode + """ + self.change_operation_mode(user_mode=self.user_mode) + + def change_operation_mode(self, user_mode=UserMode.OPERATION): + """ Different operation modes have different color schemes + """ + title_name = user_mode.name + dry_run_tags = ['HUSH', 'Dry'] + #print('title', self.header_wgt.title(), flush=True) + + if any([x in self.header_wgt.title() for x in dry_run_tags]): + title_name = 'DRY RUN' if user_mode.name == 'SIMULATION' \ + else user_mode.name + + + self.header_wgt.setObjectName(user_mode.name) + self.header_wgt.setTitle(self.header_wgt.title().replace( + self.current_user_mode_name, title_name)) + self.header_wgt.style().polish(self.header_wgt) + self.current_user_mode_name = title_name + + def beam_current_widget(self, header: str="", pv: str ="", + color_obj: str="MACHINE"): + """ QGroupBox encompassing beam current info + """ + station = QGroupBox() + station.setObjectName(color_obj.upper()) + station.setAlignment(Qt.AlignHCenter | Qt.AlignTop) + station.setFlat(False) + station.setTitle(header) + beam_current = CAQLabel(self, pv_name=pv, show_units=True) + beam_current.setFixedHeight(self.widget_height) + beam_current.setFixedWidth(140) + + grid_layout = QGridLayout() + grid_layout.addWidget(beam_current, 0, 0, 1, 1, Qt.AlignCenter) + grid_layout.setVerticalSpacing(0) + grid_layout.setHorizontalSpacing(0) + grid_layout.setContentsMargins(2, 9, 2, 0) + + station.setLayout(grid_layout) + station.setFixedHeight(beam_current.height() + self.extra_height) + station.setFixedWidth(beam_current.width() + 15) + self.beam_current_wgt_dict[header] = beam_current + return station + + diff --git a/sendeloghipa.py b/sendeloghipa.py new file mode 100644 index 0000000..354cf28 --- /dev/null +++ b/sendeloghipa.py @@ -0,0 +1,325 @@ +import inspect +import os +import socket +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(".") +_hostname = socket.gethostname().split(".")[0] + +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, projektIdx=0, eintragIdx=0, + systemIdx=0, statusIdx=0, ortIdx=2, effektIdx=0, title=None, + message=None, attachFile=None, destination_folder=None): + + super().__init__(parent, logbook=logbook, title=title, message=message) + + #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.projekt_idx = projektIdx + self.eintrag_idx = eintragIdx + self.system_idx = systemIdx + self.status_idx = statusIdx + self.ort_idx = ortIdx + self.effekt_idx = effektIdx + + #print("indices", self.projekt_idx, self.eintrag_idx, self.system_idx, self.ort_idx, flush=True) + self.projekt = None + self.eintrag = None + self.system = None + self.effekt = None + self.estatus = None + self.ort = None + self.konsole = None + + #print("LAYOUT ITEMS: ", self.layout_items) + + 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() + + for layout in self.get_logbook_specific_items(self.logbook): + remove_wgt(self.layout_to_widget_dict[layout]) + + + def create_layout_widgets(self): + + if not self.projekt: + self.projekt = QHBoxLayout() + self.projekt.addWidget(QLabel('Projekt: ')) + self.projekt_items = QComboBox() + self.projekt_items.setObjectName("Elog") + self.projekt.addWidget(self.projekt_items) + self.layout_to_widget_dict['Projekt'] = self.projekt + + if not self.eintrag: + 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.system: + 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.effekt: + self.effekt = QHBoxLayout() + self.effekt.addWidget(QLabel('Effekt: ')) + self.effekt_items = QComboBox() + self.effekt_items.setObjectName('Elog') + self.effekt.addWidget(self.effekt_items) + self.layout_to_widget_dict['Effekt'] = self.effekt + + if not self.estatus: + 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 + + + if not self.ort: + self.ort = QHBoxLayout() + self.ort.addWidget(QLabel('Ort: ')) + self.ort_items = QComboBox() + self.ort_items.setObjectName("Elog") + self.ort.addWidget(self.ort_items) + self.layout_to_widget_dict['Ort'] = self.ort + + if not self.konsole: + self.konsole = QHBoxLayout() + self.konsole.addWidget(QLabel('Konsole: ')) + self.konsole_le = QLineEdit() + self.konsole_le.setObjectName('Elog') + self.konsole_le.setText(_hostname) + self.konsole_le.setFixedWidth(100) + self.konsole_le.setAlignment(Qt.AlignCenter) + self.konsole.addWidget(self.konsole_le) + self.layout_to_widget_dict['Konsole'] = self.konsole + ''' + self.konsole = QHBoxLayout() + self.konsole.addWidget(QLabel('Konsole: ')) + self.konsole_items = QComboBox() + self.konsole_items.setObjectName("Elog") + self.konsole.addWidget(self.konsole_items) + self.layout_to_widget_dict['Konsole'] = self.konsole + ''' + + + def initialize_layout(self, logbook): + + print("initialize_layout", self.layout_items, flush=True) + + #Decide on layout + self.create_layout_widgets() + + item_no = 2 + if 'Projekt' in self.layout_items: + self.projekt_items.clear() + self.projekt_items.addItems(list(self.parent.settings.data[ + "ElogBooks"][logbook]['Required']['Projekt'])) + self.projekt_items.setCurrentIndex(self.projekt_idx) + self.layout.insertLayout(item_no, self.projekt) + if 'Eintrag' in self.layout_items: + 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 'Effekt' in self.layout_items: + item_no += 1 + self.effekt_items.clear() + self.effekt_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Optional']['Effekt'])) + self.effekt_items.setCurrentIndex(self.effekt_idx) + self.layout.insertLayout(item_no, self.effekt) + if 'System' in self.layout_items: + item_no += 1 + 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 + 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) + if 'Ort' in self.layout_items: + #Optional for HIPA, Required for HIPA-Bestandesaufnahme + item_no += 1 + self.ort_items.clear() + _target = "Optional" + if 'Required' in self.parent.settings.data['ElogBooks'][logbook]: + if 'Ort' in self.parent.settings.data['ElogBooks'][logbook][ + 'Required']: + _target = "Required" + #print("_target", _target, self.ort_idx, flush=True) + #print(list(self.parent.settings.data['ElogBooks'][ + # logbook][_target]['Ort']), flush=True) + self.ort_items.addItems(list(self.parent.settings.data['ElogBooks'][ + logbook][_target]['Ort'])) + #HIPA-Bestandesaufnahme does not have the first two entries that HIPA has + + offset = 2 if "Bestand" in logbook else 0 + + + self.ort_items.setCurrentIndex(self.ort_idx - offset) + self.layout.insertLayout(item_no, self.ort) + + if 'Konsole' in self.layout_items: + item_no += 1 + self.layout.insertLayout(item_no, self.konsole) + + ''' + self.konsole_items.clear() + self.konsole_items.addItems(list(self.parent.settings.data[ + 'ElogBooks'][logbook]['Required']['Konsole'])) + self.konsole_items.setCurrentIndex(self.konsole_idx) + self.layout.insertLayout(item_no, self.konsole) + ''' + 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) + + #Meet the new logbook. Same as the old logbook + if new_logbook == self.logbook: + return + + self.layout_items = self.get_logbook_specific_items(new_logbook) + print("self.layout_items", self.layout_items) + + ''' + #Acquire New Layout List + new_items = self.get_logbook_specific_items(new_logbook) + #Acquire Current Layout List + old_items = self.get_logbook_specific_items(self.logbook) + #Compare lists and remove Layouts that do not belong + common_items = list(set(new_items) & set(old_items)) + print("common items", common_items) + #remove common_items from old_items + + #unwanted_items = [ele for ele in old_items if ele not in common_items] + #print("unwamted items", unwanted_items) + unwanted_items = [ele for ele in old_items if ele not in new_items] + print("unwamted items", unwanted_items) + + #Add Layouts that are new + wanted_items = [ele for ele in new_items if ele not in old_items] + print("wanted items", wanted_items) + ''' + + 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 ok(self): + + el = self.elog_items.currentText() + + if 'Projekt' in self.layout_items: + self.attributes['Projekt'] = self.projekt_items.currentText() + if 'Eintrag' in self.layout_items: + self.attributes['Eintrag'] = self.eintrag_items.currentText() + if 'Effekt' in self.layout_items: + self.attributes['Effekt'] = self.effekt_items.currentText() + if 'System' in self.layout_items: + self.attributes['System'] = self.system_items.currentText() + if 'Status' in self.layout_items: + self.attributes['Status'] = self.estatus_items.currentText() + if 'auto' in self.layout_items: + self.attributes['auto'] = self.auto_items.currentText() + if 'Ort' in self.layout_items: + self.attributes['Ort'] = self.ort_items.currentText() + if 'Konsole' in self.layout_items: + self.attributes['Konsole'] = self.konsole_le.text() + + QSendToELOGFrame.ok(self) + +