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