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.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            self.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}