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.
Files changed (32) hide show
  1. lcm_cli-0.1.0/.gitignore +33 -0
  2. lcm_cli-0.1.0/LICENSE +21 -0
  3. lcm_cli-0.1.0/PKG-INFO +254 -0
  4. lcm_cli-0.1.0/README.md +215 -0
  5. lcm_cli-0.1.0/README_zh.md +211 -0
  6. lcm_cli-0.1.0/pyproject.toml +75 -0
  7. lcm_cli-0.1.0/src/lcm_tools/__init__.py +3 -0
  8. lcm_cli-0.1.0/src/lcm_tools/__main__.py +6 -0
  9. lcm_cli-0.1.0/src/lcm_tools/cli.py +52 -0
  10. lcm_cli-0.1.0/src/lcm_tools/commands/__init__.py +1 -0
  11. lcm_cli-0.1.0/src/lcm_tools/commands/node_list.py +82 -0
  12. lcm_cli-0.1.0/src/lcm_tools/commands/topic_echo.py +188 -0
  13. lcm_cli-0.1.0/src/lcm_tools/commands/topic_list.py +69 -0
  14. lcm_cli-0.1.0/src/lcm_tools/commands/topic_stats.py +87 -0
  15. lcm_cli-0.1.0/src/lcm_tools/core/__init__.py +1 -0
  16. lcm_cli-0.1.0/src/lcm_tools/core/discovery.py +135 -0
  17. lcm_cli-0.1.0/src/lcm_tools/core/lcm_type_builder.py +577 -0
  18. lcm_cli-0.1.0/src/lcm_tools/core/lcm_type_parser.py +515 -0
  19. lcm_cli-0.1.0/src/lcm_tools/core/stats.py +182 -0
  20. lcm_cli-0.1.0/src/lcm_tools/display/__init__.py +1 -0
  21. lcm_cli-0.1.0/src/lcm_tools/display/echo_display.py +233 -0
  22. lcm_cli-0.1.0/src/lcm_tools/display/stats_display.py +103 -0
  23. lcm_cli-0.1.0/src/lcm_tools/listener.py +157 -0
  24. lcm_cli-0.1.0/src/lcm_tools/protocol.py +172 -0
  25. lcm_cli-0.1.0/tests/__init__.py +1 -0
  26. lcm_cli-0.1.0/tests/conftest.py +1 -0
  27. lcm_cli-0.1.0/tests/helpers.py +52 -0
  28. lcm_cli-0.1.0/tests/test_echo_display.py +161 -0
  29. lcm_cli-0.1.0/tests/test_lcm_type_builder.py +605 -0
  30. lcm_cli-0.1.0/tests/test_lcm_type_parser.py +410 -0
  31. lcm_cli-0.1.0/tests/test_protocol.py +157 -0
  32. lcm_cli-0.1.0/tests/test_stats.py +125 -0
@@ -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
@@ -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