diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/config/solaris.json b/config/solaris.json new file mode 100644 index 0000000..9c2cbda --- /dev/null +++ b/config/solaris.json @@ -0,0 +1,14 @@ +{ + "devices": { + "TS": "testing/libera-brilliance/raf4", + "EVG": "R1-SGD/CTL/R1-SGDCAB10-CTL-EVG1" + }, + "units": { + }, + "settings": { + "x": "TS", + "y": "TS", + "global_orbit": "TS", + "global_magnet": "TS" + } +} diff --git a/errorhook.py b/errorhook.py new file mode 100644 index 0000000..5b9935e --- /dev/null +++ b/errorhook.py @@ -0,0 +1,56 @@ +# Adapted from http://timlehr.com/python-exception-hooks-with-qt-message-box/ + +import sys +import traceback +from PyQt4 import QtCore, QtGui + + +def show_exception_box(log_msg): + """ + Checks if a QApplication instance is available and shows a messagebox with the exception message + If unavailable (non-console application), log an additional notice. + """ + print "[ERROR]: An unexpected error occured: %s" % log_msg + if QtGui.QApplication.instance() is not None: + QtGui.QMessageBox.critical( + None, + "FOFB: Unexpected error", + """ + An unexpected error occured: +
+
+ %s + """ % log_msg.replace("\n", "
") + ) + + +class UncaughtHook(QtCore.QObject): + _exception_caught = QtCore.Signal(object) + + def __init__(self, *args, **kwargs): + super(UncaughtHook, self).__init__(*args, **kwargs) + + # this registers the exception_hook() function as hook with the Python interpreter + sys.excepthook = self.exception_hook + + # connect signal to execute the message box function always on main thread + self._exception_caught.connect(show_exception_box) + + def exception_hook(self, exc_type, exc_value, exc_traceback): + """Function handling uncaught exceptions. + It is triggered each time an uncaught exception occurs. + """ + if issubclass(exc_type, KeyboardInterrupt): + # ignore keyboard interrupt to support console applications + sys.__excepthook__(exc_type, exc_value, exc_traceback) + else: + exc_info = (exc_type, exc_value, exc_traceback) + log_msg = "\n".join([ + "".join(traceback.format_tb(exc_traceback)), + "%s: %s" % (exc_type.__name__, exc_value) + ]) + # trigger message box show + self._exception_caught.emit(log_msg) + +# create a global instance of our class to register the hook +qt_exception_hook = UncaughtHook() diff --git a/fofbcontrol b/fofbcontrol new file mode 100755 index 0000000..5dafddc --- /dev/null +++ b/fofbcontrol @@ -0,0 +1,38 @@ +#!/usr/bin/env python2.7 + +import os +import sys +from signal import signal, SIGINT, SIG_DFL +from subprocess import check_output + +from PyQt4.QtGui import QApplication + +# from splash import SplashScreen +from mainwindow import MainWindow +# import errorhook # noqa - this only needs to register an exception hook + + +if __name__ == "__main__": + signal(SIGINT, SIG_DFL) + base_dir = os.path.dirname(os.path.realpath(__file__)) + version = check_output(["git", "describe", "--tags", "--always"])[:-1] + + app = QApplication([]) + offline = False + + # first - create the main window + window = MainWindow(offline=offline) + window.setVersion(version) + window.show() + + # then - show the splash screen + # splash = SplashScreen(window) + # splash.setVersion(version) + + # and finally start the main window + if len(sys.argv) >= 2: + window.start(sys.argv[1]) + else: + window.start("%s/config/solaris.json" % base_dir) + + sys.exit(app.exec_()) diff --git a/grouping.py b/grouping.py new file mode 100644 index 0000000..fae046b --- /dev/null +++ b/grouping.py @@ -0,0 +1,88 @@ +import os + +from PyQt4 import QtGui, QtCore, uic + +from labels import cm + +base_dir = os.path.dirname(os.path.realpath(__file__)) + + +class GroupingWindow(QtGui.QWidget): + def __init__(self, main_window, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/grouping.ui" % base_dir, self) + + self.main_window = main_window + self.units = self.main_window.units + self.addAttribute = lambda dev, attr: self.main_window.addAttribute(dev, attr) + + self.list_layout = QtGui.QVBoxLayout() + self.ContentFrame.setLayout(self.list_layout) + self.liberas = {} + + def addLibera(self, dev): + print "[ INFO]: grouping: add Libera %s" % dev + libera = GroupingWidget(self.main_window, dev) + self.connect(self, QtCore.SIGNAL("update"), libera.update) + self.liberas[dev] = libera + self.list_layout.addWidget(libera) + + def addGlobal(self, orbit, magnet): + print "[ INFO]: grouping: global: orbit %s; magnet %s" % (orbit, magnet) + w = GlobalWidget(self.main_window, orbit, magnet) + self.connect(self, QtCore.SIGNAL("update"), w.update) + self.list_layout.addWidget(w) + + def update(self, cache): + self.emit(QtCore.SIGNAL("update"), cache) + + def showEvent(self, evt): + self.emit(QtCore.SIGNAL("show")) + evt.accept() + + def hideEvent(self, evt): + self.emit(QtCore.SIGNAL("hide")) + evt.accept() + + +class GroupingWidget(QtGui.QWidget): + def __init__(self, main_window, device, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/groupingwidget.ui" % base_dir, self) + + self.main_window = main_window + self.units = self.main_window.units + self.addAttribute = lambda dev, attr: self.main_window.addAttribute(dev, attr) + + self.device = device + + self.IDLabel.setText(device) + self.SFP3StatusLabel.setModel(device, "GdxSFP3Status") + self.SFP4StatusLabel.setModel(device, "GdxSFP4Status") + + def update(self, cache): + self.emit(QtCore.SIGNAL("update"), cache) + + +class GlobalWidget(QtGui.QWidget): + def __init__(self, main_window, orbit, magnet, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/globalwidget.ui" % base_dir, self) + + self.main_window = main_window + self.units = self.main_window.units + self.addAttribute = lambda dev, attr: self.main_window.addAttribute(dev, attr) + + self.orbit = orbit + self.magnet = magnet + + self.GlobalOrbitIDLabel.setText(orbit) + self.GlobalOrbitStateLabel.setModel(orbit, "GdxOrbitStatus") + self.GlobalOrbitStateLabel.setColorMap(cm.GREEN_BOOL) + + self.GlobalMagnetIDLabel.setText(magnet) + self.GlobalMagnetStateLabel.setModel(orbit, "GdxMagnetStatus") + self.GlobalMagnetStateLabel.setColorMap(cm.GREEN_BOOL) + + def update(self, cache): + self.emit(QtCore.SIGNAL("update"), cache) diff --git a/labels.py b/labels.py new file mode 100644 index 0000000..6855df7 --- /dev/null +++ b/labels.py @@ -0,0 +1,218 @@ +from PyQt4 import QtGui, QtCore + +from tango import DevState + + +class cm: + FOFB_STATE = { + 1: "#000000", + 2: "#7CFC00", + 4: "#FFFF00" + } + + RED_BOOL = { + 0: "#000000", + 1: "#FF0000" + } + + GREEN_BOOL = { + 0: "#000000", + 1: "#7CFC00" + } + + SER_OFF = { + 0: "#FF0000", + 1: "#000000", + 2: "#000000" + } + + SER_MANUAL = { + 0: "#000000", + 1: "#000000", + 2: "#FF8C00" + } + + SER_FOFB = { + 0: "#000000", + 1: "#7CFC00", + 2: "#000000" + } + + +class TangoLabel(QtGui.QLabel): + def __init__(self, parent=None): + QtGui.QLabel.__init__(self, parent) + + self.device = None + self.attr = None + + def setModel(self, device, attr): + self.window().addAttribute(device, attr) + self.device = device + self.attr = attr + # force the color of the tooltip text + if self.toolTip(): + self.setToolTip("%s

%s/%s
" % (self.toolTip(), device, attr)) + else: + self.setToolTip("%s/%s" % (device, attr)) + self.connect(self.window(), QtCore.SIGNAL("update"), self.update) + + def update(self, cache): + value = cache[self.device][self.attr] + if value is None: + value = float("-inf") + try: + fmt = self.window().units[self.device][self.attr]["format"] + except (KeyError, TypeError): + fmt = "%d" + try: + unit = self.window().units[self.device][self.attr]["unit"] + except (KeyError, TypeError): + unit = None + value = fmt % value + if unit is not None: + value = "%s %s" % (value, unit) + self.setText(value) + + +class StateLabel(TangoLabel): + ColorMap = { + DevState.ON: "#7CFC00", + DevState.OFF: "#FFFFFF", + DevState.CLOSE: "#FFFFFF", + DevState.OPEN: "#7CFC00", + DevState.INSERT: "#FFFFFF", + DevState.EXTRACT: "#7CFC00", + DevState.MOVING: "#80A0FF", + DevState.STANDBY: "#FFFF00", + DevState.FAULT: "#FF0000", + DevState.INIT: "#CCCC7A", + DevState.RUNNING: "#80A0FF", + DevState.ALARM: "#FF8C00", + DevState.DISABLE: "#FF00FF", + DevState.UNKNOWN: "#808080" + } + + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def setModel(self, device, attr="State"): + TangoLabel.setModel(self, device, attr) + + def update(self, cache): + state = cache[self.device][self.attr] + if state is None: + state = DevState.UNKNOWN + self.setStyleSheet("color: %s" % self.ColorMap[state]) + + +class BeamLabel(TangoLabel): + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def update(self, cache): + value = cache[self.device][self.attr] + if value > 0: + self.setStyleSheet("background-color: yellow") + else: + self.setStyleSheet("background-color: gray") + + +class OperationLabel(QtGui.QLabel): + ColorMap = { + "on": "#7CFC00", + "off": "#000000", + "manual": "#FFA500", + "skipped": "#ADD8E6", + "fault": "#FF0000" + } + + def __init__(self, parent=None): + QtGui.QLabel.__init__(self, parent) + + def setState(self, state): + self.setStyleSheet("color: %s" % self.ColorMap[state]) + + +class BstLabel(TangoLabel): + ColorMap = { + "Status: OPEN": "#7CFC00", + "Status: UNKNOWN": "#80A0FF", + "STATUS: UNKNOWN": "#80A0FF", + "Status: CLOSED": "#FFFFFF", + None: "#808080" + } + + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def update(self, cache): + state = cache[self.device][self.attr] + self.setStyleSheet("color: %s" % self.ColorMap[state]) + + +class BooleanLabel(TangoLabel): + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + self.true_color = "#7CFC00" + self.false_color = "#000000" + + def setColors(self, true_color, false_color): + self.true_color = true_color + self.false_color = false_color + + def update(self, cache): + if cache[self.device][self.attr]: + self.setStyleSheet("color: %s" % self.true_color) + else: + self.setStyleSheet("color: %s" % self.false_color) + + +class InterlockLabel(TangoLabel): + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def update(self, cache): + if cache[self.device][self.attr]: + self.setStyleSheet("background:red; color:white") + else: + self.setStyleSheet("") + + +class ArrayElementLabel(TangoLabel): + ColorMap = { + 0: "#000000", + 1: "#7CFC00" + } + + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def setModel(self, device, attr, i): + self.i = i + TangoLabel.setModel(self, device, attr) + + def setColorMap(self, cm): + self.ColorMap = cm + + def update(self, cache): + value = cache[self.device][self.attr][self.i] + self.setStyleSheet("color: %s" % self.ColorMap[value]) + + +class EnumStateLabel(TangoLabel): + ColorMap = { + 0: "#000000", + 1: "#7CFC00" + } + + def __init__(self, parent=None): + TangoLabel.__init__(self, parent) + + def setColorMap(self, cm): + self.ColorMap = cm + + def update(self, cache): + value = cache[self.device][self.attr] + self.setStyleSheet("color: %s" % self.ColorMap[value]) diff --git a/magic_numbers.py b/magic_numbers.py new file mode 100644 index 0000000..79fc4f2 --- /dev/null +++ b/magic_numbers.py @@ -0,0 +1,9 @@ +MASTER_TIMER = 1000 +IREG_EXECUTE = 238 + + +class EventID: + FOFB_OFF = 100 + FOFB_ON = 101 + FOFB_STANDBY = 102 + UPDATE_DOUBLE_BUFFERS = 103 diff --git a/mainwindow.py b/mainwindow.py new file mode 100644 index 0000000..9ffd773 --- /dev/null +++ b/mainwindow.py @@ -0,0 +1,259 @@ +import os +import json +from time import time, sleep + +from PyQt4 import QtGui, QtCore, uic + +import tango + +import magic_numbers +from labels import cm +from threads import ThreadWrapper + +from grouping import GroupingWindow +from seroutput import SERWindow + +base_dir = os.path.dirname(os.path.realpath(__file__)) + + +class MainWindow(QtGui.QWidget): + def __init__(self, offline=False, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/main.ui" % base_dir, self) + + self.offline = offline + self.devices = {} + self.units = None + self.cache = {} + self.virtual = { + "delay": 0, + "avg_delay": 0, + "_delay_sum": 0, + "_delay_n": 0 + } + + self.grouping = GroupingWindow(self) + self.ser_output = SERWindow(self) + + self.connect(self.FOFBONButton, QtCore.SIGNAL("clicked()"), + lambda: self.send_tim_event("EVG", magic_numbers.EventID.FOFB_ON) + ) + self.connect(self.FOFBOFFButton, QtCore.SIGNAL("clicked()"), + lambda: self.send_tim_event("EVG", magic_numbers.EventID.FOFB_OFF) + ) + self.connect(self.FOFBStandbyButton, QtCore.SIGNAL("clicked()"), + lambda: self.send_tim_event("EVG", magic_numbers.EventID.FOFB_STANDBY) + ) + self.connect(self.SwitchBuffersButton, QtCore.SIGNAL("clicked()"), + lambda: self.send_tim_event("EVG", magic_numbers.EventID.UPDATE_DOUBLE_BUFFERS) + ) + self.connect(self.SendEventButton, QtCore.SIGNAL("clicked()"), + lambda: self.send_tim_event("EVG", self.EventSpinBox.value()) + ) + + self.connect(self.GroupingButton, QtCore.SIGNAL("clicked()"), self.grouping.show) + self.connect(self.SERButton, QtCore.SIGNAL("clicked()"), self.ser_output.show) + + self.connect(self.XInterlockResetButton, QtCore.SIGNAL("clicked()"), + lambda: self.execute_ireg_command_on_axis("x", "GdxFOFBIlkReset") + ) + self.connect(self.XReconnectButton, QtCore.SIGNAL("clicked()"), + lambda: self.execute_ireg_command_on_axis("x", "GdxFOFBReconnect") + ) + self.connect(self.XResetPIButton, QtCore.SIGNAL("clicked()"), + lambda: self.execute_ireg_command_on_axis("x", "GdxFOFBPIReset") + ) + self.connect(self.XSaturationResetButton, QtCore.SIGNAL("clicked()"), + lambda: self.execute_ireg_command_on_axis("x", "GdxFOFBSaturationReset") + ) + + # self.connect(self.YInterlockResetButton, QtCore.SIGNAL("clicked()"), + # lambda: self.execute_ireg_command_on_axis("y", "GdxFOFBIlkReset") + # ) + # self.connect(self.YReconnectButton, QtCore.SIGNAL("clicked()"), + # lambda: self.execute_ireg_command_on_axis("y", "GdxFOFBReconnect") + # ) + # self.connect(self.YResetPIButton, QtCore.SIGNAL("clicked()"), + # lambda: self.execute_ireg_command_on_axis("y", "GdxFOFBPIReset") + # ) + # self.connect(self.YSaturationResetButton, QtCore.SIGNAL("clicked()"), + # lambda: self.execute_ireg_command_on_axis("y", "GdxFOFBSaturationReset") + # ) + + self.update_timer = QtCore.QTimer() + self.connect(self.update_timer, QtCore.SIGNAL("timeout()"), self.update) + + self.connect(self, QtCore.SIGNAL("update"), self.grouping.update) + self.connect(self, QtCore.SIGNAL("update"), self.ser_output.update) + + self.connect(self, QtCore.SIGNAL("loadingdone"), self.loading_done) + self.connect(self, QtCore.SIGNAL("update"), self.update_virtual_in_gui) + + def setModel(self): + self.EVGStateLabel.setModel("EVG") + + self.XStateLabel.setModel(self.settings["x"]) + self.XInterlockLabel.setModel(self.settings["x"], "GdxFOFBIlk") + self.XIDLabel.setText(self.settings["x"]) + self.XFOFBStateLabel.setModel(self.settings["x"], "GdxFOFBState") + self.XFOFBStateLabel.setColorMap(cm.FOFB_STATE) + # self.XBuffersLabel.setModel() + + self.XS1Label.setModel(self.settings["x"], "GdxFOFBSaturation", 0) + self.XS1Label.setColorMap(cm.RED_BOOL) + self.XS2Label.setModel(self.settings["x"], "GdxFOFBSaturation", 1) + self.XS2Label.setColorMap(cm.RED_BOOL) + self.XS3Label.setModel(self.settings["x"], "GdxFOFBSaturation", 2) + self.XS3Label.setColorMap(cm.RED_BOOL) + self.XS4Label.setModel(self.settings["x"], "GdxFOFBSaturation", 3) + self.XS4Label.setColorMap(cm.RED_BOOL) + + # self.YStateLabel.setModel(self.settings["y"]) + # self.YInterlockLabel.setModel(self.settings["y"], "GdxFOFBIlk") + # self.YIDLabel.setText(self.settings["y"]) + # self.YFOFBStateLabel.setModel(self.settings["y"], "GdxFOFBState") + # self.YFOFBStateLabel.setColorMap(cm.FOFB_STATE) + # # self.XBuffersLabel.setModel() + + # self.YS1Label.setModel(self.settings["y"], "GdxFOFBSaturation", 0) + # self.YS1Label.setColorMap(cm.RED_BOOL) + # self.YS2Label.setModel(self.settings["y"], "GdxFOFBSaturation", 1) + # self.YS2Label.setColorMap(cm.RED_BOOL) + # self.YS3Label.setModel(self.settings["y"], "GdxFOFBSaturation", 2) + # self.YS3Label.setColorMap(cm.RED_BOOL) + # self.YS4Label.setModel(self.settings["y"], "GdxFOFBSaturation", 3) + # self.YS4Label.setColorMap(cm.RED_BOOL) + + def start(self, fn, config=None): + ThreadWrapper(self.load, args=(fn, config)).start() + + def loading_done(self): + # show all startup windows + self.show() + # fill grouping and magnets + for dev in self.devices.keys(): + if dev.upper() != "EVG": + self.grouping.addLibera(dev) + self.ser_output.addLibera(dev) + self.grouping.addGlobal(self.settings["global_orbit"], self.settings["global_magnet"]) + # start updates + self.update_timer.start(magic_numbers.MASTER_TIMER) + + def closeEvent(self, evt): + self.grouping.close() + self.ser_output.close() + evt.accept() + + def setVersion(self, version): + self.VersionLabel.setText("v.%s" % version) + + def load(self, fn, cfg=None): + if fn: + with open(fn) as f: + conf = json.load(f) + else: + conf = json.loads(cfg) + for label, dev in conf["devices"].iteritems(): + self.addDevice(label, dev) + self.units = conf["units"] + self.settings = conf["settings"] + self.setModel() + self.applySettings() + self.emit(QtCore.SIGNAL("loadingdone")) + + def addDevice(self, label, dev): + self.emit(QtCore.SIGNAL("loadstatus"), "Loading %s = %s" % (label, dev)) + if not self.offline: + self.devices[label] = tango.DeviceProxy(dev) + if label not in self.cache: + self.cache[label] = {} + + def applySettings(self): + pass + + def addAttribute(self, dev, attr): + self.emit(QtCore.SIGNAL("loadstatus"), "Add attribute %s of device %s" % (attr, dev)) + if dev not in self.cache: + self.cache[dev] = {} + self.cache[dev][attr] = None + + def update(self): + print "[ INFO]: update" + try: + start = time() + threads = {} + for dev, attrs in self.cache.iteritems(): + threads[dev] = {} + for attr in attrs.keys(): + threads[dev][attr] = ThreadWrapper(self.read_attribute, args=(dev, attr)) + threads[dev][attr].start() + + for dev, attrs in self.cache.iteritems(): + for attr in attrs.keys(): + threads[dev][attr].join() + self.cache[dev][attr] = threads[dev][attr].result + + self.update_virtual(time() - start) + except Exception as e: + print "[ERROR]: update failed: %s" % str(e) + self.emit(QtCore.SIGNAL("update"), self.cache) + + def read_attribute(self, dev, attr): + try: + value = self.devices[dev].read_attribute(attr).value + if type(value) in (int, float): + try: + conv = self.units[dev][attr]["conversion"] + except (KeyError, TypeError): + conv = 1 + value = value * conv + + # special case for machine state attribute + if dev == "statemachine" and attr == "MachineState": + if not value: + value = "UNKNOWN !!!" + + return value + except Exception as e: + print "[ERROR]: could not read attribute %s of device %s: %s" % (attr, dev, str(e)) + return None + + def update_virtual(self, fetch_time): + self.virtual["delay"] = fetch_time * 1000.0 # convert to ms + self.virtual["_delay_sum"] += self.virtual["delay"] + self.virtual["_delay_n"] += 1 + self.virtual["avg_delay"] = self.virtual["_delay_sum"] / self.virtual["_delay_n"] + + def update_virtual_in_gui(self): + # This whole thing is a big, big technical debt + self.DelayLabel.setText("%.02f ms" % self.virtual["delay"]) + if self.virtual["delay"] > magic_numbers.MASTER_TIMER: + style = "background:red; color:white" + elif self.virtual["delay"] > magic_numbers.MASTER_TIMER / 2.0: + style = "background:orange; color:white" + else: + style = "" + self.DelayLabel.setStyleSheet(style) + + self.AvgDelayLabel.setText("avg: %.02f ms" % self.virtual["avg_delay"]) + if self.virtual["avg_delay"] > magic_numbers.MASTER_TIMER: + style = "background:red; color:white" + elif self.virtual["avg_delay"] > magic_numbers.MASTER_TIMER / 2.0: + style = "background:orange; color:white" + else: + style = "" + self.AvgDelayLabel.setStyleSheet(style) + + def execute_ireg_command(self, dev, cmd): + try: + print "[ INFO]: ireg_execute %s/%s" % (dev, cmd) + self.devices[dev].write_attribute(cmd, magic_numbers.IREG_EXECUTE) + except Exception as e: + print str(e) + + def execute_ireg_command_on_axis(self, axis, cmd): + self.execute_ireg_command(self.settings[axis], cmd) + + def send_tim_event(self, dev, evt): + print "[ INFO]: sw event %s/%d" % (dev, evt) + self.devices[dev].SendSoftwareEvent(evt) diff --git a/seroutput.py b/seroutput.py new file mode 100644 index 0000000..ff21398 --- /dev/null +++ b/seroutput.py @@ -0,0 +1,100 @@ +import os + +from PyQt4 import QtGui, QtCore, uic + +from labels import cm + +base_dir = os.path.dirname(os.path.realpath(__file__)) + + +class SERWindow(QtGui.QWidget): + def __init__(self, main_window, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/ser.ui" % base_dir, self) + + self.main_window = main_window + self.units = self.main_window.units + self.addAttribute = lambda dev, attr: self.main_window.addAttribute(dev, attr) + + self.list_layout = QtGui.QVBoxLayout() + self.ContentFrame.setLayout(self.list_layout) + self.liberas = {} + + def addLibera(self, dev): + print "[ INFO]: SER: add Libera %s" % dev + libera = SERWidget(self.main_window, dev) + self.connect(self, QtCore.SIGNAL("update"), libera.update) + self.liberas[dev] = libera + self.list_layout.addWidget(libera) + + def update(self, cache): + self.emit(QtCore.SIGNAL("update"), cache) + + def showEvent(self, evt): + self.emit(QtCore.SIGNAL("show")) + evt.accept() + + def hideEvent(self, evt): + self.emit(QtCore.SIGNAL("hide")) + evt.accept() + + +class SERWidget(QtGui.QWidget): + def __init__(self, main_window, device, parent=None): + QtGui.QWidget.__init__(self, parent) + uic.loadUi("%s/ui/serwidget.ui" % base_dir, self) + + self.main_window = main_window + self.units = self.main_window.units + self.addAttribute = lambda dev, attr: self.main_window.addAttribute(dev, attr) + + self.device = device + + self.IDLabel.setText(device) + + self.M1IDLabel.setModel(device, "GdxSERM1Id") + self.M1OffLabel.setModel(device, "GdxSERM1Output") + self.M1OffLabel.setColorMap(cm.SER_OFF) + self.M1ManualLabel.setModel(device, "GdxSERM1Output") + self.M1ManualLabel.setColorMap(cm.SER_MANUAL) + self.M1FOFBLabel.setModel(device, "GdxSERM1Output") + self.M1FOFBLabel.setColorMap(cm.SER_FOFB) + self.M1FOFBValueLabel.setModel(device, "GdxSERM1FOFB") + + self.M2IDLabel.setModel(device, "GdxSERM2Id") + self.M2OffLabel.setModel(device, "GdxSERM2Output") + self.M2OffLabel.setColorMap(cm.SER_OFF) + self.M2ManualLabel.setModel(device, "GdxSERM2Output") + self.M2ManualLabel.setColorMap(cm.SER_MANUAL) + self.M2FOFBLabel.setModel(device, "GdxSERM2Output") + self.M2FOFBLabel.setColorMap(cm.SER_FOFB) + self.M2FOFBValueLabel.setModel(device, "GdxSERM2FOFB") + + self.M3IDLabel.setModel(device, "GdxSERM3Id") + self.M3OffLabel.setModel(device, "GdxSERM3Output") + self.M3OffLabel.setColorMap(cm.SER_OFF) + self.M3ManualLabel.setModel(device, "GdxSERM3Output") + self.M3ManualLabel.setColorMap(cm.SER_MANUAL) + self.M3FOFBLabel.setModel(device, "GdxSERM3Output") + self.M3FOFBLabel.setColorMap(cm.SER_FOFB) + self.M3FOFBValueLabel.setModel(device, "GdxSERM3FOFB") + + self.M4IDLabel.setModel(device, "GdxSERM4Id") + self.M4OffLabel.setModel(device, "GdxSERM4Output") + self.M4OffLabel.setColorMap(cm.SER_OFF) + self.M4ManualLabel.setModel(device, "GdxSERM4Output") + self.M4ManualLabel.setColorMap(cm.SER_MANUAL) + self.M4FOFBLabel.setModel(device, "GdxSERM4Output") + self.M4FOFBLabel.setColorMap(cm.SER_FOFB) + self.M4FOFBValueLabel.setModel(device, "GdxSERM4FOFB") + + def update(self, cache): + self.emit(QtCore.SIGNAL("update"), cache) + + # def showEvent(self, evt): + # self.emit(QtCore.SIGNAL("show")) + # evt.accept() + + # def hideEvent(self, evt): + # self.emit(QtCore.SIGNAL("hide")) + # evt.accept() diff --git a/threads.py b/threads.py new file mode 100644 index 0000000..3a0f4f0 --- /dev/null +++ b/threads.py @@ -0,0 +1,19 @@ +from threading import Thread + + +class ThreadWrapper(Thread): + """ A thread you can get the result value from """ + + def __init__(self, target, args=(), kwargs={}): + Thread.__init__(self) + self.target = target + self.args = args + self.kwargs = kwargs + self.result = None + self.error = None + + def run(self): + try: + self.result = self.target(*self.args, **self.kwargs) + except Exception as e: + self.error = e diff --git a/ui/globalwidget.ui b/ui/globalwidget.ui new file mode 100644 index 0000000..0aca338 --- /dev/null +++ b/ui/globalwidget.ui @@ -0,0 +1,254 @@ + + + Form + + + + 0 + 0 + 349 + 52 + + + + Form + + + + 1 + + + 2 + + + 1 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 10 + 75 + true + + + + Global magnet + + + Qt::AlignCenter + + + + + + + + 45 + 16777215 + + + + Reset + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 10 + 75 + true + + + + Global orbit + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 45 + 16777215 + + + + Reset + + + + + + + + EnumStateLabel + QLabel +
labels
+
+
+ + +
diff --git a/ui/grouping.ui b/ui/grouping.ui new file mode 100644 index 0000000..9c9cc3f --- /dev/null +++ b/ui/grouping.ui @@ -0,0 +1,94 @@ + + + Form + + + + 0 + 0 + 395 + 699 + + + + FOFB: Libera Grouping + + + + + + Grouping timeout + + + + + + + + 16 + 75 + true + + + + color:orange + + + + + + Qt::AlignCenter + + + + + + + + 90 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + diff --git a/ui/groupingwidget.ui b/ui/groupingwidget.ui new file mode 100644 index 0000000..1de13bd --- /dev/null +++ b/ui/groupingwidget.ui @@ -0,0 +1,498 @@ + + + Form + + + + 0 + 0 + 351 + 45 + + + + Form + + + + 2 + + + 1 + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + 1 + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + 0 + + + Qt::AlignCenter + + + + + + + + 50 + 20 + + + + Clear + + + + + + + + + 1 + + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + 0 + + + Qt::AlignCenter + + + + + + + + 50 + 20 + + + + Clear + + + + + + + + + + OperationLabel + QLabel +
labels
+
+ + TangoLabel + QLabel +
labels
+
+
+ + +
diff --git a/ui/main.ui b/ui/main.ui new file mode 100644 index 0000000..f27b443 --- /dev/null +++ b/ui/main.ui @@ -0,0 +1,1467 @@ + + + Form + + + + 0 + 0 + 742 + 383 + + + + SOLARIS FOFB control + + + + + + 2 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 3 + + + + + + 10 + 75 + true + + + + Event control + + + Qt::AlignCenter + + + + + + + FOFB ON + + + + + + + FOFB OFF + + + + + + + FOFB STANDBY + + + + + + + SWITCH BUFFERS + + + + + + + 0 + + + + + 256 + + + + + + + + 40 + 16777215 + + + + Send + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 3 + + + + + + 11 + 75 + true + + + + EVG + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + + Qt::LeftToRight + + + Libera Grouping + + + + + + + + + + + + Qt::LeftToRight + + + Local magnet output + + + + ../../../../../../../../../../ + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + + true + + + + v.0.0.0 + + + Qt::AlignCenter + + + + + + + 2 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 8 + 75 + true + + + + 0.00 ms + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 8 + 75 + true + + + + avg: 0.00 ms + + + Qt::AlignCenter + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 3 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 20 + 75 + true + + + + X + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 2 + + + 3 + + + + + 0 + + + + + + 7 + + + + Buffers + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 7 + + + + FOFB + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + Reconnect + + + + + + + Reset + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + + + + QFrame::Box + + + INTERLOCK + + + Qt::AlignCenter + + + + + + + + 10 + 75 + true + + + + Status + + + Qt::AlignCenter + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 3 + + + 2 + + + + + Reset PI + + + + + + + Ki + + + + + + + + + + + + Kp + + + + + + + + + + + + Response matrix + + + + + + + + + + + + Golden orbit + + + + + + + + + + + + V + + + + + + + + + + + + + 10 + 75 + true + + + + Parameters + + + Qt::AlignCenter + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 3 + + + 2 + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + PI controller + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Multiplication V.PI[SU.dP] + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Global orbit data P + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Multiplication SU.dP + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Reset + + + + + + + + 10 + 75 + true + + + + Saturation + + + Qt::AlignCenter + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 3 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 20 + 75 + true + + + + Y + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 2 + + + 3 + + + + + 0 + + + + + + 7 + + + + Buffers + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 7 + + + + FOFB + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + + 24 + 50 + false + + + + + + + Qt::AlignCenter + + + + + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + Reconnect + + + + + + + Reset + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + + + + QFrame::Box + + + INTERLOCK + + + Qt::AlignCenter + + + + + + + + 10 + 75 + true + + + + Status + + + Qt::AlignCenter + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 3 + + + 2 + + + + + Reset PI + + + + + + + Ki + + + + + + + + + + + + + 10 + 75 + true + + + + Parameters + + + Qt::AlignCenter + + + + + + + Kp + + + + + + + + + + + + Response matrix + + + + + + + + + + + + Golden orbit + + + + + + + + + + + + V + + + + + + + + + + + + + + + QFrame::Box + + + QFrame::Plain + + + + 3 + + + 2 + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + PI controller + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Multiplication V.PI[SU.dP] + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Global orbit data P + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 41 + 25 + + + + + 24 + 50 + false + + + + Multiplication SU.dP + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Reset + + + + + + + + 10 + 75 + true + + + + Saturation + + + Qt::AlignCenter + + + + + + + + + + + + + + StateLabel + QLabel +
labels
+
+ + InterlockLabel + QLabel +
labels
+
+ + ArrayElementLabel + QLabel +
labels
+
+ + EnumStateLabel + QLabel +
labels
+
+
+ + +
diff --git a/ui/ser.ui b/ui/ser.ui new file mode 100644 index 0000000..53d8241 --- /dev/null +++ b/ui/ser.ui @@ -0,0 +1,31 @@ + + + Form + + + + 0 + 0 + 622 + 691 + + + + FOFB: Local magnet output + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + diff --git a/ui/serwidget.ui b/ui/serwidget.ui new file mode 100644 index 0000000..f6f8243 --- /dev/null +++ b/ui/serwidget.ui @@ -0,0 +1,892 @@ + + + Form + + + + 0 + 0 + 573 + 49 + + + + + 16777215 + 79 + + + + Form + + + + 2 + + + 1 + + + + + + 13 + 75 + true + + + + 00 + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 60 + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + 1 + + + + + + 75 + true + + + + M1 + + + Qt::AlignCenter + + + + + + + Magnet ID + + + 00 + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: OFF + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: MANUAL + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: FOFB + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + + 16777215 + 50 + + + + FOFB value + + + 0 + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + Manual value + + + -32768 + + + 32767 + + + -32768 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 60 + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + 1 + + + + + + 75 + true + + + + M2 + + + Qt::AlignCenter + + + + + + + Magnet ID + + + 00 + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: OFF + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: MANUAL + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: FOFB + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + FOFB value + + + 0 + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + Manual value + + + -32768 + + + 32767 + + + -32768 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 60 + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + 1 + + + + + + 75 + true + + + + M3 + + + Qt::AlignCenter + + + + + + + Magnet ID + + + 00 + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: OFF + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: MANUAL + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: FOFB + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + FOFB value + + + 0 + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + Manual value + + + -32768 + + + 32767 + + + -32768 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 60 + + + + QFrame::VLine + + + QFrame::Plain + + + + + + + 1 + + + + + + 75 + true + + + + M4 + + + Qt::AlignCenter + + + + + + + Magnet ID + + + 00 + + + Qt::AlignCenter + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: OFF + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: MANUAL + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 25 + 15 + + + + + 24 + 50 + false + + + + Output: FOFB + + + + + + Qt::AlignCenter + + + + + + + + + 1 + + + + + FOFB value + + + 0 + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + Manual value + + + -32768 + + + 32767 + + + -32768 + + + + + + + + + + EnumStateLabel + QLabel +
labels
+
+ + TangoLabel + QLabel +
labels
+
+
+ + +