Skip to main content

mbus_async/client/
mod.rs

1//! Async client module.
2//!
3//! Public entry points:
4//! - [`AsyncTcpClient`] (TCP)
5//! - [`AsyncSerialClient`] (RTU/ASCII)
6//!
7//! # Module layout
8//!
9//! | Module | Contents |
10//! |---|---|
11//! | `command` | `ClientRequest` and `TaskCommand` channel envelopes |
12//! | `response` | `ClientResponse` typed result enum |
13//! | `notifier` | [`AsyncClientNotifier`] traffic hook trait (`traffic` feature) |
14//! | `client_core` | [`AsyncClientCore`] — public request API |
15//! | `network_client` | [`AsyncTcpClient`] — TCP constructor |
16//! | `serial_client` | [`AsyncSerialClient`] — serial constructor |
17
18pub(crate) mod command;
19pub(crate) mod decode;
20pub(crate) mod encode;
21#[cfg(feature = "traffic")]
22pub mod notifier;
23pub(crate) mod response;
24pub(crate) mod task;
25
26mod client_core;
27mod network_client;
28mod serial_client;
29
30pub use client_core::AsyncClientCore;
31pub use network_client::AsyncTcpClient;
32#[cfg(feature = "traffic")]
33pub use notifier::AsyncClientNotifier;
34pub use serial_client::AsyncSerialClient;
35
36use mbus_core::errors::MbusError;
37#[cfg(feature = "diagnostics")]
38use mbus_core::function_codes::public::DiagnosticSubFunction;
39#[cfg(feature = "diagnostics")]
40pub use mbus_core::models::diagnostic::{DeviceIdentificationResponse, ObjectId, ReadDeviceIdCode};
41#[cfg(feature = "file-record")]
42pub use mbus_core::models::file_record::{SubRequest, SubRequestParams};
43
44#[cfg(feature = "diagnostics")]
45/// Diagnostics response payload returned by FC 08.
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct DiagnosticsDataResponse {
48    /// Echoed diagnostic sub-function code.
49    pub sub_function: DiagnosticSubFunction,
50    /// Echoed diagnostic data words.
51    pub data: Vec<u16>,
52}
53#[cfg(feature = "diagnostics")]
54/// Communication event log payload `(status, event_count, message_count, events)` returned by FC 12.
55pub type CommEventLogResponse = (u16, u16, u16, Vec<u8>);
56
57/// Async facade error type.
58#[derive(Debug, PartialEq, Eq)]
59pub enum AsyncError {
60    /// Error propagated from the underlying Modbus client stack.
61    Mbus(MbusError),
62    /// Background worker channel is closed or worker thread has stopped.
63    WorkerClosed,
64    /// Internal response routing mismatch between request and callback payload type.
65    UnexpectedResponseType,
66    /// Per-request timeout elapsed before the server responded.
67    ///
68    /// Set via [`AsyncClientCore::set_request_timeout`].  The in-flight entry
69    /// remains in the background task until the transport delivers or errors;
70    /// call [`connect`](AsyncClientCore::connect) to reset transport state.
71    ///
72    /// [`AsyncClientCore::set_request_timeout`]: crate::client::AsyncClientCore::set_request_timeout
73    /// [`connect`]: crate::client::AsyncClientCore::connect
74    Timeout,
75}
76
77impl From<MbusError> for AsyncError {
78    fn from(value: MbusError) -> Self {
79        Self::Mbus(value)
80    }
81}
82
83impl core::fmt::Display for AsyncError {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        match self {
86            Self::Mbus(err) => write!(f, "Modbus error: {err:?}"),
87            Self::WorkerClosed => write!(f, "async worker channel closed"),
88            Self::UnexpectedResponseType => write!(f, "unexpected response type from worker"),
89            Self::Timeout => write!(f, "request timed out"),
90        }
91    }
92}
93
94impl std::error::Error for AsyncError {}