Skip to content

Commit 3eef140

Browse files
Minipadaclaude
andcommitted
fix(dc_bringup): auto-stage passthrough sink files before dc_bridge starts
The sim CI job on #471's migration failed: qrcodes_stdout reached every nav waypoint but read zero QR codes. Root cause is a pre-existing but now much more exposed gap -- every custom_config_files passthrough recipe (10 demos after #471's migration, previously just the InfluxDB demo from #470) is documented as a manual "mkdir -p ~/.dc && cp ... ~/.dc/" step before first launch. Nothing runs that step automatically for a demo launched straight from an installed package, so a fresh environment (CI, a new dev machine) silently runs dc_bridge with no sink at all -- no crash, no error, just a route nobody consumes. dc_bringup.launch.py's build_bridge_and_mcap_actions already parses the params file and rewrites custom_config_files at launch time for the dc_mcap_writer case; extend the same OpaqueFunction to stage any custom_config_files entry that's missing, copying it from the config/ directory CMakeLists installs as a sibling of params/. Never overwrites an existing destination file, since elasticsearch.md and demos.md both document hand-editing the staged copy to experiment. Verified the staging logic in isolation (stages when missing, never clobbers an existing/edited copy, no-ops when there's no matching source file) and confirmed ruff/prek pass. The containerized deploy path (deploy/robot/compose.isolated-network.yaml's bind mount) already covered this correctly and is untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WYVDzgJx2xLAzixNFm7b7H Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
1 parent 380ef57 commit 3eef140

11 files changed

Lines changed: 66 additions & 0 deletions

dc_bringup/launch/dc_bringup.launch.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: MPL-2.0
33

44
import os
5+
import shutil
56

67
import yaml
78
from ament_index_python.packages import get_package_prefix, get_package_share_directory
@@ -191,6 +192,36 @@ def _receives(destination_name):
191192
]
192193

193194

