rosotacom-dev 2.5.dev66__tar.gz → 2.5.dev69__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 (395) hide show
  1. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/PKG-INFO +9 -1
  2. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/README.md +8 -0
  3. rosotacom_dev-2.5.dev69/docs/ota-pcap.md +150 -0
  4. rosotacom_dev-2.5.dev69/src/rosotacom/resources/ws/ros2src/com_py/com_py/ota_pcap.py +460 -0
  5. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +124 -0
  6. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +12 -0
  7. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +45 -0
  8. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/PKG-INFO +9 -1
  9. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/SOURCES.txt +4 -0
  10. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/scm_file_list.json +4 -0
  11. rosotacom_dev-2.5.dev69/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  12. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/terminology.md +54 -0
  13. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_generated_topics_have_a_producer.py +3 -1
  14. rosotacom_dev-2.5.dev69/tests/unit/test_ota_pcap.py +357 -0
  15. rosotacom_dev-2.5.dev69/tests/unit/test_terminology_stage_ownership.py +103 -0
  16. rosotacom_dev-2.5.dev66/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  17. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.devcontainer/devcontainer.json.template +0 -0
  18. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  19. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/CODEOWNERS +0 -0
  20. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  21. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  22. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  23. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  24. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  25. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  26. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  27. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  28. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  29. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/actions/setup-just/action.yml +0 -0
  30. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/dependabot.yml +0 -0
  31. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/benchmark-calibrate.yml +0 -0
  32. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/benchmark-gate.yml +0 -0
  33. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/dev-release.yml +0 -0
  34. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/image-scan.yml +0 -0
  35. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/nightly-e2e.yml +0 -0
  36. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/pr-lightweight.yml +0 -0
  37. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/pr-merge-gate.yml +0 -0
  38. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.github/workflows/release.yml +0 -0
  39. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/.pre-commit-config.yaml +0 -0
  40. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/AGENTS.md +0 -0
  41. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/CONCURRENCY.md +0 -0
  42. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/CONTRIBUTING.md +0 -0
  43. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/DEVELOPMENT_PRINCIPLES.md +0 -0
  44. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/LICENSE.txt +0 -0
  45. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/MANIFEST.in +0 -0
  46. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/budgets.jsonl +0 -0
  47. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/README.md +0 -0
  48. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/capacity-size-tight-cyclone.json +0 -0
  49. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  50. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/probe-loss-nominal-cyclone.json +0 -0
  51. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/probe-loss-tight-cyclone.json +0 -0
  52. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/probe-loss-tight-fastdds.json +0 -0
  53. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/calibration/probe-loss-tight-zenoh.json +0 -0
  54. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/codecov.yml +0 -0
  55. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/deployment-configuration.md +0 -0
  56. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/benchmark-ab.md +0 -0
  57. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/benchmark-bag-as-load.md +0 -0
  58. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/ci.md +0 -0
  59. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/ffmpeg-keyframes.md +0 -0
  60. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/README.md +0 -0
  61. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/a-transport-element-doubles-every-fastdds-sample.md +0 -0
  62. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/cyclone-fragment-equal-to-max-drops-fragmented-samples.md +0 -0
  63. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  64. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/delay-alone-no-loss.md +0 -0
  65. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +0 -0
  66. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
  67. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/head-of-line-irregularity.md +0 -0
  68. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  69. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
  70. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/lifespan-is-enforced-by-one-reader-and-not-the-other.md +0 -0
  71. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/lighter-message-loss.md +0 -0
  72. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/one-domain-two-cyclone-configs-stop-interoperating.md +0 -0
  73. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/oversubscription-queues-not-losses.md +0 -0
  74. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  75. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/repro/decode_chain_repro.py +0 -0
  76. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/repro/reorder_drop_repro.py +0 -0
  77. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
  78. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  79. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/forensics-report.md +0 -0
  80. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/geo-link-map.md +0 -0
  81. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/github-repository-settings.md +0 -0
  82. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/link-bandwidth.md +0 -0
  83. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/link-trace.md +0 -0
  84. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/owner-runbook.md +0 -0
  85. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/performance-bands.md +0 -0
  86. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/playbooks/README.md +0 -0
  87. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/quality-model.md +0 -0
  88. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release-notes/TEMPLATE.md +0 -0
  89. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release-notes/v2.1.md +0 -0
  90. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release-notes/v2.2.md +0 -0
  91. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release-notes/v2.3.md +0 -0
  92. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release-notes/v2.4.md +0 -0
  93. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/release.md +0 -0
  94. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  95. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0002-expectation-concepts.md +0 -0
  96. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0003-metric-backbone.md +0 -0
  97. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0004-network-profiles.md +0 -0
  98. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  99. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0006-dynamic-qos.md +0 -0
  100. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/0007-regression-gate.md +0 -0
  101. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/rfcs/README.md +0 -0
  102. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/testing.md +0 -0
  103. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/docs/work-items.md +0 -0
  104. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/install.sh +0 -0
  105. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/justfile +0 -0
  106. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/media/control_center.png +0 -0
  107. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/media/echo_test.png +0 -0
  108. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/media/forensics-report-example.png +0 -0
  109. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/media/host.png +0 -0
  110. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/media/vehicle.png +0 -0
  111. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/pyproject.toml +0 -0
  112. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/requirements.txt +0 -0
  113. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/rosotacom.yaml +0 -0
  114. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/session-configuration.md +0 -0
  115. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/setup.cfg +0 -0
  116. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/__init__.py +0 -0
  117. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/__main__.py +0 -0
  118. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/anonymize.py +0 -0
  119. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/bag_ground_truth.py +0 -0
  120. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/benched_set.py +0 -0
  121. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/benchmark.py +0 -0
  122. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/benchmark_delta.py +0 -0
  123. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/bundle_check.py +0 -0
  124. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/cli.py +0 -0
  125. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/cli_benchmark.py +0 -0
  126. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/deployment.py +0 -0
  127. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/ffmpeg_packet.py +0 -0
  128. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/forensics.py +0 -0
  129. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/geomap.py +0 -0
  130. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/image_cache.py +0 -0
  131. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/netem_dist.py +0 -0
  132. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/network_preflight.py +0 -0
  133. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/network_profiles.py +0 -0
  134. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/network_shaper.py +0 -0
  135. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/plots.py +0 -0
  136. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/py.typed +0 -0
  137. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/benched-set.yaml +0 -0
  138. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/.gitignore +0 -0
  139. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/README.md +0 -0
  140. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/__init__.py +0 -0
  141. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  142. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  143. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  144. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  145. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  146. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/ros2docker.lyrical.json +0 -0
  147. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  148. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  149. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  150. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  151. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.lyrical.ros2docker.json +0 -0
  152. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  153. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  154. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  155. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  156. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.lyrical.ros2docker.json +0 -0
  157. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  158. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  159. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  160. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  161. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  162. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  163. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  164. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  165. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  166. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  167. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  168. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  169. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  170. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  171. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  172. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  173. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  174. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  175. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  176. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  177. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  178. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  179. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  180. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  181. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  182. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  183. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  184. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  185. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  186. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  187. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  188. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  189. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  190. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  191. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  192. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  193. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  194. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  195. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  196. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  205. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  206. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  211. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  212. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  213. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  214. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  215. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  216. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  217. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  218. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  219. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ros2docker.json.example +0 -0
  220. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ros2docker.lyrical.json.example +0 -0
  221. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  222. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  223. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  224. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  225. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  227. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  228. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  229. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  230. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  231. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  232. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/cyclonedds_scoped.xml.template +0 -0
  233. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  234. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
  235. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
  236. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
  237. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  238. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  239. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  240. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  241. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  242. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  243. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  244. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  245. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  246. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  247. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  248. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  249. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  250. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  251. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  252. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  253. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  254. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  255. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  256. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  257. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  258. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  259. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  260. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  261. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  262. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  263. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  264. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  265. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  266. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  267. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  268. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  269. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  270. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  271. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  272. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  273. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  274. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  275. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  276. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  277. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  278. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  279. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  280. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  281. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  282. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  283. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  284. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  285. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  286. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  287. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  288. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  289. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  290. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  291. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  292. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  293. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  294. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  295. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  296. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  297. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  298. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  299. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  300. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  301. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  302. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  303. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  304. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  305. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  306. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  307. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  308. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  309. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  310. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  311. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  312. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  313. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  314. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/status_eval.py +0 -0
  315. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/stream_stats.py +0 -0
  316. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/trace_profiles.py +0 -0
  317. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/transit.py +0 -0
  318. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom/video_quality.py +0 -0
  319. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  320. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  321. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  322. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  323. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_benched_set_registry.py +0 -0
  324. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_findings.py +0 -0
  325. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_lyrical_runtime_compat.py +0 -0
  326. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_markdown_links.py +0 -0
  327. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_ota_source_bundle.py +0 -0
  328. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_package_metadata.py +0 -0
  329. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_packaged_resources.py +0 -0
  330. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  331. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_pytest_policy.py +0 -0
  332. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_readme_examples.py +0 -0
  333. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_security_maintenance_config.py +0 -0
  334. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_session_configuration_doc.py +0 -0
  335. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_session_template_substitution.py +0 -0
  336. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/contract/test_workflow_contracts.py +0 -0
  337. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/conftest.py +0 -0
  338. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_anonymize_e2e.py +0 -0
  339. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_benchmark_ab.py +0 -0
  340. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_benchmark_capacity.py +0 -0
  341. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_benchmark_replay.py +0 -0
  342. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_parallel_smoke.py +0 -0
  343. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_smoke.py +0 -0
  344. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_timeline_stepping.py +0 -0
  345. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/e2e/test_video_quality_e2e.py +0 -0
  346. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/generate_rmw_matrix.py +0 -0
  347. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  348. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  349. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  350. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  351. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
  352. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  353. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_anonymize.py +0 -0
  354. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_bag_ground_truth.py +0 -0
  355. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_benched_set.py +0 -0
  356. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_benchmark.py +0 -0
  357. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_benchmark_delta.py +0 -0
  358. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_bundle_check.py +0 -0
  359. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_catmux_pane_logging.py +0 -0
  360. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_cli.py +0 -0
  361. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_cli_benchmark.py +0 -0
  362. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_concurrency.py +0 -0
  363. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_container_shaping.py +0 -0
  364. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_deployment.py +0 -0
  365. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ffmpeg_flags.py +0 -0
  366. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ffmpeg_packet.py +0 -0
  367. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_field_profiles.py +0 -0
  368. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_forensics.py +0 -0
  369. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_geomap.py +0 -0
  370. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_image_cache.py +0 -0
  371. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_link_bytes.py +0 -0
  372. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_link_trace.py +0 -0
  373. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_netem_dist.py +0 -0
  374. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_network_preflight.py +0 -0
  375. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_network_profiles.py +0 -0
  376. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_network_shaper.py +0 -0
  377. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ota_link_configs.py +0 -0
  378. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ota_unwrapper.py +0 -0
  379. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ota_wrapper.py +0 -0
  380. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ota_xml.py +0 -0
  381. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_playout_pacer.py +0 -0
  382. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_plots.py +0 -0
  383. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_pub_sub_pair_types.py +0 -0
  384. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_qos_fallback_logging.py +0 -0
  385. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_relay_optional_parameters.py +0 -0
  386. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_ros_node_entity_ownership.py +0 -0
  387. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_session_pipeline.py +0 -0
  388. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_stage_latency.py +0 -0
  389. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_status_eval.py +0 -0
  390. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_status_overview.py +0 -0
  391. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_status_overview_observer.py +0 -0
  392. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_stream_stats.py +0 -0
  393. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_trace_profiles.py +0 -0
  394. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/tests/unit/test_transit.py +0 -0
  395. {rosotacom_dev-2.5.dev66 → rosotacom_dev-2.5.dev69}/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.dev66
