use core::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ModbusError {
ShortResponse(usize),
BadSlave(u8),
BadHeader,
BadCrc,
Exception(u8),
}
impl fmt::Display for ModbusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ShortResponse(n) => write!(f, "short response ({n} bytes)"),
Self::BadSlave(a) => write!(f, "wrong slave id 0x{a:02X}"),
Self::BadHeader => write!(f, "malformed header"),
Self::BadCrc => write!(f, "CRC mismatch"),
Self::Exception(c) => write!(f, "modbus exception 0x{c:02X}"),
}
}
}
impl core::error::Error for ModbusError {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RtuError {
Timeout,
Io,
Modbus(ModbusError),
}
impl fmt::Display for RtuError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Timeout => f.write_str("UART response timed out"),
Self::Io => f.write_str("UART I/O error"),
Self::Modbus(e) => fmt::Display::fmt(e, f),
}
}
}
impl From<ModbusError> for RtuError {
fn from(e: ModbusError) -> Self {
Self::Modbus(e)
}
}
impl core::error::Error for RtuError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Modbus(e) => Some(e),
_ => None,
}
}
}
pub trait BlockingRead {
type Error;
fn read(&mut self, buf: &mut [u8], timeout_ms: u32) -> Result<usize, Self::Error>;
}
pub trait ModbusTransport {
fn read_holding(&mut self, slave: u8, addr: u16, dst: &mut [u16]) -> Result<(), RtuError>;
fn write_single_holding(&mut self, slave: u8, addr: u16, value: u16) -> Result<(), RtuError>;
fn write_multiple_holdings(
&mut self,
slave: u8,
addr: u16,
values: &[u16],
) -> Result<(), RtuError>;
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use std::format;
#[test]
fn modbus_error_display_strings() {
assert_eq!(
format!("{}", ModbusError::ShortResponse(3)),
"short response (3 bytes)"
);
assert_eq!(
format!("{}", ModbusError::BadSlave(0x02)),
"wrong slave id 0x02"
);
assert_eq!(format!("{}", ModbusError::BadHeader), "malformed header");
assert_eq!(format!("{}", ModbusError::BadCrc), "CRC mismatch");
assert_eq!(
format!("{}", ModbusError::Exception(0x03)),
"modbus exception 0x03"
);
}
#[test]
fn rtu_error_display_strings() {
assert_eq!(format!("{}", RtuError::Timeout), "UART response timed out");
assert_eq!(format!("{}", RtuError::Io), "UART I/O error");
assert_eq!(
format!("{}", RtuError::Modbus(ModbusError::BadCrc)),
"CRC mismatch"
);
}
#[test]
fn rtu_error_source_chain() {
use core::error::Error;
let e = RtuError::Modbus(ModbusError::BadCrc);
assert!(e.source().is_some());
assert!(RtuError::Timeout.source().is_none());
assert!(RtuError::Io.source().is_none());
}
}