rosotacom-dev 2.5.dev47__tar.gz → 2.5.dev49__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 (363) hide show
  1. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/forensics-report.md +7 -0
  3. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0003-metric-backbone.md +11 -0
  4. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/session-configuration.md +29 -7
  5. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +36 -6
  6. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +44 -15
  7. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +66 -4
  8. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +72 -7
  9. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  10. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  11. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_session_pipeline.py +73 -0
  12. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_status_overview_observer.py +75 -0
  13. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_transit.py +38 -0
  14. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.devcontainer/devcontainer.json.template +0 -0
  15. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  16. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/CODEOWNERS +0 -0
  17. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  18. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  19. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  20. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  21. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  22. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  23. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  24. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  25. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/actions/setup-just/action.yml +0 -0
  27. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/dependabot.yml +0 -0
  28. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/benchmark-calibrate.yml +0 -0
  29. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/benchmark-gate.yml +0 -0
  30. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/dev-release.yml +0 -0
  31. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/image-scan.yml +0 -0
  32. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/nightly-e2e.yml +0 -0
  33. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/pr-lightweight.yml +0 -0
  34. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/pr-merge-gate.yml +0 -0
  35. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.github/workflows/release.yml +0 -0
  36. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/.pre-commit-config.yaml +0 -0
  37. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/AGENTS.md +0 -0
  38. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/CONCURRENCY.md +0 -0
  39. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/CONTRIBUTING.md +0 -0
  40. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/DEVELOPMENT_PRINCIPLES.md +0 -0
  41. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/LICENSE.txt +0 -0
  42. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/MANIFEST.in +0 -0
  43. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/README.md +0 -0
  44. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/budgets.jsonl +0 -0
  45. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/README.md +0 -0
  46. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/capacity-size-tight-cyclone.json +0 -0
  47. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  48. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/probe-loss-nominal-cyclone.json +0 -0
  49. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/probe-loss-tight-cyclone.json +0 -0
  50. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/probe-loss-tight-fastdds.json +0 -0
  51. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/calibration/probe-loss-tight-zenoh.json +0 -0
  52. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/codecov.yml +0 -0
  53. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/deployment-configuration.md +0 -0
  54. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/benchmark-ab.md +0 -0
  55. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/benchmark-bag-as-load.md +0 -0
  56. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/ci.md +0 -0
  57. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/ffmpeg-keyframes.md +0 -0
  58. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/README.md +0 -0
  59. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  60. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/delay-alone-no-loss.md +0 -0
  61. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/head-of-line-irregularity.md +0 -0
  62. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  63. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/lighter-message-loss.md +0 -0
  64. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  65. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/repro/reorder_drop_repro.py +0 -0
  66. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  67. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/geo-link-map.md +0 -0
  68. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/github-repository-settings.md +0 -0
  69. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/link-bandwidth.md +0 -0
  70. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/link-trace.md +0 -0
  71. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/owner-runbook.md +0 -0
  72. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/performance-bands.md +0 -0
  73. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/playbooks/README.md +0 -0
  74. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/quality-model.md +0 -0
  75. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release-notes/TEMPLATE.md +0 -0
  76. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release-notes/v2.1.md +0 -0
  77. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release-notes/v2.2.md +0 -0
  78. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release-notes/v2.3.md +0 -0
  79. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release-notes/v2.4.md +0 -0
  80. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/release.md +0 -0
  81. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  82. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0002-expectation-concepts.md +0 -0
  83. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0004-network-profiles.md +0 -0
  84. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  85. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0006-dynamic-qos.md +0 -0
  86. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/0007-regression-gate.md +0 -0
  87. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/rfcs/README.md +0 -0
  88. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/testing.md +0 -0
  89. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/docs/work-items.md +0 -0
  90. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/install.sh +0 -0
  91. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/justfile +0 -0
  92. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/media/control_center.png +0 -0
  93. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/media/echo_test.png +0 -0
  94. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/media/forensics-report-example.png +0 -0
  95. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/media/host.png +0 -0
  96. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/media/vehicle.png +0 -0
  97. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/pyproject.toml +0 -0
  98. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/requirements.txt +0 -0
  99. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/rosotacom.yaml +0 -0
  100. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/setup.cfg +0 -0
  101. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/__init__.py +0 -0
  102. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/__main__.py +0 -0
  103. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/anonymize.py +0 -0
  104. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/bag_ground_truth.py +0 -0
  105. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/benched_set.py +0 -0
  106. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/benchmark.py +0 -0
  107. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/benchmark_delta.py +0 -0
  108. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/bundle_check.py +0 -0
  109. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/cli.py +0 -0
  110. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/cli_benchmark.py +0 -0
  111. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/deployment.py +0 -0
  112. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/ffmpeg_packet.py +0 -0
  113. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/forensics.py +0 -0
  114. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/geomap.py +0 -0
  115. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/image_cache.py +0 -0
  116. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/netem_dist.py +0 -0
  117. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/network_profiles.py +0 -0
  118. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/network_shaper.py +0 -0
  119. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/plots.py +0 -0
  120. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/py.typed +0 -0
  121. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/benched-set.yaml +0 -0
  122. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/.gitignore +0 -0
  123. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/README.md +0 -0
  124. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/__init__.py +0 -0
  125. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  126. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  127. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  128. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  129. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  130. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  131. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  132. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  133. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  134. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  135. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  136. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  137. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  138. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  139. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  140. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  141. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  142. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  143. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  144. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  145. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  146. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  147. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  148. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  149. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  150. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  151. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  152. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  153. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  154. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  155. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  156. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  157. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  158. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  159. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  160. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  161. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  162. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  163. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  164. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  165. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  166. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  167. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  168. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  169. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  170. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  171. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  172. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  173. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  174. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  175. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  176. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  177. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ros2docker.json.example +0 -0
  201. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  202. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  203. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  204. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  205. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  206. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  207. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  208. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  209. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  210. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  211. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  212. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  213. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  214. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  215. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  216. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  217. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  218. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  219. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  220. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  221. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  222. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  223. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  224. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  225. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  226. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  227. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  228. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  229. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  230. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  231. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  232. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  233. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  234. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  235. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  236. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  237. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  238. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  239. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  240. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  241. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  242. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  243. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  244. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  245. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  246. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  247. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  248. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  249. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  250. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  251. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  252. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  253. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  254. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  255. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  256. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  257. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  258. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  259. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  260. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  261. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  262. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  263. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  264. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  265. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  266. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  267. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  268. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  269. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  270. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  271. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  272. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  273. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  274. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  275. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  276. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  277. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  278. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  279. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  280. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  281. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  282. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  283. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  284. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  285. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  286. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  287. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  288. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  289. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/status_eval.py +0 -0
  290. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/stream_stats.py +0 -0
  291. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/trace_profiles.py +0 -0
  292. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/transit.py +0 -0
  293. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom/video_quality.py +0 -0
  294. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
  295. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  296. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  297. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  298. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
  299. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  300. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/terminology.md +0 -0
  301. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_benched_set_registry.py +0 -0
  302. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_findings.py +0 -0
  303. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_markdown_links.py +0 -0
  304. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_ota_source_bundle.py +0 -0
  305. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_package_metadata.py +0 -0
  306. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_packaged_resources.py +0 -0
  307. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  308. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_pytest_policy.py +0 -0
  309. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_readme_examples.py +0 -0
  310. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_security_maintenance_config.py +0 -0
  311. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_session_configuration_doc.py +0 -0
  312. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_session_template_substitution.py +0 -0
  313. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/contract/test_workflow_contracts.py +0 -0
  314. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/conftest.py +0 -0
  315. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_anonymize_e2e.py +0 -0
  316. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_benchmark_ab.py +0 -0
  317. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_benchmark_capacity.py +0 -0
  318. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_benchmark_replay.py +0 -0
  319. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_parallel_smoke.py +0 -0
  320. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_smoke.py +0 -0
  321. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_timeline_stepping.py +0 -0
  322. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/e2e/test_video_quality_e2e.py +0 -0
  323. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/generate_rmw_matrix.py +0 -0
  324. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  325. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  326. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  327. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  328. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  329. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_anonymize.py +0 -0
  330. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_bag_ground_truth.py +0 -0
  331. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_benched_set.py +0 -0
  332. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_benchmark.py +0 -0
  333. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_benchmark_delta.py +0 -0
  334. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_bundle_check.py +0 -0
  335. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_catmux_pane_logging.py +0 -0
  336. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_cli.py +0 -0
  337. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_cli_benchmark.py +0 -0
  338. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_concurrency.py +0 -0
  339. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_deployment.py +0 -0
  340. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_ffmpeg_flags.py +0 -0
  341. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_ffmpeg_packet.py +0 -0
  342. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_field_profiles.py +0 -0
  343. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_forensics.py +0 -0
  344. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_geomap.py +0 -0
  345. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_image_cache.py +0 -0
  346. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_link_bytes.py +0 -0
  347. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_link_trace.py +0 -0
  348. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_netem_dist.py +0 -0
  349. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_network_profiles.py +0 -0
  350. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_network_shaper.py +0 -0
  351. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_ota_unwrapper.py +0 -0
  352. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_ota_wrapper.py +0 -0
  353. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_ota_xml.py +0 -0
  354. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_playout_pacer.py +0 -0
  355. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_plots.py +0 -0
  356. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_qos_fallback_logging.py +0 -0
  357. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_relay_optional_parameters.py +0 -0
  358. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_stage_latency.py +0 -0
  359. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_status_eval.py +0 -0
  360. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_status_overview.py +0 -0
  361. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_stream_stats.py +0 -0
  362. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/tests/unit/test_trace_profiles.py +0 -0
  363. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev49}/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.dev47
