#model #generated #bit-field

sunspec

SunSpec 1.1 compliant library with tokio support

12 releases (7 breaking)

0.8.0 Dec 10, 2024
0.7.1 Nov 6, 2024
0.4.0 Mar 20, 2024
0.3.1 Dec 21, 2023
0.2.0 Nov 21, 2023

#291 in Network programming

Download history 51/week @ 2024-12-13 8/week @ 2025-01-03 10/week @ 2025-01-10 24/week @ 2025-01-17 18/week @ 2025-01-24 49/week @ 2025-01-31 20/week @ 2025-02-07 52/week @ 2025-02-14 111/week @ 2025-02-21 107/week @ 2025-02-28 42/week @ 2025-03-07 134/week @ 2025-03-14 210/week @ 2025-03-21 68/week @ 2025-03-28

465 downloads per month

MIT/Apache

1MB
21K SLoC

SunSpec Rust Implementation

Latest Version CI Unsafe forbidden Rust 1.75+

This Rust crate contains code for accessing SunSpec compliant devices in a safe and convenient way.

Highlights

  • Pure Rust library
  • No unsafe code
  • Panic free
  • All communication is abstracted via traits making it runtime agnostic
  • Supports Modbus TCP and RTU (via tokio-modbus).
  • Implements "Device Information Model Discovery" as defined in the SunSpec specification.
  • Fully typed models generated from the JSON files contained in the SunSpec models repository
  • Fully typed enums
  • Fully typed bitfields
  • Fully documented. Even the generated models.
  • Reading of complete models in a single request.
⚠️ Nested and repeating groups are not supported, yet.

Features

Feature Description Extra dependencies Default
tokio Enable tokio_modbus support tokio-modbus, tokio/time yes
serde Enable serde support serde, bitflags/serde yes

Examples

The examples directory in the code repository contains the unabridged code.

Example code for accessing data from a three phase inverter using the model 103

use std::{error::Error, net::SocketAddr, time::Duration};

use clap::Parser;
use itertools::Itertools;
use sunspec::{
    client::{AsyncClient, Config},
    models::{model1::Model1, model103::Model103},
};
use tokio::time::sleep;
use tokio_modbus::client::tcp::connect;

#[derive(Parser)]
struct Args {
    addr: SocketAddr,
    device_id: u8,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let args = Args::parse();

    let client = AsyncClient::new(connect(args.addr).await?, Config::default());
    let device = client.device(args.device_id).await?;

    let m1: Model1 = device.read_model().await?;

    println!("Manufacturer: {}", m1.mn);
    println!("Model: {}", m1.md);
    println!("Version: {}", m1.vr.as_deref().unwrap_or("(unspecified)"));
    println!("Serial Number: {}", m1.sn);

    println!(
        "Supported models: {}",
        device
            .models
            .supported_model_ids()
            .iter()
            .map(|id| id.to_string())
            .join(", ")
    );

    loop {
        let m103: Model103 = device.read_model().await?;
        let w = m103.w as f32 * 10f32.powf(m103.w_sf.into());
        let wh = m103.wh as f32 * 10f32.powf(m103.wh_sf.into());
        println!("{:12.3} kWh {:9.3} kW", wh / 1000.0, w / 1000.0,);
        sleep(Duration::from_secs(1)).await;
    }
}

FAQ

How does this crate differ from crates like tokio-sunspec, sunspec-models, sunspec_rs?

  • This crate generates all code using Rust code via the official SunSpec models repository with a code generator that was written in Rust, too.

  • All generated models are plain Rust structs. A single Modbus call can return the complete data for a model rather than having to fetch points individually.

  • All public types are documented. Even the generated models.

License

Licensed under either of

at your option.

Dependencies

~2.8–9MB
~78K SLoC