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