1use super::*;
5use crate::error::*;
6
7#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Coils<'c> {
11 pub(crate) data: RawData<'c>,
12 pub(crate) quantity: usize,
13}
14
15impl<'c> Coils<'c> {
16 pub fn from_bools(bools: &[bool], target: &'c mut [u8]) -> Result<Self, Error> {
18 if bools.is_empty() {
19 return Err(Error::BufferSize);
20 }
21 pack_coils(bools, target)?;
22 Ok(Coils {
23 data: target,
24 quantity: bools.len(),
25 })
26 }
27
28 pub(crate) fn copy_to(&self, buf: &mut [u8]) {
30 let packed_len = self.packed_len();
31 debug_assert!(buf.len() >= packed_len);
32 (0..packed_len).for_each(|idx| {
33 buf[idx] = self.data[idx];
34 });
35 }
36
37 #[must_use]
39 pub const fn len(&self) -> usize {
40 self.quantity
41 }
42
43 #[must_use]
45 pub const fn packed_len(&self) -> usize {
46 packed_coils_len(self.quantity)
47 }
48
49 #[must_use]
51 pub const fn is_empty(&self) -> bool {
52 self.quantity == 0
53 }
54
55 #[must_use]
57 pub const fn get(&self, idx: usize) -> Option<Coil> {
58 if idx + 1 > self.quantity {
59 return None;
60 }
61 Some((self.data[(idx as u16 / 8u16) as usize] >> (idx % 8)) & 0b1 > 0)
62 }
63}
64
65#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))]
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct CoilsIter<'c> {
70 cnt: usize,
71 coils: Coils<'c>,
72}
73
74impl Iterator for CoilsIter<'_> {
75 type Item = Coil;
76
77 fn next(&mut self) -> Option<Self::Item> {
78 let result = self.coils.get(self.cnt);
79 self.cnt += 1;
80 result
81 }
82}
83
84impl<'c> IntoIterator for Coils<'c> {
85 type Item = Coil;
86 type IntoIter = CoilsIter<'c>;
87
88 fn into_iter(self) -> Self::IntoIter {
89 CoilsIter {
90 cnt: 0,
91 coils: self,
92 }
93 }
94}
95
96#[must_use]
98pub const fn bool_to_u16_coil(state: bool) -> u16 {
99 if state { 0xFF00 } else { 0x0000 }
100}
101
102pub const fn u16_coil_to_bool(coil: u16) -> Result<bool, Error> {
104 match coil {
105 0xFF00 => Ok(true),
106 0x0000 => Ok(false),
107 _ => Err(Error::CoilValue(coil)),
108 }
109}
110
111#[must_use]
113pub const fn packed_coils_len(bitcount: usize) -> usize {
114 bitcount.div_ceil(8)
115}
116
117pub fn pack_coils(coils: &[Coil], bytes: &mut [u8]) -> Result<usize, Error> {
121 let packed_size = packed_coils_len(coils.len());
122 if bytes.len() < packed_size {
123 return Err(Error::BufferSize);
124 }
125 coils.iter().enumerate().for_each(|(i, b)| {
126 let v = u8::from(*b);
127 bytes[i / 8] |= v << (i % 8);
128 });
129 Ok(packed_size)
130}
131
132pub fn unpack_coils(bytes: &[u8], count: u16, coils: &mut [Coil]) -> Result<(), Error> {
134 if coils.len() < count as usize {
135 return Err(Error::BufferSize);
136 }
137 (0..count).for_each(|i| {
138 coils[i as usize] = (bytes[(i / 8u16) as usize] >> (i % 8)) & 0b1 > 0;
139 });
140 Ok(())
141}
142
143#[cfg(test)]
144mod tests {
145
146 use super::*;
147
148 #[test]
149 fn from_bool_slice() {
150 let bools: &[bool] = &[true, false, true, true];
151 let buff: &mut [u8] = &mut [0];
152 let coils = Coils::from_bools(bools, buff).unwrap();
153 assert_eq!(coils.len(), 4);
154 let mut iter = coils.into_iter();
155 assert_eq!(iter.next(), Some(true));
156 assert_eq!(iter.next(), Some(false));
157 assert_eq!(iter.next(), Some(true));
158 assert_eq!(iter.next(), Some(true));
159 assert_eq!(iter.next(), None);
160 }
161
162 #[test]
163 fn coils_len() {
164 let coils = Coils {
165 data: &[0, 1, 2],
166 quantity: 5,
167 };
168 assert_eq!(coils.len(), 5);
169 }
170
171 #[test]
172 fn coils_empty() {
173 let coils = Coils {
174 data: &[0, 1, 2],
175 quantity: 0,
176 };
177 assert!(coils.is_empty());
178 }
179
180 #[test]
181 fn coils_get() {
182 let coils = Coils {
183 data: &[0b1],
184 quantity: 1,
185 };
186 assert_eq!(coils.get(0), Some(true));
187 assert_eq!(coils.get(1), None);
188
189 let coils = Coils {
190 data: &[0b01],
191 quantity: 2,
192 };
193 assert_eq!(coils.get(0), Some(true));
194 assert_eq!(coils.get(1), Some(false));
195 assert_eq!(coils.get(2), None);
196
197 let coils = Coils {
198 data: &[0xff, 0b11],
199 quantity: 10,
200 };
201 for i in 0..10 {
202 assert_eq!(coils.get(i), Some(true));
203 }
204 assert_eq!(coils.get(11), None);
205 }
206
207 #[test]
208 fn coils_iter() {
209 let coils = Coils {
210 data: &[0b0101_0011],
211 quantity: 5,
212 };
213 let mut coils_iter = CoilsIter { cnt: 0, coils };
214 assert_eq!(coils_iter.next(), Some(true));
215 assert_eq!(coils_iter.next(), Some(true));
216 assert_eq!(coils_iter.next(), Some(false));
217 assert_eq!(coils_iter.next(), Some(false));
218 assert_eq!(coils_iter.next(), Some(true));
219 assert_eq!(coils_iter.next(), None);
220 }
221
222 #[test]
223 fn coils_into_iter() {
224 let coils = Coils {
225 data: &[0b0101_0011],
226 quantity: 3,
227 };
228 let mut coils_iter = coils.into_iter();
229 assert_eq!(coils_iter.next(), Some(true));
230 assert_eq!(coils_iter.next(), Some(true));
231 assert_eq!(coils_iter.next(), Some(false));
232 assert_eq!(coils_iter.next(), None);
233 }
234
235 #[test]
236 fn iter_over_coils() {
237 let coils = Coils {
238 data: &[0b0101_0011],
239 quantity: 3,
240 };
241 let mut cnt = 0;
242 for _ in coils {
243 cnt += 1;
244 }
245 assert_eq!(cnt, 3);
246 }
247
248 #[test]
249 fn convert_bool_to_coil() {
250 assert_eq!(bool_to_u16_coil(true), 0xFF00);
251 assert_eq!(bool_to_u16_coil(false), 0x0000);
252 }
253
254 #[test]
255 fn convert_coil_to_bool() {
256 assert!(u16_coil_to_bool(0xFF00).unwrap());
257 assert!(!u16_coil_to_bool(0x0000).unwrap());
258 assert_eq!(
259 u16_coil_to_bool(0x1234).err().unwrap(),
260 Error::CoilValue(0x1234)
261 );
262 }
263
264 #[test]
265 fn pack_coils_into_byte_array() {
266 assert_eq!(pack_coils(&[], &mut []).unwrap(), 0);
267 assert_eq!(pack_coils(&[], &mut [0, 0]).unwrap(), 0);
268 assert_eq!(
269 pack_coils(&[true; 2], &mut []).err().unwrap(),
270 Error::BufferSize
271 );
272
273 let buff = &mut [0];
274 assert_eq!(pack_coils(&[true], buff).unwrap(), 1);
275 assert_eq!(buff, &[0b_1]);
276
277 let buff = &mut [0];
278 assert_eq!(pack_coils(&[false], buff).unwrap(), 1);
279 assert_eq!(buff, &[0b_0]);
280
281 let buff = &mut [0];
282 assert_eq!(pack_coils(&[true, false], buff).unwrap(), 1);
283 assert_eq!(buff, &[0b_01]);
284
285 let buff = &mut [0];
286 assert_eq!(pack_coils(&[false, true], buff).unwrap(), 1);
287 assert_eq!(buff, &[0b_10]);
288
289 let buff = &mut [0];
290 assert_eq!(pack_coils(&[true, true], buff).unwrap(), 1);
291 assert_eq!(buff, &[0b_11]);
292
293 let buff = &mut [0];
294 assert_eq!(pack_coils(&[true; 8], buff).unwrap(), 1);
295 assert_eq!(buff, &[0b_1111_1111]);
296
297 let buff = &mut [0];
298 assert_eq!(pack_coils(&[false; 8], buff).unwrap(), 1);
299 assert_eq!(buff, &[0]);
300
301 let buff = &mut [0, 0];
302 assert_eq!(pack_coils(&[true; 9], buff).unwrap(), 2);
303 assert_eq!(buff, &[0xff, 1]);
304 }
305
306 #[test]
307 fn unpack_coils_from_a_byte_array() {
308 assert!(unpack_coils(&[], 0, &mut []).is_ok());
309 assert!(unpack_coils(&[], 0, &mut [false, false]).is_ok());
310 assert!(unpack_coils(&[1, 2, 3], 0, &mut []).is_ok());
311 assert_eq!(
312 unpack_coils(&[], 1, &mut []).err().unwrap(),
313 Error::BufferSize
314 );
315
316 let buff = &mut [false];
317 assert!(unpack_coils(&[0b1], 1, buff).is_ok());
318 assert_eq!(&[true], buff);
319
320 let buff = &mut [false; 2];
321 assert!(unpack_coils(&[0b01], 2, buff).is_ok());
322 assert_eq!(&[true, false], buff);
323
324 let buff = &mut [false; 2];
325 assert!(unpack_coils(&[0b10], 2, buff).is_ok());
326 assert_eq!(&[false, true], buff);
327
328 let buff = &mut [false; 3];
329 assert!(unpack_coils(&[0b101], 3, buff).is_ok());
330 assert_eq!(&[true, false, true], buff);
331
332 let buff = &mut [false; 10];
333 assert!(unpack_coils(&[0xff, 0b11], 10, buff).is_ok());
334 assert_eq!(&[true; 10], buff);
335 }
336}