Crate rodbus

source ·
Expand description

Commercial library by Step Function I/O

A high-performance implementation of the Modbus protocol using Tokio and Rust’s async/await syntax.

Features

  • Panic-free parsing
  • Correctness and compliance to the specification
  • Built-in logging and protocol decoding
  • Automatic connection management with configurable reconnect strategy
  • Scalable performance using Tokio’s multi-threaded executor
  • TLS is implemented using rustls not openssl
  • Model-generated bindings for C, C++, Java, and .NET Core
  • Runs on all platforms and operating systems supported by the Tokio runtime:
    • Official support for: Windows x64 and Linux x64, AArch64, ARMv7 and ARMv6
    • Unofficial support: MacOS, PowerPC, MIPS, FreeBSD, and others

Supported Modes

  • TCP, RTU (serial), and Modbus security (TLS) with and without X.509 extension containing the user role.
  • Client and server

Function Codes

The client and server examples demonstrate simple usage of the API.

The following function codes are supported:

  • Read Coils (0x01)
  • Read Discrete Inputs (0x02)
  • Read Holding Registers (0x03)
  • Read Input Registers (0x04)
  • Write Single Coil (0x05)
  • Write Single Register (0x06)
  • Write Multiple Coils (0x0F)
  • Write Multiple Registers (0x10)

License

This library is publicly available under a non-commercial / non-production license. Refer to LICENSE.txt for the terms of this non-commercial license.

This software is publicly available, but is not “open source”. You must purchase a commercial license to use this software for profit.

Please inquire about commercial licensing on our website:

https://stepfunc.io/contact/

Cargo Features

Default features can be disabled at compile time:

  • tls - Build the library with support for TLS (secure Modbus)
  • serial - Build the library with support for Modbus RTU and serial ports

Bindings

Bindings in C, C++, java, and .NET Core are available for this library. See the documentation for more details.

Example Client

A simple client application that periodically polls for some Coils

use rodbus::*;
use rodbus::client::*;

use std::net::SocketAddr;
use std::time::Duration;
use std::str::FromStr;


#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

   let mut channel = spawn_tcp_client_task(
       HostAddr::ip("127.0.0.1".parse()?, 502),
       10,
       default_retry_strategy(),
       DecodeLevel::default(),
       None
   );

   channel.enable().await?;

   let param = RequestParam::new(
       UnitId::new(0x02),
       Duration::from_secs(1),
   );

   // try to poll for some coils every 3 seconds
   loop {
       match channel.read_coils(param, AddressRange::try_from(0, 5).unwrap()).await {
           Ok(values) => {
               for x in values {
                   println!("index: {} value: {}", x.index, x.value)
               }
           }
           Err(err) => println!("Error: {:?}", err)
       }

       tokio::time::sleep(std::time::Duration::from_secs(3)).await
   }
}

Example Server

use rodbus::*;
use rodbus::server::*;

use std::net::SocketAddr;
use std::str::FromStr;

use tokio::net::TcpListener;

struct CoilsOnlyHandler {
   pub coils: [bool; 10]
}

impl CoilsOnlyHandler {
   fn new() -> Self {
       Self {
           coils: [false; 10]
       }
   }
}

impl RequestHandler for CoilsOnlyHandler {
   fn read_coil(&self, address: u16) -> Result<bool, ExceptionCode> {
       self.coils.get(0).to_result()
   }
}

#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

   let handler = CoilsOnlyHandler::new().wrap();

   // map unit ids to a handler for processing requests
   let map = ServerHandlerMap::single(UnitId::new(1), handler.clone());

   // spawn a server to handle connections onto its own task
   // if the handle _server is dropped, the server shuts down
   let _server = rodbus::server::spawn_tcp_server_task(
       1,
       SocketAddr::from_str("127.0.0.1:502")?,
       map,
       AddressFilter::Any,
       DecodeLevel::default(),
   ).await?;

   let mut next = tokio::time::Instant::now();

   // toggle all coils every couple of seconds
   loop {
       next += tokio::time::Duration::from_secs(2);
       {
           let mut guard = handler.lock().unwrap();
           for c in &mut guard.coils {
               *c = !*c;
           }
       }
       tokio::time::sleep_until(next).await;
   }
}

Modules

Client API
Public constant values related to the Modbus specification
Server API

Structs

Start and count tuple used when making various requests Cannot be constructed with invalid start/count
Zero-copy type used to iterate over a collection of bits
Controls the decoding of transmitted and received data at the application, frame, and physical layer
Value and its address
Represents a result that may be computed synchronously or asynchronously by user code.
Zero-copy type used to iterate over a collection of registers
Serial port settings
The task processing requests has terminated
Modbus unit identifier, just a type-safe wrapper around u8

Enums

Errors that occur while parsing requests and responses
Controls how transmitted and received message at the application layer are decoded at the INFO log level
Number of bits per character
Exception codes defined in the Modbus specification
Flow control modes
Controls how the transmitted and received frames are decoded at the INFO log level
Errors that occur while parsing a frame off a stream (TCP or serial)
Errors that indicate faulty logic in the library itself if they occur
Errors that can be produced when validating start/count
Errors that result because of bad request parameter
Parity checking modes
Controls how data transmitted at the physical layer (TCP, serial, etc) is logged
Top level error type for the client API
Number of stop bits

Constants

Current version of the library

Traits

Trait that controls how the channel retries failed connect (TCP/TLS) or open (serial) attempts

Functions

Return a RetryStrategy that doubles on failure up to a maximum value