Metadata-Version: 2.4
Name: mcurpc
Version: 0.3.3
Summary: Lightweight RPC framework for microcontrollers and Python.
Author: Sven Schlender
License-Expression: GPL-3.0-or-later
Project-URL: Documentation, https://mcurpc-7e23fb.gitlab.io
Project-URL: Repository, https://gitlab.com/svesch/mcurpc
Keywords: rpc,embedded,microcontroller,serial,protocol,arduino
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Topic :: System :: Networking
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyserial>=3.5
Requires-Dist: pydantic>=2.13
Requires-Dist: ruamel.yaml>=0.18
Requires-Dist: platformdirs>=4.9.6
Requires-Dist: cmd2>=4.0.0
Dynamic: license-file

# mcurpc

Lightweight RPC framework for microcontrollers and Python.

MCURPC provides a small RPC stack for embedded targets together with Python tooling for API
creation, code generation, runtime export, probing and interactive communication. APIs are described
in YAML, resolved into a versioned JSON manifest and then used to generate C integration files for the
embedded side.

The root README gives only a project-level overview and the most common commands. More detailed
implementation notes belong in the Sphinx documentation under `docs/`.

## What is included

- **API model and generator** (`mcurpc_gen`): validates YAML API descriptions, resolves IDs and
  options, creates `manifest.json`, `mcurpc_api.h`, `mcurpc_api.c` and `mcurpc_config.h`.
- **Embedded C runtime** (`mcurpc_runtime`): fixed MCURPC runtime sources for request dispatch,
  responses, events, logs and link framing.
- **Python runtime/client** (`mcurpc`): link/message/payload handling, transports, registry lookup,
  device probing and manifest-driven client calls.
- **Command line interface** (`mcurpc_cli`): project start, generation, runtime export, registry
  management, probing and interactive shell.
- **Optional GUI prototype** (`mcurpc_gui`): early project-oriented GUI components.
- **Tests and CI helpers**: Python unit tests, C/C++ unit tests, CLI system tests, CMake integration
  checks, coverage and Sphinx build commands.

## Repository layout

```text
ci_commands/   Local CI command implementations
configs/       Project/tool configuration, when present
docs/          Sphinx documentation
src/           Python packages and packaged runtime resources
tests/         Python, C/C++ and CLI system tests
```

The most relevant source packages are:

```text
src/mcurpc/          Python runtime, client, transports and registry support
src/mcurpc_cli/      Unified `mcurpc` command line interface
src/mcurpc_gen/      YAML model, resolver and C source generator
src/mcurpc_runtime/  Packaged embedded runtime files and starter examples
src/mcurpc_gui/      GUI prototype modules
```

## Requirements

The Python package requires Python 3.11 or newer. Runtime dependencies include `pydantic`, `pyyaml`,
`pyserial`, `platformdirs`, `cmd2` and `cikit`.

For the native C tests and CMake-based example checks, a working C/C++ toolchain, CMake and Ninja are
expected. The repository currently uses CMake for native runtime tests and compile checks.

## Installation for development

A typical local setup with `uv` is:

```bash
uv sync
uv run mcurpc --help
```

Alternatively, use an editable install in an existing virtual environment:

```bash
python -m pip install -e .
mcurpc --help
```

## Basic workflow

A normal embedded workflow is:

1. Create or edit an API YAML file.
2. Create or prepare the target project.
3. Generate API-specific C files from the YAML description.
4. Implement the generated application callbacks in the target firmware.
5. Register the manifest on the host side.
6. Probe the device or open the interactive shell.

For a new starter project, the `start` command combines the first generation steps.

## Creating a starter project

The `start` command creates a project from one of the packaged examples, exports the selected runtime
layout and generates the API-specific files.

```bash
mcurpc start build/my_ping --example ping --layout arduino_ide
mcurpc start build/my_ping_pio --example ping --layout arduino_pio
mcurpc start build/my_counter --example counter --layout cmake
mcurpc start build/my_api --example custom --layout flat
```

The available layouts are:

- `arduino_ide`: flattened Arduino IDE-oriented project with the C runtime and `MCURPC` stream
  wrapper.
- `arduino_pio`: PlatformIO project layout with application and generated API sources below `src/`,
  generated headers below `include/` and the fixed MCURPC runtime below `lib/MCURPC/`.
- `cmake`: runtime files below an `mcurpc/` subdirectory with a CMake target.
- `flat`: all runtime and generated files in one project directory.

Useful options:

```bash
mcurpc start PROJECT_DIR --example ping --layout arduino_ide
mcurpc start PROJECT_DIR --example custom --layout cmake --name my_api
mcurpc start PROJECT_DIR --example counter --layout flat --force
```

## Generating API files

Use `generate` when the project already exists and only the files derived from the API YAML should be
updated.

```bash
mcurpc generate api.yaml -o path/to/project
```

The exact output paths depend on the selected layout. For the flat and Arduino IDE layouts, the
generated files are written directly into the output directory:

```text
manifest.json
mcurpc_api.c
mcurpc_api.h
mcurpc_config.h
```

For PlatformIO, generate with:

```bash
mcurpc generate api.yaml -o path/to/project --layout arduino_pio
```

The generated files are placed according to the normal PlatformIO project structure:

```text
manifest.json
include/mcurpc_api.h
include/mcurpc_config.h
src/mcurpc_api.c
```

