Files
AareDAQ/src/aare/gui/gui.py
T
perl_d b0863e5ddb
Build and Publish / test (pull_request) Failing after 1m23s
Build and Publish / build (pull_request) Skipped
Build and Publish / Build and Deploy Docs (pull_request) Skipped
style: format with ruff
2026-07-06 11:53:49 +02:00

217 lines
8.6 KiB
Python

import os
import sys
import traceback
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, QProgressBar, QSplashScreen
from aare.gui.auth import auth
from aare.gui.main_window import MainWindow
from aare.gui.widgets.splash_screen import LoadingSplashScreen
logger = setup_logger("aareGUI")
def main():
"""Wrapped gui as main function to make tests easier"""
splash = None
try:
# define application
app = QApplication(sys.argv)
app.setApplicationName("AareGUI")
app.setApplicationVersion("0.3.1")
app.setOrganizationName("PSI")
app.setOrganizationDomain("psi.ch")
# set icon
basedir = os.path.dirname(__file__)
icon_path = os.path.join(basedir, "graphics/aaregui_logo.svg")
app.setWindowIcon(QtGui.QIcon(icon_path))
# show splash screen
banner_path = os.path.join(basedir, "graphics/aare_banner.png")
splash_pix = QtGui.QPixmap(banner_path)
splash = LoadingSplashScreen(splash_pix)
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 initialising)."
)
logger.info("Authentication successful")
splash.set_progress(80, "Authentication successful...")
except Exception as e:
splash.finish(None)
logger.error(f"Cannot connect to AareDAQ server. Exiting. {e}")
logger.error(traceback.format_exc())
QMessageBox.critical(
None,
"Authentication Error",
f"Cannot connect to AareDAQ server:\n{str(e)}\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)
win.show()
sys.exit(app.exec())
except Exception as e:
splash.finish(None)
logger.error(f"Error starting GUI: {e}")
logger.error(f"Traceback: {traceback.format_exc()}")
try:
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{str(e)}\n\n",
)
except:
pass
sys.exit(1)
if __name__ == "__main__":
main()