1#[derive(Debug, thiserror::Error)]
11pub enum Error {
12 #[error(
14 "The address value {0} is outside the permissible range of {min} to {max}",
15 min = Address::MIN,
16 max = Address::MAX
17 )]
18 AddressOutOfRange(u8),
19
20 #[error(
22 "The password value {0} is outside the permissible range of {min} to {max}",
23 min = Password::MIN,
24 max = Password::MAX
25 )]
26 PasswordOutOfRange(u16),
27
28 #[error(
30 "The auto scroll time value {0} is outside the permissible range of {min} to {max}",
31 min = AutoScrollTime::MIN,
32 max = AutoScrollTime::MAX
33 )]
34 AutoScrollTimeOutOfRange(u8),
35
36 #[error(
38 "The backlit time value {0} is outside the permissible range of {min} to {max}",
39 min = BacklightTime::MIN,
40 max = BacklightTime::MAX
41 )]
42 BacklitTimeOutOfRange(u8),
43
44 #[error("Baud rate must by any value of 1200, 2400, 4800, 9600, 19200.")]
46 InvalidBaudRate,
47
48 #[error("Value is outside the permissible range")]
50 OutOfRange,
51
52 #[error("Unexpected value")]
54 InvalidValue,
55
56 #[error("Words count error")]
58 WordsCountError,
59}
60
61pub type Word = u16;
63
64pub trait ModbusParam: Sized {
70 const ADDRESS: u16;
72 const QUANTITY: u16;
74 type ProtocolType;
76}
77
78macro_rules! words_to_protocol_value {
80 ($words:expr) => {{
81 let bytes = $words
82 .iter()
83 .copied()
84 .flat_map(u16::to_be_bytes)
85 .collect::<Vec<u8>>();
86 let array = bytes.try_into().or(Err(Error::WordsCountError))?;
87 Ok(<Self as ModbusParam>::ProtocolType::from_be_bytes(array))
88 }};
89}
90
91macro_rules! protocol_value_to_words {
93 ($val:expr) => {
94 $val.to_be_bytes()
95 .chunks(2)
96 .map(|chunk| {
97 let array = chunk.try_into().expect("unexpected encoding error");
98 u16::from_be_bytes(array)
99 })
100 .collect()
101 };
102}
103
104#[derive(Debug, Default, Clone, Copy, PartialEq)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109pub enum SystemType {
110 Type1P2W,
112
113 #[default]
114 Type3P4W,
116}
117impl ModbusParam for SystemType {
118 type ProtocolType = f32;
119 const ADDRESS: u16 = 0x000A;
120 const QUANTITY: u16 = 2;
121}
122impl SystemType {
123 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
124 let val = words_to_protocol_value!(words)?;
125 match val {
126 1.0 => Ok(SystemType::Type1P2W),
127 3.0 => Ok(SystemType::Type3P4W),
128 _ => Err(Error::InvalidValue),
129 }
130 }
131
132 pub fn encode_for_write_registers(&self) -> Vec<Word> {
133 let val = match self {
134 SystemType::Type1P2W => 1,
135 SystemType::Type3P4W => 3,
136 } as <Self as ModbusParam>::ProtocolType;
137 protocol_value_to_words!(val)
138 }
139}
140impl std::fmt::Display for SystemType {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match self {
143 SystemType::Type1P2W => write!(f, "1 phase 2 wire"),
144 SystemType::Type3P4W => write!(f, "3 phase 4 wire"),
145 }
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct PulseWidth(u16);
155impl ModbusParam for PulseWidth {
156 type ProtocolType = f32;
157 const ADDRESS: u16 = 0x000C;
158 const QUANTITY: u16 = 2;
159}
160impl std::ops::Deref for PulseWidth {
161 type Target = u16;
162 fn deref(&self) -> &Self::Target {
163 &self.0
164 }
165}
166impl Default for PulseWidth {
167 fn default() -> Self {
168 Self(100)
169 }
170}
171impl PulseWidth {
172 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
173 let val = words_to_protocol_value!(words)?;
174 Ok(Self(val as u16))
175 }
176
177 pub fn encode_for_write_registers(&self) -> Vec<Word> {
178 let val = self.0 as <Self as ModbusParam>::ProtocolType;
179 protocol_value_to_words!(val)
180 }
181}
182impl TryFrom<u16> for PulseWidth {
183 type Error = Error;
184
185 fn try_from(value: u16) -> Result<Self, Self::Error> {
186 Ok(Self(value))
187 }
188}
189impl std::fmt::Display for PulseWidth {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 write!(f, "{}", self.0)
192 }
193}
194
195#[derive(Debug, Clone, Copy, PartialEq)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199pub enum KPPA {
200 NotAuthorized,
201 Authorized,
202}
203impl ModbusParam for KPPA {
204 type ProtocolType = f32;
205 const ADDRESS: u16 = 0x000E;
206 const QUANTITY: u16 = 2;
207}
208impl KPPA {
209 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
210 let val = words_to_protocol_value!(words)?;
211 match val {
212 0.0 => Ok(Self::NotAuthorized),
213 1.0 => Ok(Self::Authorized),
214 _ => Err(Error::InvalidValue),
215 }
216 }
217
218 pub fn encode_for_write_registers(password: Password) -> Vec<Word> {
219 password.encode_for_write_registers()
220 }
221}
222impl std::fmt::Display for KPPA {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 match self {
225 KPPA::NotAuthorized => write!(f, "not authorized"),
226 KPPA::Authorized => write!(f, "authorized"),
227 }
228 }
229}
230
231#[derive(Debug, Default, Clone, Copy, PartialEq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236pub enum ParityAndStopBit {
237 #[default]
238 NoParityOneStopBit,
240
241 EvenParityOneStopBit,
243
244 OddParityOneStopBit,
246
247 NoParityTwoStopBits,
249}
250impl ModbusParam for ParityAndStopBit {
251 type ProtocolType = f32;
252 const ADDRESS: u16 = 0x0012;
253 const QUANTITY: u16 = 2;
254}
255impl ParityAndStopBit {
256 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
257 let val = words_to_protocol_value!(words)?;
258 match val {
259 0.0 => Ok(Self::NoParityOneStopBit),
260 1.0 => Ok(Self::EvenParityOneStopBit),
261 2.0 => Ok(Self::OddParityOneStopBit),
262 3.0 => Ok(Self::NoParityTwoStopBits),
263 _ => Err(Error::InvalidValue),
264 }
265 }
266
267 pub fn encode_for_write_registers(&self) -> Vec<Word> {
268 let val = match self {
269 Self::NoParityOneStopBit => 0,
270 Self::EvenParityOneStopBit => 1,
271 Self::OddParityOneStopBit => 2,
272 Self::NoParityTwoStopBits => 3,
273 } as <Self as ModbusParam>::ProtocolType;
274 protocol_value_to_words!(val)
275 }
276}
277impl std::fmt::Display for ParityAndStopBit {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 match self {
280 Self::NoParityOneStopBit => write!(f, "no parity one stop bit"),
281 Self::EvenParityOneStopBit => write!(f, "even parity one stop bit"),
282 Self::OddParityOneStopBit => write!(f, "odd parity one stop bit"),
283 Self::NoParityTwoStopBits => write!(f, "no parity two stop bit"),
284 }
285 }
286}
287
288#[derive(Debug, Clone, Copy, PartialEq)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294pub struct Address(u8);
295impl ModbusParam for Address {
296 type ProtocolType = f32;
297 const ADDRESS: u16 = 0x0014;
298 const QUANTITY: u16 = 2;
299}
300impl std::ops::Deref for Address {
301 type Target = u8;
302 fn deref(&self) -> &Self::Target {
303 &self.0
304 }
305}
306impl Default for Address {
307 fn default() -> Self {
308 Self(0x01)
309 }
310}
311impl Address {
312 pub const MIN: u8 = 1;
313 pub const MAX: u8 = 247;
314
315 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
316 let val = words_to_protocol_value!(words)?;
317 Ok(Self(val as u8))
318 }
319
320 pub fn encode_for_write_registers(&self) -> Vec<Word> {
321 let val = self.0 as <Self as ModbusParam>::ProtocolType;
322 protocol_value_to_words!(val)
323 }
324}
325impl TryFrom<u8> for Address {
326 type Error = Error;
327
328 fn try_from(value: u8) -> Result<Self, Self::Error> {
329 if (Self::MIN..=Self::MAX).contains(&value) {
330 Ok(Self(value))
331 } else {
332 Err(Error::AddressOutOfRange(value))
333 }
334 }
335}
336impl std::fmt::Display for Address {
337 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338 write!(f, "{:#04x}", self.0)
339 }
340}
341
342#[derive(Debug, Default, Clone, Copy, PartialEq)]
346#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
347pub enum PulseConstant {
348 #[default]
349 PC1000,
351
352 PC100,
354
355 PC10,
357
358 PC1,
360}
361impl ModbusParam for PulseConstant {
362 type ProtocolType = f32;
363 const ADDRESS: u16 = 0x0016;
364 const QUANTITY: u16 = 2;
365}
366impl PulseConstant {
367 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
368 let val = words_to_protocol_value!(words)?;
369 match val {
370 0.0 => Ok(Self::PC1000),
371 1.0 => Ok(Self::PC100),
372 2.0 => Ok(Self::PC10),
373 3.0 => Ok(Self::PC1),
374 _ => Err(Error::InvalidValue),
375 }
376 }
377
378 pub fn encode_for_write_registers(&self) -> Vec<Word> {
379 let val = match self {
380 Self::PC1000 => 0,
381 Self::PC100 => 1,
382 Self::PC10 => 2,
383 Self::PC1 => 3,
384 } as <Self as ModbusParam>::ProtocolType;
385 protocol_value_to_words!(val)
386 }
387}
388impl std::fmt::Display for PulseConstant {
389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
390 match self {
391 Self::PC1000 => write!(f, "1000 imp/kWh"),
392 Self::PC100 => write!(f, "100 imp/kWh"),
393 Self::PC10 => write!(f, "10 imp/kWh"),
394 Self::PC1 => write!(f, "1 imp/kWh"),
395 }
396 }
397}
398
399#[derive(Debug, Clone, Copy, PartialEq)]
403#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
404pub struct Password(u16);
405impl ModbusParam for Password {
406 type ProtocolType = f32;
407 const ADDRESS: u16 = 0x0018;
408 const QUANTITY: u16 = 2;
409}
410impl std::ops::Deref for Password {
411 type Target = u16;
412 fn deref(&self) -> &Self::Target {
413 &self.0
414 }
415}
416impl Default for Password {
417 fn default() -> Self {
418 Self(1000)
419 }
420}
421impl Password {
422 pub const MIN: u16 = 0;
423 pub const MAX: u16 = 9999;
424
425 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
426 let val = words_to_protocol_value!(words)?;
427 Ok(Self(val as u16))
428 }
429
430 pub fn encode_for_write_registers(&self) -> Vec<Word> {
431 let val = self.0 as <Self as ModbusParam>::ProtocolType;
432 protocol_value_to_words!(val)
433 }
434}
435impl TryFrom<u16> for Password {
436 type Error = Error;
437
438 fn try_from(value: u16) -> Result<Self, Self::Error> {
439 if (Self::MIN..=Self::MAX).contains(&value) {
440 Ok(Self(value))
441 } else {
442 Err(Error::PasswordOutOfRange(value))
443 }
444 }
445}
446impl std::fmt::Display for Password {
447 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
448 write!(f, "{:04}", self.0)
449 }
450}
451
452#[derive(Debug, Default, Clone, Copy, PartialEq)]
457#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
458pub enum BaudRate {
459 B1200,
460 B2400,
461 B4800,
462 #[default]
463 B9600,
464 B19200,
465}
466impl ModbusParam for BaudRate {
467 type ProtocolType = f32;
468 const ADDRESS: u16 = 0x001C;
469 const QUANTITY: u16 = 2;
470}
471impl BaudRate {
472 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
473 let val = words_to_protocol_value!(words)?;
474 match val {
475 5.0 => Ok(Self::B1200),
476 0.0 => Ok(Self::B2400),
477 1.0 => Ok(Self::B4800),
478 2.0 => Ok(Self::B9600),
479 3.0 => Ok(Self::B19200),
480 _ => Err(Error::InvalidValue),
481 }
482 }
483
484 pub fn encode_for_write_registers(&self) -> Vec<Word> {
485 let val = match self {
486 Self::B1200 => 5,
487 Self::B2400 => 0,
488 Self::B4800 => 1,
489 Self::B9600 => 2,
490 Self::B19200 => 3,
491 } as <Self as ModbusParam>::ProtocolType;
492 protocol_value_to_words!(val)
493 }
494
495 pub fn decode(words: &[Word]) -> Result<u16, Error> {
496 let val = words_to_protocol_value!(words)?;
497 Ok(val as u16)
498 }
499}
500impl TryFrom<u16> for BaudRate {
501 type Error = Error;
502
503 fn try_from(value: u16) -> Result<Self, Self::Error> {
504 match value {
505 1200 => Ok(BaudRate::B1200),
506 2400 => Ok(BaudRate::B2400),
507 4800 => Ok(BaudRate::B4800),
508 9600 => Ok(BaudRate::B9600),
509 19200 => Ok(BaudRate::B19200),
510 _ => Err(Error::InvalidBaudRate),
511 }
512 }
513}
514impl From<&BaudRate> for u16 {
515 fn from(baud_rate: &BaudRate) -> u16 {
516 match baud_rate {
517 BaudRate::B1200 => 1200,
518 BaudRate::B2400 => 2400,
519 BaudRate::B4800 => 4800,
520 BaudRate::B9600 => 9600,
521 BaudRate::B19200 => 19200,
522 }
523 }
524}
525impl std::fmt::Display for BaudRate {
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527 write!(f, "{}", u16::from(self))
528 }
529}
530
531#[derive(Debug, Clone, Copy, PartialEq)]
536#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
537pub struct AutoScrollTime(u8);
538impl ModbusParam for AutoScrollTime {
539 type ProtocolType = f32;
540 const ADDRESS: u16 = 0x003A;
541 const QUANTITY: u16 = 2;
542}
543impl Default for AutoScrollTime {
544 fn default() -> Self {
545 Self(5)
546 }
547}
548impl std::ops::Deref for AutoScrollTime {
549 type Target = u8;
550 fn deref(&self) -> &Self::Target {
551 &self.0
552 }
553}
554impl AutoScrollTime {
555 pub const MIN: u8 = 0;
556 pub const MAX: u8 = 60;
557
558 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
559 let val = words_to_protocol_value!(words)?;
560 Ok(Self(val as u8))
561 }
562
563 pub fn encode_for_write_registers(&self) -> Vec<Word> {
564 let val = self.0 as <Self as ModbusParam>::ProtocolType;
565 protocol_value_to_words!(val)
566 }
567
568 pub fn decode(words: &[Word]) -> Result<u8, Error> {
569 let val = words_to_protocol_value!(words)?;
570 Ok(val as u8)
571 }
572}
573impl TryFrom<u8> for AutoScrollTime {
574 type Error = Error;
575
576 fn try_from(value: u8) -> Result<Self, Self::Error> {
577 if (Self::MIN..=Self::MAX).contains(&value) {
578 Ok(Self(value))
579 } else {
580 Err(Error::AutoScrollTimeOutOfRange(value))
581 }
582 }
583}
584impl std::fmt::Display for AutoScrollTime {
585 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
586 write!(f, "{} sec", self.0)
587 }
588}
589
590#[derive(Debug, Clone, Copy, PartialEq)]
595#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
596pub enum BacklightTime {
597 AlwaysOn,
598 AlwaysOff,
599 Delayed(u8),
600}
601impl ModbusParam for BacklightTime {
602 type ProtocolType = f32;
603 const ADDRESS: u16 = 0x003C;
604 const QUANTITY: u16 = 2;
605}
606impl Default for BacklightTime {
607 fn default() -> Self {
608 Self::Delayed(60)
609 }
610}
611impl BacklightTime {
612 pub const MIN: u8 = 1;
613 pub const MAX: u8 = 120;
614
615 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
616 let val = words_to_protocol_value!(words)?;
617 match val {
618 0.0 => Ok(Self::AlwaysOn),
619 121.0 => Ok(Self::AlwaysOff),
620 _ => Ok(Self::Delayed(val as u8)),
621 }
622 }
623
624 pub fn encode_for_write_registers(&self) -> Vec<Word> {
625 let val = match self {
626 Self::AlwaysOn => 0,
627 Self::AlwaysOff => 121,
628 Self::Delayed(val) => *val,
629 } as <Self as ModbusParam>::ProtocolType;
630 protocol_value_to_words!(val)
631 }
632}
633impl TryFrom<u8> for BacklightTime {
634 type Error = Error;
635
636 fn try_from(value: u8) -> Result<Self, Self::Error> {
637 if (Self::MIN..=Self::MAX).contains(&value) {
638 Ok(Self::Delayed(value))
639 } else if value == 0 {
640 Ok(Self::AlwaysOn)
641 } else if value == 121 {
642 Ok(Self::AlwaysOff)
643 } else {
644 Err(Error::BacklitTimeOutOfRange(value))
645 }
646 }
647}
648impl std::fmt::Display for BacklightTime {
649 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
650 match self {
651 Self::AlwaysOn => write!(f, "always on"),
652 Self::AlwaysOff => write!(f, "always off"),
653 Self::Delayed(val) => write!(f, "{val} min"),
654 }
655 }
656}
657
658#[derive(Debug, Default, Clone, Copy, PartialEq)]
660#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
661pub enum PulseEnergyType {
662 ImportActiveEnergy,
663
664 #[default]
665 TotalActiveEnergy,
666
667 ExportActiveEnergy,
668}
669impl ModbusParam for PulseEnergyType {
670 type ProtocolType = f32;
671 const ADDRESS: u16 = 0x0056;
672 const QUANTITY: u16 = 2;
673}
674impl PulseEnergyType {
675 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
676 let val = words_to_protocol_value!(words)?;
677 match val {
678 1.0 => Ok(Self::ImportActiveEnergy),
679 2.0 => Ok(Self::TotalActiveEnergy),
680 4.0 => Ok(Self::ExportActiveEnergy),
681 _ => Err(Error::InvalidValue),
682 }
683 }
684
685 pub fn encode_for_write_registers(&self) -> Vec<Word> {
686 let val = match self {
687 Self::ImportActiveEnergy => 1,
688 Self::TotalActiveEnergy => 2,
689 Self::ExportActiveEnergy => 4,
690 } as <Self as ModbusParam>::ProtocolType;
691 protocol_value_to_words!(val)
692 }
693}
694impl std::fmt::Display for PulseEnergyType {
695 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696 match self {
697 Self::ImportActiveEnergy => write!(f, "import active energy"),
698 Self::TotalActiveEnergy => write!(f, "total active energy"),
699 Self::ExportActiveEnergy => write!(f, "export active energy"),
700 }
701 }
702}
703
704pub struct ResetHistoricalData;
708impl ModbusParam for ResetHistoricalData {
709 type ProtocolType = u16;
710 const ADDRESS: u16 = 0xF010;
711 const QUANTITY: u16 = 1;
712}
713impl ResetHistoricalData {
714 pub fn encode_for_write_registers() -> Vec<Word> {
715 let val = 0x0003 as <Self as ModbusParam>::ProtocolType;
716 protocol_value_to_words!(val)
717 }
718}
719#[derive(Debug, Clone, Copy, PartialEq)]
720#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
721pub struct SerialNumber(u32);
722impl ModbusParam for SerialNumber {
723 type ProtocolType = u32;
724 const ADDRESS: u16 = 0xFC00;
725 const QUANTITY: u16 = 2;
726}
727impl SerialNumber {
728 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
729 let val = words_to_protocol_value!(words)?;
730 Ok(Self(val))
731 }
732}
733impl std::ops::Deref for SerialNumber {
734 type Target = u32;
735 fn deref(&self) -> &Self::Target {
736 &self.0
737 }
738}
739impl std::fmt::Display for SerialNumber {
740 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
741 write!(f, "{}", self.0)
742 }
743}
744
745#[derive(Debug, Clone, Copy, PartialEq)]
747#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
748pub struct MeterCode(u16);
749impl ModbusParam for MeterCode {
750 type ProtocolType = u16;
751 const ADDRESS: u16 = 0xFC02;
752 const QUANTITY: u16 = 1;
753}
754impl MeterCode {
755 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
756 let val = words_to_protocol_value!(words)?;
757 Ok(Self(val))
758 }
759}
760impl std::ops::Deref for MeterCode {
761 type Target = u16;
762 fn deref(&self) -> &Self::Target {
763 &self.0
764 }
765}
766impl std::fmt::Display for MeterCode {
767 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
768 write!(f, "{:0>4x}", self.0)
769 }
770}
771
772#[derive(Debug, Clone, Copy, PartialEq)]
774#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
775pub struct SoftwareVersion(u16);
776impl ModbusParam for SoftwareVersion {
777 type ProtocolType = u16;
778 const ADDRESS: u16 = 0xFC84;
779 const QUANTITY: u16 = 1;
780}
781impl SoftwareVersion {
782 pub fn decode_from_holding_registers(words: &[Word]) -> Result<Self, Error> {
783 let val = words_to_protocol_value!(words)?;
784 Ok(Self(val))
785 }
786}
787impl std::ops::Deref for SoftwareVersion {
788 type Target = u16;
789 fn deref(&self) -> &Self::Target {
790 &self.0
791 }
792}
793impl std::fmt::Display for SoftwareVersion {
794 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
795 write!(f, "{:0>2x}.{:0>2x}", (self.0 >> 8) as u8, self.0 as u8)
796 }
797}
798
799pub trait ModbusInputRegister: ModbusParam {
809 fn decode_from_input_register(words: &[Word]) -> Result<Self, Error>;
811}
812
813fn f32round(val: f32) -> f32 {
814 ((val as f64 * 100.).round() / 100.) as f32
815}
816
817#[cfg(feature = "serde")]
818fn f32ser2<S>(fv: &f32, se: S) -> Result<S::Ok, S::Error>
819where
820 S: serde::Serializer,
821{
822 se.serialize_f32(f32round(*fv))
823}
824
825macro_rules! modbus_input_register {
831 ($vis:vis $ty:ident, $address:expr, $quantity:expr, $protocol_type:ty) => {
832 #[derive(Debug, Clone, Copy, PartialEq)]
833 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
834 $vis struct $ty(
835 #[cfg_attr(feature = "serde", serde(serialize_with = "f32ser2"))]
836 $protocol_type,
837 );
838 impl std::fmt::Display for $ty {
839 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
840 write!(fmt, "{}", f32round(self.0))
841 }
842 }
843
844 impl ModbusParam for $ty {
845 type ProtocolType = $protocol_type;
846 const ADDRESS: u16 = $address;
847 const QUANTITY: u16 = $quantity;
848 }
849
850 impl std::ops::Deref for $ty {
851 type Target = $protocol_type;
852 fn deref(&self) -> &Self::Target {
853 &self.0
854 }
855 }
856
857 impl $ty {
858 pub fn decode_from_input_register(words: &[Word]) -> Result<Self, Error> {
859 let val = words_to_protocol_value!(words)?;
860 Ok(Self(val as $protocol_type))
861 }
862 }
863 };
864}
865#[macro_export]
869macro_rules! get_subset_register_range {
870 ($offset:expr, $register_name:ty) => {{
871 (<$register_name>::ADDRESS - $offset) as usize
872 ..(<$register_name>::ADDRESS - $offset + <$register_name>::QUANTITY) as usize
873 }};
874}
875
876#[macro_export]
880macro_rules! decode_subset_item_from_holding_register {
881 ($offset:expr, $register_name:ty, $rsp:expr) => {{
882 <$register_name>::decode_from_holding_registers(
883 &$rsp[$crate::get_subset_register_range!($offset, $register_name)],
884 )
885 }};
886}
887
888#[macro_export]
892macro_rules! decode_subset_item_from_input_register {
893 ($offset:expr, $register_name:ty, $rsp:expr) => {{
894 <$register_name>::decode_from_input_register(
895 &$rsp[$crate::get_subset_register_range!($offset, $register_name)],
896 )
897 }};
898}
899
900modbus_input_register!(pub L1Voltage, 0x0000, 2, f32);
902modbus_input_register!(pub L2Voltage, 0x0002, 2, f32);
903modbus_input_register!(pub L3Voltage, 0x0004, 2, f32);
904modbus_input_register!(pub L1Current, 0x0006, 2, f32);
905modbus_input_register!(pub L2Current, 0x0008, 2, f32);
906modbus_input_register!(pub L3Current, 0x000A, 2, f32);
907modbus_input_register!(pub L1PowerActive, 0x000C, 2, f32);
908modbus_input_register!(pub L2PowerActive, 0x000E, 2, f32);
909modbus_input_register!(pub L3PowerActive, 0x0010, 2, f32);
910modbus_input_register!(pub L1PowerApparent, 0x0012, 2, f32);
911modbus_input_register!(pub L2PowerApparent, 0x0014, 2, f32);
912modbus_input_register!(pub L3PowerApparent, 0x0016, 2, f32);
913modbus_input_register!(pub L1PowerReactive, 0x0018, 2, f32);
914modbus_input_register!(pub L2PowerReactive, 0x001A, 2, f32);
915modbus_input_register!(pub L3PowerReactive, 0x001C, 2, f32);
916modbus_input_register!(pub L1PowerFactor, 0x0001E, 2, f32);
917modbus_input_register!(pub L2PowerFactor, 0x0020, 2, f32);
918modbus_input_register!(pub L3PowerFactor, 0x0022, 2, f32);
919modbus_input_register!(pub LtoNAverageVoltage, 0x002A, 2, f32);
920modbus_input_register!(pub LtoNAverageCurrent, 0x002E, 2, f32);
921modbus_input_register!(pub TotalLineCurrent, 0x0030, 2, f32);
922modbus_input_register!(pub TotalPower, 0x0034, 2, f32);
923modbus_input_register!(pub TotalPowerApparent, 0x0038, 2, f32);
924modbus_input_register!(pub TotalPowerReactive, 0x003C, 2, f32);
925modbus_input_register!(pub TotalPowerFactor, 0x003E, 2, f32);
926modbus_input_register!(pub Frequency, 0x0046, 2, f32);
927modbus_input_register!(pub ImportEnergyActive, 0x0048, 2, f32);
928modbus_input_register!(pub ExportEnergyActive, 0x004A, 2, f32);
929modbus_input_register!(pub L1ToL2Voltage, 0x00C8, 2, f32);
931modbus_input_register!(pub L2ToL3Voltage, 0x00CA, 2, f32);
932modbus_input_register!(pub L3ToL1Voltage, 0x00CC, 2, f32);
933modbus_input_register!(pub LtoLAverageVoltage, 0x00CE, 2, f32);
934modbus_input_register!(pub NeutralCurrent, 0x00E0, 2, f32);
935modbus_input_register!(pub TotalEnergyActive, 0x0156, 2, f32);
937modbus_input_register!(pub TotalEnergyReactive, 0x0158, 2, f32);
938modbus_input_register!(pub ResettableTotalEnergyActive, 0x0180, 2, f32);
939modbus_input_register!(pub ResettableTotalEnergyReactive, 0x0182, 2, f32);
940modbus_input_register!(pub ResettableImportEnergyActive, 0x0184, 2, f32);
941modbus_input_register!(pub ResettableExportEnergyActive, 0x0186, 2, f32);
942modbus_input_register!(pub NetKwh, 0x018C, 2, f32);
943modbus_input_register!(pub ImportTotalPowerActive, 0x0500, 2, f32);
944modbus_input_register!(pub ExportTotalPowerActive, 0x0502, 2, f32);