Skip to main content

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}