rosotacom-dev 2.5.dev47__tar.gz → 2.5.dev48__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.dev48}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/forensics-report.md +7 -0
  3. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0003-metric-backbone.md +11 -0
  4. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +36 -6
  5. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +44 -15
  6. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  7. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  8. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_status_overview_observer.py +75 -0
  9. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_transit.py +38 -0
  10. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.devcontainer/devcontainer.json.template +0 -0
  11. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  12. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/CODEOWNERS +0 -0
  13. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  14. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  15. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  16. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  17. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  18. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  19. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  20. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  21. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  22. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/actions/setup-just/action.yml +0 -0
  23. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/dependabot.yml +0 -0
  24. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/benchmark-calibrate.yml +0 -0
  25. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/benchmark-gate.yml +0 -0
  26. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/dev-release.yml +0 -0
  27. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/image-scan.yml +0 -0
  28. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/nightly-e2e.yml +0 -0
  29. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/pr-lightweight.yml +0 -0
  30. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/pr-merge-gate.yml +0 -0
  31. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.github/workflows/release.yml +0 -0
  32. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/.pre-commit-config.yaml +0 -0
  33. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/AGENTS.md +0 -0
  34. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/CONCURRENCY.md +0 -0
  35. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/CONTRIBUTING.md +0 -0
  36. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/DEVELOPMENT_PRINCIPLES.md +0 -0
  37. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/LICENSE.txt +0 -0
  38. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/MANIFEST.in +0 -0
  39. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/README.md +0 -0
  40. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/budgets.jsonl +0 -0
  41. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/README.md +0 -0
  42. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/capacity-size-tight-cyclone.json +0 -0
  43. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  44. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/probe-loss-nominal-cyclone.json +0 -0
  45. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/probe-loss-tight-cyclone.json +0 -0
  46. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/probe-loss-tight-fastdds.json +0 -0
  47. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/calibration/probe-loss-tight-zenoh.json +0 -0
  48. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/codecov.yml +0 -0
  49. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/deployment-configuration.md +0 -0
  50. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/benchmark-ab.md +0 -0
  51. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/benchmark-bag-as-load.md +0 -0
  52. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/ci.md +0 -0
  53. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/ffmpeg-keyframes.md +0 -0
  54. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/README.md +0 -0
  55. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  56. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/delay-alone-no-loss.md +0 -0
  57. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/head-of-line-irregularity.md +0 -0
  58. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  59. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/lighter-message-loss.md +0 -0
  60. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  61. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/repro/reorder_drop_repro.py +0 -0
  62. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  63. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/geo-link-map.md +0 -0
  64. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/github-repository-settings.md +0 -0
  65. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/link-bandwidth.md +0 -0
  66. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/link-trace.md +0 -0
  67. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/owner-runbook.md +0 -0
  68. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/performance-bands.md +0 -0
  69. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/playbooks/README.md +0 -0
  70. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/quality-model.md +0 -0
  71. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release-notes/TEMPLATE.md +0 -0
  72. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release-notes/v2.1.md +0 -0
  73. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release-notes/v2.2.md +0 -0
  74. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release-notes/v2.3.md +0 -0
  75. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release-notes/v2.4.md +0 -0
  76. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/release.md +0 -0
  77. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  78. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0002-expectation-concepts.md +0 -0
  79. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0004-network-profiles.md +0 -0
  80. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  81. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0006-dynamic-qos.md +0 -0
  82. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/0007-regression-gate.md +0 -0
  83. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/rfcs/README.md +0 -0
  84. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/testing.md +0 -0
  85. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/docs/work-items.md +0 -0
  86. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/install.sh +0 -0
  87. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/justfile +0 -0
  88. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/media/control_center.png +0 -0
  89. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/media/echo_test.png +0 -0
  90. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/media/forensics-report-example.png +0 -0
  91. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/media/host.png +0 -0
  92. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/media/vehicle.png +0 -0
  93. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/pyproject.toml +0 -0
  94. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/requirements.txt +0 -0
  95. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/rosotacom.yaml +0 -0
  96. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/session-configuration.md +0 -0
  97. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/setup.cfg +0 -0
  98. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/__init__.py +0 -0
  99. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/__main__.py +0 -0
  100. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/anonymize.py +0 -0
  101. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/bag_ground_truth.py +0 -0
  102. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/benched_set.py +0 -0
  103. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/benchmark.py +0 -0
  104. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/benchmark_delta.py +0 -0
  105. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/bundle_check.py +0 -0
  106. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/cli.py +0 -0
  107. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/cli_benchmark.py +0 -0
  108. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/deployment.py +0 -0
  109. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/ffmpeg_packet.py +0 -0
  110. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/forensics.py +0 -0
  111. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/geomap.py +0 -0
  112. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/image_cache.py +0 -0
  113. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/netem_dist.py +0 -0
  114. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/network_profiles.py +0 -0
  115. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/network_shaper.py +0 -0
  116. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/plots.py +0 -0
  117. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/py.typed +0 -0
  118. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/benched-set.yaml +0 -0
  119. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/.gitignore +0 -0
  120. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/README.md +0 -0
  121. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/__init__.py +0 -0
  122. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  123. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  124. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  125. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  126. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  127. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  128. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  129. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  130. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  131. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  132. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  133. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  134. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  135. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  136. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  137. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  138. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  139. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  140. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  141. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  142. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  143. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  144. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  145. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  146. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  147. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  148. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  149. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  150. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  151. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  152. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  153. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  154. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  155. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  156. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  157. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  158. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  159. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  160. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  161. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  162. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  163. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  164. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  165. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  166. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  167. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  168. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  169. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  170. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  171. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  172. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  173. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  174. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  175. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  176. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  177. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ros2docker.json.example +0 -0
  198. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  199. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  200. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  201. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  202. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  203. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  204. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  205. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  206. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  207. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  208. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  209. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  210. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  211. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  212. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  213. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  214. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  215. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  216. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  217. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  218. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  219. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  220. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  221. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  222. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  223. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  224. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  225. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  226. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  227. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  228. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  229. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  230. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  231. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  232. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  233. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  234. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  235. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  236. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  237. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  238. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  239. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  240. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  241. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  242. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  243. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  244. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  245. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  246. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  247. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  248. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  249. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  250. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  251. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  252. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  253. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  254. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  255. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  256. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  257. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  258. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  259. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  260. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  261. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  262. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  263. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  264. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  265. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  266. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  267. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  268. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  269. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  270. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  271. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  272. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  273. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  274. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  275. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  276. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  277. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  278. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  279. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  280. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  281. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  282. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  283. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  284. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  285. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  286. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  287. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  288. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/status_eval.py +0 -0
  289. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/stream_stats.py +0 -0
  290. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/trace_profiles.py +0 -0
  291. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/transit.py +0 -0
  292. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom/video_quality.py +0 -0
  293. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
  294. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  295. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  296. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  297. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
  298. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  299. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/terminology.md +0 -0
  300. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_benched_set_registry.py +0 -0
  301. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_findings.py +0 -0
  302. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_markdown_links.py +0 -0
  303. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_ota_source_bundle.py +0 -0
  304. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_package_metadata.py +0 -0
  305. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_packaged_resources.py +0 -0
  306. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  307. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_pytest_policy.py +0 -0
  308. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_readme_examples.py +0 -0
  309. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_security_maintenance_config.py +0 -0
  310. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_session_configuration_doc.py +0 -0
  311. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_session_template_substitution.py +0 -0
  312. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/contract/test_workflow_contracts.py +0 -0
  313. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/conftest.py +0 -0
  314. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_anonymize_e2e.py +0 -0
  315. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_benchmark_ab.py +0 -0
  316. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_benchmark_capacity.py +0 -0
  317. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_benchmark_replay.py +0 -0
  318. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_parallel_smoke.py +0 -0
  319. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_smoke.py +0 -0
  320. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_timeline_stepping.py +0 -0
  321. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/e2e/test_video_quality_e2e.py +0 -0
  322. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/generate_rmw_matrix.py +0 -0
  323. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  324. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  325. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  326. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  327. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  328. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_anonymize.py +0 -0
  329. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_bag_ground_truth.py +0 -0
  330. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_benched_set.py +0 -0
  331. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_benchmark.py +0 -0
  332. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_benchmark_delta.py +0 -0
  333. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_bundle_check.py +0 -0
  334. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_catmux_pane_logging.py +0 -0
  335. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_cli.py +0 -0
  336. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_cli_benchmark.py +0 -0
  337. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_concurrency.py +0 -0
  338. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_deployment.py +0 -0
  339. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_ffmpeg_flags.py +0 -0
  340. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_ffmpeg_packet.py +0 -0
  341. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_field_profiles.py +0 -0
  342. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_forensics.py +0 -0
  343. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_geomap.py +0 -0
  344. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_image_cache.py +0 -0
  345. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_link_bytes.py +0 -0
  346. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_link_trace.py +0 -0
  347. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_netem_dist.py +0 -0
  348. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_network_profiles.py +0 -0
  349. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_network_shaper.py +0 -0
  350. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_ota_unwrapper.py +0 -0
  351. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_ota_wrapper.py +0 -0
  352. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_ota_xml.py +0 -0
  353. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_playout_pacer.py +0 -0
  354. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_plots.py +0 -0
  355. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_qos_fallback_logging.py +0 -0
  356. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_relay_optional_parameters.py +0 -0
  357. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_session_pipeline.py +0 -0
  358. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_stage_latency.py +0 -0
  359. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_status_eval.py +0 -0
  360. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_status_overview.py +0 -0
  361. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_stream_stats.py +0 -0
  362. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/tests/unit/test_trace_profiles.py +0 -0
  363. {rosotacom_dev-2.5.dev47 → rosotacom_dev-2.5.dev48}/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.dev48
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
@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev47
3
+ Version: 2.5.dev48
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": 48,
4
+ "node": "gaf076fb2c22a73a786a9a323f7382e74f2804e68",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-18"
@@ -328,3 +328,78 @@ def test_ota_unparseable_ffmpeg_payload_omits_the_flag(node_module: ModuleType)
328
328
  records = node.observations[topic].drain_transit_records()
