pymcap-cli 0.8.0__tar.gz → 0.10.0__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 (134) hide show
  1. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/PKG-INFO +221 -4
  2. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/README.md +211 -0
  3. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/pyproject.toml +16 -14
  4. pymcap_cli-0.10.0/src/pymcap_cli/cli.py +284 -0
  5. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/_run_processor.py +66 -1
  6. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/_run_processor_multi.py +6 -14
  7. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/bag2mcap_cmd.py +18 -38
  8. pymcap_cli-0.10.0/src/pymcap_cli/cmd/bridge/__init__.py +25 -0
  9. pymcap_cli-0.10.0/src/pymcap_cli/cmd/bridge/_shared.py +199 -0
  10. pymcap_cli-0.10.0/src/pymcap_cli/cmd/bridge/cat.py +429 -0
  11. pymcap_cli-0.10.0/src/pymcap_cli/cmd/bridge/inspect.py +382 -0
  12. pymcap_cli-0.10.0/src/pymcap_cli/cmd/bridge/record.py +503 -0
  13. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/cat_cmd.py +106 -144
  14. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/compress_cmd.py +23 -9
  15. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/convert_cmd.py +19 -86
  16. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/diag_cmd.py +13 -10
  17. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/diff_cmd.py +178 -73
  18. pymcap_cli-0.10.0/src/pymcap_cli/cmd/doctor_cmd.py +156 -0
  19. pymcap_cli-0.10.0/src/pymcap_cli/cmd/duplicates_cmd.py +732 -0
  20. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_csv_cmd.py +1 -6
  21. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_geo_cmd.py +4 -8
  22. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_images_cmd.py +4 -10
  23. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_json_cmd.py +1 -6
  24. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_parquet_cmd.py +6 -10
  25. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/export_pcd_cmd.py +1 -6
  26. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/filter_cmd.py +87 -9
  27. pymcap_cli-0.10.0/src/pymcap_cli/cmd/get_cmd.py +150 -0
  28. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/info_cmd.py +22 -21
  29. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/list_cmd.py +5 -4
  30. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/merge_cmd.py +45 -9
  31. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/plot_cmd.py +18 -15
  32. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/process_cmd.py +23 -18
  33. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/rechunk_cmd.py +41 -30
  34. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/records_cmd.py +5 -5
  35. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/recover_cmd.py +30 -12
  36. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/recover_inplace_cmd.py +17 -16
  37. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/roscompress_cmd.py +41 -59
  38. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/rosdecompress_cmd.py +8 -15
  39. pymcap_cli-0.10.0/src/pymcap_cli/cmd/split_cmd.py +406 -0
  40. pymcap_cli-0.10.0/src/pymcap_cli/cmd/tf_export_cmd.py +243 -0
  41. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/tftree_cmd.py +24 -115
  42. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/video_cmd.py +4 -5
  43. pymcap_cli-0.10.0/src/pymcap_cli/constants.py +14 -0
  44. pymcap_cli-0.10.0/src/pymcap_cli/core/input_options.py +113 -0
  45. pymcap_cli-0.10.0/src/pymcap_cli/core/input_processor_chain.py +75 -0
  46. pymcap_cli-0.10.0/src/pymcap_cli/core/mcap_compare.py +1090 -0
  47. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/mcap_processor.py +618 -290
  48. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/mcap_transform.py +40 -20
  49. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/always_decode.py +20 -0
  50. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/attachment_filter.py +28 -0
  51. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/base.py +263 -0
  52. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/boundary_split.py +70 -0
  53. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/channel_merge.py +79 -0
  54. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/dedup.py +143 -0
  55. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/duration_split.py +66 -0
  56. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/expression_split.py +273 -0
  57. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/latching.py +168 -0
  58. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/metadata_filter.py +28 -0
  59. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/nth_message.py +74 -0
  60. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/size_split.py +65 -0
  61. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/time_filter.py +141 -0
  62. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/time_offset.py +68 -0
  63. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/timestamp_split.py +40 -0
  64. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/topic_alias.py +132 -0
  65. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/topic_filter.py +60 -0
  66. pymcap_cli-0.10.0/src/pymcap_cli/core/processors/topic_rewrite.py +67 -0
  67. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/processors/utils.py +5 -5
  68. pymcap_cli-0.10.0/src/pymcap_cli/core/tf_tree.py +154 -0
  69. pymcap_cli-0.10.0/src/pymcap_cli/display/cat_helpers.py +65 -0
  70. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/display/display_utils.py +3 -2
  71. pymcap_cli-0.10.0/src/pymcap_cli/display/message_render.py +523 -0
  72. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/display/sparkline.py +0 -3
  73. pymcap_cli-0.10.0/src/pymcap_cli/display/time_ranges.py +60 -0
  74. pymcap_cli-0.10.0/src/pymcap_cli/doctor.py +3024 -0
  75. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/_common.py +17 -8
  76. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/base.py +7 -8
  77. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/csv_exporter.py +2 -2
  78. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/driver.py +22 -25
  79. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/geo_common.py +76 -4
  80. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/geojson_exporter.py +6 -35
  81. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/gpx_exporter.py +10 -60
  82. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/image_exporter.py +87 -59
  83. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/json_exporter.py +2 -1
  84. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/kml_exporter.py +18 -65
  85. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/parquet_exporter.py +13 -26
  86. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/pcd_exporter.py +2 -16
  87. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/plot_exporter.py +20 -30
  88. pymcap_cli-0.10.0/src/pymcap_cli/exporters/sdf_exporter.py +70 -0
  89. pymcap_cli-0.10.0/src/pymcap_cli/exporters/urdf_exporter.py +65 -0
  90. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/video_exporter.py +8 -12
  91. pymcap_cli-0.10.0/src/pymcap_cli/exporters/video_file_writer.py +560 -0
  92. pymcap_cli-0.10.0/src/pymcap_cli/log_setup.py +59 -0
  93. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/rihs01.py +2 -2
  94. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/rosbag_reader/_reader.py +2 -3
  95. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/rosbag_reader/_types.py +1 -1
  96. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/duration.py +7 -5
  97. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/info_data.py +27 -28
  98. pymcap_cli-0.10.0/src/pymcap_cli/types/size.py +52 -0
  99. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/to_plain.py +5 -3
  100. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/types_manual.py +12 -16
  101. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/utils.py +221 -53
  102. pymcap_cli-0.8.0/src/pymcap_cli/cli.py +0 -226
  103. pymcap_cli-0.8.0/src/pymcap_cli/cmd/split_cmd.py +0 -255
  104. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/always_decode.py +0 -16
  105. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/attachment_filter.py +0 -19
  106. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/base.py +0 -80
  107. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/duration_split.py +0 -73
  108. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/expression_split.py +0 -153
  109. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/metadata_filter.py +0 -19
  110. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/time_filter.py +0 -48
  111. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/timestamp_split.py +0 -70
  112. pymcap_cli-0.8.0/src/pymcap_cli/core/processors/topic_filter.py +0 -36
  113. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/__init__.py +0 -0
  114. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/__init__.py +0 -0
  115. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/du_cmd.py +0 -0
  116. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/info_json_cmd.py +0 -0
  117. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/cmd/topic_chunks_cmd.py +0 -0
  118. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/__init__.py +0 -0
  119. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/input_handler.py +0 -0
  120. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/msg_resolver.py +0 -0
  121. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/core/processors/__init__.py +0 -0
  122. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/debug_wrapper.py +0 -0
  123. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/display/__init__.py +0 -0
  124. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/display/osc_utils.py +0 -0
  125. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/encoding/__init__.py +0 -0
  126. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/encoding/arrow_schema.py +0 -0
  127. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/exporters/__init__.py +0 -0
  128. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/http_utils.py +0 -0
  129. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/py.typed +0 -0
  130. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/rosbag_reader/__init__.py +0 -0
  131. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/rosbag_reader/py.typed +0 -0
  132. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/__init__.py +0 -0
  133. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/info_link.py +0 -0
  134. {pymcap_cli-0.8.0 → pymcap_cli-0.10.0}/src/pymcap_cli/types/info_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymcap-cli
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: High-performance Python CLI for MCAP file processing with advanced recovery, filtering, and optimization capabilities
5
5
  Keywords: mcap,cli,robotics,ros,ros2,recovery,filtering,compression
