pymcap-cli 0.10.0__tar.gz → 0.13.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.
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/PKG-INFO +102 -16
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/README.md +101 -15
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/pyproject.toml +1 -1
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cli.py +37 -26
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/_rechunk_strategy.py +42 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/diff_cmd.py +95 -23
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/duplicates_cmd.py +202 -92
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/__init__.py +41 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/_helpers.py +456 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/duplicates_cmd.py +152 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/errors_cmd.py +163 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/info_cmd.py +350 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/query_cmd.py +287 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/scan_cmd.py +160 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/schemas_cmd.py +190 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/sessions_cmd.py +169 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/status_cmd.py +267 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/timeline_cmd.py +127 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/topics_cmd.py +137 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/index/tree_cmd.py +423 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/info_cmd.py +39 -2
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/msg/__init__.py +14 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/msg/def_cmd.py +66 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/msg/list_cmd.py +65 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/msg/serve_cmd.py +507 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/process_cmd.py +733 -0
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/rechunk_cmd.py +248 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/split_cmd.py +5 -12
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/tf_export_cmd.py +4 -2
- pymcap_cli-0.13.0/src/pymcap_cli/cmd/tf_get_cmd.py +182 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/tftree_cmd.py +46 -27
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/mcap_compare.py +455 -3
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/mcap_processor.py +116 -123
- pymcap_cli-0.13.0/src/pymcap_cli/core/msg_resolver.py +719 -0
- pymcap_cli-0.13.0/src/pymcap_cli/core/processors/ARCHITECTURE.md +180 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/base.py +27 -1
- pymcap_cli-0.13.0/src/pymcap_cli/core/processors/chunk_groupers.py +61 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/dedup.py +4 -2
- pymcap_cli-0.13.0/src/pymcap_cli/core/qos.py +147 -0
- pymcap_cli-0.13.0/src/pymcap_cli/core/tf_findings.py +254 -0
- pymcap_cli-0.13.0/src/pymcap_cli/core/tf_tree.py +551 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/display_utils.py +66 -4
- pymcap_cli-0.13.0/src/pymcap_cli/display/schema_html.py +206 -0
- pymcap_cli-0.13.0/src/pymcap_cli/display/schema_render.py +187 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/doctor.py +76 -1
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/video_file_writer.py +19 -12
- pymcap_cli-0.13.0/src/pymcap_cli/index/__init__.py +13 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/db.py +130 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/fingerprint.py +50 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0001.py +155 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0002_normalise_and_drop_chunks.py +247 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0003_intern_channels.py +108 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0004_intern_compress_channel_metadata.py +125 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0005_content_compression.py +43 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0006_channel_statistics.py +35 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0007_consolidate_schema.py +357 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0008_rewrite_current_file_view.py +59 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/0009_read_side_covering_indexes.py +25 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/migrations/__init__.py +96 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/scanner.py +1368 -0
- pymcap_cli-0.13.0/src/pymcap_cli/index/summary_fingerprint.py +149 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/rihs01.py +8 -1
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/duration.py +14 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/info_data.py +64 -38
- pymcap_cli-0.13.0/src/pymcap_cli/types/qos.py +83 -0
- pymcap_cli-0.10.0/src/pymcap_cli/cmd/process_cmd.py +0 -237
- pymcap_cli-0.10.0/src/pymcap_cli/cmd/rechunk_cmd.py +0 -185
- pymcap_cli-0.10.0/src/pymcap_cli/core/msg_resolver.py +0 -447
- pymcap_cli-0.10.0/src/pymcap_cli/core/tf_tree.py +0 -154
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/_run_processor.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/_run_processor_multi.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bag2mcap_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bridge/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bridge/_shared.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bridge/cat.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bridge/inspect.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/bridge/record.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/cat_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/compress_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/convert_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/diag_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/doctor_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/du_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_csv_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_geo_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_images_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_json_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_parquet_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/export_pcd_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/filter_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/get_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/info_json_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/list_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/merge_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/plot_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/records_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/recover_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/recover_inplace_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/roscompress_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/rosdecompress_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/topic_chunks_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/cmd/video_cmd.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/constants.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/input_handler.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/input_options.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/input_processor_chain.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/mcap_transform.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/always_decode.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/attachment_filter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/boundary_split.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/channel_merge.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/duration_split.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/expression_split.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/latching.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/metadata_filter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/nth_message.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/size_split.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/time_filter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/time_offset.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/timestamp_split.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/topic_alias.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/topic_filter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/topic_rewrite.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/core/processors/utils.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/debug_wrapper.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/cat_helpers.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/message_render.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/osc_utils.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/sparkline.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/display/time_ranges.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/encoding/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/encoding/arrow_schema.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/_common.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/base.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/csv_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/driver.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/geo_common.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/geojson_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/gpx_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/image_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/json_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/kml_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/parquet_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/pcd_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/plot_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/sdf_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/urdf_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/exporters/video_exporter.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/http_utils.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/log_setup.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/py.typed +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/rosbag_reader/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/rosbag_reader/_reader.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/rosbag_reader/_types.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/rosbag_reader/py.typed +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/__init__.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/info_link.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/info_types.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/size.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/to_plain.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/types/types_manual.py +0 -0
- {pymcap_cli-0.10.0 → pymcap_cli-0.13.0}/src/pymcap_cli/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pymcap-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.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
|
|
@@ -76,8 +76,14 @@ uvx pymcap-cli info data.mcap
|
|
|
76
76
|
# Or add to your project
|
|
77
77
|
uv add pymcap-cli
|
|
78
78
|
|
|
79
|
-
# With video support (for video and
|
|
80
|
-
uv add pymcap-cli[video]
|
|
79
|
+
# With video support (for video generation and ROS image compression)
|
|
80
|
+
uv add "pymcap-cli[video]"
|
|
81
|
+
|
|
82
|
+
# With ROS image and point-cloud compression support
|
|
83
|
+
uv add "pymcap-cli[video,pointcloud]"
|
|
84
|
+
|
|
85
|
+
# Add Draco point-cloud compression support
|
|
86
|
+
uv add "pymcap-cli[video,pointcloud,draco]"
|
|
81
87
|
```
|
|
82
88
|
|
|
83
89
|
## Why pymcap-cli over the official Go CLI?
|
|
@@ -158,6 +164,16 @@ pymcap-cli cat recording.mcap --bytes base64 # base64-encoded
|
|
|
158
164
|
pymcap-cli cat recording.mcap --bytes skip # omit binary fields
|
|
159
165
|
```
|
|
160
166
|
|
|
167
|
+
### `doctor` — MCAP Container Validation
|
|
168
|
+
|
|
169
|
+
Check an MCAP file structure against the MCAP container specification, with
|
|
170
|
+
summary, index, chunk, message-order, and advisory findings.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pymcap-cli doctor data.mcap
|
|
174
|
+
pymcap-cli doctor data.mcap --strict-message-order --show-all
|
|
175
|
+
```
|
|
176
|
+
|
|
161
177
|
### `tftree` — TF Transform Tree
|
|
162
178
|
|
|
163
179
|
Visualize the ROS TF transform tree with colored static/dynamic transforms, translation and rotation values.
|
|
@@ -176,6 +192,16 @@ pymcap-cli tftree data.mcap
|
|
|
176
192
|
pymcap-cli tftree data.mcap --static-only
|
|
177
193
|
```
|
|
178
194
|
|
|
195
|
+
### `tf-get` — TF Transform Lookup
|
|
196
|
+
|
|
197
|
+
Resolve the transform from a source frame into a target frame using `/tf_static`
|
|
198
|
+
and `/tf`. Without `--at`, dynamic edges use their latest sample.
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pymcap-cli tf-get data.mcap map base_link
|
|
202
|
+
pymcap-cli tf-get data.mcap odom base_link --at 2024-01-01T10:00:00Z
|
|
203
|
+
```
|
|
204
|
+
|
|
179
205
|
### `tf-export` — TF Tree to URDF / SDF / JSON
|
|
180
206
|
|
|
181
207
|
Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
|
|
@@ -376,7 +402,7 @@ Filter messages by topic patterns (simpler version of `process`).
|
|
|
376
402
|
|
|
377
403
|
```bash
|
|
378
404
|
# Include specific topics
|
|
379
|
-
pymcap-cli filter data.mcap -o filtered.mcap --
|
|
405
|
+
pymcap-cli filter data.mcap -o filtered.mcap --topics "/camera/image" "/lidar/points"
|
|
380
406
|
|
|
381
407
|
# Exclude topics
|
|
382
408
|
pymcap-cli filter data.mcap -o filtered.mcap --exclude-topics "/debug.*" "/test.*"
|
|
@@ -407,10 +433,32 @@ List various record types in an MCAP file.
|
|
|
407
433
|
pymcap-cli list channels data.mcap
|
|
408
434
|
pymcap-cli list chunks data.mcap
|
|
409
435
|
pymcap-cli list schemas data.mcap
|
|
436
|
+
pymcap-cli list schema data.mcap --name sensor_msgs/msg/Image
|
|
410
437
|
pymcap-cli list attachments data.mcap
|
|
411
438
|
pymcap-cli list metadata data.mcap
|
|
412
439
|
```
|
|
413
440
|
|
|
441
|
+
### `msg` — ROS2 Message Definitions
|
|
442
|
+
|
|
443
|
+
Resolve, list, and browse ROS2 `.msg` definitions. `msg def` prints complete
|
|
444
|
+
definitions including dependencies; `msg list` lists package message types; and
|
|
445
|
+
`msg serve` starts a local browser UI.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Resolve a standard ROS2 message
|
|
449
|
+
pymcap-cli msg def sensor_msgs/msg/Image --distro humble
|
|
450
|
+
|
|
451
|
+
# Include custom package roots before AMENT_PREFIX_PATH and the user cache
|
|
452
|
+
pymcap-cli msg def my_robot_msgs/msg/Status -I ./install/share
|
|
453
|
+
|
|
454
|
+
# List messages in a package or browse definitions locally
|
|
455
|
+
pymcap-cli msg list sensor_msgs --distro jazzy
|
|
456
|
+
pymcap-cli msg serve --distro jazzy --no-browser
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Missing standard packages are resolved from rosdistro/GitHub and cached under
|
|
460
|
+
the `pymcap_cli_msg_def` user cache.
|
|
461
|
+
|
|
414
462
|
### `get` — Extract Attachments and Metadata
|
|
415
463
|
|
|
416
464
|
Extract a single attachment's bytes or a metadata record's key/value map.
|
|
@@ -460,6 +508,24 @@ pymcap-cli duplicates /data/recordings --all
|
|
|
460
508
|
pymcap-cli duplicates /data/recordings --rebuild-missing
|
|
461
509
|
```
|
|
462
510
|
|
|
511
|
+
### `index` — Sidecar Catalog
|
|
512
|
+
|
|
513
|
+
Maintain a sidecar SQLite catalog of MCAP summaries for fast lookup across
|
|
514
|
+
large recording trees. Requires the `xxhash` extra.
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
# Scan a tree and skip unchanged files on later runs
|
|
518
|
+
pymcap-cli index scan /data/recordings
|
|
519
|
+
|
|
520
|
+
# Coverage and directory-level rollups
|
|
521
|
+
pymcap-cli index status /data/recordings
|
|
522
|
+
pymcap-cli index tree /data/recordings --max-depth 3
|
|
523
|
+
|
|
524
|
+
# Query by topic/schema/time and inspect catalog-wide topics
|
|
525
|
+
pymcap-cli index query /data/recordings --topic /camera/front --format json
|
|
526
|
+
pymcap-cli index topics /camera --sort-by messages
|
|
527
|
+
```
|
|
528
|
+
|
|
463
529
|
### `records` — Raw Record Dump
|
|
464
530
|
|
|
465
531
|
Print every MCAP record in file order using its `repr`. Useful for inspecting
|
|
@@ -480,22 +546,26 @@ pymcap-cli topic-chunks data.mcap
|
|
|
480
546
|
|
|
481
547
|
### `video` — Video Generation
|
|
482
548
|
|
|
483
|
-
Generate MP4
|
|
549
|
+
Generate one MP4 per image topic using hardware-accelerated encoding. Requires
|
|
550
|
+
the `video` extra.
|
|
484
551
|
|
|
485
552
|
```bash
|
|
486
553
|
# Basic video generation
|
|
487
|
-
pymcap-cli video data.mcap --topic /camera/front --output
|
|
554
|
+
pymcap-cli video data.mcap --topic /camera/front --output ./videos
|
|
488
555
|
|
|
489
556
|
# With quality preset
|
|
490
|
-
pymcap-cli video data.mcap --topic /camera/rear --output
|
|
557
|
+
pymcap-cli video data.mcap --topic /camera/rear --output ./videos --quality high
|
|
491
558
|
|
|
492
559
|
# Use specific codec and encoder
|
|
493
|
-
pymcap-cli video data.mcap --topic /lidar/image --output
|
|
560
|
+
pymcap-cli video data.mcap --topic /lidar/image --output ./videos --codec h265 --encoder videotoolbox
|
|
494
561
|
```
|
|
495
562
|
|
|
496
|
-
### `roscompress` — ROS Image Compression
|
|
563
|
+
### `roscompress` — ROS Image and Point-Cloud Compression
|
|
497
564
|
|
|
498
|
-
Compress ROS MCAP files by converting CompressedImage/Image topics to
|
|
565
|
+
Compress ROS MCAP files by converting CompressedImage/Image topics to
|
|
566
|
+
CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed
|
|
567
|
+
point clouds. Requires the `video` and `pointcloud` extras; Draco compression
|
|
568
|
+
also requires the `draco` extra.
|
|
499
569
|
|
|
500
570
|
```bash
|
|
501
571
|
# Basic compression
|
|
@@ -510,7 +580,7 @@ pymcap-cli roscompress data.mcap -o compressed.mcap --pc-format draco --pc-schem
|
|
|
510
580
|
|
|
511
581
|
### `rosdecompress` — ROS Decompression
|
|
512
582
|
|
|
513
|
-
Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats.
|
|
583
|
+
Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats. Requires the `video` and `pointcloud` extras.
|
|
514
584
|
|
|
515
585
|
```bash
|
|
516
586
|
# Decompress to CompressedImage (JPEG)
|
|
@@ -605,6 +675,22 @@ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
|
|
|
605
675
|
pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
|
|
606
676
|
```
|
|
607
677
|
|
|
678
|
+
### `bridge` — Live Foxglove Bridge
|
|
679
|
+
|
|
680
|
+
Inspect, stream, or record live topics from a Foxglove WebSocket bridge. Requires
|
|
681
|
+
the `bridge` extra.
|
|
682
|
+
|
|
683
|
+
```bash
|
|
684
|
+
# Inspect advertised channels
|
|
685
|
+
pymcap-cli bridge localhost:8765
|
|
686
|
+
|
|
687
|
+
# Stream decoded messages
|
|
688
|
+
pymcap-cli bridge cat localhost:8765 --topics /tf --limit 10
|
|
689
|
+
|
|
690
|
+
# Record all advertised topics to MCAP
|
|
691
|
+
pymcap-cli bridge record localhost:8765 --all -o live.mcap
|
|
692
|
+
```
|
|
693
|
+
|
|
608
694
|
### Shell Autocompletion
|
|
609
695
|
|
|
610
696
|
```bash
|
|
@@ -631,9 +717,9 @@ pymcap-cli process full_log.mcap -o camera.mcap \
|
|
|
631
717
|
# Recover corrupt file and compress in one pass
|
|
632
718
|
pymcap-cli process corrupt.mcap -o recovered.mcap --recovery-mode --compression lz4
|
|
633
719
|
|
|
634
|
-
# Fast filtering with chunk copying
|
|
720
|
+
# Fast filtering with automatic chunk copying when possible
|
|
635
721
|
pymcap-cli process 100gb_file.mcap -o filtered.mcap \
|
|
636
|
-
-y "/lidar.*" --
|
|
722
|
+
-y "/lidar.*" --compression zstd
|
|
637
723
|
|
|
638
724
|
# Optimize for topic-specific playback
|
|
639
725
|
pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
|
|
@@ -653,14 +739,14 @@ pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
|
|
|
653
739
|
|
|
654
740
|
```bash
|
|
655
741
|
# Setup development environment
|
|
656
|
-
uv sync
|
|
742
|
+
uv sync --all-groups --all-extras --all-packages
|
|
657
743
|
|
|
658
744
|
# Run locally during development
|
|
659
745
|
uv run pymcap-cli --help
|
|
660
746
|
|
|
661
747
|
# Format and lint code
|
|
662
|
-
|
|
748
|
+
pre-commit run --all-files
|
|
663
749
|
|
|
664
750
|
# Run tests
|
|
665
|
-
uv run pytest pymcap-cli/tests
|
|
751
|
+
uv run pytest pymcap-cli/tests -m "not benchmark" --no-cov -q
|
|
666
752
|
```
|
|
@@ -17,8 +17,14 @@ uvx pymcap-cli info data.mcap
|
|
|
17
17
|
# Or add to your project
|
|
18
18
|
uv add pymcap-cli
|
|
19
19
|
|
|
20
|
-
# With video support (for video and
|
|
21
|
-
uv add pymcap-cli[video]
|
|
20
|
+
# With video support (for video generation and ROS image compression)
|
|
21
|
+
uv add "pymcap-cli[video]"
|
|
22
|
+
|
|
23
|
+
# With ROS image and point-cloud compression support
|
|
24
|
+
uv add "pymcap-cli[video,pointcloud]"
|
|
25
|
+
|
|
26
|
+
# Add Draco point-cloud compression support
|
|
27
|
+
uv add "pymcap-cli[video,pointcloud,draco]"
|
|
22
28
|
```
|
|
23
29
|
|
|
24
30
|
## Why pymcap-cli over the official Go CLI?
|
|
@@ -99,6 +105,16 @@ pymcap-cli cat recording.mcap --bytes base64 # base64-encoded
|
|
|
99
105
|
pymcap-cli cat recording.mcap --bytes skip # omit binary fields
|
|
100
106
|
```
|
|
101
107
|
|
|
108
|
+
### `doctor` — MCAP Container Validation
|
|
109
|
+
|
|
110
|
+
Check an MCAP file structure against the MCAP container specification, with
|
|
111
|
+
summary, index, chunk, message-order, and advisory findings.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pymcap-cli doctor data.mcap
|
|
115
|
+
pymcap-cli doctor data.mcap --strict-message-order --show-all
|
|
116
|
+
```
|
|
117
|
+
|
|
102
118
|
### `tftree` — TF Transform Tree
|
|
103
119
|
|
|
104
120
|
Visualize the ROS TF transform tree with colored static/dynamic transforms, translation and rotation values.
|
|
@@ -117,6 +133,16 @@ pymcap-cli tftree data.mcap
|
|
|
117
133
|
pymcap-cli tftree data.mcap --static-only
|
|
118
134
|
```
|
|
119
135
|
|
|
136
|
+
### `tf-get` — TF Transform Lookup
|
|
137
|
+
|
|
138
|
+
Resolve the transform from a source frame into a target frame using `/tf_static`
|
|
139
|
+
and `/tf`. Without `--at`, dynamic edges use their latest sample.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pymcap-cli tf-get data.mcap map base_link
|
|
143
|
+
pymcap-cli tf-get data.mcap odom base_link --at 2024-01-01T10:00:00Z
|
|
144
|
+
```
|
|
145
|
+
|
|
120
146
|
### `tf-export` — TF Tree to URDF / SDF / JSON
|
|
121
147
|
|
|
122
148
|
Reconstruct robot description files from `/tf_static` (and optionally `/tf` at a
|
|
@@ -317,7 +343,7 @@ Filter messages by topic patterns (simpler version of `process`).
|
|
|
317
343
|
|
|
318
344
|
```bash
|
|
319
345
|
# Include specific topics
|
|
320
|
-
pymcap-cli filter data.mcap -o filtered.mcap --
|
|
346
|
+
pymcap-cli filter data.mcap -o filtered.mcap --topics "/camera/image" "/lidar/points"
|
|
321
347
|
|
|
322
348
|
# Exclude topics
|
|
323
349
|
pymcap-cli filter data.mcap -o filtered.mcap --exclude-topics "/debug.*" "/test.*"
|
|
@@ -348,10 +374,32 @@ List various record types in an MCAP file.
|
|
|
348
374
|
pymcap-cli list channels data.mcap
|
|
349
375
|
pymcap-cli list chunks data.mcap
|
|
350
376
|
pymcap-cli list schemas data.mcap
|
|
377
|
+
pymcap-cli list schema data.mcap --name sensor_msgs/msg/Image
|
|
351
378
|
pymcap-cli list attachments data.mcap
|
|
352
379
|
pymcap-cli list metadata data.mcap
|
|
353
380
|
```
|
|
354
381
|
|
|
382
|
+
### `msg` — ROS2 Message Definitions
|
|
383
|
+
|
|
384
|
+
Resolve, list, and browse ROS2 `.msg` definitions. `msg def` prints complete
|
|
385
|
+
definitions including dependencies; `msg list` lists package message types; and
|
|
386
|
+
`msg serve` starts a local browser UI.
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
# Resolve a standard ROS2 message
|
|
390
|
+
pymcap-cli msg def sensor_msgs/msg/Image --distro humble
|
|
391
|
+
|
|
392
|
+
# Include custom package roots before AMENT_PREFIX_PATH and the user cache
|
|
393
|
+
pymcap-cli msg def my_robot_msgs/msg/Status -I ./install/share
|
|
394
|
+
|
|
395
|
+
# List messages in a package or browse definitions locally
|
|
396
|
+
pymcap-cli msg list sensor_msgs --distro jazzy
|
|
397
|
+
pymcap-cli msg serve --distro jazzy --no-browser
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Missing standard packages are resolved from rosdistro/GitHub and cached under
|
|
401
|
+
the `pymcap_cli_msg_def` user cache.
|
|
402
|
+
|
|
355
403
|
### `get` — Extract Attachments and Metadata
|
|
356
404
|
|
|
357
405
|
Extract a single attachment's bytes or a metadata record's key/value map.
|
|
@@ -401,6 +449,24 @@ pymcap-cli duplicates /data/recordings --all
|
|
|
401
449
|
pymcap-cli duplicates /data/recordings --rebuild-missing
|
|
402
450
|
```
|
|
403
451
|
|
|
452
|
+
### `index` — Sidecar Catalog
|
|
453
|
+
|
|
454
|
+
Maintain a sidecar SQLite catalog of MCAP summaries for fast lookup across
|
|
455
|
+
large recording trees. Requires the `xxhash` extra.
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# Scan a tree and skip unchanged files on later runs
|
|
459
|
+
pymcap-cli index scan /data/recordings
|
|
460
|
+
|
|
461
|
+
# Coverage and directory-level rollups
|
|
462
|
+
pymcap-cli index status /data/recordings
|
|
463
|
+
pymcap-cli index tree /data/recordings --max-depth 3
|
|
464
|
+
|
|
465
|
+
# Query by topic/schema/time and inspect catalog-wide topics
|
|
466
|
+
pymcap-cli index query /data/recordings --topic /camera/front --format json
|
|
467
|
+
pymcap-cli index topics /camera --sort-by messages
|
|
468
|
+
```
|
|
469
|
+
|
|
404
470
|
### `records` — Raw Record Dump
|
|
405
471
|
|
|
406
472
|
Print every MCAP record in file order using its `repr`. Useful for inspecting
|
|
@@ -421,22 +487,26 @@ pymcap-cli topic-chunks data.mcap
|
|
|
421
487
|
|
|
422
488
|
### `video` — Video Generation
|
|
423
489
|
|
|
424
|
-
Generate MP4
|
|
490
|
+
Generate one MP4 per image topic using hardware-accelerated encoding. Requires
|
|
491
|
+
the `video` extra.
|
|
425
492
|
|
|
426
493
|
```bash
|
|
427
494
|
# Basic video generation
|
|
428
|
-
pymcap-cli video data.mcap --topic /camera/front --output
|
|
495
|
+
pymcap-cli video data.mcap --topic /camera/front --output ./videos
|
|
429
496
|
|
|
430
497
|
# With quality preset
|
|
431
|
-
pymcap-cli video data.mcap --topic /camera/rear --output
|
|
498
|
+
pymcap-cli video data.mcap --topic /camera/rear --output ./videos --quality high
|
|
432
499
|
|
|
433
500
|
# Use specific codec and encoder
|
|
434
|
-
pymcap-cli video data.mcap --topic /lidar/image --output
|
|
501
|
+
pymcap-cli video data.mcap --topic /lidar/image --output ./videos --codec h265 --encoder videotoolbox
|
|
435
502
|
```
|
|
436
503
|
|
|
437
|
-
### `roscompress` — ROS Image Compression
|
|
504
|
+
### `roscompress` — ROS Image and Point-Cloud Compression
|
|
438
505
|
|
|
439
|
-
Compress ROS MCAP files by converting CompressedImage/Image topics to
|
|
506
|
+
Compress ROS MCAP files by converting CompressedImage/Image topics to
|
|
507
|
+
CompressedVideo format and PointCloud2 topics to Cloudini or Draco compressed
|
|
508
|
+
point clouds. Requires the `video` and `pointcloud` extras; Draco compression
|
|
509
|
+
also requires the `draco` extra.
|
|
440
510
|
|
|
441
511
|
```bash
|
|
442
512
|
# Basic compression
|
|
@@ -451,7 +521,7 @@ pymcap-cli roscompress data.mcap -o compressed.mcap --pc-format draco --pc-schem
|
|
|
451
521
|
|
|
452
522
|
### `rosdecompress` — ROS Decompression
|
|
453
523
|
|
|
454
|
-
Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats.
|
|
524
|
+
Decompress CompressedVideo, CompressedPointCloud2, and Foxglove CompressedPointCloud topics back to standard ROS formats. Requires the `video` and `pointcloud` extras.
|
|
455
525
|
|
|
456
526
|
```bash
|
|
457
527
|
# Decompress to CompressedImage (JPEG)
|
|
@@ -546,6 +616,22 @@ pymcap-cli export-geo data.mcap -o ./geo --format gpx --mode track --stride 5
|
|
|
546
616
|
pymcap-cli export-geo data.mcap -o ./geo --include-no-fix
|
|
547
617
|
```
|
|
548
618
|
|
|
619
|
+
### `bridge` — Live Foxglove Bridge
|
|
620
|
+
|
|
621
|
+
Inspect, stream, or record live topics from a Foxglove WebSocket bridge. Requires
|
|
622
|
+
the `bridge` extra.
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
# Inspect advertised channels
|
|
626
|
+
pymcap-cli bridge localhost:8765
|
|
627
|
+
|
|
628
|
+
# Stream decoded messages
|
|
629
|
+
pymcap-cli bridge cat localhost:8765 --topics /tf --limit 10
|
|
630
|
+
|
|
631
|
+
# Record all advertised topics to MCAP
|
|
632
|
+
pymcap-cli bridge record localhost:8765 --all -o live.mcap
|
|
633
|
+
```
|
|
634
|
+
|
|
549
635
|
### Shell Autocompletion
|
|
550
636
|
|
|
551
637
|
```bash
|
|
@@ -572,9 +658,9 @@ pymcap-cli process full_log.mcap -o camera.mcap \
|
|
|
572
658
|
# Recover corrupt file and compress in one pass
|
|
573
659
|
pymcap-cli process corrupt.mcap -o recovered.mcap --recovery-mode --compression lz4
|
|
574
660
|
|
|
575
|
-
# Fast filtering with chunk copying
|
|
661
|
+
# Fast filtering with automatic chunk copying when possible
|
|
576
662
|
pymcap-cli process 100gb_file.mcap -o filtered.mcap \
|
|
577
|
-
-y "/lidar.*" --
|
|
663
|
+
-y "/lidar.*" --compression zstd
|
|
578
664
|
|
|
579
665
|
# Optimize for topic-specific playback
|
|
580
666
|
pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
|
|
@@ -594,14 +680,14 @@ pymcap-cli rechunk robot_log.mcap -o optimized.mcap \
|
|
|
594
680
|
|
|
595
681
|
```bash
|
|
596
682
|
# Setup development environment
|
|
597
|
-
uv sync
|
|
683
|
+
uv sync --all-groups --all-extras --all-packages
|
|
598
684
|
|
|
599
685
|
# Run locally during development
|
|
600
686
|
uv run pymcap-cli --help
|
|
601
687
|
|
|
602
688
|
# Format and lint code
|
|
603
|
-
|
|
689
|
+
pre-commit run --all-files
|
|
604
690
|
|
|
605
691
|
# Run tests
|
|
606
|
-
uv run pytest pymcap-cli/tests
|
|
692
|
+
uv run pytest pymcap-cli/tests -m "not benchmark" --no-cov -q
|
|
607
693
|
```
|
|
@@ -33,9 +33,11 @@ from pymcap_cli.cmd import (
|
|
|
33
33
|
recover_inplace_cmd,
|
|
34
34
|
split_cmd,
|
|
35
35
|
tf_export_cmd,
|
|
36
|
+
tf_get_cmd,
|
|
36
37
|
tftree_cmd,
|
|
37
38
|
topic_chunks_cmd,
|
|
38
39
|
)
|
|
40
|
+
from pymcap_cli.cmd import msg as msg_pkg
|
|
39
41
|
from pymcap_cli.log_setup import ERR, setup_logging
|
|
40
42
|
|
|
41
43
|
CommandFunction: TypeAlias = Callable[..., int]
|
|
@@ -120,6 +122,22 @@ def _load_optional_app(
|
|
|
120
122
|
return cast("App", getattr(module, app_name))
|
|
121
123
|
|
|
122
124
|
|
|
125
|
+
# Modules that go missing together when a given extra is absent. Used by the
|
|
126
|
+
# optional-command loader to suppress ImportError for the *expected* shape of
|
|
127
|
+
# "extra not installed."
|
|
128
|
+
_VIDEO_MODULES: tuple[str, ...] = ("av", "mcap_codec_support", "numpy")
|
|
129
|
+
_POINTCLOUD_MODULES: tuple[str, ...] = (
|
|
130
|
+
"DracoPy",
|
|
131
|
+
"mcap_codec_support",
|
|
132
|
+
"numpy",
|
|
133
|
+
"pointcloud2",
|
|
134
|
+
"pureini",
|
|
135
|
+
)
|
|
136
|
+
_VIDEO_AND_POINTCLOUD_MODULES: tuple[str, ...] = tuple(
|
|
137
|
+
sorted({*_VIDEO_MODULES, *_POINTCLOUD_MODULES})
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
123
141
|
bridge_app = _load_optional_app(
|
|
124
142
|
"pymcap_cli.cmd.bridge",
|
|
125
143
|
"bridge_app",
|
|
@@ -130,7 +148,7 @@ bridge_app = _load_optional_app(
|
|
|
130
148
|
video = _load_optional_command(
|
|
131
149
|
"pymcap_cli.cmd.video_cmd",
|
|
132
150
|
"video",
|
|
133
|
-
expected_missing_modules=
|
|
151
|
+
expected_missing_modules=_VIDEO_MODULES,
|
|
134
152
|
message="Video command requires the 'video' extra.",
|
|
135
153
|
install_command="uv add 'pymcap-cli[video]'",
|
|
136
154
|
)
|
|
@@ -144,35 +162,21 @@ plot = _load_optional_command(
|
|
|
144
162
|
roscompress = _load_optional_command(
|
|
145
163
|
"pymcap_cli.cmd.roscompress_cmd",
|
|
146
164
|
"roscompress",
|
|
147
|
-
expected_missing_modules=
|
|
148
|
-
"av",
|
|
149
|
-
"DracoPy",
|
|
150
|
-
"mcap_codec_support",
|
|
151
|
-
"numpy",
|
|
152
|
-
"pointcloud2",
|
|
153
|
-
"pureini",
|
|
154
|
-
),
|
|
165
|
+
expected_missing_modules=_VIDEO_AND_POINTCLOUD_MODULES,
|
|
155
166
|
message="ROS compression requires the 'video' and 'pointcloud' extras.",
|
|
156
167
|
install_command="uv add 'pymcap-cli[video,pointcloud]'",
|
|
157
168
|
)
|
|
158
169
|
export_parquet = _load_optional_command(
|
|
159
170
|
"pymcap_cli.cmd.export_parquet_cmd",
|
|
160
171
|
"export_parquet",
|
|
161
|
-
expected_missing_modules=(
|
|
162
|
-
"DracoPy",
|
|
163
|
-
"mcap_codec_support",
|
|
164
|
-
"numpy",
|
|
165
|
-
"pointcloud2",
|
|
166
|
-
"pureini",
|
|
167
|
-
"pyarrow",
|
|
168
|
-
),
|
|
172
|
+
expected_missing_modules=(*_POINTCLOUD_MODULES, "pyarrow"),
|
|
169
173
|
message="Parquet export requires the 'parquet' extra.",
|
|
170
174
|
install_command="uv add 'pymcap-cli[parquet]'",
|
|
171
175
|
)
|
|
172
176
|
export_pcd = _load_optional_command(
|
|
173
177
|
"pymcap_cli.cmd.export_pcd_cmd",
|
|
174
178
|
"export_pcd",
|
|
175
|
-
expected_missing_modules=
|
|
179
|
+
expected_missing_modules=_POINTCLOUD_MODULES,
|
|
176
180
|
message="PCD export requires the 'pointcloud' extra.",
|
|
177
181
|
install_command="uv add 'pymcap-cli[pointcloud]'",
|
|
178
182
|
)
|
|
@@ -186,23 +190,25 @@ export_images = _load_optional_command(
|
|
|
186
190
|
rosdecompress = _load_optional_command(
|
|
187
191
|
"pymcap_cli.cmd.rosdecompress_cmd",
|
|
188
192
|
"rosdecompress",
|
|
189
|
-
expected_missing_modules=
|
|
190
|
-
"av",
|
|
191
|
-
"DracoPy",
|
|
192
|
-
"mcap_codec_support",
|
|
193
|
-
"numpy",
|
|
194
|
-
"pointcloud2",
|
|
195
|
-
"pureini",
|
|
196
|
-
),
|
|
193
|
+
expected_missing_modules=_VIDEO_AND_POINTCLOUD_MODULES,
|
|
197
194
|
message="ROS decompression requires the 'video' and 'pointcloud' extras.",
|
|
198
195
|
install_command="uv add 'pymcap-cli[video,pointcloud]'",
|
|
199
196
|
)
|
|
197
|
+
index_app = _load_optional_app(
|
|
198
|
+
"pymcap_cli.cmd.index",
|
|
199
|
+
"index_app",
|
|
200
|
+
expected_missing_modules=("xxhash",),
|
|
201
|
+
message="Index command requires the 'xxhash' extra "
|
|
202
|
+
"(fingerprint stability requires a single committed hash function).",
|
|
203
|
+
install_command="uv add 'pymcap-cli[xxhash]'",
|
|
204
|
+
)
|
|
200
205
|
|
|
201
206
|
|
|
202
207
|
app = App(
|
|
203
208
|
name="pymcap-cli",
|
|
204
209
|
help="CLI tool for slicing and dicing MCAP files.",
|
|
205
210
|
help_format="rich",
|
|
211
|
+
help_on_error=True,
|
|
206
212
|
default_parameter=Parameter(negative_iterable=""),
|
|
207
213
|
)
|
|
208
214
|
|
|
@@ -220,11 +226,16 @@ app.command(name="diff", group=inspect_group)(diff_cmd.diff_cmd)
|
|
|
220
226
|
app.command(name="duplicates", group=inspect_group)(duplicates_cmd.duplicates)
|
|
221
227
|
app.command(name="info", group=inspect_group)(info_cmd.info)
|
|
222
228
|
app.command(name="info-json", group=inspect_group)(info_json_cmd.info_json)
|
|
229
|
+
msg_pkg.msg_app.group = (inspect_group,)
|
|
230
|
+
app.command(msg_pkg.msg_app, name="msg")
|
|
231
|
+
index_app.group = (inspect_group,)
|
|
232
|
+
app.command(index_app, name="index")
|
|
223
233
|
get_cmd.get_app.group = (inspect_group,)
|
|
224
234
|
app.command(get_cmd.get_app, name="get")
|
|
225
235
|
list_cmd.list_app.group = (inspect_group,)
|
|
226
236
|
app.command(list_cmd.list_app, name="list")
|
|
227
237
|
app.command(name="records", group=inspect_group)(records_cmd.records)
|
|
238
|
+
app.command(name="tf-get", group=inspect_group)(tf_get_cmd.tf_get)
|
|
228
239
|
app.command(name="tftree", group=inspect_group)(tftree_cmd.tftree)
|
|
229
240
|
app.command(name="topic-chunks", group=inspect_group)(topic_chunks_cmd.topic_chunks)
|
|
230
241
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""CLI-level rechunking strategy enum.
|
|
2
|
+
|
|
3
|
+
Lives in the cmd layer because it is purely a user-facing selector that the
|
|
4
|
+
``rechunk`` and ``process`` commands translate into concrete
|
|
5
|
+
``OutputProcessor`` instances. The core processor itself is strategy-agnostic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from pymcap_cli.core.processors.chunk_groupers import (
|
|
14
|
+
PatternGrouper,
|
|
15
|
+
PerChannelGrouper,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from re import Pattern
|
|
20
|
+
|
|
21
|
+
from pymcap_cli.core.processors.base import OutputProcessor
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RechunkStrategy(str, Enum):
|
|
25
|
+
"""User-facing rechunking strategy."""
|
|
26
|
+
|
|
27
|
+
NONE = "none" # No rechunking — fast-copy optimization when possible
|
|
28
|
+
PATTERN = "pattern" # Group by topic / schema regex
|
|
29
|
+
ALL = "all" # Each channel in its own chunk group
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def build_output_processors(
|
|
33
|
+
strategy: RechunkStrategy,
|
|
34
|
+
topic_patterns: list[Pattern[str]] | None = None,
|
|
35
|
+
schema_patterns: list[Pattern[str]] | None = None,
|
|
36
|
+
) -> list[OutputProcessor]:
|
|
37
|
+
"""Translate a CLI rechunking strategy into concrete OutputProcessors."""
|
|
38
|
+
if strategy == RechunkStrategy.NONE:
|
|
39
|
+
return []
|
|
40
|
+
if strategy == RechunkStrategy.ALL:
|
|
41
|
+
return [PerChannelGrouper()]
|
|
42
|
+
return [PatternGrouper(topic_patterns or [], schema_patterns or [])]
|