mbus_client/services/discrete_input/apis.rs
1use crate::{
2 app::DiscreteInputResponse,
3 services::{ClientCommon, ClientServices, Multiple, OperationMeta, Single, discrete_input},
4};
5use mbus_core::{
6 errors::MbusError,
7 transport::{Transport, UnitIdOrSlaveAddr},
8};
9
10impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
11where
12 TRANSPORT: Transport,
13 APP: ClientCommon + DiscreteInputResponse,
14{
15 /// Sends a Read Discrete Inputs request to the specified unit ID and address range, and records the expected response.
16 ///
17 /// # Parameters
18 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
19 /// does not natively use transaction IDs, the stack preserves the ID provided in
20 /// the request and returns it here to allow for asynchronous tracking.
21 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
22 /// - `unit_id`: if transport is tcp
23 /// - `slave_addr`: if transport is serial
24 /// - `address`: The starting address of the inputs to read.
25 /// - `quantity`: The number of inputs to read.
26 ///
27 /// # Returns
28 /// `Ok(())` if the request was successfully enqueued and transmitted. Or else Error
29 #[must_use = "request submission errors should be handled; the request may not have been queued/sent"]
30 pub fn read_discrete_inputs(
31 &mut self,
32 txn_id: u16,
33 unit_id_slave_addr: UnitIdOrSlaveAddr,
34 address: u16,
35 quantity: u16,
36 ) -> Result<(), MbusError> {
37 if unit_id_slave_addr.is_broadcast() {
38 return Err(MbusError::BroadcastNotAllowed); // Modbus forbids broadcast Read operations
39 }
40
41 let frame = discrete_input::service::ServiceBuilder::read_discrete_inputs(
42 txn_id,
43 unit_id_slave_addr.get(),
44 address,
45 quantity,
46 self.transport.transport_type(),
47 )?;
48
49 self.add_an_expectation(
50 txn_id,
51 unit_id_slave_addr,
52 &frame,
53 OperationMeta::Multiple(Multiple {
54 address, // Starting address of the read operation
55 quantity, // Number of discrete inputs to read
56 }),
57 Self::handle_read_discrete_inputs_response,
58 )?;
59
60 self.transport
61 .send(&frame)
62 .map_err(|_e| MbusError::SendFailed)?;
63
64 Ok(())
65 }
66
67 /// Sends a Read Discrete Inputs request for a single input.
68 ///
69 /// # Parameters
70 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
71 /// does not natively use transaction IDs, the stack preserves the ID provided in
72 /// the request and returns it here to allow for asynchronous tracking.
73 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
74 /// - `unit_id`: if transport is tcp
75 /// - `slave_addr`: if transport is serial
76 /// - `address`: The exact address of the single input to read.
77 ///
78 /// # Returns
79 /// `Ok(())` if the request was successfully enqueued and transmitted.
80 ///
81 /// # Errors
82 /// Returns `Err(MbusError::BroadcastNotAllowed)` if attempting to read from address `0` (Broadcast).
83 #[must_use = "request submission errors should be handled; the request may not have been queued/sent"]
84 pub fn read_single_discrete_input(
85 &mut self,
86 txn_id: u16,
87 unit_id_slave_addr: UnitIdOrSlaveAddr,
88 address: u16,
89 ) -> Result<(), MbusError> {
90 if unit_id_slave_addr.is_broadcast() {
91 return Err(MbusError::BroadcastNotAllowed); // Modbus forbids broadcast Read operations
92 }
93
94 let frame = discrete_input::service::ServiceBuilder::read_discrete_inputs(
95 txn_id,
96 unit_id_slave_addr.get(),
97 address,
98 1,
99 self.transport.transport_type(),
100 )?;
101
102 self.add_an_expectation(
103 txn_id,
104 unit_id_slave_addr,
105 &frame,
106 OperationMeta::Single(Single {
107 address, // Address of the single discrete input
108 value: 0, // Value is not relevant for read requests
109 }),
110 Self::handle_read_discrete_inputs_response,
111 )?;
112
113 self.transport
114 .send(&frame)
115 .map_err(|_e| MbusError::SendFailed)?;
116
117 Ok(())
118 }
119}