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}