rustmod_core/lib.rs
1//! Modbus protocol encoding and framing in pure Rust.
2//!
3//! `rustmod-core` provides zero-copy, `no_std`-compatible encoding and decoding
4//! of Modbus PDUs and TCP/RTU frames.
5//!
6//! # Supported Function Codes
7//!
8//! - FC01 Read Coils
9//! - FC02 Read Discrete Inputs
10//! - FC03 Read Holding Registers
11//! - FC04 Read Input Registers
12//! - FC05 Write Single Coil
13//! - FC06 Write Single Register
14//! - FC07 Read Exception Status
15//! - FC08 Diagnostics
16//! - FC15 Write Multiple Coils
17//! - FC16 Write Multiple Registers
18//! - FC22 Mask Write Register
19//! - FC23 Read/Write Multiple Registers
20//! - FC24 Read FIFO Queue
21//! - Custom function codes via `FunctionCode::Custom`
22//!
23//! # Design
24//!
25//! All encoding uses caller-owned `&mut [u8]` buffers via [`encoding::Writer`],
26//! and all decoding uses zero-copy [`encoding::Reader`] over `&[u8]` slices.
27//! No heap allocation is required (the `alloc` feature adds owned request types
28//! for convenience).
29
30#![cfg_attr(not(feature = "std"), no_std)]
31#![forbid(unsafe_code)]
32
33#[cfg(feature = "alloc")]
34extern crate alloc;
35#[cfg(feature = "std")]
36extern crate std;
37
38pub mod encoding;
39pub mod error;
40pub mod frame;
41pub mod pdu;
42
43pub use error::{DecodeError, EncodeError};
44
45/// A Modbus unit identifier (station address).
46///
47/// Valid addresses are 0–247, where 0 is the broadcast address.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct UnitId(u8);
50
51impl UnitId {
52 /// The broadcast address (0). Write requests sent to this address are
53 /// processed by all devices on the bus; no response is returned.
54 pub const BROADCAST: Self = Self(0);
55
56 /// The minimum valid unicast address (1).
57 pub const MIN: Self = Self(1);
58
59 /// The maximum valid unicast address (247).
60 pub const MAX: Self = Self(247);
61
62 /// Create a `UnitId` from a raw `u8` value.
63 #[must_use]
64 pub const fn new(value: u8) -> Self {
65 Self(value)
66 }
67
68 /// Return the raw `u8` value.
69 #[must_use]
70 pub const fn as_u8(self) -> u8 {
71 self.0
72 }
73}
74
75impl From<u8> for UnitId {
76 fn from(value: u8) -> Self {
77 Self(value)
78 }
79}
80
81impl From<UnitId> for u8 {
82 fn from(unit_id: UnitId) -> Self {
83 unit_id.0
84 }
85}
86
87impl core::fmt::Display for UnitId {
88 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89 write!(f, "{}", self.0)
90 }
91}