'''The GUI module for Savings Overview and magnet control ''' import decimal import getpass import os import random import socket import threading import time from datetime import timedelta #from qtpy import QtCore, QtGui from qtpy.QtGui import QColor, QPixmap from qtpy.QtCore import __version__ as QT_VERSION_STR from qtpy.QtCore import QEventLoop, Qt, QTimer, Slot from qtpy.QtWidgets import ( QApplication, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QMessageBox, QPushButton, QTabBar, QTabWidget, QTableWidgetItem, QTextEdit, QVBoxLayout, QWidget) from common.packages import elog from apps4ops.bdbase.utils import _line from apps4ops.bdbase.enumkind import MsgSeverity from caqtwidgets.pvwidgets import ( CAQLabel, CAQLineEdit, CAQMessageButton, CAQTableWidget, CAQTextEntry, QHLine) _pymodule = os.path.basename(__file__) class AppGui(QWidget): ''' Main GUI class ''' def __init__(self, parent): #super(AppGui, self).__init__() # using Python 3 style super() without arguments super().__init__() self.parent = parent self.cafe = self.parent.cafe self.cyca = self.parent.cyca self.check_status = self.parent.check_status self.check_status_list = self.parent.check_status_list self.elog_enum = self.parent.elog_enum self.send_to_log_window = self.parent.send_to_log_window self.show_log_message = self.parent.show_log_message self.statusbar = self.parent.statusbar self.gui_frame = self.parent.gui_frame self.gui_header = self.parent.gui_header self.font_gui = self.parent.gui_frame.font_gui self.input_parameters = self.parent.input_parameters self.input_labels = self.parent.input_labels self.expert_parameters = self.parent.expert_parameters self.gui_frame.expert_parameters_group.setFixedWidth(260) self.gui_frame.expert_parameters_group.setFixedHeight(130) self.gui_frame.operator_parameters_group.setFixedWidth(260) self.gui_frame.operator_parameters_group.setFixedHeight(130) self.gui_frame.measurement_tab_wgt.setFixedWidth(496) self.gui_frame.measurement_tab_wgt.setFixedHeight(210) # self.gui_frame.operator_wgt.setFixedHeight(240) # self.gui_frame.expert_wgt.setFixedHeight(240) self.gui_frame.central_tab_widget.tabBar().setTabText(0, "Control") self.gui_frame.central_tab_widget.tabBar().setTabText(1, "IOC") self.expert_labels = self.parent.expert_labels self.settings = self.parent.settings self.lock = threading.Lock() self.timer = QTimer() self.timer.setSingleShot(True) self.obj_to_upper = bool(random.randint(1, 10) > 3) self.table_sol_dict = {} self.table_pwr_dict = {} self.offtime_dict = {} self.I_min = 1.0 self.with_rf = False self.sec_state_list = ["ZIP2-HUSH:STATE", "ZIW2-HUSH:STATE", "ZPK1-HUSH:STATE", "ZPK2-HUSH:STATE", "ZSINQ-HUSH:STATE", "ZUCN-HUSH:STATE"] self.cafe.openPrepare() self.cafe.open(["ZHIPA-HUSH:LASTPWR", "ZHIPA-HUSH:TOTPWR", "ZHIPA-HUSH:LASTSAVE", "ZHIPA-HUSH:TOTSAVE"]) self.cafe.open(self.sec_state_list) self.cafe.open("UCN:BEAMREQ:STATUS") self.cafe.openNowAndWait(0.1) self.cafe.monitor("UCNQ:BEAMREQ:STATUS") idx = self.settings.data["header"].index("IP2") self.sectorI_dict = {} for sector in self.settings.data["header"][idx:]: self.sectorI_dict[sector] = 0 self.sector_designated_magnet_standby_dict = {} self.sector_designated_magnet_values_dict = {} for sector in self.settings.data["header"][idx:]: self.sector_designated_magnet_standby_dict[sector] = {} self.sector_designated_magnet_values_dict[sector] = {} self.sector_designated_magnet_standby_dict[ sector] = self.get_standby_dict(sector) self.sector_designated_magnet_values_dict[ sector] = self.get_standby_dict(sector) print(sector, self.sector_designated_magnet_standby_dict[sector]) wgt = self.group_sector_qtabwidget() self.gui_frame.measurement_layout.addWidget( wgt, 0, 1, 6, 3, alignment=Qt.AlignTop) status_wgt = self.group_sector_status() self.gui_frame.measurement_layout.addWidget( status_wgt, 2, 0, 2, 1, alignment=Qt.AlignTop) # | Qt.AlignHCenter) self.gui_frame.results_wgt.setFixedWidth(1500) self.gui_frame.results_wgt.setFixedHeight(860) self.gui_frame.results_wgt.setLayout(self.gui_frame.results_layout) self.gui_frame.results_layout.addWidget( self.reset_ioc_sm(), 0, 0, 1, 1, Qt.AlignCenter) sec_prefix_list = [ sub[0: sub.index("-")] for sub in self.sec_state_list] self.gui_frame.results_layout.addWidget( self.reset_ioc_saving(sec_prefix_list), 0, 1, 1, 1, Qt.AlignCenter) label = QLabel() pixmap = QPixmap(":/Hush.jpg") pixmap.scaled(180, 180, Qt.KeepAspectRatio) label.setFixedWidth(180) label.setFixedHeight(180) label.setPixmap(pixmap) label.setScaledContents(True) self.gui_frame.results_layout.addWidget( label, 0, 2, 1, 1, Qt.AlignLeft) self.gui_frame.results_layout.setContentsMargins(10, 40, 10, 10) self.gui_frame.results_layout.setVerticalSpacing(20) self.gui_frame.results_layout.setHorizontalSpacing(10) self.gui_frame.results_layout.addWidget( self.reset_lastpwr(), 1, 0, 1, 1, Qt.AlignmentFlag(Qt.AlignTop | Qt.AlignCenter)) ucn_line = ["PK1", "PK2", "UCN"] sinq_line = ["PK1", "PK2", "SINQ"] pk1_line = ["IW2"] pk2_line = ["IW2"] for sector in self.settings.data["header"][idx:]: gateway_indices = self.get_standby_index_list(sector) for idx in gateway_indices: pv_name_gw = self.table_sol_dict[sector].pv_gateway[idx].pv_name pvd = self.cafe.getPVCache(pv_name_gw, dt="float") #print(sector, "pv_name_gw", pv_name_gw, flush=True) # pvd.show() self.table_sol_dict[sector].pv_gateway[ idx].trigger_monitor_float.emit( pvd.value[0], pvd.status, pvd.alarmSeverity) def enable_disable_ucn(sect, sector_line, value): self.sectorI_dict[sect] = value beam_req_running = True beam_req_status = self.cafe.getCache("UCNQ:BEAMREQ:STATUS") #print(" beam_req_status", beam_req_status, flush=True) if beam_req_status is not None: if beam_req_status == "stopped": beam_req_running = False for sector in sector_line: beam_req_flag = True if sector == "UCN": beam_req_flag = beam_req_running # if self.sectorI_dict['UCN'] > self.I_min or self.sectorI_dict[ # 'SINQ'] > self.I_min: if self.sectorI_dict[sector] > self.I_min or beam_req_flag: if self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(False) else: if not self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(True) def enable_disable_end(sect, sector_line, value): self.sectorI_dict[sect] = value for sector in sector_line: # if self.sectorI_dict['UCN'] > self.I_min or self.sectorI_dict[ # 'SINQ'] > self.I_min: if self.sectorI_dict[sector] > self.I_min: if self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(False) else: if not self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(True) def enable_disable_pk(sect, sector_line, value): self.sectorI_dict[sect] = value for sector in sector_line: if self.sectorI_dict["PK1"] > self.I_min or self.sectorI_dict[ "PK2"] > self.I_min: if self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(False) else: if not self.table_sol_dict[ sector].standby_value_button.isEnabled(): self.table_sol_dict[ sector].standby_value_button.setEnabled(True) def receive_ucn_update(value, status, alarm_severity): del status, alarm_severity enable_disable_ucn("UCN", ucn_line, value) def receive_sinq_update(value, status, alarm_severity): del status, alarm_severity enable_disable_end("SINQ", sinq_line, value) def receive_pk1_update(value, status, alarm_severity): del status, alarm_severity enable_disable_pk("PK1", pk1_line, value) def receive_pk2_update(value, status, alarm_severity): del status, alarm_severity enable_disable_pk("PK2", pk2_line, value) self.gui_header.beam_current_wgt_dict[ "UCN"].trigger_monitor_float.connect(receive_ucn_update) self.gui_header.beam_current_wgt_dict[ "SINQ"].trigger_monitor_float.connect(receive_sinq_update) self.gui_header.beam_current_wgt_dict[ "PK1"].trigger_monitor_float.connect(receive_pk1_update) self.gui_header.beam_current_wgt_dict[ "PK2"].trigger_monitor_float.connect(receive_pk2_update) # Not required any longer @Slot(int, str, object) def receive_sec_state(handle, pv, pvdata): del handle pvsplit = pv.split("-") secsplit = pvsplit[0].split("Z") sec = secsplit[1] if pvdata.status == self.cyca.ICAFE_CA_OP_CONN_DOWN: return # But is SHIFTTYPE CORRECT? #is_shift_type_ok = True #shift_type = self.cafe.getCache("ZORG:SHIFT-TYPE", 'str') # if shift_type is not None: # if shift_type not in ['Produktion', 'Strahlentwicklung']: # is_shift_type_ok = False def already_in_standby(): devices = self.settings.data[sec]["iocDevice"] pv_list = [] standby_value_list = [] for magnet in devices: pv_list.append(magnet + ":SOL:2") idx = self.settings.data[sec]["device"].index(magnet) standby_value = self.settings.data[sec]["standby"][idx] standby_value_list.append(standby_value) value_list, status, status_list = self.cafe.getScalarList( pv_list, cacheFlag=True) if status != self.cyca.ICAFE_NORMAL: self.check_status_list(_pymodule, "getScalarListCache", pv_list, status_list, _line()) # check standby values in_standby = True for value, standby in zip(value_list, standby_value_list): # if abs(value) < abs(standby)*0.8 or abs(value) > # abs(standby)*1.2: if abs(value) > abs(standby) * 1.2: in_standby = False break return in_standby if pvdata.value[0] == "ON" and not already_in_standby(): print("sec-ON", secsplit[1], flush=True) self.table_sol_dict[sec].init_value_button.setEnabled(True) self.table_pwr_dict[sec].init_value_button.setEnabled(True) else: print("sec-OFF", secsplit[1], flush=True) self.table_sol_dict[sec].init_value_button.setEnabled(False) self.table_pwr_dict[sec].init_value_button.setEnabled(False) #print("sec", secsplit[1], flush=True) # April 2024 - NOT REQUIRED # for state in self.sec_state_list: #### self.cafe.monitor(state, receive_sec_state) def get_standby_index_list(self, sec): devices = self.settings.data[sec]["iocDevice"] standby_index_list = [] for magnet in devices: # get standby values idx = self.settings.data[sec]["device"].index(magnet) standby_index_list.append(idx) return standby_index_list def get_standby_list(self, sec): devices = self.settings.data[sec]["iocDevice"] pv_standby_list = [] for magnet in devices: pv_magnet = magnet + ":SOL:2" pv_standby_list.append(pv_magnet) return pv_standby_list def get_standby_dict(self, sec): devices = self.settings.data[sec]["iocDevice"] pv_standby_dict = {} for magnet in devices: pv_magnet = magnet + ":SOL:2" # get standby values idx = self.settings.data[sec]["device"].index(magnet) standby_value = self.settings.data[sec]["standby"][idx] pv_standby_dict[pv_magnet] = standby_value return pv_standby_dict def is_sector_standby(self, sector, pv_name, value): self.sector_designated_magnet_values_dict[sector][pv_name] = value #print(self.sector_designated_magnet_values_dict[sector], flush=True) is_standby = True for pv in self.sector_designated_magnet_values_dict[sector]: val_now = self.sector_designated_magnet_values_dict[sector][pv] val_standby = self.sector_designated_magnet_standby_dict[sector][pv] #print(sector, val_now, val_standby) if abs(val_now) > (1.2 * abs(val_standby)): is_standby = False break with self.lock: if not is_standby: if not self.table_sol_dict[sector].init_value_button.isEnabled( ): self.table_sol_dict[sector].init_value_button.setEnabled( True) if not self.table_pwr_dict[sector].init_value_button.isEnabled( ): self.table_pwr_dict[sector].init_value_button.setEnabled( True) else: if self.table_sol_dict[sector].init_value_button.isEnabled(): self.table_sol_dict[sector].init_value_button.setEnabled( False) if self.table_pwr_dict[sector].init_value_button.isEnabled(): self.table_pwr_dict[sector].init_value_button.setEnabled( False) @Slot(float, int, int) def designated_magnet_cb(self, value, status, alarm_severity): #print("designated_magnet_cb", status, flush=True) if status == self.cyca.ICAFE_CA_OP_CONN_DOWN: return sender = self.sender() sector = sender.sector pv_name = sender.pv_name # print("local_cb", sender.pv_name, value, status, alarm_severity, # flush=True) self.timer.singleShot( 0, lambda: self.is_sector_standby(sector, pv_name, value)) #print("designated_magnet_cb end for sector", sector, flush=True) def group_sector_status(self): qgrid = QGridLayout() # Connect all channels # Heading idx = self.settings.data["header"].index("IP2") # Sector qlp = QLabel("Power \n(kW)") f = qlp.font() f.setPixelSize(13) qlp.setFont(f) qlp.setAlignment(Qt.AlignCenter) qli = QLabel("Initial \n(kW)") f = qli.font() f.setPixelSize(13) qli.setFont(f) qli.setAlignment(Qt.AlignCenter) qgrid.addWidget(qli, 0, 1, 1, 1) qsa = QLabel("Saving \n(MWh)") f = qsa.font() f.setPixelSize(13) qsa.setFont(f) qsa.setAlignment(Qt.AlignCenter) qti = QLabel("Time in \nSaving Mode") f = qti.font() f.setPixelSize(13) qti.setFont(f) qti.setAlignment(Qt.AlignCenter) qtotsav = QLabel("Tot Saved \n(MWh)") f = qtotsav.font() f.setPixelSize(13) qtotsav.setFont(f) qtotsav.setAlignment(Qt.AlignCenter) qgrid.addWidget(qlp, 0, 2, 1, 1) qgrid.addWidget(qsa, 0, 3, 1, 1) qgrid.addWidget(qti, 0, 4, 1, 1) qgrid.addWidget(qtotsav, 0, 5, 1, 1) for i, sector in enumerate(self.settings.data["header"][idx:]): a, b, c, d, e, f = self.sector_status(sector) a.setContentsMargins(2, 0, 0, 0) qgrid.addWidget(a, i + 1, 0, 1, 1) qgrid.addWidget(b, i + 1, 1, 1, 1) qgrid.addWidget(c, i + 1, 2, 1, 1) qgrid.addWidget(d, i + 1, 3, 1, 1) qgrid.addWidget(e, i + 1, 4, 1, 1) qgrid.addWidget(f, i + 1, 5, 1, 1) qh_line = QHLine() qh_line.setFixedHeight(10) row = qgrid.rowCount() qgrid.addWidget(qh_line, row, 1, 1, 5) #qgrid.setRowMinimumHeight(row, 60) qtot = QLabel("Total:") fnt = qtot.font() fnt.setPixelSize(13) qtot.setFont(fnt) qgrid.addWidget(qtot, row + 1, 0, 1, 1) qgrid.addWidget( CAQLineEdit(self, pv_name="ZHIPA-HUSH:LASTPWR", show_units=True), row + 1, 1, 1, 1) qgrid.addWidget( CAQLineEdit(self, pv_name="ZHIPA-HUSH:TOTPWR", show_units=True), row + 1, 2, 1, 1) qgrid.addWidget( CAQLineEdit(self, pv_name="ZHIPA-HUSH:LASTSAVE", show_units=False), row + 1, 3, 1, 1) qgrid.addWidget( CAQLineEdit(self, pv_name="ZHIPA-HUSH:TOTSAVE", show_units=False), row + 1, 5, 1, 1) qgrid.setContentsMargins(9, 20, 9, 9) qw = QGroupBox("Savings Overview") qw.setContentsMargins(9, 9, 9, 9) qw.setObjectName("OUTER") qw.setLayout(qgrid) qw.setFixedWidth(496) # 480 qw.setFixedHeight(346) # 480 return qw def sector_status(self, sector): '''Create each sector line for inclusion into group ''' device = "Z" + sector + "-HUSH" # Qlabel qsector = QLabel(sector + ":") f = qsector.font() f.setPixelSize(13) qsector.setFont(f) color = "black" try: color = self.settings.data[sector]["color"] except KeyError as ex: print(ex, self.settings.data[sector]) color_str = "color : {0};".format(color) incolor = "QLabel {" + color_str + "}" qsector.setStyleSheet(incolor) # Savings pv_pwr_tot = device + ":TOTPWR" pv_pwr_last = device + ":LASTPWR" pv_pwr_saved = device + ":LASTSAVE" pv_pwr_timeout = device + ":OFFTIME" pv_pwr_totsave = device + ":TOTSAVE" self.cafe.openPrepare() self.cafe.open([pv_pwr_tot, pv_pwr_last, pv_pwr_saved, pv_pwr_timeout]) self.cafe.openNowAndWait(2.0) time.sleep(0.1) def cb_outtime(handle, pv, pvdata): try: delta = "{}".format(str(timedelta(seconds=(pvdata.value[0])))) if delta == "0:00:00": delta = "0 s " self.offtime_dict[pv].setAlignment(Qt.AlignRight) elif ", " in delta: le_split = delta.split(", ") if len(le_split) == 2: if len(le_split[1]) == len("0:00:00"): delta = le_split[0] + ", " + le_split[1] if "days," in delta: delta = delta.replace("days,", "d") elif "day," in delta: delta = delta.replace("day,", "d") self.offtime_dict[pv].setText(delta) except KeyError: pass #now = QDateTime.currentDateTime() #xdate = QDate(now.date().year(), 1, 15) #xstart = QDateTime(xdate) qpinit = CAQLineEdit(self, pv_name=pv_pwr_last, show_units=False) qpnow = CAQLineEdit(self, pv_name=pv_pwr_tot, show_units=False) qpsave = CAQLineEdit(self, pv_name=pv_pwr_saved, show_units=False) qptotsave = CAQLineEdit(self, pv_name=pv_pwr_totsave, show_units=False) qptime = CAQLineEdit(self, pv_name=pv_pwr_timeout, monitor_callback=cb_outtime, show_units=True) self.offtime_dict[pv_pwr_timeout] = qptime qptime.setFixedWidth(104) return qsector, qpinit, qpnow, qpsave, qptime, qptotsave def group_sector_qtabwidget(self): idx_inj = self.settings.data["header"].index("INJ2") idx = self.settings.data["header"].index("IP2") # open all PVS pv = [] for sector in self.settings.data["header"][idx_inj:]: device_list = self.settings.data[sector]["device"] attribute_list = self.settings.data[sector]["attribute"] for att in attribute_list: for dev in device_list: pv.append(dev + ":" + att) self.cafe.openPrepare() self.cafe.open(pv) self.cafe.openNowAndWait(1.0) # self.cafe.supplementHandles() sector_wgt_dict = {} sector_wgt_dict["INJ2"] = self.ca_table_rf_widget(sector="INJ2") for sector in self.settings.data["header"][idx:]: sector_wgt_dict[sector] = self.ca_table_sector_widget( sector=sector) sector_tab_widget = QTabWidget() sector_tab_widget.setFont(self.font_gui) sector_tab_widget.setStyleSheet("QTabBar {font-size: 12pt;}") sector_tab_widget.tabBar().setShape(QTabBar.TriangularNorth) for i, sector in enumerate(self.settings.data["header"][idx:]): sector_tab_widget.addTab(sector_wgt_dict[sector], sector) color = self.settings.data[sector]["color"] sector_tab_widget.tabBar().setTabTextColor(i, QColor(color)) sector_tab_widget.addTab(sector_wgt_dict["INJ2"], "RF") color = self.settings.data["INJ2"]["color"] sector_tab_widget.tabBar().setTabTextColor( len(self.settings.data["header"][idx:]), QColor(color)) return sector_tab_widget def ca_table_rf_widget(self, sector: str = ""): device_list = self.settings.data[sector]["device"] attribute_list = self.settings.data[sector]["attribute"] pv_dict = {} for att in attribute_list: pv_dict[att] = [] # [None] * len(device_list) for dev in device_list: pv_dict[att].append(dev + ":" + att) try: CR1IN_idx = pv_dict["IST:2"].index("CR1IN:IST:2") except ValueError: CR1IN_idx = 3 table_pwr = [None] * 2 table_pwr[0] = CAQTableWidget( self, pv_list=pv_dict["IST:2"][0:CR1IN_idx], show_units=False, notify_freq_hz=0, suffix="MW", notify_unison=False, scale_factor=0.001, show_timestamp=False, init_column=True, pv_list_show=device_list[0:CR1IN_idx]) table_pwr[1] = CAQTableWidget( self, pv_list=pv_dict["IST:2"][CR1IN_idx:], show_units=False, notify_freq_hz=0, suffix="MW", notify_unison=False, scale_factor=0.001, show_timestamp=False, init_column=True, pv_list_show=device_list[CR1IN_idx:]) for tab in table_pwr: header_item = QTableWidgetItem() header_init = QTableWidgetItem() #header_standby = QTableWidgetItem() header_value = QTableWidgetItem() f = header_value.font() f.setPixelSize(13) header_item.setFont(f) header_item.setText("Device") header_init.setFont(f) header_init.setText("Init. Value") header_value.setFont(f) header_value.setText("IST:2") tab.setContentsMargins(15, 0, 15, 10) tab.setHorizontalHeaderItem(0, header_item) tab.setHorizontalHeaderItem(1, header_init) tab.setHorizontalHeaderItem(2, header_value) tab.setColumnWidth(0, 80) tab.setColumnWidth(1, 88) tab.setColumnWidth(2, 88) tab.setFixedWidth(304) tab.restore_layout.removeWidget(tab.restore_value_button) tab.restore_value_button.deleteLater() tab.restore_value_button = None tab.init_value_button.setToolTip( ("Shows initial, pre-standby values. Update is also " + "executed automatically before the standby procedure.")) box = QVBoxLayout() box.addWidget(table_pwr[0]) box.addWidget(table_pwr[1]) box.setAlignment(Qt.AlignTop | Qt.AlignLeft) box.setSpacing(10) qw = QWidget() qw.setLayout(box) obj_name = self.settings.data[sector]["colorObj"] if self.obj_to_upper: obj_name = obj_name.upper() qw.setObjectName(obj_name) return qw def on_sector_standby(self): target = self.sender() sector = target.sector table = self.table_sol_dict[sector] def is_update_enabled(): '''Check if update buttons are enabled if NOT, then do not allow inital values values to be updated" ''' return bool( self.table_pwr_dict[sector].init_value_button.isEnabled( ) and table.init_value_button.isEnabled()) # if self.table_pwr_dict[sector].init_value_button.isEnabled() \ # and table.init_value_button.isEnabled(): # return True # else: # return False def write_to_restore_ucn(): if sector != "UCN": return restore_pvs = self.settings.data["RestoreUCN"]["PV"] default_values = self.settings.data["RestoreUCN"]["value"] #print(restore_pvs, default_values, flush=True) values_dict = self.table_sol_dict["UCN"].get_init_values() #print(values_dict, flush=True) values = [] pvs = [] for pv, val in zip(restore_pvs, default_values): pv_indict = pv[-4:] + ":SOL:2" try: _val = values_dict[pv_indict] if _val > 0.8 * val: pvs.append(pv) values.append(_val) except KeyError as ex: print("ex", ex) if pvs: status, status_list = self.cafe.setScalarList(pvs, values) if status != self.cyca.ICAFE_NORMAL: self.send_to_log_window(pv_list=pvs, status=status, status_list=status_list, operation="set", pymodule=_pymodule, line=_line()) if sector == "UCN": write_to_restore_ucn() QApplication.processEvents(QEventLoop.AllEvents, 1) # Do NOT do updates if in standby mode! # Update button is disabled when in standby # disenable widgets to avoid circular behaviour since # updates of SOL also update PWR, and vice-versa # Do not click if already on standby!! with self.lock: update_enabled = is_update_enabled() if update_enabled: self.table_pwr_dict[sector].init_value_button.setEnabled(False) table.init_value_button.click() time.sleep(0.05) self.table_pwr_dict[sector].init_value_button.setEnabled(True) table.init_value_button.setEnabled(False) self.table_pwr_dict[sector].init_value_button.click() time.sleep(0.05) self.table_sol_dict[sector].init_value_button.setEnabled(True) QApplication.processEvents(QEventLoop.AllEvents, 1) if not self.input_parameters["simulation"]: if sector == "UCN": write_to_restore_ucn() status, status_list, pv_list = table.set_standby_values() if status != self.cyca.ICAFE_NORMAL: #self.check_status_list(pv_list, status_list, _line()) self.check_status_list( _pymodule, "setScalarList", pv_list, status_list, _line()) # Seqeuncer ONLY determines when to go into STANDBY mode # and NOT the HLA ##pv = 'Z' + target.sector + "-HUSH:STATE" ##stat = self.cafe.set(pv, 0) ##self.check_status(pv, stat, _line()) # Disable because table.set_standby_values() enables it if not update_enabled: with self.lock: self.table_pwr_dict[sector].init_value_button.setEnabled(False) table.init_value_button.setEnabled(False) def on_sector_restore(self): target = self.sender() sector = target.sector table = self.table_sol_dict[sector] if not self.input_parameters["simulation"]: status, status_list, pv_list = table.restore_init_values() if status != self.cyca.ICAFE_NORMAL: #self.check_status_list(pv_list, status_list, _line()) self.check_status_list( _pymodule, "setScalarList", pv_list, status_list, _line()) # Seqeuncer ONLY determines when to go into STANDBY mode # and NOT the HLA #pv = "Z" + target.sector + "-HUSH:STATE" #stat = self.cafe.set(pv, 1) #self.check_status(pv, stat, _line()) table_pwr = self.table_pwr_dict[sector] row_pwr_dict = table_pwr.get_init_values() sum_pwr = sum(list(row_pwr_dict.values())) #pv_last_pwr = "SEC-" + target.sector + ":LASTPWR" pv_last_pwr = "Z" + target.sector + "-HUSH:LASTPWR" stat = self.cafe.set(pv_last_pwr, sum_pwr) #self.check_status(pv_last_pwr, stat, _line()) self.check_status(_pymodule, "set", pv_last_pwr, stat, _line()) def ca_table_sector_widget(self, sector: str = ""): device_list = self.settings.data[sector]["device"] attribute_list = self.settings.data[sector]["attribute"] try: standby_values = self.settings.data[sector]["standby"] except KeyError as ex: print("On Standby values not given", ex) pv_dict = {} for att in attribute_list: pv_dict[att] = [] # [None] * len(device_list) for dev in device_list: pv_dict[att].append(dev + ":" + att) self.cafe.openPrepare() # for key, val in pv_dict.items(): for val in pv_dict.values(): self.cafe.open(val) self.cafe.openNowAndWait(2.0) time.sleep(0.05) try: _delay = self.input_parameters["delayRamp"] except KeyError: _delay = 0.09 pass pvlist = self.get_standby_list(sector) for pv in pvlist: value = self.cafe.getCache(pv) if value is not None: self.sector_designated_magnet_values_dict[sector][pv] = value table_sol = CAQTableWidget( self, pv_list=pv_dict["SOL:2"], show_units=True, notify_freq_hz=0, notify_unison=False, scale_factor=1, show_timestamp=False, init_column=True, pv_list_show=device_list, standby_column=True, standby_values=standby_values, set_delay=_delay) table_sol.restore_value_button.setToolTip( ("Restore devices to their pre-standby values")) table_sol.init_value_button.setToolTip( ("Shows initial, pre-standby values. Update is also " + "executed automatically before the standby procedure.")) table_sol.standby_value_button.sector = sector table_sol.standby_value_button.clicked.disconnect() table_sol.standby_value_button.clicked.connect(self.on_sector_standby) table_sol.restore_value_button.sector = sector table_sol.restore_value_button.clicked.disconnect() table_sol.restore_value_button.clicked.connect(self.on_sector_restore) self.table_sol_dict[sector] = table_sol table_ist = CAQTableWidget( self, pv_list=pv_dict["IST:2"], show_units=True, notify_freq_hz=2, notify_unison=True, scale_factor=1, show_timestamp=False, init_column=False, pv_list_show=[False]) table_pwr = CAQTableWidget( self, pv_list=pv_dict["PWR:2"], show_units=False, notify_freq_hz=0, suffix="kW", notify_unison=False, scale_factor=0.001, show_timestamp=False, init_column=True, init_list=pv_dict["PWR:2"], pv_list_show=[False]) table_pwr.init_value_button.setToolTip( ("Shows initial, pre-standby values. Update is also " + "executed automatically before the standby procedure.")) self.table_pwr_dict[sector] = table_pwr header_item = QTableWidgetItem() header_init = QTableWidgetItem() header_standby = QTableWidgetItem() header_value = QTableWidgetItem() f = header_value.font() f.setPixelSize(13) header_item.setFont(f) header_item.setText("Device") header_init.setFont(f) header_init.setText("Init. Value") header_standby.setFont(f) header_standby.setText("Standby") header_value.setFont(f) header_value.setText("SOL:2") table_sol.setContentsMargins(15, 0, 15, 10) table_sol.setHorizontalHeaderItem(0, header_item) table_sol.setHorizontalHeaderItem(1, header_init) table_sol.setHorizontalHeaderItem(2, header_standby) table_sol.setHorizontalHeaderItem(3, header_value) table_sol.setColumnWidth(0, 80) table_sol.setColumnWidth(1, 88) table_sol.setColumnWidth(2, 80) table_sol.setColumnWidth(3, 88) table_sol.setFixedWidth(386) ioc_magnets = [] for i, device in enumerate(device_list): if device in self.settings.data[sector]["iocDevice"]: ioc_magnets.append(i) header_value = QTableWidgetItem() f = header_value.font() f.setPixelSize(13) header_value.setFont(f) header_value.setText("IST:2") table_ist.setContentsMargins(15, 0, 15, 10) table_ist.setHorizontalHeaderItem(0, header_value) table_ist.setColumnWidth(0, 90) table_ist.setFixedWidth(140) header_init = QTableWidgetItem() f = header_init.font() header_init.setFont(f) header_init.setText("Init. Value") header_value = QTableWidgetItem() f = header_value.font() f.setPixelSize(13) header_value.setFont(f) header_value.setText("PWR:2") table_pwr.setContentsMargins(15, 0, 15, 10) table_pwr.setHorizontalHeaderItem(0, header_init) table_pwr.setHorizontalHeaderItem(1, header_value) table_pwr.setColumnWidth(0, 88) table_pwr.setColumnWidth(1, 88) table_pwr.setFixedWidth(226) for i in ioc_magnets: table_sol.paint_rows(row_range=[i, i + 1], reset=False, columns=[0, 1]) table_ist.paint_rows(row_range=[i, i + 1], reset=False) table_pwr.paint_rows(row_range=[i, i + 1], reset=False) pvI = self.settings.data[sector]["current"] I = self.cafe.getCache(pvI) if I is not None: if I < self.I_min: try: values = self.settings.data[sector]["ref"] table_sol.set_init_values(values) except KeyError: pass try: pwr_values = self.settings.data[sector]["pwrref"] table_pwr.set_init_values(pwr_values) except KeyError: pass gateway_indices = self.get_standby_index_list(sector) for idx in gateway_indices: table_sol.pv_gateway[idx].sector = sector table_sol.pv_gateway[idx].trigger_monitor_float.connect( self.designated_magnet_cb) def on_sol_update(): table_sol.init_value_button.setEnabled(False) table_pwr.init_value_button.click() time.sleep(0.1) table_sol.init_value_button.setEnabled(True) #pv = "SEC-" + table_pwr.init_value_button.sector + ":LASTPWR" pv = "Z" + table_pwr.init_value_button.sector + "-HUSH:LASTPWR" _sum = sum(table_pwr.get_init_values().values()) #print("SUM up all the values", pv, _sum, flush=True) stat = self.cafe.set(pv, _sum) #self.check_status(pv, stat, _line()) self.check_status(_pymodule, "set", pv, stat, _line()) def on_init_update(): table_pwr.init_value_button.setEnabled(False) table_sol.init_value_button.click() time.sleep(0.1) table_pwr.init_value_button.setEnabled(True) table_sol.init_value_button.sector = sector table_sol.init_value_button.clicked.connect(on_sol_update) table_pwr.init_value_button.sector = sector table_pwr.init_value_button.clicked.connect(on_init_update) hbox = QHBoxLayout() hbox.addWidget(table_sol) hbox.addWidget(table_ist) hbox.addWidget(table_pwr) hbox.setSpacing(10) hbox.setAlignment(Qt.AlignTop) qw = QWidget() qw.setLayout(hbox) obj_name = self.settings.data[sector]["colorObj"] if self.obj_to_upper: obj_name = obj_name.upper() qw.setObjectName(obj_name) return qw def clear_saving(self, sector_prefix_list: list = None): if not sector_prefix_list: return qm = QMessageBox() mess = ("This action will reset the energy saving account to zero. \n" + "This is typically undertaken at the end of the calendar " + "year. \n\n" + "Present accounting figures will be entered in the elogbook\n" + "Are you sure you wish to continue with the reset?") reply = qm.warning(self, "Reset Accounting", mess, QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.No: return self.parent.prepare_elog_message() print("message", self.parent.message, flush=True) print("logbook", self.parent.logbook, flush=True) url = self.settings.data["ElogBooks"][self.parent.logbook]["url"] print("url", url, flush=True) logbook = elog.open(url, user="robot", password="robot") # QApplication.processEvents() attributes = {} attributes["Autor"] = getpass.getuser() attributes["Author"] = getpass.getuser() attributes["Application"] = self.parent.appname attributes["Titel"] = self.parent.title attributes["Title"] = self.parent.title attributes["When"] = str(time.time()) attributes["Wann"] = str(time.time()) if "Sandkasten" in self.parent.logbook: attributes["Eintrag"] = "Anregung" elif "Bestandesaufnahme" in self.parent.logbook: attributes["Konsole"] = socket.gethostname().split(".")[0] attributes["Ort"] = "Global" elif "Strahlentwicklung" in self.parent.logbook: pass # HIPA Log Book else: attributes["Eintrag"] = self.elog_enum.eintrag.INFO.name attributes["Effekt"] = self.elog_enum.effekt.NONE.name # keiner # "Elektorversogung" attributes["System"] = self.elog_enum.system.ELECTRICAL_SUPPLY.name attributes["Ort"] = self.elog_enum.ort.GLOBAL.name log_mess = self.parent.message.replace("
", "\n") try: logbook.post(log_mess, attributes=attributes) self.show_log_message(MsgSeverity.INFO, _pymodule, _line(), log_mess) self.statusbar.showMessage("Reset Savings Account." + "Last values sent to elog") except Exception as ex: print("Exception in sendelog.py", str(ex), flush=True) mess = "Failed to write last saving values to elog:" + str(ex) self.show_log_message( MsgSeverity.ERROR, _pymodule, _line(), mess) self.statusbar.showMessage(mess) for sector in sector_prefix_list: self.sector_sm_off(sector) for sector in sector_prefix_list: self.clear_sector_saving(sector) def sector_sm_off(self, sector_prefix: str = None): if not sector_prefix: return pv1 = sector_prefix + "-HUSH:SEQ-ONOFF" stat = self.cafe.set(pv1, 0) self.check_status(_pymodule, "set", pv1, stat, _line()) time.sleep(0.05) QApplication.processEvents() return def clear_sector_saving(self, sector_prefix: str = None): if not sector_prefix: return pv1 = sector_prefix + "-HUSH:SEQ-ONOFF" pv2 = sector_prefix + "-HUSH:TOTSAVE" pv3 = sector_prefix + "-HUSH:OFFTIME" stat = self.cafe.set(pv1, 0) self.check_status(_pymodule, "set", pv1, stat, _line()) off_time = self.cafe.get(pv3) self.check_status(_pymodule, "get", pv3, None, _line()) iloop = 0 if off_time is not None: while off_time > 0 and iloop < 100: time.sleep(0.1) off_time_tmp = self.cafe.get(pv3) off_time = off_time_tmp if off_time_tmp is not None else 0 iloop += 1 QApplication.processEvents() else: time.sleep(0.1) print("SECTOR ILOOP", sector_prefix, iloop) stat = self.cafe.set(pv1, 1) self.check_status(_pymodule, "set", pv1, stat, _line()) stat = self.cafe.set(pv2, 0) self.check_status(_pymodule, "set", pv2, stat, _line()) return def reset_ioc_saving(self, sector_prefix_list: list = None): if not sector_prefix_list: return qpb = QPushButton("Reset Account") qpb.setObjectName("WriteData") qpb.clicked.connect(lambda: self.clear_saving(sector_prefix_list)) qpb.setFixedHeight(40) # self.gui_frame.widget_height) qpb.setFixedWidth(160) qtext = QTextEdit() qtext.setText("""
  The Reset Account Button resets Total Power Saved (MWh) to zero.
  This is typically executed at the end of the calendar year.
  The present energy saving figures will be entered into the elogbook.
  The button prompts the user for confirmation before proceeding.