329
329
  assert [record["seq"] for record in records] == [0, 1]
330
330
  assert all("keyframe" not in record for record in records)
331
+
332
+
333
+ # ---------------------------------------------------------------------------
334
+ # the sender-side com_out branch: what the source handed to the link (#293)
335
+ # ---------------------------------------------------------------------------
336
+
337
+
338
+ def _ota_out_observer(node_module: ModuleType, topic: str):
339
+ node = object.__new__(node_module.StageObserver)
340
+ node.observations = {topic: core.StageObservation(type_str="com_msgs/msg/OtaStamped")}
341
+ node._stage_metadata = {
342
+ topic: [
343
+ {
344
+ "peer": "a",
345
+ "source": "a",
346
+ "target": "b",
347
+ "base": "/cam",
348
+ "direction": "outbound",
349
+ "stage": "com_out",
350
+ }
351
+ ]
352
+ }
353
+ # A live peer estimate exists; the branch must not apply it to a local hop.
354
+ node.clock_estimator = _Estimator(5.0)
355
+ node.get_clock = lambda: SimpleNamespace(now=lambda: SimpleNamespace(nanoseconds=int(_NOW_S * 1e9)))
356
+ node.get_logger = lambda: SimpleNamespace(error=lambda *a, **k: None, warning=lambda *a, **k: None)
357
+ return node
358
+
359
+
360
+ def test_com_out_ota_payload_produces_a_sent_transit_record(node_module: ModuleType) -> None:
361
+ topic = "/com/out/a/cam/ffmpeg/ota_stamped"
362
+ node = _ota_out_observer(node_module, topic)
363
+ cb = node._make_cb(topic)
364
+
365
+ cb(_ota_stamped(0, msg_type=ffmpeg_flags.FFMPEG_PACKET_TYPE, serialized_msg=_ffmpeg_packet(1)))
366
+ cb(_ota_stamped(1, msg_type=ffmpeg_flags.FFMPEG_PACKET_TYPE, serialized_msg=_ffmpeg_packet(0)))
367
+
368
+ records = node.observations[topic].drain_transit_records()
369
+ assert [(r["seq"], r["status"], r["keyframe"]) for r in records] == [
370
+ (0, "sent", True),
371
+ (1, "sent", False),
372
+ ]
373
+ first = records[0]
374
+ assert first["stage"] == "com_out"
375
+ assert first["direction"] == "outbound"
376
+ assert first["t_wrap"] == pytest.approx(_NOW_S - 0.03, abs=1e-6)
377
+ assert first["t_com_out"] == pytest.approx(_NOW_S, abs=1e-6)
378
+ assert "t_com_in" not in first
379
+ # Wrapper and observer share one clock, so the peer offset must not leak in.
380
+ assert first["clock_offset_ms"] is None
381
+ assert first["sections"]["wrap_to_com_out_ms"] == pytest.approx(30.0, abs=0.5)
382
+ assert "ota_hop_ms" not in first["sections"]
383
+
384
+
385
+ def test_com_out_sequence_gap_is_unobserved_not_lost(node_module: ModuleType) -> None:
386
+ """A gap at com_out means the local observer missed the message, not the link."""
387
+ topic = "/com/out/a/cam/ffmpeg/ota_stamped"
388
+ node = _ota_out_observer(node_module, topic)
389
+ cb = node._make_cb(topic)
390
+
391
+ cb(_ota_stamped(0, msg_type=ffmpeg_flags.FFMPEG_PACKET_TYPE, serialized_msg=_ffmpeg_packet(1)))
392
+ cb(_ota_stamped(3, msg_type=ffmpeg_flags.FFMPEG_PACKET_TYPE, serialized_msg=_ffmpeg_packet(0)))
393
+
394
+ records = node.observations[topic].drain_transit_records()
395
+ assert [(r["seq"], r["status"]) for r in records] == [
396
+ (0, "sent"),
397
+ (1, "unobserved"),
398
+ (2, "unobserved"),
399
+ (3, "sent"),
400
+ ]
401
+ gap = records[1]
402
+ assert gap["t_wrap"] is None
403
+ assert gap["t_com_out"] is None
404
+ assert "t_com_in" not in gap
405
+ assert gap["sections"] == {"wrap_to_com_out_ms": None}
@@ -222,3 +222,41 @@ def test_nominal_send_period_ignores_the_step_across_a_restart() -> None:
222
222
  records += [{**_record(seq, "delivered", 5.0, t_wrap=100.0 + seq * 0.1), "epoch": 1} for seq in range(10)]