6
6
  Author: Marko Bausch
@@ -16,39 +16,45 @@ Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
19
20
  Classifier: Topic :: Scientific/Engineering
20
21
  Classifier: Topic :: System :: Archiving
21
22
  Classifier: Topic :: Utilities
22
23
  Classifier: Typing :: Typed
23
24
  Requires-Dist: rich>=14.1.0
24
25
  Requires-Dist: small-mcap[compression]
25
- Requires-Dist: mcap-codec-support[ros2]
26
26
  Requires-Dist: mcap-ros2-support-fast
27
27
  Requires-Dist: cyclopts>=4
28
28
  Requires-Dist: ros-parser
29
29
  Requires-Dist: platformdirs>=4.0.0
30
30
  Requires-Dist: pyyaml>=6.0
31
31
  Requires-Dist: typing-extensions>=4.15.0
32
- Requires-Dist: pymcap-cli[video,pointcloud,plot,parquet,image,draco] ; extra == 'all'
32
+ Requires-Dist: pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash] ; extra == 'all'
33
+ Requires-Dist: robo-ws-bridge ; extra == 'bridge'
33
34
  Requires-Dist: mcap-codec-support[draco] ; extra == 'draco'
34
- Requires-Dist: mcap-codec-support[image] ; extra == 'image'
35
+ Requires-Dist: pillow>=10.0 ; extra == 'image'
36
+ Requires-Dist: pymcap-cli[image,draco,bridge,xxhash] ; extra == 'lite'
35
37
  Requires-Dist: pyarrow>=15.0.0 ; extra == 'parquet'
