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.
Files changed (389) hide show
  1. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/PKG-INFO +2 -2
  2. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/README.md +1 -1
  3. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/README.md +1 -0
  4. rosotacom_dev-2.5.dev65/docs/findings/cyclone-fragment-equal-to-max-drops-fragmented-samples.md +89 -0
  5. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +1 -1
  6. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/forensics-report.md +1 -1
  7. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/link-trace.md +10 -0
  8. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/cli.py +278 -3
  9. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/cli_benchmark.py +1 -0
  10. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +12 -1
  11. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +33 -3
  12. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/PKG-INFO +2 -2
  13. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/SOURCES.txt +1 -0
  14. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/scm_file_list.json +1 -0
  15. rosotacom_dev-2.5.dev65/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  16. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_packaged_resources.py +33 -0
  17. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_cli.py +91 -0
  18. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_concurrency.py +147 -0
  19. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_link_trace.py +72 -0
  20. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_link_configs.py +12 -2
  21. rosotacom_dev-2.5.dev61/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  22. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.devcontainer/devcontainer.json.template +0 -0
  23. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  24. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/CODEOWNERS +0 -0
  25. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  26. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  27. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  28. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  29. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  30. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  31. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  32. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  33. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  34. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/actions/setup-just/action.yml +0 -0
  35. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/dependabot.yml +0 -0
  36. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/benchmark-calibrate.yml +0 -0
  37. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/benchmark-gate.yml +0 -0
  38. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/dev-release.yml +0 -0
  39. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/image-scan.yml +0 -0
  40. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/nightly-e2e.yml +0 -0
  41. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/pr-lightweight.yml +0 -0
  42. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/pr-merge-gate.yml +0 -0
  43. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.github/workflows/release.yml +0 -0
  44. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/.pre-commit-config.yaml +0 -0
  45. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/AGENTS.md +0 -0
  46. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/CONCURRENCY.md +0 -0
  47. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/CONTRIBUTING.md +0 -0
  48. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/DEVELOPMENT_PRINCIPLES.md +0 -0
  49. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/LICENSE.txt +0 -0
  50. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/MANIFEST.in +0 -0
  51. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/budgets.jsonl +0 -0
  52. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/README.md +0 -0
  53. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/capacity-size-tight-cyclone.json +0 -0
  54. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  55. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-nominal-cyclone.json +0 -0
  56. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-cyclone.json +0 -0
  57. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-fastdds.json +0 -0
  58. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/calibration/probe-loss-tight-zenoh.json +0 -0
  59. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/codecov.yml +0 -0
  60. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/deployment-configuration.md +0 -0
  61. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/benchmark-ab.md +0 -0
  62. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/benchmark-bag-as-load.md +0 -0
  63. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/ci.md +0 -0
  64. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/ffmpeg-keyframes.md +0 -0
  65. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/a-transport-element-doubles-every-fastdds-sample.md +0 -0
  66. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  67. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/delay-alone-no-loss.md +0 -0
  68. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
  69. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/head-of-line-irregularity.md +0 -0
  70. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  71. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
  72. {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
  73. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/lighter-message-loss.md +0 -0
  74. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/oversubscription-queues-not-losses.md +0 -0
  75. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  76. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/repro/decode_chain_repro.py +0 -0
  77. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/repro/reorder_drop_repro.py +0 -0
  78. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
  79. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  80. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/geo-link-map.md +0 -0
  81. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/github-repository-settings.md +0 -0
  82. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/link-bandwidth.md +0 -0
  83. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/owner-runbook.md +0 -0
  84. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/performance-bands.md +0 -0
  85. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/playbooks/README.md +0 -0
  86. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/quality-model.md +0 -0
  87. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/TEMPLATE.md +0 -0
  88. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.1.md +0 -0
  89. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.2.md +0 -0
  90. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.3.md +0 -0
  91. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release-notes/v2.4.md +0 -0
  92. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/release.md +0 -0
  93. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  94. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0002-expectation-concepts.md +0 -0
  95. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0003-metric-backbone.md +0 -0
  96. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0004-network-profiles.md +0 -0
  97. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  98. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0006-dynamic-qos.md +0 -0
  99. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/0007-regression-gate.md +0 -0
  100. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/rfcs/README.md +0 -0
  101. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/testing.md +0 -0
  102. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/docs/work-items.md +0 -0
  103. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/install.sh +0 -0
  104. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/justfile +0 -0
  105. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/control_center.png +0 -0
  106. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/echo_test.png +0 -0
  107. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/forensics-report-example.png +0 -0
  108. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/host.png +0 -0
  109. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/media/vehicle.png +0 -0
  110. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/pyproject.toml +0 -0
  111. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/requirements.txt +0 -0
  112. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/rosotacom.yaml +0 -0
  113. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/session-configuration.md +0 -0
  114. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/setup.cfg +0 -0
  115. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/__init__.py +0 -0
  116. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/__main__.py +0 -0
  117. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/anonymize.py +0 -0
  118. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/bag_ground_truth.py +0 -0
  119. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benched_set.py +0 -0
  120. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benchmark.py +0 -0
  121. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/benchmark_delta.py +0 -0
  122. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/bundle_check.py +0 -0
  123. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/deployment.py +0 -0
  124. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/ffmpeg_packet.py +0 -0
  125. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/forensics.py +0 -0
  126. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/geomap.py +0 -0
  127. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/image_cache.py +0 -0
  128. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/netem_dist.py +0 -0
  129. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_preflight.py +0 -0
  130. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_profiles.py +0 -0
  131. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/network_shaper.py +0 -0
  132. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/plots.py +0 -0
  133. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/py.typed +0 -0
  134. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/benched-set.yaml +0 -0
  135. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/.gitignore +0 -0
  136. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/README.md +0 -0
  137. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/__init__.py +0 -0
  138. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  139. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  140. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  141. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  142. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  143. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/ros2docker.lyrical.json +0 -0
  144. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  145. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  146. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  147. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  148. {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
  149. {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
  150. {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
  151. {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
  152. {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
  153. {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
  154. {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
  155. {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
  156. {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
  157. {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
  158. {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
  159. {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
  160. {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
  161. {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
  162. {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
  163. {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
  164. {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
  165. {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
  166. {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
  167. {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
  168. {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
  169. {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
  170. {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
  171. {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
  172. {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
  173. {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
  174. {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
  175. {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
  176. {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
  177. {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
  178. {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
  179. {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
  180. {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
  181. {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
  182. {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
  183. {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
  184. {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
  185. {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
  186. {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
  187. {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
  188. {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
  189. {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
  190. {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
  191. {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
  192. {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
  193. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  198. {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
  199. {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
  200. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  205. {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
  206. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  211. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  212. {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
  213. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  214. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  215. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  216. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ros2docker.json.example +0 -0
  217. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ros2docker.lyrical.json.example +0 -0
  218. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  219. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  220. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  221. {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
  222. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  223. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  224. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  225. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  227. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  228. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  229. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
  230. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
  231. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
  232. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  233. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  234. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  235. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  236. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  237. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  238. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  239. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  240. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  241. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  242. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  243. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  244. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  245. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  246. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  247. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  248. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  249. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  250. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  251. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  252. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  253. {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
  254. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  255. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  256. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  257. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  258. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  259. {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
  260. {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
  261. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  262. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  263. {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
  264. {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
  265. {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
  266. {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
  267. {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
  268. {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
  269. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  270. {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
  271. {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
  272. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  273. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  274. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  275. {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
  276. {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
  277. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  278. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  279. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  280. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  281. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  282. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  283. {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
  284. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  285. {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
  286. {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
  287. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  288. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  289. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  290. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  291. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  292. {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
  293. {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
  294. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  295. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  296. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  297. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  298. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  299. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  300. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  301. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  302. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  303. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  304. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  305. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  306. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  307. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  308. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  309. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  310. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  311. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/status_eval.py +0 -0
  312. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/stream_stats.py +0 -0
  313. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/trace_profiles.py +0 -0
  314. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/transit.py +0 -0
  315. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom/video_quality.py +0 -0
  316. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  317. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  318. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  319. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  320. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/terminology.md +0 -0
  321. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_benched_set_registry.py +0 -0
  322. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_findings.py +0 -0
  323. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_generated_topics_have_a_producer.py +0 -0
  324. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_lyrical_runtime_compat.py +0 -0
  325. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_markdown_links.py +0 -0
  326. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_ota_source_bundle.py +0 -0
  327. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_package_metadata.py +0 -0
  328. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  329. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_pytest_policy.py +0 -0
  330. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_readme_examples.py +0 -0
  331. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_security_maintenance_config.py +0 -0
  332. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_session_configuration_doc.py +0 -0
  333. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_session_template_substitution.py +0 -0
  334. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/contract/test_workflow_contracts.py +0 -0
  335. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/conftest.py +0 -0
  336. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_anonymize_e2e.py +0 -0
  337. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_ab.py +0 -0
  338. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_capacity.py +0 -0
  339. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_benchmark_replay.py +0 -0
  340. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_parallel_smoke.py +0 -0
  341. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_smoke.py +0 -0
  342. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_timeline_stepping.py +0 -0
  343. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/e2e/test_video_quality_e2e.py +0 -0
  344. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/generate_rmw_matrix.py +0 -0
  345. {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
  346. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  347. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  348. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  349. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
  350. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  351. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_anonymize.py +0 -0
  352. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_bag_ground_truth.py +0 -0
  353. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benched_set.py +0 -0
  354. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benchmark.py +0 -0
  355. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_benchmark_delta.py +0 -0
  356. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_bundle_check.py +0 -0
  357. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_catmux_pane_logging.py +0 -0
  358. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_cli_benchmark.py +0 -0
  359. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_container_shaping.py +0 -0
  360. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_deployment.py +0 -0
  361. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ffmpeg_flags.py +0 -0
  362. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ffmpeg_packet.py +0 -0
  363. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_field_profiles.py +0 -0
  364. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_forensics.py +0 -0
  365. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_geomap.py +0 -0
  366. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_image_cache.py +0 -0
  367. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_link_bytes.py +0 -0
  368. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_netem_dist.py +0 -0
  369. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_preflight.py +0 -0
  370. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_profiles.py +0 -0
  371. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_network_shaper.py +0 -0
  372. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_unwrapper.py +0 -0
  373. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_wrapper.py +0 -0
  374. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ota_xml.py +0 -0
  375. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_playout_pacer.py +0 -0
  376. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_plots.py +0 -0
  377. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_pub_sub_pair_types.py +0 -0
  378. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_qos_fallback_logging.py +0 -0
  379. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_relay_optional_parameters.py +0 -0
  380. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_ros_node_entity_ownership.py +0 -0
  381. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_session_pipeline.py +0 -0
  382. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_stage_latency.py +0 -0
  383. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_eval.py +0 -0
  384. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_overview.py +0 -0
  385. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_status_overview_observer.py +0 -0
  386. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_stream_stats.py +0 -0
  387. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_trace_profiles.py +0 -0
  388. {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev65}/tests/unit/test_transit.py +0 -0
  389. {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.dev61
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.
@@ -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 `FragmentSize`/`MaxMessageSize` 1200 B and works. The
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 + config SHA provenance |
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(target, peer_name, instance_id, peer_args, mode=mode, link_trace_parts=link_trace_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(runtime, session, cfg, instance, network_isolated=network_isolated)
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
- <FragmentSize>1200B</FragmentSize>
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>