sdm72_lib/
tokio_common.rs

1//! This module provides common data structures and error types for the `tokio`
2//! based clients.
3//!
4//! It defines the `Error` enum, which encapsulates all possible communication
5//! errors, and the `AllSettings` and `AllValues` structs, which are used to
6//! return all the settings and values from the device in one go.
7
8use crate::protocol as proto;
9
10/// Represents all possible errors that can occur during Modbus communication.
11#[derive(Debug, thiserror::Error)]
12pub enum Error {
13    /// An error originating from the protocol logic, such as invalid data.
14    #[error(transparent)]
15    Protocol(#[from] proto::Error),
16
17    /// A Modbus exception response from the device (e.g., "Illegal Function").
18    #[error(transparent)]
19    ModbusException(#[from] tokio_modbus::ExceptionCode),
20
21    /// A transport or communication error from the underlying `tokio-modbus` client.
22    #[error(transparent)]
23    Modbus(#[from] tokio_modbus::Error),
24}
25
26/// The result type for tokio operations.
27pub(crate) type Result<T> = std::result::Result<T, Error>;
28
29/// The number of data bits used for serial communication.
30pub const DATA_BITS: &tokio_serial::DataBits = &tokio_serial::DataBits::Eight;
31
32/// Creates and configures a `tokio_serial::SerialPortBuilder` for RTU communication.
33///
34/// This function sets up the standard communication parameters required by the
35/// SDM72 device: 8 data bits.
36///
37/// Note that this function only creates and configures the builder. It does not
38/// open the serial port, and therefore does not perform any I/O and cannot fail.
39/// The actual connection is established when this builder is used by a `tokio-modbus`
40/// client constructor.
41///
42/// # Arguments
43///
44/// * `device` - The path to the serial port device (e.g., `/dev/ttyUSB0` on Linux
45///   or `COM3` on Windows).
46/// * `baud_rate` - The baud rate for the serial communication.
47/// * `parity_and_stop_bits` - The parity and stop bit settings.
48pub fn serial_port_builder(
49    device: &str,
50    baud_rate: &proto::BaudRate,
51    parity_and_stop_bits: &proto::ParityAndStopBit,
52) -> tokio_serial::SerialPortBuilder {
53    let (parity, stop_bits) = match parity_and_stop_bits {
54        proto::ParityAndStopBit::NoParityOneStopBit => {
55            (tokio_serial::Parity::None, tokio_serial::StopBits::One)
56        }
57        proto::ParityAndStopBit::EvenParityOneStopBit => {
58            (tokio_serial::Parity::Even, tokio_serial::StopBits::One)
59        }
60        proto::ParityAndStopBit::OddParityOneStopBit => {
61            (tokio_serial::Parity::Odd, tokio_serial::StopBits::One)
62        }
63        proto::ParityAndStopBit::NoParityTwoStopBits => {
64            (tokio_serial::Parity::None, tokio_serial::StopBits::Two)
65        }
66    };
67    tokio_serial::new(device, u16::from(baud_rate) as u32)
68        .parity(parity)
69        .stop_bits(stop_bits)
70        .data_bits(*DATA_BITS)
71        // .timeout(timeout) // Do not work, set it to the context
72        .flow_control(tokio_serial::FlowControl::None)
73}
74
75/// A struct containing all the settings of the SDM72 meter.
76#[derive(Debug, Clone, Copy, PartialEq)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78pub struct AllSettings {
79    pub system_type: proto::SystemType,
80    pub pulse_width: proto::PulseWidth,
81    pub kppa: proto::KPPA,
82    pub parity_and_stop_bit: proto::ParityAndStopBit,
83    pub address: proto::Address,
84    pub pulse_constant: proto::PulseConstant,
85    pub password: proto::Password,
86    pub baud_rate: proto::BaudRate,
87    pub auto_scroll_time: proto::AutoScrollTime,
88    pub backlight_time: proto::BacklightTime,
89    pub pulse_energy_type: proto::PulseEnergyType,
90    pub serial_number: proto::SerialNumber,
91    pub meter_code: proto::MeterCode,
92    pub software_version: proto::SoftwareVersion,
93}
94impl std::fmt::Display for AllSettings {
95    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
96        writeln!(fmt, "System type: {}", self.system_type)?;
97        writeln!(fmt, "Pulse width: {}", self.pulse_width)?;
98        writeln!(fmt, "KPPA: {}", self.kppa)?;
99        writeln!(fmt, "Parity and stop bit: {}", self.parity_and_stop_bit)?;
100        writeln!(fmt, "Address: {}", self.address)?;
101        writeln!(fmt, "Pulse constant: {}", self.pulse_constant)?;
102        writeln!(fmt, "Password: {}", self.password)?;
103        writeln!(fmt, "Baud rate: {}", self.baud_rate)?;
104        writeln!(fmt, "Auto scroll time: {}", self.auto_scroll_time)?;
105        writeln!(fmt, "Backlight time: {}", self.backlight_time)?;
106        writeln!(fmt, "Pulse energy type: {}", self.pulse_energy_type)?;
107        writeln!(fmt, "Serial number: {}", self.serial_number)?;
108        writeln!(fmt, "Meter code: {}", self.meter_code)?;
109        write!(fmt, "Software version: {}", self.software_version)?;
110        Ok(())
111    }
112}
113
114/// A struct containing all the measurement values of the SDM72 meter.
115#[derive(Debug, Clone, Copy, PartialEq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct AllValues {
118    // L1
119    pub l1_voltage: proto::L1Voltage,
120    pub l2_voltage: proto::L2Voltage,
121    pub l3_voltage: proto::L3Voltage,
122    pub l1_current: proto::L1Current,
123    pub l2_current: proto::L2Current,
124    pub l3_current: proto::L3Current,
125    pub l1_power_active: proto::L1PowerActive,
126    pub l2_power_active: proto::L2PowerActive,
127    pub l3_power_active: proto::L3PowerActive,
128    pub l1_power_apparent: proto::L1PowerApparent,
129    pub l2_power_apparent: proto::L2PowerApparent,
130    pub l3_power_apparent: proto::L3PowerApparent,
131    pub l1_power_reactive: proto::L1PowerReactive,
132    pub l2_power_reactive: proto::L2PowerReactive,
133    pub l3_power_reactive: proto::L3PowerReactive,
134    pub l1_power_factor: proto::L1PowerFactor,
135    pub l2_power_factor: proto::L2PowerFactor,
136    pub l3_power_factor: proto::L3PowerFactor,
137    #[cfg_attr(feature = "serde", serde(rename = "l-n_average_voltage"))]
138    pub ln_average_voltage: proto::LtoNAverageVoltage,
139    #[cfg_attr(feature = "serde", serde(rename = "l-n_average_current"))]
140    pub ln_average_current: proto::LtoNAverageCurrent,
141    pub total_line_current: proto::TotalLineCurrent,
142    pub total_power: proto::TotalPower,
143    pub total_power_apparent: proto::TotalPowerApparent,
144    pub total_power_reactive: proto::TotalPowerReactive,
145    pub total_power_factor: proto::TotalPowerFactor,
146    pub frequency: proto::Frequency,
147    pub import_energy_active: proto::ImportEnergyActive,
148    pub export_energy_active: proto::ExportEnergyActive,
149
150    #[cfg_attr(feature = "serde", serde(rename = "l1-l2_voltage"))]
151    pub l1l2_voltage: proto::L1ToL2Voltage,
152    #[cfg_attr(feature = "serde", serde(rename = "l2-l3_voltage"))]
153    pub l2l3_voltage: proto::L2ToL3Voltage,
154    #[cfg_attr(feature = "serde", serde(rename = "l3-l1_voltage"))]
155    pub l3l1_voltage: proto::L3ToL1Voltage,
156    #[cfg_attr(feature = "serde", serde(rename = "l-l_average_voltage"))]
157    pub ll_average_voltage: proto::LtoLAverageVoltage,
158    pub neutral_current: proto::NeutralCurrent,
159
160    pub total_energy_active: proto::TotalEnergyActive,
161    pub total_energy_reactive: proto::TotalEnergyReactive,
162    pub resettable_total_energy_active: proto::ResettableTotalEnergyActive,
163    pub resettable_total_energy_reactive: proto::ResettableTotalEnergyReactive,
164    pub resettable_import_energy_active: proto::ResettableImportEnergyActive,
165    pub resettable_export_energy_active: proto::ResettableExportEnergyActive,
166    #[cfg_attr(feature = "serde", serde(rename = "net_kwh_import_-_export"))]
167    pub net_kwh: proto::NetKwh,
168
169    pub import_total_energy_active: proto::ImportTotalPowerActive,
170    pub export_total_energy_active: proto::ExportTotalPowerActive,
171}
172impl std::fmt::Display for AllValues {
173    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
174        writeln!(fmt, "L1 Voltage: {}", self.l1_voltage)?;
175        writeln!(fmt, "L2 Voltage: {}", self.l2_voltage)?;
176        writeln!(fmt, "L3 Voltage: {}", self.l3_voltage)?;
177        writeln!(fmt, "L1 Current: {}", self.l1_current)?;
178        writeln!(fmt, "L2 Current: {}", self.l2_current)?;
179        writeln!(fmt, "L3 Current: {}", self.l3_current)?;
180        writeln!(fmt, "L1 Power Active: {}", self.l1_power_active)?;
181        writeln!(fmt, "L2 Power Active: {}", self.l2_power_active)?;
182        writeln!(fmt, "L3 Power Active: {}", self.l3_power_active)?;
183        writeln!(fmt, "L1 Power Apparent: {}", self.l1_power_apparent)?;
184        writeln!(fmt, "L2 Power Apparent: {}", self.l2_power_apparent)?;
185        writeln!(fmt, "L3 Power Apparent: {}", self.l3_power_apparent)?;
186        writeln!(fmt, "L1 Power Reactive: {}", self.l1_power_reactive)?;
187        writeln!(fmt, "L2 Power Reactive: {}", self.l2_power_reactive)?;
188        writeln!(fmt, "L3 Power Reactive: {}", self.l3_power_reactive)?;
189        writeln!(fmt, "L1 Power Factor: {}", self.l1_power_factor)?;
190        writeln!(fmt, "L2 Power Factor: {}", self.l2_power_factor)?;
191        writeln!(fmt, "L3 Power Factor: {}", self.l3_power_factor)?;
192        writeln!(fmt, "L-N average Voltage: {}", self.ln_average_voltage)?;
193        writeln!(fmt, "L-N average Current: {}", self.ln_average_current)?;
194        writeln!(fmt, "Total Line Current: {}", self.total_line_current)?;
195        writeln!(fmt, "Total Power: {}", self.total_power)?;
196        writeln!(fmt, "Total Power Apparent: {}", self.total_power_apparent)?;
197        writeln!(fmt, "Total Power Reactive: {}", self.total_power_reactive)?;
198        writeln!(fmt, "Total Power Factor: {}", self.total_power_factor)?;
199        writeln!(fmt, "Frequency: {}", self.frequency)?;
200        writeln!(fmt, "Import Energy Active: {}", self.import_energy_active)?;
201        writeln!(fmt, "Export Energy Active: {}", self.export_energy_active)?;
202
203        writeln!(fmt, "L1-L2 Voltage: {}", self.l1l2_voltage)?;
204        writeln!(fmt, "L2-L3 Voltage: {}", self.l2l3_voltage)?;
205        writeln!(fmt, "L3-L1 Voltage: {}", self.l3l1_voltage)?;
206        writeln!(fmt, "L-L average Voltage: {}", self.ll_average_voltage)?;
207        writeln!(fmt, "Neutral Current: {}", self.neutral_current)?;
208
209        writeln!(fmt, "Total Energy Active: {}", self.total_energy_active)?;
210        writeln!(fmt, "Total Energy Reactive: {}", self.total_energy_reactive)?;
211        writeln!(
212            fmt,
213            "Resettable Total Energy Active: {}",
214            self.resettable_total_energy_active
215        )?;
216        writeln!(
217            fmt,
218            "Resettable Total Energy Reactive: {}",
219            self.resettable_total_energy_reactive
220        )?;
221        writeln!(
222            fmt,
223            "Resettable Import Energy Active: {}",
224            self.resettable_import_energy_active
225        )?;
226        writeln!(
227            fmt,
228            "Resettable Export Energy Active: {}",
229            self.resettable_export_energy_active
230        )?;
231        writeln!(fmt, "Net kWh (Import - Export): {}", self.net_kwh)?;
232
233        writeln!(
234            fmt,
235            "Import Total Energy Active: {}",
236            self.import_total_energy_active
237        )?;
238        write!(
239            fmt,
240            "Export Total Energy Active: {}",
241            self.export_total_energy_active
242        )?;
243
244        Ok(())
245    }
246}