36
38
  Requires-Dist: numpy>=1.24.0 ; extra == 'parquet'
37
39
  Requires-Dist: mcap-codec-support[pointcloud] ; extra == 'parquet'
38
40
  Requires-Dist: plotly>=6.0.0 ; extra == 'plot'
39
41
  Requires-Dist: mcap-codec-support[pointcloud] ; extra == 'pointcloud'
40
42
  Requires-Dist: mcap-codec-support[video] ; extra == 'video'
43
+ Requires-Dist: xxhash>=3.0.0 ; extra == 'xxhash'
41
44
  Requires-Python: >=3.10
42
45
  Project-URL: Homepage, https://github.com/mrkbac/robotic-tools
43
46
  Project-URL: Issues, https://github.com/mrkbac/robotic-tools/issues
44
47
  Project-URL: Repository, https://github.com/mrkbac/robotic-tools
45
48
  Provides-Extra: all
49
+ Provides-Extra: bridge
46
50
  Provides-Extra: draco
47
51
  Provides-Extra: image
52
+ Provides-Extra: lite
48
53
  Provides-Extra: parquet
49
54
  Provides-Extra: plot
50
55
  Provides-Extra: pointcloud
51
56
  Provides-Extra: video
57
+ Provides-Extra: xxhash
52
58
  Description-Content-Type: text/markdown
53
59
 
54
60
  # pymcap-cli
@@ -80,6 +86,7 @@ uv add pymcap-cli[video]
80
86
  - **Smart Chunk Copying** — fast chunk copying without decompression when possible, up to 10x faster for filtering operations
81
87
  - **Unified Processing** — single `process` command combines recovery + filtering + compression in one optimized pass
82
88
  - **Precise Filtering** — regex topic filtering, time range filtering, and content type filtering with deferred schema/channel writing
89
+ - **Broad Format Coverage** — converts ROS 1 `.bag` and ROS 2 `.db3` to MCAP, exports to NDJSON, CSV, Parquet, PCD, GeoJSON/KML/GPX, and image/video files
83
90
  - **Rich Terminal Output** — colored topics, Unicode distribution histograms, tree views, and responsive layouts
84
91
  - **Robust Error Handling** — graceful degradation with detailed error reporting and recovery statistics
85
92
 
@@ -169,6 +176,27 @@ pymcap-cli tftree data.mcap
169
176
  pymcap-cli tftree data.mcap --static-only
170
177
  ```
171
178
 
179
+ ### `tf-export` — TF Tree to URDF / SDF / JSON
180
+
181
+ Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
182
+ snapshot timestamp). Useful when the original `.urdf` is missing — Foxglove
183
+ Studio and rviz can render the static skeleton from the exported file.
184
+
185
+ ```bash
186
+ # Write a URDF for the static tree
187
+ pymcap-cli tf-export data.mcap -o robot.urdf
188
+
189
+ # SDF or JSON instead
190
+ pymcap-cli tf-export data.mcap --format sdf -o robot.sdf
191
+ pymcap-cli tf-export data.mcap --format json
192
+
193
+ # Capture a dynamic snapshot from /tf at a given time
194
+ pymcap-cli tf-export data.mcap --include-dynamic-at 2024-01-01T10:00:00Z -o snapshot.urdf
195
+
196
+ # Pick a subtree when the recording has multiple disconnected roots
197
+ pymcap-cli tf-export data.mcap --root base_link -o robot.urdf
198
+ ```
199
+
172
200
  ### `diag` — ROS2 Diagnostics
173
201
 
174
202
  Inspect ROS2 diagnostics with per-component health overview, sparkline timelines, frequency stats, and time-in-state tracking.
@@ -269,6 +297,9 @@ pymcap-cli merge *.mcap -o all_recordings.mcap --compression lz4
269
297
 
270
298
  # Exclude metadata/attachments
271
299
  pymcap-cli merge file1.mcap file2.mcap -o merged.mcap --metadata exclude
300
+
301
+ # Drop duplicate messages (same channel, log_time, payload) from overlapping inputs
302
+ pymcap-cli merge a.mcap b.mcap -o merged.mcap --dedup-identical
272
303
  ```
273
304
 
274
305
  ### `convert` — Convert DB3 to MCAP
@@ -286,6 +317,43 @@ pymcap-cli convert input.db3 -o output.mcap --distro jazzy
286
317
  pymcap-cli convert input.db3 -o output.mcap --extra-path /path/to/msgs
