From 43d54e87c8371c7b331034cb2f4aa6115af4df40 Mon Sep 17 00:00:00 2001 From: Claude Code Agent Date: Sun, 28 Jun 2026 14:06:46 -0400 Subject: [PATCH] Fix CAMP sim lockup: non-blocking TF in running-task overlay (#136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RunningTasksOverlay::rebuildItems called getGeoCoordinate per task pose on the GUI thread every TaskFeedback (~1 Hz+). getGeoCoordinate did a blocking 1.5 s TF wait (plus a 1.5 s retry) — when map->earth wasn't immediately available the UI froze for up to ~3 s per pose, every cycle, locking up CAMP. getGeoCoordinate gains an optional timeout_sec (default 1.5 s, preserving existing callers); the overlay now passes 0.0 for a non-blocking lookup. A pose that isn't transformable this cycle is skipped and reappears on the next rebuild once the quasi-static transform is available — the UI never blocks. Regression from #129 (PR #135). --- src/camp/ros/ros_widget.cpp | 6 +++--- src/camp/ros/ros_widget.h | 6 +++++- src/camp/running_tasks/running_tasks_overlay.cpp | 7 ++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/camp/ros/ros_widget.cpp b/src/camp/ros/ros_widget.cpp index 7cef383..8e0b0f6 100644 --- a/src/camp/ros/ros_widget.cpp +++ b/src/camp/ros/ros_widget.cpp @@ -11,7 +11,7 @@ ROSWidget::ROSWidget(QWidget *parent) } -QGeoCoordinate ROSWidget::getGeoCoordinate(const geometry_msgs::msg::Pose &pose, const std_msgs::msg::Header &header) +QGeoCoordinate ROSWidget::getGeoCoordinate(const geometry_msgs::msg::Pose &pose, const std_msgs::msg::Header &header, double timeout_sec) { if(header.frame_id.empty()) return {}; @@ -23,7 +23,7 @@ QGeoCoordinate ROSWidget::getGeoCoordinate(const geometry_msgs::msg::Pose &pose, geometry_msgs::msg::PoseStamped ecef; try { - ecef = transform_buffer_->transform(ps, "earth", tf2::durationFromSec(1.5)); + ecef = transform_buffer_->transform(ps, "earth", tf2::durationFromSec(timeout_sec)); } catch (const tf2::TransformException &) { @@ -37,7 +37,7 @@ QGeoCoordinate ROSWidget::getGeoCoordinate(const geometry_msgs::msg::Pose &pose, { ps.header.stamp.sec = 0; ps.header.stamp.nanosec = 0; - ecef = transform_buffer_->transform(ps, "earth", tf2::durationFromSec(1.5)); + ecef = transform_buffer_->transform(ps, "earth", tf2::durationFromSec(timeout_sec)); } catch (const tf2::TransformException &ex) { diff --git a/src/camp/ros/ros_widget.h b/src/camp/ros/ros_widget.h index 268acdd..d3612bb 100644 --- a/src/camp/ros/ros_widget.h +++ b/src/camp/ros/ros_widget.h @@ -15,7 +15,11 @@ class ROSWidget: public ROSClient ROSWidget(QWidget *parent = nullptr); protected: - QGeoCoordinate getGeoCoordinate(const geometry_msgs::msg::Pose &pose, const std_msgs::msg::Header &header); + // timeout_sec bounds the TF wait. The default keeps the original blocking + // behaviour for existing callers; pass 0.0 for a non-blocking lookup (e.g. on + // the GUI thread in a per-frame loop, where a 1.5 s block per pose freezes the + // UI — see camp#136). + QGeoCoordinate getGeoCoordinate(const geometry_msgs::msg::Pose &pose, const std_msgs::msg::Header &header, double timeout_sec = 1.5); }; diff --git a/src/camp/running_tasks/running_tasks_overlay.cpp b/src/camp/running_tasks/running_tasks_overlay.cpp index 0bbfa0e..10ad7bc 100644 --- a/src/camp/running_tasks/running_tasks_overlay.cpp +++ b/src/camp/running_tasks/running_tasks_overlay.cpp @@ -72,10 +72,15 @@ void RunningTasksOverlay::rebuildItems( // TaskInformation.poses is geometry_msgs/PoseStamped[] with per-pose headers. // getGeoCoordinate transforms each to earth frame with stale-stamp retry, // so the source frame (commonly map or earth) is handled transparently. + // Use a NON-BLOCKING lookup (0 s timeout): this runs on the GUI thread on + // every TaskFeedback rebuild, and the default 1.5 s blocking wait per pose + // freezes the whole UI when map->earth isn't yet available (camp#136). A + // pose that isn't transformable this cycle is simply skipped and reappears + // on the next rebuild once the (quasi-static) transform is available. QList geo_poses; for (const auto& ps : task.poses) { - QGeoCoordinate gc = getGeoCoordinate(ps.pose, ps.header); + QGeoCoordinate gc = getGeoCoordinate(ps.pose, ps.header, 0.0); if (gc.isValid()) geo_poses.append(gc); }