modbus_rtu/
request.rs

1/// Represents an outgoing Modbus RTU request along with its metadata.
2#[derive(Debug, Clone, PartialEq, Eq)]
3pub struct Request<'a> {
4    modbus_id: u8,
5    function: &'a crate::Function,
6    timeout: core::time::Duration,
7}
8
9impl<'a> Request<'a> {
10    /// Creates a new request for the specified Modbus slave, function, and timeout.
11    ///
12    /// ---
13    /// # Examples
14    /// ```rust
15    /// use modbus_rtu::{Function, Request};
16    ///
17    /// let func = Function::ReadCoils { starting_address: 0x0000, quantity: 2 };
18    /// let request = Request::new(0x01, &func, std::time::Duration::from_millis(200));
19    ///
20    /// assert_eq!(request.modbus_id(), 0x01);
21    /// assert_eq!(request.timeout(), std::time::Duration::from_millis(200));
22    /// ```
23    ///
24    pub const fn new(
25        modbus_id: u8,
26        function: &'a crate::Function,
27        timeout: std::time::Duration,
28    ) -> Self {
29        Self {
30            modbus_id,
31            function,
32            timeout,
33        }
34    }
35
36    /// Returns the Modbus slave identifier targeted by this request.
37    pub const fn modbus_id(&self) -> u8 {
38        self.modbus_id
39    }
40
41    /// Updates the Modbus slave identifier targeted by this request.
42    pub fn set_modbus_id(&mut self, modbus_id: u8) {
43        self.modbus_id = modbus_id;
44    }
45
46    /// Returns the function payload that will be issued with this request.
47    pub const fn function(&self) -> &crate::Function {
48        self.function
49    }
50
51    /// Replaces the function payload associated with this request.
52    pub fn set_function(&mut self, function: &'a crate::Function) {
53        self.function = function;
54    }
55
56    /// Returns the timeout associated with this request.
57    pub const fn timeout(&self) -> std::time::Duration {
58        self.timeout
59    }
60
61    /// Updates the timeout associated with this request.
62    pub fn set_timeout(&mut self, timeout: std::time::Duration) {
63        self.timeout = timeout;
64    }
65
66    pub fn is_broadcasting(&self) -> bool {
67        self.modbus_id() == 0
68    }
69
70    /// Serializes the request into a Modbus RTU frame containing the device id,
71    /// function payload, and CRC footer.
72    ///
73    /// ---
74    /// # Errors
75    /// Returns [`RequestPacketError`](crate::error::RequestPacketError) if the inner
76    /// function cannot be encoded within the 256-byte packet limit.
77    ///
78    /// ---
79    /// # Examples
80    /// ```rust
81    /// use modbus_rtu::{Function, Request};
82    ///
83    /// let func = Function::WriteSingleRegister { address: 0x0010, value: 0xABCD };
84    /// let request = Request::new(0x11, &func, std::time::Duration::from_millis(100));
85    /// let frame = request.to_bytes().unwrap();
86    ///
87    /// assert_eq!(&frame[..], &[0x11, 0x06, 0x00, 0x10, 0xAB, 0xCD, 0x34, 0x3A]);
88    /// ```
89    ///
90    pub fn to_bytes(&self) -> Result<Box<[u8]>, crate::error::RequestPacketError> {
91        use crate::FunctionKind::*;
92        if self.is_broadcasting()
93            && [
94                ReadCoils,
95                ReadDiscreteInputs,
96                ReadHoldingRegisters,
97                ReadInputRegisters,
98            ]
99            .contains(&self.function().kind())
100        {
101            return Err(crate::error::RequestPacketError::CannotBroadcast);
102        }
103        let mut buf: Vec<u8> = Vec::new();
104        buf.push(self.modbus_id());
105        let bytes = self.function().to_bytes()?;
106        buf.extend_from_slice(&bytes);
107        let crc_bytes = crate::crc::generate(&buf[0..buf.len()]);
108        buf.extend_from_slice(&crc_bytes.to_le_bytes());
109        Ok(buf.into_boxed_slice())
110    }
111}