1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
//! # Modbus Discrete Input Models
//!
//! This module defines the data structures for handling **Discrete Inputs** (Function Code 0x02).
//!
//! In Modbus, Discrete Inputs are single-bit, read-only data objects. They are typically used
//! to represent digital inputs from physical devices, such as limit switches, sensor states,
//! or status indicators.
//!
//! ## Key Components
//! - [`DiscreteInputs`]: A container for a block of bit-packed input states.
//! - [`MAX_DISCRETE_INPUTS_PER_PDU`]: The protocol limit for a single read operation.
//!
//! ## Data Packing¯
//! Discrete inputs are packed into bytes in the Modbus PDU. The first input requested
//! is stored in the Least Significant Bit (LSB) of the first data byte.
//!
//! ### Example
//! If 3 inputs are read (Address 10, 11, 12) and the first and third are ON:
//! - Byte 0: `0000 0101` (Binary) -> `0x05` (Hex)
//! - Bit 0 (Address 10): 1 (ON)
//! - Bit 1 (Address 11): 0 (OFF)
//! - Bit 2 (Address 12): 1 (ON)
use crateMbusError;
/// The maximum number of discrete inputs that can be requested in a single Read Discrete Inputs (FC 02) PDU.
///
/// According to the Modbus Application Protocol Specification V1.1b3, the quantity of inputs
/// must be between 1 and 2000 (0x07D0).
pub const MAX_DISCRETE_INPUTS_PER_PDU: usize = 2000;
/// The maximum number of bytes required to store the bit-packed states of 2000 discrete inputs.
///
/// Calculated as `ceil(2000 / 8) = 250` bytes.
pub const MAX_DISCRETE_INPUT_BYTES: usize = MAX_DISCRETE_INPUTS_PER_PDU.div_ceil;
/// A collection of discrete input states retrieved from a Modbus server.
///
/// This structure maintains the context of the read operation (starting address and quantity)
/// and stores the actual bit-packed values in a memory-efficient `heapless::Vec`, making it
/// suitable for `no_std` and embedded environments.
///
/// Use the [`value()`](Self::value) method to extract individual boolean states without
/// manually performing bitwise operations.
///
/// # Internal Representation
/// The `values` array stores these discrete input states. Each byte in `values` holds 8 input states,
/// where the least significant bit (LSB) of the first byte (`values[0]`) corresponds to the
/// `from_address`, the next bit to `from_address + 1`, and so on. This bit-packing is efficient
/// for memory usage and network transmission.
///
/// The `MAX_DISCRETE_INPUT_BYTES` constant ensures that the `values` array has enough space to
/// accommodate the maximum possible number of discrete inputs allowed in a single Modbus PDU
/// (`MAX_DISCRETE_INPUTS_PER_PDU`).
///
/// # Examples
///
/// ```rust
/// use mbus_core::models::discrete_input::{DiscreteInputs, MAX_DISCRETE_INPUT_BYTES};
/// use mbus_core::errors::MbusError;
///
/// // Initialize a block of 8 discrete inputs starting at Modbus address 100.
/// // Initially all inputs are OFF (0).
/// let mut inputs = DiscreteInputs::new(100, 8).unwrap();
///
/// // Verify initial state: all inputs are false
/// assert_eq!(inputs.value(100).unwrap(), false);
/// assert_eq!(inputs.value(107).unwrap(), false);
///
/// // Simulate receiving data where inputs at offsets 0 and 2 are ON (0b0000_0101)
/// let received_data = [0x05, 0x00, 0x00, 0x00]; // Only the first byte is relevant for 8 inputs
/// inputs = inputs.with_values(&received_data, 8).expect("Valid quantity and data");
///
/// // Read individual input values
/// assert_eq!(inputs.value(100).unwrap(), true); // Address 100 (offset 0) -> LSB of 0x05 is 1
/// assert_eq!(inputs.value(101).unwrap(), false); // Address 101 (offset 1) -> next bit is 0
/// assert_eq!(inputs.value(102).unwrap(), true); // Address 102 (offset 2) -> next bit is 1
/// assert_eq!(inputs.value(107).unwrap(), false); // Address 107 (offset 7) -> MSB of 0x05 is 0
///
/// // Accessing values out of bounds will return an error
/// assert_eq!(inputs.value(99), Err(MbusError::InvalidAddress));
/// assert_eq!(inputs.value(108), Err(MbusError::InvalidAddress));
///
/// // Get the raw bit-packed bytes (only the first byte is active for 8 inputs)
/// assert_eq!(inputs.values(), &[0x05]);
/// ```