r4dcb08_lib/tokio_async.rs
1//! Asynchronous `tokio-modbus` client for the R4DCB08 temperature module.
2//!
3//! This module provides a high-level API (`R4DCB08` struct) to interact with
4//! the R4DCB08 8-channel temperature module using Modbus RTU or TCP. It handles
5//! the conversion between Rust types defined in the `crate::protocol` module and
6//! the raw Modbus register values.
7//!
8//! All client methods are `async` and must be `.await`ed.
9//!
10//! # Examples
11//!
12//! ## TCP Client Example
13//!
14//! ```no_run
15//! use r4dcb08_lib::tokio_async::R4DCB08;
16//! use std::net::SocketAddr;
17//! use std::time::Duration;
18//!
19//! #[tokio::main]
20//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
21//! let socket_addr: SocketAddr = "127.0.0.1:502".parse()?;
22//!
23//! // Connect to the Modbus TCP device
24//! let mut modbus_ctx = tokio_modbus::client::tcp::connect(socket_addr).await?;
25//!
26//! // Read temperatures from all 8 channels with a timeout
27//! let result = tokio::time::timeout(
28//! Duration::from_secs(1),
29//! R4DCB08::read_temperatures(&mut modbus_ctx),
30//! )
31//! .await;
32//!
33//! match result {
34//! Ok(Ok(temperatures)) => println!("Temperatures: {}", temperatures),
35//! Ok(Err(e)) => eprintln!("Modbus error: {}", e),
36//! Err(e) => eprintln!("Timeout error: {}", e),
37//! }
38//!
39//! Ok(())
40//! }
41//! ```
42//!
43//! ## RTU Client Example
44//!
45//! ```no_run
46//! use r4dcb08_lib::tokio_async::R4DCB08;
47//! use r4dcb08_lib::protocol::{Address, BaudRate};
48//! use std::time::Duration;
49//!
50//! #[tokio::main]
51//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
52//! let builder = r4dcb08_lib::tokio_common::serial_port_builder(
53//! "/dev/ttyUSB0", // Or "COM3" on Windows, etc.
54//! &BaudRate::B9600,
55//! );
56//! let port = tokio_serial::SerialStream::open(&builder)?;
57//! let slave = tokio_modbus::Slave(1);
58//! let mut modbus_ctx = tokio_modbus::client::rtu::attach_slave(port, slave);
59//!
60//! // Read the device's configured baud rate with a timeout
61//! let result = tokio::time::timeout(
62//! Duration::from_secs(1),
63//! R4DCB08::read_baud_rate(&mut modbus_ctx),
64//! )
65//! .await;
66//!
67//! match result {
68//! Ok(Ok(remote_baud_rate)) => println!("Device baud rate: {}", remote_baud_rate),
69//! Ok(Err(e)) => eprintln!("Modbus error: {}", e),
70//! Err(e) => eprintln!("Timeout error: {}", e),
71//! }
72//!
73//! Ok(())
74//! }
75//! ```
76
77use crate::{protocol as proto, tokio_common::Result};
78use tokio_modbus::prelude::{Reader, Writer};
79
80/// Asynchronous client for interacting with the R4DCB08 temperature module over Modbus.
81///
82/// This struct provides methods to read sensor data and configure the module's
83/// operational parameters by wrapping `tokio-modbus` asynchronous operations.
84///
85/// All methods that interact with the Modbus device are `async` and return `Future`s.
86#[derive(Debug)]
87pub struct R4DCB08;
88
89impl R4DCB08 {
90 /// Helper function to map tokio result to our result.
91 fn map_tokio_result<T>(result: tokio_modbus::Result<T>) -> Result<T> {
92 match result {
93 Ok(Ok(result)) => Ok(result),
94 Ok(Err(err)) => Err(err.into()), // Modbus exception
95 Err(err) => Err(err.into()), // IO error
96 }
97 }
98
99 /// Helper function to read holding registers and decode them into a specific type.
100 async fn read_and_decode<T, F>(
101 ctx: &mut tokio_modbus::client::Context,
102 address: u16,
103 quantity: u16,
104 decoder: F,
105 ) -> Result<T>
106 where
107 F: FnOnce(&[u16]) -> std::result::Result<T, proto::Error>,
108 {
109 Ok(decoder(&Self::map_tokio_result(
110 ctx.read_holding_registers(address, quantity).await,
111 )?)?)
112 }
113
114 /// Reads the current temperatures from all 8 available channels in degrees Celsius (°C).
115 ///
116 /// If a channel's sensor is not connected or reports an error, the corresponding
117 /// `proto::Temperature` value will be `proto::Temperature::NAN`.
118 ///
119 /// # Returns
120 ///
121 /// A `Result<proto::Temperatures>` containing the temperatures for all channels,
122 /// or a Modbus error.
123 ///
124 /// # Errors
125 ///
126 /// * `tokio_modbus::Error` if a Modbus communication error occurs (e.g., IO error, timeout handled by wrapper, Modbus exception).
127 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidData` if the device returns
128 /// an unexpected number of registers.
129 ///
130 /// # Examples
131 ///
132 /// ```no_run
133 /// # use r4dcb08_lib::tokio_async::R4DCB08;
134 /// # use std::time::Duration;
135 /// # #[tokio::main]
136 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
137 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
138 /// let temperatures = tokio::time::timeout(Duration::from_secs(2), R4DCB08::read_temperatures(&mut modbus_ctx)).await??;
139 /// println!("Temperatures read successfully:");
140 /// for (i, temp) in temperatures.iter().enumerate() {
141 /// println!(" Channel {}: {}", i, temp); // `temp` uses Display impl from protocol
142 /// }
143 /// # Ok(())
144 /// # }
145 /// ```
146 pub async fn read_temperatures(
147 ctx: &mut tokio_modbus::client::Context,
148 ) -> Result<proto::Temperatures> {
149 Self::read_and_decode(
150 ctx,
151 proto::Temperatures::ADDRESS,
152 proto::Temperatures::QUANTITY,
153 proto::Temperatures::decode_from_holding_registers,
154 )
155 .await
156 }
157
158 /// Reads the configured temperature correction values (°C) for all 8 channels.
159 ///
160 /// A `proto::Temperature` value of `0.0` typically means no correction is applied,
161 /// while `proto::Temperature::NAN` might indicate an uninitialized or error state for a correction value if read.
162 ///
163 /// # Returns
164 ///
165 /// A `Result<proto::TemperatureCorrection>` containing correction values for each channel,
166 /// or a Modbus error.
167 ///
168 /// # Errors
169 ///
170 /// * `tokio_modbus::Error` for Modbus communication errors.
171 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidData` if the device returns
172 /// an unexpected number of registers.
173 ///
174 /// # Examples
175 ///
176 /// ```no_run
177 /// # use r4dcb08_lib::tokio_async::R4DCB08;
178 /// # use std::time::Duration;
179 /// # #[tokio::main]
180 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
181 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
182 /// let corrections = tokio::time::timeout(Duration::from_secs(2), R4DCB08::read_temperature_correction(&mut modbus_ctx)).await??;
183 /// println!("Temperature correction values: {}", corrections);
184 /// # Ok(())
185 /// # }
186 /// ```
187 pub async fn read_temperature_correction(
188 ctx: &mut tokio_modbus::client::Context,
189 ) -> Result<proto::TemperatureCorrection> {
190 Self::read_and_decode(
191 ctx,
192 proto::TemperatureCorrection::ADDRESS,
193 proto::TemperatureCorrection::QUANTITY,
194 proto::TemperatureCorrection::decode_from_holding_registers,
195 )
196 .await
197 }
198
199 /// Sets a temperature correction value for a specific channel.
200 ///
201 /// The `correction` value will be added to the raw temperature reading by the module.
202 /// Setting a correction value of `0.0` effectively disables it for that channel.
203 ///
204 /// # Arguments
205 ///
206 /// * `channel` - The `proto::Channel` to configure.
207 /// * `correction` - The `proto::Temperature` correction value to apply (in °C).
208 /// This type ensures the temperature value is within the representable range.
209 ///
210 /// # Returns
211 ///
212 /// A `Result<()>` indicating success or failure of the write operation.
213 ///
214 /// # Errors
215 ///
216 /// * `tokio_modbus::Error` for Modbus communication errors.
217 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidInput` if the
218 /// `correction` value is `NAN`.
219 ///
220 /// # Examples
221 ///
222 /// ```no_run
223 /// # use r4dcb08_lib::tokio_async::R4DCB08;
224 /// use r4dcb08_lib::protocol::{Channel, Temperature, Error};
225 /// use std::time::Duration;
226 ///
227 /// # #[tokio::main]
228 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
229 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
230 /// // Set the temperature correction for channel 3 to +1.3°C.
231 /// let channel = Channel::try_from(3)?;
232 /// let correction_value = Temperature::try_from(1.3)?;
233 ///
234 /// tokio::time::timeout(Duration::from_secs(2), R4DCB08::set_temperature_correction(&mut modbus_ctx, channel, correction_value)).await??;
235 /// println!("Correction for channel {} set to {}.", channel, correction_value);
236 /// # Ok(())
237 /// # }
238 /// ```
239 pub async fn set_temperature_correction(
240 ctx: &mut tokio_modbus::client::Context,
241 channel: proto::Channel,
242 correction: proto::Temperature,
243 ) -> Result<()> {
244 Self::map_tokio_result(
245 ctx.write_single_register(
246 proto::TemperatureCorrection::channel_address(channel),
247 proto::TemperatureCorrection::encode_for_write_register(correction)?,
248 )
249 .await,
250 )
251 }
252
253 /// Reads the automatic temperature reporting interval.
254 ///
255 /// An interval of `0` seconds ([`proto::AutomaticReport::DISABLED`]) means automatic reporting is off.
256 ///
257 /// # Returns
258 ///
259 /// A ` Result<proto::AutomaticReport>` indicating the configured reporting interval,
260 /// or a Modbus error.
261 ///
262 /// # Errors
263 ///
264 /// * `tokio_modbus::Error` for Modbus communication errors.
265 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidData` if the device returns
266 /// malformed data.
267 ///
268 /// # Examples
269 ///
270 /// ```no_run
271 /// # use r4dcb08_lib::tokio_async::R4DCB08;
272 /// # use std::time::Duration;
273 /// # #[tokio::main]
274 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
275 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
276 /// let report = tokio::time::timeout(Duration::from_secs(2), R4DCB08::read_automatic_report(&mut modbus_ctx)).await??;
277 /// if report.is_disabled() {
278 /// println!("Automatic reporting is disabled.");
279 /// } else {
280 /// println!("Automatic report interval: {} seconds.", report.as_secs());
281 /// }
282 /// # Ok(())
283 /// # }
284 /// ```
285 pub async fn read_automatic_report(
286 ctx: &mut tokio_modbus::client::Context,
287 ) -> Result<proto::AutomaticReport> {
288 Self::read_and_decode(
289 ctx,
290 proto::AutomaticReport::ADDRESS,
291 proto::AutomaticReport::QUANTITY,
292 proto::AutomaticReport::decode_from_holding_registers,
293 )
294 .await
295 }
296
297 /// Sets the automatic temperature reporting interval.
298 ///
299 /// When enabled (interval > 0), the module may periodically send temperature data
300 /// unsolicitedly over the RS485 bus (behavior depends on module firmware).
301 ///
302 /// # Arguments
303 ///
304 /// * `report` - The `proto::AutomaticReport` interval (0 = disabled, 1-255 seconds).
305 /// The `proto::AutomaticReport` type ensures the value is within the valid hardware range.
306 ///
307 /// # Returns
308 ///
309 /// A `Result<()>` indicating success or failure of the write operation.
310 ///
311 /// # Errors
312 ///
313 /// * `tokio_modbus::Error` for Modbus communication errors.
314 ///
315 /// # Examples
316 ///
317 /// ```no_run
318 /// # use r4dcb08_lib::tokio_async::R4DCB08;
319 /// use r4dcb08_lib::protocol::{AutomaticReport, Error};
320 /// use std::time::Duration;
321 ///
322 /// # #[tokio::main]
323 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
324 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
325 /// let report_interval = AutomaticReport::try_from(Duration::from_secs(10))?;
326 ///
327 /// tokio::time::timeout(Duration::from_secs(2), R4DCB08::set_automatic_report(&mut modbus_ctx, report_interval)).await??;
328 /// println!("Automatic report interval set to 10 seconds.");
329 /// # Ok(())
330 /// # }
331 /// ```
332 pub async fn set_automatic_report(
333 ctx: &mut tokio_modbus::client::Context,
334 report: proto::AutomaticReport,
335 ) -> Result<()> {
336 Self::map_tokio_result(
337 ctx.write_single_register(
338 proto::AutomaticReport::ADDRESS,
339 report.encode_for_write_register(),
340 )
341 .await,
342 )
343 }
344
345 /// Reads the current Modbus communication baud rate setting from the device.
346 ///
347 /// # Returns
348 ///
349 /// A `Result<proto::BaudRate>` containing the configured baud rate,
350 /// or a Modbus error.
351 ///
352 /// # Errors
353 ///
354 /// * `tokio_modbus::Error` for Modbus communication errors.
355 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidData` if the device returns
356 /// an invalid baud rate code.
357 ///
358 /// # Examples
359 ///
360 /// ```no_run
361 /// # use r4dcb08_lib::tokio_async::R4DCB08;
362 /// # use std::time::Duration;
363 /// # #[tokio::main]
364 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
365 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
366 /// let baud_rate = tokio::time::timeout(Duration::from_secs(2), R4DCB08::read_baud_rate(&mut modbus_ctx)).await??;
367 /// println!("Current baud rate: {}", baud_rate);
368 /// # Ok(())
369 /// # }
370 /// ```
371 pub async fn read_baud_rate(
372 ctx: &mut tokio_modbus::client::Context,
373 ) -> Result<proto::BaudRate> {
374 Self::read_and_decode(
375 ctx,
376 proto::BaudRate::ADDRESS,
377 proto::BaudRate::QUANTITY,
378 proto::BaudRate::decode_from_holding_registers,
379 )
380 .await
381 }
382
383 /// Sets the Modbus communication baud rate for the device.
384 ///
385 /// **Important:** The new baud rate setting will only take effect after the
386 /// R4DCB08 module is **power cycled** (turned off and then on again).
387 ///
388 /// # Arguments
389 ///
390 /// * `baud_rate` - The desired `proto::BaudRate` to set.
391 ///
392 /// # Returns
393 ///
394 /// A `Result<()>` indicating success or failure of the write operation.
395 ///
396 /// # Errors
397 ///
398 /// * `tokio_modbus::Error` for Modbus communication errors.
399 ///
400 /// # Examples
401 ///
402 /// ```no_run
403 /// # use r4dcb08_lib::tokio_async::R4DCB08;
404 /// use r4dcb08_lib::protocol::{BaudRate, Error};
405 /// use std::time::Duration;
406 ///
407 /// # #[tokio::main]
408 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
409 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
410 /// // Set the baud rate to 19200.
411 /// let new_baud_rate = BaudRate::B19200; // Direct enum variant
412 /// // Or from u16:
413 /// // let new_baud_rate = BaudRate::try_from(19200)?;
414 ///
415 /// tokio::time::timeout(Duration::from_secs(2), R4DCB08::set_baud_rate(&mut modbus_ctx, new_baud_rate)).await??;
416 /// println!("Baud rate set to {}. Power cycle the device for changes to take effect.", new_baud_rate);
417 /// # Ok(())
418 /// # }
419 /// ```
420 pub async fn set_baud_rate(
421 ctx: &mut tokio_modbus::client::Context,
422 baud_rate: proto::BaudRate,
423 ) -> Result<()> {
424 Self::map_tokio_result(
425 ctx.write_single_register(
426 proto::BaudRate::ADDRESS,
427 baud_rate.encode_for_write_register(),
428 )
429 .await,
430 )
431 }
432
433 /// Resets the R4DCB08 module to its factory default settings.
434 ///
435 /// This resets all configurable parameters like Modbus Address, Baud Rate,
436 /// Temperature Corrections, etc., to their original defaults.
437 ///
438 /// **Important:**
439 /// * After this command is successfully sent, the module may become unresponsive
440 /// on the Modbus bus until it is power cycled.
441 /// * A **power cycle** (turning the device off and then on again) is **required**
442 /// to complete the factory reset process and for the default settings to be applied.
443 ///
444 /// # Returns
445 ///
446 /// A `Result<()>` indicating if the reset command was sent successfully.
447 /// It does not confirm the reset is complete, only that the Modbus write was acknowledged.
448 ///
449 /// # Errors
450 ///
451 /// * `tokio_modbus::Error` for Modbus communication errors. A timeout error after this
452 /// command might be expected as the device resets and may not send a response.
453 ///
454 /// # Examples
455 ///
456 /// ```no_run
457 /// # use r4dcb08_lib::tokio_async::R4DCB08;
458 /// # use std::time::Duration;
459 /// # #[tokio::main]
460 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
461 /// # let mut modbus_ctx = tokio_modbus::client::tcp::connect("127.0.0.1:502".parse()?).await?;
462 /// println!("Attempting to send factory reset command...");
463 /// match tokio::time::timeout(Duration::from_secs(2), R4DCB08::factory_reset(&mut modbus_ctx)).await {
464 /// Ok(Ok(())) => println!("Factory reset command sent. Power cycle the device to complete."),
465 /// Ok(Err(e)) => eprintln!("Modbus error during factory reset: {}", e),
466 /// Err(e) => {
467 /// // After the a successful factory reset we get no response :-(
468 /// println!("Factory reset command sent. Device timed out as expected. Power cycle to complete.");
469 /// }
470 /// }
471 /// # Ok(())
472 /// # }
473 /// ```
474 pub async fn factory_reset(ctx: &mut tokio_modbus::client::Context) -> Result<()> {
475 Self::map_tokio_result(
476 ctx.write_single_register(
477 proto::FactoryReset::ADDRESS,
478 proto::FactoryReset::encode_for_write_register(),
479 )
480 .await,
481 )
482 }
483
484 /// Reads the current Modbus device address (Slave ID) from the module.
485 ///
486 /// **Important Usage Notes:**
487 /// * This command is typically used when the device's
488 /// current address is unknown. To do this, the Modbus request **must be sent to
489 /// the broadcast address ([`proto::Address::BROADCAST`])**.
490 /// * **Single Device Only:** Only **one** R4DCB08 module should be connected to the
491 /// Modbus bus when executing this command with the broadcast address. If multiple
492 /// devices are present, they might all respond, leading to data collisions and errors.
493 ///
494 /// # Returns
495 ///
496 /// A `Result<proto::Address>` containing the device's configured Modbus address,
497 /// or a Modbus error.
498 ///
499 /// # Errors
500 ///
501 /// * `tokio_modbus::Error` for Modbus communication errors.
502 /// * `tokio_modbus::Error::Transport` with `std::io::ErrorKind::InvalidData` if the device returns
503 /// a malformed or out-of-range address.
504 ///
505 /// # Examples
506 ///
507 /// ```no_run
508 /// use r4dcb08_lib::tokio_async::R4DCB08;
509 /// use r4dcb08_lib::protocol;
510 /// use std::time::Duration;
511 ///
512 /// # #[tokio::main]
513 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
514 /// // Requires serial port features enabled in tokio-modbus
515 /// let builder = tokio_serial::new("/dev/ttyUSB0", 9600) // Baud rate 9600
516 /// .parity(tokio_serial::Parity::None)
517 /// .stop_bits(tokio_serial::StopBits::One)
518 /// .data_bits(tokio_serial::DataBits::Eight)
519 /// .flow_control(tokio_serial::FlowControl::None);
520 ///
521 /// let port = tokio_serial::SerialStream::open(&builder)?;
522 /// // Assume only one device connected, use broadcast address for reading
523 /// let mut modbus_ctx = tokio_modbus::client::rtu::attach_slave(port, tokio_modbus::Slave(*protocol::Address::BROADCAST));
524 ///
525 /// println!("Attempting to read device address using broadcast...");
526 /// let device_address = tokio::time::timeout(Duration::from_secs(2), R4DCB08::read_address(&mut modbus_ctx)).await??;
527 /// println!("Successfully read device address: {}", device_address);
528 /// # Ok(())
529 /// # }
530 /// ```
531 pub async fn read_address(ctx: &mut tokio_modbus::client::Context) -> Result<proto::Address> {
532 Self::read_and_decode(
533 ctx,
534 proto::Address::ADDRESS,
535 proto::Address::QUANTITY,
536 proto::Address::decode_from_holding_registers,
537 )
538 .await
539 }
540
541 /// Sets a new Modbus device address.
542 ///
543 /// **Warning:**
544 /// * This permanently changes the device's Modbus address.
545 /// * This command must be sent while addressing the device using its **current** Modbus address.
546 /// * After successfully changing the address, subsequent communication with the
547 /// device **must** use the new address.
548 ///
549 /// # Arguments
550 ///
551 /// * `new_address` - The new `proto::Address` to assign to the device.
552 ///
553 /// # Returns
554 ///
555 /// A `Result<()>` indicating success or failure of the write operation.
556 ///
557 /// # Errors
558 ///
559 /// * `tokio_modbus::Error` for Modbus communication errors.
560 ///
561 /// # Examples
562 ///
563 /// ```no_run
564 /// use r4dcb08_lib::tokio_async::R4DCB08;
565 /// use r4dcb08_lib::protocol::{Address, Error};
566 /// use std::time::Duration;
567 ///
568 /// # #[tokio::main]
569 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
570 /// // Requires serial port features enabled in tokio-modbus
571 /// let builder = tokio_serial::new("/dev/ttyUSB0", 9600) // Baud rate 9600
572 /// .parity(tokio_serial::Parity::None)
573 /// .stop_bits(tokio_serial::StopBits::One)
574 /// .data_bits(tokio_serial::DataBits::Eight)
575 /// .flow_control(tokio_serial::FlowControl::None);
576 ///
577 /// // --- Assume device is currently at address 1 ---
578 /// let current_device_address = Address::try_from(1)?;
579 ///
580 /// let port = tokio_serial::SerialStream::open(&builder)?;
581 /// let mut modbus_ctx = tokio_modbus::client::rtu::attach_slave(port, tokio_modbus::Slave(*current_device_address));
582 ///
583 /// // --- New address we want to set ---
584 /// let new_device_address = Address::try_from(10)?;
585 ///
586 /// println!("Attempting to change device address from {} to {}...", current_device_address, new_device_address);
587 /// tokio::time::timeout(Duration::from_secs(2), R4DCB08::set_address(&mut modbus_ctx, new_device_address)).await??;
588 /// println!("Device address successfully changed to {}.", new_device_address);
589 /// println!("You will need to reconnect using the new address for further communication.");
590 /// # Ok(())
591 /// # }
592 /// ```
593 pub async fn set_address(
594 ctx: &mut tokio_modbus::client::Context,
595 new_address: proto::Address,
596 ) -> Result<()> {
597 Self::map_tokio_result(
598 ctx.write_single_register(
599 proto::Address::ADDRESS,
600 new_address.encode_for_write_register(),
601 )
602 .await,
603 )
604 }
605}