rosotacom-dev 2.5.dev15__tar.gz → 2.5.dev16__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 (342) hide show
  1. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/PKG-INFO +21 -1
  2. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/README.md +20 -0
  3. rosotacom_dev-2.5.dev16/src/rosotacom/benchmark_delta.py +279 -0
  4. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/cli_benchmark.py +62 -0
  5. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/PKG-INFO +21 -1
  6. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/SOURCES.txt +2 -0
  7. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/scm_file_list.json +2 -0
  8. rosotacom_dev-2.5.dev16/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  9. rosotacom_dev-2.5.dev16/tests/unit/test_benchmark_delta.py +172 -0
  10. rosotacom_dev-2.5.dev15/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  11. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.devcontainer/devcontainer.json.template +0 -0
  12. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  13. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/CODEOWNERS +0 -0
  14. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  15. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  16. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  17. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  18. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  19. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  20. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  21. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  22. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  23. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/dependabot.yml +0 -0
  24. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/benchmark-calibrate.yml +0 -0
  25. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/benchmark-gate.yml +0 -0
  26. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/dev-release.yml +0 -0
  27. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/image-scan.yml +0 -0
  28. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/nightly-e2e.yml +0 -0
  29. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/pr-lightweight.yml +0 -0
  30. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/pr-merge-gate.yml +0 -0
  31. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.github/workflows/release.yml +0 -0
  32. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/.pre-commit-config.yaml +0 -0
  33. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/AGENTS.md +0 -0
  34. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/CONCURRENCY.md +0 -0
  35. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/CONTRIBUTING.md +0 -0
  36. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/DEVELOPMENT_PRINCIPLES.md +0 -0
  37. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/LICENSE.txt +0 -0
  38. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/MANIFEST.in +0 -0
  39. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/budgets.jsonl +0 -0
  40. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/README.md +0 -0
  41. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/capacity-size-tight-cyclone.json +0 -0
  42. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  43. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/probe-loss-nominal-cyclone.json +0 -0
  44. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/probe-loss-tight-cyclone.json +0 -0
  45. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/probe-loss-tight-fastdds.json +0 -0
  46. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/calibration/probe-loss-tight-zenoh.json +0 -0
  47. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/codecov.yml +0 -0
  48. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/deployment-configuration.md +0 -0
  49. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/benchmark-ab.md +0 -0
  50. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/benchmark-bag-as-load.md +0 -0
  51. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/ci.md +0 -0
  52. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/ffmpeg-keyframes.md +0 -0
  53. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/README.md +0 -0
  54. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  55. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/delay-alone-no-loss.md +0 -0
  56. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/head-of-line-irregularity.md +0 -0
  57. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  58. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/lighter-message-loss.md +0 -0
  59. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  60. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/forensics-report.md +0 -0
  61. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/geo-link-map.md +0 -0
  62. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/github-repository-settings.md +0 -0
  63. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/link-trace.md +0 -0
  64. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/owner-runbook.md +0 -0
  65. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/performance-bands.md +0 -0
  66. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/playbooks/README.md +0 -0
  67. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/quality-model.md +0 -0
  68. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release-notes/TEMPLATE.md +0 -0
  69. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release-notes/v2.1.md +0 -0
  70. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release-notes/v2.2.md +0 -0
  71. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release-notes/v2.3.md +0 -0
  72. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release-notes/v2.4.md +0 -0
  73. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/release.md +0 -0
  74. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  75. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0002-expectation-concepts.md +0 -0
  76. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0003-metric-backbone.md +0 -0
  77. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0004-network-profiles.md +0 -0
  78. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  79. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0006-dynamic-qos.md +0 -0
  80. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/0007-regression-gate.md +0 -0
  81. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/rfcs/README.md +0 -0
  82. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/testing.md +0 -0
  83. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/docs/work-items.md +0 -0
  84. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/install.sh +0 -0
  85. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/justfile +0 -0
  86. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/media/control_center.png +0 -0
  87. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/media/echo_test.png +0 -0
  88. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/media/forensics-report-example.png +0 -0
  89. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/media/host.png +0 -0
  90. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/media/vehicle.png +0 -0
  91. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/pyproject.toml +0 -0
  92. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/requirements.txt +0 -0
  93. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/rosotacom.yaml +0 -0
  94. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/run_session_in_container.py +0 -0
  95. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/session-configuration.md +0 -0
  96. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/setup.cfg +0 -0
  97. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/__init__.py +0 -0
  98. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/__main__.py +0 -0
  99. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/anonymize.py +0 -0
  100. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/bag_ground_truth.py +0 -0
  101. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/benched_set.py +0 -0
  102. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/benchmark.py +0 -0
  103. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/bundle_check.py +0 -0
  104. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/cli.py +0 -0
  105. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/deployment.py +0 -0
  106. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/ffmpeg_packet.py +0 -0
  107. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/forensics.py +0 -0
  108. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/geomap.py +0 -0
  109. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/network_profiles.py +0 -0
  110. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/network_shaper.py +0 -0
  111. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/plots.py +0 -0
  112. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/py.typed +0 -0
  113. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/benched-set.yaml +0 -0
  114. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/.gitignore +0 -0
  115. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/README.md +0 -0
  116. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/__init__.py +0 -0
  117. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  118. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  119. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  120. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  121. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  122. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  123. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  124. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  125. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  126. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  127. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  128. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  129. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  130. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  131. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  132. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  133. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  134. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  135. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  136. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  137. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  138. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  139. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  140. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  141. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  142. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  143. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  144. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  145. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  146. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  147. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  148. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  149. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  150. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  151. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  152. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  153. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  154. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  155. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  156. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  157. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  158. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  159. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  160. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  161. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  162. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  163. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  164. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  165. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  166. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  167. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  168. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  169. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  170. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  171. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  172. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  173. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  174. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  175. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  176. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  177. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ros2docker.json.example +0 -0
  192. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  193. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  194. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  195. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  196. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  197. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  198. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  199. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  200. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  201. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  202. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  203. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  204. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  205. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  206. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  207. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  208. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  209. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  210. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  211. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  212. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  213. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  214. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  215. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  216. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  217. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  218. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  219. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  220. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  221. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  222. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  223. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  224. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  225. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  226. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  227. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  228. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  229. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  230. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  231. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  232. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  233. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  234. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  235. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  236. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  237. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  238. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  239. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  240. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  241. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  242. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  243. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  244. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  245. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  246. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  247. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  248. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  249. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  250. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  251. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  252. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  253. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  254. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  255. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  256. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  257. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  258. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  259. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  260. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  261. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  262. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  263. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  264. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  265. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  266. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  267. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  268. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  269. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  270. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  271. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  272. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  273. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  274. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  275. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  276. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  277. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  278. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  279. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  280. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/status_eval.py +0 -0
  281. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/stream_stats.py +0 -0
  282. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/trace_profiles.py +0 -0
  283. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/transit.py +0 -0
  284. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom/video_quality.py +0 -0
  285. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  286. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  287. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  288. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  289. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/stop_session_in_container.py +0 -0
  290. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/terminology.md +0 -0
  291. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_benched_set_registry.py +0 -0
  292. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_findings.py +0 -0
  293. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_markdown_links.py +0 -0
  294. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_ota_source_bundle.py +0 -0
  295. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_package_metadata.py +0 -0
  296. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_packaged_resources.py +0 -0
  297. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  298. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_pytest_policy.py +0 -0
  299. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_readme_examples.py +0 -0
  300. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_security_maintenance_config.py +0 -0
  301. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_session_configuration_doc.py +0 -0
  302. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/contract/test_workflow_contracts.py +0 -0
  303. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/conftest.py +0 -0
  304. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_anonymize_e2e.py +0 -0
  305. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_benchmark_ab.py +0 -0
  306. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_benchmark_capacity.py +0 -0
  307. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_benchmark_replay.py +0 -0
  308. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_parallel_smoke.py +0 -0
  309. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_smoke.py +0 -0
  310. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_timeline_stepping.py +0 -0
  311. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/e2e/test_video_quality_e2e.py +0 -0
  312. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/generate_rmw_matrix.py +0 -0
  313. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  314. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  315. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  316. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  317. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  318. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_anonymize.py +0 -0
  319. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_bag_ground_truth.py +0 -0
  320. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_benched_set.py +0 -0
  321. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_benchmark.py +0 -0
  322. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_bundle_check.py +0 -0
  323. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_cli.py +0 -0
  324. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_cli_benchmark.py +0 -0
  325. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_concurrency.py +0 -0
  326. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_deployment.py +0 -0
  327. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_ffmpeg_packet.py +0 -0
  328. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_forensics.py +0 -0
  329. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_geomap.py +0 -0
  330. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_link_bytes.py +0 -0
  331. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_link_trace.py +0 -0
  332. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_network_profiles.py +0 -0
  333. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_network_shaper.py +0 -0
  334. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_ota_xml.py +0 -0
  335. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_plots.py +0 -0
  336. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_stage_latency.py +0 -0
  337. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_status_eval.py +0 -0
  338. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_status_overview.py +0 -0
  339. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_stream_stats.py +0 -0
  340. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_trace_profiles.py +0 -0
  341. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/tests/unit/test_transit.py +0 -0
  342. {rosotacom_dev-2.5.dev15 → rosotacom_dev-2.5.dev16}/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.dev15
