Skip to content

feat(viewers): display generated CDR messages in Rerun and browser - #4733

Open
TomCC7 wants to merge 16 commits into
cc/feat/cdr-unit-recordingfrom
cc/feat/cdr-unit-viewers
Open

TomCC7 wants to merge 16 commits into
cc/feat/cdr-unit-recordingfrom
cc/feat/cdr-unit-viewers

Conversation

@TomCC7

@TomCC7 TomCC7 commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

What this adds

  • Render generated CDR messages through the existing Rerun bridge while retaining legacy rendering.
  • Decode browser SDK channels from complete cdrgen schemas in their manifest, using Foxglove's ROS2 libraries.
  • Use Rerun's native importer for offline CDR MCAP files.

How to test

From this PR's checkout:

DIMOS_ALLOW_MISSING_COCKPIT=1 uv sync --locked --extra visualization --extra web
uv run --extra visualization --extra web python -I examples/message-viewer/demo_cdr_rerun.py cdr-viewer.rrd
uv run --extra visualization rerun cdr-viewer.rrd
uv run --extra visualization rerun cdr-viewer.mcap

Use a new output name. The demo runs the real bridge and creates a live RRD plus a source-scene MCAP. Check TF, geometry, images, depth, calibration, clouds and legacy rendering. Native MCAP import visualizes supported standard types; Path and custom EntityMarkers remain reflected fields.

Scene checklist and native MCAP export.

For the browser, run these in separate terminals:

uv run --extra web python -I examples/message-viewer/demo_cdr_browser.py
cd web/sdk
deno task fixture --host 127.0.0.1

Open http://127.0.0.1:5174/demo_cdr.html?live=1. Expect connected, all eight channels, increasing counts and zero decode errors. Browser checklist.

Validation includes 38 focused tests and the installed Rerun example with native MCAP conversion. This layer leaves dimos mem rerun, navigation and dimos.msgs unchanged.

Shared CDR foundation

Standard ROS definitions are fetched and verified through the existing source lock and generation pipeline in #4366. Native SDK support is included in #4722. Shared Python value implementations and regressions are reviewed in #4800–#4803 before the #4734 caller migration. SDK and consumers use the same generated Rust message crate, including Time, TransformStamped and TFMessage; there is no separate TF schema package or generation command. Camera/depth callers migrate in #4734 and mapping/navigation callers in #4737.

The external CDR and transforms library pins remain unchanged. Local verification passed 122 SDK tests, all existing native consumer suites, and all 24 real native/DimSim/port acceptance tests without skips. Seven staged Nix source layouts resolve exactly one message crate with locked Cargo metadata. This is not a full Nix build or hardware validation. Fresh CI runs verify #4722 independently and the combined stack tip.

Stack #4804 contains the 12 original feature PRs and four separately reviewed adapter/buffer PRs (#4800–#4803). The redundant #4774 is superseded by #4722. No feature PR has been merged into main; existing conflicts with main and the WIP cutover failures in #4739 remain.

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go2 replay realtime (arm64)

Details
Benchmark suite Current: aebb801 Previous: c240b54 Ratio
peak memory 1576.324 MB 1827.461 MB 0.86
peak threads 361 threads 364 threads 0.99
network (transport) 2727.33 MB 2726.743 MB 1.00
disk write 1.828 MB 1.809 MB 1.01
instructions 116.977 G 136.831 G 0.85

This comment was automatically generated by workflow using github-action-benchmark.

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
6551 2 6549 57
View the top 2 failed test(s) by shortest run time
dimos.codebase_checks.test_no_init_files::test_no_init_files
Stack Traces | 0.044s run time
def test_no_init_files():
        dimos_dir = DIMOS_PROJECT_ROOT / "dimos"
        init_files = sorted(dimos_dir.rglob("__init__.py"))
        # The root dimos/__init__.py is allowed for the porcelain lazy import.
        init_files = [f for f in init_files if f != dimos_dir / "__init__.py"]
        if init_files:
            listing = "\n".join(f"  - {f.relative_to(dimos_dir)}" for f in init_files)
