From 8bf5af9c189964fea5072a889c7295fbb58e1c70 Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Wed, 15 Jul 2026 15:08:32 -0600 Subject: [PATCH] fix(autopilot-manager): refresh the autopilot version after a reflash The cached version was requested only while it read "Unknown", and nothing ever reset it, so the first value latched for the life of the process. After flashing new firmware the UI kept reporting the old version until something else (QGC connecting) asked the FC for AUTOPILOT_VERSION, whose reply mavlink-router fans out to our endpoint. Invalidate the cache on the heartbeat-gap episode and on the flash path. The flash needs its own call because disconnect() stops the message loop, so the heartbeat-gap branch cannot run during a flash. --- services/autopilot-manager/autopilot_manager.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/services/autopilot-manager/autopilot_manager.py b/services/autopilot-manager/autopilot_manager.py index 0e3d582..1537ab9 100644 --- a/services/autopilot-manager/autopilot_manager.py +++ b/services/autopilot-manager/autopilot_manager.py @@ -259,6 +259,16 @@ def is_mavlink_connected(self): time_since_heartbeat = (datetime.now() - self.last_heartbeat).total_seconds() return time_since_heartbeat < self.heartbeat_timeout + def invalidate_version(self): + """Drop the cached version/git hash so the message loop re-requests them. + + PX4 only sends AUTOPILOT_VERSION when asked, and the loop asks only while + the version is "Unknown". Anything that can leave the FC running different + firmware must clear the latch or the stale string is reported forever.""" + with self._lock: + self.autopilot_data["version"] = "Unknown" + self.autopilot_data["git_hash"] = "Unknown" + def request_autopilot_version(self): """Request the autopilot version information""" if not self.mav_connection: @@ -435,6 +445,9 @@ def process_messages(self): if not disconnect_logged: logger.warning("No autopilot heartbeat; probing and reconnecting in the background") disconnect_logged = True + # The FC may return reflashed or rebooted, so the cached + # version is unverified until it answers again. + self.invalidate_version() # Probe with a GCS heartbeat at ~1 Hz to elicit a response. if current_time - last_probe_time >= 1.0: @@ -659,6 +672,9 @@ def flash_firmware(self, firmware_path: str) -> bool: # Disconnect from MAVLink first to avoid conflicts logger.debug("Disconnecting MAVLink connection") self.mavlink.disconnect() + # disconnect() stops the message loop, so the heartbeat-gap path that + # normally invalidates the version cannot run during a flash. + self.mavlink.invalidate_version() # Stop mavlink router service if it's running logger.debug("Checking if mavlink-router is active")