From 1d5227d4317c1066f2e47c65db03bbeca60a1478 Mon Sep 17 00:00:00 2001 From: Evan Sarkar Date: Sat, 16 May 2026 09:50:06 +0530 Subject: [PATCH] Guard log stream setup against missing TOC variables Wrap async block.add_variable() calls in _stream_motors (FlightTab), _stream_battery (MainUI), and _stream_loop (PoseLogger) with log.names() presence checks, mirroring the existing supervisor.info guard. When a variable is absent from the firmware's log TOC, it is skipped with a warning instead of raising inside the create_task() coroutine. Consumers are updated to tolerate missing keys so the streams continue to deliver any available data without crashing the tab or silently killing the streaming task. --- src/cfclient/ui/main.py | 17 +++++++--- src/cfclient/ui/pose_logger.py | 32 +++++++++++------- src/cfclient/ui/tabs/FlightTab.py | 56 +++++++++++++++++++------------ 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/cfclient/ui/main.py b/src/cfclient/ui/main.py index dd5cea48c..6549ba5e3 100644 --- a/src/cfclient/ui/main.py +++ b/src/cfclient/ui/main.py @@ -592,15 +592,22 @@ def _notify_tabs_disconnected(self) -> None: async def _stream_battery(self, cf): log = cf.log() block = await log.create_block() - await block.add_variable("pm.vbat") - await block.add_variable("pm.state") + + available = log.names() + for name in ("pm.vbat", "pm.state"): + if name in available: + await block.add_variable(name) + else: + logger.warning("Log variable %s missing from TOC, skipping", name) + stream = await block.start(1000) try: while True: data = await stream.next() - self._battery_signal.emit( - data.data["pm.vbat"], int(data.data["pm.state"]) - ) + if "pm.vbat" in data.data and "pm.state" in data.data: + self._battery_signal.emit( + data.data["pm.vbat"], int(data.data["pm.state"]) + ) finally: try: await asyncio.shield(stream.stop()) diff --git a/src/cfclient/ui/pose_logger.py b/src/cfclient/ui/pose_logger.py index 8b50936ea..97b20c82f 100644 --- a/src/cfclient/ui/pose_logger.py +++ b/src/cfclient/ui/pose_logger.py @@ -96,24 +96,32 @@ def stop(self): async def _stream_loop(self, cf): log = cf.log() block = await log.create_block() - await block.add_variable(self.LOG_NAME_ESTIMATE_X) - await block.add_variable(self.LOG_NAME_ESTIMATE_Y) - await block.add_variable(self.LOG_NAME_ESTIMATE_Z) - await block.add_variable(self.LOG_NAME_ESTIMATE_ROLL) - await block.add_variable(self.LOG_NAME_ESTIMATE_PITCH) - await block.add_variable(self.LOG_NAME_ESTIMATE_YAW) + + available = log.names() + for name in ( + self.LOG_NAME_ESTIMATE_X, + self.LOG_NAME_ESTIMATE_Y, + self.LOG_NAME_ESTIMATE_Z, + self.LOG_NAME_ESTIMATE_ROLL, + self.LOG_NAME_ESTIMATE_PITCH, + self.LOG_NAME_ESTIMATE_YAW, + ): + if name in available: + await block.add_variable(name) + else: + logger.warning("Log variable %s missing from TOC, skipping", name) stream = await block.start(16) # 16ms period try: while True: data = await stream.next() self.pose = ( - data.data[self.LOG_NAME_ESTIMATE_X], - data.data[self.LOG_NAME_ESTIMATE_Y], - data.data[self.LOG_NAME_ESTIMATE_Z], - data.data[self.LOG_NAME_ESTIMATE_ROLL], - data.data[self.LOG_NAME_ESTIMATE_PITCH], - data.data[self.LOG_NAME_ESTIMATE_YAW], + data.data.get(self.LOG_NAME_ESTIMATE_X, 0.0), + data.data.get(self.LOG_NAME_ESTIMATE_Y, 0.0), + data.data.get(self.LOG_NAME_ESTIMATE_Z, 0.0), + data.data.get(self.LOG_NAME_ESTIMATE_ROLL, 0.0), + data.data.get(self.LOG_NAME_ESTIMATE_PITCH, 0.0), + data.data.get(self.LOG_NAME_ESTIMATE_YAW, 0.0), ) self.data_received_cb.call(self, self.pose) finally: diff --git a/src/cfclient/ui/tabs/FlightTab.py b/src/cfclient/ui/tabs/FlightTab.py index 94a94e7ed..d4a6760d0 100644 --- a/src/cfclient/ui/tabs/FlightTab.py +++ b/src/cfclient/ui/tabs/FlightTab.py @@ -345,19 +345,25 @@ async def _async_flight_command(self, action): def _log_data_received(self, timestamp, data): if self.isVisible() and self._isConnected: - self.actualM1.setValue(data[self.LOG_NAME_MOTOR_1]) - self.actualM2.setValue(data[self.LOG_NAME_MOTOR_2]) - self.actualM3.setValue(data[self.LOG_NAME_MOTOR_3]) - self.actualM4.setValue(data[self.LOG_NAME_MOTOR_4]) - - self.estimateThrust.setText( - "%.2f%%" % self.thrustToPercentage(data[self.LOG_NAME_THRUST]) - ) - - if data[self.LOG_NAME_CAN_FLY] != self._can_fly_deprecated: - self._can_fly_deprecated = data[self.LOG_NAME_CAN_FLY] - if self._cf is not None: - create_task(self._update_flight_commander(self._cf)) + if self.LOG_NAME_MOTOR_1 in data: + self.actualM1.setValue(data[self.LOG_NAME_MOTOR_1]) + if self.LOG_NAME_MOTOR_2 in data: + self.actualM2.setValue(data[self.LOG_NAME_MOTOR_2]) + if self.LOG_NAME_MOTOR_3 in data: + self.actualM3.setValue(data[self.LOG_NAME_MOTOR_3]) + if self.LOG_NAME_MOTOR_4 in data: + self.actualM4.setValue(data[self.LOG_NAME_MOTOR_4]) + + if self.LOG_NAME_THRUST in data: + self.estimateThrust.setText( + "%.2f%%" % self.thrustToPercentage(data[self.LOG_NAME_THRUST]) + ) + + if self.LOG_NAME_CAN_FLY in data: + if data[self.LOG_NAME_CAN_FLY] != self._can_fly_deprecated: + self._can_fly_deprecated = data[self.LOG_NAME_CAN_FLY] + if self._cf is not None: + create_task(self._update_flight_commander(self._cf)) if self.LOG_NAME_SUPERVISOR_INFO in data: self._supervisor_info_bitfield = data[self.LOG_NAME_SUPERVISOR_INFO] @@ -534,15 +540,21 @@ def connected(self, cf): async def _stream_motors(self, cf): log = cf.log() block = await log.create_block() - await block.add_variable(self.LOG_NAME_THRUST) - await block.add_variable(self.LOG_NAME_MOTOR_1) - await block.add_variable(self.LOG_NAME_MOTOR_2) - await block.add_variable(self.LOG_NAME_MOTOR_3) - await block.add_variable(self.LOG_NAME_MOTOR_4) - await block.add_variable(self.LOG_NAME_CAN_FLY) - - if self.LOG_NAME_SUPERVISOR_INFO in log.names(): - await block.add_variable(self.LOG_NAME_SUPERVISOR_INFO) + + available = log.names() + for name in ( + self.LOG_NAME_THRUST, + self.LOG_NAME_MOTOR_1, + self.LOG_NAME_MOTOR_2, + self.LOG_NAME_MOTOR_3, + self.LOG_NAME_MOTOR_4, + self.LOG_NAME_CAN_FLY, + self.LOG_NAME_SUPERVISOR_INFO, + ): + if name in available: + await block.add_variable(name) + else: + logger.warning("Log variable %s missing from TOC, skipping", name) period_ms = Config().get("ui_update_period") stream = await block.start(period_ms)