>           raise AssertionError(
                f"Found __init__.py files in dimos/:\n{listing}\n\n"
                "__init__.py files are not allowed because they lead to unnecessary "
                "extraneous imports. Everything should be imported straight from the "
                "source module."
            )
E           AssertionError: Found __init__.py files in dimos/:
E             - message_utils/__init__.py
E           
E           __init__.py files are not allowed because they lead to unnecessary extraneous imports. Everything should be imported straight from the source module.

dimos_dir  = PosixPath('.../dimos/dimos/dimos')
init_files = [PosixPath('.../dimos/dimos/dimos/message_utils/__init__.py')]
listing    = '  - message_utils/__init__.py'

dimos/codebase_checks/test_no_init_files.py:25: AssertionError
dimos.core.test_build_native_modules::test_module_locks_pin_the_shared_code_as_it_is_now
Stack Traces | 4.25s run time
@pytest.mark.skipif(not _IN_GIT_CHECKOUT, reason="needs a git checkout to read refs")
    def test_module_locks_pin_the_shared_code_as_it_is_now() -> None:
        """Every module's pin on native/cpp or native/rust must hold this commit's tree.
    
        `nix flake lock` and `cargo build --locked` do not notice that the shared code
        moved -- they only check that the lock is complete -- so a change there can land
        while every module still builds against the revision before it. The revision is
        free to be older than HEAD; what must match is the content it pins.
        """
        stale = {
            _rel(lock): names
            for lock in _RELOCK.flake_locks()
            if (names := _RELOCK.stale_flake_inputs(lock))
        }
        stale |= {
            _rel(manifest): why
            for manifest in _RELOCK.cargo_manifests()
            if (why := _RELOCK.stale_cargo_pins(manifest))
        }
>       assert not stale, (
            "these modules pin shared code that is not the code in this commit, so they build "
            f"against the older version: {stale} -- push, run bin/relock-shared-flakes, commit"
        )
E       AssertionError: these modules pin shared code that is not the code in this commit, so they build against the older version: {'.../memory/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../realsense/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../v4l2/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../livox/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../pointlio/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../lidar/virtual_mid360/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../dim_slam/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../ray_tracing/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../mls_planner/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../local_planner/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../fancy/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../depth2depth_cloud/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../dds/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../native-modules/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5']} -- push, run bin/relock-shared-flakes, commit
E       assert not {'.../memory/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../realsense/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../v4l2/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], '.../livox/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], ...}

names      = []
stale      = {'.../memory/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], 'dimo....../livox/rust/Cargo.toml': ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5'], ...}
why        = ['dimos-module @ 6663bcb6e5', 'dimos-module-macros @ 6663bcb6e5']

dimos/core/test_build_native_modules.py:476: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@TomCC7
TomCC7 added this pull request to stack #4743 October 20, 2026 22:26
@TomCC7
TomCC7 force-pushed the cc/feat/cdr-unit-viewers branch from bd424bb to 309e817 Compare October 21, 2026 04:39
@TomCC7 TomCC7 changed the title feat(viewers): consolidate manifest CDR and Rerun consumers feat(viewers): display generated CDR messages in Rerun and browser Oct 21, 2026
@greptile-apps

greptile-apps Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The PR changes generated-message viewing across the browser and Rerun workflows.

Fix source-timeline transform recording before merging; the external-schema compatibility issue is non-blocking.

Findings

  1. P1 Transforms miss source playback ▶
  2. P2 External schemas lose package context ▶

Summary

Adds generated CDR messages to Rerun and the browser SDK while keeping legacy rendering.

  • Browser channels decode generated CDR messages from their schemas.
  • Rerun displays generated messages while keeping legacy rendering available.

Both reported failures were reproduced. Neither is resolved by the supplied higher dependent PR revisions.

Reviews (1) · Last reviewed commit: "fix(viewers): use globalThis for browser..." · Reviewed by Greptile

