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}