3
+ Version: 2.5.dev49
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -93,6 +93,13 @@ delivered below it; `unknown` without sender-side records. `min_offered_hz` /
93
93
  rate — they never enter the delivered view, where the cross-peer join would
94
94
  upgrade receiver-inferred lost rows to delivered.
95
95
 
96
+ The sending peer's status overview emits those rows itself for every wrapped
97
+ stream (stage `com_out`, status `sent`, sequence gaps in its own observation as
98
+ `unobserved` — see RFC 0003): record on both machines and place both peers'
99
+ `logs/<peer>/status/` under one instance directory before running the report,
100
+ and the `unknown` verdicts disappear. Recordings made before the sender-side
101
+ rows existed stay `unknown` for their outbound streams.
102
+
96
103
  Events across streams and kinds are grouped into **incidents** when their
97
104
  windows touch (merge gap defaults to one bin): one degradation moment, several
98
105
  lenses on it.
@@ -56,6 +56,17 @@ Every metric on the wishlist is a projection of one recorded object per message:
56
56
  inter_arrival / jitter — receiver-local regularity
57
57
  ```
58
58
 
59
+ The **sending peer** records its own view of the same message at `com_out`
60
+ (`direction: outbound`): `t_wrap` plus `t_com_out` (both its own clock, so
61
+ `sections.wrap_to_com_out_ms` is the relay hop, and there is no θ). Its status
62
+ values are deliberately different — `sent` for an observed message,
63
+ `unobserved` for a gap in its own observation (the local best-effort observer
64
+ missed it; the link cannot lose a message before the link) — so the offline
65
+ join, which ranks only `lost < reordered < delivered`, can enrich a receiver
66
+ row with sender timestamps but a sender row can never overwrite a receiver's
67
+ `lost` verdict. This is what lets the forensics report classify a rate collapse
68
+ as `source` vs `link`: offered (sender `t_wrap` bins) against delivered.
69
+
59
70
  **Why `epoch` is part of the key.** `universal_ota_wrapper` counts per topic
60
71
  from zero, so a peer that restarts inside an instance replays sequence numbers
61
72
  the receiver has already seen. The receiver detects the restart from the
@@ -568,13 +568,39 @@ number being reported.
568
568
  - `bit_rate` (int)
569
569
  - `encoder_av_options` (string)
570
570
 
571
- #### Receiver-side playout pacing (manual node)
571
+ #### Receiver-side playout pacing
572
572
 
573
573
  A receiver that hands every packet to the decoder the moment it arrives turns
574
574
  network delay jitter into display stutter. `com_py playout_pacer` re-times a
575
575
  received packet stream to `stamp + budget` (budget adaptive above the fastest
576
576
  observed path, clock-offset-proof; late packets pass through immediately, order
577
- always preserved — the decoder chain sees every packet):
577
+ always preserved — the decoder chain sees every packet). On the 2026-08-17 CCNG
578
+ field trace this removes ~95 % of delay-caused >200 ms display gaps at ~100 ms
579
+ median added age (adaptive mode).
580
+
581
+ Declaratively, add a `playout` block to the transport:
582
+
583
+ ```yaml
584
+ transport:
585
+ type: "ffmpeg"
586
+ gop_size: 3
587
+ remote_republish: compressed
588
+ playout: # receiver-side; requires remote_republish
589
+ adaptive: true # default true; false uses target_ms as a fixed budget
590
+ target_ms: 350 # fixed budget when adaptive: false
591
+ min_ms: 100 # adaptive clamp above the observed delay floor
592
+ max_ms: 800
593
+ ```
594
+
595
+ The receiving peer then runs the pacer in its own `PACE` window against the
596
+ arrived encoded stream, and the reverse republish decodes `<encoded>/paced`
597
+ instead of the raw arrival — the delivered topic name is unchanged, which is
598
+ why `playout` requires `remote_republish`: without a republish the pacer's
599
+ `/paced` suffix would rename what the receiving application subscribes to.
600
+ The sender's own preview decode (`local_republish`) is never paced — it reads
601
+ the local copy of what was just encoded, so there is no jitter to absorb.
602
+
603
+ The node also runs standalone against any packet stream:
578
604
 
579
605
  ```bash
