1use crate::{
37 protocol::{self as proto, ModbusParam},
38 tokio_common::{AllSettings, AllValues, Result},
39};
40use tokio_modbus::prelude::{Reader, Writer};
41
42pub struct SDM72;
48
49macro_rules! read_holding {
51 ($func_name:expr, $ty:ident) => {
52 paste::item! {
53 #[doc = "Reads the [`proto::" $ty "`] value from the Modbus holding register."]
54 pub async fn $func_name(ctx: &mut tokio_modbus::client::Context) -> Result<proto::$ty> {
55 let rsp = ctx
56 .read_holding_registers(<proto::$ty>::ADDRESS, <proto::$ty>::QUANTITY).await??;
57 Ok(<proto::$ty>::decode_from_holding_registers(&rsp)?)
58 }
59 }
60 };
61}
62
63macro_rules! write_holding {
65 ($func_name:expr, $ty:ident) => {
66 paste::item! {
67 #[doc = "Writes the [`proto::" $ty "`] value to the Modbus holding register."]
68 pub async fn [< set_ $func_name >](ctx: &mut tokio_modbus::client::Context, value: proto::$ty) -> Result<()> {
69 Ok(ctx.write_multiple_registers(
70 <proto::$ty>::ADDRESS,
71 &value.encode_for_write_registers(),
72 ).await??)
73 }
74 }
75 };
76}
77
78impl SDM72 {
79 read_holding!(system_type, SystemType);
80 write_holding!(system_type, SystemType);
81 read_holding!(pulse_width, PulseWidth);
82 write_holding!(pulse_width, PulseWidth);
83 read_holding!(kppa, KPPA);
84 pub async fn set_kppa(
88 ctx: &mut tokio_modbus::client::Context,
89 password: proto::Password,
90 ) -> Result<()> {
91 Ok(ctx
92 .write_multiple_registers(
93 proto::KPPA::ADDRESS,
94 &proto::KPPA::encode_for_write_registers(password),
95 )
96 .await??)
97 }
98 read_holding!(parity_and_stop_bit, ParityAndStopBit);
99 write_holding!(parity_and_stop_bit, ParityAndStopBit);
100 read_holding!(address, Address);
101 write_holding!(address, Address);
102 read_holding!(pulse_constant, PulseConstant);
103 write_holding!(pulse_constant, PulseConstant);
104 read_holding!(password, Password);
105 write_holding!(password, Password);
106 read_holding!(baud_rate, BaudRate);
107 write_holding!(baud_rate, BaudRate);
108 read_holding!(auto_scroll_time, AutoScrollTime);
109 write_holding!(auto_scroll_time, AutoScrollTime);
110 read_holding!(backlight_time, BacklightTime);
111 write_holding!(backlight_time, BacklightTime);
112 read_holding!(pulse_energy_type, PulseEnergyType);
113 write_holding!(pulse_energy_type, PulseEnergyType);
114 pub async fn reset_historical_data(ctx: &mut tokio_modbus::client::Context) -> Result<()> {
118 Ok(ctx
119 .write_multiple_registers(
120 proto::ResetHistoricalData::ADDRESS,
121 &proto::ResetHistoricalData::encode_for_write_registers(),
122 )
123 .await??)
124 }
125 read_holding!(serial_number, SerialNumber);
126 read_holding!(meter_code, MeterCode);
127 read_holding!(software_version, SoftwareVersion);
128
129 pub async fn read_all_settings(
144 ctx: &mut tokio_modbus::client::Context,
145 delay: &std::time::Duration,
146 ) -> Result<AllSettings> {
147 let offset1 = proto::SystemType::ADDRESS;
148 let quantity =
149 { proto::PulseEnergyType::ADDRESS - offset1 + proto::PulseEnergyType::QUANTITY };
150 let rsp1 = ctx.read_holding_registers(offset1, quantity).await??;
151
152 tokio::time::sleep(*delay).await;
153 let serial_number = Self::serial_number(ctx).await?;
154 tokio::time::sleep(*delay).await;
155 let meter_code = Self::meter_code(ctx).await?;
156 tokio::time::sleep(*delay).await;
157 let software_version = Self::software_version(ctx).await?;
158
159 Ok(AllSettings {
160 system_type: crate::decode_subset_item_from_holding_register!(
161 offset1,
162 proto::SystemType,
163 &rsp1
164 )?,
165 pulse_width: crate::decode_subset_item_from_holding_register!(
166 offset1,
167 proto::PulseWidth,
168 &rsp1
169 )?,
170 kppa: crate::decode_subset_item_from_holding_register!(offset1, proto::KPPA, &rsp1)?,
171 parity_and_stop_bit: crate::decode_subset_item_from_holding_register!(
172 offset1,
173 proto::ParityAndStopBit,
174 &rsp1
175 )?,
176 address: crate::decode_subset_item_from_holding_register!(
177 offset1,
178 proto::Address,
179 &rsp1
180 )?,
181 pulse_constant: crate::decode_subset_item_from_holding_register!(
182 offset1,
183 proto::PulseConstant,
184 &rsp1
185 )?,
186 password: crate::decode_subset_item_from_holding_register!(
187 offset1,
188 proto::Password,
189 &rsp1
190 )?,
191 baud_rate: crate::decode_subset_item_from_holding_register!(
192 offset1,
193 proto::BaudRate,
194 &rsp1
195 )?,
196 auto_scroll_time: crate::decode_subset_item_from_holding_register!(
197 offset1,
198 proto::AutoScrollTime,
199 &rsp1
200 )?,
201 backlight_time: crate::decode_subset_item_from_holding_register!(
202 offset1,
203 proto::BacklightTime,
204 &rsp1
205 )?,
206 pulse_energy_type: crate::decode_subset_item_from_holding_register!(
207 offset1,
208 proto::PulseEnergyType,
209 &rsp1
210 )?,
211 serial_number,
212 meter_code,
213 software_version,
214 })
215 }
216
217 pub async fn read_all(
232 ctx: &mut tokio_modbus::client::Context,
233 delay: &std::time::Duration,
234 ) -> Result<AllValues> {
235 let offset1 = proto::L1Voltage::ADDRESS;
236 let quantity =
237 { proto::ExportEnergyActive::ADDRESS - offset1 + proto::ExportEnergyActive::QUANTITY };
238 let rsp1 = ctx.read_input_registers(offset1, quantity).await??;
239
240 tokio::time::sleep(*delay).await;
241
242 let offset2 = proto::L1ToL2Voltage::ADDRESS;
243 let quantity =
244 { proto::NeutralCurrent::ADDRESS - offset2 + proto::NeutralCurrent::QUANTITY };
245 let rsp2 = ctx.read_input_registers(offset2, quantity).await??;
246
247 tokio::time::sleep(*delay).await;
248
249 let offset3 = proto::TotalEnergyActive::ADDRESS;
250 let quantity = { proto::NetKwh::ADDRESS - offset3 + proto::NetKwh::QUANTITY };
251 let rsp3 = ctx.read_input_registers(offset3, quantity).await??;
252
253 tokio::time::sleep(*delay).await;
254
255 let offset4 = proto::ImportTotalPowerActive::ADDRESS;
256 let quantity = {
257 proto::ExportTotalPowerActive::ADDRESS - offset4
258 + proto::ExportTotalPowerActive::QUANTITY
259 };
260 let rsp4 = ctx.read_input_registers(offset4, quantity).await??;
261
262 Ok(AllValues {
263 l1_voltage: crate::decode_subset_item_from_input_register!(
264 offset1,
265 proto::L1Voltage,
266 &rsp1
267 )?,
268 l2_voltage: crate::decode_subset_item_from_input_register!(
269 offset1,
270 proto::L2Voltage,
271 &rsp1
272 )?,
273 l3_voltage: crate::decode_subset_item_from_input_register!(
274 offset1,
275 proto::L3Voltage,
276 &rsp1
277 )?,
278 l1_current: crate::decode_subset_item_from_input_register!(
279 offset1,
280 proto::L1Current,
281 &rsp1
282 )?,
283 l2_current: crate::decode_subset_item_from_input_register!(
284 offset1,
285 proto::L2Current,
286 &rsp1
287 )?,
288 l3_current: crate::decode_subset_item_from_input_register!(
289 offset1,
290 proto::L3Current,
291 &rsp1
292 )?,
293 l1_power_active: crate::decode_subset_item_from_input_register!(
294 offset1,
295 proto::L1PowerActive,
296 &rsp1
297 )?,
298 l2_power_active: crate::decode_subset_item_from_input_register!(
299 offset1,
300 proto::L2PowerActive,
301 &rsp1
302 )?,
303 l3_power_active: crate::decode_subset_item_from_input_register!(
304 offset1,
305 proto::L3PowerActive,
306 &rsp1
307 )?,
308 l1_power_apparent: crate::decode_subset_item_from_input_register!(
309 offset1,
310 proto::L1PowerApparent,
311 &rsp1
312 )?,
313 l2_power_apparent: crate::decode_subset_item_from_input_register!(
314 offset1,
315 proto::L2PowerApparent,
316 &rsp1
317 )?,
318 l3_power_apparent: crate::decode_subset_item_from_input_register!(
319 offset1,
320 proto::L3PowerApparent,
321 &rsp1
322 )?,
323 l1_power_reactive: crate::decode_subset_item_from_input_register!(
324 offset1,
325 proto::L1PowerReactive,
326 &rsp1
327 )?,
328 l2_power_reactive: crate::decode_subset_item_from_input_register!(
329 offset1,
330 proto::L2PowerReactive,
331 &rsp1
332 )?,
333 l3_power_reactive: crate::decode_subset_item_from_input_register!(
334 offset1,
335 proto::L3PowerReactive,
336 &rsp1
337 )?,
338 l1_power_factor: crate::decode_subset_item_from_input_register!(
339 offset1,
340 proto::L1PowerFactor,
341 &rsp1
342 )?,
343 l2_power_factor: crate::decode_subset_item_from_input_register!(
344 offset1,
345 proto::L2PowerFactor,
346 &rsp1
347 )?,
348 l3_power_factor: crate::decode_subset_item_from_input_register!(
349 offset1,
350 proto::L3PowerFactor,
351 &rsp1
352 )?,
353 ln_average_voltage: crate::decode_subset_item_from_input_register!(
354 offset1,
355 proto::LtoNAverageVoltage,
356 &rsp1
357 )?,
358 ln_average_current: crate::decode_subset_item_from_input_register!(
359 offset1,
360 proto::LtoNAverageCurrent,
361 &rsp1
362 )?,
363 total_line_current: crate::decode_subset_item_from_input_register!(
364 offset1,
365 proto::TotalLineCurrent,
366 &rsp1
367 )?,
368 total_power: crate::decode_subset_item_from_input_register!(
369 offset1,
370 proto::TotalPower,
371 &rsp1
372 )?,
373 total_power_apparent: crate::decode_subset_item_from_input_register!(
374 offset1,
375 proto::TotalPowerApparent,
376 &rsp1
377 )?,
378 total_power_reactive: crate::decode_subset_item_from_input_register!(
379 offset1,
380 proto::TotalPowerReactive,
381 &rsp1
382 )?,
383 total_power_factor: crate::decode_subset_item_from_input_register!(
384 offset1,
385 proto::TotalPowerFactor,
386 &rsp1
387 )?,
388 frequency: crate::decode_subset_item_from_input_register!(
389 offset1,
390 proto::Frequency,
391 &rsp1
392 )?,
393 import_energy_active: crate::decode_subset_item_from_input_register!(
394 offset1,
395 proto::ImportEnergyActive,
396 &rsp1
397 )?,
398 export_energy_active: crate::decode_subset_item_from_input_register!(
399 offset1,
400 proto::ExportEnergyActive,
401 &rsp1
402 )?,
403
404 l1l2_voltage: crate::decode_subset_item_from_input_register!(
405 offset2,
406 proto::L1ToL2Voltage,
407 &rsp2
408 )?,
409 l2l3_voltage: crate::decode_subset_item_from_input_register!(
410 offset2,
411 proto::L2ToL3Voltage,
412 &rsp2
413 )?,
414 l3l1_voltage: crate::decode_subset_item_from_input_register!(
415 offset2,
416 proto::L3ToL1Voltage,
417 &rsp2
418 )?,
419 ll_average_voltage: crate::decode_subset_item_from_input_register!(
420 offset2,
421 proto::LtoLAverageVoltage,
422 &rsp2
423 )?,
424 neutral_current: crate::decode_subset_item_from_input_register!(
425 offset2,
426 proto::NeutralCurrent,
427 &rsp2
428 )?,
429
430 total_energy_active: crate::decode_subset_item_from_input_register!(
431 offset3,
432 proto::TotalEnergyActive,
433 &rsp3
434 )?,
435 total_energy_reactive: crate::decode_subset_item_from_input_register!(
436 offset3,
437 proto::TotalEnergyReactive,
438 &rsp3
439 )?,
440 resettable_total_energy_active: crate::decode_subset_item_from_input_register!(
441 offset3,
442 proto::ResettableTotalEnergyActive,
443 &rsp3
444 )?,
445 resettable_total_energy_reactive: crate::decode_subset_item_from_input_register!(
446 offset3,
447 proto::ResettableTotalEnergyReactive,
448 &rsp3
449 )?,
450 resettable_import_energy_active: crate::decode_subset_item_from_input_register!(
451 offset3,
452 proto::ResettableImportEnergyActive,
453 &rsp3
454 )?,
455 resettable_export_energy_active: crate::decode_subset_item_from_input_register!(
456 offset3,
457 proto::ResettableExportEnergyActive,
458 &rsp3
459 )?,
460 net_kwh: crate::decode_subset_item_from_input_register!(offset3, proto::NetKwh, &rsp3)?,
461
462 import_total_energy_active: crate::decode_subset_item_from_input_register!(
463 offset4,
464 proto::ImportTotalPowerActive,
465 &rsp4
466 )?,
467 export_total_energy_active: crate::decode_subset_item_from_input_register!(
468 offset4,
469 proto::ExportTotalPowerActive,
470 &rsp4
471 )?,
472 })
473 }
474}