287
318
  ```
288
319
 
320
+ ### `bag2mcap` — Convert ROS 1 Bag to MCAP
321
+
322
+ Convert ROS 1 `.bag` files to MCAP using the `ros1` profile. Message bytes are
323
+ preserved as raw ROS 1 serialization and schemas use `ros1msg` encoding with
324
+ the full message definition from the bag.
325
+
326
+ ```bash
327
+ # Basic conversion
328
+ pymcap-cli bag2mcap recording.bag -o recording.mcap
329
+
330
+ # Pick a different compression / chunk size
331
+ pymcap-cli bag2mcap recording.bag -o recording.mcap --compression lz4 --chunk-size 8388608
332
+ ```
333
+
334
+ ### `split` — Split into Segments
335
+
336
+ Split an MCAP file into multiple output segments by duration, explicit
337
+ timestamps, value-change of a message-path expression, or a byte budget per
338
+ segment.
339
+
340
+ ```bash
341
+ # Split every 60 seconds
342
+ pymcap-cli split data.mcap --duration 60s -t "out_{index:03d}.mcap"
343
+
344
+ # Split at specific RFC3339 timestamps
345
+ pymcap-cli split data.mcap --split-at "2024-01-01T10:00:00Z" --split-at "2024-01-01T10:30:00Z"
346
+
347
+ # Start a new segment when /gps/fix.status.status changes value
348
+ pymcap-cli split data.mcap -E "/gps/fix.status.status"
349
+
350
+ # Predicate trigger — split on match/no-match transitions
351
+ pymcap-cli split data.mcap -E "/detections.objects[:]{confidence>0.8}"
352
+
353
+ # Split when each output reaches roughly 1 GB
354
+ pymcap-cli split data.mcap --max-size 1G -t "shard_{index:03d}.mcap"
355
+ ```
356
+
289
357
  ### `rechunk` — Topic-Based Rechunking
290
358
 
291
359
  Reorganize MCAP messages into separate chunk groups based on topic patterns for optimized playback.
@@ -343,6 +411,73 @@ pymcap-cli list attachments data.mcap
343
411
  pymcap-cli list metadata data.mcap
344
412
  ```
345
413
 
414
+ ### `get` — Extract Attachments and Metadata
415
+
416
+ Extract a single attachment's bytes or a metadata record's key/value map.
417
+
418
+ ```bash
419
+ # Write attachment bytes to a file (or pipe stdout)
420
+ pymcap-cli get attachment --name calib.bin --output calib.bin data.mcap
421
+ pymcap-cli get attachment -n calib.bin data.mcap > calib.bin
422
+
423
+ # Disambiguate when multiple attachments share a name
424
+ pymcap-cli get attachment --name notes.txt --offset 1234 -o notes.txt data.mcap
425
+
426
+ # Print a metadata record as JSON (records sharing a name are merged)
427
+ pymcap-cli get metadata --name session data.mcap
428
+ ```
429
+
430
+ ### `diff` — Compare Files
431
+
432
+ Compare MCAP files using summary and message-index timestamps. Reads through the
433
+ footer/summary first and falls back to rebuilding metadata from the data section
434
+ when the summary is missing.
435
+
436
+ ```bash
437
+ # Compare two recordings
438
+ pymcap-cli diff a.mcap b.mcap
439
+
440
+ # Hide channels with identical timestamps
441
+ pymcap-cli diff a.mcap b.mcap --skip-identical
442
+
443
+ # Show more timestamp ranges per channel
444
+ pymcap-cli diff a.mcap b.mcap --max-ranges 10
445
+ ```
446
+
447
+ ### `duplicates` — Find Duplicate Recordings
448
+
449
+ Scan files and directories for likely duplicate MCAP recordings using summary
450
+ and message-index fingerprints.
451
+
452
+ ```bash
453
+ # Scan a directory tree
454
+ pymcap-cli duplicates /data/recordings
455
+
456
+ # Include singleton groups
457
+ pymcap-cli duplicates /data/recordings --all
458
+
459
+ # Rebuild summaries for files missing them
460
+ pymcap-cli duplicates /data/recordings --rebuild-missing
461
+ ```
462
+
463
+ ### `records` — Raw Record Dump
464
+
465
+ Print every MCAP record in file order using its `repr`. Useful for inspecting
466
+ raw file structure when debugging readers/writers.
467
+
468
+ ```bash
469
+ pymcap-cli records data.mcap
470
+ ```
471
+
472
+ ### `topic-chunks` — Topic/Chunk Layout
473
+
474
+ Show which topics appear in which chunks, sorted by chunk count and percentage
475
+ of total chunks. Helps identify topics that would benefit from `rechunk`.
476
+
477
+ ```bash
478
+ pymcap-cli topic-chunks data.mcap
479
+ ```
480
+
346
481
  ### `video` — Video Generation
347
482
 
348
483
  Generate MP4 videos from image topics using hardware-accelerated encoding. Requires the `video` extra.
