9 Commits

Author SHA1 Message Date
b67a601228 guiheader 2024-02-08 14:16:00 +01:00
d3ee217bc5 sendelogsf.py corrected 2023-08-21 15:45:36 +02:00
7fe986ae4e senfelogsf.py 2023-07-21 15:47:41 +02:00
43e582271e add Test Categories to enumkind - corrected 2023-07-17 17:18:44 +02:00
39f717ea6b add Test Categories to enumkind 2023-07-17 17:11:25 +02:00
2771f43cfa domain/section now taken from ElogBooks key 2023-07-17 13:55:49 +02:00
bf09681d39 remove prntDisconnectedHandles in guiheader 2023-06-06 09:30:53 +02:00
4ba2bff96c guiheader.py 2023-05-01 09:46:41 +02:00
ab39f9ba41 overdue update 2023-04-05 14:18:12 +02:00
4 changed files with 669 additions and 25 deletions

211
ambientdata.py Normal file
View File

@@ -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

View File

@@ -4,23 +4,51 @@ from enum import IntEnum
class ElogSwissFEL:
def __init__(self):
self.category = self.Category(0)
self.category_data = self.CategoryData(0)
self.category_test = self.CategoryTest(0)
self.domain = self.Domain(0)
self.system = self.System(0)
class Category(IntEnum):
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 CategoryData(IntEnum):
INFO = 0
MEASUREMENT = 1
class CategoryTest(IntEnum):
SHIFT_SUMMARY = 0
MEASUREMENT = 1
PROCEDURES = 2
PROBLEM = 3
INFO = 4
UBERBRUCKUNG = 5
PRE_BEAM_CHECK = 6
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 +56,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

View File

@@ -5,8 +5,8 @@ import random
from qtpy.QtCore import Qt, QTimer
from qtpy.QtGui import QFont
from qtpy.QtWidgets import (QApplication, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QRadioButton, QWidget)
from qtpy.QtWidgets import (QApplication, QGridLayout, QGroupBox,
QHBoxLayout, QLabel, QRadioButton, QWidget)
from caqtwidgets.pvwidgets import CAQLabel, QHLine, QVLine
from pyqtacc.bdbase.enumkind import UserMode
@@ -21,7 +21,7 @@ class GUIHeader(QWidget):
self.title = parent.title
self.settings = parent.settings
self.user_mode = user_mode
self.current_user_mode = user_mode
self.current_user_mode_name = user_mode.name
self.extended = extended
self.cafe = parent.cafe
self.cyca = parent.cyca
@@ -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 = []
@@ -71,7 +76,7 @@ class GUIHeader(QWidget):
self.cafe.open(pvlist)
self.cafe.openNowAndWait(0.4)
self.cafe.printDisconnected()
#self.cafe.printDisconnected()
self.station_width = 200 #default
self.station_height = 100
@@ -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
@@ -147,7 +153,7 @@ class GUIHeader(QWidget):
if reset:
self.reset_beamline()
self.target_beamline = target
def reset_beamline(self):
""" Reset QGroupBox for previous target to original colors
"""
@@ -188,11 +194,17 @@ class GUIHeader(QWidget):
def change_operation_mode(self, user_mode=UserMode.OPERATION):
""" Different operation modes have different color schemes
"""
title_name = user_mode.name
dry_run_tags = ['Emittance', 'Dry']
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, user_mode.name))
self.current_user_mode_name, title_name))
self.header_wgt.style().polish(self.header_wgt)
self.current_user_mode = user_mode
self.current_user_mode_name = title_name
def beamline_widget(self, beamline="Aramis"):
""" QGroupBox template for beamlines
@@ -322,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()

333
sendelogsf.py Normal file
View File

@@ -0,0 +1,333 @@
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)
self.settings = self.parent.settings
#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()
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.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.category:
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:
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:
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:
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:
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:
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
def initialize_layout(self, logbook):
#Decide on layout
self.create_layout_widgets()
item_no = 2
if 'Eintrag' in self.layout_items:
self.eintrag_idx = self.settings.data['ElogInit'][logbook][
'Eintrag']
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:
try:
self.category_idx = self.settings.data['ElogInit'][logbook][
'Category']
except KeyError as ex:
print("Did not find Category Index" + str(ex))
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
self.layout.insertLayout(item_no, self.effekt)
if 'Domain' in self.layout_items:
item_no += 1
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
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
self.system_idx = self.settings.data['ElogInit'][logbook][
'System']
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)
#IS this required --attached files should be passed on
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
self.layout_items = self.get_logbook_specific_items(new_logbook)
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])
#self.section_items.setCurrentIndex(0)
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)