1use chrono::{DateTime, NaiveDateTime};
2
3use crate::{numbers::Number, point::DataType, Bytes, Gain, PointDefinition, Unit, ValidateRange};
4
5use core::fmt;
6use std::string::FromUtf8Error;
7
8pub type Word = u16;
9
10pub trait Value: Sized + fmt::Display {
13 const DATA_TYPE: DataType;
15
16 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError>;
18 fn encode(self) -> Box<[Word]>;
20
21 fn validate(&self) -> Result<(), ValidationError> {
22 Ok(())
23 }
24}
25
26impl Value for u16 {
27 const DATA_TYPE: DataType = DataType::U16;
28 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
29 let &[w0] = words else {
30 return Err(DecodeError::OutOfBounds);
31 };
32 Ok(w0)
33 }
34
35 fn encode(self) -> Box<[u16]> {
36 Box::new([self])
37 }
38}
39
40impl Value for u32 {
41 const DATA_TYPE: DataType = DataType::U32;
42 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
43 let &[w1, w0] = words else {
44 return Err(DecodeError::OutOfBounds);
45 };
46 Ok((w1 as u32) << 16 | w0 as u32)
47 }
48 fn encode(self) -> Box<[u16]> {
49 Box::new([(self >> 16) as u16, self as u16])
50 }
51}
52
53impl Value for u64 {
54 const DATA_TYPE: DataType = DataType::U32;
55 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
56 let &[w3, w2, w1, w0] = words else {
57 return Err(DecodeError::OutOfBounds);
58 };
59 Ok((w3 as u64) << 0x30 | (w2 as u64) << 0x20 | (w1 as u64) << 0x10 | w0 as u64)
60 }
61 fn encode(self) -> Box<[u16]> {
62 Box::new([
63 (self >> 0x30) as u16,
64 (self >> 0x20) as u16,
65 (self >> 0x10) as u16,
66 self as u16,
67 ])
68 }
69}
70
71impl Value for u128 {
72 const DATA_TYPE: DataType = DataType::U32;
73 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
74 let &[w7, w6, w5, w4, w3, w2, w1, w0] = words else {
75 return Err(DecodeError::OutOfBounds);
76 };
77 Ok((w7 as u128) << 0x70
78 | (w6 as u128) << 0x60
79 | (w5 as u128) << 0x50
80 | (w4 as u128) << 0x40
81 | (w3 as u128) << 0x30
82 | (w2 as u128) << 0x20
83 | (w1 as u128) << 0x10
84 | (w0 as u128))
85 }
86 fn encode(self) -> Box<[u16]> {
87 Box::new([
88 (self >> 0x70) as u16,
89 (self >> 0x60) as u16,
90 (self >> 0x50) as u16,
91 (self >> 0x40) as u16,
92 (self >> 0x30) as u16,
93 (self >> 0x20) as u16,
94 (self >> 0x10) as u16,
95 self as u16,
96 ])
97 }
98}
99
100impl Value for i16 {
101 const DATA_TYPE: DataType = DataType::I16;
102 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError> {
103 Ok(u16::decode(words, pointdef)? as Self)
104 }
105 fn encode(self) -> Box<[u16]> {
106 (self as u16).encode()
107 }
108}
109
110impl Value for i32 {
111 const DATA_TYPE: DataType = DataType::I32;
112 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError> {
113 Ok(u32::decode(words, pointdef)? as Self)
114 }
115 fn encode(self) -> Box<[u16]> {
116 (self as u32).encode()
117 }
118}
119
120impl Value for i64 {
121 const DATA_TYPE: DataType = DataType::I32;
122 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError> {
123 Ok(u64::decode(words, pointdef)? as Self)
124 }
125 fn encode(self) -> Box<[u16]> {
126 (self as u64).encode()
127 }
128}
129
130impl Value for f32 {
131 const DATA_TYPE: DataType = DataType::U32;
132 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
133 let &[w1, w0] = words else {
134 return Err(DecodeError::OutOfBounds);
135 };
136 Ok(f32::from_be_bytes([
137 (w1 >> 8) as u8,
138 w1 as u8,
139 (w0 >> 8) as u8,
140 w0 as u8,
141 ]))
142 }
143 fn encode(self) -> Box<[u16]> {
144 let [b3, b2, b1, b0] = self.to_be_bytes();
145 Box::new([
146 (b3 as u16) << 8 | (b2 as u16),
147 (b1 as u16) << 8 | (b0 as u16),
148 ])
149 }
150}
151
152impl Value for f64 {
153 const DATA_TYPE: DataType = DataType::U32;
154 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
155 let &[w3, w2, w1, w0] = words else {
156 return Err(DecodeError::OutOfBounds);
157 };
158 Ok(f64::from_be_bytes([
159 (w3 >> 8) as u8,
160 w3 as u8,
161 (w2 >> 8) as u8,
162 w2 as u8,
163 (w1 >> 8) as u8,
164 w1 as u8,
165 (w0 >> 8) as u8,
166 w0 as u8,
167 ]))
168 }
169 fn encode(self) -> Box<[u16]> {
170 let [b7, b6, b5, b4, b3, b2, b1, b0] = self.to_be_bytes();
171 Box::new([
172 (b7 as u16) << 8 | (b6 as u16),
173 (b5 as u16) << 8 | (b4 as u16),
174 (b3 as u16) << 8 | (b2 as u16),
175 (b1 as u16) << 8 | (b0 as u16),
176 ])
177 }
178}
179
180fn encode_bytes(octets: &[u8]) -> Box<[u16]> {
181 octets
182 .chunks(2)
183 .map(|chunk| match *chunk {
184 [b1, b0] => (b1 as u16) << 8 | (b0 as u16),
185 [b1] => (b1 as u16) << 8,
186 _ => unreachable!(),
187 })
188 .collect()
189}
190
191impl Value for String {
192 const DATA_TYPE: DataType = DataType::String;
193 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError> {
194 let bytes = Bytes::decode(words, pointdef)?;
195 Ok(String::from_utf8(bytes.take())?)
196 }
197 fn encode(self) -> Box<[u16]> {
198 encode_bytes(self.as_bytes())
199 }
200}
201
202impl Value for Bytes {
203 const DATA_TYPE: DataType = DataType::Bytes;
204 fn decode(words: &[Word], _pointdef: &PointDefinition) -> Result<Self, DecodeError> {
205 let bytes = words
206 .iter()
207 .flat_map(|word| word.to_be_bytes())
208 .take_while(|&b| b != 0)
209 .collect::<Vec<_>>();
210 Ok(bytes.into())
211 }
212 fn encode(self) -> Box<[u16]> {
213 encode_bytes(&self.take())
214 }
215}
216
217impl Value for NaiveDateTime {
218 const DATA_TYPE: DataType = DataType::DateTime;
219 fn decode(words: &[Word], pointdef: &PointDefinition) -> Result<Self, DecodeError> {
220 let decoded = u32::decode(words, pointdef)?;
221 DateTime::from_timestamp(decoded.into(), 0)
222 .ok_or(DecodeError::OutOfBounds)
223 .map(|dt| dt.naive_utc())
224 }
225 fn encode(self) -> Box<[u16]> {
226 u32::encode(self.and_utc().timestamp() as u32)
227 }
228}
229
230impl<T: Value + Copy + Into<i64>, U: Unit, G: Gain, R: ValidateRange> Value for Number<T, U, G, R>
231where
232 Number<T, U, G, R>: fmt::Display,
233{
234 const DATA_TYPE: DataType = T::DATA_TYPE;
235 fn decode(
236 words: &[crate::Word],
237 pointdef: &crate::PointDefinition,
238 ) -> Result<Self, crate::DecodeError> {
239 let val = T::decode(words, pointdef)?;
240 Ok(Self::from(val))
241 }
242
243 fn encode(self) -> Box<[crate::Word]> {
244 self.inner().encode()
245 }
246
247 fn validate(&self) -> Result<(), ValidationError> {
248 R::validate(self.into_inner())
249 }
250}
251
252#[derive(thiserror::Error, Debug, Eq, PartialEq)]
255pub enum DecodeError {
256 #[error("Out of bounds")]
259 OutOfBounds,
260 #[error("Invalid UTF-8 data")]
262 Utf8(#[from] FromUtf8Error),
263 #[error("Invalid enum value")]
265 InvalidEnumValue,
266}
267
268#[derive(thiserror::Error, Debug, PartialEq, Eq)]
269pub enum ValidationError {
270 #[error("Value is out of range")]
272 OutOfRange,
273 #[error("Variable value ranges are currently unsupported")]
274 VariableRangeUnsupported,
275}
276
277#[derive(Debug, Clone)]
279pub enum DynValue {
280 U16(u16),
281 U32(u32),
282 I16(i16),
283 I32(i32),
284 String(String),
285 Bytes(crate::Bytes),
286 DateTime(NaiveDateTime),
287}
288
289impl std::fmt::Display for DynValue {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 match self {
292 DynValue::U16(v) => write!(f, "{v}"),
293 DynValue::U32(v) => write!(f, "{v}"),
294 DynValue::I16(v) => write!(f, "{v}"),
295 DynValue::I32(v) => write!(f, "{v}"),
296 DynValue::String(v) => write!(f, "{v}"),
297 DynValue::Bytes(v) => write!(f, "{v:?}"),
298 DynValue::DateTime(v) => write!(f, "{v}"),
299 }
300 }
301}
302
303impl PointDefinition {
304 pub fn decode_dyn(&self, words: &[Word]) -> Result<DynValue, DecodeError> {
306 match self.data_type {
307 DataType::U16 => Ok(DynValue::U16(u16::decode(words, self)?)),
308 DataType::U32 => Ok(DynValue::U32(u32::decode(words, self)?)),
309 DataType::I16 => Ok(DynValue::I16(i16::decode(words, self)?)),
310 DataType::I32 => Ok(DynValue::I32(i32::decode(words, self)?)),
311 DataType::String => Ok(DynValue::String(String::decode(words, self)?)),
312 DataType::Bytes => Ok(DynValue::Bytes(crate::Bytes::decode(words, self)?)),
313 DataType::DateTime => Ok(DynValue::DateTime(NaiveDateTime::decode(words, self)?)),
314 }
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use crate::{point::DataType, PointDefinition, Value};
321
322 const DUMMY_POINTDEF: PointDefinition =
323 PointDefinition::new(15000, 1, crate::ReadWrite::ReadOnly, DataType::U16);
324
325 #[test]
326 fn test_u16() {
327 assert_eq!(*0x0001i16.encode(), [0x1]);
328 assert_eq!(u16::decode(&[0x1], &DUMMY_POINTDEF), Ok(0x0001u16));
329 }
330
331 #[test]
332 fn test_u32() {
333 assert_eq!(*0x00010002u32.encode(), [0x1, 0x2]);
334 assert_eq!(u32::decode(&[0x1, 0x2], &DUMMY_POINTDEF), Ok(0x00010002u32));
335 }
336
337 #[test]
338 fn test_u64() {
339 assert_eq!(*0x0001000200030004u64.encode(), [0x1, 0x2, 0x3, 0x4]);
340 assert_eq!(
341 u64::decode(&[0x1, 0x2, 0x3, 0x4], &DUMMY_POINTDEF),
342 Ok(0x0001000200030004u64)
343 );
344 }
345
346 #[test]
347 fn test_u128() {
348 assert_eq!(
349 *0x00010002000300040005000600070008u128.encode(),
350 [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]
351 );
352 assert_eq!(
353 u128::decode(&[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8], &DUMMY_POINTDEF),
354 Ok(0x00010002000300040005000600070008u128)
355 );
356 }
357
358 #[test]
359 fn test_i16() {
360 assert_eq!(*(-1i16).encode(), [0xffff]);
361 assert_eq!(i16::decode(&[0xffff], &DUMMY_POINTDEF), Ok(-1i16));
362 }
363
364 #[test]
365 fn test_i32() {
366 assert_eq!(*(-1i32).encode(), [0xffff, 0xffff]);
367 assert_eq!(i32::decode(&[0xffff, 0xffff], &DUMMY_POINTDEF), Ok(-1i32));
368 }
369
370 #[test]
371 fn test_i64() {
372 assert_eq!(*(-1i64).encode(), [0xffff, 0xffff, 0xffff, 0xffff]);
373 assert_eq!(
374 i64::decode(&[0xffff, 0xffff, 0xffff, 0xffff], &DUMMY_POINTDEF),
375 Ok(-1i64)
376 );
377 }
378
379 #[test]
380 fn test_f32() {
381 assert_eq!(*(0.5f32).encode(), [0x3f00, 0x0000]);
382 assert_eq!(f32::decode(&[0x3f00, 0x0000], &DUMMY_POINTDEF), Ok(0.5f32));
383 }
384
385 #[test]
386 fn test_f64() {
387 assert_eq!(*(0.5f64).encode(), [0x3fe0, 0x0000, 0x0000, 0x0000]);
388 assert_eq!(
389 f64::decode(&[0x3fe0, 0x0000, 0x0000, 0x0000], &DUMMY_POINTDEF),
390 Ok(0.5f64)
391 );
392 }
393
394 #[test]
395 fn test_string() {
396 assert_eq!(*String::from("SunS").encode(), [0x5375, 0x6e53]);
397 assert_eq!(
398 String::decode(&[0x5375, 0x6e53], &DUMMY_POINTDEF),
399 Ok(String::from("SunS"))
400 );
401 assert_eq!(*String::from("pad").encode(), [0x7061, 0x6400]);
402 assert_eq!(
403 String::decode(&[0x7061, 0x6400], &DUMMY_POINTDEF),
404 Ok(String::from("pad"))
405 );
406 assert_eq!(
407 String::decode(&[0x7061, 0x6400, 0x0000], &DUMMY_POINTDEF),
408 Ok(String::from("pad"))
409 );
410 }
411}