3
+ Version: 2.5.dev16
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -778,6 +778,26 @@ performance bands in `budgets.jsonl` via `rosotacom benchmark compare`, and
778
778
  bands move only through `rosotacom benchmark ratchet` — see
779
779
  [docs/performance-bands.md](docs/performance-bands.md) for the band schema,
780
780
  verdicts, and the ratchet workflow (RFC 0007).
781
+
782
+ Two result sets taken in **different environments** — emulated versus a real
783
+ link, or shaped versus unshaped — are compared with `rosotacom benchmark delta`:
784
+
785
+ ```bash
786
+ rosotacom benchmark delta emulated/ ota/ \
787
+ --label-reference emulated --label-measured ota \
788
+ --out delta.json --markdown delta.md
789
+ ```
790
+
791
+ It pairs runs on genre, profile, RMW and session name, so a row that ran in one
792
+ environment and not the other is reported as unmatched rather than silently
793
+ dropped — "it got faster" and "it did not run" must not look the same. Every
794
+ report is labelled `monitor_only` and carries each side's command and profile
795
+ file, so a table pasted into a paper or an issue can be traced back without the
796
+ directory it came from.
797
+
798
+ It exits 0 whatever the numbers say, and non-zero only when the two sets share
799
+ no rows at all — a delta is an observation to explain, and an exit code would
800
+ turn "explain this" into "re-run until it is quiet".
781
801
  For live benchmark probes, `--duration` is the shaped publish window; after that