580
606
  ros2 run com_py playout_pacer --ros-args \
@@ -583,11 +609,7 @@ ros2 run com_py playout_pacer --ros-args \
583
609
  ```
584
610
 
585
611
  It republishes on `<topic>/paced` plus `.../paced/budget_ms` and
586
- `.../paced/queue_depth` debug topics; point the reverse republish (or any
587
- decoder) at the paced name. On the 2026-08-17 CCNG field trace this removes
588
- ~95 % of delay-caused >200 ms display gaps at ~100 ms median added age
589
- (adaptive mode). Declarative wiring as a `transport.playout` block is tracked
590
- in issue #284.
612
+ `.../paced/queue_depth` debug topics.
591
613
 
592
614
  `type: foxglove` supports (CompressedVideo):
593
615
  - `gop_size` (int)
@@ -253,6 +253,22 @@ class StageObserver(Node):
253
253
  ),
254
254
  None,
255
255
  )
256
+ com_out = next(
257
+ (
258
+ context
259
+ for context in contexts
260
+ if context.get("stage") == "com_out"
261
+ and context.get("direction") == "outbound"
262
+ ),
263
+ None,
264
+ )
265
+ keyframe: Optional[bool] = None
266
+ if com_in is not None or com_out is not None:
267
+ try:
268
+ if msg.msg_type == FFMPEG_PACKET_TYPE:
269
+ keyframe = parse_keyframe_flag(bytes(msg.serialized_msg))
270
+ except Exception:
271
+ keyframe = None
256
272
  if com_in is not None:
257
273
  seq = int(msg.seq)
258
274
  t_wrap_s = _stamp_seconds(msg.header.stamp)
@@ -262,12 +278,6 @@ class StageObserver(Node):
262
278
  clock_offset_s = estimate["offset_s"]
263
279
  rtt_s = estimate["rtt_s"]
264
280
  delay_s = now_ros_s + clock_offset_s - t_wrap_s
265
- keyframe: Optional[bool] = None
266
- try:
267
- if msg.msg_type == FFMPEG_PACKET_TYPE:
268
- keyframe = parse_keyframe_flag(bytes(msg.serialized_msg))
269
- except Exception:
270
- keyframe = None
271
281
  transit = {
272
282
  "peer": com_in.get("peer"),
273
283
  "source": com_in.get("source"),
@@ -279,6 +289,26 @@ class StageObserver(Node):
279
289
  "t_com_in": now_ros_s,
280
290
  "keyframe": keyframe,
281
291
  }
292
+ elif com_out is not None:
293
+ # The sending peer's own view of the same message: what the
294
+ # source handed to the link, before any OTA hop. The wrapper
295
+ # stamped `t_wrap` on THIS machine, so no peer offset applies
296
+ # (same rule as the generic local-stage branch below) and the
297
+ # delay is the wrap->com_out relay hop, not an OTA number.
298
+ seq = int(msg.seq)
299
+ t_wrap_s = _stamp_seconds(msg.header.stamp)
300
+ delay_s = now_ros_s - t_wrap_s
301
+ transit = {
302
+ "peer": com_out.get("peer"),
303
+ "source": com_out.get("source"),
304
+ "target": com_out.get("target"),
305
+ "topic": com_out.get("base"),
306
+ "direction": com_out.get("direction"),
307
+ "stage": com_out.get("stage"),
308
+ "t_wrap": t_wrap_s,
309
+ "t_com_out": now_ros_s,
310
+ "keyframe": keyframe,
311
+ }
282
312
  else:
283
313
  header = getattr(msg, "header", None)
284
314
  stamp = getattr(header, "stamp", None) if header is not None else None
@@ -451,6 +451,15 @@ class StageObservation:
451
451
  self.sequence_events.append((now, expected_delta, missing, reordered, missing))
452
452
 
453
453
  if transit is not None and seq is not None:
454
+ # A sender-side com_out row observes its own machine's wrapper:
455
+ # a sequence gap there means the local observer missed the
456
+ # message, not that the link lost it, and an observed message
457
+ # was *sent*, not delivered. The distinct status values also
458
+ # keep the offline join honest: `join_transit_records` ranks
459
+ # only lost < reordered < delivered, so a sender row can never
460
+ # overwrite the receiving side's `lost` verdict for the same
461
+ # (source, topic, epoch, seq).
462
+ outbound = transit.get("direction") == "outbound"
454
463
  common = {
455
464
  "kind": "transit",
456
465
  "peer": transit.get("peer"),
@@ -465,16 +474,21 @@ class StageObservation:
465
474
  "epoch": self.epoch,
466
475
  }
467
476
  if missing_start is not None:
477
+ gap_time_field = "t_com_out" if outbound else "t_com_in"
468
478
  for missing_seq in range(missing_start, int(seq)):
469
479
  self.transit_records.append(
470
480
  {
471
481
  **common,
472
482
  "seq": missing_seq,
473
- "status": "lost",
483
+ "status": "unobserved" if outbound else "lost",
474
484
  "t_wrap": None,
475
- "t_com_in": None,
485
+ gap_time_field: None,
476
486
  "clock_offset_ms": None,
477
- "sections": {"ota_hop_ms": None},
487
+ "sections": (
488
+ {"wrap_to_com_out_ms": None}
489
+ if outbound
490
+ else {"ota_hop_ms": None}
491
+ ),
478
492
  "size_bytes": None,
479
493
  "inter_arrival_ms": None,
480
494
  "jitter_ms": None,
@@ -493,27 +507,42 @@ class StageObservation:
493
507
  if inter_arrival_s is not None:
494
508
  self.last_inter_arrival_s = inter_arrival_s
495
509
  self.last_transit_recv_wall = wall
510
+ if outbound:
511
+ # Same clock on both ends of this hop, so there is no
512
+ # corrected/uncorrected pair and no OTA section: the only
513
+ # honest number is the wrap->com_out relay hop.
514
+ status = "sent"
515
+ time_fields = {"t_com_out": transit.get("t_com_out")}
516
+ sections: Dict[str, Any] = {
517
+ "wrap_to_com_out_ms": (
518
+ round(delay_s * 1000.0, 3) if delay_s is not None else None
519
+ ),
520
+ }
521
+ else:
522
+ status = sequence_status
523
+ time_fields = {"t_com_in": transit.get("t_com_in")}
524
+ sections = {
525
+ "ota_hop_ms": (
526
+ round(delay_s * 1000.0, 3) if delay_s is not None else None
527
+ ),
528
+ "ota_hop_uncorrected_ms": (
529
+ round(raw_delay_s * 1000.0, 3)
530
+ if raw_delay_s is not None
531
+ else None
532
+ ),
533
+ }
496
534
  row = {
497
535
  **common,
498
536
  "seq": int(seq),
499
- "status": sequence_status,
537
+ "status": status,
500
538
  "t_wrap": transit.get("t_wrap"),
501
- "t_com_in": transit.get("t_com_in"),
539
+ **time_fields,
502
540
  "clock_offset_ms": (
503
541
  round(clock_offset_s * 1000.0, 3)
504
542
  if clock_offset_s is not None
505
543
  else None
506
544
  ),
507
- "sections": {
508
- "ota_hop_ms": (
509
- round(delay_s * 1000.0, 3) if delay_s is not None else None
510
- ),
511
- "ota_hop_uncorrected_ms": (
512
- round(raw_delay_s * 1000.0, 3)
513
- if raw_delay_s is not None
514
- else None
515
- ),
516
- },
545
+ "sections": sections,
517
546
  "size_bytes": size,
518
547
  "inter_arrival_ms": (
519
548
  round(inter_arrival_s * 1000.0, 3)
@@ -232,15 +232,49 @@ parameters:
232
232
  irt_1_topic: # e.g. /sensors/camera/front_wide/image_color
233
233
  irt_1_transport: # e.g. raw
234
234
  irt_1_out_transport: compressed # e.g. raw, compressed – determines the output remap format
235
+ irt_1_paced: # "true" -> decode reads ${irt_1_topic}/paced (playout pacer, PACE window)
235
236
  irt_2_topic: # e.g. /sensors/front_camera/image_raw/compressed
236
237
  irt_2_transport: # e.g. raw
237
238
  irt_2_out_transport: compressed # e.g. raw, compressed – determines the output remap format
239
+ irt_2_paced: # "true" -> decode reads ${irt_2_topic}/paced
238
240
  irt_3_topic: # e.g. /sensor/front_camera/image_color/compressed
239
241
  irt_3_transport: # e.g. raw
240
242
  irt_3_out_transport: compressed # e.g. raw, compressed – determines the output remap format
243
+ irt_3_paced: # "true" -> decode reads ${irt_3_topic}/paced
241
244
  irt_4_topic: # e.g. /sensor/front_camera/image_color/compressed
242
245
  irt_4_transport: # e.g. raw
243
246
  irt_4_out_transport: compressed # e.g. raw, compressed – determines the output remap format
247
+ irt_4_paced: # "true" -> decode reads ${irt_4_topic}/paced
248
+
249
+ # Receiver-side playout pacing (transport.playout in the session definition):
250
+ # re-times the arrived encoded stream on '<topic>/paced' so the decode sees a
251
+ # steady cadence instead of the network's delay jitter. Never reorders, never
252
+ # drops; late packets pass through immediately.
253
+ pace: false
254
+ pace_1_topic: # the arrived encoded stream, e.g. /camera/image/ffmpeg
255
+ pace_1_msg_type: # e.g. ffmpeg_image_transport_msgs/msg/FFMPEGPacket
256
+ pace_1_adaptive: # "true" | "false"
257
+ pace_1_target_ms: # fixed budget when adaptive is "false"
258
+ pace_1_min_ms: # adaptive clamp above the observed delay floor
259
+ pace_1_max_ms:
260
+ pace_2_topic:
261
+ pace_2_msg_type:
262
+ pace_2_adaptive:
263
+ pace_2_target_ms:
264
+ pace_2_min_ms:
265
+ pace_2_max_ms:
266
+ pace_3_topic:
267
+ pace_3_msg_type:
268
+ pace_3_adaptive:
269
+ pace_3_target_ms:
270
+ pace_3_min_ms:
271
+ pace_3_max_ms:
272
+ pace_4_topic:
273
+ pace_4_msg_type:
274
+ pace_4_adaptive:
275
+ pace_4_target_ms:
276
+ pace_4_min_ms:
277
+ pace_4_max_ms:
244
278
 
245
279
  common:
246
280
  before_commands:
@@ -623,6 +657,30 @@ windows:
623
657
  - args+=(--remap "${in_remap}:=${it_4_topic}" --remap "out/${it_4_transport}:=${it_4_topic}/${outgoing_suffix}")
624
658
  - echo ${args[@]}
625
659
  - ${args[@]}
660
+ - name: PACE
661
+ if: pace
662
+ layout: even-vertical
663
+ splits:
664
+ - commands:
665
+ - if [ '${pace_1_topic}' = "None" ]; then exit; fi
666
+ - args=(ros2 run com_py playout_pacer --ros-args -p topic:=${pace_1_topic} -p msg_type:=${pace_1_msg_type} -p adaptive:=${pace_1_adaptive} -p target_ms:=${pace_1_target_ms} -p min_ms:=${pace_1_min_ms} -p max_ms:=${pace_1_max_ms} -p qos_config_file:='${qos_config_file}')
667
+ - echo ${args[@]}
668
+ - ${args[@]}
669
+ - commands:
670
+ - if [ '${pace_2_topic}' = "None" ]; then exit; fi
671
+ - args=(ros2 run com_py playout_pacer --ros-args -p topic:=${pace_2_topic} -p msg_type:=${pace_2_msg_type} -p adaptive:=${pace_2_adaptive} -p target_ms:=${pace_2_target_ms} -p min_ms:=${pace_2_min_ms} -p max_ms:=${pace_2_max_ms} -p qos_config_file:='${qos_config_file}')
672
+ - echo ${args[@]}
673
+ - ${args[@]}
674
+ - commands:
675
+ - if [ '${pace_3_topic}' = "None" ]; then exit; fi
676
+ - args=(ros2 run com_py playout_pacer --ros-args -p topic:=${pace_3_topic} -p msg_type:=${pace_3_msg_type} -p adaptive:=${pace_3_adaptive} -p target_ms:=${pace_3_target_ms} -p min_ms:=${pace_3_min_ms} -p max_ms:=${pace_3_max_ms} -p qos_config_file:='${qos_config_file}')
677
+ - echo ${args[@]}
678
+ - ${args[@]}
679
+ - commands:
680
+ - if [ '${pace_4_topic}' = "None" ]; then exit; fi
681
+ - args=(ros2 run com_py playout_pacer --ros-args -p topic:=${pace_4_topic} -p msg_type:=${pace_4_msg_type} -p adaptive:=${pace_4_adaptive} -p target_ms:=${pace_4_target_ms} -p min_ms:=${pace_4_min_ms} -p max_ms:=${pace_4_max_ms} -p qos_config_file:='${qos_config_file}')
682
+ - echo ${args[@]}
683
+ - ${args[@]}
626
684
  - name: IRT
627
685
  if: irt
628
686
  layout: even-vertical
@@ -630,28 +688,32 @@ windows:
630
688
  - commands:
631
689
  - if [ '${irt_1_topic}' = "None" ]; then exit; fi
632
690
  - if [[ "${irt_1_out_transport}" == "raw" ]]; then out_remap="out"; else out_remap="out/${irt_1_out_transport}"; fi
691
+ - if [[ "${irt_1_paced}" != "None" ]]; then irt_in_topic="${irt_1_topic}/paced"; else irt_in_topic="${irt_1_topic}"; fi
633
692
  - args=(ros2 run image_transport republish --ros-args -r __node:=irt_republish1 -p in_transport:=${irt_1_transport} -p out_transport:=${irt_1_out_transport}
634
- --remap in/${irt_1_transport}:=${irt_1_topic} --remap ${out_remap}:=${irt_1_topic}/${irt_1_out_transport})
693
+ --remap in/${irt_1_transport}:=${irt_in_topic} --remap ${out_remap}:=${irt_1_topic}/${irt_1_out_transport})
635
694
  - echo ${args[@]}
636
695
  - ${args[@]}
637
696
  - commands:
638
697
  - if [ '${irt_2_topic}' = "None" ]; then exit; fi
639
698
  - if [[ "${irt_2_out_transport}" == "raw" ]]; then out_remap="out"; else out_remap="out/${irt_2_out_transport}"; fi
699
+ - if [[ "${irt_2_paced}" != "None" ]]; then irt_in_topic="${irt_2_topic}/paced"; else irt_in_topic="${irt_2_topic}"; fi
640
700
  - args=(ros2 run image_transport republish --ros-args -r __node:=irt_republish2 -p in_transport:=${irt_2_transport} -p out_transport:=${irt_2_out_transport}
641
- --remap in/${irt_2_transport}:=${irt_2_topic} --remap ${out_remap}:=${irt_2_topic}/${irt_2_out_transport})
701
+ --remap in/${irt_2_transport}:=${irt_in_topic} --remap ${out_remap}:=${irt_2_topic}/${irt_2_out_transport})
642
702
  - echo ${args[@]}
643
703
  - ${args[@]}
644
704
  - commands:
645
705
  - if [ '${irt_3_topic}' = "None" ]; then exit; fi
646
706
  - if [[ "${irt_3_out_transport}" == "raw" ]]; then out_remap="out"; else out_remap="out/${irt_3_out_transport}"; fi
707
+ - if [[ "${irt_3_paced}" != "None" ]]; then irt_in_topic="${irt_3_topic}/paced"; else irt_in_topic="${irt_3_topic}"; fi
647
708
  - args=(ros2 run image_transport republish --ros-args -r __node:=irt_republish3 -p in_transport:=${irt_3_transport} -p out_transport:=${irt_3_out_transport}
648
- --remap in/${irt_3_transport}:=${irt_3_topic} --remap ${out_remap}:=${irt_3_topic}/${irt_3_out_transport})
709
+ --remap in/${irt_3_transport}:=${irt_in_topic} --remap ${out_remap}:=${irt_3_topic}/${irt_3_out_transport})
649
710
  - echo ${args[@]}
650
711
  - ${args[@]}
651
712
  - commands:
652
713
  - if [ '${irt_4_topic}' = "None" ]; then exit; fi
653
714
  - if [[ "${irt_4_out_transport}" == "raw" ]]; then out_remap="out"; else out_remap="out/${irt_4_out_transport}"; fi
715
+ - if [[ "${irt_4_paced}" != "None" ]]; then irt_in_topic="${irt_4_topic}/paced"; else irt_in_topic="${irt_4_topic}"; fi
654
716
  - args=(ros2 run image_transport republish --ros-args -r __node:=irt_republish4 -p in_transport:=${irt_4_transport} -p out_transport:=${irt_4_out_transport}
655
- --remap in/${irt_4_transport}:=${irt_4_topic} --remap ${out_remap}:=${irt_4_topic}/${irt_4_out_transport})
717
+ --remap in/${irt_4_transport}:=${irt_in_topic} --remap ${out_remap}:=${irt_4_topic}/${irt_4_out_transport})
656
718
  - echo ${args[@]}
657
719
  - ${args[@]}
@@ -92,6 +92,13 @@ class TransportSpec:
92
92
  #: image_transport itself, or one that only forwards or records packets,
93
93
  #: does not want a second copy of every frame.
94
94
  remote_republish: Optional[str] = None
95
+ #: Receiver-side playout pacing (issue #284): re-time the arrived packet
96
+ #: stream to smooth network delay jitter before the reverse republish
97
+ #: decodes it. Keys: adaptive / target_ms / min_ms / max_ms (validated in
98
+ #: the parser; defaults live in the playout_pacer node). Requires
99
+ #: `remote_republish`, because the pacer publishes on `<encoded>/paced`
100
+ #: and only the republish remap keeps the delivered topic name unchanged.
101
+ playout: Optional[Dict[str, Any]] = None
95
102
 
96
103
 
97
104
  @dataclass
@@ -1692,12 +1699,45 @@ def _compute_pipeline(
1692
1699
  )
1693
1700
  return str(value)
1694
1701
 
1695
- params = {k: v for k, v in t.items() if k not in ("type", *republish_keys)}
1702
+ remote_republish = _republish("remote_republish")
1703
+
1704
+ playout: Optional[Dict[str, Any]] = None
1705
+ playout_cfg = t.get("playout")
1706
+ if playout_cfg is not None:
1707
+ if not isinstance(playout_cfg, dict):
1708
+ raise ValueError(f"transport.playout must be a mapping for topic '{entry.base}'")
1709
+ allowed = {"adaptive", "target_ms", "min_ms", "max_ms"}
1710
+ unknown = sorted(set(playout_cfg) - allowed)
1711
+ if unknown:
1712
+ raise ValueError(
1713
+ f"transport.playout for topic '{entry.base}' has unknown keys {unknown}; "
1714
+ f"allowed: {sorted(allowed)}"
1715
+ )
1716
+ if remote_republish is None:
1717
+ raise ValueError(
1718
+ f"transport.playout for topic '{entry.base}' requires remote_republish: "
1719
+ "the pacer publishes on '<encoded>/paced', and only the reverse republish's "
1720
+ "input remap keeps the delivered topic name unchanged. Without a republish "
1721
+ "the receiver would have to subscribe a renamed topic."
1722
+ )
1723
+ for key in ("target_ms", "min_ms", "max_ms"):
1724
+ if key in playout_cfg and (not isinstance(playout_cfg[key], (int, float)) or playout_cfg[key] <= 0):
1725
+ raise ValueError(f"transport.playout.{key} must be a positive number for topic '{entry.base}'")
1726
+ min_ms = float(playout_cfg.get("min_ms", 100.0))
1727
+ max_ms = float(playout_cfg.get("max_ms", 800.0))
1728
+ if max_ms < min_ms:
1729
+ raise ValueError(f"transport.playout.max_ms must be >= min_ms for topic '{entry.base}'")
1730
+ if "adaptive" in playout_cfg and not isinstance(playout_cfg["adaptive"], bool):
1731
+ raise ValueError(f"transport.playout.adaptive must be a boolean for topic '{entry.base}'")
1732
+ playout = dict(playout_cfg)
1733
+
1734
+ params = {k: v for k, v in t.items() if k not in ("type", "playout", *republish_keys)}
1696
1735
  transport = TransportSpec(
1697
1736
  type=ttype,
1698
1737
  params=params,
1699
1738
  local_republish=_republish("local_republish"),
1700
- remote_republish=_republish("remote_republish"),
1739
+ remote_republish=remote_republish,
1740
+ playout=playout,
1701
1741
  )
1702
1742
 
1703
1743
  topic = entry.base
@@ -3201,21 +3241,46 @@ def func(
3201
3241
  # it encoded and (b) the decode of what arrived. Both run on *this* peer;
3202
3242
  # what differs is which copy they decode and which transport the session
3203
3243
  # asked each of them to publish in.
3204
- irt_all: List[Tuple[str, str, str]] = []
3244
+ # The sender's own preview decode is never paced: it reads the local
3245
+ # copy of what it just encoded, so there is no network jitter to
3246
+ # absorb. Playout pacing exists on the receiving side only.
3247
+ irt_all: List[Tuple[str, str, str, Optional[Dict[str, Any]]]] = []
3205
3248
  for t, tspec in irt_items_local:
3206
3249
  assert tspec is not None
3207
- irt_all.append((t, tspec.type, str(tspec.local_republish)))
3208
- irt_all.extend((topic, tspec.type, str(tspec.remote_republish)) for topic, tspec in irt_items_remote)
3250
+ irt_all.append((t, tspec.type, str(tspec.local_republish), None))
3251
+ irt_all.extend(
3252
+ (topic, tspec.type, str(tspec.remote_republish), tspec.playout) for topic, tspec in irt_items_remote
3253
+ )
3209
3254
  _assert_max("irt", irt_all, 4)
3210
3255
  if irt_all:
3211
3256
  items2 = [("irt", True)]
3212
- for i, (topic, _ttype, _out_transport) in enumerate(irt_all, 1):
3257
+ for i, (topic, _ttype, _out_transport, _playout) in enumerate(irt_all, 1):
3213
3258
  items2.append((f"irt_{i}_topic", topic))
3214
- for i, (_topic, ttype, out_transport) in enumerate(irt_all, 1):
3259
+ for i, (_topic, ttype, out_transport, playout) in enumerate(irt_all, 1):
3215
3260
  items2.append((f"irt_{i}_transport", ttype))
3216
3261
  items2.append((f"irt_{i}_out_transport", out_transport))
3262
+ if playout is not None:
3263
+ # Points the decode's input remap at '<topic>/paced'; the
3264
+ # pacer itself runs in the PACE window below.
3265
+ items2.append((f"irt_{i}_paced", "true"))
3217
3266
  blocks.append(PluginBlock("irt", items2))
3218
3267
 
3268
+ paced_entries = [
3269
+ (i, topic, ttype, playout)
3270
+ for i, (topic, ttype, _out_transport, playout) in enumerate(irt_all, 1)
3271
+ if playout is not None
3272
+ ]
3273
+ if paced_entries:
3274
+ items_pace: List[Tuple[str, Any]] = [("pace", True)]
3275
+ for i, topic, ttype, playout in paced_entries:
3276
+ items_pace.append((f"pace_{i}_topic", topic))
3277
+ items_pace.append((f"pace_{i}_msg_type", TRANSPORT_OUTPUT_TYPES[ttype]))
3278
+ items_pace.append((f"pace_{i}_adaptive", "true" if playout.get("adaptive", True) else "false"))
3279
+ items_pace.append((f"pace_{i}_target_ms", float(playout.get("target_ms", 350.0))))
3280
+ items_pace.append((f"pace_{i}_min_ms", float(playout.get("min_ms", 100.0))))
3281
+ items_pace.append((f"pace_{i}_max_ms", float(playout.get("max_ms", 800.0))))
3282
+ blocks.append(PluginBlock("pace", items_pace))
3283
+
3219
3284
  if nor_items:
3220
3285
  items2 = [("nor", True)]
3221
3286
  nor_prefix = f"/{peer_name[remote]}" if bool(inbound_cfg.get("keep_source_prefix", False)) else ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev47
3
+ Version: 2.5.dev49
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tag": "2.4",
3
- "distance": 47,
4
- "node": "g73fa1de72a3f04bd36058dcc88da7d62b6668edb",
3
+ "distance": 49,
4
+ "node": "g992b77df07a37e0060e78e24fda488156bdce238",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-18"
@@ -233,3 +233,76 @@ def test_generated_peer_files_wire_the_wrapped_camera(tmp_path: Path) -> None:
233
233
  # ... and what the application finally reads is the decoded image.
234
234
  assert stages["native_in"]["topic"] == "/camera/image/ffmpeg/raw"
235
235
  assert stages["native_in"]["type"] == "sensor_msgs/msg/Image"
236
+
237
+
238
+ # ---------------------------------------------------------------------------
239
+ # receiver-side playout pacing (transport.playout, issue #284)
240
+ # ---------------------------------------------------------------------------
241
+
242
+
243
+ def test_playout_requires_a_receiver_republish() -> None:
244
+ """Without a reverse republish the pacer would rename the delivered topic."""
245
+ with pytest.raises(ValueError, match="requires remote_republish"):
246
+ _pipeline({"transport": {"type": "ffmpeg", "playout": {"min_ms": 100}}})
247
+
248
+
249
+ def test_playout_rejects_unknown_keys_and_bad_numbers() -> None:
250
+ with pytest.raises(ValueError, match="unknown keys"):
251
+ _pipeline(_ffmpeg(playout={"budget_ms": 350}))
252
+ with pytest.raises(ValueError, match="positive number"):
253
+ _pipeline(_ffmpeg(playout={"target_ms": -1}))
254
+ with pytest.raises(ValueError, match="max_ms must be >= min_ms"):
255
+ _pipeline(_ffmpeg(playout={"min_ms": 500, "max_ms": 100}))
256
+
257
+
258
+ def test_playout_never_leaks_into_encoder_params() -> None:
259
+ _entry, pipe = _pipeline(_ffmpeg(gop_size=3, playout={"min_ms": 100, "max_ms": 800}))
260
+ tspec = pipe["transport"]
261
+ assert tspec.playout == {"min_ms": 100, "max_ms": 800}
262
+ assert "playout" not in tspec.params
263
+ assert tspec.params["gop_size"] == 3
264
+
265
+
266
+ def test_playout_does_not_rename_the_delivered_topic() -> None:
267
+ """The pacer sits between unwrapper and decode; what the application reads keeps its name."""
268
+ _entry, plain = _pipeline(_ffmpeg())
269
+ _entry, paced = _pipeline(_ffmpeg(playout={}))
270
+ assert generator._delivered_topic(paced, paced["final"]) == generator._delivered_topic(plain, plain["final"])
271
+
272
+
273
+ def test_generated_peer_files_wire_the_pacer(tmp_path: Path) -> None:
274
+ processing = {
275
+ "transport": {
276
+ "type": "ffmpeg",
277
+ "gop_size": 3,
278
+ "remote_republish": "compressed",
279
+ "playout": {"adaptive": True, "min_ms": 120, "max_ms": 600},
280
+ },
281
+ "use_ota_wrapper": True,
282
+ }
283
+ generator.func(
284
+ session_config_obj=_camera_cfg(processing),
285
+ output_dir=str(tmp_path),
286
+ force=True,
287
+ peer_addresses={"a": "127.0.0.1", "b": "127.0.0.2"},
288
+ )
289
+
290
+ receiver = (tmp_path / "a" / "plugin.yaml").read_text(encoding="utf-8")
291
+ # The pacer runs on the receiving peer against the arrived encoded stream...
292
+ assert "pace_1_topic: /camera/image/ffmpeg" in receiver
293
+ assert "pace_1_msg_type: ffmpeg_image_transport_msgs/msg/FFMPEGPacket" in receiver
294
+ assert (
295
+ "pace_1_adaptive: 'true'" in receiver
296
+ or 'pace_1_adaptive: "true"' in receiver
297
+ or "pace_1_adaptive: true" in receiver
298
+ )
299
+ assert "pace_1_min_ms: 120.0" in receiver
300
+ assert "pace_1_max_ms: 600.0" in receiver
301
+ # ...and the decode reads the paced copy.
302
+ assert "irt_1_paced" in receiver
303
+
304
+ # The sender has no pacer: its preview (if any) reads its own encode, and
305
+ # this session declares no local_republish at all.
306
+ sender = (tmp_path / "b" / "plugin.yaml").read_text(encoding="utf-8")
307
+ assert "pace" not in sender or "pace_1_topic" not in sender
308
+ assert "irt_1_paced" not in sender