@@ -388,6 +523,88 @@ pymcap-cli rosdecompress input.mcap output.mcap --video-format raw
388
523
  pymcap-cli rosdecompress input.mcap output.mcap --no-pointcloud
389
524
  ```
390
525
 
526
+ ### `export-images` — Image Files
527
+
528
+ Export image topics to per-topic folders of image files. `CompressedImage`
529
+ payloads keep their original encoding by default (`--format native`); set
530
+ `--format` to a Pillow format (e.g. `jpeg`, `png`, `webp`) to re-encode. Raw
531
+ `Image` messages always use `--raw-format` (default `png`). Requires the
532
+ `image` extra.
533
+
534
+ ```bash
535
+ # Native passthrough for CompressedImage; PNG for raw Image
536
+ pymcap-cli export-images data.mcap -o ./images -t /camera/front
537
+
538
+ # Force re-encoding to JPEG for everything
539
+ pymcap-cli export-images data.mcap -o ./images --format jpeg
540
+ ```
541
+
542
+ ### `export-csv` — CSV Files
543
+
544
+ Export an MCAP file to a directory of CSV files (one per topic). Nested fields
545
+ are flattened with dot notation (`pose.position.x`); arrays remain JSON
546
+ strings to preserve row counts. Schemas with raw media payloads (`Image`,
547
+ `CompressedImage`, …) are skipped unless `--include-blobs` is set.
548
+
549
+ ```bash
550
+ pymcap-cli export-csv data.mcap -o ./csv
551
+ pymcap-cli export-csv data.mcap -o ./csv -t /odom -t /imu
552
+ ```
553
+
554
+ ### `export-json` — NDJSON / Per-Message JSON
555
+
556
+ Export an MCAP file to NDJSON (one line per message) or per-message JSON
557
+ files. Default writes one `<topic>.ndjson` per topic; with `--per-message`
558
+ each topic gets a directory of `<log_time_ns>.json` files — handy for
559
+ downstream tools that expect one record per file.
560
+
561
+ ```bash
562
+ # One NDJSON per topic
563
+ pymcap-cli export-json data.mcap -o ./ndjson
564
+
565
+ # One JSON file per message
566
+ pymcap-cli export-json data.mcap -o ./json --per-message
567
+ ```
568
+
569
+ ### `export-parquet` — Parquet Files
570
+
571
+ Export an MCAP file to a directory of Parquet files (one per topic). Requires
572
+ the `parquet` extra.
573
+
574
+ ```bash
575
+ pymcap-cli export-parquet data.mcap -o ./parquet
576
+ pymcap-cli export-parquet data.mcap -o ./parquet --compression snappy
577
+ ```
578
+
579
+ ### `export-pcd` — Point Cloud Files
580
+
581
+ Export `sensor_msgs/PointCloud2` topics to ASCII PCD v0.7 files
582
+ (`<output>/<safe_topic>/<log_time_ns>.pcd`) — readable by `pcl_viewer`,
583
+ Open3D, and CloudCompare. Requires the `pointcloud` extra.
584
+
585
+ ```bash
586
+ pymcap-cli export-pcd data.mcap -o ./pcd
587
+ pymcap-cli export-pcd data.mcap -o ./pcd -t /lidar/points
588
+ ```
589
+
590
+ ### `export-geo` — Map Formats
591
+
592
+ Export geographic topics (`NavSatFix`, `geographic_msgs/*`) to GeoJSON, KML,
593
+ or GPX. GeoJSON writes one `<topic>.geojson` per topic; KML and GPX produce
594
+ a single `export.{kml,gpx}` covering all topics. Local-frame poses
595
+ (`Odometry`, `geometry_msgs/Pose*`) are out of scope — they need a datum.
596
+
597
+ ```bash
598
+ # Default GeoJSON, track + points per topic
599
+ pymcap-cli export-geo data.mcap -o ./geo
600
+
601
+ # GPX track every 5th sample
602
+ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
603
+
604
+ # Keep NO_FIX samples too
605
+ pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
606
+ ```
607
+
391
608
  ### Shell Autocompletion
392
609
 
393
610
  ```bash
@@ -27,6 +27,7 @@ uv add pymcap-cli[video]
27
27
  - **Smart Chunk Copying** — fast chunk copying without decompression when possible, up to 10x faster for filtering operations
28
28
  - **Unified Processing** — single `process` command combines recovery + filtering + compression in one optimized pass
29
29
  - **Precise Filtering** — regex topic filtering, time range filtering, and content type filtering with deferred schema/channel writing
30
+ - **Broad Format Coverage** — converts ROS 1 `.bag` and ROS 2 `.db3` to MCAP, exports to NDJSON, CSV, Parquet, PCD, GeoJSON/KML/GPX, and image/video files
30
31
  - **Rich Terminal Output** — colored topics, Unicode distribution histograms, tree views, and responsive layouts
31
32
  - **Robust Error Handling** — graceful degradation with detailed error reporting and recovery statistics
32
33
 
@@ -116,6 +117,27 @@ pymcap-cli tftree data.mcap
116
117
  pymcap-cli tftree data.mcap --static-only
117
118
  ```
118
119
 
120
+ ### `tf-export` — TF Tree to URDF / SDF / JSON
121
+
122
+ Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
123
+ snapshot timestamp). Useful when the original `.urdf` is missing — Foxglove
124
+ Studio and rviz can render the static skeleton from the exported file.
125
+
126
+ ```bash
127
+ # Write a URDF for the static tree
128
+ pymcap-cli tf-export data.mcap -o robot.urdf
129
+
130
+ # SDF or JSON instead
131
+ pymcap-cli tf-export data.mcap --format sdf -o robot.sdf
132
+ pymcap-cli tf-export data.mcap --format json
133
+
134
+ # Capture a dynamic snapshot from /tf at a given time
135
+ pymcap-cli tf-export data.mcap --include-dynamic-at 2024-01-01T10:00:00Z -o snapshot.urdf
136
+
137
+ # Pick a subtree when the recording has multiple disconnected roots
138
+ pymcap-cli tf-export data.mcap --root base_link -o robot.urdf
139
+ ```
140
+
119
141
  ### `diag` — ROS2 Diagnostics
120
142
 
121
143
  Inspect ROS2 diagnostics with per-component health overview, sparkline timelines, frequency stats, and time-in-state tracking.
@@ -216,6 +238,9 @@ pymcap-cli merge *.mcap -o all_recordings.mcap --compression lz4
216
238
 
217
239
  # Exclude metadata/attachments
218
240
  pymcap-cli merge file1.mcap file2.mcap -o merged.mcap --metadata exclude
241
+
242
+ # Drop duplicate messages (same channel, log_time, payload) from overlapping inputs
243
+ pymcap-cli merge a.mcap b.mcap -o merged.mcap --dedup-identical
219
244
  ```
220
245
 
221
246
  ### `convert` — Convert DB3 to MCAP
@@ -233,6 +258,43 @@ pymcap-cli convert input.db3 -o output.mcap --distro jazzy
233
258
  pymcap-cli convert input.db3 -o output.mcap --extra-path /path/to/msgs
234
259
  ```
235
260
 
261
+ ### `bag2mcap` — Convert ROS 1 Bag to MCAP
262
+
263
+ Convert ROS 1 `.bag` files to MCAP using the `ros1` profile. Message bytes are
264
+ preserved as raw ROS 1 serialization and schemas use `ros1msg` encoding with
265
+ the full message definition from the bag.
266
+
267
+ ```bash
268
+ # Basic conversion
269
+ pymcap-cli bag2mcap recording.bag -o recording.mcap
270
+
271
+ # Pick a different compression / chunk size
272
+ pymcap-cli bag2mcap recording.bag -o recording.mcap --compression lz4 --chunk-size 8388608
273
+ ```
274
+
275
+ ### `split` — Split into Segments
276
+
277
+ Split an MCAP file into multiple output segments by duration, explicit
278
+ timestamps, value-change of a message-path expression, or a byte budget per
279
+ segment.
280
+
281
+ ```bash
282
+ # Split every 60 seconds
283
+ pymcap-cli split data.mcap --duration 60s -t "out_{index:03d}.mcap"
284
+
285
+ # Split at specific RFC3339 timestamps
286
+ pymcap-cli split data.mcap --split-at "2024-01-01T10:00:00Z" --split-at "2024-01-01T10:30:00Z"
287
+
288
+ # Start a new segment when /gps/fix.status.status changes value
289
+ pymcap-cli split data.mcap -E "/gps/fix.status.status"
290
+
291
+ # Predicate trigger — split on match/no-match transitions
292
+ pymcap-cli split data.mcap -E "/detections.objects[:]{confidence>0.8}"
293
+
294
+ # Split when each output reaches roughly 1 GB
295
+ pymcap-cli split data.mcap --max-size 1G -t "shard_{index:03d}.mcap"
296
+ ```
297
+
236
298
  ### `rechunk` — Topic-Based Rechunking
237
299
 
238
300
  Reorganize MCAP messages into separate chunk groups based on topic patterns for optimized playback.
@@ -290,6 +352,73 @@ pymcap-cli list attachments data.mcap
290
352
  pymcap-cli list metadata data.mcap
291
353
  ```
292
354
 
355
+ ### `get` — Extract Attachments and Metadata
356
+
357
+ Extract a single attachment's bytes or a metadata record's key/value map.
358
+
359
+ ```bash
360
+ # Write attachment bytes to a file (or pipe stdout)
361
+ pymcap-cli get attachment --name calib.bin --output calib.bin data.mcap
362
+ pymcap-cli get attachment -n calib.bin data.mcap > calib.bin
363
+
364
+ # Disambiguate when multiple attachments share a name
365
+ pymcap-cli get attachment --name notes.txt --offset 1234 -o notes.txt data.mcap
366
+
367
+ # Print a metadata record as JSON (records sharing a name are merged)
368
+ pymcap-cli get metadata --name session data.mcap
369
+ ```
370
+
371
+ ### `diff` — Compare Files
372
+
373
+ Compare MCAP files using summary and message-index timestamps. Reads through the
374
+ footer/summary first and falls back to rebuilding metadata from the data section
375
+ when the summary is missing.
376
+
377
+ ```bash
378
+ # Compare two recordings
379
+ pymcap-cli diff a.mcap b.mcap
380
+
381
+ # Hide channels with identical timestamps
382
+ pymcap-cli diff a.mcap b.mcap --skip-identical
383
+
384
+ # Show more timestamp ranges per channel
385
+ pymcap-cli diff a.mcap b.mcap --max-ranges 10
386
+ ```
387
+
388
+ ### `duplicates` — Find Duplicate Recordings
389
+
390
+ Scan files and directories for likely duplicate MCAP recordings using summary
391
+ and message-index fingerprints.
392
+
393
+ ```bash
394
+ # Scan a directory tree
395
+ pymcap-cli duplicates /data/recordings
396
+
397
+ # Include singleton groups
398
+ pymcap-cli duplicates /data/recordings --all
399
+
400
+ # Rebuild summaries for files missing them
401
+ pymcap-cli duplicates /data/recordings --rebuild-missing
402
+ ```
403
+
404
+ ### `records` — Raw Record Dump
405
+
406
+ Print every MCAP record in file order using its `repr`. Useful for inspecting
407
+ raw file structure when debugging readers/writers.
408
+
409
+ ```bash
410
+ pymcap-cli records data.mcap
411
+ ```
412
+
413
+ ### `topic-chunks` — Topic/Chunk Layout
414
+
415
+ Show which topics appear in which chunks, sorted by chunk count and percentage
416
+ of total chunks. Helps identify topics that would benefit from `rechunk`.
417
+
418
+ ```bash
419
+ pymcap-cli topic-chunks data.mcap
420
+ ```
421
+
293
422
  ### `video` — Video Generation
294
423
 
295
424
  Generate MP4 videos from image topics using hardware-accelerated encoding. Requires the `video` extra.
@@ -335,6 +464,88 @@ pymcap-cli rosdecompress input.mcap output.mcap --video-format raw
335
464
  pymcap-cli rosdecompress input.mcap output.mcap --no-pointcloud
336
465
  ```
337
466
 
467
+ ### `export-images` — Image Files
468
+
469
+ Export image topics to per-topic folders of image files. `CompressedImage`
470
+ payloads keep their original encoding by default (`--format native`); set
471
+ `--format` to a Pillow format (e.g. `jpeg`, `png`, `webp`) to re-encode. Raw
472
+ `Image` messages always use `--raw-format` (default `png`). Requires the
473
+ `image` extra.
474
+
475
+ ```bash
476
+ # Native passthrough for CompressedImage; PNG for raw Image
477
+ pymcap-cli export-images data.mcap -o ./images -t /camera/front
478
+
479
+ # Force re-encoding to JPEG for everything
480
+ pymcap-cli export-images data.mcap -o ./images --format jpeg
481
+ ```
482
+
483
+ ### `export-csv` — CSV Files
484
+
485
+ Export an MCAP file to a directory of CSV files (one per topic). Nested fields
486
+ are flattened with dot notation (`pose.position.x`); arrays remain JSON
487
+ strings to preserve row counts. Schemas with raw media payloads (`Image`,
488
+ `CompressedImage`, …) are skipped unless `--include-blobs` is set.
489
+
490
+ ```bash
491
+ pymcap-cli export-csv data.mcap -o ./csv
492
+ pymcap-cli export-csv data.mcap -o ./csv -t /odom -t /imu
493
+ ```
494
+
495
+ ### `export-json` — NDJSON / Per-Message JSON
496
+
497
+ Export an MCAP file to NDJSON (one line per message) or per-message JSON
498
+ files. Default writes one `<topic>.ndjson` per topic; with `--per-message`
499
+ each topic gets a directory of `<log_time_ns>.json` files — handy for
500
+ downstream tools that expect one record per file.
501
+
502
+ ```bash
503
+ # One NDJSON per topic
504
+ pymcap-cli export-json data.mcap -o ./ndjson
505
+
506
+ # One JSON file per message
507
+ pymcap-cli export-json data.mcap -o ./json --per-message
508
+ ```
509
+
510
+ ### `export-parquet` — Parquet Files
511
+
512
+ Export an MCAP file to a directory of Parquet files (one per topic). Requires
513
+ the `parquet` extra.
514
+
515
+ ```bash
516
+ pymcap-cli export-parquet data.mcap -o ./parquet
517
+ pymcap-cli export-parquet data.mcap -o ./parquet --compression snappy
518
+ ```
519
+
520
+ ### `export-pcd` — Point Cloud Files
521
+
522
+ Export `sensor_msgs/PointCloud2` topics to ASCII PCD v0.7 files
523
+ (`<output>/<safe_topic>/<log_time_ns>.pcd`) — readable by `pcl_viewer`,
524
+ Open3D, and CloudCompare. Requires the `pointcloud` extra.
525
+
526
+ ```bash
527
+ pymcap-cli export-pcd data.mcap -o ./pcd
528
+ pymcap-cli export-pcd data.mcap -o ./pcd -t /lidar/points
529
+ ```
530
+
531
+ ### `export-geo` — Map Formats
532
+
533
+ Export geographic topics (`NavSatFix`, `geographic_msgs/*`) to GeoJSON, KML,
534
+ or GPX. GeoJSON writes one `<topic>.geojson` per topic; KML and GPX produce
535
+ a single `export.{kml,gpx}` covering all topics. Local-frame poses
536
+ (`Odometry`, `geometry_msgs/Pose*`) are out of scope — they need a datum.
537
+
538
+ ```bash
539
+ # Default GeoJSON, track + points per topic
540
+ pymcap-cli export-geo data.mcap -o ./geo
541
+
542
+ # GPX track every 5th sample
543
+ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
544
+
545
+ # Keep NO_FIX samples too
546
+ pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
547
+ ```
548
+
338
549
  ### Shell Autocompletion
339
550
 
340
551
  ```bash
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pymcap-cli"
3
- version = "0.8.0"
3
+ version = "0.10.0"
4
4
  description = "High-performance Python CLI for MCAP file processing with advanced recovery, filtering, and optimization capabilities"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -28,6 +28,7 @@ classifiers = [
28
28
  "Programming Language :: Python :: 3.11",
29
29
  "Programming Language :: Python :: 3.12",
30
30
  "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
31
32
  "Topic :: Scientific/Engineering",
32
33
  "Topic :: System :: Archiving",
33
34
  "Topic :: Utilities",
@@ -36,7 +37,6 @@ classifiers = [
36
37
  dependencies = [
37
38
  "rich>=14.1.0",
38
39
  "small-mcap[compression]",
39
- "mcap-codec-support[ros2]",
40
40
  "mcap-ros2-support-fast",
41
41
  "cyclopts>=4",
42
42
  "ros-parser",
@@ -45,22 +45,23 @@ dependencies = [
45
45
  "typing-extensions>=4.15.0",
46
46
  ]
47
47
 
48
+ [project.optional-dependencies]
49
+ all = ["pymcap-cli[video,pointcloud,plot,parquet,image,draco,bridge,xxhash]"]
50
+ lite = ["pymcap-cli[image,draco,bridge,xxhash]"]
51
+ bridge = ["robo-ws-bridge"]
52
+ draco = ["mcap-codec-support[draco]"]
53
+ image = ["pillow>=10.0"]
54
+ parquet = ["pyarrow>=15.0.0", "numpy>=1.24.0", "mcap-codec-support[pointcloud]"]
55
+ plot = ["plotly>=6.0.0"]
56
+ pointcloud = ["mcap-codec-support[pointcloud]"]
57
+ video = ["mcap-codec-support[video]"]
58
+ xxhash = ["xxhash>=3.0.0"]
59
+
60
+
48
61
  [project.urls]
49
62
  Homepage = "https://github.com/mrkbac/robotic-tools"
50
63
  Repository = "https://github.com/mrkbac/robotic-tools"
51
64
  Issues = "https://github.com/mrkbac/robotic-tools/issues"
52
-
53
- [project.optional-dependencies]
54
- video = ["mcap-codec-support[video]"]
55
- pointcloud = ["mcap-codec-support[pointcloud]"]
56
- plot = ["plotly>=6.0.0"]
57
- parquet = ["pyarrow>=15.0.0", "numpy>=1.24.0", "mcap-codec-support[pointcloud]"]
58
- image = ["mcap-codec-support[image]"]
59
- all = ["pymcap-cli[video,pointcloud,plot,parquet,image,draco]"]
60
- draco = [
61
- "mcap-codec-support[draco]",
62
- ]
63
-
64
65
  [build-system]
65
66
  requires = ["uv_build>=0.8.9,<0.9.0"]
66
67
  build-backend = "uv_build"
@@ -76,3 +77,4 @@ ros-parser = { workspace = true }
76
77
  pureini = { workspace = true }
77
78
  pointcloud2 = { workspace = true }
78
79
  mcap-codec-support = { workspace = true }
80
+ robo-ws-bridge = { workspace = true }