1use crate::error::{ModbusError, ModbusResult};
18use crate::mapping::ByteOrder;
19use std::fmt;
20
21#[derive(Debug, Clone, PartialEq)]
27pub enum ModbusTypedValue {
28 Bool(bool),
30 I16(i16),
32 U16(u16),
34 I32(i32),
36 U32(u32),
38 F32(f32),
40 F64(f64),
42 Str(String),
44}
45
46impl ModbusTypedValue {
47 pub fn as_f64(&self) -> Option<f64> {
51 match self {
52 ModbusTypedValue::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
53 ModbusTypedValue::I16(v) => Some(*v as f64),
54 ModbusTypedValue::U16(v) => Some(*v as f64),
55 ModbusTypedValue::I32(v) => Some(*v as f64),
56 ModbusTypedValue::U32(v) => Some(*v as f64),
57 ModbusTypedValue::F32(v) => Some(*v as f64),
58 ModbusTypedValue::F64(v) => Some(*v),
59 ModbusTypedValue::Str(_) => None,
60 }
61 }
62
63 pub fn scale(&self, scale_factor: f64, offset: f64) -> Option<f64> {
69 self.as_f64().map(|v| v * scale_factor + offset)
70 }
71
72 pub fn type_name(&self) -> &'static str {
74 match self {
75 ModbusTypedValue::Bool(_) => "Bool",
76 ModbusTypedValue::I16(_) => "I16",
77 ModbusTypedValue::U16(_) => "U16",
78 ModbusTypedValue::I32(_) => "I32",
79 ModbusTypedValue::U32(_) => "U32",
80 ModbusTypedValue::F32(_) => "F32",
81 ModbusTypedValue::F64(_) => "F64",
82 ModbusTypedValue::Str(_) => "Str",
83 }
84 }
85}
86
87impl fmt::Display for ModbusTypedValue {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 ModbusTypedValue::Bool(v) => write!(f, "{}", v),
91 ModbusTypedValue::I16(v) => write!(f, "{}", v),
92 ModbusTypedValue::U16(v) => write!(f, "{}", v),
93 ModbusTypedValue::I32(v) => write!(f, "{}", v),
94 ModbusTypedValue::U32(v) => write!(f, "{}", v),
95 ModbusTypedValue::F32(v) => write!(f, "{}", v),
96 ModbusTypedValue::F64(v) => write!(f, "{}", v),
97 ModbusTypedValue::Str(v) => write!(f, "{}", v),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum DecoderDataType {
106 Bool,
108 I16,
110 U16,
112 I32,
114 U32,
116 F32,
118 F64,
120 Str(usize),
122}
123
124impl DecoderDataType {
125 pub fn register_count(self) -> usize {
127 match self {
128 DecoderDataType::Bool | DecoderDataType::I16 | DecoderDataType::U16 => 1,
129 DecoderDataType::I32 | DecoderDataType::U32 | DecoderDataType::F32 => 2,
130 DecoderDataType::F64 => 4,
131 DecoderDataType::Str(n) => (n + 1) / 2,
132 }
133 }
134}
135
136impl From<crate::mapping::ModbusDataType> for DecoderDataType {
137 fn from(dt: crate::mapping::ModbusDataType) -> Self {
138 use crate::mapping::ModbusDataType as Mdt;
139 match dt {
140 Mdt::Int16 => DecoderDataType::I16,
141 Mdt::Uint16 | Mdt::Bit(_) => DecoderDataType::U16,
142 Mdt::Int32 => DecoderDataType::I32,
143 Mdt::Uint32 => DecoderDataType::U32,
144 Mdt::Float32 => DecoderDataType::F32,
145 Mdt::Float64 => DecoderDataType::F64,
146 Mdt::String(n) => DecoderDataType::Str(n),
147 }
148 }
149}
150
151pub struct ModbusDecoder;
156
157impl ModbusDecoder {
158 pub fn decode(
172 regs: &[u16],
173 data_type: DecoderDataType,
174 byte_order: ByteOrder,
175 ) -> ModbusResult<ModbusTypedValue> {
176 let required = data_type.register_count();
177 if regs.len() < required {
178 return Err(ModbusError::Io(std::io::Error::new(
179 std::io::ErrorKind::InvalidInput,
180 format!(
181 "decoder needs {} register(s) for {:?}, got {}",
182 required,
183 data_type,
184 regs.len()
185 ),
186 )));
187 }
188
189 match data_type {
190 DecoderDataType::Bool => {
191 Ok(ModbusTypedValue::Bool(regs[0] != 0))
193 }
194 DecoderDataType::I16 => Ok(ModbusTypedValue::I16(regs[0] as i16)),
195 DecoderDataType::U16 => Ok(ModbusTypedValue::U16(regs[0])),
196 DecoderDataType::I32 => {
197 let raw = Self::regs_to_u32(regs, byte_order);
198 Ok(ModbusTypedValue::I32(raw as i32))
199 }
200 DecoderDataType::U32 => {
201 let raw = Self::regs_to_u32(regs, byte_order);
202 Ok(ModbusTypedValue::U32(raw))
203 }
204 DecoderDataType::F32 => {
205 let raw = Self::regs_to_u32(regs, byte_order);
206 let v = f32::from_bits(raw);
207 if !v.is_finite() {
208 return Err(ModbusError::Io(std::io::Error::new(
209 std::io::ErrorKind::InvalidData,
210 format!("decoded F32 is non-finite: {}", v),
211 )));
212 }
213 Ok(ModbusTypedValue::F32(v))
214 }
215 DecoderDataType::F64 => {
216 let raw = Self::regs_to_u64(regs, byte_order);
217 let v = f64::from_bits(raw);
218 if !v.is_finite() {
219 return Err(ModbusError::Io(std::io::Error::new(
220 std::io::ErrorKind::InvalidData,
221 format!("decoded F64 is non-finite: {}", v),
222 )));
223 }
224 Ok(ModbusTypedValue::F64(v))
225 }
226 DecoderDataType::Str(char_count) => Ok(ModbusTypedValue::Str(Self::regs_to_string(
227 regs, char_count,
228 ))),
229 }
230 }
231
232 pub fn scale(value: &ModbusTypedValue, scale_factor: f64, offset: f64) -> Option<f64> {
236 value.scale(scale_factor, offset)
237 }
238
239 fn regs_to_u32(regs: &[u16], order: ByteOrder) -> u32 {
252 match order {
253 ByteOrder::BigEndian => {
254 ((regs[0] as u32) << 16) | (regs[1] as u32)
256 }
257 ByteOrder::LittleEndian => {
258 ((regs[1] as u32) << 16) | (regs[0] as u32)
260 }
261 ByteOrder::BigEndianSwapped => {
262 let hi = ((regs[0] & 0xFF) << 8) | (regs[0] >> 8);
264 let lo = ((regs[1] & 0xFF) << 8) | (regs[1] >> 8);
265 ((hi as u32) << 16) | (lo as u32)
266 }
267 ByteOrder::LittleEndianSwapped => {
268 let hi = ((regs[1] & 0xFF) << 8) | (regs[1] >> 8);
270 let lo = ((regs[0] & 0xFF) << 8) | (regs[0] >> 8);
271 ((hi as u32) << 16) | (lo as u32)
272 }
273 }
274 }
275
276 fn regs_to_u64(regs: &[u16], order: ByteOrder) -> u64 {
278 match order {
279 ByteOrder::BigEndian => {
280 ((regs[0] as u64) << 48)
281 | ((regs[1] as u64) << 32)
282 | ((regs[2] as u64) << 16)
283 | (regs[3] as u64)
284 }
285 ByteOrder::LittleEndian => {
286 ((regs[3] as u64) << 48)
287 | ((regs[2] as u64) << 32)
288 | ((regs[1] as u64) << 16)
289 | (regs[0] as u64)
290 }
291 ByteOrder::BigEndianSwapped => {
292 let mut result: u64 = 0;
294 for (i, ®) in regs[..4].iter().enumerate() {
295 let swapped = ((reg & 0xFF) << 8) | (reg >> 8);
296 result |= (swapped as u64) << ((3 - i) * 16);
297 }
298 result
299 }
300 ByteOrder::LittleEndianSwapped => {
301 let mut result: u64 = 0;
303 for (i, ®) in regs[..4].iter().enumerate() {
304 let swapped = ((reg & 0xFF) << 8) | (reg >> 8);
305 result |= (swapped as u64) << (i * 16);
306 }
307 result
308 }
309 }
310 }
311
312 fn regs_to_string(regs: &[u16], char_count: usize) -> String {
316 let n_regs = (char_count + 1) / 2;
317 let mut bytes: Vec<u8> = Vec::with_capacity(char_count);
318 for ® in regs.iter().take(n_regs) {
319 bytes.push((reg >> 8) as u8);
320 bytes.push((reg & 0xFF) as u8);
321 }
322 bytes.truncate(char_count);
323 while bytes.last() == Some(&0) {
325 bytes.pop();
326 }
327 String::from_utf8_lossy(&bytes).trim_end().to_string()
328 }
329}
330
331pub struct ModbusEncoder;
335
336impl ModbusEncoder {
337 pub fn encode(
343 value: &ModbusTypedValue,
344 data_type: DecoderDataType,
345 byte_order: ByteOrder,
346 ) -> ModbusResult<Vec<u16>> {
347 match (value, data_type) {
348 (ModbusTypedValue::Bool(v), DecoderDataType::Bool | DecoderDataType::U16) => {
349 Ok(vec![if *v { 0xFF00 } else { 0x0000 }])
350 }
351 (ModbusTypedValue::I16(v), DecoderDataType::I16) => Ok(vec![*v as u16]),
352 (ModbusTypedValue::U16(v), DecoderDataType::U16) => Ok(vec![*v]),
353 (ModbusTypedValue::I32(v), DecoderDataType::I32) => {
354 Ok(Self::u32_to_regs(*v as u32, byte_order))
355 }
356 (ModbusTypedValue::U32(v), DecoderDataType::U32) => {
357 Ok(Self::u32_to_regs(*v, byte_order))
358 }
359 (ModbusTypedValue::F32(v), DecoderDataType::F32) => {
360 Ok(Self::u32_to_regs(v.to_bits(), byte_order))
361 }
362 (ModbusTypedValue::F64(v), DecoderDataType::F64) => {
363 Ok(Self::u64_to_regs(v.to_bits(), byte_order))
364 }
365 (ModbusTypedValue::Str(s), DecoderDataType::Str(char_count)) => {
366 Ok(Self::string_to_regs(s, char_count))
367 }
368 _ => Err(ModbusError::Io(std::io::Error::new(
369 std::io::ErrorKind::InvalidInput,
370 format!("cannot encode {} as {:?}", value.type_name(), data_type),
371 ))),
372 }
373 }
374
375 fn u32_to_regs(v: u32, order: ByteOrder) -> Vec<u16> {
376 let hi = (v >> 16) as u16;
377 let lo = (v & 0xFFFF) as u16;
378 match order {
379 ByteOrder::BigEndian => vec![hi, lo],
380 ByteOrder::LittleEndian => vec![lo, hi],
381 ByteOrder::BigEndianSwapped => {
382 vec![hi.swap_bytes(), lo.swap_bytes()]
383 }
384 ByteOrder::LittleEndianSwapped => {
385 vec![lo.swap_bytes(), hi.swap_bytes()]
386 }
387 }
388 }
389
390 fn u64_to_regs(v: u64, order: ByteOrder) -> Vec<u16> {
391 let w3 = (v >> 48) as u16;
392 let w2 = ((v >> 32) & 0xFFFF) as u16;
393 let w1 = ((v >> 16) & 0xFFFF) as u16;
394 let w0 = (v & 0xFFFF) as u16;
395 match order {
396 ByteOrder::BigEndian => vec![w3, w2, w1, w0],
397 ByteOrder::LittleEndian => vec![w0, w1, w2, w3],
398 ByteOrder::BigEndianSwapped => {
399 vec![
400 w3.swap_bytes(),
401 w2.swap_bytes(),
402 w1.swap_bytes(),
403 w0.swap_bytes(),
404 ]
405 }
406 ByteOrder::LittleEndianSwapped => {
407 vec![
408 w0.swap_bytes(),
409 w1.swap_bytes(),
410 w2.swap_bytes(),
411 w3.swap_bytes(),
412 ]
413 }
414 }
415 }
416
417 fn string_to_regs(s: &str, char_count: usize) -> Vec<u16> {
418 let n_regs = (char_count + 1) / 2;
419 let bytes = s.as_bytes();
420 let mut regs = Vec::with_capacity(n_regs);
421 for i in 0..n_regs {
422 let hi = bytes.get(i * 2).copied().unwrap_or(0);
423 let lo = bytes.get(i * 2 + 1).copied().unwrap_or(0);
424 regs.push(((hi as u16) << 8) | (lo as u16));
425 }
426 regs
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 #[test]
437 fn test_decode_bool() {
438 let v =
439 ModbusDecoder::decode(&[0xFF00], DecoderDataType::Bool, ByteOrder::BigEndian).unwrap();
440 assert_eq!(v, ModbusTypedValue::Bool(true));
441
442 let v =
443 ModbusDecoder::decode(&[0x0000], DecoderDataType::Bool, ByteOrder::BigEndian).unwrap();
444 assert_eq!(v, ModbusTypedValue::Bool(false));
445 }
446
447 #[test]
448 fn test_decode_i16() {
449 let v = ModbusDecoder::decode(&[255], DecoderDataType::I16, ByteOrder::BigEndian).unwrap();
451 assert_eq!(v, ModbusTypedValue::I16(255));
452
453 let v =
455 ModbusDecoder::decode(&[0xFFFF], DecoderDataType::I16, ByteOrder::BigEndian).unwrap();
456 assert_eq!(v, ModbusTypedValue::I16(-1));
457 }
458
459 #[test]
460 fn test_decode_u16() {
461 let v =
462 ModbusDecoder::decode(&[0xFFFF], DecoderDataType::U16, ByteOrder::BigEndian).unwrap();
463 assert_eq!(v, ModbusTypedValue::U16(65535));
464 }
465
466 #[test]
467 fn test_decode_i32_big_endian() {
468 let regs = [0x0001, 0x0000];
470 let v = ModbusDecoder::decode(®s, DecoderDataType::I32, ByteOrder::BigEndian).unwrap();
471 assert_eq!(v, ModbusTypedValue::I32(65536));
472 }
473
474 #[test]
475 fn test_decode_i32_little_endian() {
476 let regs = [0x0000, 0x0001];
479 let v =
480 ModbusDecoder::decode(®s, DecoderDataType::I32, ByteOrder::LittleEndian).unwrap();
481 assert_eq!(v, ModbusTypedValue::I32(65536));
482 }
483
484 #[test]
485 fn test_decode_u32_all_byte_orders() {
486 let be_regs = [0xDEAD, 0xBEEF]; let le_regs = [0xBEEF, 0xDEAD]; let v_be =
491 ModbusDecoder::decode(&be_regs, DecoderDataType::U32, ByteOrder::BigEndian).unwrap();
492 let v_le =
493 ModbusDecoder::decode(&le_regs, DecoderDataType::U32, ByteOrder::LittleEndian).unwrap();
494
495 assert_eq!(v_be, ModbusTypedValue::U32(0xDEADBEEF));
496 assert_eq!(v_le, ModbusTypedValue::U32(0xDEADBEEF));
497 }
498
499 #[test]
500 fn test_decode_f32_big_endian() {
501 let regs = [0x3F80, 0x0000];
503 let v = ModbusDecoder::decode(®s, DecoderDataType::F32, ByteOrder::BigEndian).unwrap();
504 match v {
505 ModbusTypedValue::F32(f) => assert!((f - 1.0f32).abs() < 1e-6),
506 _ => panic!("Expected F32"),
507 }
508 }
509
510 #[test]
511 fn test_decode_f32_big_endian_swapped() {
512 let swapped_regs = [0x803F, 0x0000];
515 let v = ModbusDecoder::decode(
516 &swapped_regs,
517 DecoderDataType::F32,
518 ByteOrder::BigEndianSwapped,
519 )
520 .unwrap();
521 match v {
522 ModbusTypedValue::F32(f) => assert!((f - 1.0f32).abs() < 1e-6),
523 _ => panic!("Expected F32"),
524 }
525 }
526
527 #[test]
528 fn test_decode_f64_big_endian() {
529 let regs = [0x3FF0, 0x0000, 0x0000, 0x0000];
531 let v = ModbusDecoder::decode(®s, DecoderDataType::F64, ByteOrder::BigEndian).unwrap();
532 match v {
533 ModbusTypedValue::F64(f) => assert!((f - 1.0_f64).abs() < 1e-12),
534 _ => panic!("Expected F64"),
535 }
536 }
537
538 #[test]
539 fn test_decode_string() {
540 let regs = [0x4142, 0x4344];
542 let v =
543 ModbusDecoder::decode(®s, DecoderDataType::Str(4), ByteOrder::BigEndian).unwrap();
544 assert_eq!(v, ModbusTypedValue::Str("ABCD".to_string()));
545 }
546
547 #[test]
548 fn test_decode_string_with_nulls() {
549 let regs = [0x4142, 0x0000];
551 let v =
552 ModbusDecoder::decode(®s, DecoderDataType::Str(4), ByteOrder::BigEndian).unwrap();
553 assert_eq!(v, ModbusTypedValue::Str("AB".to_string()));
554 }
555
556 #[test]
557 fn test_insufficient_registers_error() {
558 let result = ModbusDecoder::decode(&[0x3F80], DecoderDataType::F32, ByteOrder::BigEndian);
559 assert!(result.is_err());
560 }
561
562 #[test]
565 fn test_scale_linear() {
566 let v = ModbusTypedValue::I16(625);
567 let scaled = ModbusDecoder::scale(&v, 0.1, -40.0).unwrap();
569 assert!((scaled - 22.5).abs() < 1e-9);
570 }
571
572 #[test]
573 fn test_scale_string_returns_none() {
574 let v = ModbusTypedValue::Str("hello".to_string());
575 assert!(ModbusDecoder::scale(&v, 1.0, 0.0).is_none());
576 }
577
578 #[test]
581 fn test_encode_i16_roundtrip() {
582 let original = ModbusTypedValue::I16(-1024);
583 let encoded =
584 ModbusEncoder::encode(&original, DecoderDataType::I16, ByteOrder::BigEndian).unwrap();
585 let decoded =
586 ModbusDecoder::decode(&encoded, DecoderDataType::I16, ByteOrder::BigEndian).unwrap();
587 assert_eq!(decoded, original);
588 }
589
590 #[test]
591 fn test_encode_u32_roundtrip_all_orders() {
592 let orders = [
593 ByteOrder::BigEndian,
594 ByteOrder::LittleEndian,
595 ByteOrder::BigEndianSwapped,
596 ByteOrder::LittleEndianSwapped,
597 ];
598 let original = ModbusTypedValue::U32(0xCAFEBABE);
599 for order in orders {
600 let encoded = ModbusEncoder::encode(&original, DecoderDataType::U32, order).unwrap();
601 let decoded = ModbusDecoder::decode(&encoded, DecoderDataType::U32, order).unwrap();
602 assert_eq!(
603 decoded, original,
604 "roundtrip failed for byte order {:?}",
605 order
606 );
607 }
608 }
609
610 #[test]
611 fn test_encode_f32_roundtrip() {
612 let original = ModbusTypedValue::F32(22.5);
613 let encoded =
614 ModbusEncoder::encode(&original, DecoderDataType::F32, ByteOrder::BigEndian).unwrap();
615 let decoded =
616 ModbusDecoder::decode(&encoded, DecoderDataType::F32, ByteOrder::BigEndian).unwrap();
617 match decoded {
618 ModbusTypedValue::F32(f) => assert!((f - 22.5).abs() < 1e-5),
619 _ => panic!("Expected F32"),
620 }
621 }
622
623 #[test]
624 fn test_encode_f64_roundtrip() {
625 let original = ModbusTypedValue::F64(std::f64::consts::PI);
626 let orders = [ByteOrder::BigEndian, ByteOrder::LittleEndian];
627 for order in orders {
628 let encoded = ModbusEncoder::encode(&original, DecoderDataType::F64, order).unwrap();
629 let decoded = ModbusDecoder::decode(&encoded, DecoderDataType::F64, order).unwrap();
630 match decoded {
631 ModbusTypedValue::F64(f) => {
632 assert!(
633 (f - std::f64::consts::PI).abs() < 1e-12,
634 "f64 roundtrip failed for {:?}",
635 order
636 );
637 }
638 _ => panic!("Expected F64"),
639 }
640 }
641 }
642
643 #[test]
644 fn test_encode_string_roundtrip() {
645 let original = ModbusTypedValue::Str("Test".to_string());
646 let encoded =
647 ModbusEncoder::encode(&original, DecoderDataType::Str(4), ByteOrder::BigEndian)
648 .unwrap();
649 let decoded =
650 ModbusDecoder::decode(&encoded, DecoderDataType::Str(4), ByteOrder::BigEndian).unwrap();
651 assert_eq!(decoded, original);
652 }
653
654 #[test]
655 fn test_type_mismatch_error() {
656 let v = ModbusTypedValue::Str("hello".to_string());
657 let result = ModbusEncoder::encode(&v, DecoderDataType::F32, ByteOrder::BigEndian);
658 assert!(result.is_err());
659 }
660
661 #[test]
664 fn test_as_f64_all_types() {
665 assert_eq!(ModbusTypedValue::Bool(true).as_f64(), Some(1.0));
666 assert_eq!(ModbusTypedValue::Bool(false).as_f64(), Some(0.0));
667 assert_eq!(ModbusTypedValue::I16(-5).as_f64(), Some(-5.0));
668 assert_eq!(ModbusTypedValue::U16(100).as_f64(), Some(100.0));
669 assert_eq!(ModbusTypedValue::I32(-1).as_f64(), Some(-1.0));
670 assert_eq!(ModbusTypedValue::U32(42).as_f64(), Some(42.0));
671 assert!(
672 (ModbusTypedValue::F32(std::f32::consts::PI)
673 .as_f64()
674 .unwrap()
675 - std::f64::consts::PI)
676 .abs()
677 < 1e-5
678 );
679 assert!(
680 (ModbusTypedValue::F64(std::f64::consts::E).as_f64().unwrap() - std::f64::consts::E)
681 .abs()
682 < 1e-12
683 );
684 assert!(ModbusTypedValue::Str("x".to_string()).as_f64().is_none());
685 }
686
687 #[test]
688 fn test_display() {
689 assert_eq!(format!("{}", ModbusTypedValue::I16(-42)), "-42");
690 assert_eq!(format!("{}", ModbusTypedValue::Bool(true)), "true");
691 }
692
693 #[test]
694 fn test_type_name() {
695 assert_eq!(ModbusTypedValue::Bool(false).type_name(), "Bool");
696 assert_eq!(ModbusTypedValue::F32(0.0).type_name(), "F32");
697 assert_eq!(ModbusTypedValue::Str(String::new()).type_name(), "Str");
698 }
699}