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 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.dispatch_request_frame(txn_id, unit_id_slave_addr, &frame)?;
61
62 Ok(())
63 }
64
65 /// Sends a Read Discrete Inputs request for a single input.
66 ///
67 /// # Parameters
68 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
69 /// does not natively use transaction IDs, the stack preserves the ID provided in
70 /// the request and returns it here to allow for asynchronous tracking.
71 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
72 /// - `unit_id`: if transport is tcp
73 /// - `slave_addr`: if transport is serial
74 /// - `address`: The exact address of the single input to read.
75 ///
76 /// # Returns
77 /// `Ok(())` if the request was successfully enqueued and transmitted.
78 ///
79 /// # Errors
80 /// Returns `Err(MbusError::BroadcastNotAllowed)` if attempting to read from address `0` (Broadcast).
81 #[must_use = "request submission errors should be handled; the request may not have been queued/sent"]
82 pub fn read_single_discrete_input(
83 &mut self,
84 txn_id: u16,
85 unit_id_slave_addr: UnitIdOrSlaveAddr,
86 address: u16,
87 ) -> Result<(), MbusError> {
88 if unit_id_slave_addr.is_broadcast() {
89 return Err(MbusError::BroadcastNotAllowed); // Modbus forbids broadcast Read operations
90 }
91
92 let frame = discrete_input::service::ServiceBuilder::read_discrete_inputs(
93 txn_id,
94 unit_id_slave_addr.get(),
95 address,
96 1,
97 TRANSPORT::TRANSPORT_TYPE,
98 )?;
99
100 self.add_an_expectation(
101 txn_id,
102 unit_id_slave_addr,
103 &frame,
104 OperationMeta::Single(Single {
105 address, // Address of the single discrete input
106 value: 0, // Value is not relevant for read requests
107 }),
108 Self::handle_read_discrete_inputs_response,
109 )?;
110
111 self.dispatch_request_frame(txn_id, unit_id_slave_addr, &frame)?;
112
113 Ok(())
114 }
115}