782
802
  rosotacom stops synthetic publishers and waits `--drain-s` seconds before
783
803
  tearing down shaping, so delayed in-flight messages are not miscounted as loss.
@@ -733,6 +733,26 @@ performance bands in `budgets.jsonl` via `rosotacom benchmark compare`, and
733
733
  bands move only through `rosotacom benchmark ratchet` — see
734
734
  [docs/performance-bands.md](docs/performance-bands.md) for the band schema,
735
735
  verdicts, and the ratchet workflow (RFC 0007).
736
+
737
+ Two result sets taken in **different environments** — emulated versus a real
738
+ link, or shaped versus unshaped — are compared with `rosotacom benchmark delta`:
739
+
740
+ ```bash
741
+ rosotacom benchmark delta emulated/ ota/ \
742
+ --label-reference emulated --label-measured ota \
743
+ --out delta.json --markdown delta.md
744
+ ```
745
+
746
+ It pairs runs on genre, profile, RMW and session name, so a row that ran in one
747
+ environment and not the other is reported as unmatched rather than silently
748
+ dropped — "it got faster" and "it did not run" must not look the same. Every
749
+ report is labelled `monitor_only` and carries each side's command and profile
750
+ file, so a table pasted into a paper or an issue can be traced back without the
751
+ directory it came from.
752
+
753
+ It exits 0 whatever the numbers say, and non-zero only when the two sets share
754
+ no rows at all — a delta is an observation to explain, and an exit code would
755
+ turn "explain this" into "re-run until it is quiet".
736
756
  For live benchmark probes, `--duration` is the shaped publish window; after that
737
757
  rosotacom stops synthetic publishers and waits `--drain-s` seconds before
738
758
  tearing down shaping, so delayed in-flight messages are not miscounted as loss.
