diff --git a/src/aare/daq/tellupdater.py b/src/aare/daq/tellupdater.py index aaa39897..22da69be 100644 --- a/src/aare/daq/tellupdater.py +++ b/src/aare/daq/tellupdater.py @@ -263,20 +263,26 @@ def listen_to_sse(): logger.info(f"[SSE] Reconnecting in {SSE_RECONNECT_DELAY_S} seconds") time.sleep(SSE_RECONNECT_DELAY_S) -def compare_and_report_change(old, new, key_func): - """ - Compare two lists of pucks and return (joined, left). - Ignore reference-tools puck(s) with tell_position 'X1'. - """ - # Filter out reference-tools - old_filtered = [p for p in old if getattr(p, "tell_position", None) != "X1"] - new_filtered = [p for p in new if getattr(p, "tell_position", None) != "X1"] - old_ids = set(key_func(p) for p in old_filtered) - new_ids = set(key_func(p) for p in new_filtered) +def compare_and_report_change(old, new): + """ + Compare two lists of pucks. + Detects if puck names, dewars, OR positions have changed. + """ - joined = new_ids - old_ids # newly added - left = old_ids - new_ids # removed + def get_puck_keys(pucks): + return { + # Including tell_position ensures we sync if a puck moves + (p.puck_name, p.dewar_id, getattr(p, "tell_position", None)) + for p in pucks + if getattr(p, "tell_position", None) != "X1" + } + + old_keys = get_puck_keys(old) + new_keys = get_puck_keys(new) + + joined = new_keys - old_keys + left = old_keys - new_keys return joined, left @@ -317,45 +323,20 @@ def on_sse_event(event): handle_tell_change_event() def on_message(ws, message): - #print("[WS][MESSAGE]", message) try: data = json.loads(message) - logger.debug(f"[WS] Received message: {data!r}") - if not isinstance(data, list): - logger.error(f"[WS] Unexpected message type: {type(data).__name__}") - return - new_pucks = [PuckWithTellPosition(**item) for item in data] - - # Keep module-level current_pucks in sync global current_pucks - - # First, try to refresh DB/TELL state to avoid overwriting a recent SSE update. - try: - # This will fetch detected pucks from TELL and update DB via your existing handler - handle_tell_change_event() - # Small wait gives SSE/DB handler a moment to settle (optional) - time.sleep(0.2) - except (TellCommunicationError, TellConnectionException, RequestException, ApiException): - logger.exception("[WS] Failed to refresh TELL state before websocket update") - - # Compare lists and ignore reference puck at X1 if needed - joined, left = compare_and_report_change(current_pucks, new_pucks, lambda p: getattr(p, "puck_name", None)) - - # Update local cache - current_pucks = new_pucks - + joined, left = compare_and_report_change(current_pucks, new_pucks) if joined or left: - logger.info(f"[WS] Changes detected joined={joined} left={left}") - # Send only when there is a real change + logger.info(f"[WS] Changes detected: joined={joined} left={left}") ws_update_samples_info(new_pucks) + current_pucks = new_pucks logger.info("[WS] Sent current pucks state to TELL") else: - logger.info("[WS] No change; skipping send to TELL") - except json.JSONDecodeError: - logger.exception("[WS] Failed to decode websocket message as JSON") - except (ValidationError, TypeError, ValueError, KeyError): - logger.exception("[WS] Failed to process websocket message") + logger.info("[WS] No change (same pucks and dewars); skipping send.") + except Exception: + logger.exception("[WS] Failed to process message") def on_error(ws, error): logger.error(f"[WS] Error: {error}") @@ -365,6 +346,9 @@ def on_close(ws, close_status_code, close_msg): def on_open(ws): logger.info("[WS] WebSocket opened") + global current_pucks + # Clear the cache to force a re-sync with the robot on the first message + current_pucks = [] def main():