Comment on lines +678 to +681
stamp = getattr(getattr(msg, "header", None), "stamp", None)
if stamp is not None:
nanos = int(stamp.sec) * 1_000_000_000 + int(stamp.nanosec)
rr.set_time("source", timestamp=np.datetime64(nanos, "ns"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Transforms miss source playback

Generated TFMessage timestamps belong to individual transforms, but this callback sets source only from a top-level header. The resulting transforms are recorded on log_time and log_tick, not source, while stamped images and poses are recorded on source. During source playback, Rerun cannot retrieve the transform edges needed to place that sensor data correctly.

A real bridge recording confirmed that all three transform updates were unavailable at their source timestamps, including latest-at queries, while the corresponding images and poses remained available. Set source from each transform’s timestamp when logging it, then restore or clear the timeline state. This playback failure should be fixed before merging.

Knowledge Base Used: Rerun visualization bridge

Artifacts

Executed generated-message bridge reproduction

  • The executed script sends generated TF, pose, and image updates through the production bridge and records current behavior plus an external timing control, without modifying production.

Executed source-timeline query and higher-snapshot checks

  • The executed inspection script samples actual TF availability with latest-at filling, checks static and log-time controls, and compares scoped higher-PR functions, confirming the failure and absence of a supplied fix.

Executed inspection command wrapper

  • The executed wrapper records the exact inspection command, working directory, exit code, and captured output, making the successful verification reproducible.

Observed TF queries and higher-PR comparison output

  • The captured successful run shows null source-timeline TF values before, correct values with the timing control, working static/log-time controls, and identical relevant functions in all seven higher heads.

Real Rerun recording from current PR4733 bridge

  • Actual generated messages were logged through the current bridge into this recording, whose TF values are unavailable on the source timeline.

Real Rerun recording with external TF timing control

  • The same generated message sequence was recorded with source externally assigned around each TF update, making all three TF translations available at their source timestamps.

View artifacts

TREX Ran code and verified through TREX

Comment on lines +24 to +25
const definitions = parse(schema.definition, { ros2: true });
const reader = new MessageReader(definitions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 External schemas lose package context

cdrDecoder ignores schema.type when parsing the complete definition. Generated schemas leave the root unnamed, so the parser cannot use its package to resolve short field names. A valid external message containing a local Point and a nested geometry_msgs/Point throws Cannot unambiguously determine fully-qualified type name for 'Point'; the decoder factory then returns null, and the registry marks the channel unsupported.

This is a non-blocking compatibility gap for external packages with overlapping short type names. Supply the root identity from schema.type before type resolution and add a regression test covering two packages with a Point type.

Artifacts

Decoder and registry reject a valid complete schema

  • Executed the unchanged SDK with the generated schema and binary controls, capturing the exact ambiguity error, null factory result, and undefined registry result.

Decoder and registry succeed with root identity supplied

  • Executed the same fixture with an isolated in-memory root-identity change, showing successful decoding without editing production files.

Full reproduction source and generated binary fixture

  • Captured the complete authored scripts, fixture, registry source, and generator output so the executed reproduction can be inspected and repeated.

Higher-stack source comparisons and exact source citations

  • Compared all supplied heads and printed numbered resolution helpers, showing identical decoder, registry, and exporter blobs across the stack.

View artifacts

TREX Ran code and verified through TREX

@TomCC7
TomCC7 removed this pull request from stack #4743 October 22, 2026 18:51
@TomCC7
TomCC7 added this pull request to stack #4776 October 22, 2026 18:52
@TomCC7
TomCC7 removed this pull request from stack #4776 October 23, 2026 08:47
@TomCC7
TomCC7 added this pull request to stack #4799 October 23, 2026 08:47
@TomCC7
TomCC7 removed this pull request from stack #4799 October 23, 2026 09:19
@TomCC7
TomCC7 added this pull request to stack #4804 October 23, 2026 09:19

This branch was successfully deployed

1 active (outdated) deployment
cachix — aebb8010 Deployed Oct 22, 2026 by TomCC7 via cachix-build-macos #14015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants