rosotacom-dev 2.5.dev38__tar.gz → 2.5.dev40__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 (353) hide show
  1. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/session-configuration.md +29 -2
  3. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/cli.py +5 -2
  4. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +5 -1
  5. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +32 -2
  6. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +8 -2
  7. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +49 -36
  8. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +71 -23
  9. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  10. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/SOURCES.txt +2 -0
  11. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/scm_file_list.json +2 -0
  12. rosotacom_dev-2.5.dev40/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  13. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_catmux_pane_logging.py +74 -0
  14. rosotacom_dev-2.5.dev40/tests/unit/test_qos_fallback_logging.py +139 -0
  15. rosotacom_dev-2.5.dev40/tests/unit/test_relay_optional_parameters.py +176 -0
  16. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_session_pipeline.py +42 -3
  17. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_status_overview.py +84 -2
  18. rosotacom_dev-2.5.dev38/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  19. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.devcontainer/devcontainer.json.template +0 -0
  20. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  21. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/CODEOWNERS +0 -0
  22. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  23. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  24. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  25. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  26. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  27. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  28. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  29. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  30. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  31. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/actions/setup-just/action.yml +0 -0
  32. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/dependabot.yml +0 -0
  33. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/benchmark-calibrate.yml +0 -0
  34. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/benchmark-gate.yml +0 -0
  35. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/dev-release.yml +0 -0
  36. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/image-scan.yml +0 -0
  37. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/nightly-e2e.yml +0 -0
  38. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/pr-lightweight.yml +0 -0
  39. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/pr-merge-gate.yml +0 -0
  40. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.github/workflows/release.yml +0 -0
  41. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/.pre-commit-config.yaml +0 -0
  42. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/AGENTS.md +0 -0
  43. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/CONCURRENCY.md +0 -0
  44. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/CONTRIBUTING.md +0 -0
  45. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/DEVELOPMENT_PRINCIPLES.md +0 -0
  46. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/LICENSE.txt +0 -0
  47. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/MANIFEST.in +0 -0
  48. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/README.md +0 -0
  49. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/budgets.jsonl +0 -0
  50. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/README.md +0 -0
  51. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/capacity-size-tight-cyclone.json +0 -0
  52. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  53. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/probe-loss-nominal-cyclone.json +0 -0
  54. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/probe-loss-tight-cyclone.json +0 -0
  55. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/probe-loss-tight-fastdds.json +0 -0
  56. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/calibration/probe-loss-tight-zenoh.json +0 -0
  57. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/codecov.yml +0 -0
  58. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/deployment-configuration.md +0 -0
  59. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/benchmark-ab.md +0 -0
  60. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/benchmark-bag-as-load.md +0 -0
  61. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/ci.md +0 -0
  62. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/ffmpeg-keyframes.md +0 -0
  63. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/README.md +0 -0
  64. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  65. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/delay-alone-no-loss.md +0 -0
  66. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/head-of-line-irregularity.md +0 -0
  67. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  68. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/lighter-message-loss.md +0 -0
  69. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  70. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/forensics-report.md +0 -0
  71. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/geo-link-map.md +0 -0
  72. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/github-repository-settings.md +0 -0
  73. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/link-bandwidth.md +0 -0
  74. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/link-trace.md +0 -0
  75. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/owner-runbook.md +0 -0
  76. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/performance-bands.md +0 -0
  77. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/playbooks/README.md +0 -0
  78. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/quality-model.md +0 -0
  79. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release-notes/TEMPLATE.md +0 -0
  80. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release-notes/v2.1.md +0 -0
  81. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release-notes/v2.2.md +0 -0
  82. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release-notes/v2.3.md +0 -0
  83. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release-notes/v2.4.md +0 -0
  84. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/release.md +0 -0
  85. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  86. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0002-expectation-concepts.md +0 -0
  87. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0003-metric-backbone.md +0 -0
  88. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0004-network-profiles.md +0 -0
  89. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  90. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0006-dynamic-qos.md +0 -0
  91. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/0007-regression-gate.md +0 -0
  92. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/rfcs/README.md +0 -0
  93. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/testing.md +0 -0
  94. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/docs/work-items.md +0 -0
  95. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/install.sh +0 -0
  96. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/justfile +0 -0
  97. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/media/control_center.png +0 -0
  98. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/media/echo_test.png +0 -0
  99. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/media/forensics-report-example.png +0 -0
  100. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/media/host.png +0 -0
  101. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/media/vehicle.png +0 -0
  102. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/pyproject.toml +0 -0
  103. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/requirements.txt +0 -0
  104. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/rosotacom.yaml +0 -0
  105. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/setup.cfg +0 -0
  106. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/__init__.py +0 -0
  107. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/__main__.py +0 -0
  108. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/anonymize.py +0 -0
  109. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/bag_ground_truth.py +0 -0
  110. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/benched_set.py +0 -0
  111. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/benchmark.py +0 -0
  112. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/benchmark_delta.py +0 -0
  113. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/bundle_check.py +0 -0
  114. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/cli_benchmark.py +0 -0
  115. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/deployment.py +0 -0
  116. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/ffmpeg_packet.py +0 -0
  117. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/forensics.py +0 -0
  118. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/geomap.py +0 -0
  119. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/image_cache.py +0 -0
  120. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/network_profiles.py +0 -0
  121. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/network_shaper.py +0 -0
  122. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/plots.py +0 -0
  123. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/py.typed +0 -0
  124. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/benched-set.yaml +0 -0
  125. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/.gitignore +0 -0
  126. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/README.md +0 -0
  127. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/__init__.py +0 -0
  128. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  129. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  130. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  131. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  132. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  133. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  134. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  135. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  136. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  137. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  138. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  139. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  140. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  141. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  142. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  143. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  144. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  145. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  146. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  147. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  148. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  149. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  150. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  151. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  152. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  153. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  154. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  155. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  156. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  157. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  158. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  159. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  160. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  161. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  162. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  163. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  164. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  165. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  166. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  167. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  168. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  169. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  170. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  171. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  172. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  173. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  174. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  175. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  176. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  177. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  178. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  179. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ros2docker.json.example +0 -0
  202. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  203. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  204. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  205. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  206. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  207. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  208. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  209. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  210. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  211. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  212. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  213. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  214. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  215. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  216. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  217. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  218. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  219. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  220. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  221. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  222. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  223. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  224. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  225. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  226. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  227. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  228. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  229. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  230. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  231. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  232. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  233. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  234. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  235. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  236. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  237. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  238. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  239. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  240. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  241. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  242. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  243. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  244. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  245. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  246. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  247. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  248. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  249. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  250. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  251. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  252. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  253. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  254. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  255. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  256. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  257. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  258. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  259. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  260. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  261. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  262. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  263. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  264. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  265. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  266. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  267. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  268. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  269. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  270. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  271. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  272. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  273. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  274. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  275. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  276. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  277. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  278. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  279. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  280. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  281. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  282. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  283. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  284. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  285. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  286. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  287. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/status_eval.py +0 -0
  288. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/stream_stats.py +0 -0
  289. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/trace_profiles.py +0 -0
  290. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/transit.py +0 -0
  291. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom/video_quality.py +0 -0
  292. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  293. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  294. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  295. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  296. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/terminology.md +0 -0
  297. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_benched_set_registry.py +0 -0
  298. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_findings.py +0 -0
  299. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_markdown_links.py +0 -0
  300. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_ota_source_bundle.py +0 -0
  301. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_package_metadata.py +0 -0
  302. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_packaged_resources.py +0 -0
  303. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  304. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_pytest_policy.py +0 -0
  305. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_readme_examples.py +0 -0
  306. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_security_maintenance_config.py +0 -0
  307. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_session_configuration_doc.py +0 -0
  308. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_session_template_substitution.py +0 -0
  309. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/contract/test_workflow_contracts.py +0 -0
  310. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/conftest.py +0 -0
  311. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_anonymize_e2e.py +0 -0
  312. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_benchmark_ab.py +0 -0
  313. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_benchmark_capacity.py +0 -0
  314. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_benchmark_replay.py +0 -0
  315. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_parallel_smoke.py +0 -0
  316. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_smoke.py +0 -0
  317. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_timeline_stepping.py +0 -0
  318. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/e2e/test_video_quality_e2e.py +0 -0
  319. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/generate_rmw_matrix.py +0 -0
  320. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  321. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  322. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  323. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  324. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  325. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_anonymize.py +0 -0
  326. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_bag_ground_truth.py +0 -0
  327. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_benched_set.py +0 -0
  328. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_benchmark.py +0 -0
  329. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_benchmark_delta.py +0 -0
  330. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_bundle_check.py +0 -0
  331. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_cli.py +0 -0
  332. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_cli_benchmark.py +0 -0
  333. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_concurrency.py +0 -0
  334. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_deployment.py +0 -0
  335. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_ffmpeg_packet.py +0 -0
  336. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_forensics.py +0 -0
  337. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_geomap.py +0 -0
  338. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_image_cache.py +0 -0
  339. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_link_bytes.py +0 -0
  340. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_link_trace.py +0 -0
  341. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_network_profiles.py +0 -0
  342. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_network_shaper.py +0 -0
  343. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_ota_unwrapper.py +0 -0
  344. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_ota_wrapper.py +0 -0
  345. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_ota_xml.py +0 -0
  346. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_plots.py +0 -0
  347. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_stage_latency.py +0 -0
  348. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_status_eval.py +0 -0
  349. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_status_overview_observer.py +0 -0
  350. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_stream_stats.py +0 -0
  351. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_trace_profiles.py +0 -0
  352. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/tests/unit/test_transit.py +0 -0
  353. {rosotacom_dev-2.5.dev38 → rosotacom_dev-2.5.dev40}/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.dev38
3
+ Version: 2.5.dev40
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -446,7 +446,8 @@ processing:
446
446
  pixel_cap_preset: "wsvga" # scalar, used as suffix only,
447
447
  transport:
448
448
  type: ffmpeg # ffmpeg | foxglove | compressed
449
- local_republish: false # default false
449
+ local_republish: raw # raw | compressed; omit for no sender-side decode
450
+ remote_republish: compressed # raw | compressed; omit for no receiver-side decode
450
451
  # plus type-specific params (see below)
451
452
  ```
452
453
 
@@ -507,11 +508,37 @@ The receiver undoes the chain in reverse, and every stage there is computed from
507
508
  the delivered topic rather than from the OTA topic:
508
509
 
509
510
  1) unwrap → republishes on the pre-wrap name (so wrapping renames nothing)
510
- 2) `local_republish: true` → reverse transport decodes into `+ /raw`
511
+ 2) `remote_republish: <transport>` → reverse transport decodes into `+ /<transport>`
511
512
  3) decompress → republishes on the pre-compress name
512
513
  4) framebridge `global_to_local` → the local base topic
513
514
  5) trickle → `+ /trickle`, the only receiver-side stage that adds a suffix
514
515
 
516
+ ##### The two reverse republishes
517
+
518
+ An encoded stream crosses the link as packets — `FFMPEGPacket`, `CompressedVideo`
519
+ — and something has to turn those back into an image. That decode can happen in
520
+ two places, and they answer different questions:
521
+
522
+ - **`remote_republish`** runs on the receiving peer and is the one that matters:
523
+ it produces the topic the receiving application subscribes to (`<encoded>/raw`
524
+ or `<encoded>/compressed`), and it is what `_delivered_topic` reports as the
525
+ delivered stage.
526
+ - **`local_republish`** runs on the *sending* peer, against the stream it just
527
+ encoded. Nothing crosses the link for it; it exists so the sending machine can
528
+ see approximately what the receiver will get.
529
+
530
+ Both name the image transport they publish in rather than being switched on:
531
+
532
+ | value | delivered type | cost |
533
+ |---|---|---|
534
+ | `raw` | `sensor_msgs/msg/Image` | the transport undone and nothing else, at full frame size — a 1280x800 stream is ~3 MB per frame |
535
+ | `compressed` | `sensor_msgs/msg/CompressedImage` | a JPEG encode per frame, roughly an order of magnitude less data on the wire and in a `record -a` |
536
+
537
+ Omitting a key means that side does not decode. There is deliberately **no
538
+ default**: `type: compressed` already delivers an image, so a decode that
539
+ switched itself on would produce exactly the full-size copy a session chose that
540
+ transport to avoid.
541
+
515
542
  #### Transport parameters
516
543
  `type: ffmpeg` supports:
517
544
  - `gop_size` (int)
@@ -6134,8 +6134,11 @@ def _smoke_postprocessed_topic(entry: Any, pipe: dict[str, Any]) -> str:
6134
6134
  if pipe.get("framebridge") == "global_to_local":
6135
6135
  return str(pipe["fb_g2l_base"])
6136
6136
  transport = pipe.get("transport")
6137
- if transport is not None and bool(getattr(transport, "local_republish", False)):
6138
- return str(pipe["final"]) + "/raw"
6137
+ remote_republish = getattr(transport, "remote_republish", None) if transport is not None else None
6138
+ if remote_republish:
6139
+ # The receiver's decode is what the smoke drives and asserts; the
6140
+ # sender's own preview (`local_republish`) never reaches this peer.
6141
+ return str(pipe["final"]) + f"/{remote_republish}"
6139
6142
  return str(pipe["final"])
6140
6143
 
6141
6144
 
@@ -49,7 +49,11 @@ topics:
49
49
  processing:
50
50
  transport:
51
51
  type: ffmpeg
52
- local_republish: true
52
+ # `raw` on both sides, because this example measures the codec: PSNR
53
+ # and SSIM compare pixels, and a JPEG twin would fold its own loss
54
+ # into the number the example exists to report.
55
+ local_republish: raw
56
+ remote_republish: raw
53
57
  gop_size: 4
54
58
  bit_rate: 2000000
55
59
  # Wrapping an encoded stream: the wrapper runs after the encoder, so the
@@ -61,8 +61,8 @@ class BaseBridgeRelay(Node, PairRefreshMixin):
61
61
  self.qos_config_file = self.declare_parameter('qos_config_file', '').value
62
62
 
63
63
  # Optional parameters with defaults
64
- self.source_names = self.declare_parameter('source_names', rclpy.Parameter.Type.STRING_ARRAY).value or []
65
- self.target_names = self.declare_parameter('target_names', rclpy.Parameter.Type.STRING_ARRAY).value or []
64
+ self.source_names = self.declare_optional_string_array('source_names')
65
+ self.target_names = self.declare_optional_string_array('target_names')
66
66
 
67
67
  # Derived classes can declare additional parameters here (must happen before initialize_node()).
68
68
  self.declare_additional_parameters()
@@ -73,6 +73,36 @@ class BaseBridgeRelay(Node, PairRefreshMixin):
73
73
  # Create timer for periodic refresh of invalid pairs
74
74
  self.init_pair_refresh(period_s=5.0, log_prefix=self.node_role)
75
75
 
76
+ def declare_optional_string_array(self, name: str) -> list:
77
+ """Declare an optional STRING_ARRAY that reads as [] rather than as unset.
78
+
79
+ Declaring a type without a value leaves the parameter
80
+ PARAMETER_NOT_SET. This node never notices — it reads the value once,
81
+ here, and an unset parameter simply yields None. Everything *else*
82
+ notices: reading a not-set parameter raises, and rclpy's parameter
83
+ service logs one warning per read against this node,
84
+
85
+ [WARN] [ota_relay_out]: Failed to get parameters: The parameter
86
+ 'source_names' is not initialized: source_names
87
+
88
+ which anything that enumerates parameters then produces on a timer. A
89
+ Foxglove bridge with a parameter panel open put 107 of those lines into
90
+ a single pane in one session — the kind of noise that hides the message
91
+ somebody actually needs to see.
92
+
93
+ A plain default cannot express this: `declare_parameter(name, [])`
94
+ infers the type from the value, and an empty list satisfies the
95
+ BYTE_ARRAY test first (`all(...)` is true for an empty sequence). The
96
+ parameter would be typed BYTE_ARRAY and the real string array refused
97
+ later with "Wrong parameter type, expected '5' got '9'". Declaring the
98
+ type and then setting the empty value keeps it STRING_ARRAY, so a wrong
99
+ type is still rejected and an override still wins.
100
+ """
101
+ self.declare_parameter(name, rclpy.Parameter.Type.STRING_ARRAY)
102
+ if self.get_parameter_or(name).type_ == rclpy.Parameter.Type.NOT_SET:
103
+ self.set_parameters([rclpy.Parameter(name, rclpy.Parameter.Type.STRING_ARRAY, [])])
104
+ return self.get_parameter(name).value
105
+
76
106
  def initialize_node(self):
77
107
  """Initialize node with resolved topics and QoS configuration."""
78
108
  try:
@@ -139,7 +139,13 @@ def get_topic_qos(logger, qos_config: dict, topic_name: str, role_name: str):
139
139
  Additionally supports a fallback lookup: if no exact match for topic_name exists
140
140
  in qos_config['topics'], we look for the most specific configured base topic key
141
141
  that matches as a path segment within topic_name (e.g. '/tf' for
142
- '/shuttle_ella/tf/restamped'). If such a fallback is used, it is always logged.
142
+ '/shuttle_ella/tf/restamped'). Such a fallback is logged at debug level: it is
143
+ the designed path, not a degradation. Every processing stage appends a suffix,
144
+ so a session that uses framebridge, latching or a transport reaches it for most
145
+ of its topics, and a definition that declares QoS on the base topic and lets the
146
+ processed names inherit it is doing exactly what the schema intends. Logged as a
147
+ warning it was two lines per topic per peer at every start — enough noise around
148
+ a bring-up to bury an actual fault.
143
149
  """
144
150
  default = qos_config.get('default', {})
145
151
 
@@ -151,7 +157,7 @@ def get_topic_qos(logger, qos_config: dict, topic_name: str, role_name: str):
151
157
  if topic_name not in topics_cfg:
152
158
  base_match = _find_best_base_topic_match(topics_cfg, topic_name)
153
159
  if base_match is not None:
154
- logger.warning(
160
+ logger.debug(
155
161
  f"[dds_qos] No exact QoS match for topic='{topic_name}'. "
156
162
  f"Falling back to base topic='{base_match}'."
157
163
  )
@@ -534,6 +534,19 @@ windows:
534
534
  - commands:
535
535
  - if [ '${ipx_4_topics}' = "None" ]; then exit; fi
536
536
  - ros2 run com_py image_pixel_capper --ros-args -p topics:=[${ipx_4_topics}] -p max_pixels_preset:=${ipx_4_preset} -p qos_config_file:='${qos_config_file}'
537
+ # `if ...; then ...; fi`, never `[[ ... ]] && ...`, for the optional argument
538
+ # lines below. catmux sends every one of these to an interactive shell, and
539
+ # `catmux_log_setup.sh` hooks the pane-exit recorder into PROMPT_COMMAND — so
540
+ # it sees the status of each line, not just of the node the pane finally runs.
541
+ # `&&` short-circuits to status 1 whenever the option is unset, which is the
542
+ # normal case for every option a session does not override, so each of these
543
+ # lines wrote itself into `pane_failures.log` as a dead pane:
544
+ #
545
+ # pane=13-IT/0 exit=1 command=[[ "None" != "None" ]] && args+=(-p "ut.foxglove.bit_rate:=None")
546
+ #
547
+ # A peer then reported `STARTUP CHECK FAILED ... 4 pane(s) exited` on every
548
+ # start while nothing was wrong, which is the one thing that check must not
549
+ # do: it exists to make a genuinely dead pane visible in seconds.
537
550
  - name: IT
538
551
  if: it
539
552
  layout: even-vertical
@@ -542,15 +555,15 @@ windows:
542
555
  - if [ '${it_1_topic}' = "None" ]; then exit; fi
543
556
  - if [[ "${it_1_topic}" == *"/compressed"* ]]; then it_in_transport="compressed"; else it_in_transport="raw"; fi
544
557
  - args=(ros2 run image_transport republish --ros-args -r __node:=republish1 -p in_transport:=${it_in_transport})
545
- - '[[ "${it_1_transport}" != "None" ]] && args+=(-p "out_transport:=${it_1_transport}")'
546
- - '[[ "${it_1_compressed_jpeg_quality}" != "None" ]] && args+=(-p "ut.compressed.jpeg_quality:=${it_1_compressed_jpeg_quality}")'
547
- - '[[ "${it_1_ffmpeg_gop_size}" != "None" ]] && args+=(-p "ut.ffmpeg.gop_size:=${it_1_ffmpeg_gop_size}")'
548
- - '[[ "${it_1_ffmpeg_encoder_av_options}" != "None" ]] && args+=(-p "ut.ffmpeg.encoder_av_options:=${it_1_ffmpeg_encoder_av_options}")'
549
- - '[[ "${it_1_ffmpeg_bit_rate}" != "None" ]] && args+=(-p "ut.ffmpeg.bit_rate:=${it_1_ffmpeg_bit_rate}")'
550
- - '[[ "${it_1_foxglove_gop_size}" != "None" ]] && args+=(-p "ut.foxglove.gop_size:=${it_1_foxglove_gop_size}")'
551
- - '[[ "${it_1_foxglove_encoder_av_options}" != "None" ]] && args+=(-p "ut.foxglove.encoder_av_options:=${it_1_foxglove_encoder_av_options}")'
552
- - '[[ "${it_1_foxglove_bit_rate}" != "None" ]] && args+=(-p "ut.foxglove.bit_rate:=${it_1_foxglove_bit_rate}")'
553
- - '[[ "${it_1_foxglove_qmax}" != "None" ]] && args+=(-p "ut.foxglove.qmax:=${it_1_foxglove_qmax}")'
558
+ - 'if [[ "${it_1_transport}" != "None" ]]; then args+=(-p "out_transport:=${it_1_transport}"); fi'
559
+ - 'if [[ "${it_1_compressed_jpeg_quality}" != "None" ]]; then args+=(-p "ut.compressed.jpeg_quality:=${it_1_compressed_jpeg_quality}"); fi'
560
+ - 'if [[ "${it_1_ffmpeg_gop_size}" != "None" ]]; then args+=(-p "ut.ffmpeg.gop_size:=${it_1_ffmpeg_gop_size}"); fi'
561
+ - 'if [[ "${it_1_ffmpeg_encoder_av_options}" != "None" ]]; then args+=(-p "ut.ffmpeg.encoder_av_options:=${it_1_ffmpeg_encoder_av_options}"); fi'
562
+ - 'if [[ "${it_1_ffmpeg_bit_rate}" != "None" ]]; then args+=(-p "ut.ffmpeg.bit_rate:=${it_1_ffmpeg_bit_rate}"); fi'
563
+ - 'if [[ "${it_1_foxglove_gop_size}" != "None" ]]; then args+=(-p "ut.foxglove.gop_size:=${it_1_foxglove_gop_size}"); fi'
564
+ - 'if [[ "${it_1_foxglove_encoder_av_options}" != "None" ]]; then args+=(-p "ut.foxglove.encoder_av_options:=${it_1_foxglove_encoder_av_options}"); fi'
565
+ - 'if [[ "${it_1_foxglove_bit_rate}" != "None" ]]; then args+=(-p "ut.foxglove.bit_rate:=${it_1_foxglove_bit_rate}"); fi'
566
+ - 'if [[ "${it_1_foxglove_qmax}" != "None" ]]; then args+=(-p "ut.foxglove.qmax:=${it_1_foxglove_qmax}"); fi'
554
567
  - if [[ "${it_1_outgoing_suffix}" != "None" ]]; then outgoing_suffix="${it_1_outgoing_suffix}"; else outgoing_suffix="${it_1_transport}"; fi
555
568
  - if [[ "${it_in_transport}" == "raw" ]]; then in_remap="in"; else in_remap="in/${it_in_transport}"; fi
556
569
  - args+=(--remap "${in_remap}:=${it_1_topic}" --remap "out/${it_1_transport}:=${it_1_topic}/${outgoing_suffix}")
@@ -560,15 +573,15 @@ windows:
560
573
  - if [ '${it_2_topic}' = "None" ]; then exit; fi
561
574
  - if [[ "${it_2_topic}" == *"/compressed"* ]]; then it_in_transport="compressed"; else it_in_transport="raw"; fi
562
575
  - args=(ros2 run image_transport republish --ros-args -r __node:=republish2 -p in_transport:=${it_in_transport})
563
- - '[[ "${it_2_transport}" != "None" ]] && args+=(-p "out_transport:=${it_2_transport}")'
564
- - '[[ "${it_2_compressed_jpeg_quality}" != "None" ]] && args+=(-p "ut.compressed.jpeg_quality:=${it_2_compressed_jpeg_quality}")'
565
- - '[[ "${it_2_ffmpeg_gop_size}" != "None" ]] && args+=(-p "ut.ffmpeg.gop_size:=${it_2_ffmpeg_gop_size}")'
566
- - '[[ "${it_2_ffmpeg_encoder_av_options}" != "None" ]] && args+=(-p "ut.ffmpeg.encoder_av_options:=${it_2_ffmpeg_encoder_av_options}")'
567
- - '[[ "${it_2_ffmpeg_bit_rate}" != "None" ]] && args+=(-p "ut.ffmpeg.bit_rate:=${it_2_ffmpeg_bit_rate}")'
568
- - '[[ "${it_2_foxglove_gop_size}" != "None" ]] && args+=(-p "ut.foxglove.gop_size:=${it_2_foxglove_gop_size}")'
569
- - '[[ "${it_2_foxglove_encoder_av_options}" != "None" ]] && args+=(-p "ut.foxglove.encoder_av_options:=${it_2_foxglove_encoder_av_options}")'
570
- - '[[ "${it_2_foxglove_bit_rate}" != "None" ]] && args+=(-p "ut.foxglove.bit_rate:=${it_2_foxglove_bit_rate}")'
571
- - '[[ "${it_2_foxglove_qmax}" != "None" ]] && args+=(-p "ut.foxglove.qmax:=${it_2_foxglove_qmax}")'
576
+ - 'if [[ "${it_2_transport}" != "None" ]]; then args+=(-p "out_transport:=${it_2_transport}"); fi'
577
+ - 'if [[ "${it_2_compressed_jpeg_quality}" != "None" ]]; then args+=(-p "ut.compressed.jpeg_quality:=${it_2_compressed_jpeg_quality}"); fi'
578
+ - 'if [[ "${it_2_ffmpeg_gop_size}" != "None" ]]; then args+=(-p "ut.ffmpeg.gop_size:=${it_2_ffmpeg_gop_size}"); fi'
579
+ - 'if [[ "${it_2_ffmpeg_encoder_av_options}" != "None" ]]; then args+=(-p "ut.ffmpeg.encoder_av_options:=${it_2_ffmpeg_encoder_av_options}"); fi'
580
+ - 'if [[ "${it_2_ffmpeg_bit_rate}" != "None" ]]; then args+=(-p "ut.ffmpeg.bit_rate:=${it_2_ffmpeg_bit_rate}"); fi'
581
+ - 'if [[ "${it_2_foxglove_gop_size}" != "None" ]]; then args+=(-p "ut.foxglove.gop_size:=${it_2_foxglove_gop_size}"); fi'
582
+ - 'if [[ "${it_2_foxglove_encoder_av_options}" != "None" ]]; then args+=(-p "ut.foxglove.encoder_av_options:=${it_2_foxglove_encoder_av_options}"); fi'
583
+ - 'if [[ "${it_2_foxglove_bit_rate}" != "None" ]]; then args+=(-p "ut.foxglove.bit_rate:=${it_2_foxglove_bit_rate}"); fi'
584
+ - 'if [[ "${it_2_foxglove_qmax}" != "None" ]]; then args+=(-p "ut.foxglove.qmax:=${it_2_foxglove_qmax}"); fi'
572
585
  - if [[ "${it_2_outgoing_suffix}" != "None" ]]; then outgoing_suffix="${it_2_outgoing_suffix}"; else outgoing_suffix="${it_2_transport}"; fi
573
586
  - if [[ "${it_in_transport}" == "raw" ]]; then in_remap="in"; else in_remap="in/${it_in_transport}"; fi
574
587
  - args+=(--remap "${in_remap}:=${it_2_topic}" --remap "out/${it_2_transport}:=${it_2_topic}/${outgoing_suffix}")
@@ -578,15 +591,15 @@ windows:
578
591
  - if [ '${it_3_topic}' = "None" ]; then exit; fi
579
592
  - if [[ "${it_3_topic}" == *"/compressed"* ]]; then it_in_transport="compressed"; else it_in_transport="raw"; fi
580
593
  - args=(ros2 run image_transport republish --ros-args -r __node:=republish3 -p in_transport:=${it_in_transport})
581
- - '[[ "${it_3_transport}" != "None" ]] && args+=(-p "out_transport:=${it_3_transport}")'
582
- - '[[ "${it_3_compressed_jpeg_quality}" != "None" ]] && args+=(-p "ut.compressed.jpeg_quality:=${it_3_compressed_jpeg_quality}")'
583
- - '[[ "${it_3_ffmpeg_gop_size}" != "None" ]] && args+=(-p "ut.ffmpeg.gop_size:=${it_3_ffmpeg_gop_size}")'
584
- - '[[ "${it_3_ffmpeg_encoder_av_options}" != "None" ]] && args+=(-p "ut.ffmpeg.encoder_av_options:=${it_3_ffmpeg_encoder_av_options}")'
585
- - '[[ "${it_3_ffmpeg_bit_rate}" != "None" ]] && args+=(-p "ut.ffmpeg.bit_rate:=${it_3_ffmpeg_bit_rate}")'
586
- - '[[ "${it_3_foxglove_gop_size}" != "None" ]] && args+=(-p "ut.foxglove.gop_size:=${it_3_foxglove_gop_size}")'
587
- - '[[ "${it_3_foxglove_encoder_av_options}" != "None" ]] && args+=(-p "ut.foxglove.encoder_av_options:=${it_3_foxglove_encoder_av_options}")'
588
- - '[[ "${it_3_foxglove_bit_rate}" != "None" ]] && args+=(-p "ut.foxglove.bit_rate:=${it_3_foxglove_bit_rate}")'
589
- - '[[ "${it_3_foxglove_qmax}" != "None" ]] && args+=(-p "ut.foxglove.qmax:=${it_3_foxglove_qmax}")'
594
+ - 'if [[ "${it_3_transport}" != "None" ]]; then args+=(-p "out_transport:=${it_3_transport}"); fi'
595
+ - 'if [[ "${it_3_compressed_jpeg_quality}" != "None" ]]; then args+=(-p "ut.compressed.jpeg_quality:=${it_3_compressed_jpeg_quality}"); fi'
596
+ - 'if [[ "${it_3_ffmpeg_gop_size}" != "None" ]]; then args+=(-p "ut.ffmpeg.gop_size:=${it_3_ffmpeg_gop_size}"); fi'
597
+ - 'if [[ "${it_3_ffmpeg_encoder_av_options}" != "None" ]]; then args+=(-p "ut.ffmpeg.encoder_av_options:=${it_3_ffmpeg_encoder_av_options}"); fi'
598
+ - 'if [[ "${it_3_ffmpeg_bit_rate}" != "None" ]]; then args+=(-p "ut.ffmpeg.bit_rate:=${it_3_ffmpeg_bit_rate}"); fi'
599
+ - 'if [[ "${it_3_foxglove_gop_size}" != "None" ]]; then args+=(-p "ut.foxglove.gop_size:=${it_3_foxglove_gop_size}"); fi'
600
+ - 'if [[ "${it_3_foxglove_encoder_av_options}" != "None" ]]; then args+=(-p "ut.foxglove.encoder_av_options:=${it_3_foxglove_encoder_av_options}"); fi'
601
+ - 'if [[ "${it_3_foxglove_bit_rate}" != "None" ]]; then args+=(-p "ut.foxglove.bit_rate:=${it_3_foxglove_bit_rate}"); fi'
602
+ - 'if [[ "${it_3_foxglove_qmax}" != "None" ]]; then args+=(-p "ut.foxglove.qmax:=${it_3_foxglove_qmax}"); fi'
590
603
  - if [[ "${it_3_outgoing_suffix}" != "None" ]]; then outgoing_suffix="${it_3_outgoing_suffix}"; else outgoing_suffix="${it_3_transport}"; fi
591
604
  - if [[ "${it_in_transport}" == "raw" ]]; then in_remap="in"; else in_remap="in/${it_in_transport}"; fi
592
605
  - args+=(--remap "${in_remap}:=${it_3_topic}" --remap "out/${it_3_transport}:=${it_3_topic}/${outgoing_suffix}")
@@ -596,15 +609,15 @@ windows:
596
609
  - if [ '${it_4_topic}' = "None" ]; then exit; fi
597
610
  - if [[ "${it_4_topic}" == *"/compressed"* ]]; then it_in_transport="compressed"; else it_in_transport="raw"; fi
598
611
  - args=(ros2 run image_transport republish --ros-args -r __node:=republish4 -p in_transport:=${it_in_transport})
599
- - '[[ "${it_4_transport}" != "None" ]] && args+=(-p "out_transport:=${it_4_transport}")'
600
- - '[[ "${it_4_compressed_jpeg_quality}" != "None" ]] && args+=(-p "ut.compressed.jpeg_quality:=${it_4_compressed_jpeg_quality}")'
601
- - '[[ "${it_4_ffmpeg_gop_size}" != "None" ]] && args+=(-p "ut.ffmpeg.gop_size:=${it_4_ffmpeg_gop_size}")'
602
- - '[[ "${it_4_ffmpeg_encoder_av_options}" != "None" ]] && args+=(-p "ut.ffmpeg.encoder_av_options:=${it_4_ffmpeg_encoder_av_options}")'
603
- - '[[ "${it_4_ffmpeg_bit_rate}" != "None" ]] && args+=(-p "ut.ffmpeg.bit_rate:=${it_4_ffmpeg_bit_rate}")'
604
- - '[[ "${it_4_foxglove_gop_size}" != "None" ]] && args+=(-p "ut.foxglove.gop_size:=${it_4_foxglove_gop_size}")'
605
- - '[[ "${it_4_foxglove_encoder_av_options}" != "None" ]] && args+=(-p "ut.foxglove.encoder_av_options:=${it_4_foxglove_encoder_av_options}")'
606
- - '[[ "${it_4_foxglove_bit_rate}" != "None" ]] && args+=(-p "ut.foxglove.bit_rate:=${it_4_foxglove_bit_rate}")'
607
- - '[[ "${it_4_foxglove_qmax}" != "None" ]] && args+=(-p "ut.foxglove.qmax:=${it_4_foxglove_qmax}")'
612
+ - 'if [[ "${it_4_transport}" != "None" ]]; then args+=(-p "out_transport:=${it_4_transport}"); fi'
613
+ - 'if [[ "${it_4_compressed_jpeg_quality}" != "None" ]]; then args+=(-p "ut.compressed.jpeg_quality:=${it_4_compressed_jpeg_quality}"); fi'
614
+ - 'if [[ "${it_4_ffmpeg_gop_size}" != "None" ]]; then args+=(-p "ut.ffmpeg.gop_size:=${it_4_ffmpeg_gop_size}"); fi'
615
+ - 'if [[ "${it_4_ffmpeg_encoder_av_options}" != "None" ]]; then args+=(-p "ut.ffmpeg.encoder_av_options:=${it_4_ffmpeg_encoder_av_options}"); fi'
616
+ - 'if [[ "${it_4_ffmpeg_bit_rate}" != "None" ]]; then args+=(-p "ut.ffmpeg.bit_rate:=${it_4_ffmpeg_bit_rate}"); fi'
617
+ - 'if [[ "${it_4_foxglove_gop_size}" != "None" ]]; then args+=(-p "ut.foxglove.gop_size:=${it_4_foxglove_gop_size}"); fi'
618
+ - 'if [[ "${it_4_foxglove_encoder_av_options}" != "None" ]]; then args+=(-p "ut.foxglove.encoder_av_options:=${it_4_foxglove_encoder_av_options}"); fi'
619
+ - 'if [[ "${it_4_foxglove_bit_rate}" != "None" ]]; then args+=(-p "ut.foxglove.bit_rate:=${it_4_foxglove_bit_rate}"); fi'
620
+ - 'if [[ "${it_4_foxglove_qmax}" != "None" ]]; then args+=(-p "ut.foxglove.qmax:=${it_4_foxglove_qmax}"); fi'
608
621
  - if [[ "${it_4_outgoing_suffix}" != "None" ]]; then outgoing_suffix="${it_4_outgoing_suffix}"; else outgoing_suffix="${it_4_transport}"; fi
609
622
  - if [[ "${it_in_transport}" == "raw" ]]; then in_remap="in"; else in_remap="in/${it_in_transport}"; fi
610
623
  - args+=(--remap "${in_remap}:=${it_4_topic}" --remap "out/${it_4_transport}:=${it_4_topic}/${outgoing_suffix}")
@@ -64,18 +64,34 @@ HEARTBEAT_MSG_TYPE = "com_msgs/msg/EchoHeartbeat"
64
64
  COMPRESSED_MSG_TYPE = "com_msgs/msg/CompressedData"
65
65
  OTA_STAMPED_MSG_TYPE = "com_msgs/msg/OtaStamped"
66
66
  SENSOR_IMAGE_MSG_TYPE = "sensor_msgs/msg/Image"
67
+ SENSOR_COMPRESSED_IMAGE_MSG_TYPE = "sensor_msgs/msg/CompressedImage"
67
68
  TRANSPORT_OUTPUT_TYPES = {
68
69
  "compressed": "sensor_msgs/msg/CompressedImage",
69
70
  "ffmpeg": "ffmpeg_image_transport_msgs/msg/FFMPEGPacket",
70
71
  "foxglove": "foxglove_msgs/msg/CompressedVideo",
71
72
  }
73
+ #: The image transports a reverse republish may decode *into*, and the message
74
+ #: type each one delivers. `raw` is the neutral reconstruction -- the transport
75
+ #: undone and nothing else -- and `compressed` trades a JPEG encode for roughly
76
+ #: an order of magnitude less data on the wire and in a `record -a`.
77
+ REPUBLISH_OUTPUT_TYPES = {
78
+ "raw": SENSOR_IMAGE_MSG_TYPE,
79
+ "compressed": SENSOR_COMPRESSED_IMAGE_MSG_TYPE,
80
+ }
72
81
 
73
82
 
74
83
  @dataclass
75
84
  class TransportSpec:
76
85
  type: str
77
86
  params: Dict[str, Any]
78
- local_republish: bool = False
87
+ #: Reverse republish on the sending peer: decode the stream it just encoded,
88
+ #: so the machine that sends can see what the receiver will get. Optional.
89
+ local_republish: Optional[str] = None
90
+ #: Reverse republish on the receiving peer: the decode the receiving
91
+ #: application consumes. Optional too -- a consumer that subscribes through
92
+ #: image_transport itself, or one that only forwards or records packets,
93
+ #: does not want a second copy of every frame.
94
+ remote_republish: Optional[str] = None
79
95
 
80
96
 
81
97
  @dataclass
@@ -1288,11 +1304,14 @@ def _delivered_topic(pipe: Dict[str, Any], final: str) -> str:
1288
1304
  # renames nothing downstream of it.
1289
1305
  topic = str(pipe["ota_in"])
1290
1306
  transport = pipe.get("transport")
1291
- if isinstance(transport, TransportSpec) and transport.local_republish:
1292
- # image_transport reverse republish decodes the encoded topic into
1293
- # `<encoded>/raw`; PSNR/SSIM and application-level checks need that
1294
- # decoded sensor_msgs/Image stage, not the encoded packet.
1295
- topic = str(pipe["irt_in"]) + "/raw"
1307
+ if isinstance(transport, TransportSpec) and transport.remote_republish:
1308
+ # The receiver's reverse republish decodes the encoded topic into
1309
+ # `<encoded>/<transport>`, and that decoded image -- not the encoded
1310
+ # packet -- is what the receiving application subscribes to.
1311
+ # `local_republish` is deliberately not consulted here: it is the
1312
+ # *sender's* preview, on the sender's machine, and says nothing about
1313
+ # what the receiver ends up reading.
1314
+ topic = str(pipe["irt_in"]) + f"/{transport.remote_republish}"
1296
1315
  elif pipe.get("compress"):
1297
1316
  topic = str(pipe["comp_in"])
1298
1317
  if pipe.get("framebridge") == "global_to_local":
@@ -1544,8 +1563,8 @@ def _build_status_pipeline_spec(
1544
1563
  if postprocessed != final:
1545
1564
  native_in_type = base_type
1546
1565
  transport = p.get("transport")
1547
- if isinstance(transport, TransportSpec) and transport.local_republish:
1548
- native_in_type = SENSOR_IMAGE_MSG_TYPE
1566
+ if isinstance(transport, TransportSpec) and transport.remote_republish:
1567
+ native_in_type = REPUBLISH_OUTPUT_TYPES[transport.remote_republish]
1549
1568
  stages.append(
1550
1569
  {
1551
1570
  "stage": "native_in",
@@ -1659,9 +1678,27 @@ def _compute_pipeline(
1659
1678
  if not isinstance(t, dict) or "type" not in t:
1660
1679
  raise ValueError(f"transport must be dict with 'type' for topic '{entry.base}'")
1661
1680
  ttype = t["type"]
1662
- local_republish = bool(t.get("local_republish", False))
1663
- params = {k: v for k, v in t.items() if k not in ("type", "local_republish")}
1664
- transport = TransportSpec(type=ttype, params=params, local_republish=local_republish)
1681
+ republish_keys = ("local_republish", "remote_republish")
1682
+
1683
+ def _republish(key: str) -> Optional[str]:
1684
+ value = t.get(key)
1685
+ if value is None:
1686
+ return None
1687
+ if value not in REPUBLISH_OUTPUT_TYPES:
1688
+ raise ValueError(
1689
+ f"transport.{key} must name an image transport "
1690
+ f"({'|'.join(sorted(REPUBLISH_OUTPUT_TYPES))}) for topic '{entry.base}', got {value!r}. "
1691
+ "Omit the key for no reverse republish on that side."
1692
+ )
1693
+ return str(value)
1694
+
1695
+ params = {k: v for k, v in t.items() if k not in ("type", *republish_keys)}
1696
+ transport = TransportSpec(
1697
+ type=ttype,
1698
+ params=params,
1699
+ local_republish=_republish("local_republish"),
1700
+ remote_republish=_republish("remote_republish"),
1701
+ )
1665
1702
 
1666
1703
  topic = entry.base
1667
1704
 
@@ -1747,7 +1784,11 @@ def _compute_pipeline(
1747
1784
  if transport:
1748
1785
  it_in = topic
1749
1786
  topic = topic + f"/{transport.type}"
1750
- if transport.local_republish:
1787
+ if transport.local_republish or transport.remote_republish:
1788
+ # The encoded topic a reverse republish decodes *from*. One name for
1789
+ # both sides: each peer runs its own decoder against the copy it has
1790
+ # (the sender against what it just encoded, the receiver against what
1791
+ # arrived), and which of them exists is decided per side below.
1751
1792
  irt_in = topic
1752
1793
 
1753
1794
  # The wrapper is last, always. Its `seq` and send stamp describe whatever it
@@ -2607,7 +2648,11 @@ def func(
2607
2648
  thr_items = [(p["thr_in"], p["throttle"]) for p in out_pipes if p["thr_in"]]
2608
2649
  ipx_items = [(p["ipx_in"], p["pixel"]) for p in out_pipes if p["ipx_in"]]
2609
2650
  it_items = [(p["it_in"], p["transport"]) for p in out_pipes if p["it_in"]]
2610
- irt_items_local = [(p["irt_in"], p["transport"]) for p in out_pipes if p["irt_in"]]
2651
+ # Sender-side reverse transport: this peer decodes what it just encoded,
2652
+ # so the machine that sends can see what the receiver will get.
2653
+ irt_items_local = [
2654
+ (p["irt_in"], p["transport"]) for p in out_pipes if p["irt_in"] and p["transport"].local_republish
2655
+ ]
2611
2656
 
2612
2657
  # Compression happens on the sender side for topics marked with processing.compress
2613
2658
  comp_topics = _dedup_keep_order([p["comp_in"] for p in out_pipes if p["comp_in"]])
@@ -2660,15 +2705,15 @@ def func(
2660
2705
  _assert_max("it", it_items, 4)
2661
2706
  _assert_max("nor", nor_items, 4)
2662
2707
 
2663
- # Remote-side reverse transport is configured (if requested via local_republish) on the receiver, too.
2708
+ # Receiver-side reverse transport: this peer decodes what arrived, which
2709
+ # is the copy the receiving application reads (see `_delivered_topic`).
2664
2710
  irt_items_remote = []
2665
2711
  for e, p in zip(in_entries, in_pipes):
2666
- if not p["irt_in"]:
2667
- continue
2668
2712
  tspec = p["transport"]
2669
- assert tspec is not None
2713
+ if not p["irt_in"] or not (tspec and tspec.remote_republish):
2714
+ continue
2670
2715
  topic = _prefix_with_source_name_if_needed(local, remote, p["irt_in"])
2671
- irt_items_remote.append((topic, tspec.type))
2716
+ irt_items_remote.append((topic, tspec))
2672
2717
 
2673
2718
  # Build plugin.yaml blocks
2674
2719
  blocks: List[PluginBlock] = []
@@ -2953,9 +2998,9 @@ def func(
2953
2998
  if p.get("it_in")
2954
2999
  ]
2955
3000
  + [
2956
- f'{p["irt_in"]}/raw'
3001
+ f'{p["irt_in"]}/{p["transport"].local_republish}'
2957
3002
  for p in out_pipes
2958
- if p.get("irt_in")
3003
+ if p.get("irt_in") and p["transport"].local_republish
2959
3004
  ]
2960
3005
  )
2961
3006
  blocks.append(
@@ -3152,12 +3197,15 @@ def func(
3152
3197
  raise ValueError(f"Unsupported transport type '{tspec.type}'")
3153
3198
  blocks.append(PluginBlock("it", items2))
3154
3199
 
3155
- # Merge reverse-transport config for (a) local reconstruction and (b) inbound remote topics (if requested).
3200
+ # Merge reverse-transport config for (a) the sender's own preview of what
3201
+ # it encoded and (b) the decode of what arrived. Both run on *this* peer;
3202
+ # what differs is which copy they decode and which transport the session
3203
+ # asked each of them to publish in.
3156
3204
  irt_all: List[Tuple[str, str, str]] = []
3157
3205
  for t, tspec in irt_items_local:
3158
3206
  assert tspec is not None
3159
- irt_all.append((t, tspec.type, "raw"))
3160
- irt_all.extend((topic, ttype, "raw") for topic, ttype in irt_items_remote)
3207
+ irt_all.append((t, tspec.type, str(tspec.local_republish)))
3208
+ irt_all.extend((topic, tspec.type, str(tspec.remote_republish)) for topic, tspec in irt_items_remote)
3161
3209
  _assert_max("irt", irt_all, 4)
3162
3210
  if irt_all:
3163
3211
  items2 = [("irt", True)]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev38
3
+ Version: 2.5.dev40
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -337,6 +337,8 @@ tests/unit/test_ota_unwrapper.py
337
337
  tests/unit/test_ota_wrapper.py
338
338
  tests/unit/test_ota_xml.py
339
339
  tests/unit/test_plots.py
340
+ tests/unit/test_qos_fallback_logging.py
341
+ tests/unit/test_relay_optional_parameters.py
340
342
  tests/unit/test_session_pipeline.py
341
343
  tests/unit/test_stage_latency.py
342
344
  tests/unit/test_status_eval.py
@@ -332,6 +332,8 @@
332
332
  "tests/unit/test_ota_wrapper.py",
333
333
  "tests/unit/test_ota_xml.py",
334
334
  "tests/unit/test_plots.py",
335
+ "tests/unit/test_qos_fallback_logging.py",
336
+ "tests/unit/test_relay_optional_parameters.py",
335
337
  "tests/unit/test_session_pipeline.py",
336
338
  "tests/unit/test_stage_latency.py",
337
339
  "tests/unit/test_status_eval.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.4",
3
+ "distance": 40,
4
+ "node": "g679b8af8945b83eea3c582a4e0db6916592635bc",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-17"
8
+ }
@@ -111,3 +111,77 @@ def test_the_hook_is_installed_once_even_if_sourced_twice(pane_env: tuple[Path,
111
111
 
112
112
  (line,) = [ln for ln in result.stdout.splitlines() if ln.startswith("PC=")]
113
113
  assert line.count("__rosotacom_note_exit") == 1
114
+
115
+
116
+ # What the recorder cannot know, and why the session template has to care.
117
+ #
118
+ # The hook runs from PROMPT_COMMAND, so it sees every line a pane shell runs --
119
+ # including the lines that only assemble an argument list before the node
120
+ # starts. A line of the form `[[ cond ]] && args+=(...)` exits 1 whenever the
121
+ # condition is false, which is the normal case for every option a session leaves
122
+ # unset, so each one recorded itself as a dead pane. A peer then reported four
123
+ # exited panes at every start while nothing was wrong.
124
+
125
+ _UNSET_OPTION = '[[ "None" != "None" ]]'
126
+ _SET_OPTION = '[[ "22" != "None" ]]'
127
+
128
+
129
+ def _failure_log(catmux_dir: Path) -> Path:
130
+ return catmux_dir.parent / "pane_failures.log"
131
+
132
+
133
+ def test_the_short_circuit_form_records_a_false_failure(pane_env: tuple[Path, dict[str, str]]) -> None:
134
+ """The shape that was in the template: correct behaviour, status 1."""
135
+ catmux_dir, env = pane_env
136
+
137
+ _run(f'args=(); {_UNSET_OPTION} && args+=(-p "bit_rate:=None")', env)
138
+
139
+ assert _failure_log(catmux_dir).is_file()
140
+ assert "exit=1" in _failure_log(catmux_dir).read_text(encoding="utf-8")
141
+
142
+
143
+ def test_the_if_form_leaves_an_unset_option_silent(pane_env: tuple[Path, dict[str, str]]) -> None:
144
+ """The shape it was replaced with: same behaviour, status 0."""
145
+ catmux_dir, env = pane_env
146
+
147
+ _run(f'args=(); if {_UNSET_OPTION}; then args+=(-p "bit_rate:=None"); fi', env)
148
+
149
+ assert not _failure_log(catmux_dir).exists()
150
+
151
+
152
+ def test_the_if_form_still_appends_a_set_option(pane_env: tuple[Path, dict[str, str]]) -> None:
153
+ """Silencing the status must not silence the argument."""
154
+ catmux_dir, env = pane_env
155
+
156
+ result = _run(
157
+ f'args=(); if {_SET_OPTION}; then args+=(-p "bit_rate:=22"); fi\necho "ARGS=${{args[*]}}"',
158
+ env,
159
+ )
160
+
161
+ assert "ARGS=-p bit_rate:=22" in result.stdout
162
+ assert not _failure_log(catmux_dir).exists()
163
+
164
+
165
+ def test_no_session_template_line_uses_the_short_circuit_form() -> None:
166
+ """The template is where this reaches a running session, so it is checked there."""
167
+ template = (
168
+ REPO_ROOT
169
+ / "src"
170
+ / "rosotacom"
171
+ / "resources"
172
+ / "ws"
173
+ / "session"
174
+ / "content"
175
+ / "base"
176
+ / "session_plugin_base.yaml"
177
+ )
178
+ offenders = [
179
+ line.strip()
180
+ for line in template.read_text(encoding="utf-8").splitlines()
181
+ if line.lstrip().startswith("- '[[") and "&&" in line
182
+ ]
183
+
184
+ assert offenders == [], (
185
+ "these lines exit non-zero when their option is unset and land in "
186
+ f"pane_failures.log; use `if ...; then ...; fi`: {offenders}"
187
+ )