r4dcb08_lib/
tokio_async_safe_client.rs

1//! Asynchronous `tokio-modbus` client for the R4DCB08 temperature module.
2//!
3//! This module provides a high-level API (`SafeClient` 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//! ## Example
11//!
12//! ```no_run
13//! use r4dcb08_lib::{
14//!     protocol::Address,
15//!     tokio_async_safe_client::SafeClient,
16//! };
17//! use tokio_modbus::client::tcp;
18//! use tokio_modbus::Slave;
19//!
20//! #[tokio::main]
21//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
22//!     // Connect to the device and create a stateful, safe client
23//!     let socket_addr = "192.168.1.100:502".parse()?;
24//!     let ctx = tcp::connect_slave(socket_addr, Slave(*Address::default())).await?;
25//!     let client = SafeClient::new(ctx);
26//!
27//!     // Use the client to interact with the device
28//!     let temperatures = client.read_temperatures().await?;
29//!
30//!     println!("Successfully read temperatures. Current values: {}", temperatures);
31//!
32//!     Ok(())
33//! }
34//! ```
35
36use crate::{protocol as proto, tokio_async, tokio_common::Result};
37use std::sync::Arc;
38use tokio::sync::Mutex;
39use tokio_modbus::{client::Context, prelude::*};
40
41/// Asynchronous client for interacting with the R4DCB08 temperature module over Modbus.
42///
43/// This struct provides methods to read sensor data and configure the module's
44/// operational parameters by wrapping `tokio-modbus` asynchronous operations.
45///
46/// All methods that interact with the Modbus device are `async` and return `Future`s.
47#[derive(Clone)]
48pub struct SafeClient {
49    ctx: Arc<Mutex<Context>>,
50}
51
52impl SafeClient {
53    /// Creates a new `SafeClient` with a given `tokio-modbus` asynchronous context.
54    pub fn new(ctx: Context) -> Self {
55        Self {
56            ctx: Arc::new(Mutex::new(ctx)),
57        }
58    }
59
60    /// Creates a new `SafeClient` from a shared `tokio-modbus` asynchronous context.
61    pub fn from_shared(ctx: Arc<Mutex<Context>>) -> Self {
62        Self { ctx }
63    }
64
65    /// Clones the shared `tokio-modbus` asynchronous context.
66    pub fn clone_shared(&self) -> Arc<Mutex<Context>> {
67        self.ctx.clone()
68    }
69
70    /// Reads the current temperatures from all 8 available channels in degrees Celsius (°C).
71    pub async fn read_temperatures(&self) -> Result<proto::Temperatures> {
72        let mut ctx = self.ctx.lock().await;
73        tokio_async::R4DCB08::read_temperatures(&mut ctx).await
74    }
75
76    /// Reads the configured temperature correction values (°C) for all 8 channels.
77    pub async fn read_temperature_correction(&self) -> Result<proto::TemperatureCorrection> {
78        let mut ctx = self.ctx.lock().await;
79        tokio_async::R4DCB08::read_temperature_correction(&mut ctx).await
80    }
81
82    /// Sets a temperature correction value for a specific channel.
83    pub async fn set_temperature_correction(
84        &self,
85        channel: proto::Channel,
86        correction: proto::Temperature,
87    ) -> Result<()> {
88        let mut ctx = self.ctx.lock().await;
89        tokio_async::R4DCB08::set_temperature_correction(&mut ctx, channel, correction).await
90    }
91
92    /// Reads the automatic temperature reporting interval.
93    pub async fn read_automatic_report(&self) -> Result<proto::AutomaticReport> {
94        let mut ctx = self.ctx.lock().await;
95        tokio_async::R4DCB08::read_automatic_report(&mut ctx).await
96    }
97
98    /// Sets the automatic temperature reporting interval.
99    pub async fn set_automatic_report(&self, report: proto::AutomaticReport) -> Result<()> {
100        let mut ctx = self.ctx.lock().await;
101        tokio_async::R4DCB08::set_automatic_report(&mut ctx, report).await
102    }
103
104    /// Reads the current Modbus communication baud rate setting from the device.
105    pub async fn read_baud_rate(&self) -> Result<proto::BaudRate> {
106        let mut ctx = self.ctx.lock().await;
107        tokio_async::R4DCB08::read_baud_rate(&mut ctx).await
108    }
109
110    /// Sets the Modbus communication baud rate for the device.
111    pub async fn set_baud_rate(&self, baud_rate: proto::BaudRate) -> Result<()> {
112        let mut ctx = self.ctx.lock().await;
113        tokio_async::R4DCB08::set_baud_rate(&mut ctx, baud_rate).await
114    }
115
116    /// Resets the R4DCB08 module to its factory default settings.
117    pub async fn factory_reset(&self) -> Result<()> {
118        let mut ctx = self.ctx.lock().await;
119        tokio_async::R4DCB08::factory_reset(&mut ctx).await
120    }
121
122    /// Reads the current Modbus device address (Slave ID) from the module.
123    pub async fn read_address(&self) -> Result<proto::Address> {
124        let mut ctx = self.ctx.lock().await;
125        tokio_async::R4DCB08::read_address(&mut ctx).await
126    }
127
128    /// Sets a new Modbus device address.
129    ///
130    /// A successful call makes the existing `Context` invalid (as it
131    /// still points to the old address). This function automatically
132    /// updates the slave ID within its managed `Context`.
133    pub async fn set_address(&self, new_address: proto::Address) -> Result<()> {
134        let mut ctx = self.ctx.lock().await;
135        tokio_async::R4DCB08::set_address(&mut ctx, new_address).await?;
136        ctx.set_slave(Slave(*new_address));
137        Ok(())
138    }
139}