The generated files are project-specific. The fixed runtime files are exported separately through
`runtime` or indirectly through `start`.

## Exporting the runtime

Use `runtime` to copy only the fixed MCURPC runtime files into an existing project.

```bash
mcurpc runtime path/to/project --layout arduino_ide
mcurpc runtime path/to/project --layout arduino_pio
mcurpc runtime path/to/project --layout cmake
mcurpc runtime path/to/project --layout flat --force
```

The runtime export does not generate `mcurpc_api.c`, `mcurpc_api.h`, `mcurpc_config.h` or
`manifest.json`. Those files come from the API generator.

## API YAML at a glance

A minimal API can look like this:

```yaml
name: garage_api
uuid: 11111111-1111-4111-8111-111111111111
version: 1

options:
  endpoint_count: 1
  tx_queue_length: 2

requests:
  - name: open_door
    request:
      - name: duration_ms
        type: u32

events:
  - name: state_changed
    payload:
      - name: state
        type: u8

errors:
  - name: invalid_state
```

The resolver adds internal system requests such as API identity probing. Application request IDs are
kept separate from those system entries.

## Registry and probing

The host-side registry maps API UUID and version to a resolved manifest. This lets tools probe a
connected device without requiring the user to manually select a manifest.

Register an API YAML or resolved manifest:

```bash
mcurpc register api.yaml
mcurpc register manifest.json
mcurpc register api.yaml --registry ./registry --force
```

Show the default registry path:

```bash
mcurpc registry
```

Probe a device and print the matching manifest summary:

```bash
mcurpc probe serial -p /dev/ttyACM0 -b 115200
mcurpc probe --registry ./registry serial -p /dev/ttyACM0 -b 115200
```

The CLI also contains UDP frame transport support for host-side and test scenarios.

## Interactive shell

The interactive shell probes the connected device, resolves the matching manifest and exposes
manifest-driven commands.

```bash
mcurpc shell serial -p /dev/ttyACM0 -b 115200
```

Typical shell commands include:

```text
info                  Show API summary
requests              List application requests
rpc <name> [options]  Call one manifest request
events                Show received events
logs                  Show received log messages
help                  Show command help
quit                  Exit the shell
```

The `rpc` command is generated from the manifest, so request parameters follow the fields declared in
the API YAML.

## Python client usage

The Python side can also be used directly. 
A manifest-independent `MrpcConnection` owns transport communication and message routing. 
After probing the device and resolving its manifest, the same connection can be attached to a manifest-driven client.

```python
from mcurpc.client import build_client
from mcurpc.connection import MrpcConnection
from mcurpc.probe import query_api_info
from mcurpc.registry import ManifestRegistry
from mcurpc.transport import SerialTransport

transport = SerialTransport("/dev/ttyACM0", baudrate=115200)
transport.open()

connection = MrpcConnection(
    transport,
    enable_trace=False,
    enable_events=True,
    enable_logs=True,
)
connection.start()

try:
    api_info = query_api_info(connection)
    manifest = ManifestRegistry().find(api_info)
    client = build_client(manifest, connection)

    response = client.call("open_door", {"duration_ms": 1000})
finally:
    connection.stop()
    transport.close()
```

Check the current Python API before relying on helper names in external scripts, because the command
line interface is the more stable integration surface at this stage.

## Embedded integration

On the embedded side, generated request declarations are implemented by the application. The runtime
calls these functions when matching request frames are received.

For PlatformIO projects, the generated configuration header is stored below the project `include/`
directory while the fixed MCURPC runtime is compiled from `lib/MCURPC/`. Add the project include
directory to the build flags so the runtime can resolve `mcurpc_config.h`:

```ini
[env]
build_flags =
    -I${PROJECT_DIR}/include
```

The common `[env]` section applies the flag to all PlatformIO environments. It may also be placed in a
specific `[env:<name>]` section when only one environment should use it.

For Arduino projects, the packaged `MCURPC` wrapper connects the runtime to an Arduino `Stream`:

```cpp
#include "MCURPC.hpp"
#include "mcurpc_api.h"

MCURPC mcurpc(Serial);

void setup(void)
{
    Serial.begin(115200);
    mcurpc.begin();
}

void loop(void)
{
    mcurpc.process();
}
```

For plain C/CMake projects, call `mcurpc_setup()` once and `mcurpc_loop()` cyclically. Transport
integration feeds RX bytes or frames into the runtime and consumes pending TX bytes or frames through
the public transport functions.

## Tests and local CI

The repository contains several test layers:

- Python unit tests in `tests/pyunittests/`
- CLI/system tests in `tests/systemtests/`
- native C/C++ runtime tests in `tests/cunittests/`
- generated-code compile checks in the system test cases

Common commands:

```bash
uv run pytest tests/pyunittests
uv run pytest tests/systemtests
uv run cikit ctest . --alias cunittests
uv run cikit all
```

`cikit all` runs the local workflow, including linting, Python tests, system tests, C tests, coverage
steps and the Sphinx documentation build.

## Documentation

The Sphinx documentation lives in `docs/`.

```bash
uv run cikit sphinx docs
```

Use the Sphinx docs for detailed runtime behavior, generator model details and build reports. The root
README should stay focused on orientation and first-use workflows.

## License

MCURPC is licensed under GPL-3.0-or-later.
