lcm-cli 0.1.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.
- lcm_cli-0.1.0/.gitignore +33 -0
- lcm_cli-0.1.0/LICENSE +21 -0
- lcm_cli-0.1.0/PKG-INFO +254 -0
- lcm_cli-0.1.0/README.md +215 -0
- lcm_cli-0.1.0/README_zh.md +211 -0
- lcm_cli-0.1.0/pyproject.toml +75 -0
- lcm_cli-0.1.0/src/lcm_tools/__init__.py +3 -0
- lcm_cli-0.1.0/src/lcm_tools/__main__.py +6 -0
- lcm_cli-0.1.0/src/lcm_tools/cli.py +52 -0
- lcm_cli-0.1.0/src/lcm_tools/commands/__init__.py +1 -0
- lcm_cli-0.1.0/src/lcm_tools/commands/node_list.py +82 -0
- lcm_cli-0.1.0/src/lcm_tools/commands/topic_echo.py +188 -0
- lcm_cli-0.1.0/src/lcm_tools/commands/topic_list.py +69 -0
- lcm_cli-0.1.0/src/lcm_tools/commands/topic_stats.py +87 -0
- lcm_cli-0.1.0/src/lcm_tools/core/__init__.py +1 -0
- lcm_cli-0.1.0/src/lcm_tools/core/discovery.py +135 -0
- lcm_cli-0.1.0/src/lcm_tools/core/lcm_type_builder.py +577 -0
- lcm_cli-0.1.0/src/lcm_tools/core/lcm_type_parser.py +515 -0
- lcm_cli-0.1.0/src/lcm_tools/core/stats.py +182 -0
- lcm_cli-0.1.0/src/lcm_tools/display/__init__.py +1 -0
- lcm_cli-0.1.0/src/lcm_tools/display/echo_display.py +233 -0
- lcm_cli-0.1.0/src/lcm_tools/display/stats_display.py +103 -0
- lcm_cli-0.1.0/src/lcm_tools/listener.py +157 -0
- lcm_cli-0.1.0/src/lcm_tools/protocol.py +172 -0
- lcm_cli-0.1.0/tests/__init__.py +1 -0
- lcm_cli-0.1.0/tests/conftest.py +1 -0
- lcm_cli-0.1.0/tests/helpers.py +52 -0
- lcm_cli-0.1.0/tests/test_echo_display.py +161 -0
- lcm_cli-0.1.0/tests/test_lcm_type_builder.py +605 -0
- lcm_cli-0.1.0/tests/test_lcm_type_parser.py +410 -0
- lcm_cli-0.1.0/tests/test_protocol.py +157 -0
- lcm_cli-0.1.0/tests/test_stats.py +125 -0
lcm_cli-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Byte-compiled / cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
|
|
6
|
+
# Tests & coverage
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
htmlcov/
|
|
9
|
+
.coverage
|
|
10
|
+
|
|
11
|
+
# Build artifacts
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
*.egg
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
|
|
22
|
+
# LCM reference repo (not part of this project)
|
|
23
|
+
lcm_ref/
|
|
24
|
+
|
|
25
|
+
# lcm-gen generated Python types
|
|
26
|
+
gen_types/
|
|
27
|
+
|
|
28
|
+
# Virtual environment
|
|
29
|
+
venv/
|
|
30
|
+
|
|
31
|
+
# OS files
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
lcm_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 lcm-tools contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lcm_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lcm-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ROS2-like command line tools for LCM (Lightweight Communications and Marshalling)
|
|
5
|
+
Project-URL: Homepage, https://github.com/lcm-tools/lcm-tools
|
|
6
|
+
Project-URL: Repository, https://github.com/lcm-tools/lcm-tools
|
|
7
|
+
Project-URL: Issues, https://github.com/lcm-tools/lcm-tools/issues
|
|
8
|
+
Author: lcm-tools contributors
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,debugging,lcm,middleware,multicast,robotics,ros2
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering
|
|
27
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
28
|
+
Classifier: Topic :: System :: Networking
|
|
29
|
+
Classifier: Topic :: Utilities
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Requires-Dist: rich>=13.0.0
|
|
32
|
+
Requires-Dist: typer>=0.12.0
|
|
33
|
+
Provides-Extra: decode
|
|
34
|
+
Requires-Dist: lcm>=1.5.0; extra == 'decode'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest-timeout>=2.1; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# LCM CLI Tools
|
|
41
|
+
|
|
42
|
+
**[English](README.md)** | [中文](README_zh.md)
|
|
43
|
+
|
|
44
|
+
> ROS2-style command line tools for monitoring and debugging LCM (Lightweight Communications and Marshalling) networks.
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
lcm topic echo <channel> — View real-time topic data (like ros2 topic echo)
|
|
50
|
+
lcm topic list — List active topics/channels (like ros2 topic list)
|
|
51
|
+
lcm topic stats — Real-time topic stats: rate, bandwidth, msg count (like ros2 topic hz)
|
|
52
|
+
lcm node list — List discovered publisher nodes (like ros2 node list)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Highlight**: Built-in pure-Python `.lcm` file parser — no `lcm-gen` or `PYTHONPATH` needed. Just point to your `.lcm` files and messages are decoded automatically.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# From PyPI
|
|
61
|
+
pip install lcm-tools
|
|
62
|
+
|
|
63
|
+
# From source (development)
|
|
64
|
+
git clone https://github.com/your-username/lcm-tools.git
|
|
65
|
+
cd lcm-tools
|
|
66
|
+
pip install -e .
|
|
67
|
+
|
|
68
|
+
# Optional: traditional lcm-gen Python package decode support
|
|
69
|
+
pip install lcm-tools[decode]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Requirements**: Python >= 3.9, `typer`, `rich` (`lcm` package is optional, only for legacy `--type module.Class` decoding).
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Show all subcommands
|
|
78
|
+
lcm --help
|
|
79
|
+
|
|
80
|
+
# List active channels (listens for 5 seconds)
|
|
81
|
+
lcm topic list
|
|
82
|
+
|
|
83
|
+
# View messages on a channel (raw hex format)
|
|
84
|
+
lcm topic echo EXAMPLE
|
|
85
|
+
|
|
86
|
+
# Receive only 10 messages
|
|
87
|
+
lcm topic echo EXAMPLE -n 10
|
|
88
|
+
|
|
89
|
+
# Match multiple channels with regex
|
|
90
|
+
lcm topic echo "CAM.*"
|
|
91
|
+
|
|
92
|
+
# Monitor real-time statistics for all channels
|
|
93
|
+
lcm topic stats
|
|
94
|
+
|
|
95
|
+
# Monitor a specific channel only
|
|
96
|
+
lcm topic stats CAMERA
|
|
97
|
+
|
|
98
|
+
# List discovered publisher nodes
|
|
99
|
+
lcm node list
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Message Decoding
|
|
103
|
+
|
|
104
|
+
### Method 1: Specify `.lcm` files directly (recommended)
|
|
105
|
+
|
|
106
|
+
No `lcm-gen` installation, no `PYTHONPATH` configuration. The tool includes a built-in pure-Python parser:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Specify a single .lcm file — auto-matches message type by fingerprint
|
|
110
|
+
lcm topic echo EXAMPLE --lcm-file types/example_t.lcm
|
|
111
|
+
|
|
112
|
+
# Specify a directory (recursively scans all .lcm files)
|
|
113
|
+
lcm topic echo EXAMPLE -f types/
|
|
114
|
+
|
|
115
|
+
# Specify multiple paths
|
|
116
|
+
lcm topic echo EXAMPLE -f types/ -f extra_types/
|
|
117
|
+
|
|
118
|
+
# Specify a concrete type name (when .lcm files contain multiple structs)
|
|
119
|
+
lcm topic echo EXAMPLE -f types/ --type example_t
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Supports the complete LCM type system:
|
|
123
|
+
- All primitive types (`int8_t` ~ `int64_t`, `float`, `double`, `string`, `boolean`, `byte`)
|
|
124
|
+
- Fixed-length and variable-length arrays (`double position[3]`, `int16_t ranges[num_ranges]`)
|
|
125
|
+
- Multi-dimensional arrays (`int32_t data[size_a][size_b][size_c]`)
|
|
126
|
+
- Nested structs and cross-file type references
|
|
127
|
+
- Recursive types (e.g., `node_t children[n]` in a linked-list `node_t`)
|
|
128
|
+
- Constant declarations (`const int32_t MAX_SIZE = 100`)
|
|
129
|
+
|
|
130
|
+
**How it works**: Parses `.lcm` files → builds decode classes in memory (`type()` dynamic creation) → auto-matches by the first 8-byte fingerprint of the payload → decodes and recursively expands nested structs. No files are generated at any point.
|
|
131
|
+
|
|
132
|
+
### Method 2: Traditional `lcm-gen` generated files
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Install the lcm Python package
|
|
136
|
+
pip install lcm-tools[decode]
|
|
137
|
+
|
|
138
|
+
# Generate Python files with lcm-gen, then configure PYTHONPATH
|
|
139
|
+
lcm-gen --python -d types/ types/example_t.lcm
|
|
140
|
+
export PYTHONPATH=types:$PYTHONPATH
|
|
141
|
+
|
|
142
|
+
# Use --type to specify the decode class (module.Class format)
|
|
143
|
+
lcm topic echo EXAMPLE --type exlcm.example_t
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Custom Multicast Address
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
lcm topic list --lcm-url 239.255.76.68 --lcm-port 7668
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Statistics
|
|
153
|
+
|
|
154
|
+
| Metric | Description |
|
|
155
|
+
|--------|-------------|
|
|
156
|
+
| Rate (Hz) | Message frequency within a sliding window (last 2000 messages) |
|
|
157
|
+
| BW (KB/s) | Bandwidth within the sliding window |
|
|
158
|
+
| Avg Size (B) | Average bytes per message |
|
|
159
|
+
| Total (KB) | Cumulative total transferred |
|
|
160
|
+
|
|
161
|
+
## Architecture
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
┌───────────────────────────────────────────────────┐
|
|
165
|
+
│ CLI Layer (Typer) │
|
|
166
|
+
│ topic echo │ topic list │ topic stats │ node │
|
|
167
|
+
├───────────────────────────────────────────────────┤
|
|
168
|
+
│ Display Layer (Rich Panel) │
|
|
169
|
+
│ recursive nesting │ hex dump │ stats table │
|
|
170
|
+
├───────────────────────────────────────────────────┤
|
|
171
|
+
│ Type Parsing Layer (Pure Python) │
|
|
172
|
+
│ .lcm parse → AST → fingerprint → dynamic class │
|
|
173
|
+
├───────────────────────────────────────────────────┤
|
|
174
|
+
│ Protocol Layer (Raw UDP Socket) │
|
|
175
|
+
│ LCM Wire Protocol parsing (zero deps) │
|
|
176
|
+
├───────────────────────────────────────────────────┤
|
|
177
|
+
│ UDP Multicast (239.255.76.67) │
|
|
178
|
+
└───────────────────────────────────────────────────┘
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- **Zero external LCM dependency**: Core functionality directly parses the LCM wire protocol from UDP multicast packets
|
|
182
|
+
- **Built-in type parsing**: Pure-Python `.lcm` file parser + runtime decode class generator
|
|
183
|
+
- **Node discovery**: Infers different publishers from UDP packet source IP:port
|
|
184
|
+
- **Legacy decode compatible**: Optional `lcm` Python package for `--type module.Class` decoding
|
|
185
|
+
|
|
186
|
+
## LCM Protocol
|
|
187
|
+
|
|
188
|
+
LCM uses UDP multicast for communication (default `239.255.76.67:7667`).
|
|
189
|
+
|
|
190
|
+
**Short messages** (< 64KB): 8-byte header (magic=0x4c433032 + seqno) + channel name (null-terminated) + payload
|
|
191
|
+
|
|
192
|
+
**Fragmented messages**: 20-byte header (magic=0x4c433033 + seqno + payload_size + fragment_offset + fragment_no + n_fragments)
|
|
193
|
+
|
|
194
|
+
Reference: [LCM UDP Multicast Protocol](https://lcm-proj.github.io/lcm/content/udp-multicast-protocol.html)
|
|
195
|
+
|
|
196
|
+
## LCM vs ROS2 Concepts
|
|
197
|
+
|
|
198
|
+
| LCM Concept | ROS2 Equivalent | Description |
|
|
199
|
+
|-------------|----------------|-------------|
|
|
200
|
+
| Channel | Topic | Message publish/subscribe conduit |
|
|
201
|
+
| UDP (IP:port) | Node | LCM has no native node concept; inferred from publisher address |
|
|
202
|
+
| Fingerprint | Message Type Hash | Unique identifier for a message type |
|
|
203
|
+
|
|
204
|
+
## Project Structure
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
src/lcm_tools/
|
|
208
|
+
├── cli.py # Typer entry point, registers subcommands
|
|
209
|
+
├── commands/
|
|
210
|
+
│ ├── topic_echo.py # lcm topic echo
|
|
211
|
+
│ ├── topic_list.py # lcm topic list
|
|
212
|
+
│ ├── topic_stats.py # lcm topic stats
|
|
213
|
+
│ └── node_list.py # lcm node list
|
|
214
|
+
├── core/
|
|
215
|
+
│ ├── discovery.py # Passive channel/node discovery
|
|
216
|
+
│ ├── stats.py # Real-time statistics (rate, bandwidth)
|
|
217
|
+
│ ├── lcm_type_parser.py # .lcm file parser + fingerprint algorithm
|
|
218
|
+
│ └── lcm_type_builder.py # Runtime decode class generation + TypeRegistry
|
|
219
|
+
├── display/
|
|
220
|
+
│ ├── echo_display.py # Rich panel display (with recursive nesting)
|
|
221
|
+
│ └── stats_display.py # Statistics table display
|
|
222
|
+
├── listener.py # UDP multicast listener thread
|
|
223
|
+
└── protocol.py # LCM Wire Protocol parser
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Testing
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install -e ".[dev]"
|
|
230
|
+
pytest tests/ -v
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Network Configuration
|
|
234
|
+
|
|
235
|
+
If you can't receive messages, check your multicast routing:
|
|
236
|
+
|
|
237
|
+
**macOS:**
|
|
238
|
+
```bash
|
|
239
|
+
# View multicast routes
|
|
240
|
+
netstat -rn | grep 239
|
|
241
|
+
|
|
242
|
+
# Add route if needed
|
|
243
|
+
sudo route add -net 239.255.76.0/24 -interface en0
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Linux:**
|
|
247
|
+
```bash
|
|
248
|
+
# Add route
|
|
249
|
+
sudo ip route add 239.255.76.0/24 dev eth0
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
lcm_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# LCM CLI Tools
|
|
2
|
+
|
|
3
|
+
**[English](README.md)** | [中文](README_zh.md)
|
|
4
|
+
|
|
5
|
+
> ROS2-style command line tools for monitoring and debugging LCM (Lightweight Communications and Marshalling) networks.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
lcm topic echo <channel> — View real-time topic data (like ros2 topic echo)
|
|
11
|
+
lcm topic list — List active topics/channels (like ros2 topic list)
|
|
12
|
+
lcm topic stats — Real-time topic stats: rate, bandwidth, msg count (like ros2 topic hz)
|
|
13
|
+
lcm node list — List discovered publisher nodes (like ros2 node list)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Highlight**: Built-in pure-Python `.lcm` file parser — no `lcm-gen` or `PYTHONPATH` needed. Just point to your `.lcm` files and messages are decoded automatically.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# From PyPI
|
|
22
|
+
pip install lcm-tools
|
|
23
|
+
|
|
24
|
+
# From source (development)
|
|
25
|
+
git clone https://github.com/your-username/lcm-tools.git
|
|
26
|
+
cd lcm-tools
|
|
27
|
+
pip install -e .
|
|
28
|
+
|
|
29
|
+
# Optional: traditional lcm-gen Python package decode support
|
|
30
|
+
pip install lcm-tools[decode]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Requirements**: Python >= 3.9, `typer`, `rich` (`lcm` package is optional, only for legacy `--type module.Class` decoding).
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Show all subcommands
|
|
39
|
+
lcm --help
|
|
40
|
+
|
|
41
|
+
# List active channels (listens for 5 seconds)
|
|
42
|
+
lcm topic list
|
|
43
|
+
|
|
44
|
+
# View messages on a channel (raw hex format)
|
|
45
|
+
lcm topic echo EXAMPLE
|
|
46
|
+
|
|
47
|
+
# Receive only 10 messages
|
|
48
|
+
lcm topic echo EXAMPLE -n 10
|
|
49
|
+
|
|
50
|
+
# Match multiple channels with regex
|
|
51
|
+
lcm topic echo "CAM.*"
|
|
52
|
+
|
|
53
|
+
# Monitor real-time statistics for all channels
|
|
54
|
+
lcm topic stats
|
|
55
|
+
|
|
56
|
+
# Monitor a specific channel only
|
|
57
|
+
lcm topic stats CAMERA
|
|
58
|
+
|
|
59
|
+
# List discovered publisher nodes
|
|
60
|
+
lcm node list
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Message Decoding
|
|
64
|
+
|
|
65
|
+
### Method 1: Specify `.lcm` files directly (recommended)
|
|
66
|
+
|
|
67
|
+
No `lcm-gen` installation, no `PYTHONPATH` configuration. The tool includes a built-in pure-Python parser:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Specify a single .lcm file — auto-matches message type by fingerprint
|
|
71
|
+
lcm topic echo EXAMPLE --lcm-file types/example_t.lcm
|
|
72
|
+
|
|
73
|
+
# Specify a directory (recursively scans all .lcm files)
|
|
74
|
+
lcm topic echo EXAMPLE -f types/
|
|
75
|
+
|
|
76
|
+
# Specify multiple paths
|
|
77
|
+
lcm topic echo EXAMPLE -f types/ -f extra_types/
|
|
78
|
+
|
|
79
|
+
# Specify a concrete type name (when .lcm files contain multiple structs)
|
|
80
|
+
lcm topic echo EXAMPLE -f types/ --type example_t
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Supports the complete LCM type system:
|
|
84
|
+
- All primitive types (`int8_t` ~ `int64_t`, `float`, `double`, `string`, `boolean`, `byte`)
|
|
85
|
+
- Fixed-length and variable-length arrays (`double position[3]`, `int16_t ranges[num_ranges]`)
|
|
86
|
+
- Multi-dimensional arrays (`int32_t data[size_a][size_b][size_c]`)
|
|
87
|
+
- Nested structs and cross-file type references
|
|
88
|
+
- Recursive types (e.g., `node_t children[n]` in a linked-list `node_t`)
|
|
89
|
+
- Constant declarations (`const int32_t MAX_SIZE = 100`)
|
|
90
|
+
|
|
91
|
+
**How it works**: Parses `.lcm` files → builds decode classes in memory (`type()` dynamic creation) → auto-matches by the first 8-byte fingerprint of the payload → decodes and recursively expands nested structs. No files are generated at any point.
|
|
92
|
+
|
|
93
|
+
### Method 2: Traditional `lcm-gen` generated files
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Install the lcm Python package
|
|
97
|
+
pip install lcm-tools[decode]
|
|
98
|
+
|
|
99
|
+
# Generate Python files with lcm-gen, then configure PYTHONPATH
|
|
100
|
+
lcm-gen --python -d types/ types/example_t.lcm
|
|
101
|
+
export PYTHONPATH=types:$PYTHONPATH
|
|
102
|
+
|
|
103
|
+
# Use --type to specify the decode class (module.Class format)
|
|
104
|
+
lcm topic echo EXAMPLE --type exlcm.example_t
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Custom Multicast Address
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
lcm topic list --lcm-url 239.255.76.68 --lcm-port 7668
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Statistics
|
|
114
|
+
|
|
115
|
+
| Metric | Description |
|
|
116
|
+
|--------|-------------|
|
|
117
|
+
| Rate (Hz) | Message frequency within a sliding window (last 2000 messages) |
|
|
118
|
+
| BW (KB/s) | Bandwidth within the sliding window |
|
|
119
|
+
| Avg Size (B) | Average bytes per message |
|
|
120
|
+
| Total (KB) | Cumulative total transferred |
|
|
121
|
+
|
|
122
|
+
## Architecture
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
┌───────────────────────────────────────────────────┐
|
|
126
|
+
│ CLI Layer (Typer) │
|
|
127
|
+
│ topic echo │ topic list │ topic stats │ node │
|
|
128
|
+
├───────────────────────────────────────────────────┤
|
|
129
|
+
│ Display Layer (Rich Panel) │
|
|
130
|
+
│ recursive nesting │ hex dump │ stats table │
|
|
131
|
+
├───────────────────────────────────────────────────┤
|
|
132
|
+
│ Type Parsing Layer (Pure Python) │
|
|
133
|
+
│ .lcm parse → AST → fingerprint → dynamic class │
|
|
134
|
+
├───────────────────────────────────────────────────┤
|
|
135
|
+
│ Protocol Layer (Raw UDP Socket) │
|
|
136
|
+
│ LCM Wire Protocol parsing (zero deps) │
|
|
137
|
+
├───────────────────────────────────────────────────┤
|
|
138
|
+
│ UDP Multicast (239.255.76.67) │
|
|
139
|
+
└───────────────────────────────────────────────────┘
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **Zero external LCM dependency**: Core functionality directly parses the LCM wire protocol from UDP multicast packets
|
|
143
|
+
- **Built-in type parsing**: Pure-Python `.lcm` file parser + runtime decode class generator
|
|
144
|
+
- **Node discovery**: Infers different publishers from UDP packet source IP:port
|
|
145
|
+
- **Legacy decode compatible**: Optional `lcm` Python package for `--type module.Class` decoding
|
|
146
|
+
|
|
147
|
+
## LCM Protocol
|
|
148
|
+
|
|
149
|
+
LCM uses UDP multicast for communication (default `239.255.76.67:7667`).
|
|
150
|
+
|
|
151
|
+
**Short messages** (< 64KB): 8-byte header (magic=0x4c433032 + seqno) + channel name (null-terminated) + payload
|
|
152
|
+
|
|
153
|
+
**Fragmented messages**: 20-byte header (magic=0x4c433033 + seqno + payload_size + fragment_offset + fragment_no + n_fragments)
|
|
154
|
+
|
|
155
|
+
Reference: [LCM UDP Multicast Protocol](https://lcm-proj.github.io/lcm/content/udp-multicast-protocol.html)
|
|
156
|
+
|
|
157
|
+
## LCM vs ROS2 Concepts
|
|
158
|
+
|
|
159
|
+
| LCM Concept | ROS2 Equivalent | Description |
|
|
160
|
+
|-------------|----------------|-------------|
|
|
161
|
+
| Channel | Topic | Message publish/subscribe conduit |
|
|
162
|
+
| UDP (IP:port) | Node | LCM has no native node concept; inferred from publisher address |
|
|
163
|
+
| Fingerprint | Message Type Hash | Unique identifier for a message type |
|
|
164
|
+
|
|
165
|
+
## Project Structure
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
src/lcm_tools/
|
|
169
|
+
├── cli.py # Typer entry point, registers subcommands
|
|
170
|
+
├── commands/
|
|
171
|
+
│ ├── topic_echo.py # lcm topic echo
|
|
172
|
+
│ ├── topic_list.py # lcm topic list
|
|
173
|
+
│ ├── topic_stats.py # lcm topic stats
|
|
174
|
+
│ └── node_list.py # lcm node list
|
|
175
|
+
├── core/
|
|
176
|
+
│ ├── discovery.py # Passive channel/node discovery
|
|
177
|
+
│ ├── stats.py # Real-time statistics (rate, bandwidth)
|
|
178
|
+
│ ├── lcm_type_parser.py # .lcm file parser + fingerprint algorithm
|
|
179
|
+
│ └── lcm_type_builder.py # Runtime decode class generation + TypeRegistry
|
|
180
|
+
├── display/
|
|
181
|
+
│ ├── echo_display.py # Rich panel display (with recursive nesting)
|
|
182
|
+
│ └── stats_display.py # Statistics table display
|
|
183
|
+
├── listener.py # UDP multicast listener thread
|
|
184
|
+
└── protocol.py # LCM Wire Protocol parser
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Testing
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pip install -e ".[dev]"
|
|
191
|
+
pytest tests/ -v
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Network Configuration
|
|
195
|
+
|
|
196
|
+
If you can't receive messages, check your multicast routing:
|
|
197
|
+
|
|
198
|
+
**macOS:**
|
|
199
|
+
```bash
|
|
200
|
+
# View multicast routes
|
|
201
|
+
netstat -rn | grep 239
|
|
202
|
+
|
|
203
|
+
# Add route if needed
|
|
204
|
+
sudo route add -net 239.255.76.0/24 -interface en0
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Linux:**
|
|
208
|
+
```bash
|
|
209
|
+
# Add route
|
|
210
|
+
sudo ip route add 239.255.76.0/24 dev eth0
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT
|