CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / test (3.11) (pull_request) Failing after 44s
CI / test (3.12) (pull_request) Failing after 41s
CI / test (3.13) (pull_request) Failing after 42s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Failing after 47s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Failing after 49s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Failing after 1m0s
CI / test-with-coverage (pull_request) Failing after 1m1s
CI / coverage-analysis (pull_request) Skipped
CI / lint (pull_request) Failing after 2m2s
221 lines
9.0 KiB
Python
221 lines
9.0 KiB
Python
import os
|
|
import sys
|
|
|
|
from aarecommon.config.beamline import cfg_get, mx_beamline
|
|
from aarecommon.config.logger import setup_logger
|
|
from aarecommon.models.beamline import MXBeamline
|
|
from PySide6 import QtGui
|
|
from PySide6.QtCore import QCommandLineOption, QCommandLineParser
|
|
from PySide6.QtWidgets import QApplication, QMessageBox
|
|
|
|
from aare.gui.auth import auth
|
|
from aare.gui.constants import LOGGER_NAME
|
|
from aare.gui.main_window import MainWindow
|
|
from aare.gui.widgets.splash_screen import LoadingSplashScreen
|
|
|
|
logger = setup_logger(LOGGER_NAME)
|
|
|
|
|
|
def main():
|
|
"""Wrapped gui as main function to make tests easier"""
|
|
try:
|
|
basedir = os.path.dirname(__file__)
|
|
icon_path = os.path.join(basedir, "graphics/aaregui_logo.svg")
|
|
# SVG looks strange on consoles...only show one line
|
|
banner_path = os.path.join(basedir, "graphics/aare_banner.png")
|
|
except Exception:
|
|
logger.exception("Failed to load resources for splash screen")
|
|
sys.exit(1)
|
|
try:
|
|
# define application
|
|
app = QApplication(sys.argv)
|
|
splash_pix = QtGui.QPixmap(banner_path)
|
|
splash = LoadingSplashScreen(splash_pix)
|
|
app.setWindowIcon(QtGui.QIcon(icon_path))
|
|
app.setApplicationName("AareGUI")
|
|
app.setApplicationVersion("0.3.1")
|
|
app.setOrganizationName("PSI")
|
|
app.setOrganizationDomain("psi.ch")
|
|
|
|
splash.show()
|
|
splash.set_progress(10, "Initializing Application...")
|
|
|
|
# Create the parser
|
|
parser = QCommandLineParser()
|
|
parser.setApplicationDescription("PSI AareGUI")
|
|
parser.addHelpOption() # Adds --help option
|
|
parser.addVersionOption() # Adds --version option
|
|
# TODO if zmq and pred stream come from same source, do not need images from both streams, can combine
|
|
match mx_beamline():
|
|
case MXBeamline.X06DA:
|
|
default_url = cfg_get("gui.daq.daq_url", "https://mx-x06da-queue-01.psi.ch")
|
|
default_cert_path = cfg_get("gui.daq.cert_path", "/sls/x06da/misc/.cert/6d.crt")
|
|
default_zmq_addr = cfg_get(
|
|
"gui.cameras.sample_camera_zmq_url", "tcp://x06da-pserv-01:9089"
|
|
)
|
|
default_pred_zmq_addr = cfg_get(
|
|
"gui.cameras.prediction_zmq_url", "tcp://mx-ml:9091"
|
|
)
|
|
default_beamline_cam_addr = cfg_get(
|
|
"gui.cameras.beamline_camera_url", "x06da-axis-1.psi.ch"
|
|
)
|
|
default_gonio_cam_addr = cfg_get(
|
|
"gui.cameras.gonio_camera_url", "axis-accc8ed2972e.psi.ch"
|
|
)
|
|
default_gonio_camera_id = int(cfg_get("gui.cameras.gonio_camera_id", 3))
|
|
case MXBeamline.X10SA:
|
|
default_url = cfg_get("gui.daq.daq_url", "https://mx-x10sa-queue-01.psi.ch")
|
|
default_cert_path = cfg_get("gui.daq.cert_path", "/sls/x10sa/misc/.cert/10s.crt")
|
|
default_zmq_addr = cfg_get(
|
|
"gui.cameras.sample_camera_zmq_url", "tcp://x10sa-spark-01:9091"
|
|
)
|
|
default_pred_zmq_addr = cfg_get(
|
|
"gui.cameras.prediction_zmq_url", "tcp://x10sa-spark-01:9091"
|
|
)
|
|
default_beamline_cam_addr = cfg_get(
|
|
"gui.cameras.beamline_camera_url", "axis-accc8eb02488.psi.ch"
|
|
)
|
|
default_gonio_cam_addr = cfg_get(
|
|
"gui.cameras.gonio_camera_url", "axis-accc8ea5e463.psi.ch"
|
|
)
|
|
default_gonio_camera_id = int(cfg_get("gui.cameras.gonio_camera_id", 1))
|
|
case MXBeamline.X06SA:
|
|
default_url = cfg_get("gui.daq.daq_url", "https://mx-x06sa-queue-01.psi.ch")
|
|
default_cert_path = cfg_get("gui.daq.cert_path", "/sls/x06sa/misc/.cert/6s.crt")
|
|
default_zmq_addr = cfg_get("gui.cameras.sample_camera_zmq_url", "")
|
|
default_pred_zmq_addr = cfg_get("gui.cameras.prediction_zmq_url", "")
|
|
default_beamline_cam_addr = cfg_get("gui.cameras.beamline_camera_url", "")
|
|
default_gonio_cam_addr = cfg_get("gui.cameras.gonio_camera_url", "")
|
|
default_gonio_camera_id = int(cfg_get("gui.cameras.gonio_camera_id", 1))
|
|
case _:
|
|
default_url = cfg_get("gui.daq.daq_url", "")
|
|
default_cert_path = cfg_get("gui.daq.cert_path", "")
|
|
default_zmq_addr = cfg_get("gui.cameras.sample_camera_zmq_url", "")
|
|
default_pred_zmq_addr = cfg_get("gui.cameras.prediction_zmq_url", "")
|
|
default_beamline_cam_addr = cfg_get("gui.cameras.beamline_camera_url", "")
|
|
default_gonio_cam_addr = cfg_get("gui.cameras.gonio_camera_url", "")
|
|
default_gonio_camera_id = int(cfg_get("gui.cameras.gonio_camera_id", 1))
|
|
|
|
# Add custom options as needed
|
|
urlOption = QCommandLineOption(["u", "aaredaq-url"], "Base AareDAQ URL", "url", default_url)
|
|
parser.addOption(urlOption)
|
|
|
|
certPath = QCommandLineOption(
|
|
["c", "aaredaq-cert-path"],
|
|
"Server Certificate Path (for self-signed certificates)",
|
|
"cert",
|
|
default_cert_path,
|
|
)
|
|
parser.addOption(certPath)
|
|
|
|
defaultImage = QCommandLineOption(
|
|
["i", "image"], "Default image to display in absence of the ZMQ stream", "image"
|
|
)
|
|
parser.addOption(defaultImage)
|
|
|
|
cameraZeroMQ = QCommandLineOption(
|
|
["s", "sample-camera-zmq"],
|
|
"Sample camera ZeroMQ URL",
|
|
"sample-camera-zmq",
|
|
default_zmq_addr,
|
|
)
|
|
parser.addOption(cameraZeroMQ)
|
|
|
|
predZmqOption = QCommandLineOption(
|
|
["p", "pred-zmq"],
|
|
"Prediction ZeroMQ URL (PUB) to subscribe to (e.g. tcp://mx-ml:9091)",
|
|
"pred-zmq",
|
|
default_pred_zmq_addr,
|
|
)
|
|
parser.addOption(predZmqOption)
|
|
|
|
splash.set_progress(30, "Parsing Arguments...")
|
|
parser.process(app)
|
|
|
|
base_url = parser.value(urlOption)
|
|
if base_url == "" or base_url.lower() == "none":
|
|
base_url = None
|
|
|
|
cert_path = parser.value(certPath)
|
|
if cert_path == "" or cert_path.lower() == "none":
|
|
cert_path = None
|
|
|
|
zmq_addr = parser.value(cameraZeroMQ)
|
|
if zmq_addr == "":
|
|
zmq_addr = None
|
|
|
|
pred_zmq_addr = parser.value(predZmqOption)
|
|
if pred_zmq_addr == "":
|
|
pred_zmq_addr = None
|
|
|
|
if pred_zmq_addr is None:
|
|
pred_zmq_addr = zmq_addr
|
|
|
|
if parser.isSet(defaultImage):
|
|
default_image = parser.value(defaultImage)
|
|
else:
|
|
default_image = None
|
|
|
|
try:
|
|
splash.set_progress(50, f"Connecting to {base_url or 'backend'}...")
|
|
token = auth(base_url, cert_path)
|
|
if not token or token.count(".") != 2:
|
|
raise RuntimeError(
|
|
"Authentication did not return a valid token. "
|
|
"Please check the server is running (it may still be initializing)."
|
|
)
|
|
logger.info("Authentication successful")
|
|
splash.set_progress(80, "Authentication successful...")
|
|
except Exception as e:
|
|
splash.finish(None)
|
|
logger.exception("Cannot connect to AareDAQ server. Exiting.")
|
|
QMessageBox.critical(
|
|
None,
|
|
"Authentication Error",
|
|
f"Cannot connect to AareDAQ server:\n{e!s}\n\n Please check the server is running and your network connection.",
|
|
)
|
|
sys.exit(1)
|
|
|
|
splash.set_progress(90, "Loading Main Window...")
|
|
win = MainWindow(
|
|
base_url=base_url,
|
|
token=token,
|
|
default_image=default_image,
|
|
zmq_addr=zmq_addr,
|
|
pred_zmq_addr=pred_zmq_addr,
|
|
beamline_cam_addr=default_beamline_cam_addr,
|
|
gonio_cam_addr=default_gonio_cam_addr,
|
|
gonio_cam_id=default_gonio_camera_id,
|
|
)
|
|
|
|
splash.set_progress(100, "Ready")
|
|
splash.finish(win)
|
|
# Start windowed (not maximized) at a fraction of the primary screen,
|
|
# centered — sized explicitly because the size hint is wider than the
|
|
# monitor. Other panels may still enforce a somewhat larger minimum;
|
|
# the window then lands on that minimum instead.
|
|
unmax_w, unmax_h = 0.5, 0.7
|
|
available = app.primaryScreen().availableGeometry()
|
|
win.resize(int(available.width() * unmax_w), int(available.height() * unmax_h))
|
|
win.move(available.center() - win.rect().center())
|
|
win.show()
|
|
sys.exit(app.exec())
|
|
|
|
except Exception as e:
|
|
splash.finish(None)
|
|
logger.exception("Error starting GUI")
|
|
|
|
QMessageBox.critical(
|
|
None,
|
|
"Fatal Error",
|
|
f"An error occurred during startup. See console for details."
|
|
f"\nPlease check the server is running and your network connection."
|
|
f"\n\n{e!s}\n\n",
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|