Metadata-Version: 2.4
Name: mcurpc
Version: 0.3.1
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. Export or create a target project layout.
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_counter --example counter --layout cmake
mcurpc start build/my_api --example custom --layout flat
```

The available layouts are:

- `arduino`: flattened Arduino-oriented project with the C runtime and `MCURPC` stream wrapper.
- `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
```

This writes:

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

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-driven client exposes generic calls and dynamic
request methods built from the manifest.

```python
from mcurpc.client import build_client
from mcurpc.registry import ManifestRegistry
from mcurpc.transport import SerialTransport

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

manifest = ManifestRegistry().find_by_uuid_version(
    "11111111-1111-4111-8111-111111111111",
    1,
)

client = build_client(manifest, transport)
client.start()
try:
    response = client.call("open_door", {"duration_ms": 1000})
finally:
    client.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 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.
