async_modbus/
request.rs

1//! Modbus request messages. You can use [`zerocopy::IntoBytes`] to convert
2//! them into byte buffers for sending.
3//!
4//! ```
5//! # use async_modbus::request::WriteHolding;
6//! # use hex_literal::hex;
7//! use async_modbus::zerocopy::IntoBytes;
8//!
9//! let message = WriteHolding::new(0x01, 0x10BC, 12345);
10//! assert_eq!(message.as_bytes(), hex!("01 06 10 BC 30 39 98 FC"));
11//! ```
12
13use super::util::modbus_message;
14use zerocopy::{IntoBytes, big_endian};
15use zerocopy_derive::*;
16
17modbus_message! {
18    /// Write single holding register request
19    WriteHolding {
20        function_code: 0x06,
21        register: big_endian::U16,
22        value: big_endian::U16,
23    }
24}
25
26impl WriteHolding {
27    /// Create a new write holding register request
28    pub fn new(addr: u8, register: u16, value: u16) -> Self {
29        Self::new_inner(addr, register.into(), value.into())
30    }
31}
32
33modbus_message! {
34    /// Read holding registers request
35    ReadHoldings {
36        function_code: 0x03,
37        starting_register: big_endian::U16,
38        n_registers: big_endian::U16,
39    }
40}
41
42impl ReadHoldings {
43    /// Create a new read holding registers request
44    pub fn new(addr: u8, starting_register: u16, n_registers: u16) -> Self {
45        Self::new_inner(addr, starting_register.into(), n_registers.into())
46    }
47}
48
49modbus_message! {
50    /// Write multiple holding registers request
51    WriteHoldings<const N: usize> {
52        function_code: 0x10,
53        starting_register: big_endian::U16,
54        n_registers: big_endian::U16,
55        data_bytes: u8,
56        data: [big_endian::U16; N],
57    }
58}
59
60impl<const N: usize> WriteHoldings<N> {
61    /// Create a new write multiple holding registers request
62    pub fn new(addr: u8, starting_register: u16, data: [u16; N]) -> Self {
63        assert!(
64            N <= 127,
65            "cannot write more than 127 registers in a single request"
66        );
67
68        Self::new_inner(
69            addr,
70            starting_register.into(),
71            big_endian::U16::new(N as u16),
72            (N as u8) * 2,
73            data.map(big_endian::U16::new),
74        )
75    }
76}
77
78modbus_message! {
79    /// Read input registers request
80    ReadInputs {
81        function_code: 0x04,
82        starting_register: big_endian::U16,
83        n_registers: big_endian::U16,
84    }
85}
86
87impl ReadInputs {
88    /// Create a new read input registers request
89    pub fn new(addr: u8, starting_register: u16, n_registers: u16) -> Self {
90        Self::new_inner(addr, starting_register.into(), n_registers.into())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use hex_literal::hex;
97    use zerocopy::IntoBytes;
98
99    use super::*;
100
101    #[test]
102    fn test_write_holding_register() {
103        let msg = WriteHolding::new(0x01, 0x1001, 0x03E8);
104        assert_eq!(msg.as_bytes(), hex!("01 06 10 01 03 E8 DC 74"),);
105    }
106
107    #[test]
108    fn test_read_holding_registers() {
109        let msg = ReadHoldings::new(0x01, 0x1001, 1000);
110        assert_eq!(msg.as_bytes(), hex!("01 03 10 01 03 E8 10 74"),);
111    }
112
113    #[test]
114    #[should_panic(expected = "cannot write more than 127 registers in a single request")]
115    fn too_much_data() {
116        WriteHoldings::<128>::new(0x01, 0x1001, [0u16; 128]);
117    }
118}