Files
oc2r-rust/README.md
2025-10-29 18:03:54 +01:00

3.7 KiB
Raw Blame History

oc2r-rust

Thin Rust bindings for the OC2R HLAPI controller. The crate mirrors the behaviour of the Lua devices.lua helper: it speaks JSON over the first VirtIO console (/dev/hvc0) and exposes ergonomic helpers for enumerating devices, inspecting their methods, and invoking them.

Status

  • mirrors devices.lua: list, get, find, find_all, methods, invoke
  • Device wrapper with lazy method cache
  • JsonValue alias you can use to build invocation parameters
  • event subscription (subscribe / unsubscribe) not exposed yet

The low-level framing logic follows the Java implementation in RPCDeviceBusAdapter: messages are framed with leading/trailing NUL bytes and the console is switched into raw mode before use.

Using the library

Add the crate to your workspace and interact with it directly:

use oc2r_rust::{DeviceBus, Result, DEFAULT_DEVICE_PATH};

fn main() -> Result<()> {
    let mut bus = DeviceBus::connect(DEFAULT_DEVICE_PATH)?;

    for device in bus.list()? {
        println!("deviceId: {}", device.device_id);
        println!("  types: {}", device.type_names.join(", "));
    }

    // Invoke a method on the first device as an example.
    if let Some(mut device) = bus.device("some-device-id")? {
        let response = device.call("help", ())?;
        println!("help(): {response:?}");
    }

    Ok(())
}

Dynamic method calls

Device::call (and the matching DeviceBus::call) accept anything that implements IntoJsonArgs, so you can stay close to the Lua ergonomics:

device.call("setRedstoneOutput", ("east", 15_u8))?;
device.call("getRedstoneInput", ("north",))?;

Typed helpers

For common peripherals you can rely on typed wrappers. The redstone helper mirrors the behaviour of redstone.lua and performs basic clamping/parsing:

use oc2r_rust::devices::redstone::{Redstone, Side};
use oc2r_rust::{DeviceBus, Result, DEFAULT_DEVICE_PATH};

fn main() -> Result<()> {
    let mut bus = DeviceBus::connect(DEFAULT_DEVICE_PATH)?;
    let mut redstone = Redstone::attach(&mut bus)?
        .expect("no redstone interface attached");

    redstone.set_output_state(Side::East, true)?;
    println!("in: {}", redstone.input(Side::East)?);
    Ok(())
}

Building the examples

Two runnable examples live in examples/:

  • list-devices enumerate devices and pretty-print their metadata
  • invoke-method list available methods for the first device and invoke one
  • redstone toggle/query a redstone interface, mirroring redstone.lua

The OC2R VM does not ship a Rust toolchain, so build the binaries on your host and copy them into the VM:

# once
cargo install cross --git https://github.com/cross-rs/cross

# build release binaries
cross build --release --example list-devices
cross build --release --example invoke-method
cross build --release --example redstone

The resulting executables live under target/<triple>/release/examples/. Copy them into Minux using your preferred workflow—an Import/Export card in Minecraft works well if you drop the binary onto the card outside the VM. Once the file is on disk (e.g. /usr/bin/), you can execute it from the shell.

Architecture overview

  • bus.rs high-level DeviceBus/Device helpers
  • transport.rs POSIX plumbing (termios, poll, read/write)
  • rpc.rs miniserde data structures for OC2R messages
  • error.rs shared error type and Result<T> alias

DeviceBus flushes the descriptor before sending each request, matching the behaviour of both Lua and Java implementations and preventing stale replies from leaking into new invocations.