1use crate::ClientError;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct CoilPoints {
5 start_address: u16,
6 values: Vec<bool>,
7}
8
9impl CoilPoints {
10 pub fn new(start_address: u16, count: usize) -> Self {
11 Self {
12 start_address,
13 values: vec![false; count],
14 }
15 }
16
17 pub fn from_values(start_address: u16, values: Vec<bool>) -> Self {
18 Self {
19 start_address,
20 values,
21 }
22 }
23
24 pub fn start_address(&self) -> u16 {
25 self.start_address
26 }
27
28 pub fn len(&self) -> usize {
29 self.values.len()
30 }
31
32 pub fn is_empty(&self) -> bool {
33 self.values.is_empty()
34 }
35
36 pub fn values(&self) -> &[bool] {
37 &self.values
38 }
39
40 pub fn get(&self, address: u16) -> Option<bool> {
41 let offset = usize::from(address.checked_sub(self.start_address)?);
42 self.values.get(offset).copied()
43 }
44
45 pub fn set(&mut self, address: u16, value: bool) -> Result<(), ClientError> {
46 let offset = usize::from(
47 address
48 .checked_sub(self.start_address)
49 .ok_or(ClientError::InvalidResponse("coil address out of range"))?,
50 );
51 let slot = self
52 .values
53 .get_mut(offset)
54 .ok_or(ClientError::InvalidResponse("coil address out of range"))?;
55 *slot = value;
56 Ok(())
57 }
58
59 pub fn apply_read(&mut self, start_address: u16, values: &[bool]) -> Result<(), ClientError> {
60 for (i, value) in values.iter().copied().enumerate() {
61 let addr = start_address
62 .checked_add(i as u16)
63 .ok_or(ClientError::InvalidResponse("coil address overflow"))?;
64 self.set(addr, value)?;
65 }
66 Ok(())
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct RegisterPoints {
72 start_address: u16,
73 values: Vec<u16>,
74}
75
76impl RegisterPoints {
77 pub fn new(start_address: u16, count: usize) -> Self {
78 Self {
79 start_address,
80 values: vec![0; count],
81 }
82 }
83
84 pub fn from_values(start_address: u16, values: Vec<u16>) -> Self {
85 Self {
86 start_address,
87 values,
88 }
89 }
90
91 pub fn start_address(&self) -> u16 {
92 self.start_address
93 }
94
95 pub fn len(&self) -> usize {
96 self.values.len()
97 }
98
99 pub fn is_empty(&self) -> bool {
100 self.values.is_empty()
101 }
102
103 pub fn values(&self) -> &[u16] {
104 &self.values
105 }
106
107 pub fn get(&self, address: u16) -> Option<u16> {
108 let offset = usize::from(address.checked_sub(self.start_address)?);
109 self.values.get(offset).copied()
110 }
111
112 pub fn set(&mut self, address: u16, value: u16) -> Result<(), ClientError> {
113 let offset = usize::from(
114 address
115 .checked_sub(self.start_address)
116 .ok_or(ClientError::InvalidResponse("register address out of range"))?,
117 );
118 let slot = self
119 .values
120 .get_mut(offset)
121 .ok_or(ClientError::InvalidResponse("register address out of range"))?;
122 *slot = value;
123 Ok(())
124 }
125
126 pub fn apply_read(&mut self, start_address: u16, values: &[u16]) -> Result<(), ClientError> {
127 for (i, value) in values.iter().copied().enumerate() {
128 let addr = start_address
129 .checked_add(i as u16)
130 .ok_or(ClientError::InvalidResponse("register address overflow"))?;
131 self.set(addr, value)?;
132 }
133 Ok(())
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::{CoilPoints, RegisterPoints};
140
141 #[test]
142 fn coil_points_apply_read() {
143 let mut points = CoilPoints::new(10, 4);
144 points.apply_read(11, &[true, false]).unwrap();
145 assert_eq!(points.get(10), Some(false));
146 assert_eq!(points.get(11), Some(true));
147 assert_eq!(points.get(12), Some(false));
148 }
149
150 #[test]
151 fn register_points_apply_read() {
152 let mut points = RegisterPoints::new(100, 3);
153 points.apply_read(100, &[10, 20, 30]).unwrap();
154 assert_eq!(points.get(101), Some(20));
155 points.set(102, 42).unwrap();
156 assert_eq!(points.get(102), Some(42));
157 }
158}