3
+ Version: 2.5.dev69
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -684,6 +684,14 @@ the [link trace recorder](docs/link-trace.md). It writes
684
684
  `link_trace.jsonl` under the same status directory with `/proc/net/dev` counter
685
685
  deltas, echo-heartbeat RTT/loss provenance, and an optional modem-metrics hook.
686
686
 
687
+ Those counters localise loss to a *direction* and no further. When the question
688
+ is which fragment was lost, whether a retransmit followed, or how one sample's
689
+ sub-messages were spaced, enable the [header-only OTA capture](docs/ota-pcap.md)
690
+ next to them — `shared.ota_pcap.enabled`, off by default, roughly 1 % of payload
691
+ volume, and every packet labelled uplink or downlink. It needs no capability,
692
+ no image change and no extra container — only the passwordless sudo the
693
+ container already has, and only until its socket is open.
694
+
687
695
  Convert a recorded link trace into a profiles-file entry when you want to replay
688
696
  the same drive conditions in a repeatable benchmark:
689
697
 
@@ -639,6 +639,14 @@ the [link trace recorder](docs/link-trace.md). It writes
639
639
  `link_trace.jsonl` under the same status directory with `/proc/net/dev` counter
640
640
  deltas, echo-heartbeat RTT/loss provenance, and an optional modem-metrics hook.
