modbus_relay/errors/
transport.rs

1use std::time::Duration;
2use thiserror::Error;
3use tokio::time::error::Elapsed;
4
5use super::{IoOperation, RtsError, SerialErrorKind};
6
7#[derive(Error, Debug)]
8pub enum TransportError {
9    #[error("Serial port error: {kind} on {port} - {details}")]
10    Serial {
11        kind: SerialErrorKind,
12        port: String,
13        details: String,
14        #[source]
15        source: Option<serialport::Error>,
16    },
17
18    #[error("Network error: {0}")]
19    Network(std::io::Error),
20
21    #[error("I/O error: {operation} failed on {details}")]
22    Io {
23        operation: IoOperation,
24        details: String,
25        #[source]
26        source: std::io::Error,
27    },
28
29    #[error("Transaction timeout after {elapsed:?}, limit was {limit:?}")]
30    Timeout {
31        elapsed: Duration,
32        limit: Duration,
33        #[source]
34        source: Elapsed,
35    },
36
37    #[error("No response received after {attempts} attempts over {elapsed:?}")]
38    NoResponse { attempts: u8, elapsed: Duration },
39
40    #[error("RTS error: {0}")]
41    Rts(#[from] RtsError),
42}
43
44impl From<serialport::Error> for TransportError {
45    fn from(err: serialport::Error) -> Self {
46        match err.kind {
47            serialport::ErrorKind::NoDevice => TransportError::Serial {
48                kind: SerialErrorKind::OpenFailed,
49                port: err.to_string(),
50                details: "Device not found".into(),
51                source: Some(err),
52            },
53            serialport::ErrorKind::InvalidInput => TransportError::Serial {
54                kind: SerialErrorKind::ConfigurationFailed,
55                port: err.to_string(),
56                details: "Invalid configuration".into(),
57                source: Some(err),
58            },
59            serialport::ErrorKind::Io(io_err) => TransportError::Io {
60                operation: match io_err {
61                    std::io::ErrorKind::NotFound => IoOperation::Configure,
62                    std::io::ErrorKind::PermissionDenied => IoOperation::Configure,
63                    std::io::ErrorKind::TimedOut => IoOperation::Read,
64                    std::io::ErrorKind::WriteZero => IoOperation::Write,
65                    _ => IoOperation::Control,
66                },
67                details: io_err.to_string(),
68                source: std::io::Error::new(io_err, err.description),
69            },
70            _ => TransportError::Serial {
71                kind: SerialErrorKind::OpenFailed,
72                port: err.to_string(),
73                details: err.to_string(),
74                source: Some(err),
75            },
76        }
77    }
78}
79
80impl From<std::io::Error> for TransportError {
81    fn from(err: std::io::Error) -> Self {
82        TransportError::Io {
83            operation: match err.kind() {
84                std::io::ErrorKind::TimedOut => IoOperation::Read,
85                std::io::ErrorKind::WouldBlock => IoOperation::Read,
86                std::io::ErrorKind::WriteZero => IoOperation::Write,
87                std::io::ErrorKind::Interrupted => IoOperation::Control,
88                _ => IoOperation::Control,
89            },
90            details: err.to_string(),
91            source: err,
92        }
93    }
94}
95
96impl From<Elapsed> for TransportError {
97    fn from(err: Elapsed) -> Self {
98        TransportError::Timeout {
99            elapsed: Duration::from_secs(1), // W Elapsed nie ma duration(), używamy stałej
100            limit: Duration::from_secs(1), // TODO(aljen): Pass the actual limit from configuration
101            source: err,
102        }
103    }
104}