rosotacom-dev 2.5.dev77__tar.gz → 2.5.dev79__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 (399) hide show
  1. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/cli.py +186 -15
  3. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/cli_benchmark.py +27 -21
  4. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  5. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/SOURCES.txt +1 -0
  6. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/scm_file_list.json +1 -0
  7. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  8. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_cli_benchmark.py +9 -1
  9. rosotacom_dev-2.5.dev79/tests/unit/test_ota_benchmark_peer_exec.py +134 -0
  10. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.devcontainer/devcontainer.json.template +0 -0
  11. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  12. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/CODEOWNERS +0 -0
  13. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  14. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  15. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  16. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  17. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  18. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  19. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  20. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  21. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  22. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/actions/setup-just/action.yml +0 -0
  23. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/dependabot.yml +0 -0
  24. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/benchmark-calibrate.yml +0 -0
  25. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/benchmark-gate.yml +0 -0
  26. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/dev-release.yml +0 -0
  27. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/image-scan.yml +0 -0
  28. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/nightly-e2e.yml +0 -0
  29. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/pr-lightweight.yml +0 -0
  30. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/pr-merge-gate.yml +0 -0
  31. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.github/workflows/release.yml +0 -0
  32. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/.pre-commit-config.yaml +0 -0
  33. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/AGENTS.md +0 -0
  34. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/CONCURRENCY.md +0 -0
  35. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/CONTRIBUTING.md +0 -0
  36. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/DEVELOPMENT_PRINCIPLES.md +0 -0
  37. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/LICENSE.txt +0 -0
  38. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/MANIFEST.in +0 -0
  39. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/README.md +0 -0
  40. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/budgets.jsonl +0 -0
  41. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/README.md +0 -0
  42. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/capacity-size-tight-cyclone.json +0 -0
  43. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  44. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/probe-loss-nominal-cyclone.json +0 -0
  45. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/probe-loss-tight-cyclone.json +0 -0
  46. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/probe-loss-tight-fastdds.json +0 -0
  47. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/calibration/probe-loss-tight-zenoh.json +0 -0
  48. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/codecov.yml +0 -0
  49. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/deployment-configuration.md +0 -0
  50. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/benchmark-ab.md +0 -0
  51. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/benchmark-bag-as-load.md +0 -0
  52. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/ci.md +0 -0
  53. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/ffmpeg-keyframes.md +0 -0
  54. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/README.md +0 -0
  55. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/a-fitted-delay-table-never-reached-the-shaper.md +0 -0
  56. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/a-shorter-group-does-not-lose-fewer-key-frames.md +0 -0
  57. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/a-transport-element-doubles-every-fastdds-sample.md +0 -0
  58. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/cyclone-fragment-equal-to-max-drops-fragmented-samples.md +0 -0
  59. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  60. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/delay-alone-no-loss.md +0 -0
  61. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +0 -0
  62. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
  63. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/head-of-line-irregularity.md +0 -0
  64. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  65. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
  66. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/lifespan-is-enforced-by-one-reader-and-not-the-other.md +0 -0
  67. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/lighter-message-loss.md +0 -0
  68. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/neither-a-queue-nor-a-burst-tilts-the-size-law.md +0 -0
  69. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/one-domain-two-cyclone-configs-stop-interoperating.md +0 -0
  70. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/oversubscription-queues-not-losses.md +0 -0
  71. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  72. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/repro/decode_chain_repro.py +0 -0
  73. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/repro/reorder_drop_repro.py +0 -0
  74. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
  75. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/the-size-law-belongs-to-the-transport-not-the-link.md +0 -0
  76. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  77. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/forensics-report.md +0 -0
  78. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/geo-link-map.md +0 -0
  79. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/github-repository-settings.md +0 -0
  80. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/link-bandwidth.md +0 -0
  81. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/link-trace.md +0 -0
  82. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/ota-pcap.md +0 -0
  83. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/owner-runbook.md +0 -0
  84. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/performance-bands.md +0 -0
  85. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/playbooks/README.md +0 -0
  86. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/quality-model.md +0 -0
  87. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release-notes/TEMPLATE.md +0 -0
  88. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release-notes/v2.1.md +0 -0
  89. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release-notes/v2.2.md +0 -0
  90. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release-notes/v2.3.md +0 -0
  91. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release-notes/v2.4.md +0 -0
  92. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/release.md +0 -0
  93. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  94. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0002-expectation-concepts.md +0 -0
  95. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0003-metric-backbone.md +0 -0
  96. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0004-network-profiles.md +0 -0
  97. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  98. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0006-dynamic-qos.md +0 -0
  99. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/0007-regression-gate.md +0 -0
  100. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/rfcs/README.md +0 -0
  101. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/testing.md +0 -0
  102. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/docs/work-items.md +0 -0
  103. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/install.sh +0 -0
  104. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/justfile +0 -0
  105. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/media/control_center.png +0 -0
  106. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/media/echo_test.png +0 -0
  107. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/media/forensics-report-example.png +0 -0
  108. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/media/host.png +0 -0
  109. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/media/vehicle.png +0 -0
  110. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/pyproject.toml +0 -0
  111. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/requirements.txt +0 -0
  112. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/rosotacom.yaml +0 -0
  113. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/session-configuration.md +0 -0
  114. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/setup.cfg +0 -0
  115. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/__init__.py +0 -0
  116. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/__main__.py +0 -0
  117. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/anonymize.py +0 -0
  118. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/bag_ground_truth.py +0 -0
  119. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/benched_set.py +0 -0
  120. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/benchmark.py +0 -0
  121. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/benchmark_delta.py +0 -0
  122. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/bundle_check.py +0 -0
  123. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/deployment.py +0 -0
  124. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/ffmpeg_packet.py +0 -0
  125. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/forensics.py +0 -0
  126. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/geomap.py +0 -0
  127. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/image_cache.py +0 -0
  128. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/netem_dist.py +0 -0
  129. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/network_preflight.py +0 -0
  130. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/network_profiles.py +0 -0
  131. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/network_shaper.py +0 -0
  132. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/plots.py +0 -0
  133. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/py.typed +0 -0
  134. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/benched-set.yaml +0 -0
  135. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/.gitignore +0 -0
  136. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/README.md +0 -0
  137. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/__init__.py +0 -0
  138. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  139. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  140. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  141. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  142. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  143. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/ros2docker.lyrical.json +0 -0
  144. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  145. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  146. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  147. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  148. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.lyrical.ros2docker.json +0 -0
  149. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  150. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  151. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  152. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  153. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.lyrical.ros2docker.json +0 -0
  154. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  155. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  156. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  157. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  158. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  159. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  160. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  161. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  162. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  163. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  164. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  165. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  166. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  167. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  168. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  169. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  170. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  171. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  172. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/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.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  174. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  175. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  176. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  177. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  178. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  179. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  180. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  181. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  182. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  183. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  184. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  185. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  186. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  187. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  188. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  189. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  190. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  191. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  192. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  193. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  205. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  206. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  211. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  212. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  213. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  214. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  215. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  216. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ros2docker.json.example +0 -0
  217. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ros2docker.lyrical.json.example +0 -0
  218. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  219. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  220. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  221. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  222. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  223. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  224. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  225. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  227. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  228. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  229. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/cyclonedds_scoped.xml.template +0 -0
  230. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  231. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
  232. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
  233. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
  234. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  235. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  236. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  237. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  238. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  239. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  240. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  241. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  242. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  243. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  244. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  245. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  246. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  247. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  248. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  249. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  250. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  251. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  252. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  253. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  254. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  255. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  256. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  257. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  258. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  259. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  260. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  261. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  262. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  263. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  264. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  265. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  266. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  267. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  268. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  269. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  270. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  271. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  272. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  273. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  274. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  275. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  276. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ota_pcap.py +0 -0
  277. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  278. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  279. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  280. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  281. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  282. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  283. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  284. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  285. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  286. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  287. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  288. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  289. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  290. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  291. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  292. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  293. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  294. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  295. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  296. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  297. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  298. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  299. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  300. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  301. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  302. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  303. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  304. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  305. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  306. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  307. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  308. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  309. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  310. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  311. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  312. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  313. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  314. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  315. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/status_eval.py +0 -0
  316. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/stream_stats.py +0 -0
  317. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/trace_profiles.py +0 -0
  318. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/transit.py +0 -0
  319. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom/video_quality.py +0 -0
  320. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  321. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  322. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  323. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  324. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/terminology.md +0 -0
  325. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_benched_set_registry.py +0 -0
  326. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_findings.py +0 -0
  327. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_generated_topics_have_a_producer.py +0 -0
  328. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_lyrical_runtime_compat.py +0 -0
  329. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_markdown_links.py +0 -0
  330. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_ota_source_bundle.py +0 -0
  331. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_package_metadata.py +0 -0
  332. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_packaged_resources.py +0 -0
  333. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  334. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_pytest_policy.py +0 -0
  335. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_readme_examples.py +0 -0
  336. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_security_maintenance_config.py +0 -0
  337. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_session_configuration_doc.py +0 -0
  338. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_session_template_substitution.py +0 -0
  339. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/contract/test_workflow_contracts.py +0 -0
  340. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/conftest.py +0 -0
  341. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_anonymize_e2e.py +0 -0
  342. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_benchmark_ab.py +0 -0
  343. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_benchmark_capacity.py +0 -0
  344. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_benchmark_replay.py +0 -0
  345. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_parallel_smoke.py +0 -0
  346. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_smoke.py +0 -0
  347. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_timeline_stepping.py +0 -0
  348. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/e2e/test_video_quality_e2e.py +0 -0
  349. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/generate_rmw_matrix.py +0 -0
  350. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  351. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  352. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  353. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  354. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
  355. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  356. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_anonymize.py +0 -0
  357. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_bag_ground_truth.py +0 -0
  358. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_benched_set.py +0 -0
  359. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_benchmark.py +0 -0
  360. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_benchmark_delta.py +0 -0
  361. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_bundle_check.py +0 -0
  362. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_catmux_pane_logging.py +0 -0
  363. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_cli.py +0 -0
  364. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_concurrency.py +0 -0
  365. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_container_shaping.py +0 -0
  366. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_deployment.py +0 -0
  367. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ffmpeg_flags.py +0 -0
  368. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ffmpeg_packet.py +0 -0
  369. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_field_profiles.py +0 -0
  370. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_forensics.py +0 -0
  371. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_geomap.py +0 -0
  372. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_image_cache.py +0 -0
  373. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_link_bytes.py +0 -0
  374. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_link_trace.py +0 -0
  375. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_netem_dist.py +0 -0
  376. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_network_preflight.py +0 -0
  377. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_network_profiles.py +0 -0
  378. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_network_shaper.py +0 -0
  379. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ota_link_configs.py +0 -0
  380. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ota_pcap.py +0 -0
  381. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ota_unwrapper.py +0 -0
  382. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ota_wrapper.py +0 -0
  383. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ota_xml.py +0 -0
  384. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_playout_pacer.py +0 -0
  385. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_plots.py +0 -0
  386. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_pub_sub_pair_types.py +0 -0
  387. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_qos_fallback_logging.py +0 -0
  388. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_relay_optional_parameters.py +0 -0
  389. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_ros_node_entity_ownership.py +0 -0
  390. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_session_pipeline.py +0 -0
  391. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_stage_latency.py +0 -0
  392. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_status_eval.py +0 -0
  393. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_status_overview.py +0 -0
  394. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_status_overview_observer.py +0 -0
  395. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_stream_stats.py +0 -0
  396. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_terminology_stage_ownership.py +0 -0
  397. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_trace_profiles.py +0 -0
  398. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/tests/unit/test_transit.py +0 -0
  399. {rosotacom_dev-2.5.dev77 → rosotacom_dev-2.5.dev79}/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.dev77
