Skip to main content

async_modbus/
client.rs

1//! Client functions for [`embedded_io_async`]-based IO.
2
3use core::error::Error as CoreError;
4
5use embedded_io_async::{Read, ReadExactError, Write};
6use zerocopy::{FromBytes, Immutable, IntoBytes};
7
8mod generated {
9    include!(concat!(env!("OUT_DIR"), "/client.rs"));
10}
11
12pub use generated::*;
13
14use crate::pdu::{CrcError, ValidationError};
15
16async fn write_frame<T, E>(mut dst: impl Write<Error = E>, frame: &T) -> Result<(), E>
17where
18    T: IntoBytes + Immutable,
19{
20    dst.write_all(frame.as_bytes()).await?;
21    dst.flush().await
22}
23
24async fn read_frame<T, E>(mut src: impl Read<Error = E>) -> Result<T, ReadExactError<E>>
25where
26    T: FromBytes + IntoBytes,
27{
28    let mut message = T::new_zeroed();
29    src.read_exact(message.as_mut_bytes()).await?;
30    Ok(message)
31}
32
33/// Errors that can occur when talking to a Modbus server.
34#[derive(Debug, thiserror::Error)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum Error<Io: CoreError> {
37    /// IO error.
38    #[error(transparent)]
39    Io(Io),
40    /// Unexpected end of file when reading.
41    #[error("unexpected end of file")]
42    UnexpectedEof,
43    /// Invalid CRC checksum.
44    #[error(transparent)]
45    Crc(#[from] CrcError),
46    /// Unexpected response from the Modbus server.
47    #[error("unexpected response from server")]
48    UnexpectedResponse,
49}
50
51impl<E: CoreError> From<ValidationError> for Error<E> {
52    fn from(e: ValidationError) -> Self {
53        match e {
54            ValidationError::Crc(crc) => Error::Crc(crc),
55            ValidationError::UnexpectedResponse => Error::UnexpectedResponse,
56        }
57    }
58}
59
60impl<E: CoreError> From<ReadExactError<E>> for Error<E> {
61    fn from(e: ReadExactError<E>) -> Self {
62        match e {
63            ReadExactError::Other(e) => Self::Io(e),
64            ReadExactError::UnexpectedEof => Self::UnexpectedEof,
65        }
66    }
67}