rosotacom-dev 2.5.dev56__tar.gz → 2.5.dev57__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 (379) hide show
  1. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/session-configuration.md +29 -2
  3. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +17 -3
  4. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +6 -0
  5. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +68 -4
  6. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +16 -10
  7. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/status_eval.py +11 -2
  8. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  9. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  10. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_generated_topics_have_a_producer.py +30 -4
  11. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_playout_pacer.py +32 -0
  12. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_session_pipeline.py +51 -0
  13. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_eval.py +41 -0
  14. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_overview.py +124 -0
  15. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.devcontainer/devcontainer.json.template +0 -0
  16. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  17. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/CODEOWNERS +0 -0
  18. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  19. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  20. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  21. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  22. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  23. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  24. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  25. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  26. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  27. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/actions/setup-just/action.yml +0 -0
  28. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/dependabot.yml +0 -0
  29. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/benchmark-calibrate.yml +0 -0
  30. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/benchmark-gate.yml +0 -0
  31. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/dev-release.yml +0 -0
  32. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/image-scan.yml +0 -0
  33. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/nightly-e2e.yml +0 -0
  34. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/pr-lightweight.yml +0 -0
  35. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/pr-merge-gate.yml +0 -0
  36. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.github/workflows/release.yml +0 -0
  37. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/.pre-commit-config.yaml +0 -0
  38. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/AGENTS.md +0 -0
  39. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/CONCURRENCY.md +0 -0
  40. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/CONTRIBUTING.md +0 -0
  41. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/DEVELOPMENT_PRINCIPLES.md +0 -0
  42. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/LICENSE.txt +0 -0
  43. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/MANIFEST.in +0 -0
  44. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/README.md +0 -0
  45. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/budgets.jsonl +0 -0
  46. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/README.md +0 -0
  47. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/capacity-size-tight-cyclone.json +0 -0
  48. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  49. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-nominal-cyclone.json +0 -0
  50. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-cyclone.json +0 -0
  51. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-fastdds.json +0 -0
  52. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/calibration/probe-loss-tight-zenoh.json +0 -0
  53. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/codecov.yml +0 -0
  54. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/deployment-configuration.md +0 -0
  55. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/benchmark-ab.md +0 -0
  56. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/benchmark-bag-as-load.md +0 -0
  57. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/ci.md +0 -0
  58. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/ffmpeg-keyframes.md +0 -0
  59. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/README.md +0 -0
  60. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  61. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/delay-alone-no-loss.md +0 -0
  62. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +0 -0
  63. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
  64. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/head-of-line-irregularity.md +0 -0
  65. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  66. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
  67. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/lighter-message-loss.md +0 -0
  68. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/oversubscription-queues-not-losses.md +0 -0
  69. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  70. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/repro/decode_chain_repro.py +0 -0
  71. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/repro/reorder_drop_repro.py +0 -0
  72. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
  73. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  74. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/forensics-report.md +0 -0
  75. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/geo-link-map.md +0 -0
  76. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/github-repository-settings.md +0 -0
  77. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/link-bandwidth.md +0 -0
  78. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/link-trace.md +0 -0
  79. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/owner-runbook.md +0 -0
  80. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/performance-bands.md +0 -0
  81. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/playbooks/README.md +0 -0
  82. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/quality-model.md +0 -0
  83. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release-notes/TEMPLATE.md +0 -0
  84. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.1.md +0 -0
  85. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.2.md +0 -0
  86. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.3.md +0 -0
  87. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release-notes/v2.4.md +0 -0
  88. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/release.md +0 -0
  89. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  90. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0002-expectation-concepts.md +0 -0
  91. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0003-metric-backbone.md +0 -0
  92. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0004-network-profiles.md +0 -0
  93. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  94. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0006-dynamic-qos.md +0 -0
  95. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/0007-regression-gate.md +0 -0
  96. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/rfcs/README.md +0 -0
  97. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/testing.md +0 -0
  98. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/docs/work-items.md +0 -0
  99. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/install.sh +0 -0
  100. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/justfile +0 -0
  101. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/media/control_center.png +0 -0
  102. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/media/echo_test.png +0 -0
  103. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/media/forensics-report-example.png +0 -0
  104. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/media/host.png +0 -0
  105. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/media/vehicle.png +0 -0
  106. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/pyproject.toml +0 -0
  107. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/requirements.txt +0 -0
  108. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/rosotacom.yaml +0 -0
  109. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/setup.cfg +0 -0
  110. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/__init__.py +0 -0
  111. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/__main__.py +0 -0
  112. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/anonymize.py +0 -0
  113. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/bag_ground_truth.py +0 -0
  114. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/benched_set.py +0 -0
  115. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/benchmark.py +0 -0
  116. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/benchmark_delta.py +0 -0
  117. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/bundle_check.py +0 -0
  118. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/cli.py +0 -0
  119. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/cli_benchmark.py +0 -0
  120. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/deployment.py +0 -0
  121. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/ffmpeg_packet.py +0 -0
  122. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/forensics.py +0 -0
  123. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/geomap.py +0 -0
  124. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/image_cache.py +0 -0
  125. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/netem_dist.py +0 -0
  126. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_preflight.py +0 -0
  127. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_profiles.py +0 -0
  128. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/network_shaper.py +0 -0
  129. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/plots.py +0 -0
  130. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/py.typed +0 -0
  131. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/benched-set.yaml +0 -0
  132. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/.gitignore +0 -0
  133. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/README.md +0 -0
  134. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/__init__.py +0 -0
  135. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  136. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  137. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  138. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  139. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  140. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  141. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  142. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  143. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  144. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  145. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  146. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  147. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  148. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  149. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  150. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  151. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  152. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  153. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  154. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  155. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  156. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  157. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  158. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  159. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  160. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  161. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  162. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  163. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  164. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  165. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  166. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  167. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  168. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  169. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  170. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  171. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  172. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  173. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  174. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  175. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  176. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  177. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  178. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  179. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  180. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  181. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  182. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  183. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  184. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  185. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  186. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  187. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  205. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  206. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ros2docker.json.example +0 -0
  211. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  212. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  213. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  214. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  215. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  216. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  217. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  218. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  219. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  220. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  221. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  222. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  223. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
  224. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
  225. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  227. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  228. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  229. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  230. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  231. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  232. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  233. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  234. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  235. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  236. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  237. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  238. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  239. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  240. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  241. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  242. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  243. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  244. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  245. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  246. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  247. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  248. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  249. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  250. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  251. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  252. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  253. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  254. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  255. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  256. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  257. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  258. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  259. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  260. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  261. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  262. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  263. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  264. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  265. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  266. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  267. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  268. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  269. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  270. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  271. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  272. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  273. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  274. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  275. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  276. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  277. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  278. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  279. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  280. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  281. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  282. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  283. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  284. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  285. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  286. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  287. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  288. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  289. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  290. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  291. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  292. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  293. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  294. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  295. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  296. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  297. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  298. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  299. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  300. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  301. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  302. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/stream_stats.py +0 -0
  303. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/trace_profiles.py +0 -0
  304. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/transit.py +0 -0
  305. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom/video_quality.py +0 -0
  306. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
  307. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  308. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  309. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  310. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
  311. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  312. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/terminology.md +0 -0
  313. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_benched_set_registry.py +0 -0
  314. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_findings.py +0 -0
  315. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_markdown_links.py +0 -0
  316. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_ota_source_bundle.py +0 -0
  317. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_package_metadata.py +0 -0
  318. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_packaged_resources.py +0 -0
  319. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  320. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_pytest_policy.py +0 -0
  321. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_readme_examples.py +0 -0
  322. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_security_maintenance_config.py +0 -0
  323. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_session_configuration_doc.py +0 -0
  324. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_session_template_substitution.py +0 -0
  325. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/contract/test_workflow_contracts.py +0 -0
  326. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/conftest.py +0 -0
  327. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_anonymize_e2e.py +0 -0
  328. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_ab.py +0 -0
  329. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_capacity.py +0 -0
  330. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_benchmark_replay.py +0 -0
  331. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_parallel_smoke.py +0 -0
  332. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_smoke.py +0 -0
  333. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_timeline_stepping.py +0 -0
  334. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/e2e/test_video_quality_e2e.py +0 -0
  335. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/generate_rmw_matrix.py +0 -0
  336. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  337. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  338. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  339. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  340. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
  341. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  342. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_anonymize.py +0 -0
  343. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_bag_ground_truth.py +0 -0
  344. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_benched_set.py +0 -0
  345. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_benchmark.py +0 -0
  346. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_benchmark_delta.py +0 -0
  347. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_bundle_check.py +0 -0
  348. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_catmux_pane_logging.py +0 -0
  349. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_cli.py +0 -0
  350. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_cli_benchmark.py +0 -0
  351. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_concurrency.py +0 -0
  352. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_container_shaping.py +0 -0
  353. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_deployment.py +0 -0
  354. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ffmpeg_flags.py +0 -0
  355. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ffmpeg_packet.py +0 -0
  356. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_field_profiles.py +0 -0
  357. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_forensics.py +0 -0
  358. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_geomap.py +0 -0
  359. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_image_cache.py +0 -0
  360. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_link_bytes.py +0 -0
  361. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_link_trace.py +0 -0
  362. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_netem_dist.py +0 -0
  363. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_preflight.py +0 -0
  364. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_profiles.py +0 -0
  365. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_network_shaper.py +0 -0
  366. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_link_configs.py +0 -0
  367. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_unwrapper.py +0 -0
  368. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_wrapper.py +0 -0
  369. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_ota_xml.py +0 -0
  370. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_plots.py +0 -0
  371. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_pub_sub_pair_types.py +0 -0
  372. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_qos_fallback_logging.py +0 -0
  373. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_relay_optional_parameters.py +0 -0
  374. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_stage_latency.py +0 -0
  375. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_status_overview_observer.py +0 -0
  376. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_stream_stats.py +0 -0
  377. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_trace_profiles.py +0 -0
  378. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/tests/unit/test_transit.py +0 -0
  379. {rosotacom_dev-2.5.dev56 → rosotacom_dev-2.5.dev57}/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.dev56