3
+ Version: 2.5.dev79
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -293,6 +293,12 @@ class OtaSmokePlan:
293
293
  peers: dict[str, OtaSmokePeer]
294
294
  install_mode: str = "source"
295
295
  install_pin: str | None = None
296
+ #: Where the peers write session instances, relative to the project file's
297
+ #: own directory. `session-instances` is the default a project that says
298
+ #: nothing gets; a project that sets `session_instances_dir` to anything
299
+ #: else -- `../instances`, say -- used to have its artifacts looked for in a
300
+ #: directory nothing writes to, and the miss was silent.
301
+ instances_rel: str = "session-instances"
296
302
 
297
303
 
298
304
  @dataclass(frozen=True)
@@ -1835,6 +1841,26 @@ def _ota_checkout_project_path(project_config: Path) -> tuple[Path, str]:
1835
1841
  )
1836
1842
 
1837
1843
 
1844
+ def _ota_instances_rel(runtime: RuntimeConfig) -> str:
1845
+ """Where this project writes session instances, relative to its own file.
1846
+
1847
+ The peers resolve the project from their own checkout, so the *relative*
1848
+ answer is the one that travels; an absolute path is the orchestrator's and
1849
+ means nothing on the far side. A project that says nothing gets
1850
+ `session-instances`, which is what the collection used to assume for every
1851
+ project -- and a project that says `../instances` had its artifacts looked
1852
+ for in a directory nothing writes to.
1853
+ """
1854
+ default = "session-instances"
1855
+ if not runtime.rosotacom_config:
1856
+ return default
1857
+ raw = _load_yaml_file(runtime.rosotacom_config)
1858
+ value = raw.get("session_instances_dir")
1859
+ if not value:
1860
+ return default
1861
+ return str(value)
1862
+
1863
+
1838
1864
  def _ota_plan_from_bindings(
1839
1865
  bindings: dict[str, PeerBinding],
1840
1866
  *,
@@ -1845,6 +1871,7 @@ def _ota_plan_from_bindings(
1845
1871
  project: str | None = None,
1846
1872
  exec_commands: Mapping[str, str] | None = None,
1847
1873
  cpusets: Mapping[str, str] | None = None,
1874
+ instances_rel: str = "session-instances",
1848
1875
  ) -> OtaSmokePlan:
1849
1876
  _ota_validate_install_mode(install_mode)
1850
1877
  if cpusets:
@@ -1923,6 +1950,7 @@ def _ota_plan_from_bindings(
1923
1950
  peers=peers,
1924
1951
  install_mode=install_mode,
1925
1952
  install_pin=install_pin,
1953
+ instances_rel=instances_rel,
1926
1954
  )
1927
1955
 
1928
1956
 
@@ -1935,6 +1963,7 @@ def _ota_write_state(instance: SessionInstance, plan: OtaSmokePlan) -> OtaSmokeP
1935
1963
  "project": plan.project,
1936
1964
  "install_mode": plan.install_mode,
1937
1965
  "install_pin": plan.install_pin,
1966
+ "instances_rel": plan.instances_rel,
1938
1967
  "peers": {
1939
1968
  name: {
1940
1969
  "address": peer.address,
@@ -1979,6 +2008,7 @@ def _ota_load_state(path_arg: str) -> OtaSmokePlan:
1979
2008
  peers=peers,
1980
2009
  install_mode=_ota_validate_install_mode(str(raw["install_mode"])),
1981
2010
  install_pin=str(install_pin) if install_pin else None,
2011
+ instances_rel=str(raw.get("instances_rel") or "session-instances"),
1982
2012
  )
1983
2013
 
1984
2014
 
@@ -2404,12 +2434,26 @@ def _ota_stop_parts(
2404
2434
  return parts
2405
2435
 
2406
2436
 
2437
+ #: How a benchmark's load maps onto `publish-test-topics` flags. The keys are
2438
+ #: `_sized_publisher_param_args`'s own, so a load dict travels from the
2439
+ #: benchmark to the peer's publisher without being rewritten on the way.
2440
+ _OTA_LOAD_FLAGS: tuple[tuple[str, str], ...] = (
2441
+ ("size", "--load-size"),
2442
+ ("pattern", "--load-size-pattern"),
2443
+ ("rate", "--load-rate-hz"),
2444
+ ("streams", "--load-streams"),
2445
+ ("interval_jitter_ms", "--load-interval-jitter-ms"),
2446
+ ("interval_jitter_seed", "--load-interval-jitter-seed"),
2447
+ )
2448
+
2449
+
2407
2450
  def _ota_publish_parts(
2408
2451
  target: InteractiveSmokeTarget,
2409
2452
  identity: str,
2410
2453
  peer_args: list[str],
2411
2454
  *,
2412
2455
  stop: bool = False,
2456
+ load: Mapping[str, Any] | None = None,
2413
2457
  ) -> list[str]:
2414
2458
  parts = [
2415
2459
  "publish-test-topics",
@@ -2421,6 +2465,13 @@ def _ota_publish_parts(
2421
2465
  ]
2422
2466
  if stop:
2423
2467
  parts.append("--stop")
2468
+ else:
2469
+ merged = dict(load or {})
2470
+ if "size" not in merged and merged.get("size_a") is not None:
2471
+ merged["size"] = merged["size_a"]
2472
+ for key, flag in _OTA_LOAD_FLAGS:
2473
+ if merged.get(key) is not None:
2474
+ parts.extend([flag, str(merged[key])])
2424
2475
  for override in peer_args:
2425
2476
  parts.extend(["--peer-address", override])
2426
2477
  return parts
@@ -2769,6 +2820,7 @@ def _resolve_ota_smoke_context(
2769
2820
  project=project,
2770
2821
  exec_commands=exec_commands,
2771
2822
  cpusets=_parse_peer_cpuset_overrides(getattr(args, "peer_cpuset", None)),
2823
+ instances_rel=_ota_instances_rel(runtime),
2772
2824
  )
2773
2825
  plan_peers = set(plan.peers)
2774
2826
  target_peers = set(_peer_keys_from_cfg(target.cfg))
@@ -3732,14 +3784,41 @@ def _resolve_ota_profile(
3732
3784
  return name, profile
3733
3785
 
3734
3786
 
3735
- def _profile_directions(plan: OtaSmokePlan, cfg: dict[str, Any]) -> dict[str, str]:
3787
+ def _loaded_peer(cfg: dict[str, Any]) -> str | None:
3788
+ """Which peer's egress carries this session's dominant direction.
3789
+
3790
+ A profile's ``uplink`` leg is the impaired one in almost every profile this
3791
+ project ships, so which peer it lands on decides what the run measures. The
3792
+ session already says: `topics.a_to_b` and `topics.b_to_a` are its two
3793
+ directions, and the one with more topics is the one the load runs over.
3794
+ Reading it here is what keeps a one-host and a two-host run of the *same*
3795
+ session under the *same* profile from shaping opposite directions -- which
3796
+ they did, silently, until 2026-08-31.
3797
+ """
3798
+ topics = cfg.get("topics")
3799
+ if not isinstance(topics, dict):
3800
+ return None
3801
+ a_to_b = len(topics.get("a_to_b") or ())
3802
+ b_to_a = len(topics.get("b_to_a") or ())
3803
+ if a_to_b == b_to_a:
3804
+ return None
3805
+ return "a" if a_to_b > b_to_a else "b"
3806
+
3807
+
3808
+ def _profile_directions_for(peers: list[str], cfg: dict[str, Any]) -> dict[str, str]:
3736
3809
  """Map each peer to the profile direction its egress carries (RFC 0004).
3737
3810
 
3738
- Default for the a/b convention: the first peer (center / receiver) shapes
3739
- ``downlink``, the second (vehicle / sender) shapes ``uplink`` — the dominant
3740
- telemetry direction. Override with ``shared.profile_directions: { a: …, b: }``."""
3741
- peers = sorted(plan.peers)
3742
- directions: dict[str, str] = {peers[0]: "downlink", peers[1]: "uplink"} if len(peers) == 2 else {}
3811
+ The sender of the session's dominant direction shapes ``uplink``; the other
3812
+ peer shapes ``downlink``. Where the two directions carry the same number of
3813
+ topics the session does not say, and the a/b convention decides: the first
3814
+ peer is the centre and receives, the second is the vehicle and sends.
3815
+ ``shared.profile_directions: { a: …, b: }`` overrides either.
3816
+ """
3817
+ directions: dict[str, str] = {}
3818
+ if len(peers) == 2:
3819
+ sender = _loaded_peer(cfg) or peers[1]
3820
+ other = next(name for name in peers if name != sender)
3821
+ directions = {sender: "uplink", other: "downlink"}
3743
3822
  shared = cfg.get("shared")
3744
3823
  override = shared.get("profile_directions") if isinstance(shared, dict) else None
3745
3824
  if isinstance(override, dict):
@@ -3751,6 +3830,11 @@ def _profile_directions(plan: OtaSmokePlan, cfg: dict[str, Any]) -> dict[str, st
3751
3830
  return directions
3752
3831
 
3753
3832
 
3833
+ def _profile_directions(plan: OtaSmokePlan, cfg: dict[str, Any]) -> dict[str, str]:
3834
+ """`_profile_directions_for` against a plan's peers."""
3835
+ return _profile_directions_for(sorted(plan.peers), cfg)
3836
+
3837
+
3754
3838
  def _ota_resolve_interfaces(peer: OtaSmokePeer, peer_addr: str, *, dry_run: bool) -> tuple[str, str | None]:
3755
3839
  """Discover ``(OTA egress, control)`` interfaces on ``peer`` from the kernel, not
3756
3840
  guesses: the OTA interface is the route to the other peer's data address; the
@@ -3935,6 +4019,7 @@ def _ota_start_session_publishers(
3935
4019
  plan: OtaSmokePlan,
3936
4020
  *,
3937
4021
  dry_run: bool,
4022
+ load: Mapping[str, Any] | None = None,
3938
4023
  ) -> None:
3939
4024
  if target.target_type != "session":
3940
4025
  print("OTA smoke: target is a scenario; using its application publishers.")
@@ -3942,7 +4027,7 @@ def _ota_start_session_publishers(
3942
4027
  peer_args = _ota_peer_address_args(plan)
3943
4028
  for peer_name in sorted(plan.peers):
3944
4029
  peer = plan.peers[peer_name]
3945
- command = _ota_rosotacom_command(plan, _ota_publish_parts(target, peer_name, peer_args), peer)
4030
+ command = _ota_rosotacom_command(plan, _ota_publish_parts(target, peer_name, peer_args, load=load), peer)
3946
4031
  result = _ota_run(peer, command, label=f"{peer_name}: start synthetic publishers", dry_run=dry_run, check=False)
3947
4032
  _print_completed_output(result)
3948
4033
  if result.returncode != 0:
@@ -4068,21 +4153,44 @@ def _ota_collect_logs(
4068
4153
  dry_run: bool,
4069
4154
  ) -> None:
4070
4155
  project = shlex.quote(plan.project)
4156
+ instances_rel = shlex.quote(plan.instances_rel)
4071
4157
  instance_suffix = shlex.quote(f"*_{instance.instance_id}")
4158
+ collected = 0
4072
4159
  for peer in plan.peers.values():
4073
4160
  script = (
4074
4161
  f"cd {shlex.quote(_ota_peer_workdir(plan, peer))} && "
4075
4162
  f"project_dir=$(dirname {project}) && "
4076
- f'instance_dir=$(find "$project_dir/session-instances" -mindepth 2 -maxdepth 2 '
4163
+ f'instances_root=$(cd "$project_dir" && cd "$(dirname {instances_rel})" 2>/dev/null '
4164
+ f'&& printf %s "$PWD/$(basename {instances_rel})") && '
4165
+ f'instance_dir=$(find "$instances_root" -mindepth 2 -maxdepth 2 '
4077
4166
  f"-type d -name {instance_suffix} -print -quit 2>/dev/null) && "
4078
4167
  'if [ -n "$instance_dir" ]; then '
4079
- 'relative=${instance_dir#"$project_dir"/}; '
4080
- 'tar cz -C "$project_dir" "$relative" 2>/dev/null | base64; fi'
4168
+ 'root=$(dirname "$(dirname "$instance_dir")"); '
4169
+ 'relative=${instance_dir#"$root"/}; '
4170
+ 'tar cz -C "$root" "$relative" 2>/dev/null | base64; fi'
4081
4171
  )
4082
4172
  result = _ota_run(peer, script, label=f"{peer.name}: collect session-instances", dry_run=dry_run, check=False)
4083
- if dry_run or not result.stdout.strip():
4173
+ if dry_run:
4174
+ continue
4175
+ if not result.stdout.strip():
4176
+ # Silence here used to end as "no status/events.jsonl files found"
4177
+ # after the run was already over, which describes the symptom and
4178
+ # not the cause. Say which peer, and where it was looked for.
4179
+ print(
4180
+ f"OTA smoke: peer {peer.name} returned no session instance matching "
4181
+ f"*_{instance.instance_id} under its {plan.instances_rel!r} "
4182
+ f"(relative to {plan.project!r}).",
4183
+ file=sys.stderr,
4184
+ )
4084
4185
  continue
4085
4186
  _ota_extract_peer_artifacts(instance, peer, result.stdout)
4187
+ collected += 1
4188
+ if not dry_run and not collected:
4189
+ print(
4190
+ "OTA smoke: nothing was collected from any peer -- the run produced no local records. "
4191
+ "Check that the project's session_instances_dir is the one the peers write to.",
4192
+ file=sys.stderr,
4193
+ )
4086
4194
 
4087
4195
 
4088
4196
  def _ota_extract_peer_artifacts(instance: SessionInstance, peer: OtaSmokePeer, encoded: str) -> None:
@@ -7372,9 +7480,30 @@ def _smoke_topic_pub_qos_args(qos: dict[str, Any] | None) -> str:
7372
7480
  return " ".join(shlex.quote(arg) for arg in args) + (" " if args else "")
7373
7481
 
7374
7482
 
7375
- def _smoke_publisher_command(spec: SmokeTopicSpec, ros_setup: str, duration: float) -> str:
7483
+ def _smoke_publisher_command(
7484
+ spec: SmokeTopicSpec,
7485
+ ros_setup: str,
7486
+ duration: float,
7487
+ load: Mapping[str, Any] | None = None,
7488
+ ) -> str:
7489
+ """The publisher command for one crossed topic.
7490
+
7491
+ `load` overrides what the session's own `expect` says, and only for a
7492
+ `SizedPayload` stream: a benchmark chooses its load, and a session that also
7493
+ declares one is describing what it expects to see rather than what a
7494
+ measurement should offer. Without this the benchmark's `--size` and
7495
+ `--rate-hz` reached the local runner and stopped there, so a two-host run
7496
+ measured the session's defaults while reporting the load that was asked for.
7497
+ """
7376
7498
  assert spec.publish_topic is not None and spec.publish_type is not None
7377
7499
  if spec.publish_type == "com_msgs/msg/SizedPayload":
7500
+ from .cli_benchmark import _sized_publisher_param_args
7501
+
7502
+ if load:
7503
+ args = " ".join(
7504
+ shlex.quote(str(part)) for part in _sized_publisher_param_args(spec.publish_topic, dict(load))
7505
+ )
7506
+ return f"{ros_setup} && timeout {duration} ros2 run com_py sized_publisher --ros-args {args}"
7378
7507
  size = spec.expected_size or SMOKE_SIZED_PAYLOAD_BYTES
7379
7508
  return (
7380
7509
  f"{ros_setup} && timeout {duration} ros2 run com_py sized_publisher --ros-args "
@@ -7427,16 +7556,23 @@ def _start_smoke_topic_publishers(
7427
7556
  log_line: Callable[[str], None] = print,
7428
7557
  duration: float = 180.0,
7429
7558
  source_peer_key: str | None = None,
7559
+ load: Mapping[str, Any] | None = None,
7430
7560
  ) -> list[SmokeTopicSpec]:
7431
7561
  started: list[SmokeTopicSpec] = []
7432
7562
  for spec in _smoke_publish_specs(cfg, source_peer_key=source_peer_key):
7433
7563
  assert spec.publish_topic is not None and spec.publish_type is not None
7434
7564
  container = containers[spec.source_peer_key]
7435
7565
  ros_setup = ros_setups[spec.source_peer_key]
7436
- cmd = _smoke_publisher_command(spec, ros_setup, duration)
7566
+ cmd = _smoke_publisher_command(spec, ros_setup, duration, load)
7567
+ # The rate the publisher was actually given, not the one the session
7568
+ # expects: they differ exactly when a benchmark chose the load, and a
7569
+ # log that reports the session's number there is how a run that measured
7570
+ # the wrong load reads as one that measured the right one.
7571
+ rate = (load or {}).get("rate", spec.publish_rate)
7572
+ offered = f" at {rate:g} Hz" + (" (benchmark load)" if load else "")
7437
7573
  log_line(
7438
7574
  f"Starting smoke publisher {spec.source_peer_key}->{spec.receiver_peer_key} "
7439
- f"{spec.publish_topic} ({spec.publish_type}) in {container}"
7575
+ f"{spec.publish_topic} ({spec.publish_type}) in {container}{offered}"
7440
7576
  )
7441
7577
  subprocess.run(
7442
7578
  ["docker", "exec", "-d", container, "bash", "-c", cmd],
@@ -7449,7 +7585,7 @@ def _start_smoke_topic_publishers(
7449
7585
  log_line(
7450
7586
  f"OK: smoke publisher {spec.source_peer_key}->{spec.receiver_peer_key} "
7451
7587
  f"{spec.publish_topic} ({spec.publish_type}) is advertising in {container} "
7452
- f"at {spec.publish_rate:g} Hz"
7588
+ f"at {rate:g} Hz"
7453
7589
  )
7454
7590
  started.append(spec)
7455
7591
  break
@@ -7801,6 +7937,30 @@ def probe_content_command(args: argparse.Namespace) -> int:
7801
7937
  return 0 if ok else 1
7802
7938
 
7803
7939
 
7940
+ def _publish_load_override(args: argparse.Namespace) -> dict[str, Any] | None:
7941
+ """The benchmark's load as `_sized_publisher_param_args` wants it, or None.
7942
+
7943
+ None means "use what the session expects", which is what an ordinary smoke
7944
+ run wants. A benchmark always passes something, because choosing the load is
7945
+ the whole point of it.
7946
+ """
7947
+ load: dict[str, Any] = {}
7948
+ if getattr(args, "load_size", None) is not None:
7949
+ load["size"] = int(args.load_size)
7950
+ load["size_a"] = int(args.load_size)
7951
+ if getattr(args, "load_size_pattern", None):
7952
+ load["pattern"] = args.load_size_pattern
7953
+ if getattr(args, "load_rate_hz", None) is not None:
7954
+ load["rate"] = float(args.load_rate_hz)
7955
+ if getattr(args, "load_streams", None) is not None:
7956
+ load["streams"] = int(args.load_streams)
7957
+ if getattr(args, "load_interval_jitter_ms", None) is not None:
7958
+ load["interval_jitter_ms"] = float(args.load_interval_jitter_ms)
7959
+ if getattr(args, "load_interval_jitter_seed", None) is not None:
7960
+ load["interval_jitter_seed"] = int(args.load_interval_jitter_seed)
7961
+ return load or None
7962
+
7963
+
7804
7964
  def publish_test_topics_command(args: argparse.Namespace) -> int:
7805
7965
  """Start or stop synthetic local app publishers for the peer's crossed topics.
7806
7966
 
@@ -7826,6 +7986,7 @@ def publish_test_topics_command(args: argparse.Namespace) -> int:
7826
7986
  cfg,
7827
7987
  duration=args.duration,
7828
7988
  source_peer_key=args.identity,
7989
+ load=_publish_load_override(args),
7829
7990
  )
7830
7991
  except RuntimeError as exc:
7831
7992
  print(f"ERROR: {exc}", file=sys.stderr)
@@ -10357,6 +10518,16 @@ def main(argv: list[str] | None = None) -> int:
10357
10518
  publish_test_topics_parser.add_argument(
10358
10519
  "--stop", action="store_true", help="Stop running synthetic test topic publishers."
10359
10520
  )
10521
+ # The benchmark's load, for the peer that publishes it. Without these a
10522
+ # two-host benchmark offered the session's own expected load and reported
10523
+ # the one that was asked for -- a run that reads as a success and measures
10524
+ # something else.
10525
+ publish_test_topics_parser.add_argument("--load-size", type=int, help=argparse.SUPPRESS)
10526
+ publish_test_topics_parser.add_argument("--load-size-pattern", help=argparse.SUPPRESS)
10527
+ publish_test_topics_parser.add_argument("--load-rate-hz", type=float, help=argparse.SUPPRESS)
10528
+ publish_test_topics_parser.add_argument("--load-streams", type=int, help=argparse.SUPPRESS)
10529
+ publish_test_topics_parser.add_argument("--load-interval-jitter-ms", type=float, help=argparse.SUPPRESS)
10530
+ publish_test_topics_parser.add_argument("--load-interval-jitter-seed", type=int, help=argparse.SUPPRESS)
10360
10531
  publish_test_topics_parser.set_defaults(func=publish_test_topics_command)
10361
10532
 
10362
10533
  list_parser = subparsers.add_parser("list-sessions", help="List configured sessions.")
@@ -5163,6 +5163,7 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
5163
5163
  _peer_command_runner,
5164
5164
  _peer_watchdog_launcher,
5165
5165
  _profile_directions,
5166
+ _profile_directions_for,
5166
5167
  _remove_smoke_network,
5167
5168
  _resolve_ota_profile,
5168
5169
  _resolve_ota_smoke_context,
@@ -5327,7 +5328,10 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
5327
5328
  _ota_start_peers(target, plan, instance.instance_id, dry_run=dry_run)
5328
5329
  if not dry_run:
5329
5330
  time.sleep(12)
5330
- _ota_start_session_publishers(target, plan, dry_run=dry_run)
5331
+ # The load is the benchmark's, not the session's expectation:
5332
+ # without this the peer published what its own `expect` declares
5333
+ # and the run reported the load that was asked for.
5334
+ _ota_start_session_publishers(target, plan, dry_run=dry_run, load=load)
5331
5335
 
5332
5336
  if not dry_run:
5333
5337
  if profile_obj is not None and profile_obj.is_timeline:
@@ -5651,34 +5655,36 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
5651
5655
  print(
5652
5656
  f" netem distribution {table_name!r} installed in {dist_container}: {dist_target}"
5653
5657
  )
5658
+ # Which peer's egress carries `uplink` is the session's
5659
+ # answer, not the runner's: the same session under the same
5660
+ # profile must be shaped identically here and across two
5661
+ # machines, and hard-coding a=uplink here while the OTA path
5662
+ # hard-coded b=uplink meant it was not.
5663
+ lab_directions = _profile_directions_for(["a", "b"], cfg)
5664
+ lab_containers = {"a": a_container, "b": b_container}
5654
5665
  if profile_obj.is_timeline:
5655
- # Peer A shapes uplink (egress to B)
5656
- shaper_a = ProfileShaper("eth0", make_container_runner(a_container))
5657
- lab_shapers.append(shaper_a)
5658
- steps_a = expand_timeline(profile_obj, "eth0", direction="uplink")
5659
- lab_peer_steps["a"] = (shaper_a, steps_a)
5660
-
5661
- # Peer B shapes downlink (egress to A)
5662
- shaper_b = ProfileShaper("eth0", make_container_runner(b_container))
5663
- lab_shapers.append(shaper_b)
5664
- steps_b = expand_timeline(profile_obj, "eth0", direction="downlink")
5665
- lab_peer_steps["b"] = (shaper_b, steps_b)
5666
+ for peer_name in ("a", "b"):
5667
+ shaper = ProfileShaper("eth0", make_container_runner(lab_containers[peer_name]))
5668
+ lab_shapers.append(shaper)
5669
+ steps = expand_timeline(profile_obj, "eth0", direction=lab_directions[peer_name])
5670
+ lab_peer_steps[peer_name] = (shaper, steps)
5666
5671
 
5667
5672
  for shaper in lab_shapers:
5668
5673
  shaper.arm([])
5669
5674
  else:
5670
- if profile_obj.uplink and not profile_obj.uplink.is_empty:
5671
- shaper_a = ProfileShaper("eth0", make_container_runner(a_container))
5672
- lab_shapers.append(shaper_a)
5673
- shaper_a.arm(shaping_commands("eth0", profile_obj.uplink))
5674
- if profile_obj.downlink and not profile_obj.downlink.is_empty:
5675
- shaper_b = ProfileShaper("eth0", make_container_runner(b_container))
5676
- lab_shapers.append(shaper_b)
5677
- shaper_b.arm(shaping_commands("eth0", profile_obj.downlink))
5675
+ for peer_name in ("a", "b"):
5676
+ leg = profile_obj.uplink if lab_directions[peer_name] == "uplink" else profile_obj.downlink
5677
+ if leg is None or leg.is_empty:
5678
+ continue
5679
+ shaper = ProfileShaper("eth0", make_container_runner(lab_containers[peer_name]))
5680
+ lab_shapers.append(shaper)
5681
+ shaper.arm(shaping_commands("eth0", leg))
5678
5682
 
5679
5683
  # 4. Verify shaping was applied (diagnostic)
5680
5684
  if profile_obj is not None and not getattr(args, "dry_run", False):
5681
- for label, container in [("A (uplink)", a_container), ("B (downlink)", b_container)]:
5685
+ for peer_name in ("a", "b"):
5686
+ label = f"{peer_name.upper()} ({lab_directions[peer_name]})"
5687
+ container = lab_containers[peer_name]
5682
5688
  tc_binary = container_tc_overrides.get(container, "tc")
5683
5689
  res = subprocess.run(
5684
5690
  ["docker", "exec", "-u", "root", container, tc_binary, "qdisc", "show", "dev", "eth0"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev77
3
+ Version: 2.5.dev79
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -373,6 +373,7 @@ tests/unit/test_netem_dist.py
373
373
  tests/unit/test_network_preflight.py
374
374
  tests/unit/test_network_profiles.py
375
375
  tests/unit/test_network_shaper.py
376
+ tests/unit/test_ota_benchmark_peer_exec.py
376
377
  tests/unit/test_ota_link_configs.py
377
378
  tests/unit/test_ota_pcap.py
378
379
  tests/unit/test_ota_unwrapper.py
@@ -368,6 +368,7 @@
368
368
  "tests/unit/test_network_preflight.py",
369
369
  "tests/unit/test_network_profiles.py",
370
370
  "tests/unit/test_network_shaper.py",
371
+ "tests/unit/test_ota_benchmark_peer_exec.py",
371
372
  "tests/unit/test_ota_link_configs.py",
372
373
  "tests/unit/test_ota_pcap.py",
373
374
  "tests/unit/test_ota_unwrapper.py",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tag": "2.4",
3
- "distance": 77,
4
- "node": "g51aa90c2d4f62ee5f2ba79609d271be225bfb766",
3
+ "distance": 79,
4
+ "node": "gb3c573d7bb5a6a2c6fffffe10d8be77e86c4d624",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-31"
@@ -2309,7 +2309,15 @@ def test_live_lab_run_point_uses_instance_scoped_smoke_network(
2309
2309
  monkeypatch.setattr(cli, "_load_runtime_config", lambda args: runtime)
2310
2310
  monkeypatch.setattr(cli, "_resolve_session", lambda session_name, runtime: session)
2311
2311
  monkeypatch.setattr(cli, "_resolve_session_instance", lambda runtime, session, instance_id=None: instance)
2312
- monkeypatch.setattr(cli, "_effective_session_config", lambda *args, **kwargs: {"topics": {"a_to_b": []}})
2312
+ # One a->b topic, as every benchmark session has: which container meets the
2313
+ # profile's `uplink` leg follows the session's own loaded direction, so a
2314
+ # fixture with no topics would be asserting an arbitrary default rather than
2315
+ # the rule.
2316
+ monkeypatch.setattr(
2317
+ cli,
2318
+ "_effective_session_config",
2319
+ lambda *args, **kwargs: {"topics": {"a_to_b": [{"topic": "/bench_capacity"}], "b_to_a": []}},
2320
+ )
2313
2321
  monkeypatch.setattr(cli, "_list_docker_containers", lambda all_states=False: [])
2314
2322
  monkeypatch.setattr(
2315
2323
  cli, "_ensure_smoke_network", lambda name, subnet, labels=None: networks_created.append((name, subnet))
@@ -0,0 +1,134 @@
1
+ """What a two-host benchmark must carry to its peers, and where it looks after.
2
+
3
+ Three defects this pins, all found by running one: the same session under the
4
+ same profile was shaped in opposite directions on one host and on two; the
5
+ benchmark's load never left the orchestrator; and the collection looked for the
6
+ peer's artifacts in a directory the project does not use, then reported the
7
+ symptom rather than the cause.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+
14
+ from rosotacom.cli import (
15
+ _ota_instances_rel,
16
+ _ota_publish_parts,
17
+ _profile_directions_for,
18
+ _publish_load_override,
19
+ )
20
+
21
+
22
+ class _Target:
23
+ target_type = "session"
24
+ name = "bench_1_1_capacity"
25
+
26
+
27
+ def _cfg(a_to_b: int, b_to_a: int, **shared) -> dict:
28
+ return {
29
+ "topics": {
30
+ "a_to_b": [{"topic": f"/a{i}"} for i in range(a_to_b)],
31
+ "b_to_a": [{"topic": f"/b{i}"} for i in range(b_to_a)],
32
+ },
33
+ "shared": shared,
34
+ }
35
+
36
+
37
+ # ------------------------------------------------- which peer meets `uplink`
38
+
39
+
40
+ def test_the_sender_of_the_loaded_direction_shapes_uplink():
41
+ """A profile's impaired leg must land on the egress that carries the load.
42
+
43
+ `bench_1_1_capacity` sends a->b. The OTA runner used to hand `uplink` to b
44
+ on the a=centre/b=vehicle convention, so a run under a loss profile shaped
45
+ the idle direction and delivered every message -- a green run measuring
46
+ nothing.
47
+ """
48
+ assert _profile_directions_for(["a", "b"], _cfg(1, 0)) == {"a": "uplink", "b": "downlink"}
49
+
50
+
51
+ def test_a_vehicle_to_centre_session_still_shapes_the_vehicle():
52
+ assert _profile_directions_for(["a", "b"], _cfg(0, 1)) == {"b": "uplink", "a": "downlink"}
53
+
54
+
55
+ def test_a_symmetric_session_falls_back_to_the_a_b_convention():
56
+ """Neither direction dominates, so the session does not say; b sends."""
57
+ assert _profile_directions_for(["a", "b"], _cfg(2, 2)) == {"b": "uplink", "a": "downlink"}
58
+
59
+
60
+ def test_an_explicit_declaration_wins_over_both():
61
+ cfg = _cfg(1, 0, profile_directions={"a": "downlink", "b": "uplink"})
62
+ assert _profile_directions_for(["a", "b"], cfg) == {"a": "downlink", "b": "uplink"}
63
+
64
+
65
+ # ------------------------------------------------------- the load must travel
66
+
67
+
68
+ def test_the_benchmark_load_reaches_the_peer_command():
69
+ parts = _ota_publish_parts(_Target(), "a", [], load={"size": 12000, "rate": 10.0, "streams": 2})
70
+ assert "--load-size" in parts and parts[parts.index("--load-size") + 1] == "12000"
71
+ assert "--load-rate-hz" in parts and parts[parts.index("--load-rate-hz") + 1] == "10.0"
72
+ assert "--load-streams" in parts and parts[parts.index("--load-streams") + 1] == "2"
73
+
74
+
75
+ def test_a_size_pattern_travels_as_itself():
76
+ parts = _ota_publish_parts(_Target(), "a", [], load={"pattern": "a*4,b*1", "size_a": 5000})
77
+ assert parts[parts.index("--load-size-pattern") + 1] == "a*4,b*1"
78
+ # `size_a` is the pattern's own base size and must reach the peer as --load-size.
79
+ assert parts[parts.index("--load-size") + 1] == "5000"
80
+
81
+
82
+ def test_no_load_leaves_the_session_to_say_what_it_expects():
83
+ parts = _ota_publish_parts(_Target(), "a", [])
84
+ assert not [p for p in parts if p.startswith("--load-")]
85
+
86
+
87
+ def test_stopping_carries_no_load():
88
+ parts = _ota_publish_parts(_Target(), "a", [], stop=True, load={"size": 1})
89
+ assert "--stop" in parts
90
+ assert not [p for p in parts if p.startswith("--load-")]
91
+
92
+
93
+ def test_the_peer_turns_the_flags_back_into_a_load():
94
+ args = argparse.Namespace(
95
+ load_size=12000,
96
+ load_size_pattern=None,
97
+ load_rate_hz=10.0,
98
+ load_streams=None,
99
+ load_interval_jitter_ms=None,
100
+ load_interval_jitter_seed=None,
101
+ )
102
+ assert _publish_load_override(args) == {"size": 12000, "size_a": 12000, "rate": 10.0}
103
+
104
+
105
+ def test_an_empty_namespace_means_use_the_session():
106
+ args = argparse.Namespace(
107
+ load_size=None,
108
+ load_size_pattern=None,
109
+ load_rate_hz=None,
110
+ load_streams=None,
111
+ load_interval_jitter_ms=None,
112
+ load_interval_jitter_seed=None,
113
+ )
114
+ assert _publish_load_override(args) is None
115
+
116
+
117
+ # --------------------------------------------- where the artifacts are looked for
118
+
119
+
120
+ class _Runtime:
121
+ def __init__(self, path):
122
+ self.rosotacom_config = path
123
+
124
+
125
+ def test_the_collection_follows_the_project_not_a_convention(tmp_path):
126
+ project = tmp_path / "rosotacom.yaml"
127
+ project.write_text("session_instances_dir: ../instances\n", encoding="utf-8")
128
+ assert _ota_instances_rel(_Runtime(project)) == "../instances"
129
+
130
+
131
+ def test_a_project_that_says_nothing_gets_the_default(tmp_path):
132
+ project = tmp_path / "rosotacom.yaml"
133
+ project.write_text("session_configs_dir:\n - sessions\n", encoding="utf-8")
134
+ assert _ota_instances_rel(_Runtime(project)) == "session-instances"