@@ -0,0 +1,279 @@
1
+ """Compare two benchmark result sets taken in different environments.
2
+
3
+ The local emulated lanes encode most of the performance truth cheaply and
4
+ deterministically. What they cannot show is **which effects exist only on the
5
+ real link** — cellular variability, VPN/MTU quirks, radio behaviour, real
6
+ cross-traffic — and which are emulation artifacts. Real links stay monitor-only
7
+ by the determinism rule, so the missing piece was never another measurement: it
8
+ was the *comparison* between two sets of the same rows.
9
+
10
+ This is that comparison. It reads two directories (or lists) of `result.json`
11
+ runs, pairs them by what makes a row a row, and reports per-row, per-metric
12
+ deltas as JSON plus a short Markdown summary.
13
+
14
+ Three properties are deliberate, because it is easy to produce numbers that lie:
15
+
16
+ * **Pairing is explicit and refuses to guess.** Rows are matched on genre,
17
+ profile, RMW and session name. A row present on one side only is reported as
18
+ unmatched rather than silently dropped, because "it got faster" and "it did
19
+ not run" must not look the same.
20
+ * **Every report says it is correlational.** The two sets differ in the
21
+ environment *and* in everything the environment drags along; a delta is an
22
+ observation, never a verdict. `monitor_only: true` is in the JSON and the
23
+ first line of the Markdown.
24
+ * **It is self-describing.** Each side carries its rosotacom SHA, profile file,
25
+ and the command that produced it, so a report pasted into a paper or an issue
26
+ can be traced back without the directory it came from.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import json
32
+ from dataclasses import dataclass, field
33
+ from pathlib import Path
34
+ from typing import Any
35
+
36
+ SCHEMA_VERSION = 1
37
+
38
+ #: Numeric leaves under `result` that are worth comparing across environments.
39
+ #: A deny-list rather than an allow-list would drag in ids and counts whose
40
+ #: "delta" means nothing.
41
+ INTERESTING_SUFFIXES = (
42
+ "capacity",
43
+ "latency_ms",
44
+ "latency_p95_ms",
45
+ "loss_pct",
46
+ "rate_hz",
47
+ "mean_payload_bytes",
48
+ "offered_bandwidth_bps",
49
+ "jitter_ms",
50
+ "throughput_bps",
51
+ "delivered",
52
+ "received",
53
+ "published",
54
+ )
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class RowKey:
59
+ """What makes two runs the same row in different environments."""
60
+
61
+ genre: str
62
+ profile: str
63
+ rmw: str
64
+ session: str
65
+
66
+ def label(self) -> str:
67
+ return f"{self.genre}/{self.profile}/{self.rmw}/{self.session}"
68
+
69
+
70
+ @dataclass
71
+ class Run:
72
+ key: RowKey
73
+ metrics: dict[str, float]
74
+ provenance: dict[str, Any] = field(default_factory=dict)
75
+ path: str = ""
76
+
77
+
78
+ def _flatten_numbers(value: Any, prefix: str = "") -> dict[str, float]:
79
+ out: dict[str, float] = {}
80
+ if isinstance(value, dict):
81
+ for key, child in value.items():
82
+ out.update(_flatten_numbers(child, f"{prefix}.{key}" if prefix else str(key)))
83
+ elif isinstance(value, bool):
84
+ # Explicitly before the numeric branch: bool is an int in Python, and a
85
+ # "delta" between True and False is a category error dressed as -1.
86
+ return {}
87
+ elif isinstance(value, (int, float)):
88
+ return {prefix: float(value)}
89
+ return out
90
+
91
+
92
+ def interesting_metrics(result: Any) -> dict[str, float]:
93
+ numbers = _flatten_numbers(result)
94
+ return {
95
+ name: value
96
+ for name, value in numbers.items()
97
+ if any(name == suffix or name.endswith("." + suffix) for suffix in INTERESTING_SUFFIXES)
98
+ }
99
+
100
+
101
+ def load_run(path: Path) -> Run | None:
102
+ """Read one `result.json`. Returns None when it is not one."""
103
+ try:
104
+ data = json.loads(path.read_text(encoding="utf-8"))
105
+ except (OSError, json.JSONDecodeError):
106
+ return None
107
+ if not isinstance(data, dict) or "genre" not in data:
108
+ return None
109
+
110
+ context = data.get("context") or {}
111
+ session = context.get("session") or {}
112
+ profile = context.get("profile") or {}
113
+ rmw = context.get("rmw") or {}
114
+
115
+ key = RowKey(
116
+ genre=str(data.get("genre") or ""),
117
+ profile=str(profile.get("name") or (data.get("configuration") or {}).get("profile") or ""),
118
+ rmw=str(rmw.get("requested") or session.get("rmw") or ""),
119
+ session=str(session.get("name") or ""),
120
+ )
121
+ return Run(
122
+ key=key,
123
+ metrics=interesting_metrics(data.get("result")),
124
+ provenance={
125
+ "created_at": data.get("created_at"),
126
+ "command": context.get("command"),
127
+ "profiles_file": (context.get("paths") or {}).get("profiles_file"),
128
+ "rosotacom_sha": (context.get("versions") or {}).get("rosotacom_sha"),
129
+ "verdict": (data.get("verdict") or {}).get("status"),
130
+ },
131
+ path=str(path),
132
+ )
133
+
134
+
135
+ def collect(paths: list[Path]) -> dict[RowKey, Run]:
136
+ """Every `result.json` under the given files or directories, by row."""
137
+ found: dict[RowKey, Run] = {}
138
+ for path in paths:
139
+ candidates = [path] if path.is_file() else sorted(path.rglob("result.json"))
140
+ for candidate in candidates:
141
+ run = load_run(candidate)
142
+ if run is None:
143
+ continue
144
+ # Last one wins, and it is the newest by directory ordering — a
145
+ # re-run of a row supersedes it rather than doubling it.
146
+ found[run.key] = run
147
+ return found
148
+
149
+
150
+ def compare(
151
+ reference: dict[RowKey, Run],
152
+ measured: dict[RowKey, Run],
153
+ *,
154
+ label_reference: str,
155
+ label_measured: str,
156
+ ) -> dict[str, Any]:
157
+ rows: list[dict[str, Any]] = []
158
+ for key in sorted(set(reference) & set(measured), key=lambda k: k.label()):
159
+ a, b = reference[key], measured[key]
160
+ metrics = []
161
+ for name in sorted(set(a.metrics) | set(b.metrics)):
162
+ left = a.metrics.get(name)
163
+ right = b.metrics.get(name)
164
+ if left is None or right is None:
165
+ metrics.append(
166
+ {
167
+ "metric": name,
168
+ label_reference: left,
169
+ label_measured: right,
170
+ "delta": None,
171
+ "relative": None,
172
+ "note": "present on one side only",
173
+ }
174
+ )
175
+ continue
176
+ delta = right - left
177
+ metrics.append(
178
+ {
179
+ "metric": name,
180
+ label_reference: left,
181
+ label_measured: right,
182
+ "delta": delta,
183
+ "relative": (delta / left) if left else None,
184
+ }
185
+ )
186
+ rows.append(
187
+ {
188
+ "row": key.label(),
189
+ "genre": key.genre,
190
+ "profile": key.profile,
191
+ "rmw": key.rmw,
192
+ "session": key.session,
193
+ "metrics": metrics,
194
+ "provenance": {label_reference: a.provenance, label_measured: b.provenance},
195
+ }
196
+ )
197
+
198
+ return {
199
+ "schema_version": SCHEMA_VERSION,
200
+ # First key, and repeated in the Markdown: a delta between two
201
+ # environments is correlational. The two sets differ in the environment
202
+ # and in everything it drags along.
203
+ "monitor_only": True,
204
+ "labels": {"reference": label_reference, "measured": label_measured},
205
+ "rows": rows,
206
+ "unmatched": {
207
+ label_reference: sorted(k.label() for k in set(reference) - set(measured)),
208
+ label_measured: sorted(k.label() for k in set(measured) - set(reference)),
209
+ },
210
+ "counts": {
211
+ "matched": len(rows),
212
+ f"only_{label_reference}": len(set(reference) - set(measured)),
213
+ f"only_{label_measured}": len(set(measured) - set(reference)),
214
+ },
215
+ }
216
+
217
+
218
+ def render_markdown(report: dict[str, Any]) -> str:
219
+ ref = report["labels"]["reference"]
220
+ mea = report["labels"]["measured"]
221
+ lines = [
222
+ f"# Benchmark delta: {ref} vs {mea}",
223
+ "",
224
+ "**Monitor-only and correlational.** The two sets differ in the environment "
225
+ "and in everything the environment drags along; a delta here is an "
226
+ "observation to explain, never a verdict and never a gate.",
227
+ "",
228
+ f"{report['counts']['matched']} row(s) matched, "
229
+ f"{report['counts'][f'only_{ref}']} only in {ref}, "
230
+ f"{report['counts'][f'only_{mea}']} only in {mea}.",
231
+ "",
232
+ ]
233
+
234
+ for row in report["rows"]:
235
+ lines.append(f"## `{row['row']}`")
236
+ lines.append("")
237
+ lines.append(f"| metric | {ref} | {mea} | Δ | Δ% |")
238
+ lines.append("|---|---:|---:|---:|---:|")
239
+ for metric in row["metrics"]:
240
+ left = metric[ref]
241
+ right = metric[mea]
242
+ delta = metric["delta"]
243
+ relative = metric["relative"]
244
+ lines.append(
245
+ f"| `{metric['metric']}` | {_fmt(left)} | {_fmt(right)} | {_fmt(delta, sign=True)} | {_pct(relative)} |"
246
+ )
247
+ lines.append("")
248
+ for side in (ref, mea):
249
+ provenance = row["provenance"][side]
250
+ command = provenance.get("command") or "-"
251
+ lines.append(f"- {side}: `{command}`")
252
+ lines.append("")
253
+
254
+ for side in (ref, mea):
255
+ missing = report["unmatched"][side]
256
+ if missing:
257
+ lines.append(f"## Only in {side}")
258
+ lines.append("")
259
+ lines.extend(f"- `{name}`" for name in missing)
260
+ lines.append("")
261
+ lines.append(
262
+ "A row on one side only is not a delta. It ran in one environment "
263
+ "and not the other, and treating that as an improvement is the "
264
+ "commonest way this kind of report lies."
265
+ )
266
+ lines.append("")
267
+
268
+ return "\n".join(lines)
269
+
270
+
271
+ def _fmt(value: float | None, *, sign: bool = False) -> str:
272
+ if value is None:
273
+ return "—"
274
+ formatted = f"{value:+.4g}" if sign else f"{value:.4g}"
275
+ return formatted
276
+
277
+
278
+ def _pct(value: float | None) -> str:
279
+ return "—" if value is None else f"{value * 100:+.1f}%"
@@ -4843,6 +4843,56 @@ def _parse_values(raw: str) -> list[float]:
4843
4843
  # --------------------------------------------------------------------------- #
4844
4844
 
4845
4845
 
4846
+ def benchmark_delta(args: argparse.Namespace) -> int:
4847
+ """`rosotacom benchmark delta` — the cross-environment comparison.
4848
+
4849
+ Exits 0 whenever the comparison itself succeeded, including when rows are
4850
+ unmatched or every metric moved. This is monitor-only by construction
4851
+ (`benchmark_delta.compare` puts `monitor_only: true` first in the report):
4852
+ the number is the deliverable, and an exit code would turn "explain this"
4853
+ into "re-run until it is quiet".
4854
+
4855
+ It exits non-zero for the one thing that is a failure — no rows in common,
4856
+ which means the two sides were not the same rows and the report would be
4857
+ an empty table with a confident heading.
4858
+ """
4859
+ from . import benchmark_delta as delta_module
4860
+
4861
+ reference = delta_module.collect([Path(args.reference)])
4862
+ measured = delta_module.collect([Path(args.measured)])
4863
+ report = delta_module.compare(
4864
+ reference,
4865
+ measured,
4866
+ label_reference=args.label_reference,
4867
+ label_measured=args.label_measured,
4868
+ )
4869
+
4870
+ if args.out:
4871
+ Path(args.out).write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
4872
+ markdown = delta_module.render_markdown(report)
4873
+ if args.markdown:
4874
+ Path(args.markdown).write_text(markdown, encoding="utf-8")
4875
+ else:
4876
+ print(markdown)
4877
+
4878
+ counts = report["counts"]
4879
+ print(
4880
+ f"BENCHMARK DELTA: {counts['matched']} row(s) matched, "
4881
+ f"{counts[f'only_{args.label_reference}']} only in {args.label_reference}, "
4882
+ f"{counts[f'only_{args.label_measured}']} only in {args.label_measured} "
4883
+ "(monitor-only)"
4884
+ )
4885
+ if not report["rows"]:
4886
+ print(
4887
+ "rosotacom: error: the two sets share no rows, so there is nothing to "
4888
+ "compare. Check that both sides ran the same registry rows under the "
4889
+ "same profile and RMW.",
4890
+ file=sys.stderr,
4891
+ )
4892
+ return 1
4893
+ return 0
4894
+
4895
+
4846
4896
  def _add_benchmark_common_args(parser: argparse.ArgumentParser, *, ota_benchmark: bool = False) -> None:
4847
4897
  from .cli import (
4848
4898
  OTA_DEFAULT_SUDO_MODE,
@@ -8068,6 +8118,18 @@ def _register_benchmark_band_parsers(benchmark_subparsers: Any) -> None:
8068
8118
  )
8069
8119
  compare_parser.set_defaults(func=benchmark_compare)
8070
8120
 
8121
+ delta_parser = benchmark_subparsers.add_parser(
8122
+ "delta",
8123
+ help="Compare two result.json sets taken in different environments (monitor-only).",
8124
+ )
8125
+ delta_parser.add_argument("reference", help="Directory or result.json for the reference environment.")
8126
+ delta_parser.add_argument("measured", help="Directory or result.json for the measured environment.")
8127
+ delta_parser.add_argument("--label-reference", default="emulated", help="Name for the reference side.")
8128
+ delta_parser.add_argument("--label-measured", default="ota", help="Name for the measured side.")
8129
+ delta_parser.add_argument("--out", default=None, help="Write the JSON report here.")
8130
+ delta_parser.add_argument("--markdown", default=None, help="Write the Markdown summary here.")
8131
+ delta_parser.set_defaults(func=benchmark_delta)
8132
+
8071
8133
  ratchet_parser = benchmark_subparsers.add_parser(
8072
8134
  "ratchet",
8073
8135
  help="Rewrite committed bands from result.json run(s) — bands are never hand-edited.",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev15
3
+ Version: 2.5.dev16
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -778,6 +778,26 @@ performance bands in `budgets.jsonl` via `rosotacom benchmark compare`, and
778
778
  bands move only through `rosotacom benchmark ratchet` — see
779
779
  [docs/performance-bands.md](docs/performance-bands.md) for the band schema,
780
780
  verdicts, and the ratchet workflow (RFC 0007).
781
+
782
+ Two result sets taken in **different environments** — emulated versus a real
783
+ link, or shaped versus unshaped — are compared with `rosotacom benchmark delta`:
784
+
785
+ ```bash
786
+ rosotacom benchmark delta emulated/ ota/ \
787
+ --label-reference emulated --label-measured ota \
788
+ --out delta.json --markdown delta.md
789
+ ```
790
+
791
+ It pairs runs on genre, profile, RMW and session name, so a row that ran in one
792
+ environment and not the other is reported as unmatched rather than silently
793
+ dropped — "it got faster" and "it did not run" must not look the same. Every
794
+ report is labelled `monitor_only` and carries each side's command and profile
795
+ file, so a table pasted into a paper or an issue can be traced back without the
796
+ directory it came from.
797
+
798
+ It exits 0 whatever the numbers say, and non-zero only when the two sets share
799
+ no rows at all — a delta is an observation to explain, and an exit code would
800
+ turn "explain this" into "re-run until it is quiet".
781
801
  For live benchmark probes, `--duration` is the shaped publish window; after that
782
802
  rosotacom stops synthetic publishers and waits `--drain-s` seconds before
783
803
  tearing down shaping, so delayed in-flight messages are not miscounted as loss.
@@ -92,6 +92,7 @@ src/rosotacom/anonymize.py
92
92
  src/rosotacom/bag_ground_truth.py
93
93
  src/rosotacom/benched_set.py
94
94
  src/rosotacom/benchmark.py
95
+ src/rosotacom/benchmark_delta.py
95
96
  src/rosotacom/bundle_check.py
96
97
  src/rosotacom/cli.py
97
98
  src/rosotacom/cli_benchmark.py
@@ -314,6 +315,7 @@ tests/unit/test_anonymize.py
314
315
  tests/unit/test_bag_ground_truth.py
315
316
  tests/unit/test_benched_set.py
316
317
  tests/unit/test_benchmark.py
318
+ tests/unit/test_benchmark_delta.py
317
319
  tests/unit/test_bundle_check.py
318
320
  tests/unit/test_cli.py
319
321
  tests/unit/test_cli_benchmark.py
@@ -93,6 +93,7 @@
93
93
  "src/rosotacom/bag_ground_truth.py",
94
94
  "src/rosotacom/benched_set.py",
95
95
  "src/rosotacom/benchmark.py",
96
+ "src/rosotacom/benchmark_delta.py",
96
97
  "src/rosotacom/bundle_check.py",
97
98
  "src/rosotacom/cli.py",
98
99
  "src/rosotacom/cli_benchmark.py",
@@ -309,6 +310,7 @@
309
310
  "tests/unit/test_bag_ground_truth.py",
310
311
  "tests/unit/test_benched_set.py",
311
312
  "tests/unit/test_benchmark.py",
313
+ "tests/unit/test_benchmark_delta.py",
312
314
  "tests/unit/test_bundle_check.py",
313
315
  "tests/unit/test_cli.py",
314
316
  "tests/unit/test_cli_benchmark.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.4",
3
+ "distance": 16,
4
+ "node": "ge8bcbe8f985ad79a3be256a4e360cdad4b9d13c0",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-12"
8
+ }
@@ -0,0 +1,172 @@
1
+ """Unit tests for the cross-environment benchmark delta (harness #36).
2
+
3
+ Fixtures, not runs. The point of the tool is what it says when two sets differ,
4
+ and a real pair of environments is the one input that cannot be made to differ
5
+ on purpose.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from pathlib import Path
12
+
13
+ from rosotacom.benchmark_delta import collect, compare, interesting_metrics, render_markdown
14
+
15
+
16
+ def write_result(
17
+ directory: Path,
18
+ *,
19
+ genre: str = "capacity",
20
+ profile: str = "cellular-4g-typical",
21
+ rmw: str = "cyclone",
22
+ session: str = "bench_1_1_capacity",
23
+ result: dict | None = None,
24
+ command: str = "rosotacom benchmark capacity",
25
+ ) -> Path:
26
+ directory.mkdir(parents=True, exist_ok=True)
27
+ payload = {
28
+ "schema_version": 1,
29
+ "genre": genre,
30
+ "created_at": "2026-08-12T09:00:00",
31
+ "context": {
32
+ "command": command,
33
+ "profile": {"name": profile},
34
+ "rmw": {"requested": rmw},
35
+ "session": {"name": session, "rmw": rmw},
36
+ "paths": {"profiles_file": "profiles/benchmark-profiles.yaml"},
37
+ },
38
+ "result": result if result is not None else {"capacity": 43000, "slice": {"loss_pct": 0.5}},
39
+ "verdict": {"passed": True, "status": "capacity_found"},
40
+ }
41
+ path = directory / "result.json"
42
+ path.write_text(json.dumps(payload), encoding="utf-8")
43
+ return path
44
+
45
+
46
+ def test_metrics_are_selected_not_swept_up() -> None:
47
+ metrics = interesting_metrics(
48
+ {
49
+ "capacity": 43000,
50
+ "slice": {"loss_pct": 0.5, "knob": "size"},
51
+ "run_id": 17,
52
+ "passed": True,
53
+ }
54
+ )
55
+ assert metrics == {"capacity": 43000.0, "slice.loss_pct": 0.5}
56
+ assert "run_id" not in metrics, "an id's delta means nothing"
57
+ assert "passed" not in metrics, "a bool delta is a category error dressed as -1"
58
+
59
+
60
+ def test_matching_rows_produce_a_delta(tmp_path: Path) -> None:
61
+ write_result(tmp_path / "emu" / "run1", result={"capacity": 43000, "slice": {"loss_pct": 0.5}})
62
+ write_result(tmp_path / "ota" / "run1", result={"capacity": 21000, "slice": {"loss_pct": 2.0}})
63
+
64
+ report = compare(
65
+ collect([tmp_path / "emu"]),
66
+ collect([tmp_path / "ota"]),
67
+ label_reference="emulated",
68
+ label_measured="ota",
69
+ )
70
+
71
+ assert report["counts"]["matched"] == 1
72
+ metrics = {m["metric"]: m for m in report["rows"][0]["metrics"]}
73
+ assert metrics["capacity"]["delta"] == -22000
74
+ assert metrics["capacity"]["relative"] < 0
75
+ assert metrics["slice.loss_pct"]["delta"] == 1.5
76
+
77
+
78
+ def test_a_row_on_one_side_only_is_never_a_delta(tmp_path: Path) -> None:
79
+ """ "It got faster" and "it did not run" must not look the same."""
80
+ write_result(tmp_path / "emu" / "a", session="row_a")
81
+ write_result(tmp_path / "emu" / "b", session="row_b")
82
+ write_result(tmp_path / "ota" / "a", session="row_a")
83
+
84
+ report = compare(
85
+ collect([tmp_path / "emu"]),
86
+ collect([tmp_path / "ota"]),
87
+ label_reference="emulated",
88
+ label_measured="ota",
89
+ )
90
+
91
+ assert report["counts"]["matched"] == 1
92
+ assert report["counts"]["only_emulated"] == 1
93
+ assert any("row_b" in name for name in report["unmatched"]["emulated"])
94
+
95
+
96
+ def test_rows_differing_only_in_profile_do_not_pair(tmp_path: Path) -> None:
97
+ """A tight profile is not a delta against a nominal one."""
98
+ write_result(tmp_path / "emu" / "a", profile="cellular-4g-typical")
99
+ write_result(tmp_path / "ota" / "a", profile="cellular-4g-degraded")
100
+
101
+ report = compare(
102
+ collect([tmp_path / "emu"]),
103
+ collect([tmp_path / "ota"]),
104
+ label_reference="emulated",
105
+ label_measured="ota",
106
+ )
107
+ assert report["counts"]["matched"] == 0
108
+
109
+
110
+ def test_rows_differing_only_in_rmw_do_not_pair(tmp_path: Path) -> None:
111
+ write_result(tmp_path / "emu" / "a", rmw="cyclone")
112
+ write_result(tmp_path / "ota" / "a", rmw="fastdds")
113
+
114
+ report = compare(
115
+ collect([tmp_path / "emu"]),
116
+ collect([tmp_path / "ota"]),
117
+ label_reference="emulated",
118
+ label_measured="ota",
119
+ )
120
+ assert report["counts"]["matched"] == 0
121
+
122
+
123
+ def test_the_report_says_it_is_monitor_only(tmp_path: Path) -> None:
124
+ write_result(tmp_path / "emu" / "a")
125
+ write_result(tmp_path / "ota" / "a")
126
+ report = compare(
127
+ collect([tmp_path / "emu"]),
128
+ collect([tmp_path / "ota"]),
129
+ label_reference="emulated",
130
+ label_measured="ota",
131
+ )
132
+
133
+ assert report["monitor_only"] is True
134
+ markdown = render_markdown(report)
135
+ assert "Monitor-only" in markdown.splitlines()[2]
136
+ assert "never a verdict" in markdown
137
+
138
+
139
+ def test_the_report_carries_its_own_provenance(tmp_path: Path) -> None:
140
+ write_result(tmp_path / "emu" / "a", command="rosotacom benchmark capacity --profile nominal")
141
+ write_result(tmp_path / "ota" / "a", command="rosotacom ota-benchmark row --row s3")
142
+ report = compare(
143
+ collect([tmp_path / "emu"]),
144
+ collect([tmp_path / "ota"]),
145
+ label_reference="emulated",
146
+ label_measured="ota",
147
+ )
148
+ markdown = render_markdown(report)
149
+ assert "--profile nominal" in markdown
150
+ assert "ota-benchmark row --row s3" in markdown
151
+
152
+
153
+ def test_a_metric_present_on_one_side_only_is_flagged(tmp_path: Path) -> None:
154
+ write_result(tmp_path / "emu" / "a", result={"capacity": 1000, "slice": {"loss_pct": 0.0}})
155
+ write_result(tmp_path / "ota" / "a", result={"capacity": 900})
156
+ report = compare(
157
+ collect([tmp_path / "emu"]),
158
+ collect([tmp_path / "ota"]),
159
+ label_reference="emulated",
160
+ label_measured="ota",
161
+ )
162
+ metrics = {m["metric"]: m for m in report["rows"][0]["metrics"]}
163
+ assert metrics["slice.loss_pct"]["delta"] is None
164
+ assert metrics["slice.loss_pct"]["note"] == "present on one side only"
165
+
166
+
167
+ def test_non_result_json_files_are_ignored(tmp_path: Path) -> None:
168
+ write_result(tmp_path / "emu" / "a")
169
+ (tmp_path / "emu" / "noise").mkdir()
170
+ (tmp_path / "emu" / "noise" / "result.json").write_text('{"not": "a benchmark"}', encoding="utf-8")
171
+
172
+ assert len(collect([tmp_path / "emu"])) == 1
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.4",
3
- "distance": 15,
4
- "node": "g2001e7ec951a5e84a9457e2c47947db11655bb1b",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-08-11"
8
- }