use alloc::vec::Vec;
use core::fmt::Debug;
use crate::protocol::{
Function,
function,
function::{ReadRegisters, ReadRegistersExact, read_registers},
};
pub trait AsyncClient {
type UnitId: Debug;
type Error: From<crate::protocol::Error>;
#[expect(async_fn_in_trait)]
async fn call<F: Function>(
&self,
unit_id: Self::UnitId,
args: F::Args,
) -> Result<F::Output, Self::Error>;
#[expect(async_fn_in_trait)]
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "trace"))]
async fn read_registers<C: function::Code, V: read_registers::Value>(
&self,
unit_id: Self::UnitId,
starting_address: u16,
n_values: usize,
) -> Result<Vec<V>, Self::Error> {
#[cfg(feature = "tracing")]
tracing::trace!(?unit_id, starting_address, n_values, "reading holding registers…");
let args = read_registers::Args::new(starting_address, n_values)?;
Ok(self.call::<ReadRegisters<C, V>>(unit_id, args).await?.values)
}
#[expect(async_fn_in_trait)]
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "trace"))]
async fn read_registers_exact<C: function::Code, V: read_registers::Value, const N: usize>(
&self,
unit_id: Self::UnitId,
starting_address: u16,
) -> Result<[V; N], Self::Error> {
#[cfg(feature = "tracing")]
tracing::trace!(?unit_id, starting_address, N, "reading registers…");
let args = read_registers::Args::new(starting_address, N)?;
Ok(self.call::<ReadRegistersExact<C, V, N>>(unit_id, args).await?.values)
}
#[expect(async_fn_in_trait)]
async fn read_registers_value<C: function::Code, V: read_registers::Value>(
&self,
unit_id: Self::UnitId,
address: u16,
) -> Result<V, Self::Error> {
let [value] = self.read_registers_exact::<C, V, 1>(unit_id, address).await?;
Ok(value)
}
}