rosotacom-dev 2.5.dev55__tar.gz → 2.5.dev57__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/PKG-INFO +1 -1
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/session-configuration.md +29 -2
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/cli.py +141 -5
- rosotacom_dev-2.5.dev57/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +80 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +34 -1
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +17 -3
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +6 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +68 -4
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +4 -4
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +37 -12
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/status_eval.py +11 -2
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/SOURCES.txt +1 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/scm_file_list.json +1 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_generated_topics_have_a_producer.py +30 -4
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_packaged_resources.py +39 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_concurrency.py +98 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_playout_pacer.py +32 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_session_pipeline.py +51 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_eval.py +41 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_overview.py +124 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.devcontainer/devcontainer.json.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.devcontainer/re_generate_devcontainer_json.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/CODEOWNERS +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/actions/setup-just/action.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/dependabot.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/benchmark-calibrate.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/benchmark-gate.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/dev-release.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/image-scan.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/nightly-e2e.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/pr-lightweight.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/pr-merge-gate.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.github/workflows/release.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/.pre-commit-config.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/AGENTS.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/CONCURRENCY.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/CONTRIBUTING.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/DEVELOPMENT_PRINCIPLES.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/MANIFEST.in +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/budgets.jsonl +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/capacity-size-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-nominal-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-fastdds.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-zenoh.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/codecov.yml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/deployment-configuration.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/benchmark-ab.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/benchmark-bag-as-load.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/ci.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/ffmpeg-keyframes.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/delay-alone-no-loss.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/head-of-line-irregularity.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/lighter-message-loss.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/oversubscription-queues-not-losses.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/reorder-becomes-reader-loss.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/repro/decode_chain_repro.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/repro/reorder_drop_repro.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/forensics-report.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/geo-link-map.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/github-repository-settings.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/link-bandwidth.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/link-trace.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/owner-runbook.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/performance-bands.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/playbooks/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/quality-model.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release-notes/TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.1.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.2.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.3.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.4.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/release.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0002-expectation-concepts.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0003-metric-backbone.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0004-network-profiles.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0006-dynamic-qos.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/0007-regression-gate.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/rfcs/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/testing.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/docs/work-items.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/install.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/justfile +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/media/control_center.png +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/media/echo_test.png +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/media/forensics-report-example.png +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/media/host.png +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/media/vehicle.png +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/pyproject.toml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/requirements.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/__init__.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/__main__.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/anonymize.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/benched_set.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/benchmark.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/cli_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/deployment.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/forensics.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/geomap.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/image_cache.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/plots.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/py.typed +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/benched-set.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/.gitignore +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/README.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/__init__.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/profiles.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ros2docker.json.example +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/transit.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom/video_quality.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/requires.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/terminology.md +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_benched_set_registry.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_findings.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_markdown_links.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_ota_source_bundle.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_package_metadata.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_profile_covers_generated_commands.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_pytest_policy.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_readme_examples.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_security_maintenance_config.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_session_configuration_doc.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_session_template_substitution.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/contract/test_workflow_contracts.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/conftest.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_anonymize_e2e.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_ab.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_capacity.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_replay.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_parallel_smoke.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_smoke.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_timeline_stepping.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/e2e/test_video_quality_e2e.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/generate_rmw_matrix.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_anonymize.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_benched_set.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_catmux_pane_logging.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_cli.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_cli_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_container_shaping.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_deployment.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_field_profiles.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_forensics.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_geomap.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_image_cache.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_link_trace.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_link_configs.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_plots.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_pub_sub_pair_types.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_qos_fallback_logging.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_relay_optional_parameters.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_overview_observer.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_transit.py +0 -0
- {rosotacom_dev-2.5.dev55 → rosotacom_dev-2.5.dev57}/tests/unit/test_video_quality.py +0 -0
|
@@ -720,8 +720,35 @@ ros2 run com_py playout_pacer --ros-args \
|
|
|
720
720
|
-p target_ms:=350.0 -p adaptive:=true
|
|
721
721
|
```
|
|
722
722
|
|
|
723
|
-
It republishes on `<topic>/paced` plus
|
|
724
|
-
`.../paced/queue_depth`
|
|
723
|
+
It republishes on `<topic>/paced` plus three topics next to it:
|
|
724
|
+
`.../paced/budget_ms` and `.../paced/queue_depth` at 1 Hz, and
|
|
725
|
+
`.../paced/hold_ms` with every release, carrying how long *that* message was
|
|
726
|
+
actually held.
|
|
727
|
+
|
|
728
|
+
##### What the status overview does with it
|
|
729
|
+
|
|
730
|
+
A paced stage's age is the link plus a delay this session asked for, and one
|
|
731
|
+
number cannot be judged against a threshold meant for the link. So the
|
|
732
|
+
generator names the pacer's report on the stage it explains
|
|
733
|
+
(`paced_hold_topic` in `pipeline_spec.yaml`), and `status_overview` takes the
|
|
734
|
+
hold back out before classifying:
|
|
735
|
+
|
|
736
|
+
```
|
|
737
|
+
native_in FLOWING .../ffmpeg/compressed 10.0Hz 286ms(link 46+paced 240) ...
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
`latency_ms` stays the age on screen; `link_latency_ms` and `pacing_hold_ms`
|
|
741
|
+
are the split, and the quality verdict uses the former. Without this the
|
|
742
|
+
default 200 ms bad-threshold — set before anything on a link was held on
|
|
743
|
+
purpose — marks every healthy paced stream BAD, which is how an operator learns
|
|
744
|
+
to stop reading the panel.
|
|
745
|
+
|
|
746
|
+
It is the *applied* hold that is subtracted, never the configured budget: a
|
|
747
|
+
message that arrived later than its release time passes straight through, is
|
|
748
|
+
reported as held for 0 ms, and still flags. A budget-based correction would
|
|
749
|
+
credit that message a buffer it never received and quietly stop reporting a slow
|
|
750
|
+
link. Before the pacer has reported at all, nothing is subtracted — neither zero
|
|
751
|
+
nor the budget is a safe guess.
|
|
725
752
|
|
|
726
753
|
`type: foxglove` supports (CompressedVideo):
|
|
727
754
|
- `gop_size` (int)
|
|
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
import argparse
|
|
13
13
|
import base64
|
|
14
14
|
import binascii
|
|
15
|
+
import contextlib
|
|
15
16
|
import getpass
|
|
16
17
|
import hashlib
|
|
17
18
|
import importlib
|
|
@@ -23,12 +24,13 @@ import os
|
|
|
23
24
|
import re
|
|
24
25
|
import shlex
|
|
25
26
|
import shutil
|
|
27
|
+
import signal
|
|
26
28
|
import subprocess
|
|
27
29
|
import sys
|
|
28
30
|
import tarfile
|
|
29
31
|
import tempfile
|
|
30
32
|
import time
|
|
31
|
-
from collections.abc import Callable, Mapping, Sequence
|
|
33
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
32
34
|
from dataclasses import dataclass, replace
|
|
33
35
|
from datetime import datetime
|
|
34
36
|
from pathlib import Path
|
|
@@ -2761,12 +2763,49 @@ def _passwordless_watchdog_command(argv: Sequence[str]) -> str:
|
|
|
2761
2763
|
return f"nohup sudo -n {shlex.join(argv)} >/dev/null 2>&1 &"
|
|
2762
2764
|
|
|
2763
2765
|
|
|
2766
|
+
@contextlib.contextmanager
|
|
2767
|
+
def _terminate_via_exception() -> Iterator[None]:
|
|
2768
|
+
"""Make SIGTERM unwind the stack instead of ending the process outright.
|
|
2769
|
+
|
|
2770
|
+
The OTA run already has a `finally` that reverts shaping, collects logs and
|
|
2771
|
+
stops both peers. SIGINT reaches it, because Python raises KeyboardInterrupt.
|
|
2772
|
+
SIGTERM does not: the default disposition ends the process immediately, so
|
|
2773
|
+
`finally` never runs and every peer keeps its containers -- and, worse, any
|
|
2774
|
+
netem qdisc stays armed on the peer's own interface, quietly degrading a
|
|
2775
|
+
machine nobody is looking at.
|
|
2776
|
+
|
|
2777
|
+
SIGTERM is not the exotic case. `timeout` sends it, systemd sends it, and a
|
|
2778
|
+
CI job cancelling a step sends it. On 2026-08-19 a run killed by `timeout`
|
|
2779
|
+
left containers behind on both peers, which then blocked the next run's
|
|
2780
|
+
exclusivity check.
|
|
2781
|
+
|
|
2782
|
+
Raising SystemExit gives the same exit status while letting the cleanup run.
|
|
2783
|
+
The handler is restored on the way out so this does not leak into a caller.
|
|
2784
|
+
"""
|
|
2785
|
+
|
|
2786
|
+
def handler(signum: int, _frame: object) -> None:
|
|
2787
|
+
raise SystemExit(128 + signum)
|
|
2788
|
+
|
|
2789
|
+
try:
|
|
2790
|
+
previous = signal.signal(signal.SIGTERM, handler)
|
|
2791
|
+
except ValueError:
|
|
2792
|
+
# Not the main thread; the parent already owns signal disposition.
|
|
2793
|
+
yield
|
|
2794
|
+
return
|
|
2795
|
+
try:
|
|
2796
|
+
yield
|
|
2797
|
+
finally:
|
|
2798
|
+
signal.signal(signal.SIGTERM, previous)
|
|
2799
|
+
|
|
2800
|
+
|
|
2764
2801
|
def _ota_conflict_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
2765
2802
|
"""Fail-safe preflight: OTA runs require exclusive control of the peers.
|
|
2766
2803
|
|
|
2767
|
-
Aborts when a peer already runs rosotacom containers
|
|
2768
|
-
shaping (netem/tbf/htb) on the data interface toward another peer
|
|
2769
|
-
|
|
2804
|
+
Aborts when a peer already runs rosotacom containers, has active network
|
|
2805
|
+
shaping (netem/tbf/htb) on the data interface toward another peer, or is
|
|
2806
|
+
carrying so much unrelated load that a measurement taken on it would not
|
|
2807
|
+
mean anything. The first two signal an OTA run that is still active or was
|
|
2808
|
+
not cleaned up; the third signals a machine somebody else is using.
|
|
2770
2809
|
"""
|
|
2771
2810
|
from .network_shaper import ota_interface_from_route
|
|
2772
2811
|
|
|
@@ -2820,6 +2859,59 @@ def _ota_conflict_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
2820
2859
|
)
|
|
2821
2860
|
|
|
2822
2861
|
|
|
2862
|
+
# A run on a machine somebody else is saturating does not fail — it succeeds
|
|
2863
|
+
# and reports numbers that describe the contention rather than the link. That
|
|
2864
|
+
# is the expensive kind of wrong, because nothing in the artefact says so
|
|
2865
|
+
# afterwards.
|
|
2866
|
+
#
|
|
2867
|
+
# The thresholds are per-CPU, so they read the same on an 8-core laptop and a
|
|
2868
|
+
# 32-thread desktop. At 2.0 the run stops: with twice as many runnable threads
|
|
2869
|
+
# as CPUs, a thread waits on average as long as it runs, which lands directly
|
|
2870
|
+
# in the scheduling latency a transport measurement is trying to resolve.
|
|
2871
|
+
_LOAD_WARN_PER_CPU = 0.7
|
|
2872
|
+
_LOAD_REFUSE_PER_CPU = 2.0
|
|
2873
|
+
|
|
2874
|
+
|
|
2875
|
+
def _ota_load_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
2876
|
+
"""Refuse to measure on a peer that is busy with someone else's work."""
|
|
2877
|
+
for peer in plan.peers.values():
|
|
2878
|
+
result = _ota_run(
|
|
2879
|
+
peer,
|
|
2880
|
+
"cut -d' ' -f1-3 /proc/loadavg; nproc",
|
|
2881
|
+
label=f"{peer.name}: host load",
|
|
2882
|
+
dry_run=dry_run,
|
|
2883
|
+
batch=True,
|
|
2884
|
+
check=False,
|
|
2885
|
+
)
|
|
2886
|
+
if dry_run or result.returncode != 0:
|
|
2887
|
+
continue
|
|
2888
|
+
lines = (result.stdout or "").split()
|
|
2889
|
+
if len(lines) < 4:
|
|
2890
|
+
continue
|
|
2891
|
+
try:
|
|
2892
|
+
load1, load5, load15 = (float(value) for value in lines[:3])
|
|
2893
|
+
cpus = max(1, int(lines[3]))
|
|
2894
|
+
except ValueError:
|
|
2895
|
+
continue
|
|
2896
|
+
# load5 as well as load1: a run takes minutes, and a machine that was
|
|
2897
|
+
# busy a moment ago is about to be busy again.
|
|
2898
|
+
ratio = max(load1, load5) / cpus
|
|
2899
|
+
# Two decimals to match /proc/loadavg and uptime, so the number in an
|
|
2900
|
+
# artefact can be compared against what the machine reports itself.
|
|
2901
|
+
summary = f"load {load1:.2f}/{load5:.2f}/{load15:.2f} on {cpus} CPUs ({ratio:.2f} per CPU)"
|
|
2902
|
+
if ratio >= _LOAD_REFUSE_PER_CPU:
|
|
2903
|
+
raise RuntimeError(
|
|
2904
|
+
f"Peer {peer.name} is carrying unrelated load: {summary}.\n"
|
|
2905
|
+
"A measurement taken here would describe the contention, not the link.\n"
|
|
2906
|
+
"Wait for the machine to go quiet, measure on another pair, or — if this "
|
|
2907
|
+
"load is yours and expected — pass --skip-conflict-check."
|
|
2908
|
+
)
|
|
2909
|
+
if ratio >= _LOAD_WARN_PER_CPU:
|
|
2910
|
+
print(f"! {peer.name}: {summary} — comparisons survive if runs are interleaved, absolute latencies do not")
|
|
2911
|
+
else:
|
|
2912
|
+
print(f"+ {peer.name}: {summary}")
|
|
2913
|
+
|
|
2914
|
+
|
|
2823
2915
|
def _ota_preflight(
|
|
2824
2916
|
plan: OtaSmokePlan,
|
|
2825
2917
|
*,
|
|
@@ -2924,6 +3016,7 @@ def _ota_preflight(
|
|
|
2924
3016
|
|
|
2925
3017
|
if check_conflicts:
|
|
2926
3018
|
_ota_conflict_check(plan, dry_run=dry_run)
|
|
3019
|
+
_ota_load_check(plan, dry_run=dry_run)
|
|
2927
3020
|
|
|
2928
3021
|
|
|
2929
3022
|
_OTA_STAGE_EXCLUDES = {
|
|
@@ -3137,6 +3230,7 @@ def _ota_verify_checkouts(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
3137
3230
|
distinct one-line failure here, before any container starts, rather than a
|
|
3138
3231
|
confusing one an hour later.
|
|
3139
3232
|
"""
|
|
3233
|
+
peer_profiles: dict[str, list[str]] = {}
|
|
3140
3234
|
for peer in plan.peers.values():
|
|
3141
3235
|
checkout = shlex.quote(_ota_peer_workdir(plan, peer))
|
|
3142
3236
|
project = shlex.quote(plan.project)
|
|
@@ -3174,6 +3268,46 @@ def _ota_verify_checkouts(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
3174
3268
|
# a real hazard, but it is also a legitimate mid-migration state,
|
|
3175
3269
|
# and a check that refuses it would be the wrong place to find out.
|
|
3176
3270
|
print(f"+ {peer.name}: rosotacom {version.stdout.strip() or 'unknown'}")
|
|
3271
|
+
profiles = _ota_run(
|
|
3272
|
+
peer,
|
|
3273
|
+
f"{OTA_CHECKOUT_PATH_PREFIX}"
|
|
3274
|
+
'ls "$(rosotacom resources path ws)"/ota_configs/*.template '
|
|
3275
|
+
"2>/dev/null | xargs -r -n1 basename | sort | tr '\\n' ' '",
|
|
3276
|
+
label=f"{peer.name}: OTA profile templates",
|
|
3277
|
+
dry_run=dry_run,
|
|
3278
|
+
batch=True,
|
|
3279
|
+
check=False,
|
|
3280
|
+
)
|
|
3281
|
+
if not dry_run and profiles.returncode == 0:
|
|
3282
|
+
peer_profiles[peer.name] = profiles.stdout.split()
|
|
3283
|
+
|
|
3284
|
+
# Unlike the version, this one refuses. A peer whose installed stack lacks
|
|
3285
|
+
# the template a session names does not fail loudly: the renderer errors,
|
|
3286
|
+
# the profile file is left unusable, and the DDS stack silently falls back
|
|
3287
|
+
# to its own defaults — multicast discovery, which no tunnelled OTA link
|
|
3288
|
+
# carries. The run then completes and reports zero delivered messages, which
|
|
3289
|
+
# reads as a middleware that does not work. That is exactly what happened on
|
|
3290
|
+
# 2026-08-19, when #299 made fastdds_tuned.xml the Fast DDS default while
|
|
3291
|
+
# the deployed stack predated the template: eight runs, all recorded as
|
|
3292
|
+
# evidence about Fast DDS, none of which had ever configured it.
|
|
3293
|
+
if len(peer_profiles) > 1:
|
|
3294
|
+
names = sorted(peer_profiles)
|
|
3295
|
+
reference = set(peer_profiles[names[0]])
|
|
3296
|
+
for name in names[1:]:
|
|
3297
|
+
missing = reference - set(peer_profiles[name])
|
|
3298
|
+
extra = set(peer_profiles[name]) - reference
|
|
3299
|
+
if missing or extra:
|
|
3300
|
+
detail = []
|
|
3301
|
+
if missing:
|
|
3302
|
+
detail.append(f"absent on {name}: {', '.join(sorted(missing))}")
|
|
3303
|
+
if extra:
|
|
3304
|
+
detail.append(f"only on {name}: {', '.join(sorted(extra))}")
|
|
3305
|
+
raise RuntimeError(
|
|
3306
|
+
f"Peers disagree about their OTA profile templates ({'; '.join(detail)}). "
|
|
3307
|
+
"A missing template renders nothing and the DDS stack falls back to "
|
|
3308
|
+
"defaults, so the link would come up and carry no data. Bring the peers "
|
|
3309
|
+
"to the same rosotacom before measuring."
|
|
3310
|
+
)
|
|
3177
3311
|
|
|
3178
3312
|
|
|
3179
3313
|
def _ota_prepare_hosts(args: argparse.Namespace, runtime: RuntimeConfig, plan: OtaSmokePlan) -> None:
|
|
@@ -4159,6 +4293,7 @@ def _start_interactive_ota_smoke(args: argparse.Namespace) -> int:
|
|
|
4159
4293
|
|
|
4160
4294
|
if not getattr(args, "skip_preflight", False) and not getattr(args, "skip_conflict_check", False):
|
|
4161
4295
|
_ota_conflict_check(plan, dry_run=dry_run)
|
|
4296
|
+
_ota_load_check(plan, dry_run=dry_run)
|
|
4162
4297
|
_ota_prepare_hosts(args, runtime, plan)
|
|
4163
4298
|
instance = _resolve_session_instance(
|
|
4164
4299
|
runtime,
|
|
@@ -4355,7 +4490,8 @@ def ota_smoke(args: argparse.Namespace) -> int:
|
|
|
4355
4490
|
"session). Run the non-interactive ota-smoke to shape the link, or apply the profile manually."
|
|
4356
4491
|
)
|
|
4357
4492
|
return _start_interactive_ota_smoke(args)
|
|
4358
|
-
|
|
4493
|
+
with _terminate_via_exception():
|
|
4494
|
+
return _start_noninteractive_ota_smoke(args)
|
|
4359
4495
|
|
|
4360
4496
|
|
|
4361
4497
|
def _scenario_name_completer(
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Local-domain Fast DDS configuration: pin the loopback locators, change nothing else.
|
|
4
|
+
|
|
5
|
+
This is the Fast DDS counterpart to cyclonedds_local_participants.xml, and it
|
|
6
|
+
exists because the two stacks fail differently when nothing is configured.
|
|
7
|
+
CycloneDDS talks between containers on a host out of the box. Fast DDS does
|
|
8
|
+
not, and the way it fails is quiet: the publisher reports a matched
|
|
9
|
+
subscriber, the subscriber reports zero messages, and the session's status
|
|
10
|
+
shows the last stage IDLE with `pub=1` next to it — a pipeline that looks
|
|
11
|
+
connected and moves nothing.
|
|
12
|
+
|
|
13
|
+
Measured on lamborghini, 2026-08-19, two containers on `--network host`,
|
|
14
|
+
84 B at 10 Hz, ~80 samples per run:
|
|
15
|
+
|
|
16
|
+
no profile at all .................................. 0 of 80
|
|
17
|
+
+ shared memory off (useBuiltinTransports=false) ... 0 of 80
|
|
18
|
+
+ a unicast initial peer on 127.0.0.1 .............. 0 of 80
|
|
19
|
+
+ explicit unicast locator lists ................... 79 of 80
|
|
20
|
+
locator lists ALONE, shared memory left on ......... 79 of 80
|
|
21
|
+
(control) CycloneDDS, no profile ................... 79 of 80
|
|
22
|
+
|
|
23
|
+
So the locator lists are the whole fix, and shared memory is a red herring —
|
|
24
|
+
worth stating because turning SHM off is the obvious first guess and it
|
|
25
|
+
changes nothing here.
|
|
26
|
+
|
|
27
|
+
Why they are load-bearing: with no locator list Fast DDS binds every interface
|
|
28
|
+
it can enumerate and announces all of them. A developer machine running Docker
|
|
29
|
+
has dozens of `br-*` bridges, most of them DOWN, and a peer that walks that
|
|
30
|
+
list can settle on one that carries nothing. Naming 127.0.0.1 makes the
|
|
31
|
+
announcement match the one path that is certain to work between processes on
|
|
32
|
+
a shared network namespace.
|
|
33
|
+
|
|
34
|
+
Scope, and its one limit: this pins the LOCAL domain, which is host-local by
|
|
35
|
+
definition, so loopback is enough — and it is more robust than the host's
|
|
36
|
+
routable address, which depends on which interface is up. It assumes the
|
|
37
|
+
session's containers share a network namespace (`--network host`, which the
|
|
38
|
+
ros2docker profiles set). A container on its own bridge network has its own
|
|
39
|
+
127.0.0.1 and would need its address named here instead.
|
|
40
|
+
|
|
41
|
+
Nothing else is set: no interface allowlist, no transport override, no
|
|
42
|
+
discovery tuning. Unlike the OTA templates this profile is not trying to cross
|
|
43
|
+
a tunnel.
|
|
44
|
+
-->
|
|
45
|
+
<dds xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
|
|
46
|
+
<profiles>
|
|
47
|
+
<participant profile_name="rosotacom_fastdds_local" is_default_profile="true">
|
|
48
|
+
<rtps>
|
|
49
|
+
<defaultUnicastLocatorList>
|
|
50
|
+
<locator>
|
|
51
|
+
<udpv4>
|
|
52
|
+
<address>127.0.0.1</address>
|
|
53
|
+
<port>0</port>
|
|
54
|
+
</udpv4>
|
|
55
|
+
</locator>
|
|
56
|
+
</defaultUnicastLocatorList>
|
|
57
|
+
|
|
58
|
+
<builtin>
|
|
59
|
+
<metatrafficUnicastLocatorList>
|
|
60
|
+
<locator>
|
|
61
|
+
<udpv4>
|
|
62
|
+
<address>127.0.0.1</address>
|
|
63
|
+
<port>0</port>
|
|
64
|
+
</udpv4>
|
|
65
|
+
</locator>
|
|
66
|
+
</metatrafficUnicastLocatorList>
|
|
67
|
+
|
|
68
|
+
<initialPeersList>
|
|
69
|
+
<locator>
|
|
70
|
+
<udpv4>
|
|
71
|
+
<address>127.0.0.1</address>
|
|
72
|
+
<port>0</port>
|
|
73
|
+
</udpv4>
|
|
74
|
+
</locator>
|
|
75
|
+
</initialPeersList>
|
|
76
|
+
</builtin>
|
|
77
|
+
</rtps>
|
|
78
|
+
</participant>
|
|
79
|
+
</profiles>
|
|
80
|
+
</dds>
|
|
@@ -12,6 +12,8 @@ get_fastdds_easy_mode_xml.py, get_cyclonedds_xml.py).
|
|
|
12
12
|
import argparse
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
KNOWN_PLACEHOLDERS = {"#host_ip", "#peer", "#easy_mode_ip", "#spdp_interval"}
|
|
@@ -204,6 +206,16 @@ if __name__ == "__main__":
|
|
|
204
206
|
"-e", "--easy-mode-ip", dest="easy_mode_ip",
|
|
205
207
|
help="Resolved Fast DDS Easy Mode address (#easy_mode_ip).",
|
|
206
208
|
)
|
|
209
|
+
parser.add_argument(
|
|
210
|
+
"-o", "--output", dest="output",
|
|
211
|
+
help=(
|
|
212
|
+
"Write the rendered XML to this path, atomically. Prefer this over a shell "
|
|
213
|
+
"redirect: `> file` truncates the target before this script runs, so a "
|
|
214
|
+
"failure here leaves a zero-byte profile behind, and a zero-byte profile is "
|
|
215
|
+
"not an error to a DDS stack — it falls back to its defaults and the link "
|
|
216
|
+
"comes up carrying nothing."
|
|
217
|
+
),
|
|
218
|
+
)
|
|
207
219
|
parser.add_argument(
|
|
208
220
|
"--spdp-interval",
|
|
209
221
|
dest="spdp_interval",
|
|
@@ -211,4 +223,25 @@ if __name__ == "__main__":
|
|
|
211
223
|
help=f"CycloneDDS SPDP interval duration (#spdp_interval, default: {DEFAULT_SPDP_INTERVAL}).",
|
|
212
224
|
)
|
|
213
225
|
args = parser.parse_args()
|
|
214
|
-
|
|
226
|
+
output = args.output
|
|
227
|
+
rendered = main(**{k: v for k, v in vars(args).items()
|
|
228
|
+
if v is not None and k != "output"})
|
|
229
|
+
if not output:
|
|
230
|
+
print(rendered)
|
|
231
|
+
raise SystemExit(0)
|
|
232
|
+
if not rendered.strip():
|
|
233
|
+
raise SystemExit(f"Refusing to write an empty profile to {output}.")
|
|
234
|
+
directory = os.path.dirname(os.path.abspath(output)) or "."
|
|
235
|
+
os.makedirs(directory, exist_ok=True)
|
|
236
|
+
handle, temporary = tempfile.mkstemp(dir=directory, suffix=".partial")
|
|
237
|
+
try:
|
|
238
|
+
with os.fdopen(handle, "w", encoding="utf-8") as stream:
|
|
239
|
+
stream.write(rendered)
|
|
240
|
+
os.replace(temporary, output)
|
|
241
|
+
except BaseException:
|
|
242
|
+
try:
|
|
243
|
+
os.unlink(temporary)
|
|
244
|
+
except OSError:
|
|
245
|
+
pass
|
|
246
|
+
raise
|
|
247
|
+
print(f"Wrote {output} ({len(rendered)} bytes).", file=sys.stderr)
|
|
@@ -8,7 +8,16 @@ Inserted between the OTA unwrapper and a decoder chain it turns network jitter
|
|
|
8
8
|
into a constant, bounded age instead of visible stutter.
|
|
9
9
|
|
|
10
10
|
Debug topics next to the output: ``.../paced/budget_ms`` (current delay
|
|
11
|
-
budget) and ``.../paced/queue_depth
|
|
11
|
+
budget) and ``.../paced/queue_depth``, both at 1 Hz, and ``.../paced/hold_ms``
|
|
12
|
+
— published with *every* release, carrying how long that message was actually
|
|
13
|
+
held.
|
|
14
|
+
|
|
15
|
+
``hold_ms`` exists so the status overview can tell the link's contribution from
|
|
16
|
+
this node's. Deliberately the applied hold rather than the budget: a message
|
|
17
|
+
that arrived later than its release time passes straight through and is held
|
|
18
|
+
for 0 ms, so a genuinely slow link cannot be credited with a buffer it never
|
|
19
|
+
used. Subtracting the *budget* would do exactly that, and would quietly stop
|
|
20
|
+
reporting the problem it exists to make visible.
|
|
12
21
|
"""
|
|
13
22
|
|
|
14
23
|
from __future__ import annotations
|
|
@@ -62,6 +71,7 @@ class PlayoutPacerNode(Node):
|
|
|
62
71
|
self._pub = self.create_publisher(msg_cls, out_topic, qos)
|
|
63
72
|
self._budget_pub = self.create_publisher(Float64, out_topic + "/budget_ms", 10)
|
|
64
73
|
self._depth_pub = self.create_publisher(Int32, out_topic + "/queue_depth", 10)
|
|
74
|
+
self._hold_pub = self.create_publisher(Float64, out_topic + "/hold_ms", 10)
|
|
65
75
|
self.create_subscription(msg_cls, topic, self._on_message, qos)
|
|
66
76
|
# 2 ms release tick: bounds added jitter to well below frame cadence.
|
|
67
77
|
self.create_timer(0.002, self._release_due)
|
|
@@ -72,12 +82,16 @@ class PlayoutPacerNode(Node):
|
|
|
72
82
|
now = time.time()
|
|
73
83
|
stamp = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9
|
|
74
84
|
release = self.schedule.on_message(stamp, now)
|
|
75
|
-
self._queue.append((release, msg))
|
|
85
|
+
self._queue.append((release, msg, now))
|
|
76
86
|
|
|
77
87
|
def _release_due(self) -> None:
|
|
78
88
|
now = time.time()
|
|
79
89
|
while self._queue and self._queue[0][0] <= now:
|
|
80
|
-
self.
|
|
90
|
+
release, msg, arrival = self._queue.popleft()
|
|
91
|
+
self._pub.publish(msg)
|
|
92
|
+
# What this node added, not what it was allowed to add. Clamped
|
|
93
|
+
# because a late message's release time is already in the past.
|
|
94
|
+
self._hold_pub.publish(Float64(data=max(0.0, (release - arrival) * 1000.0)))
|
|
81
95
|
|
|
82
96
|
def _publish_debug(self) -> None:
|
|
83
97
|
self._budget_pub.publish(Float64(data=float(self.schedule.budget_ms)))
|
|
@@ -71,6 +71,7 @@ from com_py.ffmpeg_flags import FFMPEG_PACKET_TYPE, parse_keyframe_flag
|
|
|
71
71
|
from com_py.link_bytes import LinkByteSampler, resolve_link_interface
|
|
72
72
|
from com_py.link_trace import LinkTraceRecorder
|
|
73
73
|
from com_py.status_overview_core import (
|
|
74
|
+
SCALAR_MSG_TYPE,
|
|
74
75
|
STARTUP_GRACE_S,
|
|
75
76
|
ClockOffsetEstimator,
|
|
76
77
|
StageObservation,
|
|
@@ -309,6 +310,11 @@ class StageObserver(Node):
|
|
|
309
310
|
"t_com_out": now_ros_s,
|
|
310
311
|
"keyframe": keyframe,
|
|
311
312
|
}
|
|
313
|
+
elif obs.type_str == SCALAR_MSG_TYPE:
|
|
314
|
+
# Not a stage: a number this node reads to interpret one. The
|
|
315
|
+
# pacing hold arrives here (`.../paced/hold_ms`); it carries no
|
|
316
|
+
# header, so there is no latency to record, only the value.
|
|
317
|
+
obs.last_value = float(msg.data)
|
|
312
318
|
else:
|
|
313
319
|
header = getattr(msg, "header", None)
|
|
314
320
|
stamp = getattr(header, "stamp", None) if header is not None else None
|
|
@@ -61,6 +61,12 @@ GOOD = "GOOD"
|
|
|
61
61
|
DEGRADED = "DEGRADED"
|
|
62
62
|
BAD = "BAD"
|
|
63
63
|
|
|
64
|
+
#: A stage whose age includes a delay this session asked for names the topic on
|
|
65
|
+
#: which the node responsible reports what it actually held. Set by the
|
|
66
|
+
#: generator for a decode that reads a playout pacer's output.
|
|
67
|
+
PACED_HOLD_KEY = "paced_hold_topic"
|
|
68
|
+
SCALAR_MSG_TYPE = "std_msgs/msg/Float64"
|
|
69
|
+
|
|
64
70
|
# Topic-level rollup
|
|
65
71
|
OK = "OK"
|
|
66
72
|
PARTIAL = "PARTIAL"
|
|
@@ -371,6 +377,9 @@ class StageObservation:
|
|
|
371
377
|
last_recv_wall: Optional[float] = None
|
|
372
378
|
last_delay_s: Optional[float] = None
|
|
373
379
|
last_raw_delay_s: Optional[float] = None
|
|
380
|
+
#: Payload of the last message, for the scalar topics this node reads as
|
|
381
|
+
#: numbers rather than observes as stages (the pacing hold).
|
|
382
|
+
last_value: Optional[float] = None
|
|
374
383
|
last_rtt_s: Optional[float] = None
|
|
375
384
|
last_clock_offset_s: Optional[float] = None
|
|
376
385
|
# (t_mono, size_bytes, delay_s_or_None)
|
|
@@ -673,6 +682,21 @@ class StatusAggregator:
|
|
|
673
682
|
self._startup_pending: Optional[Tuple[float, set]] = None
|
|
674
683
|
|
|
675
684
|
# -- observation lookup --
|
|
685
|
+
def _pacing_hold_ms(self, stage: Dict[str, Any]) -> Optional[float]:
|
|
686
|
+
"""How long this session deliberately held the last message of a stage.
|
|
687
|
+
|
|
688
|
+
None whenever the answer is not actually known -- no pacer named, or the
|
|
689
|
+
pacer has not reported yet. Guessing zero would read a paced picture as
|
|
690
|
+
a slow link; guessing the configured budget would do the opposite and
|
|
691
|
+
credit a hold that a late message never received.
|
|
692
|
+
"""
|
|
693
|
+
hold_topic = stage.get(PACED_HOLD_KEY)
|
|
694
|
+
if not hold_topic:
|
|
695
|
+
return None
|
|
696
|
+
observer = self._observers.get("local")
|
|
697
|
+
observation = observer.observations.get(str(hold_topic)) if observer else None
|
|
698
|
+
return None if observation is None else observation.last_value
|
|
699
|
+
|
|
676
700
|
def _obs_for(self, stage: Dict[str, Any]) -> Optional[StageObservation]:
|
|
677
701
|
domain = stage.get("domain", "local")
|
|
678
702
|
observer = self._observers.get(domain) or self._observers.get("local")
|
|
@@ -714,6 +738,9 @@ class StatusAggregator:
|
|
|
714
738
|
"observation": "graph" if obs is not None and obs.graph_only else "payload",
|
|
715
739
|
"inferred_from": None,
|
|
716
740
|
"latched": str((expect or {}).get("mode", "")).strip().lower() == "latched",
|
|
741
|
+
# Split of `latency_ms` for a stage this session delays on purpose.
|
|
742
|
+
"pacing_hold_ms": None,
|
|
743
|
+
"link_latency_ms": None,
|
|
717
744
|
}
|
|
718
745
|
if obs is None:
|
|
719
746
|
return result
|
|
@@ -727,6 +754,10 @@ class StatusAggregator:
|
|
|
727
754
|
result["age_s"] = round(m["age_s"], 3) if m["age_s"] is not None else None
|
|
728
755
|
if m["last_delay_s"] is not None:
|
|
729
756
|
result["latency_ms"] = round(m["last_delay_s"] * 1000.0, 1)
|
|
757
|
+
hold_ms = self._pacing_hold_ms(stage)
|
|
758
|
+
if hold_ms is not None:
|
|
759
|
+
result["pacing_hold_ms"] = round(hold_ms, 1)
|
|
760
|
+
result["link_latency_ms"] = round(max(0.0, result["latency_ms"] - hold_ms), 1)
|
|
730
761
|
if m["last_raw_delay_s"] is not None:
|
|
731
762
|
result["latency_uncorrected_ms"] = round(m["last_raw_delay_s"] * 1000.0, 1)
|
|
732
763
|
if m["last_rtt_s"] is not None:
|
|
@@ -759,7 +790,9 @@ class StatusAggregator:
|
|
|
759
790
|
return result
|
|
760
791
|
|
|
761
792
|
result["state"] = FLOWING
|
|
762
|
-
quality, reason = self._classify_quality(
|
|
793
|
+
quality, reason = self._classify_quality(
|
|
794
|
+
m, expected_hz, expect, pacing_hold_ms=result["pacing_hold_ms"]
|
|
795
|
+
)
|
|
763
796
|
result["quality"] = quality
|
|
764
797
|
result["quality_reason"] = reason
|
|
765
798
|
return result
|
|
@@ -823,7 +856,12 @@ class StatusAggregator:
|
|
|
823
856
|
self._copy_inferred_activity(stage_result, source)
|
|
824
857
|
|
|
825
858
|
def _classify_quality(
|
|
826
|
-
self,
|
|
859
|
+
self,
|
|
860
|
+
m: Dict[str, Any],
|
|
861
|
+
expected_hz: Optional[float],
|
|
862
|
+
expect: Optional[Dict[str, Any]] = None,
|
|
863
|
+
*,
|
|
864
|
+
pacing_hold_ms: Optional[float] = None,
|
|
827
865
|
) -> Tuple[str, Optional[str]]:
|
|
828
866
|
expect = expect or {}
|
|
829
867
|
hz_exp = expect.get("hz") or {}
|
|
@@ -833,8 +871,17 @@ class StatusAggregator:
|
|
|
833
871
|
# Latency: a declared `expect.latency_ms.max` overrides the global bad
|
|
834
872
|
# threshold; the good band stays the default so a tight contract still
|
|
835
873
|
# surfaces a DEGRADED before BAD where it makes sense.
|
|
874
|
+
#
|
|
875
|
+
# A stage the session delays on purpose is judged on what is left after
|
|
876
|
+
# that delay is taken back out. Otherwise a threshold calibrated for a
|
|
877
|
+
# link measures a buffer, which reads as a fault on a healthy stream and
|
|
878
|
+
# teaches an operator to stop believing the panel. The subtracted value
|
|
879
|
+
# is the hold actually applied, so a late message -- which the pacer
|
|
880
|
+
# passes straight through -- is credited nothing and still flags.
|
|
836
881
|
bad_lat = float(lat_exp["max"]) if "max" in lat_exp else self._delay_bad_ms
|
|
837
882
|
delay_ms = (m["last_delay_s"] * 1000.0) if m["last_delay_s"] is not None else None
|
|
883
|
+
if delay_ms is not None and pacing_hold_ms is not None:
|
|
884
|
+
delay_ms = max(0.0, delay_ms - pacing_hold_ms)
|
|
838
885
|
if delay_ms is not None:
|
|
839
886
|
if delay_ms >= bad_lat:
|
|
840
887
|
return BAD, "latency"
|
|
@@ -1142,7 +1189,16 @@ class StatusAggregator:
|
|
|
1142
1189
|
if st["state"] in (FLOWING, STALE):
|
|
1143
1190
|
parts = [f"{st['hz']:.1f}Hz"]
|
|
1144
1191
|
if st["latency_ms"] is not None:
|
|
1145
|
-
|
|
1192
|
+
# A paced stage reads as one big number unless the split
|
|
1193
|
+
# is on the same line: what the link cost, and what this
|
|
1194
|
+
# session chose to add on top of it.
|
|
1195
|
+
if st.get("pacing_hold_ms") is not None:
|
|
1196
|
+
parts.append(
|
|
1197
|
+
f"{st['latency_ms']:.0f}ms"
|
|
1198
|
+
f"(link {st['link_latency_ms']:.0f}+paced {st['pacing_hold_ms']:.0f})"
|
|
1199
|
+
)
|
|
1200
|
+
else:
|
|
1201
|
+
parts.append(f"{st['latency_ms']:.0f}ms")
|
|
1146
1202
|
elif st["latency_uncorrected_ms"] is not None:
|
|
1147
1203
|
parts.append(f"{st['latency_uncorrected_ms']:.0f}ms(raw)")
|
|
1148
1204
|
if st["loss_pct"] is not None:
|
|
@@ -1298,7 +1354,12 @@ class StatusAggregator:
|
|
|
1298
1354
|
|
|
1299
1355
|
|
|
1300
1356
|
def collect_stage_topics(spec: Dict[str, Any]) -> Dict[str, Dict[str, Optional[str]]]:
|
|
1301
|
-
"""Return {domain: {topic: type_hint}} from the pipeline spec.
|
|
1357
|
+
"""Return {domain: {topic: type_hint}} from the pipeline spec.
|
|
1358
|
+
|
|
1359
|
+
Includes the pacing-hold topics a stage names: they are not stages of the
|
|
1360
|
+
link -- nothing is delivered on them -- but the aggregator has to read them
|
|
1361
|
+
to tell this session's deliberate delay from the network's.
|
|
1362
|
+
"""
|
|
1302
1363
|
by_domain: Dict[str, Dict[str, Optional[str]]] = {"local": {}, "ota": {}}
|
|
1303
1364
|
for topic_spec in spec.get("topics", []):
|
|
1304
1365
|
type_hint = topic_spec.get("type")
|
|
@@ -1307,6 +1368,9 @@ def collect_stage_topics(spec: Dict[str, Any]) -> Dict[str, Dict[str, Optional[s
|
|
|
1307
1368
|
by_domain.setdefault(domain, {})
|
|
1308
1369
|
existing = by_domain[domain].get(stage["topic"])
|
|
1309
1370
|
by_domain[domain][stage["topic"]] = existing or stage.get("type") or type_hint
|
|
1371
|
+
hold_topic = stage.get(PACED_HOLD_KEY)
|
|
1372
|
+
if hold_topic:
|
|
1373
|
+
by_domain["local"].setdefault(str(hold_topic), SCALAR_MSG_TYPE)
|
|
1310
1374
|
return by_domain
|
|
1311
1375
|
|
|
1312
1376
|
|
|
@@ -292,7 +292,7 @@ common:
|
|
|
292
292
|
before_commands:
|
|
293
293
|
- if [ '${local_domain_id}' != "None" ] && [ -n '${local_domain_id}' ]; then export ROS_DOMAIN_ID='${local_domain_id}'; fi
|
|
294
294
|
- if [ '${rmw_local}' != "None" ]; then case ${rmw_local} in cyclone) export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp;; fastdds) export RMW_IMPLEMENTATION=rmw_fastrtps_cpp; export FASTDDS_BUILTIN_TRANSPORTS=${FASTDDS_BUILTIN_TRANSPORTS:-UDPv4};; zenoh) export RMW_IMPLEMENTATION=rmw_zenoh_cpp;; *) export RMW_IMPLEMENTATION='${rmw_local}';; esac; fi
|
|
295
|
-
- if [ '${local_config_template}' != "None" ] && [ -n '${local_config_template}' ]; then local_args=(--config "${local_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${local_easy_mode_ip}' != "None" ] && [ -n '${local_easy_mode_ip}' ] && local_args+=(--easy-mode-ip "${local_easy_mode_ip}"); [ '${local_spdp_interval}' != "None" ] && [ -n '${local_spdp_interval}' ] && local_args+=(--spdp-interval "${local_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${local_args[@]}"
|
|
295
|
+
- if [ '${local_config_template}' != "None" ] && [ -n '${local_config_template}' ]; then local_args=(--config "${local_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${local_easy_mode_ip}' != "None" ] && [ -n '${local_easy_mode_ip}' ] && local_args+=(--easy-mode-ip "${local_easy_mode_ip}"); [ '${local_spdp_interval}' != "None" ] && [ -n '${local_spdp_interval}' ] && local_args+=(--spdp-interval "${local_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${local_args[@]}" --output "${local_config_file}" || { echo "FATAL - could not render the local DDS profile ${local_config_template}; refusing to start on defaults." >&2; exit 1; }; case ${rmw_local} in cyclone) export CYCLONEDDS_URI="file://${local_config_file}";; fastdds) export FASTDDS_DEFAULT_PROFILES_FILE="${local_config_file}"; export RMW_FASTRTPS_USE_QOS_FROM_XML=1;; esac; fi
|
|
296
296
|
- if [ '${before_command}' != "None" ]; then ${before_command}; fi
|
|
297
297
|
|
|
298
298
|
windows:
|
|
@@ -338,7 +338,7 @@ windows:
|
|
|
338
338
|
if: use_domain_bridge
|
|
339
339
|
splits:
|
|
340
340
|
- commands:
|
|
341
|
-
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}"
|
|
341
|
+
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}" --output "${ota_config_file}" || { echo "FATAL - could not render the OTA DDS profile ${ota_config_template}; refusing to start on defaults." >&2; exit 1; }; case ${rmw_ota} in cyclone) export CYCLONEDDS_URI="file://${ota_config_file}";; fastdds) export FASTDDS_DEFAULT_PROFILES_FILE="${ota_config_file}"; export RMW_FASTRTPS_USE_QOS_FROM_XML=1;; esac; fi
|
|
342
342
|
- ros2 run domain_bridge domain_bridge --wait-for-publisher false ${domain_bridge_config_file}
|
|
343
343
|
- name: IN
|
|
344
344
|
if: in
|
|
@@ -346,7 +346,7 @@ windows:
|
|
|
346
346
|
- commands:
|
|
347
347
|
- if [ '${ota_domain_id}' != "None" ] && [ -n '${ota_domain_id}' ]; then export ROS_DOMAIN_ID='${ota_domain_id}'; fi
|
|
348
348
|
- case ${rmw_ota} in cyclone) export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp;; fastdds) export RMW_IMPLEMENTATION=rmw_fastrtps_cpp; export FASTDDS_BUILTIN_TRANSPORTS=${FASTDDS_BUILTIN_TRANSPORTS:-UDPv4};; zenoh) export RMW_IMPLEMENTATION=rmw_zenoh_cpp;; esac
|
|
349
|
-
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}"
|
|
349
|
+
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}" --output "${ota_config_file}" || { echo "FATAL - could not render the OTA DDS profile ${ota_config_template}; refusing to start on defaults." >&2; exit 1; }; case ${rmw_ota} in cyclone) export CYCLONEDDS_URI="file://${ota_config_file}";; fastdds) export FASTDDS_DEFAULT_PROFILES_FILE="${ota_config_file}"; export RMW_FASTRTPS_USE_QOS_FROM_XML=1;; esac; fi
|
|
350
350
|
# For zenoh_ros2dds the OTA domain is loopback-only (zenoh is the inter-host
|
|
351
351
|
# transport), so confine discovery to localhost instead of statically peering
|
|
352
352
|
# the remote host — otherwise the OTA-domain DDS double-delivers alongside zenoh.
|
|
@@ -373,7 +373,7 @@ windows:
|
|
|
373
373
|
- commands:
|
|
374
374
|
- if [ '${ota_domain_id}' != "None" ] && [ -n '${ota_domain_id}' ]; then export ROS_DOMAIN_ID='${ota_domain_id}'; fi
|
|
375
375
|
- case ${rmw_ota} in cyclone) export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp;; fastdds) export RMW_IMPLEMENTATION=rmw_fastrtps_cpp; export FASTDDS_BUILTIN_TRANSPORTS=${FASTDDS_BUILTIN_TRANSPORTS:-UDPv4};; zenoh) export RMW_IMPLEMENTATION=rmw_zenoh_cpp;; esac
|
|
376
|
-
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}"
|
|
376
|
+
- if [ '${ota_config_template}' != "None" ] && [ -n '${ota_config_template}' ]; then ota_args=(--config "${ota_config_template}" --host-ip "${ip_local}" --peer "${ip_remote}"); [ '${ota_easy_mode_ip}' != "None" ] && [ -n '${ota_easy_mode_ip}' ] && ota_args+=(--easy-mode-ip "${ota_easy_mode_ip}"); [ '${ota_spdp_interval}' != "None" ] && [ -n '${ota_spdp_interval}' ] && ota_args+=(--spdp-interval "${ota_spdp_interval}"); python3 /ws/ota_configs/get_ota_xml.py "${ota_args[@]}" --output "${ota_config_file}" || { echo "FATAL - could not render the OTA DDS profile ${ota_config_template}; refusing to start on defaults." >&2; exit 1; }; case ${rmw_ota} in cyclone) export CYCLONEDDS_URI="file://${ota_config_file}";; fastdds) export FASTDDS_DEFAULT_PROFILES_FILE="${ota_config_file}"; export RMW_FASTRTPS_USE_QOS_FROM_XML=1;; esac; fi
|
|
377
377
|
# For zenoh_ros2dds the OTA domain is loopback-only (zenoh is the inter-host
|
|
378
378
|
# transport), so confine discovery to localhost instead of statically peering
|
|
379
379
|
# the remote host — otherwise the OTA-domain DDS double-delivers alongside zenoh.
|