1use crate::error::{ModbusError, ModbusResult};
7use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ModbusDataType {
13 Int16,
15 Uint16,
17 Int32,
19 Uint32,
21 Float32,
23 Float64,
25 Bit(u8),
27 String(usize),
29}
30
31impl ModbusDataType {
32 pub fn register_count(&self) -> usize {
34 match self {
35 ModbusDataType::Int16 | ModbusDataType::Uint16 => 1,
36 ModbusDataType::Int32 | ModbusDataType::Uint32 | ModbusDataType::Float32 => 2,
37 ModbusDataType::Float64 => 4,
38 ModbusDataType::Bit(_) => 1,
39 ModbusDataType::String(len) => (*len + 1) / 2, }
41 }
42
43 pub fn xsd_datatype(&self) -> &'static str {
45 match self {
46 ModbusDataType::Int16 => "http://www.w3.org/2001/XMLSchema#short",
47 ModbusDataType::Uint16 => "http://www.w3.org/2001/XMLSchema#unsignedShort",
48 ModbusDataType::Int32 => "http://www.w3.org/2001/XMLSchema#int",
49 ModbusDataType::Uint32 => "http://www.w3.org/2001/XMLSchema#unsignedInt",
50 ModbusDataType::Float32 | ModbusDataType::Float64 => {
51 "http://www.w3.org/2001/XMLSchema#float"
52 }
53 ModbusDataType::Bit(_) => "http://www.w3.org/2001/XMLSchema#boolean",
54 ModbusDataType::String(_) => "http://www.w3.org/2001/XMLSchema#string",
55 }
56 }
57}
58
59impl FromStr for ModbusDataType {
60 type Err = ModbusError;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 let s_upper = s.to_uppercase();
64 match s_upper.as_str() {
65 "INT16" | "SHORT" => Ok(ModbusDataType::Int16),
66 "UINT16" | "WORD" | "USHORT" => Ok(ModbusDataType::Uint16),
67 "INT32" | "DINT" | "INT" => Ok(ModbusDataType::Int32),
68 "UINT32" | "DWORD" | "UDINT" => Ok(ModbusDataType::Uint32),
69 "FLOAT32" | "REAL" | "FLOAT" => Ok(ModbusDataType::Float32),
70 "FLOAT64" | "LREAL" | "DOUBLE" => Ok(ModbusDataType::Float64),
71 _ if s_upper.starts_with("BIT") => {
72 let bit_num: u8 = s_upper[3..].parse().map_err(|_| {
74 ModbusError::Io(std::io::Error::new(
75 std::io::ErrorKind::InvalidInput,
76 format!("Invalid bit number in {}", s),
77 ))
78 })?;
79 if bit_num > 15 {
80 return Err(ModbusError::Io(std::io::Error::new(
81 std::io::ErrorKind::InvalidInput,
82 "Bit number must be 0-15",
83 )));
84 }
85 Ok(ModbusDataType::Bit(bit_num))
86 }
87 _ if s_upper.starts_with("STRING") => {
88 let len: usize = s_upper[6..].parse().map_err(|_| {
90 ModbusError::Io(std::io::Error::new(
91 std::io::ErrorKind::InvalidInput,
92 format!("Invalid string length in {}", s),
93 ))
94 })?;
95 Ok(ModbusDataType::String(len))
96 }
97 _ => Err(ModbusError::Io(std::io::Error::new(
98 std::io::ErrorKind::InvalidInput,
99 format!("Unknown data type: {}", s),
100 ))),
101 }
102 }
103}
104
105impl fmt::Display for ModbusDataType {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 ModbusDataType::Int16 => write!(f, "INT16"),
109 ModbusDataType::Uint16 => write!(f, "UINT16"),
110 ModbusDataType::Int32 => write!(f, "INT32"),
111 ModbusDataType::Uint32 => write!(f, "UINT32"),
112 ModbusDataType::Float32 => write!(f, "FLOAT32"),
113 ModbusDataType::Float64 => write!(f, "FLOAT64"),
114 ModbusDataType::Bit(n) => write!(f, "BIT{}", n),
115 ModbusDataType::String(len) => write!(f, "STRING{}", len),
116 }
117 }
118}
119
120#[derive(Debug, Clone, PartialEq)]
122pub enum ModbusValue {
123 Int(i64),
125 Uint(u64),
127 Float(f64),
129 Bool(bool),
131 String(String),
133}
134
135impl ModbusValue {
136 pub fn as_f64(&self) -> Option<f64> {
138 match self {
139 ModbusValue::Int(v) => Some(*v as f64),
140 ModbusValue::Uint(v) => Some(*v as f64),
141 ModbusValue::Float(v) => Some(*v),
142 ModbusValue::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
143 ModbusValue::String(_) => None,
144 }
145 }
146
147 pub fn to_rdf_literal(&self) -> String {
149 match self {
150 ModbusValue::Int(v) => v.to_string(),
151 ModbusValue::Uint(v) => v.to_string(),
152 ModbusValue::Float(v) => {
153 if v.fract() == 0.0 {
155 format!("{:.1}", v)
156 } else {
157 format!("{}", v)
158 }
159 }
160 ModbusValue::Bool(v) => if *v { "true" } else { "false" }.to_string(),
161 ModbusValue::String(s) => s.clone(),
162 }
163 }
164}
165
166impl fmt::Display for ModbusValue {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 match self {
169 ModbusValue::Int(v) => write!(f, "{}", v),
170 ModbusValue::Uint(v) => write!(f, "{}", v),
171 ModbusValue::Float(v) => write!(f, "{}", v),
172 ModbusValue::Bool(v) => write!(f, "{}", v),
173 ModbusValue::String(s) => write!(f, "{}", s),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq)]
180pub struct LinearScaling {
181 pub multiplier: f64,
183 pub offset: f64,
185}
186
187impl Default for LinearScaling {
188 fn default() -> Self {
189 Self {
190 multiplier: 1.0,
191 offset: 0.0,
192 }
193 }
194}
195
196impl LinearScaling {
197 pub fn new(multiplier: f64, offset: f64) -> Self {
199 Self { multiplier, offset }
200 }
201
202 pub fn apply(&self, raw: f64) -> f64 {
204 raw * self.multiplier + self.offset
205 }
206
207 pub fn reverse(&self, physical: f64) -> f64 {
209 (physical - self.offset) / self.multiplier
210 }
211
212 pub fn is_identity(&self) -> bool {
214 (self.multiplier - 1.0).abs() < f64::EPSILON && self.offset.abs() < f64::EPSILON
215 }
216}
217
218pub fn decode_registers(registers: &[u16], data_type: ModbusDataType) -> ModbusResult<ModbusValue> {
220 let required = data_type.register_count();
221 if registers.len() < required {
222 return Err(ModbusError::Io(std::io::Error::new(
223 std::io::ErrorKind::InvalidInput,
224 format!(
225 "Need {} registers for {:?}, got {}",
226 required,
227 data_type,
228 registers.len()
229 ),
230 )));
231 }
232
233 match data_type {
234 ModbusDataType::Int16 => {
235 let value = registers[0] as i16;
236 Ok(ModbusValue::Int(value as i64))
237 }
238 ModbusDataType::Uint16 => {
239 let value = registers[0];
240 Ok(ModbusValue::Uint(value as u64))
241 }
242 ModbusDataType::Int32 => {
243 let value = ((registers[0] as i32) << 16) | (registers[1] as i32);
245 Ok(ModbusValue::Int(value as i64))
246 }
247 ModbusDataType::Uint32 => {
248 let value = ((registers[0] as u32) << 16) | (registers[1] as u32);
250 Ok(ModbusValue::Uint(value as u64))
251 }
252 ModbusDataType::Float32 => {
253 let bits = ((registers[0] as u32) << 16) | (registers[1] as u32);
255 let value = f32::from_bits(bits);
256 Ok(ModbusValue::Float(value as f64))
257 }
258 ModbusDataType::Float64 => {
259 let bits = ((registers[0] as u64) << 48)
261 | ((registers[1] as u64) << 32)
262 | ((registers[2] as u64) << 16)
263 | (registers[3] as u64);
264 let value = f64::from_bits(bits);
265 Ok(ModbusValue::Float(value))
266 }
267 ModbusDataType::Bit(bit_num) => {
268 let value = (registers[0] >> bit_num) & 1 == 1;
269 Ok(ModbusValue::Bool(value))
270 }
271 ModbusDataType::String(len) => {
272 let mut chars = Vec::with_capacity(len);
273 for reg in registers.iter().take((len + 1) / 2) {
274 chars.push((reg >> 8) as u8);
275 chars.push((reg & 0xFF) as u8);
276 }
277 chars.truncate(len);
278 while chars.last() == Some(&0) {
280 chars.pop();
281 }
282 let s = String::from_utf8_lossy(&chars).to_string();
283 Ok(ModbusValue::String(s))
284 }
285 }
286}
287
288pub fn encode_value(value: &ModbusValue, data_type: ModbusDataType) -> ModbusResult<Vec<u16>> {
290 match (value, data_type) {
291 (ModbusValue::Int(v), ModbusDataType::Int16) => {
292 let val = *v as i16;
293 Ok(vec![val as u16])
294 }
295 (ModbusValue::Uint(v), ModbusDataType::Uint16) => {
296 let val = *v as u16;
297 Ok(vec![val])
298 }
299 (ModbusValue::Int(v), ModbusDataType::Uint16) => {
300 let val = *v as u16;
301 Ok(vec![val])
302 }
303 (ModbusValue::Int(v), ModbusDataType::Int32) => {
304 let val = *v as i32;
305 Ok(vec![(val >> 16) as u16, (val & 0xFFFF) as u16])
306 }
307 (ModbusValue::Uint(v), ModbusDataType::Uint32) => {
308 let val = *v as u32;
309 Ok(vec![(val >> 16) as u16, (val & 0xFFFF) as u16])
310 }
311 (ModbusValue::Int(v), ModbusDataType::Uint32) => {
312 let val = *v as u32;
313 Ok(vec![(val >> 16) as u16, (val & 0xFFFF) as u16])
314 }
315 (ModbusValue::Float(v), ModbusDataType::Float32) => {
316 let bits = (*v as f32).to_bits();
317 Ok(vec![(bits >> 16) as u16, (bits & 0xFFFF) as u16])
318 }
319 (ModbusValue::Float(v), ModbusDataType::Float64) => {
320 let bits = v.to_bits();
321 Ok(vec![
322 (bits >> 48) as u16,
323 ((bits >> 32) & 0xFFFF) as u16,
324 ((bits >> 16) & 0xFFFF) as u16,
325 (bits & 0xFFFF) as u16,
326 ])
327 }
328 (ModbusValue::Bool(v), ModbusDataType::Bit(bit_num)) => {
329 let val = if *v { 1u16 << bit_num } else { 0 };
332 Ok(vec![val])
333 }
334 (ModbusValue::String(s), ModbusDataType::String(len)) => {
335 let mut registers = Vec::with_capacity((len + 1) / 2);
336 let bytes = s.as_bytes();
337 for i in (0..len).step_by(2) {
338 let high = bytes.get(i).copied().unwrap_or(0);
339 let low = bytes.get(i + 1).copied().unwrap_or(0);
340 registers.push(((high as u16) << 8) | (low as u16));
341 }
342 Ok(registers)
343 }
344 _ => Err(ModbusError::Io(std::io::Error::new(
345 std::io::ErrorKind::InvalidInput,
346 format!("Cannot encode {:?} as {:?}", value, data_type),
347 ))),
348 }
349}
350
351impl serde::Serialize for ModbusDataType {
356 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
357 serializer.serialize_str(&self.to_string())
358 }
359}
360
361impl<'de> serde::Deserialize<'de> for ModbusDataType {
362 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
363 let s = String::deserialize(deserializer)?;
364 s.parse().map_err(serde::de::Error::custom)
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 #[test]
373 fn test_data_type_from_str() {
374 assert_eq!(
375 "INT16".parse::<ModbusDataType>().unwrap(),
376 ModbusDataType::Int16
377 );
378 assert_eq!(
379 "UINT16".parse::<ModbusDataType>().unwrap(),
380 ModbusDataType::Uint16
381 );
382 assert_eq!(
383 "FLOAT32".parse::<ModbusDataType>().unwrap(),
384 ModbusDataType::Float32
385 );
386 assert_eq!(
387 "BIT5".parse::<ModbusDataType>().unwrap(),
388 ModbusDataType::Bit(5)
389 );
390 assert_eq!(
391 "STRING10".parse::<ModbusDataType>().unwrap(),
392 ModbusDataType::String(10)
393 );
394 }
395
396 #[test]
397 fn test_decode_int16() {
398 let regs = [0x00FF];
400 let val = decode_registers(®s, ModbusDataType::Int16).unwrap();
401 assert_eq!(val, ModbusValue::Int(255));
402
403 let regs = [0xFFFF];
405 let val = decode_registers(®s, ModbusDataType::Int16).unwrap();
406 assert_eq!(val, ModbusValue::Int(-1));
407 }
408
409 #[test]
410 fn test_decode_uint16() {
411 let regs = [0xFFFF];
412 let val = decode_registers(®s, ModbusDataType::Uint16).unwrap();
413 assert_eq!(val, ModbusValue::Uint(65535));
414 }
415
416 #[test]
417 fn test_decode_int32() {
418 let regs = [0x0001, 0x0000];
420 let val = decode_registers(®s, ModbusDataType::Int32).unwrap();
421 assert_eq!(val, ModbusValue::Int(65536));
422
423 let regs = [0xFFFF, 0xFFFF];
425 let val = decode_registers(®s, ModbusDataType::Int32).unwrap();
426 assert_eq!(val, ModbusValue::Int(-1));
427 }
428
429 #[test]
430 fn test_decode_float32() {
431 let regs = [0x3F80, 0x0000];
433 let val = decode_registers(®s, ModbusDataType::Float32).unwrap();
434 match val {
435 ModbusValue::Float(v) => assert!((v - 1.0).abs() < 0.0001),
436 _ => panic!("Expected Float"),
437 }
438
439 let regs = [0x41B4, 0x0000];
441 let val = decode_registers(®s, ModbusDataType::Float32).unwrap();
442 match val {
443 ModbusValue::Float(v) => assert!((v - 22.5).abs() < 0.0001),
444 _ => panic!("Expected Float"),
445 }
446 }
447
448 #[test]
449 fn test_decode_bit() {
450 let regs = [0b0000_0000_0010_0100]; let val = decode_registers(®s, ModbusDataType::Bit(2)).unwrap();
453 assert_eq!(val, ModbusValue::Bool(true));
454
455 let val = decode_registers(®s, ModbusDataType::Bit(5)).unwrap();
456 assert_eq!(val, ModbusValue::Bool(true));
457
458 let val = decode_registers(®s, ModbusDataType::Bit(0)).unwrap();
459 assert_eq!(val, ModbusValue::Bool(false));
460 }
461
462 #[test]
463 fn test_decode_string() {
464 let regs = [0x4142, 0x4344];
466 let val = decode_registers(®s, ModbusDataType::String(4)).unwrap();
467 assert_eq!(val, ModbusValue::String("ABCD".to_string()));
468 }
469
470 #[test]
471 fn test_linear_scaling() {
472 let scaling = LinearScaling::new(0.1, -40.0);
473
474 let raw = 625.0;
476 let physical = scaling.apply(raw);
477 assert!((physical - 22.5).abs() < 0.01);
478
479 let back = scaling.reverse(physical);
481 assert!((back - raw).abs() < 0.01);
482 }
483
484 #[test]
485 fn test_scaling_identity() {
486 let identity = LinearScaling::default();
487 assert!(identity.is_identity());
488
489 let non_identity = LinearScaling::new(0.1, 0.0);
490 assert!(!non_identity.is_identity());
491 }
492
493 #[test]
494 fn test_encode_int16() {
495 let val = ModbusValue::Int(-1);
496 let regs = encode_value(&val, ModbusDataType::Int16).unwrap();
497 assert_eq!(regs, vec![0xFFFF]);
498 }
499
500 #[test]
501 fn test_encode_float32() {
502 let val = ModbusValue::Float(1.0);
503 let regs = encode_value(&val, ModbusDataType::Float32).unwrap();
504 assert_eq!(regs, vec![0x3F80, 0x0000]);
505 }
506
507 #[test]
508 fn test_rdf_literal() {
509 assert_eq!(ModbusValue::Int(42).to_rdf_literal(), "42");
510 assert_eq!(ModbusValue::Float(22.5).to_rdf_literal(), "22.5");
511 assert_eq!(ModbusValue::Bool(true).to_rdf_literal(), "true");
512 assert_eq!(
513 ModbusValue::String("test".to_string()).to_rdf_literal(),
514 "test"
515 );
516 }
517
518 #[test]
519 fn test_xsd_datatypes() {
520 assert_eq!(
521 ModbusDataType::Int16.xsd_datatype(),
522 "http://www.w3.org/2001/XMLSchema#short"
523 );
524 assert_eq!(
525 ModbusDataType::Float32.xsd_datatype(),
526 "http://www.w3.org/2001/XMLSchema#float"
527 );
528 assert_eq!(
529 ModbusDataType::Bit(0).xsd_datatype(),
530 "http://www.w3.org/2001/XMLSchema#boolean"
531 );
532 }
533
534 #[test]
535 fn test_register_count() {
536 assert_eq!(ModbusDataType::Int16.register_count(), 1);
537 assert_eq!(ModbusDataType::Float32.register_count(), 2);
538 assert_eq!(ModbusDataType::Float64.register_count(), 4);
539 assert_eq!(ModbusDataType::String(10).register_count(), 5);
540 }
541}