1use serde::{Deserialize, Serialize};
8
9use crate::{obis::*, types::*};
10
11#[derive(Default, Debug, Serialize, Deserialize)]
13pub struct MeterReading {
14 pub to: Option<f64>,
15 pub by: Option<f64>,
16}
17
18#[derive(Default, Debug, Serialize, Deserialize)]
20pub struct Line {
21 pub voltage_sags: Option<u64>,
22 pub voltage_swells: Option<u64>,
23 pub voltage: Option<f64>,
24 pub current: Option<u64>,
25 pub active_power_plus: Option<f64>,
26 pub active_power_neg: Option<f64>,
27}
28
29#[derive(Default, Debug, Serialize, Deserialize)]
33pub struct Slave {
34 pub device_type: Option<u64>,
35 pub meter_reading: Option<(TST, f64)>,
36}
37
38#[derive(Default, Debug, Serialize, Deserialize)]
40pub struct State {
41 pub datetime: Option<TST>,
42 pub meterreadings: [MeterReading; 2],
43 pub tariff_indicator: Option<[u8; 2]>,
44 pub power_delivered: Option<f64>,
45 pub power_received: Option<f64>,
46 pub power_failures: Option<u64>,
47 pub long_power_failures: Option<u64>,
48 pub lines: [Line; 3],
49 pub slaves: [Slave; 4],
50}
51
52impl<'a> core::convert::TryFrom<&crate::Telegram<'a>> for State {
53 type Error = crate::Error;
54
55 fn try_from(t: &crate::Telegram<'a>) -> Result<Self, Self::Error> {
56 t.objects().try_fold(State::default(), |mut state, o| {
57 match o? {
58 OBIS::DateTime(tst) => {
59 state.datetime = Some(tst);
60 }
61 OBIS::MeterReadingTo(t, mr) => {
62 state.meterreadings[t as usize].to = Some(f64::from(&mr));
63 }
64 OBIS::MeterReadingBy(t, mr) => {
65 state.meterreadings[t as usize].by = Some(f64::from(&mr));
66 }
67 OBIS::TariffIndicator(ti) => {
68 let mut buf = [0u8; 2];
69 let mut octets = ti.as_octets();
70 buf[0] = octets.next().unwrap_or(Err(crate::Error::InvalidFormat))?;
71 buf[1] = octets.next().unwrap_or(Err(crate::Error::InvalidFormat))?;
72
73 state.tariff_indicator = Some(buf);
74 }
75 OBIS::PowerDelivered(p) => {
76 state.power_delivered = Some(f64::from(&p));
77 }
78 OBIS::PowerReceived(p) => {
79 state.power_received = Some(f64::from(&p));
80 }
81 OBIS::PowerFailures(UFixedInteger(pf)) => {
82 state.power_failures = Some(pf);
83 }
84 OBIS::LongPowerFailures(UFixedInteger(lpf)) => {
85 state.long_power_failures = Some(lpf);
86 }
87 OBIS::VoltageSags(l, UFixedInteger(n)) => {
88 state.lines[l as usize].voltage_sags = Some(n);
89 }
90 OBIS::VoltageSwells(l, UFixedInteger(n)) => {
91 state.lines[l as usize].voltage_swells = Some(n);
92 }
93 OBIS::InstantaneousVoltage(l, v) => {
94 state.lines[l as usize].voltage = Some(f64::from(&v));
95 }
96 OBIS::InstantaneousCurrent(l, UFixedInteger(a)) => {
97 state.lines[l as usize].current = Some(a);
98 }
99 OBIS::InstantaneousActivePowerPlus(l, p) => {
100 state.lines[l as usize].active_power_plus = Some(f64::from(&p));
101 }
102 OBIS::InstantaneousActivePowerNeg(l, p) => {
103 state.lines[l as usize].active_power_neg = Some(f64::from(&p));
104 }
105 OBIS::SlaveDeviceType(s, value_x) => {
106 if let Some(UFixedInteger(dt)) = value_x {
107 state.slaves[s as usize].device_type = Some(dt);
108 } else {
109 state.slaves[s as usize].device_type = None;
110 }
111 }
112 OBIS::SlaveMeterReading(s, tst, mr) => {
113 if let Some(mr_value) = mr {
114 state.slaves[s as usize].meter_reading = Some((tst, f64::from(&mr_value)));
115 } else {
116 state.slaves[s as usize].meter_reading = None;
117 }
118 }
119 _ => {} }
121 Ok(state)
122 })
123 }
124}
125
126impl<'a> core::convert::From<&crate::Telegram<'a>> for crate::Result<State> {
127 fn from(t: &crate::Telegram<'a>) -> Self {
128 t.try_into()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 #[test]
135 fn example() {
136 let mut buffer = [0u8; 2048];
137 let file = std::fs::read("test/isk.txt").unwrap();
138
139 let (left, _right) = buffer.split_at_mut(file.len());
140 left.copy_from_slice(file.as_slice());
141
142 let readout = crate::Readout { buffer };
143 let telegram = &readout.to_telegram().unwrap();
144 let state: super::State = telegram.try_into().unwrap();
145
146 assert_eq!(
147 state.datetime.as_ref().unwrap(),
148 &crate::types::TST {
149 year: 19,
150 month: 3,
151 day: 20,
152 hour: 18,
153 minute: 14,
154 second: 3,
155 dst: false
156 }
157 );
158
159 use crate::obis::Tariff::*;
160
161 assert_eq!(state.meterreadings[Tariff1 as usize].to.unwrap(), 576.239);
162 assert_eq!(state.meterreadings[Tariff2 as usize].to.unwrap(), 465.162);
163 assert_eq!(state.tariff_indicator.unwrap(), [0, 2]);
164
165 eprintln!("{:?}", state);
166 }
167}