Skip to main content

mbus_client/services/file_record/
apis.rs

1use crate::{
2    app::FileRecordResponse,
3    services::{
4        ClientCommon, ClientServices, OperationMeta,
5        file_record::{self, SubRequest},
6    },
7};
8use mbus_core::{
9    errors::MbusError,
10    transport::{Transport, UnitIdOrSlaveAddr},
11};
12
13impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
14where
15    TRANSPORT: Transport,
16    APP: ClientCommon + FileRecordResponse,
17{
18    /// Sends a Read File Record request (Function Code 0x14).
19    ///
20    /// This function performs a data reference operation to allow reading of "File Records".
21    /// A Modbus file is a collection of records. Each file can contain up to 10000 records
22    /// (addressed 0000 to 9999 decimal).
23    ///
24    /// # Parameters
25    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
26    ///   does not natively use transaction IDs, the stack preserves the ID provided in
27    ///   the request and returns it here to allow for asynchronous tracking.
28    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
29    ///   - `unit_id`: if transport is tcp
30    ///   - `slave_addr`: if transport is serial/// - `sub_request`: A structure containing one or more sub-requests. Each sub-request
31    ///     specifies the file number, record number, and the number of registers to read.
32    ///
33    /// # Returns
34    /// - `Ok(())`: If the request was successfully built, the expectation was queued,
35    ///   and the frame was transmitted.
36    /// - `Err(MbusError)`: If the address is a broadcast address (not allowed for FC 0x14),
37    ///   if the PDU exceeds the maximum allowed size, or if transport fails.
38    #[must_use = "request submission errors should be handled; the request may not have been queued/sent"]
39    pub fn read_file_record(
40        &mut self,
41        txn_id: u16,
42        unit_id_slave_addr: UnitIdOrSlaveAddr,
43        sub_request: &SubRequest,
44    ) -> Result<(), MbusError> {
45        // Modbus protocol specification: Broadcast is not supported for Read File Record.
46        if unit_id_slave_addr.is_broadcast() {
47            return Err(MbusError::broadcast_not_allowed());
48        }
49
50        // Construct the ADU frame (MBAP/Serial Header + PDU + CRC/LRC if applicable)
51        let frame = file_record::service::ServiceBuilder::read_file_record(
52            txn_id,
53            unit_id_slave_addr.get(),
54            sub_request,
55            self.transport.transport_type(),
56        )?;
57
58        // Register an expectation so the client knows how to route the incoming response.
59        self.add_an_expectation(
60            txn_id,
61            unit_id_slave_addr,
62            &frame,
63            OperationMeta::Other,
64            Self::handle_read_file_record_response,
65        )?;
66
67        // Dispatch the frame through the underlying transport (TCP/RTU/ASCII).
68        self.transport
69            .send(&frame)
70            .map_err(|_e| MbusError::SendFailed)?;
71        Ok(())
72    }
73
74    /// Sends a Write File Record request (Function Code 0x15).
75    ///
76    /// This function performs a data reference operation to allow writing of "File Records".
77    /// The request can contain multiple sub-requests, each writing a sequence of registers
78    /// to a specific file and record.
79    ///
80    /// # Parameters
81    /// - `txn_id`: Transaction ID for tracking the request/response pair.
82    /// - `unit_id_slave_addr`: The target Modbus unit ID (TCP) or slave address (Serial).
83    /// - `sub_request`: A structure containing one or more sub-requests. Each sub-request
84    ///   specifies the file number, record number, record length, and the actual data to write.
85    ///
86    /// # Returns
87    /// - `Ok(())`: If the request was successfully built and transmitted.
88    /// - `Err(MbusError)`: If broadcast is attempted, if the PDU is malformed, or transport fails.
89    #[must_use = "request submission errors should be handled; the request may not have been queued/sent"]
90    pub fn write_file_record(
91        &mut self,
92        txn_id: u16,
93        unit_id_slave_addr: UnitIdOrSlaveAddr,
94        sub_request: &SubRequest,
95    ) -> Result<(), MbusError> {
96        // Modbus protocol specification: Broadcast is not supported for Write File Record.
97        if unit_id_slave_addr.is_broadcast() {
98            return Err(MbusError::broadcast_not_allowed()); // Modbus forbids broadcast for Write File Record
99        }
100
101        // Construct the ADU frame using the service builder.
102        let frame = file_record::service::ServiceBuilder::write_file_record(
103            txn_id,
104            unit_id_slave_addr.get(),
105            sub_request,
106            self.transport.transport_type(),
107        )?;
108
109        // Register the expectation for the response handler.
110        self.add_an_expectation(
111            txn_id,
112            unit_id_slave_addr,
113            &frame,
114            OperationMeta::Other,
115            Self::handle_write_file_record_response,
116        )?;
117
118        // Send the compiled frame.
119        self.transport
120            .send(&frame)
121            .map_err(|_e| MbusError::SendFailed)?;
122        Ok(())
123    }
124}