Metadata-Version: 2.4
Name: lcm-cli
Version: 0.1.0
Summary: ROS2-like command line tools for LCM (Lightweight Communications and Marshalling)
Project-URL: Homepage, https://github.com/lcm-tools/lcm-tools
Project-URL: Repository, https://github.com/lcm-tools/lcm-tools
Project-URL: Issues, https://github.com/lcm-tools/lcm-tools/issues
Author: lcm-tools contributors
License-Expression: MIT
License-File: LICENSE
Keywords: cli,debugging,lcm,middleware,multicast,robotics,ros2
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Topic :: System :: Networking
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.12.0
Provides-Extra: decode
Requires-Dist: lcm>=1.5.0; extra == 'decode'
Provides-Extra: dev
Requires-Dist: pytest-timeout>=2.1; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# LCM CLI Tools

**[English](README.md)** | [中文](README_zh.md)

> ROS2-style command line tools for monitoring and debugging LCM (Lightweight Communications and Marshalling) networks.

## Features

```
lcm topic echo <channel>   — View real-time topic data (like ros2 topic echo)
lcm topic list             — List active topics/channels (like ros2 topic list)
lcm topic stats            — Real-time topic stats: rate, bandwidth, msg count (like ros2 topic hz)
lcm node list              — List discovered publisher nodes (like ros2 node list)
```

**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.

## Installation

```bash
# From PyPI
pip install lcm-tools

# From source (development)
git clone https://github.com/your-username/lcm-tools.git
cd lcm-tools
pip install -e .

# Optional: traditional lcm-gen Python package decode support
pip install lcm-tools[decode]
```

**Requirements**: Python >= 3.9, `typer`, `rich` (`lcm` package is optional, only for legacy `--type module.Class` decoding).

## Quick Start

```bash
# Show all subcommands
lcm --help

# List active channels (listens for 5 seconds)
lcm topic list

# View messages on a channel (raw hex format)
lcm topic echo EXAMPLE

# Receive only 10 messages
lcm topic echo EXAMPLE -n 10

# Match multiple channels with regex
lcm topic echo "CAM.*"

# Monitor real-time statistics for all channels
lcm topic stats

# Monitor a specific channel only
lcm topic stats CAMERA

# List discovered publisher nodes
lcm node list
```

## Message Decoding

### Method 1: Specify `.lcm` files directly (recommended)

No `lcm-gen` installation, no `PYTHONPATH` configuration. The tool includes a built-in pure-Python parser:

```bash
# Specify a single .lcm file — auto-matches message type by fingerprint
lcm topic echo EXAMPLE --lcm-file types/example_t.lcm

# Specify a directory (recursively scans all .lcm files)
lcm topic echo EXAMPLE -f types/

# Specify multiple paths
lcm topic echo EXAMPLE -f types/ -f extra_types/

# Specify a concrete type name (when .lcm files contain multiple structs)
lcm topic echo EXAMPLE -f types/ --type example_t
```

Supports the complete LCM type system:
- All primitive types (`int8_t` ~ `int64_t`, `float`, `double`, `string`, `boolean`, `byte`)
- Fixed-length and variable-length arrays (`double position[3]`, `int16_t ranges[num_ranges]`)
- Multi-dimensional arrays (`int32_t data[size_a][size_b][size_c]`)
- Nested structs and cross-file type references
- Recursive types (e.g., `node_t children[n]` in a linked-list `node_t`)
- Constant declarations (`const int32_t MAX_SIZE = 100`)

**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.

### Method 2: Traditional `lcm-gen` generated files

```bash
# Install the lcm Python package
pip install lcm-tools[decode]

# Generate Python files with lcm-gen, then configure PYTHONPATH
lcm-gen --python -d types/ types/example_t.lcm
export PYTHONPATH=types:$PYTHONPATH

# Use --type to specify the decode class (module.Class format)
lcm topic echo EXAMPLE --type exlcm.example_t
```

### Custom Multicast Address

```bash
lcm topic list --lcm-url 239.255.76.68 --lcm-port 7668
```

### Statistics

| Metric | Description |
|--------|-------------|
| Rate (Hz) | Message frequency within a sliding window (last 2000 messages) |
| BW (KB/s) | Bandwidth within the sliding window |
| Avg Size (B) | Average bytes per message |
| Total (KB) | Cumulative total transferred |

## Architecture

```
┌───────────────────────────────────────────────────┐
│                  CLI Layer (Typer)                │
│   topic echo │ topic list │ topic stats │ node    │
├───────────────────────────────────────────────────┤
│              Display Layer (Rich Panel)           │
│   recursive nesting │ hex dump │ stats table      │
├───────────────────────────────────────────────────┤
│           Type Parsing Layer (Pure Python)        │
│   .lcm parse → AST → fingerprint → dynamic class  │
├───────────────────────────────────────────────────┤
│            Protocol Layer (Raw UDP Socket)        │
│       LCM Wire Protocol parsing (zero deps)       │
├───────────────────────────────────────────────────┤
│            UDP Multicast (239.255.76.67)          │
└───────────────────────────────────────────────────┘
```

- **Zero external LCM dependency**: Core functionality directly parses the LCM wire protocol from UDP multicast packets
- **Built-in type parsing**: Pure-Python `.lcm` file parser + runtime decode class generator
- **Node discovery**: Infers different publishers from UDP packet source IP:port
- **Legacy decode compatible**: Optional `lcm` Python package for `--type module.Class` decoding

## LCM Protocol

LCM uses UDP multicast for communication (default `239.255.76.67:7667`).

**Short messages** (< 64KB): 8-byte header (magic=0x4c433032 + seqno) + channel name (null-terminated) + payload

**Fragmented messages**: 20-byte header (magic=0x4c433033 + seqno + payload_size + fragment_offset + fragment_no + n_fragments)

Reference: [LCM UDP Multicast Protocol](https://lcm-proj.github.io/lcm/content/udp-multicast-protocol.html)

## LCM vs ROS2 Concepts

| LCM Concept | ROS2 Equivalent | Description |
|-------------|----------------|-------------|
| Channel | Topic | Message publish/subscribe conduit |
| UDP (IP:port) | Node | LCM has no native node concept; inferred from publisher address |
| Fingerprint | Message Type Hash | Unique identifier for a message type |

## Project Structure

```
src/lcm_tools/
├── cli.py                       # Typer entry point, registers subcommands
├── commands/
│   ├── topic_echo.py            # lcm topic echo
│   ├── topic_list.py            # lcm topic list
│   ├── topic_stats.py           # lcm topic stats
│   └── node_list.py             # lcm node list
├── core/
│   ├── discovery.py             # Passive channel/node discovery
│   ├── stats.py                 # Real-time statistics (rate, bandwidth)
│   ├── lcm_type_parser.py       # .lcm file parser + fingerprint algorithm
│   └── lcm_type_builder.py      # Runtime decode class generation + TypeRegistry
├── display/
│   ├── echo_display.py          # Rich panel display (with recursive nesting)
│   └── stats_display.py         # Statistics table display
├── listener.py                  # UDP multicast listener thread
└── protocol.py                  # LCM Wire Protocol parser
```

## Testing

```bash
pip install -e ".[dev]"
pytest tests/ -v
```

## Network Configuration

If you can't receive messages, check your multicast routing:

**macOS:**
```bash
# View multicast routes
netstat -rn | grep 239

# Add route if needed
sudo route add -net 239.255.76.0/24 -interface en0
```

**Linux:**
```bash
# Add route
sudo ip route add 239.255.76.0/24 dev eth0
```

## License

MIT
