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
+
+
+
+
+
+
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
+
+
+
+ TangoLabel
+ QLabel
+
+
+
+
+
+
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
+
+
+
+ InterlockLabel
+ QLabel
+
+
+
+ ArrayElementLabel
+ QLabel
+
+
+
+ EnumStateLabel
+ QLabel
+
+
+
+
+
+
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
+
+
+
+ TangoLabel
+ QLabel
+
+
+
+
+
+