223
223
 
224
224
  assert _nominal_send_period_ms(records) == 100.0
225
+
226
+
227
+ def test_sender_rows_can_never_mask_a_receiver_loss() -> None:
228
+ """The sending peer's com_out rows join with the receiver's rows by
229
+ (source, topic, epoch, seq). `sent` ranks below every receiver verdict in
230
+ `join_transit_records`, so a message the link lost stays lost -- while the
231
+ joined record still gains the sender's timestamps."""
232
+ sent = {
233
+ **_record(1, "sent", t_wrap=100.0),
234
+ "stage": "com_out",
235
+ "direction": "outbound",
236
+ "t_com_out": 100.005,
237
+ "sections": {"wrap_to_com_out_ms": 5.0},
238
+ }
239
+ lost = {**_record(1, "lost"), "stage": "com_in", "direction": "inbound"}
240
+
241
+ for ordering in ([sent, lost], [lost, sent]):
242
+ (joined,) = join_transit_records(ordering)
243
+ assert joined["status"] == "lost"
244
+ assert joined["t_wrap"] == 100.0
245
+ assert joined["t_com_out"] == 100.005
246
+
247
+ delivered = {**_record(1, "delivered", 12.0, t_wrap=100.0), "stage": "com_in", "direction": "inbound"}
248
+ (joined,) = join_transit_records([sent, delivered])
249
+ assert joined["status"] == "delivered"
250
+ assert joined["sections"]["ota_hop_ms"] == 12.0
251
+ assert joined["sections"]["wrap_to_com_out_ms"] == 5.0
252
+
253
+
254
+ def test_unobserved_sender_gaps_do_not_count_as_loss() -> None:
255
+ records = [
256
+ {**_record(0, "sent", t_wrap=10.0), "direction": "outbound", "stage": "com_out"},
257
+ {**_record(1, "unobserved"), "direction": "outbound", "stage": "com_out"},
258
+ {**_record(2, "sent", t_wrap=10.2), "direction": "outbound", "stage": "com_out"},
259
+ ]
260
+ summary = summarize_transit_records(records)["topics"]["/x"]
261
+ assert summary["lost"] == 0
262
+ assert summary["loss_pct"] == 0.0