195+
def _stage_custom_config_files(params_file_path, custom_config_files):
196+
"""Stage a demo's passthrough sink TOML(s) at the path `custom_config_files` names.
197+
198+
A `custom_config_files` entry (ADR-0003 passthrough, #471) is handed to `dc_bridge`
199+
as a plain filesystem path -- nothing installs a file there on its own. A
200+
containerized deploy solves this with a bind mount straight onto that path
201+
(deploy/robot/compose.isolated-network.yaml); a demo launched from an installed
202+
package has no such mount, so without this the referenced file is simply missing
203+
and `dc_bridge` runs without the sink (see the sim CI job this fixed: qrcodes_stdout
204+
reached every nav waypoint but read zero QR codes, because the passthrough console
205+
sink was never in place, and no `dc.*` route makes it to the Measurement pipeline
206+
without one). Recipes live in a `config/` directory `dc_demos/CMakeLists.txt`
207+
installs as a sibling of `params/`, so that sibling is where this looks for a
208+
same-named file to copy from. Never overwrites an existing destination file --
209+
demos.md and elasticsearch.md both document hand-editing the staged copy to
210+
experiment, which a blind refresh on every launch would clobber.
211+
"""
212+
config_dir = os.path.join(
213+
os.path.dirname(os.path.dirname(os.path.abspath(params_file_path))), "config"
214+
)
215+
for raw_path in custom_config_files:
216+
dest_path = os.path.expanduser(os.path.expandvars(raw_path))
217+
if os.path.exists(dest_path):
218+
continue
219+
candidate = os.path.join(config_dir, os.path.basename(dest_path))
220+
if os.path.isfile(candidate):
221+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
222+
shutil.copyfile(candidate, dest_path)
223+
224+
194225
def build_bridge_and_mcap_actions(configured_params):
195226
"""Build the `dc_bridge` Node, plus `dc_mcap_writer` if the params file enables it.
196227
@@ -241,6 +272,11 @@ def _build(context, *args, **kwargs):
241272
except OSError:
242273
raw_params = {}
243274

275+
dc_bridge_params_early = (raw_params.get("dc_bridge") or {}).get("ros__parameters") or {}
276+
_stage_custom_config_files(
277+
params_file_path, dc_bridge_params_early.get("custom_config_files", [])
278+
)
279+
244280
mcap_params = (raw_params.get("dc_mcap_writer") or {}).get("ros__parameters") or {}
245281
bridge_parameters = [configured_params]
246282
extra_actions = []

dc_demos/params/elasticsearch.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ dc_bridge:
1010
# ADR-0003 passthrough: a raw Vector sink config listed in `custom_config_files`,
1111
# consuming the public `dc.<tag>` routes. Copy it into place before launching:
1212
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/elasticsearch_sink.toml" ~/.dc/
13+
# (dc_bringup.launch.py now stages this file automatically at launch time if
14+
# it's not already there -- see _stage_custom_config_files -- so this manual
15+
# step is only needed when running dc_bridge some other way.)
1316
#
1417
# `destinations` still has to name at least one blessed Destination, and the routes
1518
# the snippet consumes exist only for topics listed in *its* `inputs` — dc_bridge

dc_demos/params/fastdds_stats_pgsql_grafana.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dc_bridge:
1111
# subscriptions and `dc.<tag>` routes from `destinations` alone, never from a
1212
# passthrough snippet's `inputs`. Copy the passthrough sink into place first:
1313
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/fastdds_stats_pgsql_grafana_sink.toml" ~/.dc/
14+
# (dc_bringup.launch.py now stages this file automatically at launch time if
15+
# it's not already there -- see _stage_custom_config_files -- so this manual
16+
# step is only needed when running dc_bridge some other way.)
1417
destinations: ["records_log"]
1518
records_log:
1619
type: file

dc_demos/params/group_memory_uptime_stdout.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dc_bridge:
1111
# subscriptions and `dc.<tag>` routes from `destinations` alone, never from a
1212
# passthrough snippet's `inputs`. Copy the passthrough sink into place first:
1313
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/group_memory_uptime_stdout_sink.toml" ~/.dc/
14+
# (dc_bringup.launch.py now stages this file automatically at launch time if
15+
# it's not already there -- see _stage_custom_config_files -- so this manual
16+
# step is only needed when running dc_bridge some other way.)
1417
destinations: ["records_log"]
1518
records_log:
1619
type: file

dc_demos/params/qrcodes_minio_pgsql.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ dc_bridge:
2323
#
2424
# Copy the passthrough sink into place first:
2525
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/qrcodes_minio_pgsql_sink.toml" ~/.dc/
26+
# (dc_bringup.launch.py now stages this file automatically at launch time if
27+
# it's not already there -- see _stage_custom_config_files -- so this manual
28+
# step is only needed when running dc_bridge some other way.)
2629
destinations: ["records_log", "rustfs"]
2730
records_log:
2831
type: file

dc_demos/params/qrcodes_stdout.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dc_bridge:
1111
# subscriptions and `dc.<tag>` routes from `destinations` alone, never from a
1212
# passthrough snippet's `inputs`. Copy the passthrough sink into place first:
1313
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/qrcodes_stdout_sink.toml" ~/.dc/
14+
# (dc_bringup.launch.py now stages this file automatically at launch time if
15+
# it's not already there -- see _stage_custom_config_files -- so this manual
16+
# step is only needed when running dc_bridge some other way.)
1417
destinations: ["records_log"]
1518
records_log:
1619
type: file

dc_demos/params/tb3_simulation_influxdb.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ dc_bridge:
1212
# doc/src/dc/destinations.md. Copy the example sink shipped with this package into
1313
# place before launching this demo:
1414
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/tb3_simulation_influxdb_sink.toml" ~/.dc/
15+
# (dc_bringup.launch.py now stages this file automatically at launch time if
16+
# it's not already there -- see _stage_custom_config_files -- so this manual
17+
# step is only needed when running dc_bridge some other way.)
1518
#
1619
# `destinations` must still name at least one *blessed* Destination, and its `inputs`
1720
# are what create the `dc.<tag>` routes the snippet consumes — dc_bridge derives both

dc_demos/params/tb3_simulation_pgsql_minio.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ dc_bridge:
2323
#
2424
# Copy the passthrough sink into place first:
2525
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/tb3_simulation_pgsql_minio_sink.toml" ~/.dc/
26+
# (dc_bringup.launch.py now stages this file automatically at launch time if
27+
# it's not already there -- see _stage_custom_config_files -- so this manual
28+
# step is only needed when running dc_bridge some other way.)
2629
destinations: ["records_log", "rustfs"]
2730
records_log:
2831
type: file

dc_demos/params/tb3_simulation_stdout.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dc_bridge:
1111
# subscriptions and `dc.<tag>` routes from `destinations` alone, never from a
1212
# passthrough snippet's `inputs`. Copy the passthrough sink into place first:
1313
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/tb3_simulation_stdout_sink.toml" ~/.dc/
14+
# (dc_bringup.launch.py now stages this file automatically at launch time if
15+
# it's not already there -- see _stage_custom_config_files -- so this manual
16+
# step is only needed when running dc_bridge some other way.)
1417
destinations: ["records_log"]
1518
records_log:
1619
type: file

dc_demos/params/uptime_custom_stdout.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dc_bridge:
1111
# subscriptions and `dc.<tag>` routes from `destinations` alone, never from a
1212
# passthrough snippet's `inputs`. Copy the passthrough sink into place first:
1313
# mkdir -p ~/.dc && cp "$(ros2 pkg prefix dc_demos)/share/dc_demos/config/uptime_custom_stdout_sink.toml" ~/.dc/
14+
# (dc_bringup.launch.py now stages this file automatically at launch time if
15+
# it's not already there -- see _stage_custom_config_files -- so this manual
16+
# step is only needed when running dc_bridge some other way.)
1417
destinations: ["records_log"]
1518
records_log:
1619
type: file

0 commit comments

Comments
 (0)