""") qtext.setReadOnly(True) qtext.setStyleSheet("background-color: QColor(0, 0, 50, 10);") qtext.setFixedHeight(104) qtext.setFixedWidth(440) qgrid = QGridLayout() qgrid.setSpacing(0) qgrid.addWidget(qtext, 0, 0, 1, 1, Qt.AlignHCenter) qgrid.addWidget(qpb, 1, 0, 1, 1, Qt.AlignHCenter) qw = QGroupBox("HUSH! Accounting") qw.setContentsMargins(5, 10, 5, 0) qw.setAlignment(Qt.AlignTop) qw.setObjectName("OUTER") qw.setLayout(qgrid) qw.setFixedWidth(460) qw.setFixedHeight(280) return qw def reset_ioc_sm(self): msg_button = [None] * len(self.sec_state_list) monitor_seq_onoff = [None] * len(self.sec_state_list) monitor_seq_state = [None] * len(self.sec_state_list) monitor_standby_state = [None] * len(self.sec_state_list) qgrid = QGridLayout() qreset = QLabel("Reset") qreset.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) qsm = QLabel("SM ") qsm.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) qss = QLabel("Sequencer State ") qss.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) qstandby = QLabel("ON or \nStandby") qstandby.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) qgrid.addWidget(qreset, 0, 0, 1, 1) qgrid.addWidget(qsm, 0, 1, 1, 1) qgrid.addWidget(qss, 0, 2, 1, 1) qgrid.addWidget(qstandby, 0, 3, 1, 1) for i, pv in enumerate(self.sec_state_list): pvsplit = pv.split("-") sec = pvsplit[0][1:] pv_seq_onoff = pv.replace("STATE", "SEQ-ONOFF") pv_seq_state = pv.replace("STATE", "SEQ-STATE") msg_button[i] = CAQMessageButton( self, pv_name=pv_seq_onoff, msg_label=sec, msg_press_value="Off", msg_release_value="On") msg_button[i].setFixedWidth(60) monitor_seq_onoff[i] = CAQLabel(self, pv_name=pv_seq_onoff) monitor_seq_state[i] = CAQLabel(self, pv_name=pv_seq_state) monitor_standby_state[i] = CAQLabel(self, pv_name=pv) qgrid.addWidget(msg_button[i], i + 1, 0, 1, 1, Qt.AlignLeft) qgrid.addWidget(monitor_seq_onoff[i], i + 1, 1, 1, 1, Qt.AlignLeft) qgrid.addWidget(monitor_seq_state[i], i + 1, 2, 1, 1, Qt.AlignLeft) qgrid.addWidget( monitor_standby_state[i], i + 1, 3, 1, 1, Qt.AlignLeft) qgrid.setContentsMargins(9, 9, 9, 9) qgrid.setSpacing(10) qw = QGroupBox("State Machine") qw.setContentsMargins(5, 10, 5, 0) qw.setAlignment(Qt.AlignTop) qw.setObjectName("OUTER") qw.setLayout(qgrid) qw.setFixedWidth(460) qw.setFixedHeight(280) return qw def reset_lastpwr(self): sector_label = [None] * len(self.sec_state_list) pv_initpwr = [None] * len(self.sec_state_list) pv_lastpwr = [None] * len(self.sec_state_list) qgrid = QGridLayout() qsection = QLabel("Section") qsection.setAlignment( Qt.AlignmentFlag( Qt.AlignBottom | Qt.AlignHCenter)) qnominal = QLabel("Nominal \n Value (kW)") qnominal.setAlignment( Qt.AlignmentFlag( Qt.AlignBottom | Qt.AlignHCenter)) qset = QLabel("Set \n Value (kW)") qset.setAlignment(Qt.AlignmentFlag(Qt.AlignBottom | Qt.AlignHCenter)) qgrid.addWidget(qsection, 0, 0, 1, 1) qgrid.addWidget(qnominal, 0, 1, 1, 1) qgrid.addWidget(qset, 0, 2, 1, 1) for i, pv in enumerate(self.sec_state_list): pvsplit = pv.split("-") sector = pvsplit[0][1:] sum_pwr = round(sum(self.settings.data[sector]["pwrref"]), 3) pv_initpwr_value = decimal.Decimal(format(sum_pwr, ".3f")) pv_lastpwr_name = pv.replace("STATE", "LASTPWR") qsector = QLabel(sector + ":") f = qsector.font() f.setPixelSize(14) qsector.setFont(f) color = "black" try: color = self.settings.data[sector]["color"] except KeyError as ex: print(ex, self.settings.data[sector]) weight = "medium" color_str = "color : {0}; font-weight:{1};".format(color, weight) incolor = "QLabel {" + color_str + "}" qsector.setStyleSheet(incolor) sector_label[i] = qsector ql = QLabel(str(pv_initpwr_value)) ql.setFont(f) pv_initpwr[i] = ql ca_qtext_entry = CAQTextEntry(self, pv_name=pv_lastpwr_name) f = ca_qtext_entry.font() f.setPixelSize(8) ca_qtext_entry.setFont(f) pv_lastpwr[i] = ca_qtext_entry qgrid.addWidget(sector_label[i], i + 1, 0, 1, 1, Qt.AlignLeft) qgrid.addWidget(pv_initpwr[i], i + 1, 1, 1, 1, Qt.AlignRight) qgrid.addWidget(pv_lastpwr[i], i + 1, 2, 1, 1, Qt.AlignLeft) qgrid.setContentsMargins(9, 9, 9, 9) qgrid.setSpacing(10) qw = QGroupBox("Last Power") qw.setContentsMargins(5, 10, 5, 0) qw.setAlignment(Qt.AlignTop) qw.setObjectName("OUTER") qw.setLayout(qgrid) qw.setFixedWidth(240) qw.setFixedHeight(280) return qw