use anyhow::{anyhow, bail, Context, Result};
use serde_json::{json, Value};
pub const CAN_EFF_FLAG: u32 = 0x8000_0000;
pub const CAN_RTR_FLAG: u32 = 0x4000_0000;
pub const CAN_ERR_FLAG: u32 = 0x2000_0000;
pub const CAN_SFF_MASK: u32 = 0x0000_07FF;
pub const CAN_EFF_MASK: u32 = 0x1FFF_FFFF;
pub const CANFD_BRS: u8 = 0x01;
pub const CANFD_ESI: u8 = 0x02;
pub const CANFD_FDF: u8 = 0x04;
pub const CAN_MTU: usize = 16;
pub const CANFD_MTU: usize = 72;
pub const CAN_MAX_DLEN: usize = 8;
pub const CANFD_MAX_DLEN: usize = 64;
pub const CAN_ERR_TX_TIMEOUT: u32 = 0x0000_0001;
pub const CAN_ERR_LOSTARB: u32 = 0x0000_0002;
pub const CAN_ERR_CRTL: u32 = 0x0000_0004;
pub const CAN_ERR_PROT: u32 = 0x0000_0008;
pub const CAN_ERR_TRX: u32 = 0x0000_0010;
pub const CAN_ERR_ACK: u32 = 0x0000_0020;
pub const CAN_ERR_BUSOFF: u32 = 0x0000_0040;
pub const CAN_ERR_BUSERROR: u32 = 0x0000_0080;
pub const CAN_ERR_RESTARTED: u32 = 0x0000_0100;
pub const CAN_ERR_CNT: u32 = 0x0000_0200;
pub const CAN_ERR_CRTL_RX_OVERFLOW: u8 = 0x01;
pub const CAN_ERR_CRTL_TX_OVERFLOW: u8 = 0x02;
pub const CAN_ERR_CRTL_RX_WARNING: u8 = 0x04;
pub const CAN_ERR_CRTL_TX_WARNING: u8 = 0x08;
pub const CAN_ERR_CRTL_RX_PASSIVE: u8 = 0x10;
pub const CAN_ERR_CRTL_TX_PASSIVE: u8 = 0x20;
pub const CAN_ERR_CRTL_ACTIVE: u8 = 0x40;
pub const FD_DLC_TO_LEN: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64];
pub fn len_for_dlc(dlc: u8, fd: bool) -> usize {
let dlc = (dlc & 0x0F) as usize;
if fd {
FD_DLC_TO_LEN[dlc]
} else {
dlc.min(CAN_MAX_DLEN)
}
}
pub fn dlc_for_len(len: usize, fd: bool) -> Result<u8> {
if fd {
FD_DLC_TO_LEN
.iter()
.position(|&candidate| candidate == len)
.map(|dlc| dlc as u8)
.ok_or_else(|| {
anyhow!(
"a CAN FD frame carries 0-8, 12, 16, 20, 24, 32, 48 or 64 bytes; {len} is not \
an encodable length. Pad the payload to the next encodable size yourself if \
that is what you mean - this codec will not invent bytes."
)
})
} else if len <= CAN_MAX_DLEN {
Ok(len as u8)
} else {
Err(anyhow!(
"a classic CAN frame carries at most 8 bytes; {len} given. Set \"fd\": true for a CAN \
FD frame, which carries up to 64."
))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusState {
ErrorActive,
ErrorWarning,
ErrorPassive,
BusOff,
}
impl BusState {
pub fn as_str(self) -> &'static str {
match self {
BusState::ErrorActive => "error_active",
BusState::ErrorWarning => "error_warning",
BusState::ErrorPassive => "error_passive",
BusState::BusOff => "bus_off",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CanFrame {
pub id: u32,
pub extended: bool,
pub rtr: bool,
pub error: bool,
pub fd: bool,
pub brs: bool,
pub esi: bool,
pub data: Vec<u8>,
pub rtr_dlc: u8,
}
impl CanFrame {
pub fn classic(id: u32, extended: bool, data: Vec<u8>) -> Result<Self> {
let frame = Self {
id,
extended,
rtr: false,
error: false,
fd: false,
brs: false,
esi: false,
data,
rtr_dlc: 0,
};
frame.validate()?;
Ok(frame)
}
pub fn fd(id: u32, extended: bool, data: Vec<u8>, brs: bool) -> Result<Self> {
let frame = Self {
id,
extended,
rtr: false,
error: false,
fd: true,
brs,
esi: false,
data,
rtr_dlc: 0,
};
frame.validate()?;
Ok(frame)
}
pub fn remote(id: u32, extended: bool, dlc: u8) -> Result<Self> {
let frame = Self {
id,
extended,
rtr: true,
error: false,
fd: false,
brs: false,
esi: false,
data: Vec::new(),
rtr_dlc: dlc,
};
frame.validate()?;
Ok(frame)
}
pub fn validate(&self) -> Result<()> {
if self.error {
dlc_for_len(self.data.len(), self.fd)?;
return Ok(());
}
let limit = if self.extended {
CAN_EFF_MASK
} else {
CAN_SFF_MASK
};
if self.id > limit {
bail!(
"identifier 0x{:X} does not fit in {} bits (maximum 0x{:X}). {}",
self.id,
if self.extended { 29 } else { 11 },
limit,
if self.extended {
"29 bits is the largest CAN identifier there is."
} else {
"Set \"extended\": true for a 29-bit identifier."
}
);
}
if self.rtr {
if self.fd {
bail!(
"CAN FD has no remote frames: the RTR bit was reused as RRS and is always \
dominant. Send an RTR request as a classic frame (\"fd\": false), or send a \
data frame."
);
}
if !self.data.is_empty() {
bail!(
"a remote frame carries no data by definition; {} byte(s) were given. Its DLC \
names how many bytes are being *requested*.",
self.data.len()
);
}
if self.rtr_dlc as usize > CAN_MAX_DLEN {
bail!(
"a remote frame can request at most 8 bytes; dlc {} given",
self.rtr_dlc
);
}
return Ok(());
}
if self.brs && !self.fd {
bail!(
"the bit-rate switch is a CAN FD feature; a classic frame has one bit rate. Set \
\"fd\": true, or drop \"brs\"."
);
}
dlc_for_len(self.data.len(), self.fd)?;
Ok(())
}
pub fn dlc(&self) -> u8 {
if self.rtr {
self.rtr_dlc
} else {
dlc_for_len(self.data.len(), self.fd).unwrap_or(0)
}
}
pub fn id_word(&self) -> u32 {
let mut word = if self.error {
self.id
} else if self.extended {
self.id & CAN_EFF_MASK
} else {
self.id & CAN_SFF_MASK
};
if self.extended {
word |= CAN_EFF_FLAG;
}
if self.rtr {
word |= CAN_RTR_FLAG;
}
if self.error {
word |= CAN_ERR_FLAG;
}
word
}
pub fn from_id_word(id_word: u32, data: Vec<u8>, fd: bool, brs: bool, esi: bool) -> Self {
let extended = id_word & CAN_EFF_FLAG != 0;
let rtr = id_word & CAN_RTR_FLAG != 0;
let error = id_word & CAN_ERR_FLAG != 0;
let id = if error {
id_word & CAN_EFF_MASK
} else if extended {
id_word & CAN_EFF_MASK
} else {
id_word & CAN_SFF_MASK
};
Self {
id,
extended,
rtr,
error,
fd,
brs,
esi,
data: if rtr { Vec::new() } else { data },
rtr_dlc: 0,
}
}
pub fn to_wire_bytes(&self) -> Result<Vec<u8>> {
self.validate()?;
let id_word = self.id_word().to_le_bytes();
if self.fd {
let mut out = vec![0u8; CANFD_MTU];
out[0..4].copy_from_slice(&id_word);
out[4] = self.data.len() as u8;
let mut flags = CANFD_FDF;
if self.brs {
flags |= CANFD_BRS;
}
if self.esi {
flags |= CANFD_ESI;
}
out[5] = flags;
out[8..8 + self.data.len()].copy_from_slice(&self.data);
Ok(out)
} else {
let mut out = vec![0u8; CAN_MTU];
out[0..4].copy_from_slice(&id_word);
out[4] = if self.rtr {
self.rtr_dlc
} else {
self.data.len() as u8
};
out[8..8 + self.data.len()].copy_from_slice(&self.data);
Ok(out)
}
}
pub fn from_wire_bytes(bytes: &[u8]) -> Result<Self> {
match bytes.len() {
CAN_MTU => {
let id_word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let rtr = id_word & CAN_RTR_FLAG != 0;
let len = (bytes[4] as usize).min(CAN_MAX_DLEN);
let mut frame =
Self::from_id_word(id_word, bytes[8..8 + len].to_vec(), false, false, false);
if rtr {
frame.rtr_dlc = bytes[4] & 0x0F;
}
Ok(frame)
}
CANFD_MTU => {
let id_word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let len = (bytes[4] as usize).min(CANFD_MAX_DLEN);
let flags = bytes[5];
Ok(Self::from_id_word(
id_word,
bytes[8..8 + len].to_vec(),
true,
flags & CANFD_BRS != 0,
flags & CANFD_ESI != 0,
))
}
other => Err(anyhow!(
"a SocketCAN frame is {CAN_MTU} octets (struct can_frame) or {CANFD_MTU} octets \
(struct canfd_frame); got {other}"
)),
}
}
pub fn error_classes(&self) -> Option<Vec<&'static str>> {
if !self.error {
return None;
}
let mut classes = Vec::new();
for (bit, name) in [
(CAN_ERR_TX_TIMEOUT, "tx_timeout"),
(CAN_ERR_LOSTARB, "arbitration_lost"),
(CAN_ERR_CRTL, "controller_problem"),
(CAN_ERR_PROT, "protocol_violation"),
(CAN_ERR_TRX, "transceiver_status"),
(CAN_ERR_ACK, "no_acknowledgement"),
(CAN_ERR_BUSOFF, "bus_off"),
(CAN_ERR_BUSERROR, "bus_error"),
(CAN_ERR_RESTARTED, "controller_restarted"),
(CAN_ERR_CNT, "error_counters"),
] {
if self.id & bit != 0 {
classes.push(name);
}
}
if classes.is_empty() {
classes.push("unspecified");
}
Some(classes)
}
pub fn bus_state(&self) -> Option<BusState> {
if !self.error {
return None;
}
if self.id & CAN_ERR_BUSOFF != 0 {
return Some(BusState::BusOff);
}
if self.id & CAN_ERR_RESTARTED != 0 {
return Some(BusState::ErrorActive);
}
if self.id & CAN_ERR_CRTL == 0 {
return None;
}
let status = *self.data.get(1)?;
if status & (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE) != 0 {
Some(BusState::ErrorPassive)
} else if status & (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING) != 0 {
Some(BusState::ErrorWarning)
} else if status & CAN_ERR_CRTL_ACTIVE != 0 {
Some(BusState::ErrorActive)
} else {
None
}
}
pub fn id_hex(&self) -> String {
if self.extended {
format!("0x{:08X}", self.id)
} else {
format!("0x{:03X}", self.id)
}
}
pub fn describe(&self) -> String {
if self.error {
return format!(
"error frame [{}]",
self.error_classes().unwrap_or_default().join(",")
);
}
let kind = match (self.fd, self.rtr) {
(true, _) => {
if self.brs {
"FD/BRS"
} else {
"FD"
}
}
(false, true) => "RTR",
(false, false) => "CAN",
};
format!(
"{} {} dlc={} {}",
kind,
self.id_hex(),
self.dlc(),
if self.rtr {
"(no data)".to_string()
} else {
hex::encode(&self.data)
}
)
}
pub fn to_event_data(&self) -> serde_json::Map<String, Value> {
let mut map = serde_json::Map::new();
map.insert("id".into(), json!(self.id_hex()));
map.insert("id_decimal".into(), json!(self.id));
map.insert("extended".into(), json!(self.extended));
map.insert("rtr".into(), json!(self.rtr));
map.insert("error".into(), json!(self.error));
map.insert("fd".into(), json!(self.fd));
map.insert("brs".into(), json!(self.brs));
map.insert("esi".into(), json!(self.esi));
map.insert("dlc".into(), json!(self.dlc()));
map.insert("data_length".into(), json!(self.data.len()));
map.insert("data".into(), json!(hex::encode(&self.data)));
map.insert("data_encoding".into(), json!("hex"));
if let Some(classes) = self.error_classes() {
map.insert("error_classes".into(), json!(classes));
}
if let Some(state) = self.bus_state() {
map.insert("bus_state".into(), json!(state.as_str()));
}
map
}
pub fn from_action(action: &Value) -> Result<Self> {
let extended = action
.get("extended")
.and_then(Value::as_bool)
.unwrap_or(false);
let rtr = action.get("rtr").and_then(Value::as_bool).unwrap_or(false);
let fd = action.get("fd").and_then(Value::as_bool).unwrap_or(false);
let brs = action.get("brs").and_then(Value::as_bool).unwrap_or(false);
let id = parse_id(
action
.get("id")
.context("send_can_frame requires 'id' (e.g. \"0x7DF\" or 2015)")?,
)?;
if rtr {
let dlc = action
.get("dlc")
.and_then(Value::as_u64)
.or_else(|| action.get("data_length").and_then(Value::as_u64))
.unwrap_or(0);
if dlc > CAN_MAX_DLEN as u64 {
bail!("a remote frame can request at most 8 bytes; dlc {dlc} given");
}
let mut frame = Self::remote(id, extended, dlc as u8)?;
frame.fd = fd;
frame.brs = brs;
frame.validate()?;
return Ok(frame);
}
let data = decode_payload(action)?;
let frame = Self {
id,
extended,
rtr: false,
error: false,
fd,
brs,
esi: false,
data,
rtr_dlc: 0,
};
frame.validate()?;
Ok(frame)
}
}
fn decode_payload(action: &Value) -> Result<Vec<u8>> {
let Some(data) = action.get("data") else {
return Ok(Vec::new());
};
if let Some(array) = data.as_array() {
return array
.iter()
.map(|v| {
v.as_u64()
.filter(|n| *n <= 0xFF)
.map(|n| n as u8)
.ok_or_else(|| {
anyhow!("every element of a 'data' array must be 0-255, got {v}")
})
})
.collect();
}
let text = data
.as_str()
.ok_or_else(|| anyhow!("'data' must be a string (see 'encoding') or an array of bytes"))?;
match action.get("encoding").and_then(Value::as_str) {
None | Some("hex") => {
let cleaned: String = text
.chars()
.filter(|c| !c.is_whitespace() && *c != ':' && *c != '-')
.collect();
let cleaned = cleaned
.strip_prefix("0x")
.or_else(|| cleaned.strip_prefix("0X"))
.unwrap_or(&cleaned)
.to_string();
hex::decode(&cleaned).with_context(|| {
format!(
"'data' is hex-encoded (the default for CAN) but {text:?} is not valid hex. \
Pass \"encoding\": \"text\" if you meant these characters literally."
)
})
}
Some("text") => Ok(text.as_bytes().to_vec()),
Some(other) => Err(anyhow!(
"unknown encoding {other:?}: expected \"hex\" (the default) or \"text\""
)),
}
}
pub fn parse_id(value: &Value) -> Result<u32> {
if let Some(n) = value.as_u64() {
return u32::try_from(n)
.map_err(|_| anyhow!("identifier {n} does not fit in 32 bits, let alone 29"));
}
let text = value
.as_str()
.ok_or_else(|| anyhow!("'id' must be a number or a hex string such as \"0x7DF\""))?
.trim();
let (body, radix) = match text.strip_prefix("0x").or_else(|| text.strip_prefix("0X")) {
Some(body) => (body, 16),
None => (text, 16),
};
u32::from_str_radix(body, radix)
.with_context(|| format!("'id' {text:?} is not a valid CAN identifier"))
}