GUI/DAQ: fixed connection error handling for tell, daq, smargon. Added handling for aerotech. Fixed bug where connection messaged flashed when polling status, fixed bug where tell connected would quash other messages and appear as an error.

This commit is contained in:
2026-03-24 11:06:32 +01:00
parent e9f3688952
commit 5634554c00
4 changed files with 78 additions and 9 deletions
+13
View File
@@ -1622,6 +1622,16 @@ class AareDAQ:
# Keep status flowing even if Tell code throws something unexpected
return self.__cfg.current_sample, False, f"TELL unavailable: {e}"
def _aerotech_status(self) -> tuple[bool, str | None]:
aerotech_ok = True
aerotech_err: str | None = None
try:
_ = self.__devs.aerotech.status()
except Exception as e:
aerotech_ok = False
aerotech_err = str(e)
return aerotech_ok, aerotech_err
def _safe_geom(self) -> tuple[SampleGeometryModel, bool, str | None]:
"""
Return (geom, smargon_connected, smargon_error) without raising.
@@ -1704,6 +1714,7 @@ class AareDAQ:
def status(self) -> DAQStatusModel:
safe_sample, tell_ok, tell_err = self._safe_sample()
safe_geom, smargon_ok, smargon_err = self._safe_geom()
aerotech_ok, aerotech_err = self._aerotech_status()
return DAQStatusModel(
state=self.state,
@@ -1721,6 +1732,8 @@ class AareDAQ:
tell_error=tell_err,
smargon_connected=smargon_ok,
smargon_error=smargon_err,
aerotech_connected=aerotech_ok,
aerotech_error=aerotech_err,
)
def cancel(self):
+19 -1
View File
@@ -78,6 +78,8 @@ class MainWindow(QMainWindow):
self._controls_help_dialog = None
self._cleanup_done = False
self._default_window_state = None
self._last_status_message: str | None = None
self._last_status_is_error: bool | None = None
# Tutorial manager (define tutorials after widgets exist)
self._tutorial_event_bus = TutorialEventBus(self)
@@ -482,10 +484,26 @@ class MainWindow(QMainWindow):
self.daq.fluorimeter_spectrum_update.connect(lambda: self.fluor_panel_dock.setVisible(True))
self.daq.status_message.connect(self.status_bar.show_connection_message)
self.daq.status_message.connect(self.alert_banner.show_message)
self.daq.status_message.connect(self._show_status_message_once)
register_tutorials(self, self.tutorial_manager)
@Slot(str, bool)
def _show_status_message_once(self, message: str, is_error: bool = True) -> None:
message = (message or "").strip()
if not message:
self.alert_banner.clear_message()
self._last_status_message = None
self._last_status_is_error = None
return
if message == self._last_status_message and is_error == self._last_status_is_error:
return
self._last_status_message = message
self._last_status_is_error = is_error
self.alert_banner.show_message(message, is_error=is_error)
@Slot(QPixmap)
def _on_samcam_prediction_pixmap(self, pix: QPixmap) -> None:
self._last_pred_image_ts = time.monotonic()
+39 -7
View File
@@ -80,6 +80,9 @@ class DAQWorker(QObject):
self._last_tell_connected: bool | None = None
self._last_tell_error: str | None = None
self._last_smargon_connected: bool | None = None
self._last_smargon_error: str | None = None
self._last_aerotech_connected: bool | None = None
self._last_aerotech_error: str | None = None
self._server_connected: bool | None = None
self._last_server_error: str | None = None
@@ -196,8 +199,10 @@ class DAQWorker(QObject):
*,
tell_conn: bool,
smargon_conn: bool,
aerotech_conn: bool,
tell_changed: bool,
smargon_changed: bool,
aerotech_changed: bool,
) -> tuple[str | None, str | None, bool]:
disconnected: list[str] = []
restored: list[str] = []
@@ -212,11 +217,22 @@ class DAQWorker(QObject):
elif smargon_changed:
restored.append("Smargon")
if not aerotech_conn:
disconnected.append("Aerotech")
elif aerotech_changed:
restored.append("Aerotech")
if disconnected:
if len(disconnected) == 3:
return (
"all-devices-down",
"TELL, Smargon, and Aerotech connection errors, please inform your local contact.",
True,
)
if len(disconnected) == 2:
return (
"tell+smargon-down",
"TELL and Smargon connection errors, please inform your local contact.",
"+".join(sorted(d.lower() for d in disconnected)) + "-down",
f"{disconnected[0]} and {disconnected[1]} connection errors, please inform your local contact.",
True,
)
device = disconnected[0]
@@ -227,10 +243,11 @@ class DAQWorker(QObject):
)
if restored:
if len(restored) == 3:
return (None, "TELL, Smargon, and Aerotech connections restored.", False)
if len(restored) == 2:
return (None, "TELL and Smargon connections restored.", False,)
device = restored[0]
return (None, f"{device} connection restored.",False)
return (None, f"{restored[0]} and {restored[1]} connections restored.", False)
return (None, f"{restored[0]} connection restored.", False)
return (None, None, False)
@@ -258,19 +275,31 @@ class DAQWorker(QObject):
or self._last_tell_connected != tell_conn
or self._last_tell_error != tell_err_text
)
smargon_changed = (
self._last_smargon_connected is not None and self._last_smargon_connected != smargon_conn
self._last_smargon_connected is None
or self._last_smargon_connected != smargon_conn
or self._last_smargon_error != smargon_err_text
)
aerotech_changed = (
self._last_aerotech_connected is None
or self._last_aerotech_connected != aerotech_conn
or self._last_aerotech_error != aerotech_err_text
)
self._last_tell_connected = tell_conn
self._last_smargon_connected = smargon_conn
self._last_aerotech_connected = aerotech_conn
self._last_tell_error = tell_err_text
self._last_smargon_error = smargon_err_text
self._last_aerotech_error = aerotech_err_text
status_key, status_msg, is_error = self._compose_device_status_message(
tell_conn=tell_conn,
smargon_conn=smargon_conn,
aerotech_conn=aerotech_conn,
tell_changed=tell_changed,
smargon_changed=smargon_changed,
aerotech_changed=aerotech_changed,
)
self._emit_status_if_changed(status_key, status_msg, is_error)
@@ -278,6 +307,8 @@ class DAQWorker(QObject):
self._log_device_error_throttled(device="tell", message=tell_err)
if not smargon_conn:
self._log_device_error_throttled(device="smargon", message=smargon_err)
if not aerotech_conn:
self._log_device_error_throttled(device="aerotech", message=aerotech_err)
except Exception as e:
err_msg = str(e)
@@ -293,6 +324,7 @@ class DAQWorker(QObject):
self._last_server_error = err_msg
self._last_tell_connected = None
self._last_smargon_connected = None
self._last_aerotech_connected = None
logger.error(f"Exception from status response: {e}")
+7 -1
View File
@@ -12,6 +12,7 @@ class AlertBanner(QFrame):
super().__init__(parent)
self._current_message: str | None = None
self._current_is_error: bool | None = None
self._clear_timer = QTimer(self)
self._clear_timer.setSingleShot(True)
@@ -42,7 +43,7 @@ class AlertBanner(QFrame):
self.clear_message()
return
if msg == self._current_message:
if self._current_message == msg and self._current_is_error == is_error:
return
if is_error:
@@ -62,6 +63,8 @@ class AlertBanner(QFrame):
"}"
)
else:
if msg == self._current_message:
return
decorated = f"✅ {msg} ✅"
self.setStyleSheet(
"QFrame {"
@@ -79,12 +82,15 @@ class AlertBanner(QFrame):
)
self._clear_timer.start(5000)
self._current_message = msg
self._current_is_error = is_error
self._label.setText(decorated)
self.setVisible(True)
@Slot()
def clear_message(self):
self._current_message = None
self._current_is_error = None
self._clear_timer.stop()
self._label.clear()
self.setVisible(False)