use crate::{CanErrorFrame, EmbeddedFrame, Frame};
use std::{convert::TryFrom, error, fmt, io};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Can(#[from] CanError),
#[error(transparent)]
Io(#[from] io::Error),
}
impl embedded_can::Error for Error {
fn kind(&self) -> embedded_can::ErrorKind {
match *self {
Error::Can(err) => err.kind(),
_ => embedded_can::ErrorKind::Other,
}
}
}
impl From<CanErrorFrame> for Error {
fn from(frame: CanErrorFrame) -> Self {
Error::Can(CanError::from(frame))
}
}
impl From<io::ErrorKind> for Error {
fn from(ek: io::ErrorKind) -> Self {
Self::from(io::Error::from(ek))
}
}
#[cfg(feature = "enumerate")]
impl From<libudev::Error> for Error {
fn from(e: libudev::Error) -> Error {
match e.kind() {
libudev::ErrorKind::NoMem => Self::from(io::ErrorKind::OutOfMemory),
libudev::ErrorKind::InvalidInput => Self::from(io::ErrorKind::InvalidInput),
libudev::ErrorKind::Io(ek) => Self::from(ek),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub type IoError = io::Error;
pub type IoErrorKind = io::ErrorKind;
pub type IoResult<T> = io::Result<T>;
#[derive(Debug, Clone, Copy)]
pub enum CanError {
TransmitTimeout,
LostArbitration(u8),
ControllerProblem(ControllerProblem),
ProtocolViolation {
vtype: ViolationType,
location: Location,
},
TransceiverError,
NoAck,
BusOff,
BusError,
Restarted,
DecodingFailure(CanErrorDecodingFailure),
Unknown(u32),
}
impl error::Error for CanError {}
impl fmt::Display for CanError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CanError::*;
match *self {
TransmitTimeout => write!(f, "transmission timeout"),
LostArbitration(n) => write!(f, "arbitration lost after {} bits", n),
ControllerProblem(e) => write!(f, "controller problem: {}", e),
ProtocolViolation { vtype, location } => {
write!(f, "protocol violation at {}: {}", location, vtype)
}
TransceiverError => write!(f, "transceiver error"),
NoAck => write!(f, "no ack"),
BusOff => write!(f, "bus off"),
BusError => write!(f, "bus error"),
Restarted => write!(f, "restarted"),
DecodingFailure(err) => write!(f, "decoding failure: {}", err),
Unknown(err) => write!(f, "unknown error ({})", err),
}
}
}
impl embedded_can::Error for CanError {
fn kind(&self) -> embedded_can::ErrorKind {
match *self {
CanError::ControllerProblem(cp) => {
use ControllerProblem::*;
match cp {
ReceiveBufferOverflow | TransmitBufferOverflow => {
embedded_can::ErrorKind::Overrun
}
_ => embedded_can::ErrorKind::Other,
}
}
CanError::NoAck => embedded_can::ErrorKind::Acknowledge,
_ => embedded_can::ErrorKind::Other,
}
}
}
impl From<CanErrorFrame> for CanError {
fn from(frame: CanErrorFrame) -> Self {
match frame.error_bits() {
0x0001 => CanError::TransmitTimeout,
0x0002 => CanError::LostArbitration(frame.data()[0]),
0x0004 => match ControllerProblem::try_from(frame.data()[1]) {
Ok(err) => CanError::ControllerProblem(err),
Err(err) => CanError::DecodingFailure(err),
},
0x0008 => {
match (
ViolationType::try_from(frame.data()[2]),
Location::try_from(frame.data()[3]),
) {
(Ok(vtype), Ok(location)) => CanError::ProtocolViolation { vtype, location },
(Err(err), _) | (_, Err(err)) => CanError::DecodingFailure(err),
}
}
0x0010 => CanError::TransceiverError,
0x0020 => CanError::NoAck,
0x0040 => CanError::BusOff,
0x0080 => CanError::BusError,
0x0100 => CanError::Restarted,
err => CanError::Unknown(err),
}
}
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum ControllerProblem {
Unspecified = 0x00,
ReceiveBufferOverflow = 0x01,
TransmitBufferOverflow = 0x02,
ReceiveErrorWarning = 0x04,
TransmitErrorWarning = 0x08,
ReceiveErrorPassive = 0x10,
TransmitErrorPassive = 0x20,
Active = 0x40,
}
impl error::Error for ControllerProblem {}
impl fmt::Display for ControllerProblem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ControllerProblem::*;
let msg = match *self {
Unspecified => "unspecified controller problem",
ReceiveBufferOverflow => "receive buffer overflow",
TransmitBufferOverflow => "transmit buffer overflow",
ReceiveErrorWarning => "ERROR WARNING (receive)",
TransmitErrorWarning => "ERROR WARNING (transmit)",
ReceiveErrorPassive => "ERROR PASSIVE (receive)",
TransmitErrorPassive => "ERROR PASSIVE (transmit)",
Active => "ERROR ACTIVE",
};
write!(f, "{}", msg)
}
}
impl TryFrom<u8> for ControllerProblem {
type Error = CanErrorDecodingFailure;
fn try_from(val: u8) -> std::result::Result<Self, Self::Error> {
use ControllerProblem::*;
Ok(match val {
0x00 => Unspecified,
0x01 => ReceiveBufferOverflow,
0x02 => TransmitBufferOverflow,
0x04 => ReceiveErrorWarning,
0x08 => TransmitErrorWarning,
0x10 => ReceiveErrorPassive,
0x20 => TransmitErrorPassive,
0x40 => Active,
_ => return Err(CanErrorDecodingFailure::InvalidControllerProblem),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ViolationType {
Unspecified = 0x00,
SingleBitError = 0x01,
FrameFormatError = 0x02,
BitStuffingError = 0x04,
UnableToSendDominantBit = 0x08,
UnableToSendRecessiveBit = 0x10,
BusOverload = 0x20,
Active = 0x40,
TransmissionError = 0x80,
}
impl error::Error for ViolationType {}
impl fmt::Display for ViolationType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ViolationType::*;
let msg = match *self {
Unspecified => "unspecified",
SingleBitError => "single bit error",
FrameFormatError => "frame format error",
BitStuffingError => "bit stuffing error",
UnableToSendDominantBit => "unable to send dominant bit",
UnableToSendRecessiveBit => "unable to send recessive bit",
BusOverload => "bus overload",
Active => "active",
TransmissionError => "transmission error",
};
write!(f, "{}", msg)
}
}
impl TryFrom<u8> for ViolationType {
type Error = CanErrorDecodingFailure;
fn try_from(val: u8) -> std::result::Result<Self, Self::Error> {
use ViolationType::*;
Ok(match val {
0x00 => Unspecified,
0x01 => SingleBitError,
0x02 => FrameFormatError,
0x04 => BitStuffingError,
0x08 => UnableToSendDominantBit,
0x10 => UnableToSendRecessiveBit,
0x20 => BusOverload,
0x40 => Active,
0x80 => TransmissionError,
_ => return Err(CanErrorDecodingFailure::InvalidViolationType),
})
}
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum Location {
Unspecified = 0x00,
StartOfFrame = 0x03,
Id2821 = 0x02,
Id2018 = 0x06,
SubstituteRtr = 0x04,
IdentifierExtension = 0x05,
Id1713 = 0x07,
Id1205 = 0x0F,
Id0400 = 0x0E,
Rtr = 0x0C,
Reserved1 = 0x0D,
Reserved0 = 0x09,
DataLengthCode = 0x0B,
DataSection = 0x0A,
CrcSequence = 0x008,
CrcDelimiter = 0x18,
AckSlot = 0x19,
AckDelimiter = 0x1B,
EndOfFrame = 0x1A,
Intermission = 0x12,
}
impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Location::*;
let msg = match *self {
Unspecified => "unspecified location",
StartOfFrame => "start of frame",
Id2821 => "ID, bits 28-21",
Id2018 => "ID, bits 20-18",
SubstituteRtr => "substitute RTR bit",
IdentifierExtension => "ID, extension",
Id1713 => "ID, bits 17-13",
Id1205 => "ID, bits 12-05",
Id0400 => "ID, bits 04-00",
Rtr => "RTR bit",
Reserved1 => "reserved bit 1",
Reserved0 => "reserved bit 0",
DataLengthCode => "data length code",
DataSection => "data section",
CrcSequence => "CRC sequence",
CrcDelimiter => "CRC delimiter",
AckSlot => "ACK slot",
AckDelimiter => "ACK delimiter",
EndOfFrame => "end of frame",
Intermission => "intermission",
};
write!(f, "{}", msg)
}
}
impl TryFrom<u8> for Location {
type Error = CanErrorDecodingFailure;
fn try_from(val: u8) -> std::result::Result<Self, Self::Error> {
use Location::*;
Ok(match val {
0x00 => Unspecified,
0x03 => StartOfFrame,
0x02 => Id2821,
0x06 => Id2018,
0x04 => SubstituteRtr,
0x05 => IdentifierExtension,
0x07 => Id1713,
0x0F => Id1205,
0x0E => Id0400,
0x0C => Rtr,
0x0D => Reserved1,
0x09 => Reserved0,
0x0B => DataLengthCode,
0x0A => DataSection,
0x08 => CrcSequence,
0x18 => CrcDelimiter,
0x19 => AckSlot,
0x1B => AckDelimiter,
0x1A => EndOfFrame,
0x12 => Intermission,
_ => return Err(CanErrorDecodingFailure::InvalidLocation),
})
}
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum TransceiverError {
Unspecified = 0x00,
CanHighNoWire = 0x04,
CanHighShortToBat = 0x05,
CanHighShortToVcc = 0x06,
CanHighShortToGnd = 0x07,
CanLowNoWire = 0x40,
CanLowShortToBat = 0x50,
CanLowShortToVcc = 0x60,
CanLowShortToGnd = 0x70,
CanLowShortToCanHigh = 0x80,
}
impl TryFrom<u8> for TransceiverError {
type Error = CanErrorDecodingFailure;
fn try_from(val: u8) -> std::result::Result<Self, Self::Error> {
use TransceiverError::*;
Ok(match val {
0x00 => Unspecified,
0x04 => CanHighNoWire,
0x05 => CanHighShortToBat,
0x06 => CanHighShortToVcc,
0x07 => CanHighShortToGnd,
0x40 => CanLowNoWire,
0x50 => CanLowShortToBat,
0x60 => CanLowShortToVcc,
0x70 => CanLowShortToGnd,
0x80 => CanLowShortToCanHigh,
_ => return Err(CanErrorDecodingFailure::InvalidTransceiverError),
})
}
}
pub trait ControllerSpecificErrorInformation {
fn get_ctrl_err(&self) -> Option<&[u8]>;
}
impl<T: Frame> ControllerSpecificErrorInformation for T {
fn get_ctrl_err(&self) -> Option<&[u8]> {
let data = self.data();
if data.len() == 8 {
Some(&data[5..])
} else {
None
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum CanErrorDecodingFailure {
NotAnError,
UnknownErrorType(u32),
NotEnoughData(u8),
InvalidControllerProblem,
InvalidViolationType,
InvalidLocation,
InvalidTransceiverError,
}
impl error::Error for CanErrorDecodingFailure {}
impl fmt::Display for CanErrorDecodingFailure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CanErrorDecodingFailure::*;
let msg = match *self {
NotAnError => "CAN frame is not an error",
UnknownErrorType(_) => "unknown error type",
NotEnoughData(_) => "not enough data",
InvalidControllerProblem => "not a valid controller problem",
InvalidViolationType => "not a valid violation type",
InvalidLocation => "not a valid location",
InvalidTransceiverError => "not a valid transceiver error",
};
write!(f, "{}", msg)
}
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum ConstructionError {
WrongFrameType,
IDTooLarge,
TooMuchData,
}
impl error::Error for ConstructionError {}
impl fmt::Display for ConstructionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ConstructionError::*;
let msg = match *self {
WrongFrameType => "Incompatible frame type",
IDTooLarge => "CAN ID too large",
TooMuchData => "Payload is too large",
};
write!(f, "{}", msg)
}
}
#[cfg(test)]
mod tests {
use crate::Error;
use std::io;
#[test]
fn test_errors() {
const KIND: io::ErrorKind = io::ErrorKind::TimedOut;
let err = Error::from(io::Error::from(KIND));
if let Error::Io(ioerr) = err {
assert_eq!(ioerr.kind(), KIND);
} else {
panic!("Wrong error conversion");
}
let err = Error::from(KIND);
if let Error::Io(ioerr) = err {
assert_eq!(ioerr.kind(), KIND);
} else {
panic!("Wrong error conversion");
}
}
}