rosotacom-dev 2.5.dev61__tar.gz → 2.5.dev65__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.dev61 → rosotacom_dev-2.5.dev65}/PKG-INFO +2 -2
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/README.md +1 -1
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/README.md +1 -0
- rosotacom_dev-2.5.dev65/docs/findings/cyclone-fragment-equal-to-max-drops-fragmented-samples.md +89 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +1 -1
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/forensics-report.md +1 -1
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/link-trace.md +10 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/cli.py +278 -3
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/cli_benchmark.py +1 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +12 -1
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +33 -3
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/PKG-INFO +2 -2
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/SOURCES.txt +1 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/scm_file_list.json +1 -0
- rosotacom_dev-2.5.dev65/src/rosotacom_dev.egg-info/scm_version.json +8 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_packaged_resources.py +33 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_cli.py +91 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_concurrency.py +147 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_link_trace.py +72 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_link_configs.py +12 -2
- rosotacom_dev-2.5.dev61/src/rosotacom_dev.egg-info/scm_version.json +0 -8
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.devcontainer/devcontainer.json.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.devcontainer/re_generate_devcontainer_json.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/CODEOWNERS +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/actions/setup-just/action.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/dependabot.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/benchmark-calibrate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/benchmark-gate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/dev-release.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/image-scan.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/nightly-e2e.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/pr-lightweight.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/pr-merge-gate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/release.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.pre-commit-config.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/AGENTS.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/CONCURRENCY.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/CONTRIBUTING.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/DEVELOPMENT_PRINCIPLES.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/MANIFEST.in +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/budgets.jsonl +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/capacity-size-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-nominal-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-fastdds.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-zenoh.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/codecov.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/deployment-configuration.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/benchmark-ab.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/benchmark-bag-as-load.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/ci.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/ffmpeg-keyframes.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/a-transport-element-doubles-every-fastdds-sample.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/delay-alone-no-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/head-of-line-irregularity.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/lifespan-is-enforced-by-one-reader-and-not-the-other.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/lighter-message-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/oversubscription-queues-not-losses.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/reorder-becomes-reader-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/repro/decode_chain_repro.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/repro/reorder_drop_repro.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/geo-link-map.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/github-repository-settings.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/link-bandwidth.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/owner-runbook.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/performance-bands.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/playbooks/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/quality-model.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.1.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.2.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.3.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.4.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0002-expectation-concepts.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0003-metric-backbone.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0004-network-profiles.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0006-dynamic-qos.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0007-regression-gate.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/testing.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/work-items.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/install.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/justfile +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/control_center.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/echo_test.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/forensics-report-example.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/host.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/vehicle.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/pyproject.toml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/requirements.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/session-configuration.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/__main__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/anonymize.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benched_set.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/deployment.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/forensics.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/geomap.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/image_cache.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/plots.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/py.typed +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/benched-set.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/.gitignore +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/profiles.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/ros2docker.lyrical.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.lyrical.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.lyrical.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ros2docker.json.example +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ros2docker.lyrical.json.example +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/status_eval.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/transit.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/video_quality.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/requires.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/terminology.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_benched_set_registry.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_findings.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_generated_topics_have_a_producer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_lyrical_runtime_compat.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_markdown_links.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_ota_source_bundle.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_package_metadata.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_profile_covers_generated_commands.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_pytest_policy.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_readme_examples.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_security_maintenance_config.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_session_configuration_doc.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_session_template_substitution.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_workflow_contracts.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/conftest.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_anonymize_e2e.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_ab.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_capacity.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_replay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_parallel_smoke.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_smoke.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_timeline_stepping.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_video_quality_e2e.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/generate_rmw_matrix.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_anonymize.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benched_set.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_catmux_pane_logging.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_cli_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_container_shaping.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_deployment.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_field_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_forensics.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_geomap.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_image_cache.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_plots.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_pub_sub_pair_types.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_qos_fallback_logging.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_relay_optional_parameters.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ros_node_entity_ownership.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_session_pipeline.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_eval.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_overview.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_overview_observer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_transit.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_video_quality.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rosotacom-dev
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.dev65
|
|
4
4
|
Summary: Host CLI for ROS OTA communication sessions.
|
|
5
5
|
Author: develNor
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -367,7 +367,7 @@ Peer reachability is opt-in because some valid networks block ICMP and peers may
|
|
|
367
367
|
be started in either order. `rosotacom start` accepts the same
|
|
368
368
|
`--require-peer-reachable` policy when a deployment does require it.
|
|
369
369
|
|
|
370
|
-
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
370
|
+
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. The instance's `manifest.yaml` records what produced it: `rosotacom_version`, the effective config SHA, and `source_revision` — the commit, branch and tracked-dirtiness of the session source directory at creation time, so a recording stays attributable to a revision after the checkout has moved on (`null` when the source is not a git repository). Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
371
371
|
|
|
372
372
|
What else a run leaves under `logs/<peer>/`, and which file to open first when a
|
|
373
373
|
peer looked healthy but did nothing:
|
|
@@ -322,7 +322,7 @@ Peer reachability is opt-in because some valid networks block ICMP and peers may
|
|
|
322
322
|
be started in either order. `rosotacom start` accepts the same
|
|
323
323
|
`--require-peer-reachable` policy when a deployment does require it.
|
|
324
324
|
|
|
325
|
-
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
325
|
+
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. The instance's `manifest.yaml` records what produced it: `rosotacom_version`, the effective config SHA, and `source_revision` — the commit, branch and tracked-dirtiness of the session source directory at creation time, so a recording stays attributable to a revision after the checkout has moved on (`null` when the source is not a git repository). Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
326
326
|
|
|
327
327
|
What else a run leaves under `logs/<peer>/`, and which file to open first when a
|
|
328
328
|
peer looked healthy but did nothing:
|
|
@@ -31,6 +31,7 @@ living verification hook.
|
|
|
31
31
|
- [Sender-side transit rows made a dead link report zero loss](sender-rows-make-a-dead-link-look-loss-free.md) - `delivered = rows - lost` counted the sender's own `sent` rows as deliveries, so a topic the receiver never saw summarised as `delivered == expected` at 0.0% loss, with the publisher's timer reported as arrival spacing.
|
|
32
32
|
- [A bridge that learns types from the graph cannot use a transport that does not carry the graph](graph-derived-types-block-a-non-graph-transport.md) - over `zenoh_ros2dds` the payload topic never arrived in either run: the bridge routes a topic once a local reader exists and the reader waited for the bridge, leaving only `Pending=1` in a log.
|
|
33
33
|
- [A Fast DDS datagram cap stops every message larger than it](fastdds-datagram-cap-drops-large-samples.md) - copying CycloneDDS's 1200 B fragment size into the Fast DDS OTA profile makes every sample above the cap never arrive, at 1200 B and at 8192 B, sync and async — while the 84 B heartbeat keeps flowing at 10 Hz.
|
|
34
|
+
- [A fragment sized exactly like the datagram cap stops every fragmented sample](cyclone-fragment-equal-to-max-drops-fragmented-samples.md) - CycloneDDS 11.0.1 delivers nothing above one fragment when `FragmentSize` equals `MaxMessageSize`, where 0.10.5 delivers everything on the identical profile; a 40 B string still crosses, and discovery, endpoint announcement and proxy-writer matching all succeed while no sample arrives.
|
|
34
35
|
- [A lost keyframe silences exactly the next gop_size−2 deltas](keyframe-loss-kills-the-next-deltas.md) - libavcodec emits nothing for them, the last delta decodes with artifacts, the next keyframe restores clean decode; a lost delta never blocks decode — so the display hole is gop_size × frame period, deterministically.
|
|
35
36
|
- [Any transport element in a Fast DDS profile doubles every sample on the wire](a-transport-element-doubles-every-fastdds-sample.md) - 366 datagrams for 180 samples against 183 without it; `<builtinTransports>DEFAULT` is a no-op selection and doubles too, so the trigger is the element's presence — removing it takes the shipped OTA profile from 2.10x the payload to 1.04x at identical delivery.
|
|
36
37
|
- [Lifespan is enforced by one DDS reader and not the other](lifespan-is-enforced-by-one-reader-and-not-the-other.md) - under the same 20% oversubscription both stacks queue alike (472 and 433 delivered at ~1.1-1.2 s); with `lifespan: 0.7` CycloneDDS is unchanged and delivers 9.1% inside the bound, while Fast DDS drops to 4 delivered and never exceeds 570 ms — the policy decides shown-vs-suppressed, not how long the wait is.
|
rosotacom_dev-2.5.dev65/docs/findings/cyclone-fragment-equal-to-max-drops-fragmented-samples.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# A Fragment Sized Exactly Like the Datagram Cap Stops Every Fragmented Sample
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
Setting CycloneDDS's `FragmentSize` **equal to** `MaxMessageSize` makes every
|
|
6
|
+
sample large enough to fragment **never arrive** on CycloneDDS 11.0.1, while the
|
|
7
|
+
identical profile carries them on 0.10.5. Samples small enough to fit in one
|
|
8
|
+
fragment keep flowing on the same link, which is what makes it dangerous: the
|
|
9
|
+
endpoints match, neither side logs anything, and the reader simply never sees a
|
|
10
|
+
sample.
|
|
11
|
+
|
|
12
|
+
An RTPS message carries headers as well as the fragment, so a fragment of
|
|
13
|
+
exactly `MaxMessageSize` cannot be put into a message of `MaxMessageSize`.
|
|
14
|
+
0.10.5 evidently coped with that arithmetic; 11.0.1 does not, and drops
|
|
15
|
+
silently.
|
|
16
|
+
|
|
17
|
+
This is the CycloneDDS sibling of
|
|
18
|
+
[the Fast DDS datagram cap](fastdds-datagram-cap-drops-large-samples.md), found
|
|
19
|
+
from the opposite direction: there the cap was a value copied *from* this
|
|
20
|
+
profile, here the profile's own two values were equal to each other.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
- Host pair / topology: two hosts joined by a point-to-point VPN (`/24`,
|
|
25
|
+
1.3 ms RTT, 0.0% loss measured with three probes each way), one container per
|
|
26
|
+
host from the packaged communication image, `--network host --ipc host`, one
|
|
27
|
+
ROS domain. Not the local smoke rig: on a single host nothing fragments far
|
|
28
|
+
enough to show it.
|
|
29
|
+
- Session: none. A bare `ros2 topic pub -r 1` of a 64 kB
|
|
30
|
+
`std_msgs/msg/String` against a subscriber given the type explicitly — with
|
|
31
|
+
`EnableTopicDiscoveryEndpoints false` a bare `ros2 topic echo <topic>`
|
|
32
|
+
resolves no type and reports nothing, in both versions, which is a trap worth
|
|
33
|
+
naming because it looks exactly like the failure under test.
|
|
34
|
+
- rosotacom SHA: measured on the packaged 2.5.dev61 images; re-runs record
|
|
35
|
+
theirs.
|
|
36
|
+
- Profile: none — no impairment. The failure needs none, which is what makes it
|
|
37
|
+
attributable to the configuration rather than to the link.
|
|
38
|
+
- Seed policy: deterministic (no netem, no random load), `n=1` per variant; the
|
|
39
|
+
effect is present/absent, not a distribution.
|
|
40
|
+
- Stacks: `ros-kilted-cyclonedds` 0.10.5-2noble against
|
|
41
|
+
`ros-lyrical-cyclonedds` 11.0.1-4resolute.
|
|
42
|
+
|
|
43
|
+
## Evidence
|
|
44
|
+
|
|
45
|
+
| profile | 0.10.5 | 11.0.1 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `FragmentSize` 1200 B, `MaxMessageSize` 1200 B | arrives | **nothing** |
|
|
48
|
+
| no size limits at all | — | arrives |
|
|
49
|
+
| `FragmentSize` 1024 B, `MaxMessageSize` 1200 B | — | arrives |
|
|
50
|
+
|
|
51
|
+
- A 40 B string crosses on 11.0.1 with the 1200/1200 profile, so the trigger is
|
|
52
|
+
fragmentation and not the link.
|
|
53
|
+
- The discovery trace of a failing run rules out everything above the data path:
|
|
54
|
+
both participants discover each other across the hosts, both sides announce
|
|
55
|
+
their endpoints, the reading side builds proxy writers for the remote
|
|
56
|
+
participants, and neither side logs a QoS incompatibility or a rejection.
|
|
57
|
+
|
|
58
|
+
Verification: automated:
|
|
59
|
+
`pytest tests/unit/test_ota_link_configs.py::test_only_cyclone_caps_the_datagram_and_fast_dds_must_not`
|
|
60
|
+
asserts the fragment stays strictly below the datagram cap, which is the
|
|
61
|
+
invariant the two numbers exist for. Manual, for the effect itself: one
|
|
62
|
+
container per host from the communication image on one ROS domain, the shipped
|
|
63
|
+
OTA profile with only the three size elements varied, a 64 kB string at 1 Hz,
|
|
64
|
+
and `ros2 topic echo <topic> std_msgs/msg/String --once` on the other side.
|
|
65
|
+
|
|
66
|
+
## Status
|
|
67
|
+
|
|
68
|
+
confirmed, 2026-08-20.
|
|
69
|
+
|
|
70
|
+
Fixed by making `FragmentSize` strictly smaller than `MaxMessageSize` (1024 B
|
|
71
|
+
against 1200 B) in `cyclonedds_tuned.xml.template`. The datagram stays inside
|
|
72
|
+
the same MTU budget the 1200 B was chosen for — only the header room is added —
|
|
73
|
+
and the value works on both versions, so it is one number rather than a branch
|
|
74
|
+
on the distribution.
|
|
75
|
+
|
|
76
|
+
## Publication notes
|
|
77
|
+
|
|
78
|
+
A distribution bump silently changed what a profile *means*, and the profile
|
|
79
|
+
text did not move. That is the reportable part: measurements taken under one ROS
|
|
80
|
+
distribution cannot be presented as the other's on the strength of an identical
|
|
81
|
+
configuration file, because identical configuration is not identical behaviour
|
|
82
|
+
across a DDS major version. State the middleware version beside the profile
|
|
83
|
+
wherever OTA numbers are reported.
|
|
84
|
+
|
|
85
|
+
The second consequence is about what counts as evidence that a link works. A
|
|
86
|
+
heartbeat crossing at 10 Hz with 0.0% loss certified nothing here — the small
|
|
87
|
+
samples were exactly the ones that could not fail. A link check that does not
|
|
88
|
+
include a sample past the fragment threshold does not test the transport the
|
|
89
|
+
payload uses.
|
|
@@ -10,7 +10,7 @@ this dangerous: an 84 B heartbeat crossing at 10 Hz with 0.0% loss is a link
|
|
|
10
10
|
that looks alive from every direction except the one that matters.
|
|
11
11
|
|
|
12
12
|
The obvious reason to set it is symmetry with CycloneDDS, whose OTA profile
|
|
13
|
-
carries this link at
|
|
13
|
+
carries this link at a 1024 B fragment inside a 1200 B datagram and works. The
|
|
14
14
|
symmetry does not transfer: on ROS 2 Kilted with Fast DDS 3.2.4 the cap is the
|
|
15
15
|
single knob that breaks the link, at 1200 B and at 8192 B, with a synchronous
|
|
16
16
|
writer and with an asynchronous one.
|
|
@@ -37,7 +37,7 @@ gracefully when absent:
|
|
|
37
37
|
|---|---|
|
|
38
38
|
| `status.json` | declared topic types (FFMPEGPacket detection) |
|
|
39
39
|
| `link_trace.jsonl` ([link trace recorder](link-trace.md)) | link state around each incident |
|
|
40
|
-
| `manifest.yaml` | instance id
|
|
40
|
+
| `manifest.yaml` | instance id, config SHA, and `source_revision` — the commit of the session source at instance creation |
|
|
41
41
|
| `--profile NAME --profiles-file FILE` | RFC 0004 environment context (static params or timeline steps) |
|
|
42
42
|
| `state_transition` rows in `events.jsonl` | pipeline diagnoses around each incident |
|
|
43
43
|
|
|
@@ -30,6 +30,16 @@ rosotacom ota-smoke 13_link_latency --link-trace
|
|
|
30
30
|
`--link-trace-interval` and `--link-trace-modem-command` also enable the
|
|
31
31
|
recorder and force `shared.use_status_overview: true` for the generated instance.
|
|
32
32
|
|
|
33
|
+
`interval_s` is the period between rows, and the status-overview node is ticked
|
|
34
|
+
at the same period when the trace is enabled. Those two being equal is what
|
|
35
|
+
makes the trace continuous: each row's `passive_counter_delta` covers one tick,
|
|
36
|
+
so consecutive rows meet rather than leaving traffic between them. The recorder
|
|
37
|
+
therefore schedules against an advancing deadline with a small tolerance instead
|
|
38
|
+
of a stopwatch since the last row — without that, a tick landing a hair early is
|
|
39
|
+
rejected and waits a whole further period, which halves the trace and leaves
|
|
40
|
+
every second interval described by no row at all (measured on a 5 h drive:
|
|
41
|
+
1.984 s between rows carrying 1.002 s windows).
|
|
42
|
+
|
|
33
43
|
## Row Schema
|
|
34
44
|
|
|
35
45
|
Each line is one JSON object:
|
|
@@ -275,6 +275,13 @@ class OtaSmokePeer:
|
|
|
275
275
|
#: list. Naming the transport lets the narrow thing be substituted without
|
|
276
276
|
#: this file having to know what narrow means.
|
|
277
277
|
exec_command: str | None = None
|
|
278
|
+
#: CPUs this peer's session containers are confined to, e.g. "0-3".
|
|
279
|
+
#:
|
|
280
|
+
#: The reason it is per peer: a reservation is a property of one machine's
|
|
281
|
+
#: contention, not of the run. A bench pair is usually one busy workstation
|
|
282
|
+
#: shared with somebody else and one idle machine, and pinning the idle one
|
|
283
|
+
#: would only take capacity away from the measurement.
|
|
284
|
+
cpuset: str | None = None
|
|
278
285
|
|
|
279
286
|
|
|
280
287
|
@dataclass(frozen=True)
|
|
@@ -875,6 +882,61 @@ def _append_log(path: Path, text: str) -> None:
|
|
|
875
882
|
fp.write("\n")
|
|
876
883
|
|
|
877
884
|
|
|
885
|
+
#: A provenance read must never be what makes an instance fail to start.
|
|
886
|
+
_SOURCE_REVISION_TIMEOUT_S = 5.0
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def _source_revision(path: Path | str) -> dict[str, Any] | None:
|
|
890
|
+
"""The VCS revision of the directory this session was taken from.
|
|
891
|
+
|
|
892
|
+
`rosotacom_version` pins the tool. This pins its *input*: the session source
|
|
893
|
+
is normally a checkout of a deployment repository, and that checkout moves.
|
|
894
|
+
A consumer reading the manifest later cannot recover the revision by looking
|
|
895
|
+
at the machine, because looking at the machine answers a different question —
|
|
896
|
+
on one two-machine drive the vehicle had been synced by the time the
|
|
897
|
+
recording was consolidated the next morning, so the only commit anybody
|
|
898
|
+
could still read was not the commit that ran. The manifest is written once
|
|
899
|
+
at creation and never rewritten, which is exactly what such a fact needs.
|
|
900
|
+
|
|
901
|
+
`dirty` deliberately ignores untracked files: deployment checkouts routinely
|
|
902
|
+
carry recordings, logs and scratch files that are not the code, so counting
|
|
903
|
+
them would make every real deployment permanently dirty and the flag
|
|
904
|
+
meaningless. What it reports is tracked content differing from the commit.
|
|
905
|
+
|
|
906
|
+
Best effort throughout — not a directory, no git, not a repository, a git
|
|
907
|
+
that hangs: all yield None.
|
|
908
|
+
"""
|
|
909
|
+
directory = Path(path)
|
|
910
|
+
if not directory.is_dir():
|
|
911
|
+
return None
|
|
912
|
+
|
|
913
|
+
def git(*args: str) -> str | None:
|
|
914
|
+
try:
|
|
915
|
+
result = subprocess.run(
|
|
916
|
+
["git", "-C", str(directory), *args],
|
|
917
|
+
capture_output=True,
|
|
918
|
+
text=True,
|
|
919
|
+
check=False,
|
|
920
|
+
timeout=_SOURCE_REVISION_TIMEOUT_S,
|
|
921
|
+
)
|
|
922
|
+
except (OSError, subprocess.SubprocessError):
|
|
923
|
+
return None
|
|
924
|
+
return result.stdout.strip() if result.returncode == 0 else None
|
|
925
|
+
|
|
926
|
+
commit = git("rev-parse", "HEAD")
|
|
927
|
+
if not commit:
|
|
928
|
+
return None
|
|
929
|
+
branch = git("rev-parse", "--abbrev-ref", "HEAD")
|
|
930
|
+
tracked_changes = git("status", "--porcelain", "--untracked-files=no")
|
|
931
|
+
return {
|
|
932
|
+
"vcs": "git",
|
|
933
|
+
"commit": commit,
|
|
934
|
+
"branch": None if branch in (None, "HEAD") else branch,
|
|
935
|
+
"dirty": None if tracked_changes is None else bool(tracked_changes),
|
|
936
|
+
"root": git("rev-parse", "--show-toplevel"),
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
|
|
878
940
|
def _effective_config_sha256(cfg: dict[str, Any]) -> str:
|
|
879
941
|
text = yaml.safe_dump(cfg, sort_keys=True, default_flow_style=False, allow_unicode=True)
|
|
880
942
|
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
@@ -901,6 +963,7 @@ def _write_instance_manifest(
|
|
|
901
963
|
"instance_id": instance.instance_id,
|
|
902
964
|
"rosotacom_version": __version__,
|
|
903
965
|
"source_session_host_dir": str(session.host_dir),
|
|
966
|
+
"source_revision": _source_revision(session.host_dir),
|
|
904
967
|
"source_session_container_dir": session.container_dir,
|
|
905
968
|
"source": session.source,
|
|
906
969
|
"config_dir": str(instance.config_host_dir),
|
|
@@ -955,6 +1018,7 @@ def _write_scenario_manifest(
|
|
|
955
1018
|
"instance_id": instance.instance_id,
|
|
956
1019
|
"rosotacom_version": __version__,
|
|
957
1020
|
"source_session_host_dir": str(session.host_dir),
|
|
1021
|
+
"source_revision": _source_revision(session.host_dir),
|
|
958
1022
|
"source_session_container_dir": session.container_dir,
|
|
959
1023
|
"source": session.source,
|
|
960
1024
|
"config_dir": str(instance.config_host_dir),
|
|
@@ -1021,6 +1085,7 @@ def _write_interactive_smoke_manifest(
|
|
|
1021
1085
|
"instance_id": instance.instance_id,
|
|
1022
1086
|
"rosotacom_version": __version__,
|
|
1023
1087
|
"source_session_host_dir": str(target.session.host_dir),
|
|
1088
|
+
"source_revision": _source_revision(target.session.host_dir),
|
|
1024
1089
|
"source_session_container_dir": target.session.container_dir,
|
|
1025
1090
|
"source": target.session.source,
|
|
1026
1091
|
"config_dir": str(instance.config_host_dir),
|
|
@@ -1189,6 +1254,11 @@ def _parse_peer_exec_overrides(overrides: list[str] | None) -> dict[str, str]:
|
|
|
1189
1254
|
return parse_assignments(overrides, option="--peer-exec")
|
|
1190
1255
|
|
|
1191
1256
|
|
|
1257
|
+
def _parse_peer_cpuset_overrides(overrides: list[str] | None) -> dict[str, str]:
|
|
1258
|
+
parsed = parse_assignments(overrides, option="--peer-cpuset")
|
|
1259
|
+
return {name: validate_cpuset(value) for name, value in parsed.items()}
|
|
1260
|
+
|
|
1261
|
+
|
|
1192
1262
|
def _deployment_config(runtime: RuntimeConfig) -> DeploymentConfig | None:
|
|
1193
1263
|
return load_deployment(runtime.deployment)
|
|
1194
1264
|
|
|
@@ -1749,8 +1819,13 @@ def _ota_plan_from_bindings(
|
|
|
1749
1819
|
checkouts: Mapping[str, str] | None = None,
|
|
1750
1820
|
project: str | None = None,
|
|
1751
1821
|
exec_commands: Mapping[str, str] | None = None,
|
|
1822
|
+
cpusets: Mapping[str, str] | None = None,
|
|
1752
1823
|
) -> OtaSmokePlan:
|
|
1753
1824
|
_ota_validate_install_mode(install_mode)
|
|
1825
|
+
if cpusets:
|
|
1826
|
+
unknown = sorted(set(cpusets) - set(bindings))
|
|
1827
|
+
if unknown:
|
|
1828
|
+
raise RuntimeError(f"--peer-cpuset names a peer the session does not have: {', '.join(unknown)}")
|
|
1754
1829
|
if exec_commands:
|
|
1755
1830
|
unknown = sorted(set(exec_commands) - set(bindings))
|
|
1756
1831
|
if unknown:
|
|
@@ -1811,6 +1886,7 @@ def _ota_plan_from_bindings(
|
|
|
1811
1886
|
address=binding.address,
|
|
1812
1887
|
checkout=(checkouts or {}).get(name) if install_mode == "checkout" else None,
|
|
1813
1888
|
exec_command=(exec_commands or {}).get(name),
|
|
1889
|
+
cpuset=(cpusets or {}).get(name),
|
|
1814
1890
|
)
|
|
1815
1891
|
for name, binding in bindings.items()
|
|
1816
1892
|
}
|
|
@@ -1839,6 +1915,7 @@ def _ota_write_state(instance: SessionInstance, plan: OtaSmokePlan) -> OtaSmokeP
|
|
|
1839
1915
|
"address": peer.address,
|
|
1840
1916
|
"ssh": peer.ssh,
|
|
1841
1917
|
"checkout": peer.checkout,
|
|
1918
|
+
"cpuset": peer.cpuset,
|
|
1842
1919
|
"exec": peer.exec_command,
|
|
1843
1920
|
}
|
|
1844
1921
|
for name, peer in sorted(plan.peers.items())
|
|
@@ -1865,6 +1942,7 @@ def _ota_load_state(path_arg: str) -> OtaSmokePlan:
|
|
|
1865
1942
|
address=str(peer_raw["address"]),
|
|
1866
1943
|
ssh=str(peer_raw["ssh"]) if peer_raw.get("ssh") else None,
|
|
1867
1944
|
checkout=str(peer_raw["checkout"]) if peer_raw.get("checkout") else None,
|
|
1945
|
+
cpuset=str(peer_raw["cpuset"]) if peer_raw.get("cpuset") else None,
|
|
1868
1946
|
exec_command=str(peer_raw["exec"]) if peer_raw.get("exec") else None,
|
|
1869
1947
|
)
|
|
1870
1948
|
install_pin = raw.get("install_pin")
|
|
@@ -2199,6 +2277,7 @@ def _ota_start_parts(
|
|
|
2199
2277
|
mode: str,
|
|
2200
2278
|
force: bool = True,
|
|
2201
2279
|
link_trace_parts: list[str] | None = None,
|
|
2280
|
+
cpuset: str | None = None,
|
|
2202
2281
|
) -> list[str]:
|
|
2203
2282
|
if target.target_type == "scenario":
|
|
2204
2283
|
parts = ["scenario", "start", target.name, "--identity", identity, "--mode", mode, "--instance-id", instance_id]
|
|
@@ -2215,6 +2294,8 @@ def _ota_start_parts(
|
|
|
2215
2294
|
]
|
|
2216
2295
|
parts.append("--force" if force else "--no-force")
|
|
2217
2296
|
parts.extend(link_trace_parts or [])
|
|
2297
|
+
if cpuset:
|
|
2298
|
+
parts.extend(["--cpuset", cpuset])
|
|
2218
2299
|
for override in peer_args:
|
|
2219
2300
|
parts.extend(["--peer-address", override])
|
|
2220
2301
|
return parts
|
|
@@ -2229,6 +2310,7 @@ def _ota_communication_start_parts(
|
|
|
2229
2310
|
mode: str,
|
|
2230
2311
|
force: bool = True,
|
|
2231
2312
|
link_trace_parts: list[str] | None = None,
|
|
2313
|
+
cpuset: str | None = None,
|
|
2232
2314
|
) -> list[str]:
|
|
2233
2315
|
parts = [
|
|
2234
2316
|
"start",
|
|
@@ -2243,6 +2325,8 @@ def _ota_communication_start_parts(
|
|
|
2243
2325
|
]
|
|
2244
2326
|
parts.append("--force" if force else "--no-force")
|
|
2245
2327
|
parts.extend(link_trace_parts or [])
|
|
2328
|
+
if cpuset:
|
|
2329
|
+
parts.extend(["--cpuset", cpuset])
|
|
2246
2330
|
for override in peer_args:
|
|
2247
2331
|
parts.extend(["--peer-address", override])
|
|
2248
2332
|
return parts
|
|
@@ -2419,6 +2503,7 @@ def _ota_write_manifest(
|
|
|
2419
2503
|
"instance_id": instance.instance_id,
|
|
2420
2504
|
"rosotacom_version": __version__,
|
|
2421
2505
|
"source_session_host_dir": str(target.session.host_dir),
|
|
2506
|
+
"source_revision": _source_revision(target.session.host_dir),
|
|
2422
2507
|
"source_session_container_dir": target.session.container_dir,
|
|
2423
2508
|
"source": target.session.source,
|
|
2424
2509
|
"config_dir": str(instance.config_host_dir),
|
|
@@ -2658,6 +2743,7 @@ def _resolve_ota_smoke_context(
|
|
|
2658
2743
|
checkouts=checkouts,
|
|
2659
2744
|
project=project,
|
|
2660
2745
|
exec_commands=exec_commands,
|
|
2746
|
+
cpusets=_parse_peer_cpuset_overrides(getattr(args, "peer_cpuset", None)),
|
|
2661
2747
|
)
|
|
2662
2748
|
plan_peers = set(plan.peers)
|
|
2663
2749
|
target_peers = set(_peer_keys_from_cfg(target.cfg))
|
|
@@ -2877,10 +2963,121 @@ def _ota_conflict_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
2877
2963
|
_LOAD_WARN_PER_CPU = 0.7
|
|
2878
2964
|
_LOAD_REFUSE_PER_CPU = 2.0
|
|
2879
2965
|
|
|
2966
|
+
# Thresholds for a peer whose containers are pinned to a CPU set. These are a
|
|
2967
|
+
# different quantity from the ones above and deliberately not the same numbers:
|
|
2968
|
+
# load-per-CPU counts runnable threads and is unbounded, while this is the busy
|
|
2969
|
+
# fraction of the pinned CPUs and cannot exceed 1.0. What it answers is the only
|
|
2970
|
+
# question a reservation raises — is anybody else on the cores this run was
|
|
2971
|
+
# given — so it is read before the run puts its own work there.
|
|
2972
|
+
_CPUSET_WARN_BUSY = 0.5
|
|
2973
|
+
_CPUSET_REFUSE_BUSY = 0.85
|
|
2974
|
+
|
|
2975
|
+
|
|
2976
|
+
#: Two snapshots of the per-CPU jiffy counters, a second apart. A busy fraction
|
|
2977
|
+
#: needs a delta -- the absolute counters describe the machine since boot, which
|
|
2978
|
+
#: says nothing about who is on those cores now.
|
|
2979
|
+
_CPU_BUSY_PROBE = "grep '^cpu[0-9]' /proc/stat; sleep 1; echo ---; grep '^cpu[0-9]' /proc/stat"
|
|
2980
|
+
|
|
2981
|
+
|
|
2982
|
+
def _parse_cpu_busy(stdout: str, cpus: Sequence[int]) -> dict[int, float] | None:
|
|
2983
|
+
"""Busy fraction per CPU between the two /proc/stat snapshots.
|
|
2984
|
+
|
|
2985
|
+
Busy is everything but idle and iowait: a core waiting on disk is not
|
|
2986
|
+
competing for the runqueue this measurement cares about, and counting it
|
|
2987
|
+
would refuse runs on a machine that is merely writing a bag.
|
|
2988
|
+
"""
|
|
2989
|
+
before, sep, after = stdout.partition("---")
|
|
2990
|
+
if not sep:
|
|
2991
|
+
return None
|
|
2992
|
+
|
|
2993
|
+
def snapshot(text: str) -> dict[int, tuple[int, int]]:
|
|
2994
|
+
out: dict[int, tuple[int, int]] = {}
|
|
2995
|
+
for line in text.splitlines():
|
|
2996
|
+
fields = line.split()
|
|
2997
|
+
if len(fields) < 6 or not fields[0].startswith("cpu") or not fields[0][3:].isdigit():
|
|
2998
|
+
continue
|
|
2999
|
+
try:
|
|
3000
|
+
values = [int(x) for x in fields[1:]]
|
|
3001
|
+
except ValueError:
|
|
3002
|
+
continue
|
|
3003
|
+
total = sum(values)
|
|
3004
|
+
idle = values[3] + values[4] # idle + iowait
|
|
3005
|
+
out[int(fields[0][3:])] = (total, idle)
|
|
3006
|
+
return out
|
|
3007
|
+
|
|
3008
|
+
first, second = snapshot(before), snapshot(after)
|
|
3009
|
+
busy: dict[int, float] = {}
|
|
3010
|
+
for cpu in cpus:
|
|
3011
|
+
if cpu not in first or cpu not in second:
|
|
3012
|
+
return None
|
|
3013
|
+
total_delta = second[cpu][0] - first[cpu][0]
|
|
3014
|
+
idle_delta = second[cpu][1] - first[cpu][1]
|
|
3015
|
+
if total_delta <= 0:
|
|
3016
|
+
return None
|
|
3017
|
+
busy[cpu] = max(0.0, min(1.0, 1.0 - idle_delta / total_delta))
|
|
3018
|
+
return busy or None
|
|
3019
|
+
|
|
3020
|
+
|
|
3021
|
+
def _ota_cpuset_load_check(peer: OtaSmokePeer, *, dry_run: bool) -> bool:
|
|
3022
|
+
"""Judge the CPUs this peer is pinned to, not the machine around them.
|
|
3023
|
+
|
|
3024
|
+
A reservation makes a machine measurable without making it quiet: the other
|
|
3025
|
+
account keeps every thread it had, they just queue on fewer cores, so
|
|
3026
|
+
/proc/loadavg reads higher than before and says nothing about whether the
|
|
3027
|
+
reserved cores are free. Reading those cores directly is the only way the
|
|
3028
|
+
preflight can tell a partitioned workstation from a saturated one.
|
|
3029
|
+
|
|
3030
|
+
Returns False when the probe could not produce a number, so the caller can
|
|
3031
|
+
fall back to the machine-wide check rather than silently skip it.
|
|
3032
|
+
"""
|
|
3033
|
+
if not peer.cpuset:
|
|
3034
|
+
return False
|
|
3035
|
+
cpus = parse_cpuset_list(peer.cpuset)
|
|
3036
|
+
result = _ota_run(
|
|
3037
|
+
peer,
|
|
3038
|
+
_CPU_BUSY_PROBE,
|
|
3039
|
+
label=f"{peer.name}: load on CPUs {peer.cpuset}",
|
|
3040
|
+
dry_run=dry_run,
|
|
3041
|
+
batch=True,
|
|
3042
|
+
check=False,
|
|
3043
|
+
)
|
|
3044
|
+
if dry_run:
|
|
3045
|
+
return True
|
|
3046
|
+
if result.returncode != 0:
|
|
3047
|
+
return False
|
|
3048
|
+
busy = _parse_cpu_busy(result.stdout or "", cpus)
|
|
3049
|
+
if busy is None:
|
|
3050
|
+
return False
|
|
3051
|
+
|
|
3052
|
+
worst_cpu = max(busy, key=lambda cpu: busy[cpu])
|
|
3053
|
+
mean = sum(busy.values()) / len(busy)
|
|
3054
|
+
detail = " ".join(f"cpu{cpu}={busy[cpu] * 100:.0f}%" for cpu in sorted(busy))
|
|
3055
|
+
summary = (
|
|
3056
|
+
f"CPUs {peer.cpuset} are {mean * 100:.0f}% busy on average "
|
|
3057
|
+
f"(worst cpu{worst_cpu} at {busy[worst_cpu] * 100:.0f}%) — {detail}"
|
|
3058
|
+
)
|
|
3059
|
+
if mean >= _CPUSET_REFUSE_BUSY:
|
|
3060
|
+
raise RuntimeError(
|
|
3061
|
+
f"Peer {peer.name} is pinned to CPUs {peer.cpuset}, and they are not free: {summary}.\n"
|
|
3062
|
+
"The reservation is either not in force or somebody else is inside it. Check it on that "
|
|
3063
|
+
"machine, pin to different CPUs, or — if this load is yours and expected — pass "
|
|
3064
|
+
"--skip-conflict-check."
|
|
3065
|
+
)
|
|
3066
|
+
if mean >= _CPUSET_WARN_BUSY:
|
|
3067
|
+
print(f"! {peer.name}: {summary} — the run has less of its reservation than it looks")
|
|
3068
|
+
else:
|
|
3069
|
+
print(f"+ {peer.name}: {summary}")
|
|
3070
|
+
return True
|
|
3071
|
+
|
|
2880
3072
|
|
|
2881
3073
|
def _ota_load_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
2882
3074
|
"""Refuse to measure on a peer that is busy with someone else's work."""
|
|
2883
3075
|
for peer in plan.peers.values():
|
|
3076
|
+
# A pinned peer is judged on its own CPUs. The machine-wide figure is the
|
|
3077
|
+
# wrong question there: it is high by construction on exactly the shared
|
|
3078
|
+
# workstation the pinning exists for.
|
|
3079
|
+
if _ota_cpuset_load_check(peer, dry_run=dry_run):
|
|
3080
|
+
continue
|
|
2884
3081
|
result = _ota_run(
|
|
2885
3082
|
peer,
|
|
2886
3083
|
"cut -d' ' -f1-3 /proc/loadavg; nproc",
|
|
@@ -2910,7 +3107,9 @@ def _ota_load_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
2910
3107
|
f"Peer {peer.name} is carrying unrelated load: {summary}.\n"
|
|
2911
3108
|
"A measurement taken here would describe the contention, not the link.\n"
|
|
2912
3109
|
"Wait for the machine to go quiet, measure on another pair, or — if this "
|
|
2913
|
-
"load is yours and expected — pass --skip-conflict-check
|
|
3110
|
+
"load is yours and expected — pass --skip-conflict-check.\n"
|
|
3111
|
+
"If those CPUs were reserved away from another account, pin this peer to them "
|
|
3112
|
+
"with --peer-cpuset and the check will read them instead of the whole machine."
|
|
2914
3113
|
)
|
|
2915
3114
|
if ratio >= _LOAD_WARN_PER_CPU:
|
|
2916
3115
|
print(f"! {peer.name}: {summary} — comparisons survive if runs are interleaved, absolute latencies do not")
|
|
@@ -3692,7 +3891,15 @@ def _ota_start_peers(
|
|
|
3692
3891
|
peer = plan.peers[peer_name]
|
|
3693
3892
|
command = _ota_rosotacom_command(
|
|
3694
3893
|
plan,
|
|
3695
|
-
_ota_start_parts(
|
|
3894
|
+
_ota_start_parts(
|
|
3895
|
+
target,
|
|
3896
|
+
peer_name,
|
|
3897
|
+
instance_id,
|
|
3898
|
+
peer_args,
|
|
3899
|
+
mode=mode,
|
|
3900
|
+
link_trace_parts=link_trace_parts,
|
|
3901
|
+
cpuset=peer.cpuset,
|
|
3902
|
+
),
|
|
3696
3903
|
peer,
|
|
3697
3904
|
)
|
|
3698
3905
|
_ota_run(peer, command, label=f"{peer_name}: start {target.name}", dry_run=dry_run)
|
|
@@ -4089,6 +4296,7 @@ def _ota_create_tmux(
|
|
|
4089
4296
|
peer_args,
|
|
4090
4297
|
mode="attach",
|
|
4091
4298
|
link_trace_parts=link_trace_parts,
|
|
4299
|
+
cpuset=first_peer.cpuset,
|
|
4092
4300
|
),
|
|
4093
4301
|
first_peer,
|
|
4094
4302
|
)
|
|
@@ -4169,6 +4377,7 @@ def _ota_create_tmux(
|
|
|
4169
4377
|
peer_args,
|
|
4170
4378
|
mode="attach",
|
|
4171
4379
|
link_trace_parts=link_trace_parts,
|
|
4380
|
+
cpuset=peer.cpuset,
|
|
4172
4381
|
),
|
|
4173
4382
|
peer,
|
|
4174
4383
|
)
|
|
@@ -5235,6 +5444,43 @@ def _local_ipc_run_args(network_isolated: bool) -> list[str]:
|
|
|
5235
5444
|
return [] if network_isolated else ["--ipc", "host"]
|
|
5236
5445
|
|
|
5237
5446
|
|
|
5447
|
+
_CPUSET_RE = re.compile(r"^\d+(-\d+)?(,\d+(-\d+)?)*$")
|
|
5448
|
+
|
|
5449
|
+
|
|
5450
|
+
def validate_cpuset(cpuset: str) -> str:
|
|
5451
|
+
"""Accept a docker `--cpuset-cpus` list, and nothing else.
|
|
5452
|
+
|
|
5453
|
+
This string is handed to `docker run` and travels to a peer over ssh, so it
|
|
5454
|
+
is validated rather than trusted: only decimal CPU numbers and ranges, the
|
|
5455
|
+
exact grammar docker documents. Anything else — a shell metacharacter, a
|
|
5456
|
+
negative number, an empty element — is a caller error and says so here
|
|
5457
|
+
instead of at the far end of an ssh pipe.
|
|
5458
|
+
"""
|
|
5459
|
+
cleaned = cpuset.strip()
|
|
5460
|
+
if not _CPUSET_RE.match(cleaned):
|
|
5461
|
+
raise RuntimeError(
|
|
5462
|
+
f"--cpuset {cpuset!r} is not a CPU list: expected decimal numbers and ranges like '0-3' or '0,2,4-7'."
|
|
5463
|
+
)
|
|
5464
|
+
for part in cleaned.split(","):
|
|
5465
|
+
if "-" in part:
|
|
5466
|
+
low, high = (int(x) for x in part.split("-", 1))
|
|
5467
|
+
if high < low:
|
|
5468
|
+
raise RuntimeError(f"--cpuset {cpuset!r}: range {part!r} counts backwards.")
|
|
5469
|
+
return cleaned
|
|
5470
|
+
|
|
5471
|
+
|
|
5472
|
+
def parse_cpuset_list(cpuset: str) -> list[int]:
|
|
5473
|
+
"""Expand a validated CPU list into the individual CPU numbers."""
|
|
5474
|
+
cpus: list[int] = []
|
|
5475
|
+
for part in validate_cpuset(cpuset).split(","):
|
|
5476
|
+
if "-" in part:
|
|
5477
|
+
low, high = (int(x) for x in part.split("-", 1))
|
|
5478
|
+
cpus.extend(range(low, high + 1))
|
|
5479
|
+
else:
|
|
5480
|
+
cpus.append(int(part))
|
|
5481
|
+
return sorted(set(cpus))
|
|
5482
|
+
|
|
5483
|
+
|
|
5238
5484
|
def _base_extra_run_args(
|
|
5239
5485
|
runtime: RuntimeConfig,
|
|
5240
5486
|
session: ResolvedSession,
|
|
@@ -5242,8 +5488,15 @@ def _base_extra_run_args(
|
|
|
5242
5488
|
instance: SessionInstance,
|
|
5243
5489
|
*,
|
|
5244
5490
|
network_isolated: bool = False,
|
|
5491
|
+
cpuset: str | None = None,
|
|
5245
5492
|
) -> list[str]:
|
|
5246
5493
|
args: list[str] = _local_ipc_run_args(network_isolated)
|
|
5494
|
+
if cpuset:
|
|
5495
|
+
# Confine every container of this session to the given CPUs. On a shared
|
|
5496
|
+
# workstation the point is not to slow this session down: it is to put it
|
|
5497
|
+
# on the cores that were reserved away from whoever else is on the
|
|
5498
|
+
# machine, so a latency measurement stops competing for a runqueue.
|
|
5499
|
+
args.extend(["--cpuset-cpus", validate_cpuset(cpuset)])
|
|
5247
5500
|
ros2docker_cfg = load_config(runtime.ros2docker_config)
|
|
5248
5501
|
|
|
5249
5502
|
if not ros2docker_cfg.get("mount_ws"):
|
|
@@ -5427,7 +5680,9 @@ def start_session(args: argparse.Namespace) -> str:
|
|
|
5427
5680
|
" (or pass --force to replace it, --skip-conflict-check to ignore it).",
|
|
5428
5681
|
)
|
|
5429
5682
|
image_name = _scoped_image_name(runtime)
|
|
5430
|
-
extra_run_args = _base_extra_run_args(
|
|
5683
|
+
extra_run_args = _base_extra_run_args(
|
|
5684
|
+
runtime, session, cfg, instance, network_isolated=network_isolated, cpuset=getattr(args, "cpuset", None)
|
|
5685
|
+
)
|
|
5431
5686
|
run_override: dict[str, object] = {}
|
|
5432
5687
|
network_name = getattr(args, "network_name", None)
|
|
5433
5688
|
if network_name:
|
|
@@ -8912,6 +9167,18 @@ def _add_ota_install_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8912
9167
|
"Required for every peer in that mode; the same repository is at a different path on each machine."
|
|
8913
9168
|
),
|
|
8914
9169
|
)
|
|
9170
|
+
parser.add_argument(
|
|
9171
|
+
"--peer-cpuset",
|
|
9172
|
+
action="append",
|
|
9173
|
+
default=[],
|
|
9174
|
+
metavar="PEER=CPUS",
|
|
9175
|
+
help=(
|
|
9176
|
+
"Confine this peer's session containers to these CPUs, e.g. 'a=0-3'. For a shared "
|
|
9177
|
+
"workstation whose CPUs were reserved away from another account: the run then measures "
|
|
9178
|
+
"on cores nobody else can take, and the load preflight judges those cores instead of "
|
|
9179
|
+
"the whole machine."
|
|
9180
|
+
),
|
|
9181
|
+
)
|
|
8915
9182
|
parser.add_argument(
|
|
8916
9183
|
"--peer-exec",
|
|
8917
9184
|
action="append",
|
|
@@ -8980,6 +9247,14 @@ def _add_start_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8980
9247
|
help="Require three bounded ICMP probes to the peer to succeed before starting.",
|
|
8981
9248
|
)
|
|
8982
9249
|
parser.add_argument("--instance-id", help="Join or create a named runtime session instance.")
|
|
9250
|
+
parser.add_argument(
|
|
9251
|
+
"--cpuset",
|
|
9252
|
+
help=(
|
|
9253
|
+
"Confine this session's containers to these CPUs, e.g. '0-3'. Use on a shared "
|
|
9254
|
+
"workstation where those CPUs were reserved away from another account: the "
|
|
9255
|
+
"session then measures on cores nobody else can take."
|
|
9256
|
+
),
|
|
9257
|
+
)
|
|
8983
9258
|
parser.add_argument(
|
|
8984
9259
|
"--skip-conflict-check",
|
|
8985
9260
|
action="store_true",
|
|
@@ -5214,6 +5214,7 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
|
|
|
5214
5214
|
peer_address=getattr(args, "peer_address", None),
|
|
5215
5215
|
peer_ssh=getattr(args, "peer_ssh", None),
|
|
5216
5216
|
peer_checkout=getattr(args, "peer_checkout", None),
|
|
5217
|
+
peer_cpuset=getattr(args, "peer_cpuset", None),
|
|
5217
5218
|
peer_exec=getattr(args, "peer_exec", None),
|
|
5218
5219
|
install_mode=getattr(args, "install_mode", None),
|
|
5219
5220
|
install_pin=getattr(args, "install_pin", None),
|
|
@@ -6,7 +6,18 @@
|
|
|
6
6
|
<NetworkInterface address="#host_ip"/>
|
|
7
7
|
</Interfaces>
|
|
8
8
|
<MulticastRecvNetworkInterfaceAddresses>none</MulticastRecvNetworkInterfaceAddresses>
|
|
9
|
-
|
|
9
|
+
<!-- Strictly below MaxMessageSize, and that is the whole point: an RTPS
|
|
10
|
+
message carries headers as well as the fragment, so a fragment of
|
|
11
|
+
exactly MaxMessageSize cannot be sent at all. Both were 1200B, which
|
|
12
|
+
CycloneDDS 0.10.5 coped with and 11.0.1 does not — under Lyrical no
|
|
13
|
+
fragmented sample crossed, silently: the endpoints matched and the
|
|
14
|
+
reader never saw a sample, while a sample small enough not to
|
|
15
|
+
fragment arrived normally. Measured between two hosts with a 64 kB
|
|
16
|
+
sample: 1200/1200 delivers nothing on 11.0.1 and everything on
|
|
17
|
+
0.10.5; 1024/1200 delivers on both. The datagram stays inside the
|
|
18
|
+
same MTU budget the 1200B was chosen for; only the header room is
|
|
19
|
+
added. -->
|
|
20
|
+
<FragmentSize>1024B</FragmentSize>
|
|
10
21
|
<MaxMessageSize>1200B</MaxMessageSize>
|
|
11
22
|
<MaxRexmitMessageSize>1200B</MaxRexmitMessageSize>
|
|
12
23
|
</General>
|