641
641
 
642
+ Those counters localise loss to a *direction* and no further. When the question
643
+ is which fragment was lost, whether a retransmit followed, or how one sample's
644
+ sub-messages were spaced, enable the [header-only OTA capture](docs/ota-pcap.md)
645
+ next to them — `shared.ota_pcap.enabled`, off by default, roughly 1 % of payload
646
+ volume, and every packet labelled uplink or downlink. It needs no capability,
647
+ no image change and no extra container — only the passwordless sudo the
648
+ container already has, and only until its socket is open.
649
+
642
650
  Convert a recorded link trace into a profiles-file entry when you want to replay
643
651
  the same drive conditions in a repeatable benchmark:
644
652
 
@@ -0,0 +1,150 @@
1
+ # Header-Only OTA Capture
2
+
3
+ A passive, header-only packet capture of the OTA link, written beside the
4
+ [link trace](link-trace.md) by the same node:
5
+
6
+ ```text
7
+ session-instances/.../logs/<peer>/status/ota.pcap
8
+ session-instances/.../logs/<peer>/status/ota.json
9
+ ```
10
+
11
+ Enable it in a session definition:
12
+
13
+ ```yaml
14
+ shared:
15
+ use_status_overview: true
16
+ ota_pcap:
17
+ enabled: true
18
+ ```
19
+
20
+ Everything else is optional:
21
+
22
+ ```yaml
23
+ ota_pcap:
24
+ enabled: true
25
+ snaplen: 96 # bytes kept per packet; headers, no payload
26
+ max_mb: 2000 # ceiling for one capture file
27
+ peer_filter: true # keep only packets to or from the remote peer
28
+ ```
29
+
30
+ Off by default, and off is exactly the session that existed before: a
31
+ definition without the block renders the same peer files it always did, which
32
+ a test asserts by rendering both and diffing them.
33
+
34
+ ## What it answers that the link trace cannot
35
+
36
+ `link_trace.jsonl` counts `/proc/net/dev` bytes per direction once a second.
37
+ That localises loss to a **direction** and no further. It cannot say which
38
+ fragment of which sample was lost, whether a retransmit followed, or how one
39
+ sample's sub-messages were spaced on the wire.
40
+
41
+ The capture answers those, at roughly 1 % of payload volume. Use the two
42
+ together: the trace is the cheap continuous record, the capture is what you
43
+ turn on for a measurement run.
44
+
45
+ ## What the file is
46
+
47
+ `LINKTYPE_LINUX_SLL` (113), microsecond `pcap`, snaplen 96 by default. Every
48
+ reader takes it — Wireshark, `tshark`, `scapy`, `capinfos`.
49
+
50
+ The link type is the important choice. The capture uses a `SOCK_DGRAM` packet
51
+ socket, so one code path works on both link types the fleet has: a VPN `tun`
52
+ device is layer 3 and has no link-layer header at all, while a control-centre
53
+ LAN port is Ethernet. The kernel hands `SOCK_DGRAM` the network-layer packet
54
+ either way, plus the `sockaddr_ll` that `LINKTYPE_LINUX_SLL` is defined over.
55
+
56
+ That pseudo-header carries the field the analysis is for:
57
+
58
+ ```bash
59
+ # uplink (this host sent it) against downlink, per packet
60
+ tshark -r ota.pcap -T fields -e sll.pkttype | sort | uniq -c
61
+ # 4 = PACKET_OUTGOING, 0 = PACKET_HOST
62
+ ```
63
+
64
+ A `LINKTYPE_RAW` capture would have thrown that away.
65
+
66
+ Timestamps come from the kernel (`SO_TIMESTAMPNS`), not from the moment python
67
+ reached the packet.
68
+
69
+ ## The sidecar, and the number that matters
70
+
71
+ `ota.json` is written when the capture stops:
72
+
73
+ ```json
74
+ {
75
+ "interface": "tun0",
76
+ "snaplen": 96,
77
+ "linktype": "LINKTYPE_LINUX_SLL",
78
+ "filter": "udp and host 10.254.0.37",
79
+ "promiscuous": false,
80
+ "packets": 2434,
81
+ "bytes": 272498,
82
+ "kernel_drops": 0,
83
+ "kernel_seen": 4638,
84
+ "first_timestamp": 1787297717.008177,
85
+ "last_timestamp": 1787297733.10892,
86
+ "stopped_because": "signal"
87
+ }
88
+ ```
89
+
90
+ **`kernel_drops` is a gap in the file, not on the link.** A capture that fell
91
+ behind looks exactly like radio loss to anyone reading the pcap months later,
92
+ so subtract it before reading any loss out of the capture. Zero is the normal
93
+ result on a link of a few Mbit/s; a non-zero value means this process, not the
94
+ radio.
95
+
96
+ ## Which interface, and the refusal that matters
97
+
98
+ The interface is not configured. It is resolved from the peer's own OTA
99
+ address by `com_py.link_bytes.resolve_link_interface` — the same resolution the
100
+ status snapshot's `link` block uses, including its refusal to report a loopback
101
+ interface for a non-loopback address. That refusal is issue #267: sampling `lo`
102
+ on a machine that also runs an `-a` recorder reads about 28 Gbit/s next to a
103
+ 6 Mbit/s link, and it survived a field session and an offline analysis before
104
+ anyone doubted the number. A capture on the wrong device would be the same
105
+ mistake in a bigger file.
106
+
107
+ If no interface resolves, the capture is skipped with a warning and the session
108
+ runs on. `shared.ota_pcap.enabled` therefore requires
109
+ `shared.use_status_overview: true`, which is where that resolution lives.
110
+
111
+ ## Privilege, containers, and what the option costs
112
+
113
+ No image change, no rebuild, no added capability and no extra container in
114
+ `docker ps`. What it does need is `sudo`, and only until the socket is open.
115
+
116
+ ros2docker's entrypoint runs this container's processes as `containeruser`,
117
+ whose `CapEff` is `0000000000000000`. A non-root process cannot open an
118
+ `AF_PACKET` socket however the container was started, so `--cap-add NET_RAW`
119
+ would buy nothing. `containeruser` does have passwordless sudo here — the NET
120
+ pane's `sudo iftop` already depends on it — so the capture is launched through
121
+ it, and `--drop-to` hands the process back to `containeruser` the moment the
122
+ socket exists. The pcap therefore belongs to the same user as
123
+ `link_trace.jsonl` beside it, rather than to root.
124
+
125
+ Two earlier attempts at this assumed root from a `docker run` that bypassed the
126
+ entrypoint, and were wrong in the same way both times. If you change this,
127
+ check the **running** container:
128
+
129
+ ```bash
130
+ docker exec <com-container> bash -lc 'id; grep CapEff /proc/self/status'
131
+ ```
132
+
133
+ `CAP_NET_ADMIN` is *not* taken and is not needed: it buys promiscuous mode,
134
+ which this capture deliberately does not use. Everything of interest is
135
+ addressed to or from this host, and promiscuous mode on a control-centre LAN
136
+ port would collect other people's traffic.
137
+
138
+ The capture runs as a separate process rather than inside the status node's
139
+ thread, so a capture that misbehaves cannot reach the snapshot an operator is
140
+ watching. It is stopped with `SIGINT` and waited for, because that is what
141
+ writes the sidecar JSON.
142
+
143
+ ## Cost
144
+
145
+ Header-only at 96 bytes: about 6 MB per minute at 500 packets/s, so a two-hour
146
+ run is well under a gigabyte. `max_mb` bounds a mistake — a capture pointed at
147
+ a busy LAN port — rather than shaping a run; when it is reached the capture
148
+ stops and says so in `stopped_because`.
149
+
150
+ Nothing is transmitted, so the capture costs no link capacity.
@@ -0,0 +1,460 @@
1
+ """Header-only packet capture of the OTA link, beside the link trace.
2
+
3
+ Runs inside the communication container for the lifetime of one peer, started
4
+ by `status_overview` when a session definition asks for it:
5
+
6
+ shared:
7
+ use_status_overview: true
8
+ ota_pcap:
9
+ enabled: true
10
+
11
+ python3 -m com_py.ota_pcap --iface tun0 --out .../ota.pcap [--peer 10.254.0.37]
12
+
13
+ WHAT IT ANSWERS THAT `link_trace.jsonl` CANNOT
14
+ ----------------------------------------------
15
+ The link trace counts `/proc/net/dev` bytes per direction once a second, which
16
+ localises loss to a *direction* and no further. It cannot say which fragment of
17
+ which sample was lost, whether a retransmit followed, or how one sample's
18
+ sub-messages were spaced on the wire. Those are the questions a header-only
19
+ capture answers, at roughly 1 % of payload volume.
20
+
21
+ It is passive and local: nothing is transmitted, so it costs no link capacity,
22
+ and the interface is never put into promiscuous mode — everything of interest
23
+ is addressed to or from this host, and promiscuous mode on a control-centre LAN
24
+ port would collect other people's traffic. `CAP_NET_ADMIN`, which the
25
+ originating issue assumed was needed, buys only that.
26
+
27
+ WHY IT LIVES HERE AND NOT NEXT TO A RECORDER
28
+ --------------------------------------------
29
+ The first implementation of this sat beside the recording tool in the
30
+ downstream project, and had to rebuild what this container already knows:
31
+ which interface carries the link, and to which peer. `link_bytes` answers both
32
+ from the peer's own OTA address — including the refusal that matters most, a
33
+ loopback interface for a non-loopback address, which once read 28 Gbit/s next
34
+ to a 6 Mbit/s link and survived a field session (#267). A capture that has to
35
+ re-derive link knowledge is in the wrong place; this one asks the same
36
+ resolution the status snapshot's own link block uses.
37
+
38
+ WHAT IT NEEDS, MEASURED RATHER THAN ASSUMED
39
+ -------------------------------------------
40
+ `sudo`, and only until the socket is open. ros2docker's entrypoint runs this
41
+ container's processes as `containeruser`, whose `CapEff` is
42
+ 0000000000000000 — a non-root process cannot open an `AF_PACKET` socket
43
+ however the container was started, so `--cap-add` buys nothing. `containeruser`
44
+ does have passwordless sudo here, which the NET pane's `sudo iftop` already
45
+ relies on, so the capture is launched through it and `--drop-to` hands the
46
+ process back to `containeruser` the moment the socket exists. The pcap in
47
+ `logs/<peer>/status/` therefore belongs to the same user as `link_trace.jsonl`
48
+ beside it.
49
+
50
+ That is the whole cost: no image change, no rebuild, no added capability and no
51
+ extra container. Two earlier attempts at this assumed root from a `docker run`
52
+ that bypassed the entrypoint, and both were wrong in the same way — check the
53
+ *running* container, not the image.
54
+
55
+ THE FILE
56
+ --------
57
+ `SOCK_DGRAM` rather than `SOCK_RAW`, which is what makes one code path work on
58
+ both link types the fleet uses: a VPN `tun` device is layer 3 and has no
59
+ link-layer header at all, while a LAN port is Ethernet. The kernel hands a
60
+ `SOCK_DGRAM` packet socket the network-layer packet either way, plus the
61
+ `sockaddr_ll` that says what the link layer was.
62
+
63
+ That is exactly the input `LINKTYPE_LINUX_SLL` (113) is defined over, so the
64
+ 16-byte SLL pseudo-header is synthesized from the address tuple and the file
65
+ reads in Wireshark, tshark and scapy without a hint. It carries the field the
66
+ analysis is actually for: `pkttype` distinguishes PACKET_OUTGOING from
67
+ PACKET_HOST, so **every packet in the file is labelled uplink or downlink** —
68
+ which is the direction question the per-second counters answer only coarsely,
69
+ and the one a direction-blind `LINKTYPE_RAW` capture would have thrown away.
70
+
71
+ Timestamps come from the kernel (`SO_TIMESTAMPNS`), not from the moment python
72
+ got round to the packet, and are written at microsecond resolution — the
73
+ classic `0xa1b2c3d4` magic, because every reader takes it and a millisecond
74
+ phenomenon does not need nanoseconds.
75
+
76
+ HONESTY ABOUT WHAT IT MISSED
77
+ ----------------------------
78
+ A capture that quietly drops packets is worse than none: the analysis would
79
+ read the gap as radio loss. So `PACKET_STATISTICS` is read from the kernel and
80
+ the sidecar `.json` reports `kernel_drops` next to `packets`. A non-zero drop
81
+ count means this process fell behind, not that the link did.
82
+ """
83
+
84
+ from __future__ import annotations
85
+
86
+ import argparse
87
+ import json
88
+ import os
89
+ import signal
90
+ import socket
91
+ import struct
92
+ import sys
93
+ import time
94
+ from pathlib import Path
95
+
96
+ #: Capture every ethertype; the protocol filter is applied below, on the
97
+ #: network-layer header the kernel hands a SOCK_DGRAM packet socket.
98
+ ETH_P_ALL = 0x0003
99
+
100
+ ETH_P_IP = 0x0800
101
+ ETH_P_IPV6 = 0x86DD
102
+ IPPROTO_UDP = 17
103
+
104
+ #: linux/if_packet.h. Only the outgoing one is load-bearing — it is what makes
105
+ #: a packet uplink rather than downlink — but all five are written through so a
106
+ #: reader sees what the kernel saw.
107
+ PACKET_OUTGOING = 4
108
+
109
+ SOL_PACKET = 263
110
+ PACKET_STATISTICS = 6
111
+
112
+ #: CPython does not export these on every build — 3.12 in the recorder image
113
+ #: has neither, which cost the first capture its kernel timestamps and left it
114
+ #: stamping packets when python got round to them. The numbers are the
115
+ #: asm-generic ones every architecture the fleet runs uses, and `SCM_` shares
116
+ #: the `SO_` value by definition (linux/socket.h).
117
+ SO_TIMESTAMPNS = getattr(socket, "SO_TIMESTAMPNS", 35)
118
+ SCM_TIMESTAMPNS = getattr(socket, "SCM_TIMESTAMPNS", 35)
119
+
120
+ #: pcap, classic microsecond magic (LE). Every reader takes it.
121
+ PCAP_MAGIC = 0xA1B2C3D4
122
+ LINKTYPE_LINUX_SLL = 113
123
+
124
+ #: `-s 96` in the issue: enough for the link, network, UDP and RTPS sub-message
125
+ #: headers, and short of any payload.
126
+ DEFAULT_SNAPLEN = 96
127
+
128
+ #: Not part of the recorder's own disk budget, and deliberately far above what
129
+ #: a drive produces: header-only at 500 packets/s is ~6 MB per minute, so a
130
+ #: two-hour drive is well under a gigabyte. It exists to bound a mistake — a
131
+ #: capture pointed at a busy control-centre LAN port — not to shape a drive.
132
+ DEFAULT_MAX_MB = 2000
133
+
134
+ #: 4 MiB of kernel socket buffer. The default is a few hundred kilobytes, which
135
+ #: is where `kernel_drops` comes from on a burst.
136
+ RCVBUF_BYTES = 4 << 20
137
+
138
+
139
+ def log(message: str) -> None:
140
+ print(f"[ota_pcap] {message}", flush=True)
141
+
142
+
143
+ # ------------------------------------------------------------------ the file
144
+
145
+
146
+ def pcap_header(snaplen: int) -> bytes:
147
+ return struct.pack(
148
+ "<IHHiIII",
149
+ PCAP_MAGIC,
150
+ 2, # version_major
151
+ 4, # version_minor
152
+ 0, # thiszone: timestamps are UTC
153
+ 0, # sigfigs, as every writer leaves it
154
+ snaplen,
155
+ LINKTYPE_LINUX_SLL,
156
+ )
157
+
158
+
159
+ def sll_header(pkttype: int, hatype: int, addr: bytes, protocol: int) -> bytes:
160
+ """The 16-byte pseudo-header LINKTYPE_LINUX_SLL is defined over.
161
+
162
+ Big-endian, unlike the pcap headers around it — that is the format, not an
163
+ oversight. `addr` is padded to the fixed 8 bytes and its true length is
164
+ written next to it, which is how a reader tells a 6-byte MAC from the empty
165
+ address a layer-3 tunnel has.
166
+ """
167
+ address = (addr or b"")[:8]
168
+ return struct.pack(
169
+ "!HHH8sH",
170
+ pkttype,
171
+ hatype,
172
+ len(address),
173
+ address.ljust(8, b"\0"),
174
+ protocol,
175
+ )
176
+
177
+
178
+ def record_header(seconds: int, micros: int, incl_len: int, orig_len: int) -> bytes:
179
+ return struct.pack("<IIII", seconds, micros, incl_len, orig_len)
180
+
181
+
182
+ # --------------------------------------------------------------- the filter
183
+
184
+
185
+ def is_udp(protocol: int, packet: bytes) -> bool:
186
+ """UDP, judged from the network-layer header the packet socket handed us.
187
+
188
+ Both branches read a fixed offset of the header the kernel guarantees is
189
+ there, so a truncated or malformed packet is answered `False` rather than
190
+ raising inside the capture loop.
191
+
192
+ IPv4 fragments after the first carry no UDP header, and are still matched:
193
+ the protocol field is in the IP header of every fragment, which is what
194
+ keeps a fragmented sample whole in the file.
195
+ """
196
+ if protocol == ETH_P_IP:
197
+ return len(packet) > 9 and packet[9] == IPPROTO_UDP
198
+ if protocol == ETH_P_IPV6:
199
+ # Next Header. An extension-header chain would hide the UDP behind it;
200
+ # nothing on this link uses one, and guessing past it would be the kind
201
+ # of cleverness that silently drops traffic.
202
+ return len(packet) > 6 and packet[6] == IPPROTO_UDP
203
+ return False
204
+
205
+
206
+ def addresses_of(protocol: int, packet: bytes) -> tuple[str, str] | None:
207
+ """Source and destination, for the optional peer filter."""
208
+ try:
209
+ if protocol == ETH_P_IP and len(packet) >= 20:
210
+ return (
211
+ socket.inet_ntop(socket.AF_INET, packet[12:16]),
212
+ socket.inet_ntop(socket.AF_INET, packet[16:20]),
213
+ )
214
+ if protocol == ETH_P_IPV6 and len(packet) >= 40:
215
+ return (
216
+ socket.inet_ntop(socket.AF_INET6, packet[8:24]),
217
+ socket.inet_ntop(socket.AF_INET6, packet[24:40]),
218
+ )
219
+ except (ValueError, OSError):
220
+ return None
221
+ return None
222
+
223
+
224
+ def wanted(protocol: int, packet: bytes, peer: str | None) -> bool:
225
+ if not is_udp(protocol, packet):
226
+ return False
227
+ if peer is None:
228
+ return True
229
+ pair = addresses_of(protocol, packet)
230
+ return bool(pair) and peer in pair
231
+
232
+
233
+ # ---------------------------------------------------------------- capturing
234
+
235
+
236
+ def open_socket(iface: str) -> socket.socket:
237
+ """A non-promiscuous packet socket, bound to one interface.
238
+
239
+ `SOCK_DGRAM` normalises the two link types the fleet has — see the module
240
+ header. No `PACKET_ADD_MEMBERSHIP`, so the interface is never put into
241
+ promiscuous mode and only this host's own traffic is seen.
242
+ """
243
+ sock = socket.socket(socket.AF_PACKET, socket.SOCK_DGRAM, socket.htons(ETH_P_ALL))
244
+ try:
245
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, RCVBUF_BYTES)
246
+ except OSError: # a smaller buffer is a worse capture, not a broken one
247
+ pass
248
+ try:
249
+ sock.setsockopt(socket.SOL_SOCKET, SO_TIMESTAMPNS, 1)
250
+ except OSError:
251
+ log("WARN the kernel refused SO_TIMESTAMPNS — timestamps are taken in "
252
+ "userspace, so they carry this process's scheduling delay")
253
+ sock.bind((iface, ETH_P_ALL))
254
+ return sock
255
+
256
+
257
+ def drop_privileges(spec: str | None) -> str | None:
258
+ """Hand the process back to the user that started it.
259
+
260
+ The capture is launched through `sudo` — see the module header — because a
261
+ non-root process cannot open an `AF_PACKET` socket. Nothing after that call
262
+ needs root, and the artifacts in `logs/<peer>/status/` belong to
263
+ `containeruser` like `link_trace.jsonl` beside them: a root-owned file in
264
+ that directory is a `sudo rm` waiting to happen months later, and
265
+ `gather_drive.py` would copy it without noticing.
266
+
267
+ `spec` is "uid:gid". An argument rather than an environment variable
268
+ because sudo does not pass the environment through. Returns what it did, or
269
+ None when there was nothing to do.
270
+ """
271
+ if not spec:
272
+ return None
273
+ try:
274
+ uid_text, _, gid_text = spec.partition(":")
275
+ uid, gid = int(uid_text), int(gid_text or uid_text)
276
+ except ValueError:
277
+ log(f"WARN --drop-to {spec!r} is not uid:gid — staying as I am")
278
+ return None
279
+ if os.geteuid() != 0:
280
+ return None
281
+ try:
282
+ os.setgroups([])
283
+ os.setgid(gid)
284
+ os.setuid(uid)
285
+ except OSError as error:
286
+ log(f"WARN could not drop to {uid}:{gid} ({error}) — the file will be root's")
287
+ return None
288
+ return f"{uid}:{gid}"
289
+
290
+
291
+ def kernel_timestamp(ancdata) -> tuple[int, int] | None:
292
+ """(seconds, microseconds) out of SCM_TIMESTAMPNS, if the kernel sent it."""
293
+ for level, kind, data in ancdata:
294
+ if level == socket.SOL_SOCKET and kind == SCM_TIMESTAMPNS:
295
+ if len(data) >= 16:
296
+ seconds, nanos = struct.unpack("qq", data[:16])
297
+ return int(seconds), int(nanos) // 1000
298
+ return None
299
+
300
+
301
+ def packet_statistics(sock: socket.socket) -> tuple[int, int]:
302
+ """(seen, dropped) since the last call — reading resets the counters."""
303
+ try:
304
+ raw = sock.getsockopt(SOL_PACKET, PACKET_STATISTICS, 8)
305
+ except OSError:
306
+ return 0, 0
307
+ seen, dropped = struct.unpack("II", raw)
308
+ return int(seen), int(dropped)
309
+
310
+
311
+ class Capture:
312
+ """One pcap file, and the counts needed to trust it."""
313
+
314
+ def __init__(self, iface: str, out: Path, *, snaplen: int = DEFAULT_SNAPLEN,
315
+ peer: str | None = None, max_bytes: int = DEFAULT_MAX_MB * 1000 * 1000,
316
+ drop_to: str | None = None):
317
+ self.drop_to = drop_to
318
+ self.iface = iface
319
+ self.out = out
320
+ self.snaplen = snaplen
321
+ self.peer = peer
322
+ self.max_bytes = max_bytes
323
+ self.packets = 0
324
+ self.bytes_written = 0
325
+ self.kernel_drops = 0
326
+ self.kernel_seen = 0
327
+ self.first_ts: float | None = None
328
+ self.last_ts: float | None = None
329
+ self.stopped_because = "signal"
330
+
331
+ def sidecar(self) -> dict:
332
+ """What the file is, and what it is missing. Written next to the pcap."""
333
+ return {
334
+ "interface": self.iface,
335
+ "snaplen": self.snaplen,
336
+ "linktype": "LINKTYPE_LINUX_SLL",
337
+ "filter": "udp" + (f" and host {self.peer}" if self.peer else ""),
338
+ "promiscuous": False,
339
+ "packets": self.packets,
340
+ "bytes": self.bytes_written,
341
+ # Packets the kernel dropped because this process fell behind. A
342
+ # non-zero value here is a gap in the FILE, not in the link, and
343
+ # any loss analysis has to subtract it rather than report it.
344
+ "kernel_drops": self.kernel_drops,
345
+ "kernel_seen": self.kernel_seen,
346
+ "first_timestamp": self.first_ts,
347
+ "last_timestamp": self.last_ts,
348
+ "stopped_because": self.stopped_because,
349
+ }
350
+
351
+ def run(self, stop: list) -> None:
352
+ sock = open_socket(self.iface)
353
+ # Root was needed for that one call and for nothing after it.
354
+ dropped = drop_privileges(self.drop_to)
355
+ if dropped:
356
+ log(f"privileges dropped to {dropped}; the socket stays open")
357
+ packet_statistics(sock) # zero the counters; the run owns them from here
358
+ ancillary = socket.CMSG_SPACE(16)
359
+ self.out.parent.mkdir(parents=True, exist_ok=True)
360
+ with open(self.out, "wb", buffering=1 << 20) as handle:
361
+ handle.write(pcap_header(self.snaplen))
362
+ self.bytes_written = 24
363
+ sock.settimeout(0.5)
364
+ while not stop:
365
+ try:
366
+ packet, ancdata, _flags, address = sock.recvmsg(65535, ancillary)
367
+ except socket.timeout:
368
+ continue
369
+ except OSError as error:
370
+ log(f"WARN capture socket: {error}")
371
+ self.stopped_because = f"socket error: {error}"
372
+ break
373
+ # (ifname, protocol, pkttype, hatype, addr) — everything the
374
+ # SLL header needs, which is why SOCK_DGRAM is enough.
375
+ _name, protocol, pkttype, hatype, addr = address
376
+ if not wanted(protocol, packet, self.peer):
377
+ continue
378
+ stamp = kernel_timestamp(ancdata)
379
+ if stamp is None:
380
+ now = time.time()
381
+ stamp = (int(now), int((now % 1) * 1_000_000))
382
+ body = sll_header(pkttype, hatype, addr, protocol) + packet
383
+ orig_len = len(body)
384
+ clipped = body[: self.snaplen]
385
+ handle.write(record_header(stamp[0], stamp[1], len(clipped), orig_len))
386
+ handle.write(clipped)
387
+ self.packets += 1
388
+ self.bytes_written += 16 + len(clipped)
389
+ seconds = stamp[0] + stamp[1] / 1_000_000
390
+ if self.first_ts is None:
391
+ self.first_ts = seconds
392
+ self.last_ts = seconds
393
+ if self.bytes_written >= self.max_bytes:
394
+ log(f"stopping at the {self.max_bytes // 1000 // 1000} MB ceiling "
395
+ f"after {self.packets} packets — the recording is untouched")
396
+ self.stopped_because = "size ceiling"
397
+ break
398
+ if self.packets % 20000 == 0:
399
+ self._collect(sock)
400
+ self._collect(sock)
401
+ sock.close()
402
+
403
+ def _collect(self, sock: socket.socket) -> None:
404
+ seen, dropped = packet_statistics(sock)
405
+ self.kernel_seen += seen
406
+ self.kernel_drops += dropped
407
+
408
+
409
+ # --------------------------------------------------------------------- main
410
+
411
+
412
+ def main(argv: list[str] | None = None) -> int:
413
+ parser = argparse.ArgumentParser(
414
+ description="Header-only UDP capture on one interface, written as pcap.")
415
+ parser.add_argument("--iface", required=True, help="interface to capture on")
416
+ parser.add_argument("--out", required=True, type=Path, help="pcap file to write")
417
+ parser.add_argument("--snaplen", type=int, default=DEFAULT_SNAPLEN,
418
+ help=f"bytes kept per packet (default {DEFAULT_SNAPLEN})")
419
+ parser.add_argument("--peer", help="keep only packets to or from this address")
420
+ parser.add_argument("--max-mb", type=int, default=DEFAULT_MAX_MB,
421
+ help=f"stop after this many MB (default {DEFAULT_MAX_MB})")
422
+ parser.add_argument("--drop-to", metavar="UID:GID",
423
+ help="give root back once the socket is open, so the pcap "
424
+ "belongs to the user that started the session")
425
+ args = parser.parse_args(argv)
426
+
427
+ capture = Capture(args.iface, args.out, snaplen=args.snaplen, peer=args.peer,
428
+ max_bytes=args.max_mb * 1000 * 1000, drop_to=args.drop_to)
429
+
430
+ stop: list = []
431
+ for name in (signal.SIGINT, signal.SIGTERM):
432
+ signal.signal(name, lambda *_: stop.append(True))
433
+
434
+ log(f"{args.iface} -> {args.out} (-s {args.snaplen}, udp"
435
+ f"{f' and host {args.peer}' if args.peer else ''})")
436
+ try:
437
+ capture.run(stop)
438
+ except PermissionError:
439
+ log("FAIL no permission to open a packet socket. This is meant to run "
440
+ "through `sudo` from inside the communication container, where "
441
+ "containeruser has passwordless sudo; a non-root process cannot "
442
+ "open AF_PACKET whatever capabilities the container was given.")
443
+ return 3
444
+ except OSError as error:
445
+ log(f"FAIL {error}")
446
+ return 3
447
+
448
+ sidecar = capture.sidecar()
449
+ args.out.with_suffix(".json").write_text(
450
+ json.dumps(sidecar, indent=2) + "\n", encoding="utf-8")
451
+ log(f"{sidecar['packets']} packets, {sidecar['bytes'] / 1e6:.1f} MB, "
452
+ f"{sidecar['kernel_drops']} dropped by the kernel")
453
+ if sidecar["kernel_drops"]:
454
+ log("WARN the kernel dropped packets: that is a gap in THIS FILE, not "
455
+ "on the link. Subtract it before reading any loss out of the pcap.")
456
+ return 0
457
+
458
+
459
+ if __name__ == "__main__":
460
+ sys.exit(main())