3
+ Version: 2.5.dev57
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -720,8 +720,35 @@ ros2 run com_py playout_pacer --ros-args \
720
720
  -p target_ms:=350.0 -p adaptive:=true
721
721
  ```
722
722
 
723
- It republishes on `<topic>/paced` plus `.../paced/budget_ms` and
724
- `.../paced/queue_depth` debug topics.
723
+ It republishes on `<topic>/paced` plus three topics next to it:
724
+ `.../paced/budget_ms` and `.../paced/queue_depth` at 1 Hz, and
725
+ `.../paced/hold_ms` with every release, carrying how long *that* message was
726
+ actually held.
727
+
728
+ ##### What the status overview does with it
729
+
730
+ A paced stage's age is the link plus a delay this session asked for, and one
731
+ number cannot be judged against a threshold meant for the link. So the
732
+ generator names the pacer's report on the stage it explains
733
+ (`paced_hold_topic` in `pipeline_spec.yaml`), and `status_overview` takes the
734
+ hold back out before classifying:
735
+
736
+ ```
737
+ native_in FLOWING .../ffmpeg/compressed 10.0Hz 286ms(link 46+paced 240) ...
738
+ ```
739
+
740
+ `latency_ms` stays the age on screen; `link_latency_ms` and `pacing_hold_ms`
741
+ are the split, and the quality verdict uses the former. Without this the
742
+ default 200 ms bad-threshold — set before anything on a link was held on
743
+ purpose — marks every healthy paced stream BAD, which is how an operator learns
744
+ to stop reading the panel.
745
+
746
+ It is the *applied* hold that is subtracted, never the configured budget: a
747
+ message that arrived later than its release time passes straight through, is
748
+ reported as held for 0 ms, and still flags. A budget-based correction would
749
+ credit that message a buffer it never received and quietly stop reporting a slow
750
+ link. Before the pacer has reported at all, nothing is subtracted — neither zero
751
+ nor the budget is a safe guess.
725
752
 
726
753
  `type: foxglove` supports (CompressedVideo):
727
754
  - `gop_size` (int)
@@ -8,7 +8,16 @@ Inserted between the OTA unwrapper and a decoder chain it turns network jitter
8
8
  into a constant, bounded age instead of visible stutter.
9
9
 
10
10
  Debug topics next to the output: ``.../paced/budget_ms`` (current delay
11
- budget) and ``.../paced/queue_depth``.
11
+ budget) and ``.../paced/queue_depth``, both at 1 Hz, and ``.../paced/hold_ms``
12
+ — published with *every* release, carrying how long that message was actually
13
+ held.
14
+
15
+ ``hold_ms`` exists so the status overview can tell the link's contribution from
16
+ this node's. Deliberately the applied hold rather than the budget: a message
17
+ that arrived later than its release time passes straight through and is held
18
+ for 0 ms, so a genuinely slow link cannot be credited with a buffer it never
19
+ used. Subtracting the *budget* would do exactly that, and would quietly stop
20
+ reporting the problem it exists to make visible.
12
21
  """
13
22
 
14
23
  from __future__ import annotations
@@ -62,6 +71,7 @@ class PlayoutPacerNode(Node):
62
71
  self._pub = self.create_publisher(msg_cls, out_topic, qos)
63
72
  self._budget_pub = self.create_publisher(Float64, out_topic + "/budget_ms", 10)
64
73
  self._depth_pub = self.create_publisher(Int32, out_topic + "/queue_depth", 10)
74
+ self._hold_pub = self.create_publisher(Float64, out_topic + "/hold_ms", 10)
65
75
  self.create_subscription(msg_cls, topic, self._on_message, qos)
66
76
  # 2 ms release tick: bounds added jitter to well below frame cadence.
67
77
  self.create_timer(0.002, self._release_due)
@@ -72,12 +82,16 @@ class PlayoutPacerNode(Node):
72
82
  now = time.time()
73
83
  stamp = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9
74
84
  release = self.schedule.on_message(stamp, now)
75
- self._queue.append((release, msg))
85
+ self._queue.append((release, msg, now))
76
86
 
77
87
  def _release_due(self) -> None:
78
88
  now = time.time()
79
89
  while self._queue and self._queue[0][0] <= now:
80
- self._pub.publish(self._queue.popleft()[1])
90
+ release, msg, arrival = self._queue.popleft()
91
+ self._pub.publish(msg)
92
+ # What this node added, not what it was allowed to add. Clamped
93
+ # because a late message's release time is already in the past.
94
+ self._hold_pub.publish(Float64(data=max(0.0, (release - arrival) * 1000.0)))
81
95
 
82
96
  def _publish_debug(self) -> None:
83
97
  self._budget_pub.publish(Float64(data=float(self.schedule.budget_ms)))
@@ -71,6 +71,7 @@ from com_py.ffmpeg_flags import FFMPEG_PACKET_TYPE, parse_keyframe_flag
71
71
  from com_py.link_bytes import LinkByteSampler, resolve_link_interface
72
72
  from com_py.link_trace import LinkTraceRecorder
73
73
  from com_py.status_overview_core import (
74
+ SCALAR_MSG_TYPE,
74
75
  STARTUP_GRACE_S,
75
76
  ClockOffsetEstimator,
76
77
  StageObservation,
@@ -309,6 +310,11 @@ class StageObserver(Node):
309
310
  "t_com_out": now_ros_s,
310
311
  "keyframe": keyframe,
311
312
  }
313
+ elif obs.type_str == SCALAR_MSG_TYPE:
314
+ # Not a stage: a number this node reads to interpret one. The
315
+ # pacing hold arrives here (`.../paced/hold_ms`); it carries no
316
+ # header, so there is no latency to record, only the value.
317
+ obs.last_value = float(msg.data)
312
318
  else:
313
319
  header = getattr(msg, "header", None)
314
320
  stamp = getattr(header, "stamp", None) if header is not None else None
@@ -61,6 +61,12 @@ GOOD = "GOOD"
61
61
  DEGRADED = "DEGRADED"
62
62
  BAD = "BAD"
63
63
 
64
+ #: A stage whose age includes a delay this session asked for names the topic on
65
+ #: which the node responsible reports what it actually held. Set by the
66
+ #: generator for a decode that reads a playout pacer's output.
67
+ PACED_HOLD_KEY = "paced_hold_topic"
68
+ SCALAR_MSG_TYPE = "std_msgs/msg/Float64"
69
+
64
70
  # Topic-level rollup
65
71
  OK = "OK"
66
72
  PARTIAL = "PARTIAL"
@@ -371,6 +377,9 @@ class StageObservation:
371
377
  last_recv_wall: Optional[float] = None
372
378
  last_delay_s: Optional[float] = None
373
379
  last_raw_delay_s: Optional[float] = None
380
+ #: Payload of the last message, for the scalar topics this node reads as
381
+ #: numbers rather than observes as stages (the pacing hold).
382
+ last_value: Optional[float] = None
374
383
  last_rtt_s: Optional[float] = None
375
384
  last_clock_offset_s: Optional[float] = None
376
385
  # (t_mono, size_bytes, delay_s_or_None)
@@ -673,6 +682,21 @@ class StatusAggregator:
673
682
  self._startup_pending: Optional[Tuple[float, set]] = None
674
683
 
675
684
  # -- observation lookup --
685
+ def _pacing_hold_ms(self, stage: Dict[str, Any]) -> Optional[float]:
686
+ """How long this session deliberately held the last message of a stage.
687
+
688
+ None whenever the answer is not actually known -- no pacer named, or the
689
+ pacer has not reported yet. Guessing zero would read a paced picture as
690
+ a slow link; guessing the configured budget would do the opposite and
691
+ credit a hold that a late message never received.
692
+ """
693
+ hold_topic = stage.get(PACED_HOLD_KEY)
694
+ if not hold_topic:
695
+ return None
696
+ observer = self._observers.get("local")
697
+ observation = observer.observations.get(str(hold_topic)) if observer else None
698
+ return None if observation is None else observation.last_value
699
+
676
700
  def _obs_for(self, stage: Dict[str, Any]) -> Optional[StageObservation]:
677
701
  domain = stage.get("domain", "local")
678
702
  observer = self._observers.get(domain) or self._observers.get("local")
@@ -714,6 +738,9 @@ class StatusAggregator:
714
738
  "observation": "graph" if obs is not None and obs.graph_only else "payload",
715
739
  "inferred_from": None,
716
740
  "latched": str((expect or {}).get("mode", "")).strip().lower() == "latched",
741
+ # Split of `latency_ms` for a stage this session delays on purpose.
742
+ "pacing_hold_ms": None,
743
+ "link_latency_ms": None,
717
744
  }
718
745
  if obs is None:
719
746
  return result
@@ -727,6 +754,10 @@ class StatusAggregator:
727
754
  result["age_s"] = round(m["age_s"], 3) if m["age_s"] is not None else None
728
755
  if m["last_delay_s"] is not None:
729
756
  result["latency_ms"] = round(m["last_delay_s"] * 1000.0, 1)
757
+ hold_ms = self._pacing_hold_ms(stage)
758
+ if hold_ms is not None:
759
+ result["pacing_hold_ms"] = round(hold_ms, 1)
760
+ result["link_latency_ms"] = round(max(0.0, result["latency_ms"] - hold_ms), 1)
730
761
  if m["last_raw_delay_s"] is not None:
731
762
  result["latency_uncorrected_ms"] = round(m["last_raw_delay_s"] * 1000.0, 1)
732
763
  if m["last_rtt_s"] is not None:
@@ -759,7 +790,9 @@ class StatusAggregator:
759
790
  return result
760
791
 
761
792
  result["state"] = FLOWING
762
- quality, reason = self._classify_quality(m, expected_hz, expect)
793
+ quality, reason = self._classify_quality(
794
+ m, expected_hz, expect, pacing_hold_ms=result["pacing_hold_ms"]
795
+ )
763
796
  result["quality"] = quality
764
797
  result["quality_reason"] = reason
765
798
  return result
@@ -823,7 +856,12 @@ class StatusAggregator:
823
856
  self._copy_inferred_activity(stage_result, source)
824
857
 
825
858
  def _classify_quality(
826
- self, m: Dict[str, Any], expected_hz: Optional[float], expect: Optional[Dict[str, Any]] = None
859
+ self,
860
+ m: Dict[str, Any],
861
+ expected_hz: Optional[float],
862
+ expect: Optional[Dict[str, Any]] = None,
863
+ *,
864
+ pacing_hold_ms: Optional[float] = None,
827
865
  ) -> Tuple[str, Optional[str]]:
828
866
  expect = expect or {}
829
867
  hz_exp = expect.get("hz") or {}
@@ -833,8 +871,17 @@ class StatusAggregator:
833
871
  # Latency: a declared `expect.latency_ms.max` overrides the global bad
834
872
  # threshold; the good band stays the default so a tight contract still
835
873
  # surfaces a DEGRADED before BAD where it makes sense.
874
+ #
875
+ # A stage the session delays on purpose is judged on what is left after
876
+ # that delay is taken back out. Otherwise a threshold calibrated for a
877
+ # link measures a buffer, which reads as a fault on a healthy stream and
878
+ # teaches an operator to stop believing the panel. The subtracted value
879
+ # is the hold actually applied, so a late message -- which the pacer
880
+ # passes straight through -- is credited nothing and still flags.
836
881
  bad_lat = float(lat_exp["max"]) if "max" in lat_exp else self._delay_bad_ms
837
882
  delay_ms = (m["last_delay_s"] * 1000.0) if m["last_delay_s"] is not None else None
883
+ if delay_ms is not None and pacing_hold_ms is not None:
884
+ delay_ms = max(0.0, delay_ms - pacing_hold_ms)
838
885
  if delay_ms is not None:
839
886
  if delay_ms >= bad_lat:
840
887
  return BAD, "latency"
@@ -1142,7 +1189,16 @@ class StatusAggregator:
1142
1189
  if st["state"] in (FLOWING, STALE):
1143
1190
  parts = [f"{st['hz']:.1f}Hz"]
1144
1191
  if st["latency_ms"] is not None:
1145
- parts.append(f"{st['latency_ms']:.0f}ms")
1192
+ # A paced stage reads as one big number unless the split
1193
+ # is on the same line: what the link cost, and what this
1194
+ # session chose to add on top of it.
1195
+ if st.get("pacing_hold_ms") is not None:
1196
+ parts.append(
1197
+ f"{st['latency_ms']:.0f}ms"
1198
+ f"(link {st['link_latency_ms']:.0f}+paced {st['pacing_hold_ms']:.0f})"
1199
+ )
1200
+ else:
1201
+ parts.append(f"{st['latency_ms']:.0f}ms")
1146
1202
  elif st["latency_uncorrected_ms"] is not None:
1147
1203
  parts.append(f"{st['latency_uncorrected_ms']:.0f}ms(raw)")
1148
1204
  if st["loss_pct"] is not None:
@@ -1298,7 +1354,12 @@ class StatusAggregator:
1298
1354
 
1299
1355
 
1300
1356
  def collect_stage_topics(spec: Dict[str, Any]) -> Dict[str, Dict[str, Optional[str]]]:
1301
- """Return {domain: {topic: type_hint}} from the pipeline spec."""
1357
+ """Return {domain: {topic: type_hint}} from the pipeline spec.
1358
+
1359
+ Includes the pacing-hold topics a stage names: they are not stages of the
1360
+ link -- nothing is delivered on them -- but the aggregator has to read them
1361
+ to tell this session's deliberate delay from the network's.
1362
+ """
1302
1363
  by_domain: Dict[str, Dict[str, Optional[str]]] = {"local": {}, "ota": {}}
1303
1364
  for topic_spec in spec.get("topics", []):
1304
1365
  type_hint = topic_spec.get("type")
@@ -1307,6 +1368,9 @@ def collect_stage_topics(spec: Dict[str, Any]) -> Dict[str, Dict[str, Optional[s
1307
1368
  by_domain.setdefault(domain, {})
1308
1369
  existing = by_domain[domain].get(stage["topic"])
1309
1370
  by_domain[domain][stage["topic"]] = existing or stage.get("type") or type_hint
1371
+ hold_topic = stage.get(PACED_HOLD_KEY)
1372
+ if hold_topic:
1373
+ by_domain["local"].setdefault(str(hold_topic), SCALAR_MSG_TYPE)
1310
1374
  return by_domain
1311
1375
 
1312
1376
 
@@ -1666,17 +1666,23 @@ def _build_status_pipeline_spec(
1666
1666
  if postprocessed != final:
1667
1667
  native_in_type = base_type
1668
1668
  transport = p.get("transport")
1669
+ native_in: Dict[str, Any] = {
1670
+ "stage": "native_in",
1671
+ "topic": _relay_in_local_topic(postprocessed),
1672
+ "type": native_in_type,
1673
+ "domain": "local",
1674
+ "produced_by": "postprocessing",
1675
+ }
1669
1676
  if isinstance(transport, TransportSpec) and transport.remote_republish:
1670
- native_in_type = REPUBLISH_OUTPUT_TYPES[transport.remote_republish]
1671
- stages.append(
1672
- {
1673
- "stage": "native_in",
1674
- "topic": _relay_in_local_topic(postprocessed),
1675
- "type": native_in_type,
1676
- "domain": "local",
1677
- "produced_by": "postprocessing",
1678
- }
1679
- )
1677
+ native_in["type"] = REPUBLISH_OUTPUT_TYPES[transport.remote_republish]
1678
+ if transport.playout is not None and playout_republish(transport.playout) != "unpaced":
1679
+ # This stage's age is the link plus a hold this session
1680
+ # asked for. The pacer reports the hold it applied; naming
1681
+ # the topic here is what lets the status separate the two
1682
+ # instead of reading a deliberate buffer as a fault.
1683
+ paced = _relay_in_local_topic(str(p.get("irt_in"))) + "/paced"
1684
+ native_in["paced_hold_topic"] = f"{paced}/hold_ms"
1685
+ stages.append(native_in)
1680
1686
 
1681
1687
  topics.append(
1682
1688
  {
@@ -265,10 +265,19 @@ def _check_expect(
265
265
  failures.append(f"[{peer}] {base}: latency_ms.stage '{stage_name}' not in pipeline")
266
266
  return failures
267
267
  lat_stage = named
268
- lat = lat_stage.get("latency_ms")
268
+ # A stage the session delays on purpose reports the split, and `expect` is
269
+ # about what the *link* delivers -- the buffer is already bounded by the
270
+ # `playout` block that asked for it. The live status classifies on the same
271
+ # number, and the two must not disagree about one contract: that is how a
272
+ # session ends up passing `rosotacom test` while its own panel reads BAD.
273
+ lat = lat_stage.get("link_latency_ms")
274
+ measured = "link latency"
275
+ if lat is None:
276
+ lat = lat_stage.get("latency_ms")
277
+ measured = "latency"
269
278
  if "max" in lat_exp and (lat is None or lat > lat_exp["max"]):
270
279
  where = f" at '{stage_name}'" if stage_name else ""
271
- failures.append(f"[{peer}] {base}: latency {lat}ms{where} > expected max {lat_exp['max']}ms")
280
+ failures.append(f"[{peer}] {base}: {measured} {lat}ms{where} > expected max {lat_exp['max']}ms")
272
281
 
273
282
  loss_exp = expect.get("loss_pct") or {}
274
283
  if "max" in loss_exp:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev56
3
+ Version: 2.5.dev57
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": 56,
4
- "node": "g18a62c4bee965c3d64b7eaa70544f6bb7f078a98",
3
+ "distance": 57,
4
+ "node": "gd5c85e0fb2b514ef6c7827c2706cb96b8467fdea",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-19"
@@ -218,7 +218,7 @@ def touches(params: dict, peer_dir: Path) -> Touches:
218
218
  for index in _slots(params, "pace_{}_topic", limit=4):
219
219
  topic = _one(params, f"pace_{index}_topic")
220
220
  result.through(str(topic), f"{topic}/paced")
221
- result.writes.update({f"{topic}/paced/budget_ms", f"{topic}/paced/queue_depth"})
221
+ result.writes.update({f"{topic}/paced/budget_ms", f"{topic}/paced/queue_depth", f"{topic}/paced/hold_ms"})
222
222
 
223
223
  # NOR: relays a prefixed, suffixed name back onto the bare base, so a local
224
224
  # consumer (a TF tree) finds the name it expects.
@@ -245,14 +245,35 @@ def touches(params: dict, peer_dir: Path) -> Touches:
245
245
 
246
246
  # Observability: reads only, never republishes.
247
247
  result.reads.update(_many(params, "metric_stage_topics"))
248
+ # STAT: the status overview reads the pacing hold a stage names, to tell
249
+ # this session's deliberate delay from the network's. It is configured out
250
+ # of the pipeline spec rather than out of these parameters, but it is a read
251
+ # by a node this peer starts, so it belongs here -- a hold topic no pacer
252
+ # publishes would leave the status silently subtracting nothing.
253
+ result.reads.update(paced_hold_topics(peer_dir))
248
254
 
249
255
  return result
250
256
 
251
257
 
258
+ def _spec(peer_dir: Path) -> dict:
259
+ path = peer_dir / "pipeline_spec.yaml"
260
+ if not path.is_file():
261
+ return {}
262
+ return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
263
+
264
+
265
+ def _stages(peer_dir: Path) -> Iterator[dict]:
266
+ for topic in _spec(peer_dir).get("topics", []):
267
+ yield from topic.get("stages", [])
268
+
269
+
270
+ def paced_hold_topics(peer_dir: Path) -> set[str]:
271
+ return {stage["paced_hold_topic"] for stage in _stages(peer_dir) if stage.get("paced_hold_topic")}
272
+
273
+
252
274
  def spec_topics(peer_dir: Path) -> set[str]:
253
275
  """Every topic this peer's pipeline spec models, at any stage."""
254
- spec = yaml.safe_load((peer_dir / "pipeline_spec.yaml").read_text(encoding="utf-8"))
255
- return {stage["topic"] for topic in spec.get("topics", []) for stage in topic.get("stages", [])}
276
+ return {stage["topic"] for stage in _stages(peer_dir)}
256
277
 
257
278
 
258
279
  # ---------------------------------------------------------------------------
@@ -352,7 +373,12 @@ def test_the_composed_names_are_the_ones_the_template_composes() -> None:
352
373
  # reads that name, and `touches` claims those two writes, so all three have
353
374
  # to agree -- they are three files apart and nothing else compares them.
354
375
  pacer = PACER_PY.read_text(encoding="utf-8")
355
- for composed in ('declare_parameter("topic_suffix", "/paced")', '+ "/budget_ms"', '+ "/queue_depth"'):
376
+ for composed in (
377
+ 'declare_parameter("topic_suffix", "/paced")',
378
+ '+ "/budget_ms"',
379
+ '+ "/queue_depth"',
380
+ '+ "/hold_ms"',
381
+ ):
356
382
  assert composed in pacer, f"the pacer no longer composes {composed}"
357
383
 
358
384
 
@@ -79,3 +79,35 @@ def test_config_validation() -> None:
79
79
  PacerConfig(min_ms=500.0, max_ms=100.0)
80
80
  with pytest.raises(ValueError):
81
81
  PacerConfig(window=3)
82
+
83
+
84
+ # The hold the node reports (#301).
85
+ #
86
+ # `.../paced/hold_ms` carries what this node actually added, so the status
87
+ # overview can take it back out of a decoded stage's age and judge what is left
88
+ # -- the link. The node computes `max(0, release - arrival)`; both halves of
89
+ # that clamp are properties of the schedule, so they are asserted here rather
90
+ # than in three lines of publishing glue.
91
+
92
+
93
+ def _hold_ms(schedule: PlayoutSchedule, stamp: float, arrival: float) -> float:
94
+ """What the node publishes for a message: the delay it applied, never negative."""
95
+ return max(0.0, (schedule.on_message(stamp, arrival) - arrival) * 1000.0)
96
+
97
+
98
+ def test_the_reported_hold_is_what_was_added_not_what_was_allowed() -> None:
99
+ schedule = PlayoutSchedule(PacerConfig(adaptive=False, target_ms=200.0))
100
+ # First message sets the floor at its own delay, so it is released at
101
+ # floor + 200 ms and waits exactly the budget.
102
+ assert _hold_ms(schedule, 0.0, 0.030) == pytest.approx(200.0, abs=1.0)
103
+ # One that arrives 150 ms later than the floor has already spent 150 of the
104
+ # budget on the wire and waits only the remainder.
105
+ assert _hold_ms(schedule, 1.0, 1.180) == pytest.approx(50.0, abs=1.0)
106
+
107
+
108
+ def test_a_late_message_is_reported_as_held_for_nothing() -> None:
109
+ """The clamp: a pass-through must not be credited with a buffer it skipped."""
110
+ schedule = PlayoutSchedule(PacerConfig(adaptive=False, target_ms=200.0))
111
+ _hold_ms(schedule, 0.0, 0.030)
112
+
113
+ assert _hold_ms(schedule, 1.0, 1.400) == 0.0
@@ -394,3 +394,54 @@ def test_every_mode_delivers_the_same_topic_name(tmp_path: Path) -> None:
394
394
  delivered.add(generator._delivered_topic(unpaced_link, unpaced_link["final"]))
395
395
 
396
396
  assert len(delivered) == 1, delivered
397
+
398
+
399
+ # The status has to be told which pacer belongs to which stage (#301).
400
+ #
401
+ # A decoded stage's age is the link plus a hold this session asked for, and the
402
+ # status overview cannot tell them apart by looking at one number. The generator
403
+ # knows both nodes, so it names the pacer's report on the stage it explains --
404
+ # rather than the status guessing from a topic-name convention.
405
+
406
+
407
+ def _native_in(receiver: str) -> dict:
408
+ spec = yaml.safe_load(receiver)
409
+ for topic in spec["topics"]:
410
+ if topic["direction"] == "inbound":
411
+ for stage in topic["stages"]:
412
+ if stage["stage"] == "native_in":
413
+ return stage
414
+ raise AssertionError("no inbound native_in stage")
415
+
416
+
417
+ def _receiver_spec(tmp_path: Path, playout: dict | None) -> str:
418
+ transport: dict = {"type": "ffmpeg", "remote_republish": "compressed"}
419
+ if playout is not None:
420
+ transport["playout"] = playout
421
+ generator.func(
422
+ session_config_obj=_camera_cfg({"transport": transport, "use_ota_wrapper": True}),
423
+ output_dir=str(tmp_path),
424
+ force=True,
425
+ peer_addresses={"a": "127.0.0.1", "b": "127.0.0.2"},
426
+ )
427
+ return (tmp_path / "a" / "pipeline_spec.yaml").read_text(encoding="utf-8")
428
+
429
+
430
+ def test_a_paced_decode_names_the_pacer_that_explains_its_age(tmp_path: Path) -> None:
431
+ stage = _native_in(_receiver_spec(tmp_path, {"adaptive": True, "max_ms": 300}))
432
+
433
+ assert stage["paced_hold_topic"] == "/camera/image/ffmpeg/paced/hold_ms"
434
+
435
+
436
+ def test_an_unpaced_link_names_no_pacer(tmp_path: Path) -> None:
437
+ """Nothing holds this stream, so nothing may be subtracted from its age."""
438
+ stage = _native_in(_receiver_spec(tmp_path, None))
439
+
440
+ assert "paced_hold_topic" not in stage
441
+
442
+
443
+ def test_a_pacer_kept_off_the_display_path_explains_nothing(tmp_path: Path) -> None:
444
+ """`republish: unpaced` decodes the arrived stream; its age is all link."""
445
+ stage = _native_in(_receiver_spec(tmp_path, {"adaptive": True, "republish": "unpaced"}))
446
+
447
+ assert "paced_hold_topic" not in stage
@@ -531,3 +531,44 @@ def test_suggest_profile_band_nests_conditional_keys_under_per_profile() -> None
531
531
  entry = band["/heartbeat_a"]["per_profile"]["cellular"]
532
532
  assert "hz" in entry and "latency_ms" in entry # conditional keys only
533
533
  assert "presence" not in entry and "mode" not in entry # invariant stays top-level
534
+
535
+
536
+ # A contract about a stage the session delays on purpose (#301).
537
+ #
538
+ # `status_overview` classifies such a stage on what is left after the pacer's
539
+ # own hold is taken back out. This evaluator has to measure the same thing, or a
540
+ # session passes `rosotacom test` while its own panel reads BAD -- one contract,
541
+ # two answers, which is worse than either answer alone.
542
+
543
+
544
+ def _paced_report(latency_ms: float, link_latency_ms: float | None) -> dict:
545
+ stage = _stage("native_in", 10.0, latency_ms)
546
+ if link_latency_ms is not None:
547
+ stage["link_latency_ms"] = link_latency_ms
548
+ stage["pacing_hold_ms"] = latency_ms - link_latency_ms
549
+ return {"peer": "a", "topics": [_topic("/camera", "inbound", "OK", [stage])]}
550
+
551
+
552
+ def test_a_paced_stage_is_asserted_on_the_link_not_the_buffer() -> None:
553
+ """286 ms on screen, 46 of them the link's: a 200 ms link contract holds."""
554
+ report = _paced_report(latency_ms=286.0, link_latency_ms=46.0)
555
+
556
+ assert evaluate_report(report, {"/camera": {"latency_ms": {"max": 200}}}) == []
557
+
558
+
559
+ def test_a_slow_link_under_a_pacer_still_fails() -> None:
560
+ report = _paced_report(latency_ms=480.0, link_latency_ms=480.0)
561
+
562
+ failures = evaluate_report(report, {"/camera": {"latency_ms": {"max": 200}}})
563
+
564
+ assert len(failures) == 1
565
+ assert "link latency 480.0ms" in failures[0]
566
+
567
+
568
+ def test_an_unpaced_stage_is_asserted_on_what_it_always_was() -> None:
569
+ report = _paced_report(latency_ms=286.0, link_latency_ms=None)
570
+
571
+ failures = evaluate_report(report, {"/camera": {"latency_ms": {"max": 200}}})
572
+
573
+ assert len(failures) == 1
574
+ assert "latency 286.0ms" in failures[0]