From 38309bfb25f3007c0843251fcec055d09f44a90a Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 18:09:43 +0100 Subject: [PATCH 01/29] init j1939 datalink --- Cargo.toml | 1 + examples/forward.rs | 2 +- src/driver/can_id.rs | 297 --------------------- src/driver/frame.rs | 19 -- src/driver/socketcan.rs | 153 ----------- src/{driver => j1939}/address.rs | 0 src/{driver => j1939}/driver.rs | 36 +-- src/j1939/frame.rs | 68 +++++ src/j1939/id.rs | 174 ++++++++++++ src/{driver => j1939}/mod.rs | 11 +- src/{driver => j1939}/pgn.rs | 114 ++++---- src/j1939/priority.rs | 55 ++++ src/lib.rs | 2 +- src/network_management/can_message.rs | 8 +- src/network_management/control_function.rs | 2 +- src/network_management/network_manager.rs | 22 +- 16 files changed, 385 insertions(+), 579 deletions(-) delete mode 100644 src/driver/can_id.rs delete mode 100644 src/driver/frame.rs delete mode 100644 src/driver/socketcan.rs rename src/{driver => j1939}/address.rs (100%) rename src/{driver => j1939}/driver.rs (80%) create mode 100644 src/j1939/frame.rs create mode 100644 src/j1939/id.rs rename src/{driver => j1939}/mod.rs (71%) rename src/{driver => j1939}/pgn.rs (53%) create mode 100644 src/j1939/priority.rs diff --git a/Cargo.toml b/Cargo.toml index c9efc1f..3478513 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ bitvec = "1.0.1" rand = "0.8.5" socketcan = { version = "2.0.0", optional = true } strum_macros = "0.25.2" +embedded-can = "0.4.1" [features] default = [] diff --git a/examples/forward.rs b/examples/forward.rs index a90c5fd..35ad0fa 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -2,7 +2,7 @@ use std::sync::mpsc::channel; -use ag_iso_stack::driver::{Driver, DriverReadError, Frame, SocketcanDriver}; +use ag_iso_stack::j1939::{Driver, DriverReadError, Frame, SocketcanDriver}; use clap::Parser; /// Forward CAN traffic from one interface to another diff --git a/src/driver/can_id.rs b/src/driver/can_id.rs deleted file mode 100644 index 89575fe..0000000 --- a/src/driver/can_id.rs +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2023 Raven Industries inc. -use crate::driver::{Address, Pgn}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Priority { - /// You may also use [`Priority::Highest`] as an alias - Zero = 0x0, - One = 0x1, - Two = 0x2, - Three = 0x3, - Four = 0x4, - Five = 0x5, - /// You may also use [`Priority::Default`] as an alias - Six = 0x6, - /// You may also use [`Priority::Lowest`] as an alias - Seven = 0x7, -} - -#[allow(non_upper_case_globals)] -impl Priority { - pub const Highest: Priority = Priority::Zero; - pub const Default: Priority = Priority::Six; - pub const Lowest: Priority = Priority::Seven; -} - -impl From for Priority { - fn from(value: u8) -> Priority { - match value { - 0x0 => Priority::Zero, - 0x1 => Priority::One, - 0x2 => Priority::Two, - 0x3 => Priority::Three, - 0x4 => Priority::Four, - 0x5 => Priority::Five, - 0x6 => Priority::Six, - 0x7 => Priority::Seven, - _ => unreachable!( - "Internal error converting a value larger than 3 bits to a CAN ID priority" - ), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Type { - /// 11-bit CAN ID - Standard = 0x0, - /// 29-bit CAN ID - Extended = 0x1, -} - -#[derive(Debug, Clone)] -pub struct EncodingError { - pub priority: Priority, - pub parameter_group_number: Pgn, - pub source_address: Address, - pub destination_address: Address, -} - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(transparent)] -pub struct CanId(u32); - -// Linux uses the top three unused bits to indicate whether the frame is standard/extended, remote, -// or an error frame. We do the same, because it's convenient. -const CAN_EFF_FLAG: u32 = 0x80000000; -// const CAN_RTR_FLAG: u32 = 0x40000000; -// const CAN_ERR_FLAG: u32 = 0x20000000; - -const CAN_EFF_MASK: u32 = 0x1FFFFFFF; -const CAN_SFF_MASK: u32 = 0x000007FF; - -impl CanId { - pub fn new(raw: u32, type_: Type) -> Self { - let raw = match type_ { - Type::Extended => (raw & CAN_EFF_MASK) | CAN_EFF_FLAG, - Type::Standard => raw & CAN_SFF_MASK, - }; - Self(raw) - } - - /// Encodes a new extended ID using the discrete parts of an identifier - pub fn try_encode( - parameter_group_number: Pgn, - source_address: Address, - destination_address: Address, - priority: Priority, - ) -> Result { - if destination_address != Address::GLOBAL && parameter_group_number.is_broadcast() { - return Err(EncodingError { - priority, - parameter_group_number, - source_address, - destination_address, - }); - } - Ok(unsafe { - CanId::encode_unchecked( - parameter_group_number, - source_address, - destination_address, - priority, - ) - }) - } - - /// Encodes a new extended ID using the discrete parts of an identifier but won't validate - /// your combination of PGN and destination address. - /// - /// # Safety - /// Calling this without validating your PGN and destination address combination may result in your PGN field - /// getting trashed. Specifically, the risk is when you are using a broadcast PGN but supply a non-0xFF - /// destination address. - pub unsafe fn encode_unchecked( - parameter_group_number: Pgn, - source_address: Address, - destination_address: Address, - priority: Priority, - ) -> CanId { - let mut raw_id: u32 = 0; - - raw_id |= (priority as u32 & 0x07) << 26; - raw_id |= source_address.0 as u32; - - if Address::GLOBAL == destination_address { - if (parameter_group_number.raw() & 0xF000) >= 0xF000 { - raw_id |= (parameter_group_number.raw() & 0x3FFFF) << 8; - } else { - raw_id |= (destination_address.0 as u32) << 8; - raw_id |= (parameter_group_number.raw() & 0x3FF00) << 8; - } - } else if (parameter_group_number.raw() & 0xF000) < 0xF000 { - raw_id |= (destination_address.0 as u32) << 8; - raw_id |= (parameter_group_number.raw() & 0x3FF00) << 8; - } - CanId::new(raw_id & CAN_EFF_MASK, Type::Extended) - } - - /// Get the raw value of the CAN ID - #[inline] - pub fn raw(&self) -> u32 { - match self.type_() { - Type::Extended => self.0 & CAN_EFF_MASK, - Type::Standard => self.0 & CAN_SFF_MASK, - } - } - - /// Get the type of the ID (standard or extended) - #[inline] - pub fn type_(&self) -> Type { - if self.0 & CAN_EFF_FLAG != 0 { - Type::Extended - } else { - Type::Standard - } - } - - /// Get the priority of the ID - #[inline] - pub fn priority(&self) -> Priority { - match self.type_() { - Type::Standard => Priority::Highest, - Type::Extended => { - let raw = ((self.raw() & 0x1C000000) >> 26) as u8; - raw.into() - } - } - } - - /// Get the source address of the ID - #[inline] - pub fn source_address(&self) -> Address { - match self.type_() { - Type::Standard => Address::GLOBAL, - Type::Extended => Address((self.raw() & 0xFF) as u8), - } - } - - /// Get the ID's PGN - /// - /// In the case the the ID is a standard 11-bit ID, a NULL PGN will be returned. - #[inline] - pub fn pgn(&self) -> Pgn { - match self.type_() { - Type::Standard => Pgn::NULL, - Type::Extended => Pgn::from_id(self.raw()), - } - } - - /// Get the destination address for this CAN ID, if it's a destination-specific PGN - #[inline] - pub fn destination_address(&self) -> Address { - let pgn = self.pgn(); - if pgn == Pgn::NULL || pgn.is_broadcast() { - return Address::GLOBAL; - } - - let raw_pdu_s = ((self.raw() & 0xFF00) >> 8) as u8; - Address(raw_pdu_s) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_priority() { - let can_id = CanId::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.priority(), Priority::Default); - } - - #[test] - fn test_source_address() { - let can_id = CanId::new(0x0705, Type::Standard); - assert_eq!(can_id.type_(), Type::Standard); - // TODO: Is this right? Do 11-bit IDs always have a global address? - assert_eq!(can_id.source_address(), Address::GLOBAL); - - let can_id = CanId::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.source_address(), Address(0xF5)); - } - - #[test] - fn test_destination_address() { - let can_id = CanId::new(0x0705, Type::Standard); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = CanId::new(0x18EEFF1C, Type::Extended); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = CanId::new(0x09F8031C, Type::Extended); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = CanId::new(0x0CAC1C13, Type::Extended); - assert_eq!(can_id.destination_address(), Address(0x1C)); - } - - #[test] - fn test_pgn() { - let can_id = CanId::new(0x07FF, Type::Standard); - assert_eq!(can_id.pgn(), Pgn::NULL); - - let can_id = CanId::new(0x0CAC1C13, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0AC00)); - - let can_id = CanId::new(0x18FF3F13, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0FF3F)); - - let can_id = CanId::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EF00)); - - let can_id = CanId::new(0x18EEFF1C, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EE00)); - } - - #[test] - fn test_encode() { - let encode_result = CanId::try_encode( - Pgn::from_raw(0x00EF00), - Address(0x81), - Address(0xF9), - Priority::Six, - ); - let can_id = encode_result.expect("EF00 Message was not encodable"); - assert_eq!(can_id.pgn(), Pgn::from_raw(0xEF00)); - assert_eq!(can_id.destination_address(), Address(0xF9)); - assert_eq!(can_id.source_address(), Address(0x81)); - assert_eq!(can_id.priority(), Priority::Six); - - let encode_result = CanId::try_encode( - Pgn::from_raw(0x00FF40), - Address(0x81), - Address(0xFF), - Priority::Six, - ); - let can_id = encode_result.expect("FF40 Message was not encodable"); - assert_eq!(can_id.pgn(), Pgn::from_raw(0xFF40)); - assert_eq!(can_id.destination_address(), Address(0xFF)); - assert_eq!(can_id.source_address(), Address(0x81)); - assert_eq!(can_id.priority(), Priority::Six); - - let encode_result = CanId::try_encode( - Pgn::from_raw(0x00FF40), - Address(0x81), - Address(0x0F), - Priority::Six, - ); - assert!(encode_result.is_err()); - - let error_contents: EncodingError = encode_result.unwrap_err(); - assert_eq!(error_contents.priority, Priority::Six); - assert_eq!(error_contents.source_address, Address(0x81)); - assert_eq!(error_contents.destination_address, Address(0x0F)); - assert_eq!(error_contents.parameter_group_number, Pgn::from_raw(0xFF40)); - } -} diff --git a/src/driver/frame.rs b/src/driver/frame.rs deleted file mode 100644 index 6a4744b..0000000 --- a/src/driver/frame.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2023 Raven Industries inc. -use crate::driver::CanId; - -#[derive(Debug, Default)] -#[repr(transparent)] -pub struct Channel(u8); - -#[derive(Debug, Default)] -pub struct Frame { - // TODO: Is a Duration too large (64 + 32 bits) for an object that will be created so often? - // Would it be better to use a u64 for microseconds? - // TODO: Is this just a monotonically increasing number, or is it a unix timestamp? - pub timestamp: std::time::Duration, - pub id: CanId, - pub channel: Channel, - pub data: [u8; 8], - pub data_length: u8, - pub extended: bool, -} diff --git a/src/driver/socketcan.rs b/src/driver/socketcan.rs deleted file mode 100644 index 09b0e35..0000000 --- a/src/driver/socketcan.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2023 Raven Industries inc. -use std::time::Instant; - -use socketcan::frame::{CanDataFrame, CanFrame, Frame}; -use socketcan::{CanSocket, EmbeddedFrame, ExtendedId, Id, Socket, StandardId}; - -use crate::driver::{ - CanId, Channel, Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError, - Frame as InternalFrame, Type, -}; - -impl From for DriverReadError { - fn from(e: socketcan::Error) -> DriverReadError { - match e { - socketcan::Error::Can(_) => DriverReadError::ErrorFrame(), - socketcan::Error::Io(e) => DriverReadError::IoError(e), - } - } -} - -impl From for DriverWriteError { - fn from(e: socketcan::Error) -> DriverWriteError { - match e { - socketcan::Error::Can(_) => DriverWriteError::BusError(), - socketcan::Error::Io(e) => DriverWriteError::IoError(e), - } - } -} - -impl From<&InternalFrame> for socketcan::frame::CanDataFrame { - fn from(f: &InternalFrame) -> socketcan::frame::CanDataFrame { - let id = match f.id.type_() { - Type::Standard => Id::Standard(unsafe { StandardId::new_unchecked(f.id.raw() as u16) }), - Type::Extended => Id::Extended(unsafe { ExtendedId::new_unchecked(f.id.raw()) }), - }; - CanDataFrame::new(id, &f.data[..f.data_length.min(8) as usize]) - // guaranteed to not crash, because `f.data` is an [u8; 8] - .expect("Can frame had too much data") - } -} - -enum SocketcanIface { - Name(String), - Index(u32), -} - -/// A Linux socketcan [Driver] -/// -/// Enabled with the optional `socketcan` feature -pub struct SocketcanDriver { - iface: SocketcanIface, - sock: Option, - opened_timestamp: Instant, -} - -impl SocketcanDriver { - /// Create a socketcan driver with the given interface name. E.g., `can0`, or `vcan1` - pub fn new_by_name(if_name: &str) -> Self { - Self { - iface: SocketcanIface::Name(if_name.to_string()), - sock: None, - opened_timestamp: Instant::now(), - } - } - - /// Create a socketcan driver with the given interface index - pub fn new_by_index(if_index: u32) -> Self { - Self { - iface: SocketcanIface::Index(if_index), - sock: None, - opened_timestamp: Instant::now(), - } - } - - fn to_frame(&self, f: CanFrame) -> InternalFrame { - match f { - CanFrame::Remote(_r) => todo!("Remote frames unsupported yet"), - CanFrame::Error(_e) => todo!("Error frames unsupported yet"), - CanFrame::Data(f) => { - let timestamp = self.opened_timestamp.elapsed(); - let raw_id = f.raw_id(); - let extended = f.is_extended(); - let frame_type = if extended { - Type::Extended - } else { - Type::Standard - }; - - let id = CanId::new(raw_id, frame_type); - // TODO: The Driver trait doesn't know anything about Channels yet. - // - // The channel exists so that we can tie Frames and CANMessages back to the network - // manager they originated from. This channel value should be passed to the Driver - // when it's created (or opened?) - let channel = Channel::default(); - let mut data = [0; 8]; - let data_length = f.dlc().min(8); - data[..data_length].copy_from_slice(f.data()); - let data_length = data_length as u8; - - InternalFrame { - timestamp, - id, - channel, - data, - data_length, - extended, - } - } - } - } -} - -impl Driver for SocketcanDriver { - fn is_valid(&self) -> bool { - self.sock.is_some() - } - fn open(&mut self) -> Result<(), DriverOpenError> { - match &self.iface { - SocketcanIface::Name(s) => self.sock = Some(CanSocket::open(s)?), - SocketcanIface::Index(i) => self.sock = Some(CanSocket::open_iface(*i)?), - } - self.opened_timestamp = Instant::now(); - - // NOTE: unwrap() is safe, because we return a DriverOpenError if we fail to create it. - self.sock.as_ref().unwrap().set_nonblocking(true)?; - Ok(()) - } - fn close(&mut self) -> Result<(), DriverCloseError> { - self.sock = None; - Ok(()) - } - - /// Read a frame from the driver, if possible - /// - /// The timestamp on the frame is the duration since [`open`](Self::open) was last called. - fn read_nonblocking(&mut self, frame: &mut InternalFrame) -> Result<(), DriverReadError> { - let Some(sock) = self.sock.as_mut() else { - return Err(DriverReadError::DriverClosed); - }; - let socketcan_frame = sock.read_frame()?; - *frame = self.to_frame(socketcan_frame); - Ok(()) - } - fn write_nonblocking(&mut self, frame: &InternalFrame) -> Result<(), DriverWriteError> { - let Some(sock) = self.sock.as_mut() else { - return Err(DriverWriteError::DriverClosed); - }; - let socketcan_frame: socketcan::frame::CanDataFrame = frame.into(); - sock.write_frame(&socketcan_frame)?; - Ok(()) - } -} diff --git a/src/driver/address.rs b/src/j1939/address.rs similarity index 100% rename from src/driver/address.rs rename to src/j1939/address.rs diff --git a/src/driver/driver.rs b/src/j1939/driver.rs similarity index 80% rename from src/driver/driver.rs rename to src/j1939/driver.rs index fa7d0dc..75d1b37 100644 --- a/src/driver/driver.rs +++ b/src/j1939/driver.rs @@ -1,16 +1,16 @@ // Copyright 2023 Raven Industries inc. -use crate::driver::Frame; +use crate::j1939::Frame; #[derive(Debug)] #[non_exhaustive] pub enum DriverOpenError { - /// The driver failed to open with filesystem semantics + /// The j1939 failed to open with filesystem semantics IoError(std::io::Error), } impl std::fmt::Display for DriverOpenError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Failed to open driver: {:?}", self) + write!(f, "Failed to open j1939: {:?}", self) } } impl std::error::Error for DriverOpenError { @@ -43,11 +43,11 @@ impl std::error::Error for DriverCloseError {} pub enum DriverReadError { /// There is no frame ready to be read NoFrameReady, - /// The driver has been closed + /// The j1939 has been closed DriverClosed, - /// The driver received an error frame + /// The j1939 received an error frame ErrorFrame(), - /// The driver failed to read with filesystem semantics + /// The j1939 failed to read with filesystem semantics IoError(std::io::Error), } @@ -71,9 +71,9 @@ impl From for DriverReadError { #[derive(Debug)] #[non_exhaustive] pub enum DriverWriteError { - /// The driver's internal buffer is full, or the driver is otherwise busy + /// The j1939's internal buffer is full, or the j1939 is otherwise busy NotReady, - /// The driver has been closed + /// The j1939 has been closed DriverClosed, /// Some fault with the CAN bus BusError(), @@ -102,23 +102,23 @@ impl From for DriverWriteError { /// /// This layer is meant to abstract the hardware, and should not do its own queuing/buffering. /// -/// This trait does _not_ define how to construct and configure a driver, as the details are likely -/// to differ from driver to driver. +/// This trait does _not_ define how to construct and configure a j1939, as the details are likely +/// to differ from j1939 to j1939. pub trait Driver { - /// Determine whether the driver is connected and healthy + /// Determine whether the j1939 is connected and healthy fn is_valid(&self) -> bool; - /// Open the driver + /// Open the j1939 /// - /// It is expected you must open the driver after creating it + /// It is expected you must open the j1939 after creating it fn open(&mut self) -> Result<(), DriverOpenError>; - /// Close the driver + /// Close the j1939 /// - /// It is not necessary to close the driver before dropping it + /// It is not necessary to close the j1939 before dropping it fn close(&mut self) -> Result<(), DriverCloseError>; - /// Read a [Frame] from the driver, if possible + /// Read a [Frame] from the j1939, if possible /// /// This is a non-blocking read. If there is no frame ready to read, this function will return /// [DriverReadError::NoFrameReady]. @@ -127,9 +127,9 @@ pub trait Driver { /// each call, or to re-use memory. fn read_nonblocking(&mut self, frame: &mut Frame) -> Result<(), DriverReadError>; - /// Write a [Frame] to the driver, if possible + /// Write a [Frame] to the j1939, if possible /// - /// This is a non-blocking write. If the frame cannot be written because the driver's + /// This is a non-blocking write. If the frame cannot be written because the j1939's /// queue/buffer is full (for drivers like `socketcan` that do internal buffering), or if /// it's otherwise busy, this function will return [DriverWriteError::NotReady]. /// diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs new file mode 100644 index 0000000..82e8baa --- /dev/null +++ b/src/j1939/frame.rs @@ -0,0 +1,68 @@ +use socketcan::{embedded_can, Id as EmbeddedId}; +// Copyright 2023 Raven Industries inc. +use crate::j1939::Id; +use embedded_can::Frame as EmbeddedFrame; + +#[derive(Debug, Default)] +#[repr(transparent)] +pub struct Channel(u8); + +#[derive(Debug, Default)] +pub struct Frame { + pub timestamp: std::time::Duration, + pub id: Id, + pub data: Vec, +} + +impl Frame { + pub fn new(id: Id, data: Vec) -> Self { + Self { + timestamp: todo!(), + id, + data, + } + } +} + +impl EmbeddedFrame for Frame { + fn new(id: impl Into, data: &[u8]) -> Option { + Self::new(id.into(), data.to_vec()) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + //J1939 does not support remote frame + None + } + + fn is_extended(&self) -> bool { + // J1939 only supports extended frames + true + } + + fn is_standard(&self) -> bool { + // J1939 only supports extended frames + false + } + + fn is_remote_frame(&self) -> bool { + // J1939 does not support remote frames + false + } + + fn is_data_frame(&self) -> bool { + // J1939 only supports data frames + true + } + + fn id(&self) -> EmbeddedId { + todo!() + } + + fn dlc(&self) -> usize { + self.data.len() + } + + fn data(&self) -> &[u8] { + self.data.as_slice() + } +} diff --git a/src/j1939/id.rs b/src/j1939/id.rs new file mode 100644 index 0000000..d5db945 --- /dev/null +++ b/src/j1939/id.rs @@ -0,0 +1,174 @@ +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::vec::BitVec; +use bitvec::view::BitView; +// Copyright 2023 Raven Industries inc. +use crate::j1939::priority::Priority; +use crate::j1939::{Address, Pgn}; + +pub enum ParseIdError { + priority_parse_error, + pgn_parse_error, + source_address_parse_error, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct Id { + priority: Priority, + pgn: Pgn, + source_address: Address, +} + +impl Id { + pub fn new(priority: Priority, pgn: Pgn, source_address: Address) -> Self { + Self { + priority, + pgn, + source_address, + } + } + + pub fn try_from_raw(raw_id: u32) -> Result { + let bit_data = raw_id.view_bits::().to_bitvec(); + let priority_parse = Priority::try_from(bit_data.load::()); + let pgn_parse = Pgn::try_from(); + } + + /// Get the raw value of the CAN ID + #[inline] + pub fn raw(&self) -> u32 { + let mut raw_id: BitVec = BitVec::new(); + raw_id.extend(self.priority); + raw_id.extend(self.pgn); + raw_id.extend(self.source_address); + raw_id.load::().unwrap() + } + + /// Get the type of the ID (standard or extended) + #[inline] + pub fn type_(&self) -> Type { + if self.0 & CAN_EFF_FLAG != 0 { + Type::Extended + } else { + Type::Standard + } + } + + /// Get the priority of the ID + #[inline] + pub fn priority(&self) -> Priority { + match self.type_() { + Type::Standard => Priority::Highest, + Type::Extended => { + let raw = ((self.raw() & 0x1C000000) >> 26) as u8; + raw.into() + } + } + } + + /// Get the source address of the ID + #[inline] + pub fn source_address(&self) -> Address { + match self.type_() { + Type::Standard => Address::GLOBAL, + Type::Extended => Address((self.raw() & 0xFF) as u8), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_priority() { + let can_id = Id::new(0x18EF1CF5, Type::Extended); + assert_eq!(can_id.priority(), Priority::Default); + } + + #[test] + fn test_source_address() { + let can_id = Id::new(0x0705, Type::Standard); + assert_eq!(can_id.type_(), Type::Standard); + // TODO: Is this right? Do 11-bit IDs always have a global address? + assert_eq!(can_id.source_address(), Address::GLOBAL); + + let can_id = Id::new(0x18EF1CF5, Type::Extended); + assert_eq!(can_id.source_address(), Address(0xF5)); + } + + #[test] + fn test_destination_address() { + let can_id = Id::new(0x0705, Type::Standard); + assert_eq!(can_id.destination_address(), Address::GLOBAL); + + let can_id = Id::new(0x18EEFF1C, Type::Extended); + assert_eq!(can_id.destination_address(), Address::GLOBAL); + + let can_id = Id::new(0x09F8031C, Type::Extended); + assert_eq!(can_id.destination_address(), Address::GLOBAL); + + let can_id = Id::new(0x0CAC1C13, Type::Extended); + assert_eq!(can_id.destination_address(), Address(0x1C)); + } + + #[test] + fn test_pgn() { + let can_id = Id::new(0x07FF, Type::Standard); + assert_eq!(can_id.pgn(), Pgn::NULL); + + let can_id = Id::new(0x0CAC1C13, Type::Extended); + assert_eq!(can_id.pgn(), Pgn::from_raw(0x0AC00)); + + let can_id = Id::new(0x18FF3F13, Type::Extended); + assert_eq!(can_id.pgn(), Pgn::from_raw(0x0FF3F)); + + let can_id = Id::new(0x18EF1CF5, Type::Extended); + assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EF00)); + + let can_id = Id::new(0x18EEFF1C, Type::Extended); + assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EE00)); + } + + #[test] + fn test_encode() { + let encode_result = Id::try_encode( + Pgn::from_raw(0x00EF00), + Address(0x81), + Address(0xF9), + Priority::Six, + ); + let can_id = encode_result.expect("EF00 Message was not encodable"); + assert_eq!(can_id.pgn(), Pgn::from_raw(0xEF00)); + assert_eq!(can_id.destination_address(), Address(0xF9)); + assert_eq!(can_id.source_address(), Address(0x81)); + assert_eq!(can_id.priority(), Priority::Six); + + let encode_result = Id::try_encode( + Pgn::from_raw(0x00FF40), + Address(0x81), + Address(0xFF), + Priority::Six, + ); + let can_id = encode_result.expect("FF40 Message was not encodable"); + assert_eq!(can_id.pgn(), Pgn::from_raw(0xFF40)); + assert_eq!(can_id.destination_address(), Address(0xFF)); + assert_eq!(can_id.source_address(), Address(0x81)); + assert_eq!(can_id.priority(), Priority::Six); + + let encode_result = Id::try_encode( + Pgn::from_raw(0x00FF40), + Address(0x81), + Address(0x0F), + Priority::Six, + ); + assert!(encode_result.is_err()); + + let error_contents: EncodingError = encode_result.unwrap_err(); + assert_eq!(error_contents.priority, Priority::Six); + assert_eq!(error_contents.source_address, Address(0x81)); + assert_eq!(error_contents.destination_address, Address(0x0F)); + assert_eq!(error_contents.parameter_group_number, Pgn::from_raw(0xFF40)); + } +} diff --git a/src/driver/mod.rs b/src/j1939/mod.rs similarity index 71% rename from src/driver/mod.rs rename to src/j1939/mod.rs index 0e39c02..eb7243a 100644 --- a/src/driver/mod.rs +++ b/src/j1939/mod.rs @@ -7,19 +7,14 @@ //! 2. `Frame`, `Pgn`, `Address`, et al types mod address; -mod can_id; mod driver; mod frame; +mod id; mod pgn; - -#[cfg(feature = "socketcan")] -mod socketcan; +mod priority; pub use address::Address; -pub use can_id::{CanId, Priority, Type}; pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; pub use frame::{Channel, Frame}; +pub use id::{Id, Priority}; pub use pgn::Pgn; - -#[cfg(feature = "socketcan")] -pub use self::socketcan::SocketcanDriver; diff --git a/src/driver/pgn.rs b/src/j1939/pgn.rs similarity index 53% rename from src/driver/pgn.rs rename to src/j1939/pgn.rs index f79c426..aa1ad06 100644 --- a/src/driver/pgn.rs +++ b/src/j1939/pgn.rs @@ -1,85 +1,67 @@ // Copyright 2023 Raven Industries inc. +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::vec::BitVec; +use bitvec::view::BitView; +use std::io::Read; + +pub enum ParsePgnError { + InvalidPgnLength, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] -pub struct Pgn(u32); +pub struct Pgn { + reserved: bool, + data_page: bool, + pdu_format: u8, + pdu_specific: u8, +} impl Pgn { - /// A fake PGN used to denote a PGN that does not exist - pub const NULL: Pgn = Pgn(0xFFFFFFFF); - - pub fn from_id(can_id: u32) -> Self { - const PDU2_FORMAT_MASK: u32 = 0x00F00000; - let raw_pgn = if (can_id & PDU2_FORMAT_MASK) < PDU2_FORMAT_MASK { - // point-to-point - (can_id >> 8) & 0x03FF00 - } else { - // broadcast - (can_id >> 8) & 0x03FFFF - }; - Pgn(raw_pgn) - } - - pub fn from_raw(pgn: u32) -> Self { - Pgn(pgn) - } - - #[inline] - pub fn is_broadcast(&self) -> bool { - !self.is_destination_specific() - } - - #[inline] - pub fn is_destination_specific(&self) -> bool { - // PDU1 / destination specific PGNs have a PDU Format 0x00 - 0xEF - // PDU2 / broadcast PGNs have a PDU Format 0xF0 - 0xFF - self.pdu_format() <= 0xEF - } - - #[inline] - pub fn is_proprietary(&self) -> bool { - self.pdu_format() == 0xEF - } - - #[inline] - pub fn raw(&self) -> u32 { - self.0 - } - - #[inline] - pub fn pdu_specific(&self) -> u8 { - (self.raw() & 0x00FF) as u8 - } - - #[inline] - pub fn pdu_format(&self) -> u8 { - ((self.raw() & 0xFF00) >> 8) as u8 - } - - #[inline] - pub fn data_page(&self) -> u8 { - ((self.raw() & 0x10000) >> 16) as u8 + pub const MAX_PGN_VALUE: u32 = 0x3FFFF; + + pub fn new(reserved: bool, data_page: bool, pdu_format: u8, pdu_specific: u8) -> Self { + Self { + reserved, + data_page, + pdu_format, + pdu_specific, + } } +} - #[inline] - pub fn extended_data_page(&self) -> u8 { - ((self.raw() & 0x20000) >> 17) as u8 +impl TryFrom for Pgn { + type Error = ParsePgnError; + + fn try_from(raw_pgn: u32) -> Result { + if (raw_pgn > Self::MAX_PGN_VALUE) { + // raw value is too large to fit in PGN with 18 bits + Err(ParsePgnError::InvalidPgnLength) + } + + let mut bit_data = raw_pgn.view_bits::().to_bitvec(); + Ok(Self { + reserved: bit_data.pop().unwrap(), + data_page: bit_data.pop().unwrap(), + pdu_format: bit_data.load::().unwrap(), + pdu_specific: bit_data.load::().unwrap(), + }) } } #[cfg(test)] mod tests { - use super::*; + use crate::j1939::Pgn; #[test] - fn test_from_id() { - let pgn = Pgn::from_id(0x18EF1CF5); - let expected = Pgn::from_raw(0x0EF00); - assert_eq!(pgn, expected); + fn test_from_raw() { + let pgn_parsed = Pgn::from_raw(0x00F04); + assert_eq!(pgn_parsed.is_ok(), true); - let pgn = Pgn::from_id(0x18FF3F13); - let expected = Pgn::from_raw(0x0FF3F); - assert_eq!(pgn, expected); + let pgn = Pgn::new(false, false, 0xF0, 0x04); + assert_eq!(pgn, pgn_parsed.unwrap()); } #[test] diff --git a/src/j1939/priority.rs b/src/j1939/priority.rs new file mode 100644 index 0000000..f8c71c8 --- /dev/null +++ b/src/j1939/priority.rs @@ -0,0 +1,55 @@ +use crate::j1939::DriverOpenError; + +#[derive(Debug)] +struct ParsePriorityError(u8); + +//TODO!: custom parse priority error + +impl std::fmt::Display for ParsePriorityError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Parse '{:?}' failed because the permitted priority value is between 0 and 7!", + self + ) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Priority { + /// You may also use [`Priority::HIGHEST`] as an alias + Zero = 0x0, + One = 0x1, + Two = 0x2, + Three = 0x3, + Four = 0x4, + Five = 0x5, + /// You may also use [`Priority::DEFAULT`] as an alias + Six = 0x6, + /// You may also use [`Priority::LOWEST`] as an alias + Seven = 0x7, +} + +impl Priority { + pub const HIGHEST: Priority = Priority::Zero; + pub const DEFAULT: Priority = Priority::Six; + pub const LOWEST: Priority = Priority::Seven; +} + +impl TryFrom for Priority { + type Error = ParsePriorityError; + + fn try_from(raw_priority: u8) -> Result { + match raw_priority { + 0x0 => Ok(Priority::Zero), + 0x1 => Ok(Priority::One), + 0x2 => Ok(Priority::Two), + 0x3 => Ok(Priority::Three), + 0x4 => Ok(Priority::Four), + 0x5 => Ok(Priority::Five), + 0x6 => Ok(Priority::Six), + 0x7 => Ok(Priority::Seven), + _ => Err(ParsePriorityError(raw_priority)), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b3b85af..77dd717 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,6 @@ #![allow(clippy::needless_return)] #![allow(clippy::module_inception)] -pub mod driver; +pub mod j1939; pub mod network_management; pub mod object_pool; diff --git a/src/network_management/can_message.rs b/src/network_management/can_message.rs index 0355e7e..dcafb8b 100644 --- a/src/network_management/can_message.rs +++ b/src/network_management/can_message.rs @@ -1,16 +1,16 @@ // Copyright 2023 Raven Industries inc. use super::name::NAME; -use crate::driver::CanId; +use crate::j1939::Id; pub struct CANMessage { data: Vec, - identifier: CanId, + identifier: Id, source_name: NAME, destination_name: NAME, } impl CANMessage { - pub(super) fn new(data: Vec, identifier: CanId) -> CANMessage { + pub(super) fn new(data: Vec, identifier: Id) -> CANMessage { CANMessage { data, identifier, @@ -23,7 +23,7 @@ impl CANMessage { self.data.as_slice() } - pub fn get_identifier(&self) -> CanId { + pub fn get_identifier(&self) -> Id { self.identifier } diff --git a/src/network_management/control_function.rs b/src/network_management/control_function.rs index 34999a6..2bb57ed 100644 --- a/src/network_management/control_function.rs +++ b/src/network_management/control_function.rs @@ -1,5 +1,5 @@ // Copyright 2023 Raven Industries inc. -use crate::driver::Address; +use crate::j1939::Address; use crate::network_management::name::NAME; use rand::Rng; use std::cell::RefCell; diff --git a/src/network_management/network_manager.rs b/src/network_management/network_manager.rs index 7314bd1..f8e713e 100644 --- a/src/network_management/network_manager.rs +++ b/src/network_management/network_manager.rs @@ -2,7 +2,7 @@ use std::time::Instant; use super::control_function::{AddressClaimingState, ControlFunction}; -use crate::driver::{Address, CanId, Pgn, Priority}; +use crate::j1939::{Address, Id, Pgn, Priority}; use crate::network_management::can_message::CANMessage; use crate::network_management::common_parameter_group_numbers::CommonParameterGroupNumbers; use crate::network_management::name::NAME; @@ -12,9 +12,9 @@ use std::rc::Rc; #[derive(Debug, Clone, Copy)] pub(super) enum MessageQueuePriority { - /// High priority messages are always sent to the driver before normal ones + /// High priority messages are always sent to the j1939 before normal ones High, - /// Normal messages are sent to the driver when no high priority messages are in the queue (todo) + /// Normal messages are sent to the j1939 when no high priority messages are in the queue (todo) Normal, } @@ -101,7 +101,7 @@ impl NetworkManager { pub(super) fn construct_address_claim(source_address: Address, name: NAME) -> CANMessage { let address_claim = >::into(name).to_le_bytes().to_vec(); - let request_id = CanId::try_encode( + let request_id = Id::try_encode( Pgn::from_raw(CommonParameterGroupNumbers::AddressClaim as u32), source_address, Address::BROADCAST, @@ -113,7 +113,7 @@ impl NetworkManager { pub(super) fn construct_request_for_address_claim() -> CANMessage { let pgn_to_request: u32 = CommonParameterGroupNumbers::AddressClaim as u32; let request = pgn_to_request.to_le_bytes().to_vec(); - let request_id = CanId::try_encode( + let request_id = Id::try_encode( Pgn::from_raw(CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32), Address::NULL, Address::BROADCAST, @@ -152,15 +152,15 @@ impl NetworkManager { if data.len() <= 8 { let source = source.borrow(); let destination = destination.borrow(); - let message_id = CanId::try_encode( + let message_id = Id::try_encode( parameter_group_number, self.get_control_function_address_by_name(source.get_name()), self.get_control_function_address_by_name(destination.get_name()), priority, ) - .unwrap_or(CanId::default()); + .unwrap_or(Id::default()); - if message_id.raw() != CanId::default().raw() { + if message_id.raw() != Id::default().raw() { self.enqueue_can_message( CANMessage::new(data.to_vec(), message_id), MessageQueuePriority::Normal, @@ -298,15 +298,15 @@ impl NetworkManager { } fn update_transmit_messages(&mut self) { - let should_continue_sending: bool = true; // Todo, check driver return values. + let should_continue_sending: bool = true; // Todo, check j1939 return values. while !self.high_priority_can_message_tx_queue.is_empty() { - // todo hand off to driver + // todo hand off to j1939 self.high_priority_can_message_tx_queue.pop_front(); } while should_continue_sending && !self.normal_priority_can_message_tx_queue.is_empty() { - // todo hand off to driver + // todo hand off to j1939 self.normal_priority_can_message_tx_queue.pop_front(); } } From bf52d3016bf89ce2d11f9c1e09b7c7d06a537600 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 18:24:58 +0100 Subject: [PATCH 02/29] remove socketcan feature and move socketcan to dev dependency socketcan will only be needed only inside the example so we can change it into dev-dependencies --- Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3478513..deee44b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,11 @@ keywords = ["agriculture", "can", "canbus", "isobus", "j1939", "agritech", "smar [dependencies] bitvec = "1.0.1" rand = "0.8.5" -socketcan = { version = "2.0.0", optional = true } strum_macros = "0.25.2" embedded-can = "0.4.1" [features] default = [] -socketcan = ["dep:socketcan"] [dev-dependencies] clap = { version = "4.3.19", features = ["derive"] } @@ -23,7 +21,7 @@ ctrlc = "3.4.0" # TODO: Add optional tracing to the main library tracing = "0.1.37" tracing-subscriber = "0.3.17" +socketcan = { version = "3.3.0" } [[example]] name = "forward" -required-features = ["socketcan"] From 4b154032fbee0c4e60ca347b0cbef53bdba9d161 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 18:54:18 +0100 Subject: [PATCH 03/29] extend address helper functions --- src/j1939/address.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index ca65d05..14af203 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -2,15 +2,34 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] -pub struct Address(pub u8); +pub struct Address(u8); impl Address { /// Address representing broadcasts for destination specific PGNs - pub const GLOBAL: Address = Address(0xFF); + pub const GLOBAL: Address = Self::BROADCAST; /// Alias for the global address pub const BROADCAST: Address = Address(0xFF); /// The null address is used by ECUs without an address such as during address claiming pub const NULL: Address = Address(0xFE); + + pub fn new(raw_address: u8) -> Self { + Self { 0: raw_address } + } + + #[inline] + pub fn is_global(&self) -> bool { + self.is_broadcast() + } + + #[inline] + pub fn is_broadcast(&self) -> bool { + self == Self::BROADCAST + } + + #[inline] + pub fn is_null(&self) -> bool { + self == Self::NULL + } } // TODO: custom Debug impl and helpers From aa657d771f393328f503926c9c574b587ce9cab6 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 18:57:50 +0100 Subject: [PATCH 04/29] add custom error for priority and helper functions --- src/j1939/priority.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/j1939/priority.rs b/src/j1939/priority.rs index f8c71c8..c6859c0 100644 --- a/src/j1939/priority.rs +++ b/src/j1939/priority.rs @@ -3,8 +3,6 @@ use crate::j1939::DriverOpenError; #[derive(Debug)] struct ParsePriorityError(u8); -//TODO!: custom parse priority error - impl std::fmt::Display for ParsePriorityError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -34,6 +32,21 @@ impl Priority { pub const HIGHEST: Priority = Priority::Zero; pub const DEFAULT: Priority = Priority::Six; pub const LOWEST: Priority = Priority::Seven; + + #[inline] + pub fn is_highest(&self) -> bool { + self == Self::HIGHEST + } + + #[inline] + pub fn is_default(&self) -> bool { + self == Self::DEFAULT + } + + #[inline] + pub fn is_lowest(&self) -> bool { + self == Self::LOWEST + } } impl TryFrom for Priority { From bfb4a82e5d65e8fe124bbfcff60a5f5fb08146f3 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 19:14:37 +0100 Subject: [PATCH 05/29] socketcan only on unix platforms --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index deee44b..ab3c2ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ ctrlc = "3.4.0" # TODO: Add optional tracing to the main library tracing = "0.1.37" tracing-subscriber = "0.3.17" + +[target.'cfg(unix)'.dev-dependencies] socketcan = { version = "3.3.0" } [[example]] From d611862f0ba1eb46fa369b976b528c3a7f162833 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 19:38:18 +0100 Subject: [PATCH 06/29] replace old id code and remove tests to add them later again --- src/j1939/id.rs | 132 ++++++------------------------------------------ 1 file changed, 15 insertions(+), 117 deletions(-) diff --git a/src/j1939/id.rs b/src/j1939/id.rs index d5db945..f42fb51 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -5,6 +5,7 @@ use bitvec::view::BitView; // Copyright 2023 Raven Industries inc. use crate::j1939::priority::Priority; use crate::j1939::{Address, Pgn}; +use embedded_can::{ExtendedId, Id as EmbeddedId}; pub enum ParseIdError { priority_parse_error, @@ -29,12 +30,6 @@ impl Id { } } - pub fn try_from_raw(raw_id: u32) -> Result { - let bit_data = raw_id.view_bits::().to_bitvec(); - let priority_parse = Priority::try_from(bit_data.load::()); - let pgn_parse = Pgn::try_from(); - } - /// Get the raw value of the CAN ID #[inline] pub fn raw(&self) -> u32 { @@ -45,130 +40,33 @@ impl Id { raw_id.load::().unwrap() } - /// Get the type of the ID (standard or extended) - #[inline] - pub fn type_(&self) -> Type { - if self.0 & CAN_EFF_FLAG != 0 { - Type::Extended - } else { - Type::Standard - } - } - /// Get the priority of the ID #[inline] pub fn priority(&self) -> Priority { - match self.type_() { - Type::Standard => Priority::Highest, - Type::Extended => { - let raw = ((self.raw() & 0x1C000000) >> 26) as u8; - raw.into() - } - } + self.priority } /// Get the source address of the ID #[inline] pub fn source_address(&self) -> Address { - match self.type_() { - Type::Standard => Address::GLOBAL, - Type::Extended => Address((self.raw() & 0xFF) as u8), - } + self.source_address } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_priority() { - let can_id = Id::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.priority(), Priority::Default); - } - - #[test] - fn test_source_address() { - let can_id = Id::new(0x0705, Type::Standard); - assert_eq!(can_id.type_(), Type::Standard); - // TODO: Is this right? Do 11-bit IDs always have a global address? - assert_eq!(can_id.source_address(), Address::GLOBAL); - - let can_id = Id::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.source_address(), Address(0xF5)); +impl From for EmbeddedId { + fn from(id: Id) -> Self { + EmbeddedId::Extended(ExtendedId(id.raw())) } +} - #[test] - fn test_destination_address() { - let can_id = Id::new(0x0705, Type::Standard); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = Id::new(0x18EEFF1C, Type::Extended); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = Id::new(0x09F8031C, Type::Extended); - assert_eq!(can_id.destination_address(), Address::GLOBAL); - - let can_id = Id::new(0x0CAC1C13, Type::Extended); - assert_eq!(can_id.destination_address(), Address(0x1C)); - } - - #[test] - fn test_pgn() { - let can_id = Id::new(0x07FF, Type::Standard); - assert_eq!(can_id.pgn(), Pgn::NULL); - - let can_id = Id::new(0x0CAC1C13, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0AC00)); - - let can_id = Id::new(0x18FF3F13, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0FF3F)); - - let can_id = Id::new(0x18EF1CF5, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EF00)); - - let can_id = Id::new(0x18EEFF1C, Type::Extended); - assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EE00)); - } - - #[test] - fn test_encode() { - let encode_result = Id::try_encode( - Pgn::from_raw(0x00EF00), - Address(0x81), - Address(0xF9), - Priority::Six, - ); - let can_id = encode_result.expect("EF00 Message was not encodable"); - assert_eq!(can_id.pgn(), Pgn::from_raw(0xEF00)); - assert_eq!(can_id.destination_address(), Address(0xF9)); - assert_eq!(can_id.source_address(), Address(0x81)); - assert_eq!(can_id.priority(), Priority::Six); - - let encode_result = Id::try_encode( - Pgn::from_raw(0x00FF40), - Address(0x81), - Address(0xFF), - Priority::Six, - ); - let can_id = encode_result.expect("FF40 Message was not encodable"); - assert_eq!(can_id.pgn(), Pgn::from_raw(0xFF40)); - assert_eq!(can_id.destination_address(), Address(0xFF)); - assert_eq!(can_id.source_address(), Address(0x81)); - assert_eq!(can_id.priority(), Priority::Six); - - let encode_result = Id::try_encode( - Pgn::from_raw(0x00FF40), - Address(0x81), - Address(0x0F), - Priority::Six, - ); - assert!(encode_result.is_err()); +impl TryFrom for Id { + type Error = ParseIdError; - let error_contents: EncodingError = encode_result.unwrap_err(); - assert_eq!(error_contents.priority, Priority::Six); - assert_eq!(error_contents.source_address, Address(0x81)); - assert_eq!(error_contents.destination_address, Address(0x0F)); - assert_eq!(error_contents.parameter_group_number, Pgn::from_raw(0xFF40)); + fn try_from(raw_id: u32) -> Result { + let bit_data = raw_id.view_bits::().to_bitvec(); + let priority_parse = Priority::try_from(bit_data.load::()); + let pgn_parse = Pgn::try_from(bit_data.load::()); } } + +//TODO: tests -> especially for 'bit_data.load::()' From bb2428aa7bafb30148152da5e5f2c2491e720d6b Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sat, 27 Jan 2024 19:41:24 +0100 Subject: [PATCH 07/29] fix mod.rs --- src/j1939/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/j1939/mod.rs b/src/j1939/mod.rs index eb7243a..1d26ae0 100644 --- a/src/j1939/mod.rs +++ b/src/j1939/mod.rs @@ -16,5 +16,6 @@ mod priority; pub use address::Address; pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; pub use frame::{Channel, Frame}; -pub use id::{Id, Priority}; +pub use id::Id; pub use pgn::Pgn; +pub use priority::Priority; From b4cdd4c9c9eb696dd364e7da2097862811a95a62 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 8 Feb 2024 00:14:26 +0100 Subject: [PATCH 08/29] compiling but tests are missing --- examples/forward.rs | 7 +- src/j1939/address.rs | 14 +- src/j1939/frame.rs | 48 +++-- src/j1939/id.rs | 63 +++++-- src/j1939/pgn.rs | 164 ++++++++++++++---- src/j1939/priority.rs | 33 ++-- src/network_management/can_message.rs | 37 ---- .../common_parameter_group_numbers.rs | 68 ++------ src/network_management/mod.rs | 1 - src/network_management/network_manager.rs | 96 +++++----- 10 files changed, 308 insertions(+), 223 deletions(-) delete mode 100644 src/network_management/can_message.rs diff --git a/examples/forward.rs b/examples/forward.rs index 35ad0fa..b40cdd2 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -2,8 +2,9 @@ use std::sync::mpsc::channel; -use ag_iso_stack::j1939::{Driver, DriverReadError, Frame, SocketcanDriver}; +use ag_iso_stack::j1939::{DriverReadError, Frame}; use clap::Parser; +use embedded_can::nb::Can; /// Forward CAN traffic from one interface to another #[derive(Debug, Parser)] @@ -26,9 +27,9 @@ struct Options { pub output_interface: String, } -fn create_driver(iface: &str) -> impl Driver { +fn create_driver(iface: &str) -> impl Can { if let Ok(index) = iface.parse::() { - SocketcanDriver::new_by_index(index) + CanSocket::new_by_index(index) } else { SocketcanDriver::new_by_name(iface) } diff --git a/src/j1939/address.rs b/src/j1939/address.rs index 14af203..5650d44 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -16,20 +16,30 @@ impl Address { Self { 0: raw_address } } + pub fn raw(&self) -> u8 { + self.0 + } + #[inline] pub fn is_global(&self) -> bool { self.is_broadcast() } #[inline] - pub fn is_broadcast(&self) -> bool { + pub fn is_broadcast(self) -> bool { self == Self::BROADCAST } #[inline] - pub fn is_null(&self) -> bool { + pub fn is_null(self) -> bool { self == Self::NULL } } +impl Default for Address { + fn default() -> Self { + Self::NULL + } +} + // TODO: custom Debug impl and helpers diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs index 82e8baa..14112e3 100644 --- a/src/j1939/frame.rs +++ b/src/j1939/frame.rs @@ -1,36 +1,54 @@ -use socketcan::{embedded_can, Id as EmbeddedId}; // Copyright 2023 Raven Industries inc. use crate::j1939::Id; -use embedded_can::Frame as EmbeddedFrame; +use embedded_can::{Frame as EmbeddedFrame, Id as EmbeddedId}; #[derive(Debug, Default)] #[repr(transparent)] pub struct Channel(u8); -#[derive(Debug, Default)] +#[derive(Debug, Clone, Default)] pub struct Frame { - pub timestamp: std::time::Duration, - pub id: Id, - pub data: Vec, + id: Id, + data: Vec, } impl Frame { - pub fn new(id: Id, data: Vec) -> Self { - Self { - timestamp: todo!(), - id, - data, + pub fn new(id: impl Into, data: Vec) -> Option { + let frame_id = match id.into() { + EmbeddedId::Standard(_) => None, + EmbeddedId::Extended(id) => Some(id), + }; + + let parsed_id = Id::try_from(EmbeddedId::Extended(frame_id.unwrap())); + + if frame_id.is_none() || parsed_id.is_err() { + return None; } + + Some(Self { + id: parsed_id.unwrap(), + data, + }) + } + + #[inline] + pub fn id(&self) -> Id { + self.id + } + + #[inline] + pub fn data(self) -> Vec { + self.data } } impl EmbeddedFrame for Frame { fn new(id: impl Into, data: &[u8]) -> Option { - Self::new(id.into(), data.to_vec()) + Frame::new(id, data.to_vec()) } - fn new_remote(id: impl Into, dlc: usize) -> Option { - //J1939 does not support remote frame + fn new_remote(_id: impl Into, _dlc: usize) -> Option { + //J1939 does not support remote frames None } @@ -55,7 +73,7 @@ impl EmbeddedFrame for Frame { } fn id(&self) -> EmbeddedId { - todo!() + EmbeddedId::from(self.id) } fn dlc(&self) -> usize { diff --git a/src/j1939/id.rs b/src/j1939/id.rs index f42fb51..850415f 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -7,14 +7,14 @@ use crate::j1939::priority::Priority; use crate::j1939::{Address, Pgn}; use embedded_can::{ExtendedId, Id as EmbeddedId}; +#[derive(Debug)] pub enum ParseIdError { - priority_parse_error, - pgn_parse_error, - source_address_parse_error, + PriorityParseError, + PgnParseError, + SourceAddressParseError, } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct Id { priority: Priority, pgn: Pgn, @@ -34,10 +34,10 @@ impl Id { #[inline] pub fn raw(&self) -> u32 { let mut raw_id: BitVec = BitVec::new(); - raw_id.extend(self.priority); - raw_id.extend(self.pgn); - raw_id.extend(self.source_address); - raw_id.load::().unwrap() + raw_id.append(&mut (self.priority as u8).view_bits_mut::().to_bitvec()); + raw_id.append(&mut self.pgn.raw()); + raw_id.append(&mut self.source_address.raw().view_bits::().to_bitvec()); + raw_id.load::() } /// Get the priority of the ID @@ -51,11 +51,43 @@ impl Id { pub fn source_address(&self) -> Address { self.source_address } + + /// Get the PGN of the ID + #[inline] + pub fn pgn(&self) -> Pgn { + self.pgn + } } impl From for EmbeddedId { fn from(id: Id) -> Self { - EmbeddedId::Extended(ExtendedId(id.raw())) + EmbeddedId::Extended(ExtendedId::new(id.raw()).unwrap_or(ExtendedId::ZERO)) + } +} + +impl TryFrom for Id { + type Error = ParseIdError; + + fn try_from(value: EmbeddedId) -> Result { + match value { + EmbeddedId::Standard(_) => Err(ParseIdError::PgnParseError), + EmbeddedId::Extended(id) => { + let bit_data = id.as_raw().view_bits::().to_bitvec(); + let priority = Priority::try_from(bit_data.load::()); + let pgn = Pgn::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() { + return Err(ParseIdError::PriorityParseError); + } + + if pgn.is_err() { + return Err(ParseIdError::PgnParseError); + } + + Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) + } + } } } @@ -64,8 +96,15 @@ impl TryFrom for Id { fn try_from(raw_id: u32) -> Result { let bit_data = raw_id.view_bits::().to_bitvec(); - let priority_parse = Priority::try_from(bit_data.load::()); - let pgn_parse = Pgn::try_from(bit_data.load::()); + let priority = Priority::try_from(bit_data.load::()); + let pgn = Pgn::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() || pgn.is_err() { + return Err(ParseIdError::PriorityParseError); + } + + Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) } } diff --git a/src/j1939/pgn.rs b/src/j1939/pgn.rs index aa1ad06..62b847e 100644 --- a/src/j1939/pgn.rs +++ b/src/j1939/pgn.rs @@ -1,19 +1,30 @@ // Copyright 2023 Raven Industries inc. - +use crate::j1939::Address; use bitvec::field::BitField; use bitvec::order::Msb0; use bitvec::vec::BitVec; use bitvec::view::BitView; -use std::io::Read; +#[derive(Debug)] pub enum ParsePgnError { - InvalidPgnLength, + InvalidPgnLength(u32), +} + +impl std::fmt::Display for ParsePgnError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParsePgnError::InvalidPgnLength(value) => write!( + f, + "Parse '{:?}' failed because the permitted PGN value is between 0 and 0x3FFFF!", + value + ), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(transparent)] pub struct Pgn { - reserved: bool, + extended_data_page: bool, data_page: bool, pdu_format: u8, pdu_specific: u8, @@ -21,32 +32,102 @@ pub struct Pgn { impl Pgn { pub const MAX_PGN_VALUE: u32 = 0x3FFFF; + pub const PDU_1: u8 = 0xEF; + pub const PDU_2: u8 = 0xFF; - pub fn new(reserved: bool, data_page: bool, pdu_format: u8, pdu_specific: u8) -> Self { + pub fn new( + extended_data_page: bool, + data_page: bool, + pdu_format: u8, + pdu_specific: u8, + ) -> Self { Self { - reserved, + extended_data_page, data_page, pdu_format, pdu_specific, } } + + pub fn raw(self) -> BitVec { + let mut raw_pgn: BitVec = BitVec::new(); + raw_pgn.append( + &mut (self.extended_data_page as u8) + .view_bits_mut::() + .to_bitvec(), + ); + raw_pgn.append(&mut (self.data_page as u8).view_bits_mut::().to_bitvec()); + raw_pgn.extend(self.pdu_format.view_bits::()); + raw_pgn.extend(self.pdu_specific.view_bits::()); + raw_pgn + } + + pub fn is_destination_specific(&self) -> bool { + self.pdu_format <= Self::PDU_1 + } + + pub fn get_destination_address(&self) -> Option
{ + if self.is_destination_specific() { + Some(Address::new(self.pdu_specific)) + } else { + None + } + } + + pub fn set_destination_address(&mut self, address: Address) { + if self.is_destination_specific() { + self.pdu_specific = address.raw(); + } + } + + pub fn is_group_extension(&self) -> bool { + self.pdu_format <= Self::PDU_2 && self.pdu_specific > Self::PDU_1 + } + + pub fn get_group_extension(&self) -> Option { + if self.is_group_extension() { + Some(self.pdu_specific) + } else { + None + } + } + + #[inline] + pub fn extended_data_page(&self) -> bool { + self.extended_data_page + } + + #[inline] + pub fn data_page(&self) -> bool { + self.data_page + } + + #[inline] + pub fn pdu_format(&self) -> u8 { + self.pdu_format + } + + #[inline] + pub fn pdu_specific(&self) -> u8 { + self.pdu_specific + } } impl TryFrom for Pgn { type Error = ParsePgnError; fn try_from(raw_pgn: u32) -> Result { - if (raw_pgn > Self::MAX_PGN_VALUE) { + if raw_pgn > Self::MAX_PGN_VALUE { // raw value is too large to fit in PGN with 18 bits - Err(ParsePgnError::InvalidPgnLength) + return Err(ParsePgnError::InvalidPgnLength(raw_pgn)); } let mut bit_data = raw_pgn.view_bits::().to_bitvec(); Ok(Self { - reserved: bit_data.pop().unwrap(), + extended_data_page: bit_data.pop().unwrap(), data_page: bit_data.pop().unwrap(), - pdu_format: bit_data.load::().unwrap(), - pdu_specific: bit_data.load::().unwrap(), + pdu_format: bit_data.load::(), + pdu_specific: bit_data.load::(), }) } } @@ -56,8 +137,8 @@ mod tests { use crate::j1939::Pgn; #[test] - fn test_from_raw() { - let pgn_parsed = Pgn::from_raw(0x00F04); + fn test_try_from() { + let pgn_parsed = Pgn::try_from(0x00F04); assert_eq!(pgn_parsed.is_ok(), true); let pgn = Pgn::new(false, false, 0xF0, 0x04); @@ -66,46 +147,57 @@ mod tests { #[test] fn test_bitmath() { - let pgn = Pgn::from_raw(0x30000); - assert_eq!(pgn.data_page(), 0x01); - assert_eq!(pgn.extended_data_page(), 0x01); + let pgn = Pgn::try_from(0x30000).unwrap(); + assert_eq!(pgn.data_page, true); + assert_eq!(pgn.extended_data_page, true); - let pgn = Pgn::from_raw(0x0FF00); - assert_eq!(pgn.pdu_format(), 0xFF); - assert_eq!(pgn.pdu_specific(), 0x00); + let pgn = Pgn::try_from(0x0FF00).unwrap(); + assert_eq!(pgn.pdu_format, 0xFF); + assert_eq!(pgn.pdu_specific, 0x00); - let pgn = Pgn::from_raw(0x000FF); - assert_eq!(pgn.pdu_format(), 0x00); - assert_eq!(pgn.pdu_specific(), 0xFF); + let pgn = Pgn::try_from(0x000FF).unwrap(); + assert_eq!(pgn.pdu_format, 0x00); + assert_eq!(pgn.pdu_specific, 0xFF); } #[test] fn test_p2p() { - let pgn = Pgn::from_raw(0x0EE00); + let pgn = Pgn::try_from(0x0EE00).unwrap(); assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::from_raw(0x0EF00); + let pgn = Pgn::try_from(0x0EF00).unwrap(); assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::from_raw(0x0F000); + let pgn = Pgn::try_from(0x0F000).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x0FEFF); + let pgn = Pgn::try_from(0x0FEFF).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x0FF00); + let pgn = Pgn::try_from(0x0FF00).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x0FFFF); + let pgn = Pgn::try_from(0x0FFFF).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x10000); + let pgn = Pgn::try_from(0x10000).unwrap(); assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::from_raw(0x1EE00); + let pgn = Pgn::try_from(0x1EE00).unwrap(); assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::from_raw(0x1EF00); + let pgn = Pgn::try_from(0x1EF00).unwrap(); assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::from_raw(0x1F000); + let pgn = Pgn::try_from(0x1F000).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x1FEFF); + let pgn = Pgn::try_from(0x1FEFF).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x1FF00); + let pgn = Pgn::try_from(0x1FF00).unwrap(); assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::from_raw(0x1FFFF); + let pgn = Pgn::try_from(0x1FFFF).unwrap(); assert_eq!(pgn.is_destination_specific(), false); } } + +impl Default for Pgn { + fn default() -> Self { + Self { + extended_data_page: false, + data_page: false, + pdu_format: 0, + pdu_specific: 0, + } + } +} diff --git a/src/j1939/priority.rs b/src/j1939/priority.rs index c6859c0..cff6f0b 100644 --- a/src/j1939/priority.rs +++ b/src/j1939/priority.rs @@ -1,7 +1,5 @@ -use crate::j1939::DriverOpenError; - #[derive(Debug)] -struct ParsePriorityError(u8); +pub struct ParsePriorityError(u8); impl std::fmt::Display for ParsePriorityError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -14,18 +12,19 @@ impl std::fmt::Display for ParsePriorityError { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] pub enum Priority { /// You may also use [`Priority::HIGHEST`] as an alias - Zero = 0x0, - One = 0x1, - Two = 0x2, - Three = 0x3, - Four = 0x4, - Five = 0x5, + Zero = 0, + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, /// You may also use [`Priority::DEFAULT`] as an alias - Six = 0x6, + Six = 6, /// You may also use [`Priority::LOWEST`] as an alias - Seven = 0x7, + Seven = 7, } impl Priority { @@ -34,21 +33,27 @@ impl Priority { pub const LOWEST: Priority = Priority::Seven; #[inline] - pub fn is_highest(&self) -> bool { + pub fn is_highest(self) -> bool { self == Self::HIGHEST } #[inline] - pub fn is_default(&self) -> bool { + pub fn is_default(self) -> bool { self == Self::DEFAULT } #[inline] - pub fn is_lowest(&self) -> bool { + pub fn is_lowest(self) -> bool { self == Self::LOWEST } } +impl Default for Priority { + fn default() -> Self { + Priority::DEFAULT + } +} + impl TryFrom for Priority { type Error = ParsePriorityError; diff --git a/src/network_management/can_message.rs b/src/network_management/can_message.rs deleted file mode 100644 index dcafb8b..0000000 --- a/src/network_management/can_message.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 Raven Industries inc. -use super::name::NAME; -use crate::j1939::Id; - -pub struct CANMessage { - data: Vec, - identifier: Id, - source_name: NAME, - destination_name: NAME, -} - -impl CANMessage { - pub(super) fn new(data: Vec, identifier: Id) -> CANMessage { - CANMessage { - data, - identifier, - source_name: NAME::default(), - destination_name: NAME::default(), - } - } - - pub fn get_data(&self) -> &[u8] { - self.data.as_slice() - } - - pub fn get_identifier(&self) -> Id { - self.identifier - } - - pub fn get_source_name(&self) -> NAME { - self.source_name - } - - pub fn get_destination_name(&self) -> NAME { - self.destination_name - } -} diff --git a/src/network_management/common_parameter_group_numbers.rs b/src/network_management/common_parameter_group_numbers.rs index c83a71b..700138d 100644 --- a/src/network_management/common_parameter_group_numbers.rs +++ b/src/network_management/common_parameter_group_numbers.rs @@ -1,62 +1,20 @@ // Copyright 2023 Raven Industries inc. +use crate::j1939::Pgn; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum CommonParameterGroupNumbers { - TractorImplementManagementServerToTimClient = 0x002300, - TractorImplementManagementClientToTimServer = 0x002400, - AuthenticationClientToAuthenticationServer = 0x006F00, - AuthenticationServerToAuthenticationClient = 0x007000, - NameManagement = 0x009300, - GuidanceMachineStatus = 0x00AC00, - GuidanceSystemCommand = 0x00AD00, - ExtendedTransportProtocolData = 0x00C700, - ExtendedTransportProtocolCommand = 0x00C800, - RequestForRepetitionRate = 0x00CC00, - BinaryDataTransfer = 0x00D700, - MemoryAccessResponse = 0x00D800, - MemoryAccessRequest = 0x00D900, - StopStartBroadcast = 0x00DF00, - VirtualTerminalToNode = 0x00E600, - NodeToVirtualTerminal = 0x00E700, - Acknowledgement = 0x00E800, ParameterGroupNumberRequest = 0x00EA00, - TransportProtocolData = 0x00EB00, - TransportProtocolCommand = 0x00EC00, AddressClaim = 0x00EE00, - ProprietaryA = 0x00EF00, - ElectronicEngineController2 = 0x00F003, - ElectronicEngineController1 = 0x00F004, - HeartbeatMessage = 0x00F0E4, - ProductIdentification = 0x00FC8D, - ControlFunctionFunctionalities = 0x00FC8E, - DiagnosticProtocol = 0x00FD32, - IsobusComplianceCertificationMessage = 0x00FD42, - EcuIdentificationInformation = 0x00FDC5, - WorkingSetMaster = 0x00FE0D, - ResponseForRepetitionRate = 0x00FE0E, - MaintainPower = 0x00FE47, - WheelBasedSpeedAndDistance = 0x00FE48, - GroundBasedSpeedAndDistance = 0x00FE49, - ActiveDiagnosticTroubleCodes = 0x00FECA, - PreviouslyActiveDiagnosticTroubleCodes = 0x00FECB, - DiagnosticDataClearResetOfPreviouslyActiveDtcs = 0x00FECC, - FreezeFrameParameters = 0x00FECD, - DiagnosticDataClearResetForActiveDtcs = 0x00FED3, - CommandedAddress = 0x00FED8, - SoftwareIdentification = 0x00FEDA, - TimeDate = 0x00FEE6, - EngineTemperature1 = 0x00FEEE, - CruiseControlVehicleSpeed1 = 0x00FEF1, - IntakeExhaustConditions1 = 0x00FEF6, - NmeaAttitude = 0x01F119, - NmeaCogSogRapidUpdate = 0x01F802, - NmeaPositionDeltaHighPrecisionRapidUpdate = 0x01F803, - NmeaAltitudeDeltaHighPrecisionRapidUpdate = 0x01F804, - NmeaGnssPositionData = 0x01F805, - NmeaTimeDate = 0x01F809, - NmeaGnssDops = 0x01FA03, - NmeaGnssSatsInView = 0x01FA04, - NmeaGnssPseudoRangeNoiseStatistics = 0x01FA06, - NmeaGnssPseudoRangeErrorStatistics = 0x01FA0B, - AllowAll = 0xFFFFFF, +} + +impl CommonParameterGroupNumbers { + pub fn get_pgn(&self) -> Pgn { + match self { + CommonParameterGroupNumbers::AddressClaim => Pgn::new(false, false, 0xEE, 0x00), + CommonParameterGroupNumbers::ParameterGroupNumberRequest => { + Pgn::new(false, false, 0xEA, 0x00) + } + } + } } diff --git a/src/network_management/mod.rs b/src/network_management/mod.rs index 99d372c..a8adf36 100644 --- a/src/network_management/mod.rs +++ b/src/network_management/mod.rs @@ -1,5 +1,4 @@ // Copyright 2023 Raven Industries inc. -pub mod can_message; pub mod common_parameter_group_numbers; pub mod control_function; pub mod name; diff --git a/src/network_management/network_manager.rs b/src/network_management/network_manager.rs index f8e713e..f384bfe 100644 --- a/src/network_management/network_manager.rs +++ b/src/network_management/network_manager.rs @@ -2,8 +2,7 @@ use std::time::Instant; use super::control_function::{AddressClaimingState, ControlFunction}; -use crate::j1939::{Address, Id, Pgn, Priority}; -use crate::network_management::can_message::CANMessage; +use crate::j1939::{Address, Frame, Id, Pgn, Priority}; use crate::network_management::common_parameter_group_numbers::CommonParameterGroupNumbers; use crate::network_management::name::NAME; use std::cell::RefCell; @@ -30,9 +29,9 @@ pub struct NetworkManager { control_function_table: [Option>>; 253], inactive_control_functions: Vec>>, address_claim_state_machines: Vec>>, - high_priority_can_message_tx_queue: VecDeque, - normal_priority_can_message_tx_queue: VecDeque, - receive_message_queue: VecDeque, + high_priority_can_message_tx_queue: VecDeque, + normal_priority_can_message_tx_queue: VecDeque, + receive_message_queue: VecDeque, } impl NetworkManager { @@ -51,14 +50,14 @@ impl NetworkManager { &self, address: Address, ) -> &Option>> { - &self.control_function_table[address.0 as usize] + &self.control_function_table[address.raw() as usize] } pub fn get_control_function_address_by_name(&self, name: NAME) -> Address { for (i, cf) in self.control_function_table.iter().enumerate() { if let Some(extant_cf) = cf { if extant_cf.borrow().get_name() == name { - return Address(i as u8); + return Address::new(i as u8); } } } @@ -75,11 +74,11 @@ impl NetworkManager { pub(super) fn get_next_free_arbitrary_address(&self) -> Address { for address in 129..247 { - let is_device_at_address = self.get_control_function_by_address(Address(address)); + let is_device_at_address = self.get_control_function_by_address(Address::new(address)); let is_valid_device: bool = is_device_at_address.is_some(); if !is_valid_device { - return Address(address); + return Address::new(address); } else { let device_at_our_address = is_device_at_address.as_ref().unwrap().borrow(); @@ -91,40 +90,36 @@ impl NetworkManager { }; if >::into(NAME::default()) == preferred_address_name { - return Address(address); + return Address::new(address); } } } Address::NULL } - pub(super) fn construct_address_claim(source_address: Address, name: NAME) -> CANMessage { + pub(super) fn construct_address_claim(source_address: Address, name: NAME) -> Frame { let address_claim = >::into(name).to_le_bytes().to_vec(); - let request_id = Id::try_encode( - Pgn::from_raw(CommonParameterGroupNumbers::AddressClaim as u32), + let request_id = Id::new( + Priority::DEFAULT, + CommonParameterGroupNumbers::AddressClaim.get_pgn(), source_address, - Address::BROADCAST, - Priority::Default, ); - CANMessage::new(address_claim, request_id.unwrap()) + Frame::new(request_id, address_claim).unwrap() } - pub(super) fn construct_request_for_address_claim() -> CANMessage { + pub(super) fn construct_request_for_address_claim() -> Frame { let pgn_to_request: u32 = CommonParameterGroupNumbers::AddressClaim as u32; let request = pgn_to_request.to_le_bytes().to_vec(); - let request_id = Id::try_encode( - Pgn::from_raw(CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32), - Address::NULL, - Address::BROADCAST, - Priority::Three, - ); - CANMessage::new(request, request_id.unwrap()) + let mut pgn = CommonParameterGroupNumbers::ParameterGroupNumberRequest.get_pgn(); + pgn.set_destination_address(Address::BROADCAST); + let request_id = Id::new(Priority::Three, pgn, Address::NULL); + Frame::new(request_id, request).unwrap() } pub(super) fn enqueue_can_message( &mut self, - message: CANMessage, + message: Frame, queue_priority: MessageQueuePriority, ) { // Todo, max queue depth? @@ -152,21 +147,21 @@ impl NetworkManager { if data.len() <= 8 { let source = source.borrow(); let destination = destination.borrow(); - let message_id = Id::try_encode( - parameter_group_number, - self.get_control_function_address_by_name(source.get_name()), + let mut pgn = parameter_group_number; + pgn.set_destination_address( self.get_control_function_address_by_name(destination.get_name()), + ); + let message_id = Id::new( priority, - ) - .unwrap_or(Id::default()); - - if message_id.raw() != Id::default().raw() { - self.enqueue_can_message( - CANMessage::new(data.to_vec(), message_id), - MessageQueuePriority::Normal, - ); - return CANTransmitState::Success; - } + pgn, + self.get_control_function_address_by_name(source.get_name()), + ); + + self.enqueue_can_message( + Frame::new(message_id, data.to_vec()).unwrap(), + MessageQueuePriority::Normal, + ); + return CANTransmitState::Success; } } CANTransmitState::Fail @@ -250,19 +245,24 @@ impl NetworkManager { let current_message = self.receive_message_queue.front().unwrap(); // Process address claims and requests to claim - if NAME::default() == current_message.get_destination_name() { + if NAME::default() == NAME::default() + /*TODO!: Replaced following code line by upper NAME::default(). There needs to be another abstraction of ISO 11783 specific frames which includes the NAME*/ + /*current_message.get_destination_name()*/ + { // Broadcast Message - if current_message.get_identifier().pgn() - == Pgn::from_raw(CommonParameterGroupNumbers::AddressClaim as u32) + if current_message.id().pgn().pdu_format() + == CommonParameterGroupNumbers::AddressClaim + .get_pgn() + .pdu_format() { // Todo - } else if current_message.get_identifier().pgn() - == Pgn::from_raw( - CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32, - ) - && current_message.get_data().len() >= 3 + } else if current_message.id().pgn().pdu_format() + == CommonParameterGroupNumbers::ParameterGroupNumberRequest + .get_pgn() + .pdu_format() + && current_message.clone().data().len() >= 3 { - let message_data = current_message.get_data(); + let message_data = current_message.clone().data(); let requested_pgn: u32 = (message_data[0] as u32) | ((message_data[1] as u32) << 8) | ((message_data[2] as u32) << 16); @@ -350,7 +350,7 @@ mod tests { let new_cf = ControlFunction::new_internal_control_function( test_name, - Address(0x81), + Address::new(0x81), true, &mut network, ); From 0299a219914e69e8fb1f656760dfb086a832fcde Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 8 Feb 2024 00:18:43 +0100 Subject: [PATCH 09/29] fix clippy --- src/j1939/address.rs | 2 +- src/j1939/id.rs | 14 +++++++------- src/j1939/pgn.rs | 15 ++------------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index 5650d44..e3e3550 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -13,7 +13,7 @@ impl Address { pub const NULL: Address = Address(0xFE); pub fn new(raw_address: u8) -> Self { - Self { 0: raw_address } + Self(raw_address) } pub fn raw(&self) -> u8 { diff --git a/src/j1939/id.rs b/src/j1939/id.rs index 850415f..e9c8787 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -9,9 +9,9 @@ use embedded_can::{ExtendedId, Id as EmbeddedId}; #[derive(Debug)] pub enum ParseIdError { - PriorityParseError, - PgnParseError, - SourceAddressParseError, + Priority, + Pgn, + SourceAddress, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] @@ -70,7 +70,7 @@ impl TryFrom for Id { fn try_from(value: EmbeddedId) -> Result { match value { - EmbeddedId::Standard(_) => Err(ParseIdError::PgnParseError), + EmbeddedId::Standard(_) => Err(ParseIdError::Pgn), EmbeddedId::Extended(id) => { let bit_data = id.as_raw().view_bits::().to_bitvec(); let priority = Priority::try_from(bit_data.load::()); @@ -78,11 +78,11 @@ impl TryFrom for Id { let source_address = Address::new(bit_data.load::()); if priority.is_err() { - return Err(ParseIdError::PriorityParseError); + return Err(ParseIdError::Priority); } if pgn.is_err() { - return Err(ParseIdError::PgnParseError); + return Err(ParseIdError::Pgn); } Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) @@ -101,7 +101,7 @@ impl TryFrom for Id { let source_address = Address::new(bit_data.load::()); if priority.is_err() || pgn.is_err() { - return Err(ParseIdError::PriorityParseError); + return Err(ParseIdError::Priority); } Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) diff --git a/src/j1939/pgn.rs b/src/j1939/pgn.rs index 62b847e..d71ca8a 100644 --- a/src/j1939/pgn.rs +++ b/src/j1939/pgn.rs @@ -22,7 +22,7 @@ impl std::fmt::Display for ParsePgnError { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct Pgn { extended_data_page: bool, data_page: bool, @@ -81,7 +81,7 @@ impl Pgn { } pub fn is_group_extension(&self) -> bool { - self.pdu_format <= Self::PDU_2 && self.pdu_specific > Self::PDU_1 + self.pdu_specific > Self::PDU_1 } pub fn get_group_extension(&self) -> Option { @@ -190,14 +190,3 @@ mod tests { assert_eq!(pgn.is_destination_specific(), false); } } - -impl Default for Pgn { - fn default() -> Self { - Self { - extended_data_page: false, - data_page: false, - pdu_format: 0, - pdu_specific: 0, - } - } -} From 448abf5c05a10c7a4744c69e215a4cba9abcf02f Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 8 Feb 2024 00:24:23 +0100 Subject: [PATCH 10/29] clippy allow enum variant names --- src/object_pool/object.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/object_pool/object.rs b/src/object_pool/object.rs index 39bdb66..0dd6fa3 100644 --- a/src/object_pool/object.rs +++ b/src/object_pool/object.rs @@ -12,6 +12,7 @@ use crate::object_pool::object_id::ObjectId; use crate::object_pool::{Colour, ObjectType}; #[derive(Debug)] +#[allow(clippy::enum_variant_names)] pub enum Object { WorkingSet(WorkingSet), DataMask(DataMask), From a31ff257735cac9c697f4409d1030d6ca2ef6742 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sun, 11 Feb 2024 13:36:23 +0100 Subject: [PATCH 11/29] simple example without library usage yet --- examples/forward.rs | 51 +++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index b40cdd2..d8d7654 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -1,10 +1,8 @@ // Copyright 2023 Raven Industries inc. -use std::sync::mpsc::channel; - -use ag_iso_stack::j1939::{DriverReadError, Frame}; use clap::Parser; use embedded_can::nb::Can; +use socketcan::{CanSocket, Socket}; /// Forward CAN traffic from one interface to another #[derive(Debug, Parser)] @@ -17,27 +15,21 @@ struct Options { /// The interface to read traffic from /// /// Can be either a string interface name, or an integer interface index - #[clap(short, long, default_value_t = String::from("can0"))] + #[clap(short, long, default_value_t = String::from("vcan0"))] pub input_interface: String, /// The interface to write traffic to /// /// Can be either a string interface name, or an integer interface index - #[clap(short, long, default_value_t = String::from("can1"))] + #[clap(short, long, default_value_t = String::from("vcan1"))] pub output_interface: String, } -fn create_driver(iface: &str) -> impl Can { - if let Ok(index) = iface.parse::() { - CanSocket::new_by_index(index) - } else { - SocketcanDriver::new_by_name(iface) - } -} - fn main() { let opts = Options::parse(); + + let subscriber = tracing_subscriber::fmt() // ... add configuration .finish(); @@ -51,33 +43,20 @@ fn main() { opts.output_interface ); - let mut input = create_driver(&opts.input_interface); - let mut output = create_driver(&opts.output_interface); + let mut input = CanSocket::open(&opts.input_interface).expect("The given input interface cannot be opened!"); + let mut output = CanSocket::open(&opts.output_interface).expect("The given output interface cannot be opened!"); - input.open().unwrap(); - output.open().unwrap(); - - let (tx, rx) = channel(); - ctrlc::set_handler(move || tx.send(true).unwrap()).unwrap(); + input.set_nonblocking(true).expect("Could not set input bus to non-blocking!"); + output.set_nonblocking(true).expect("Could not set output bus to non-blocking!"); loop { - if rx.try_recv().is_ok() { - break; + match input.receive() { + Ok(frame) => { + output.transmit(&frame).expect("Could not forward received message!"); + }, + Err(_err) => continue, } + } - let mut frame = Frame::default(); - match input.read_nonblocking(&mut frame) { - Ok(_) => { - tracing::info!("Read frame: {frame:?}"); - tracing::info!("Attempting to write frame"); - match output.write_nonblocking(&frame) { - Ok(_) => tracing::info!("Wrote frame: {frame:?}"), - Err(e) => tracing::info!("Failed to write frame: {e:?}"), - } - } - Err(DriverReadError::NoFrameReady) => {} - Err(e) => tracing::error!("Failed to read frame: {e:?}"), - } - } } From 2133ea4c2dcbfe71bdc71fef5a156193928f46bd Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Sun, 11 Feb 2024 13:43:52 +0100 Subject: [PATCH 12/29] fmt --- examples/forward.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index d8d7654..e6b8245 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -28,8 +28,6 @@ struct Options { fn main() { let opts = Options::parse(); - - let subscriber = tracing_subscriber::fmt() // ... add configuration .finish(); @@ -43,20 +41,26 @@ fn main() { opts.output_interface ); - let mut input = CanSocket::open(&opts.input_interface).expect("The given input interface cannot be opened!"); - let mut output = CanSocket::open(&opts.output_interface).expect("The given output interface cannot be opened!"); + let mut input = CanSocket::open(&opts.input_interface) + .expect("The given input interface cannot be opened!"); + let mut output = CanSocket::open(&opts.output_interface) + .expect("The given output interface cannot be opened!"); - input.set_nonblocking(true).expect("Could not set input bus to non-blocking!"); - output.set_nonblocking(true).expect("Could not set output bus to non-blocking!"); + input + .set_nonblocking(true) + .expect("Could not set input bus to non-blocking!"); + output + .set_nonblocking(true) + .expect("Could not set output bus to non-blocking!"); loop { match input.receive() { Ok(frame) => { - output.transmit(&frame).expect("Could not forward received message!"); - }, + output + .transmit(&frame) + .expect("Could not forward received message!"); + } Err(_err) => continue, } } - - } From e9e8d1e10705e3d418cd9d91679a4df187fab5c5 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 15 Feb 2024 21:02:10 +0100 Subject: [PATCH 13/29] add standard id support & testing of pgn --- src/j1939/extended_id.rs | 112 ++++++++++++++++++++++ src/j1939/frame.rs | 20 ++-- src/j1939/id.rs | 110 +++------------------ src/j1939/mod.rs | 7 +- src/j1939/page.rs | 41 ++++++++ src/j1939/pgn.rs | 54 ++++++++--- src/j1939/standard_id.rs | 61 ++++++++++++ src/network_management/network_manager.rs | 8 +- 8 files changed, 285 insertions(+), 128 deletions(-) create mode 100644 src/j1939/extended_id.rs create mode 100644 src/j1939/page.rs create mode 100644 src/j1939/standard_id.rs diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs new file mode 100644 index 0000000..0951ff5 --- /dev/null +++ b/src/j1939/extended_id.rs @@ -0,0 +1,112 @@ +// Copyright 2023 Raven Industries inc. +use crate::j1939::id::{Id, ParseIdError}; +use crate::j1939::priority::Priority; +use crate::j1939::standard_id::StandardId; +use crate::j1939::{Address, Pgn}; +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::vec::BitVec; +use bitvec::view::BitView; +use embedded_can::{ExtendedId as EmbeddedExtendedId, Id as EmbeddedId}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub struct ExtendedId { + standard_id: StandardId, + pgn: Pgn, +} + +impl ExtendedId { + pub fn new(standard_id: StandardId, pgn: Pgn) -> Self { + Self { standard_id, pgn } + } + + /// Get the raw value of the CAN ID + #[inline] + pub fn raw(&self) -> u32 { + let mut raw_id: BitVec = BitVec::new(); + raw_id.append( + &mut (self.standard_id.priority() as u8) + .view_bits_mut::() + .to_bitvec(), + ); + raw_id.append(&mut self.pgn.raw()); + raw_id.append( + &mut self + .standard_id + .source_address() + .raw() + .view_bits::() + .to_bitvec(), + ); + raw_id.load::() + } + + /// Get the PGN of the ID + #[inline] + pub fn pgn(&self) -> Pgn { + self.pgn + } +} + +impl From for EmbeddedId { + fn from(id: ExtendedId) -> Self { + EmbeddedId::Extended(EmbeddedExtendedId::new(id.raw()).unwrap_or(EmbeddedExtendedId::ZERO)) + } +} + +impl TryFrom for ExtendedId { + type Error = ParseIdError; + + fn try_from(value: EmbeddedId) -> Result { + match value { + EmbeddedId::Standard(_) => Err(ParseIdError::StandardId), + EmbeddedId::Extended(id) => { + let bit_data = id.as_raw().view_bits::().to_bitvec(); + let priority = Priority::try_from(bit_data.load::()); + let pgn = Pgn::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() { + return Err(ParseIdError::Priority); + } + + if pgn.is_err() { + return Err(ParseIdError::Pgn); + } + + Ok(ExtendedId::new( + StandardId::new(priority.unwrap(), source_address), + pgn.unwrap(), + )) + } + } + } +} + +impl TryFrom for ExtendedId { + type Error = ParseIdError; + + fn try_from(raw_id: u32) -> Result { + let bit_data = raw_id.view_bits::().to_bitvec(); + let priority = Priority::try_from(bit_data.load::()); + let pgn = Pgn::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() || pgn.is_err() { + return Err(ParseIdError::Priority); + } + + Ok(ExtendedId::new( + StandardId::new(priority.unwrap(), source_address), + pgn.unwrap(), + )) + } +} + +impl From for Id { + fn from(id: ExtendedId) -> Self { + Id::Extended(id) + } +} + +//TODO: tests -> especially for 'bit_data.load::()' diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs index 14112e3..80ad49c 100644 --- a/src/j1939/frame.rs +++ b/src/j1939/frame.rs @@ -1,5 +1,7 @@ // Copyright 2023 Raven Industries inc. -use crate::j1939::Id; +use crate::j1939::id::Id; +use crate::j1939::standard_id::StandardId; +use crate::j1939::ExtendedId; use embedded_can::{Frame as EmbeddedFrame, Id as EmbeddedId}; #[derive(Debug, Default)] @@ -14,14 +16,16 @@ pub struct Frame { impl Frame { pub fn new(id: impl Into, data: Vec) -> Option { - let frame_id = match id.into() { - EmbeddedId::Standard(_) => None, - EmbeddedId::Extended(id) => Some(id), + let frame_id: Id = match id.into() { + EmbeddedId::Standard(id) => StandardId::try_from(EmbeddedId::Standard(id)) + .expect("Invalid standard ID") + .into(), + EmbeddedId::Extended(id) => ExtendedId::try_from(EmbeddedId::Extended(id)) + .expect("Invalid extended ID") + .into(), }; - let parsed_id = Id::try_from(EmbeddedId::Extended(frame_id.unwrap())); - - if frame_id.is_none() || parsed_id.is_err() { + if frame_id { return None; } @@ -32,7 +36,7 @@ impl Frame { } #[inline] - pub fn id(&self) -> Id { + pub fn id(&self) -> ExtendedId { self.id } diff --git a/src/j1939/id.rs b/src/j1939/id.rs index e9c8787..1ca4853 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -1,111 +1,23 @@ -use bitvec::field::BitField; -use bitvec::order::Msb0; -use bitvec::vec::BitVec; -use bitvec::view::BitView; -// Copyright 2023 Raven Industries inc. -use crate::j1939::priority::Priority; -use crate::j1939::{Address, Pgn}; -use embedded_can::{ExtendedId, Id as EmbeddedId}; +use crate::j1939::standard_id::StandardId; +use crate::j1939::ExtendedId; #[derive(Debug)] pub enum ParseIdError { Priority, Pgn, SourceAddress, + StandardId, + ExtendedId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct Id { - priority: Priority, - pgn: Pgn, - source_address: Address, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Id { + Standard(StandardId), + Extended(ExtendedId), } -impl Id { - pub fn new(priority: Priority, pgn: Pgn, source_address: Address) -> Self { - Self { - priority, - pgn, - source_address, - } - } - - /// Get the raw value of the CAN ID - #[inline] - pub fn raw(&self) -> u32 { - let mut raw_id: BitVec = BitVec::new(); - raw_id.append(&mut (self.priority as u8).view_bits_mut::().to_bitvec()); - raw_id.append(&mut self.pgn.raw()); - raw_id.append(&mut self.source_address.raw().view_bits::().to_bitvec()); - raw_id.load::() - } - - /// Get the priority of the ID - #[inline] - pub fn priority(&self) -> Priority { - self.priority - } - - /// Get the source address of the ID - #[inline] - pub fn source_address(&self) -> Address { - self.source_address - } - - /// Get the PGN of the ID - #[inline] - pub fn pgn(&self) -> Pgn { - self.pgn - } -} - -impl From for EmbeddedId { - fn from(id: Id) -> Self { - EmbeddedId::Extended(ExtendedId::new(id.raw()).unwrap_or(ExtendedId::ZERO)) +impl Default for Id { + fn default() -> Self { + Id::Extended(ExtendedId::default()) } } - -impl TryFrom for Id { - type Error = ParseIdError; - - fn try_from(value: EmbeddedId) -> Result { - match value { - EmbeddedId::Standard(_) => Err(ParseIdError::Pgn), - EmbeddedId::Extended(id) => { - let bit_data = id.as_raw().view_bits::().to_bitvec(); - let priority = Priority::try_from(bit_data.load::()); - let pgn = Pgn::try_from(bit_data.load::()); - let source_address = Address::new(bit_data.load::()); - - if priority.is_err() { - return Err(ParseIdError::Priority); - } - - if pgn.is_err() { - return Err(ParseIdError::Pgn); - } - - Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) - } - } - } -} - -impl TryFrom for Id { - type Error = ParseIdError; - - fn try_from(raw_id: u32) -> Result { - let bit_data = raw_id.view_bits::().to_bitvec(); - let priority = Priority::try_from(bit_data.load::()); - let pgn = Pgn::try_from(bit_data.load::()); - let source_address = Address::new(bit_data.load::()); - - if priority.is_err() || pgn.is_err() { - return Err(ParseIdError::Priority); - } - - Ok(Id::new(priority.unwrap(), pgn.unwrap(), source_address)) - } -} - -//TODO: tests -> especially for 'bit_data.load::()' diff --git a/src/j1939/mod.rs b/src/j1939/mod.rs index 1d26ae0..971bb5e 100644 --- a/src/j1939/mod.rs +++ b/src/j1939/mod.rs @@ -8,14 +8,17 @@ mod address; mod driver; +mod extended_id; mod frame; -mod id; +mod page; mod pgn; mod priority; +mod standard_id; +mod id; pub use address::Address; pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; +pub use extended_id::ExtendedId; pub use frame::{Channel, Frame}; -pub use id::Id; pub use pgn::Pgn; pub use priority::Priority; diff --git a/src/j1939/page.rs b/src/j1939/page.rs new file mode 100644 index 0000000..fd26e9a --- /dev/null +++ b/src/j1939/page.rs @@ -0,0 +1,41 @@ +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::prelude::BitVec; + +#[derive(Debug)] +enum ParsePageError { + InvalidPage(u8), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum Page { + J1939Page0 = 0, + J1939Page1 = 1, + J1939PageReserved = 2, + ISO11992_4 = 3, +} + +impl TryFrom for Page { + type Error = ParsePageError; + + fn try_from(raw_page: u8) -> Result { + match raw_page { + 0x0 => Ok(Page::J1939Page0), + 0x1 => Ok(Page::J1939Page1), + 0x2 => Ok(Page::J1939PageReserved), + 0x3 => Ok(Page::ISO11992_4), + _ => Err(ParsePageError::InvalidPage(raw_page)), + } + } +} + +impl From<[bool; 2]> for Page { + fn from(value: [bool; 2]) -> Self { + let mut page_vec: BitVec = BitVec::new(); + page_vec.resize(8, false); + page_vec[0] = value[0]; + page_vec[1] = value[1]; + Page::try_from(page_vec.load::()).unwrap() + } +} diff --git a/src/j1939/pgn.rs b/src/j1939/pgn.rs index d71ca8a..a033e52 100644 --- a/src/j1939/pgn.rs +++ b/src/j1939/pgn.rs @@ -1,7 +1,8 @@ // Copyright 2023 Raven Industries inc. +use crate::j1939::page::Page; use crate::j1939::Address; use bitvec::field::BitField; -use bitvec::order::Msb0; +use bitvec::order::{Lsb0, Msb0}; use bitvec::vec::BitVec; use bitvec::view::BitView; @@ -31,9 +32,16 @@ pub struct Pgn { } impl Pgn { + pub const PGN_LENGTH: usize = 18; pub const MAX_PGN_VALUE: u32 = 0x3FFFF; pub const PDU_1: u8 = 0xEF; pub const PDU_2: u8 = 0xFF; + pub const EDP_START: u8 = 0; + pub const DP_START: u8 = 1; + pub const PDU_FORMAT_START: usize = 2; + pub const PDU_FORMAT_END: usize = 10; + pub const PDU_SPECIFIC_START: usize = 10; + pub const PDU_SPECIFIC_END: usize = 18; pub fn new( extended_data_page: bool, @@ -56,9 +64,9 @@ impl Pgn { .view_bits_mut::() .to_bitvec(), ); - raw_pgn.append(&mut (self.data_page as u8).view_bits_mut::().to_bitvec()); - raw_pgn.extend(self.pdu_format.view_bits::()); - raw_pgn.extend(self.pdu_specific.view_bits::()); + raw_pgn.append(&mut (self.data_page as u8).view_bits_mut::().to_bitvec()); + raw_pgn.extend(self.pdu_format.view_bits::()); + raw_pgn.extend(self.pdu_specific.view_bits::()); raw_pgn } @@ -102,6 +110,10 @@ impl Pgn { self.data_page } + pub fn page(&self) -> Page { + Page::from([self.extended_data_page, self.data_page]) + } + #[inline] pub fn pdu_format(&self) -> u8 { self.pdu_format @@ -122,13 +134,26 @@ impl TryFrom for Pgn { return Err(ParsePgnError::InvalidPgnLength(raw_pgn)); } - let mut bit_data = raw_pgn.view_bits::().to_bitvec(); - Ok(Self { - extended_data_page: bit_data.pop().unwrap(), - data_page: bit_data.pop().unwrap(), - pdu_format: bit_data.load::(), - pdu_specific: bit_data.load::(), - }) + let raw_pgn_be_bytes = raw_pgn.to_le_bytes(); + let mut bit_data = raw_pgn_be_bytes.view_bits::().to_bitvec(); + bit_data.truncate(Pgn::PGN_LENGTH); + bit_data.reverse(); + + let mut pdu_format = bit_data[Self::PDU_FORMAT_START..Self::PDU_FORMAT_END].to_bitvec(); + pdu_format.reverse(); + + let mut pdu_specific = + bit_data[Self::PDU_SPECIFIC_START..Self::PDU_SPECIFIC_END].to_bitvec(); + pdu_specific.reverse(); + + let pgn = Self { + extended_data_page: bit_data[Self::EDP_START as usize], + data_page: bit_data[Self::DP_START as usize], + pdu_format: pdu_format.load(), + pdu_specific: pdu_specific.load(), + }; + + Ok(pgn) } } @@ -138,11 +163,10 @@ mod tests { #[test] fn test_try_from() { - let pgn_parsed = Pgn::try_from(0x00F04); - assert_eq!(pgn_parsed.is_ok(), true); + let pgn_parsed = Pgn::try_from(0x2E6BA).expect("Failed to parse PGN"); - let pgn = Pgn::new(false, false, 0xF0, 0x04); - assert_eq!(pgn, pgn_parsed.unwrap()); + let pgn = Pgn::new(true, false, 0xE6, 0xBA); + assert_eq!(pgn, pgn_parsed); } #[test] diff --git a/src/j1939/standard_id.rs b/src/j1939/standard_id.rs new file mode 100644 index 0000000..56382a3 --- /dev/null +++ b/src/j1939/standard_id.rs @@ -0,0 +1,61 @@ +use crate::j1939::id::Id; +use crate::j1939::id::ParseIdError; +use crate::j1939::{Address, Priority}; +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::view::BitView; +use embedded_can::Id as EmbeddedId; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub struct StandardId { + priority: Priority, + source_address: Address, +} + +impl StandardId { + pub fn new(priority: Priority, source_address: Address) -> Self { + Self { + priority, + source_address, + } + } + + /// Get the priority of the ID + #[inline] + pub fn priority(&self) -> Priority { + self.priority + } + + /// Get the source address of the ID + #[inline] + pub fn source_address(&self) -> Address { + self.source_address + } +} + +impl TryFrom for StandardId { + type Error = ParseIdError; + + fn try_from(id: EmbeddedId) -> Result { + match id { + EmbeddedId::Standard(id) => { + let bit_data = id.as_raw().view_bits::().to_bitvec(); + let priority = Priority::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() { + return Err(ParseIdError::Priority); + } + + Ok(Self::new(priority.unwrap(), source_address)) + } + EmbeddedId::Extended(_) => Err(ParseIdError::ExtendedId), + } + } +} + +impl From for Id { + fn from(id: StandardId) -> Self { + Id::Standard(id) + } +} diff --git a/src/network_management/network_manager.rs b/src/network_management/network_manager.rs index f384bfe..e4f8eb5 100644 --- a/src/network_management/network_manager.rs +++ b/src/network_management/network_manager.rs @@ -2,7 +2,7 @@ use std::time::Instant; use super::control_function::{AddressClaimingState, ControlFunction}; -use crate::j1939::{Address, Frame, Id, Pgn, Priority}; +use crate::j1939::{Address, ExtendedId, Frame, Pgn, Priority}; use crate::network_management::common_parameter_group_numbers::CommonParameterGroupNumbers; use crate::network_management::name::NAME; use std::cell::RefCell; @@ -100,7 +100,7 @@ impl NetworkManager { pub(super) fn construct_address_claim(source_address: Address, name: NAME) -> Frame { let address_claim = >::into(name).to_le_bytes().to_vec(); - let request_id = Id::new( + let request_id = ExtendedId::new( Priority::DEFAULT, CommonParameterGroupNumbers::AddressClaim.get_pgn(), source_address, @@ -113,7 +113,7 @@ impl NetworkManager { let request = pgn_to_request.to_le_bytes().to_vec(); let mut pgn = CommonParameterGroupNumbers::ParameterGroupNumberRequest.get_pgn(); pgn.set_destination_address(Address::BROADCAST); - let request_id = Id::new(Priority::Three, pgn, Address::NULL); + let request_id = ExtendedId::new(Priority::Three, pgn, Address::NULL); Frame::new(request_id, request).unwrap() } @@ -151,7 +151,7 @@ impl NetworkManager { pgn.set_destination_address( self.get_control_function_address_by_name(destination.get_name()), ); - let message_id = Id::new( + let message_id = ExtendedId::new( priority, pgn, self.get_control_function_address_by_name(source.get_name()), From ce79721bf07227ffa5e4139a4e14781e6f4c7da3 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Wed, 21 Feb 2024 22:57:30 +0100 Subject: [PATCH 14/29] adds a ton of testing, documentation & fixes --- examples/forward.rs | 18 +- src/j1939/address.rs | 37 +- src/j1939/byte_field.rs | 54 +++ src/j1939/extended_id.rs | 72 ++-- src/j1939/frame.rs | 40 +-- src/j1939/id.rs | 57 ++- src/j1939/mod.rs | 21 +- src/j1939/page.rs | 68 +++- src/j1939/pdu_format.rs | 56 +++ src/j1939/pdu_specific.rs | 18 + src/j1939/pgn.rs | 334 +++++++++++++----- src/j1939/priority.rs | 127 ++++++- src/j1939/standard_id.rs | 90 +++-- .../common_parameter_group_numbers.rs | 8 +- src/network_management/network_manager.rs | 102 +++--- 15 files changed, 843 insertions(+), 259 deletions(-) create mode 100644 src/j1939/byte_field.rs create mode 100644 src/j1939/pdu_format.rs create mode 100644 src/j1939/pdu_specific.rs diff --git a/examples/forward.rs b/examples/forward.rs index e6b8245..90ce32f 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -1,7 +1,8 @@ // Copyright 2023 Raven Industries inc. use clap::Parser; -use embedded_can::nb::Can; + +#[cfg(target_os = "unix")] use socketcan::{CanSocket, Socket}; /// Forward CAN traffic from one interface to another @@ -25,6 +26,16 @@ struct Options { pub output_interface: String, } +fn open_socket_can_interface() -> (CanSocket, CanSocket) { + let mut input = CanSocket::open(&opts.input_interface) + .expect("The given input interface cannot be opened!"); + + let mut output = CanSocket::open(&opts.output_interface) + .expect("The given output interface cannot be opened!"); + + (input, output) +} + fn main() { let opts = Options::parse(); @@ -41,10 +52,7 @@ fn main() { opts.output_interface ); - let mut input = CanSocket::open(&opts.input_interface) - .expect("The given input interface cannot be opened!"); - let mut output = CanSocket::open(&opts.output_interface) - .expect("The given output interface cannot be opened!"); + let (input, output) = open_can_interface(); input .set_nonblocking(true) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index e3e3550..9294ac6 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -1,10 +1,15 @@ // Copyright 2023 Raven Industries inc. +use crate::j1939::byte_field::ByteField; + +/// J1939 address (8-bits) used to identify ECUs on the network #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct Address(u8); impl Address { + /// The number of bits in the address + pub const BIT_LENGTH: u8 = 8; /// Address representing broadcasts for destination specific PGNs pub const GLOBAL: Address = Self::BROADCAST; /// Alias for the global address @@ -12,24 +17,24 @@ impl Address { /// The null address is used by ECUs without an address such as during address claiming pub const NULL: Address = Address(0xFE); + /// Create a new address pub fn new(raw_address: u8) -> Self { Self(raw_address) } - pub fn raw(&self) -> u8 { - self.0 - } - + /// Returns if the address is the [Address::GLOBAL] (same as [Address::is_broadcast]) #[inline] pub fn is_global(&self) -> bool { self.is_broadcast() } + /// Returns if the address is the [Address::BROADCAST] (same as [Address::is_global]) #[inline] pub fn is_broadcast(self) -> bool { self == Self::BROADCAST } + /// Returns if the address is the [Address::NULL] #[inline] pub fn is_null(self) -> bool { self == Self::NULL @@ -42,4 +47,26 @@ impl Default for Address { } } -// TODO: custom Debug impl and helpers +impl ByteField for Address { + fn raw(self) -> u8 { + self.0 + } +} + +mod tests { + use crate::j1939::Address; + + #[test] + fn test_address() { + let address = Address::new(0b1010_1010); + assert!(!address.is_global()); + assert!(!address.is_broadcast()); + assert!(!address.is_null()); + + let address = Address::BROADCAST; + assert!(address.is_global()); + + let address = Address::NULL; + assert!(address.is_null()); + } +} diff --git a/src/j1939/byte_field.rs b/src/j1939/byte_field.rs new file mode 100644 index 0000000..a6217ae --- /dev/null +++ b/src/j1939/byte_field.rs @@ -0,0 +1,54 @@ +use bitvec::order::Msb0; +use bitvec::view::BitView; + +/// A byte field +pub trait ByteField: Sized { + /// Get the raw value of the field + fn raw(self) -> u8; + + /// Get the raw bits of the field + fn raw_bits(self) -> [bool; 8] { + let raw = self.raw(); + let field_bits = raw.view_bits::(); + [ + field_bits[0], + field_bits[1], + field_bits[2], + field_bits[3], + field_bits[4], + field_bits[5], + field_bits[6], + field_bits[7], + ] + } +} + +#[cfg(test)] +mod tests { + use crate::j1939::byte_field::ByteField; + use crate::j1939::PduFormat; + + #[test] + fn test_byte_field() { + let byte_field = PduFormat::new(0b1010_1010); + assert_eq!(byte_field.raw(), 0b1010_1010); + assert_eq!( + byte_field.raw_bits(), + [true, false, true, false, true, false, true, false] + ); + + let byte_field = PduFormat::new(0xFF); + assert_eq!(byte_field.raw(), 0xFF); + assert_eq!( + byte_field.raw_bits(), + [true, true, true, true, true, true, true, true] + ); + + let byte_field = PduFormat::new(0x00); + assert_eq!(byte_field.raw(), 0x00); + assert_eq!( + byte_field.raw_bits(), + [false, false, false, false, false, false, false, false] + ); + } +} diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs index 0951ff5..c5b5b8a 100644 --- a/src/j1939/extended_id.rs +++ b/src/j1939/extended_id.rs @@ -1,4 +1,5 @@ // Copyright 2023 Raven Industries inc. +use crate::j1939::byte_field::ByteField; use crate::j1939::id::{Id, ParseIdError}; use crate::j1939::priority::Priority; use crate::j1939::standard_id::StandardId; @@ -9,6 +10,7 @@ use bitvec::vec::BitVec; use bitvec::view::BitView; use embedded_can::{ExtendedId as EmbeddedExtendedId, Id as EmbeddedId}; +/// Extended 29-bit J1939 identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct ExtendedId { standard_id: StandardId, @@ -16,32 +18,23 @@ pub struct ExtendedId { } impl ExtendedId { + pub const BIT_LENGTH: u8 = 29; + pub fn new(standard_id: StandardId, pgn: Pgn) -> Self { Self { standard_id, pgn } } - /// Get the raw value of the CAN ID - #[inline] + /// Get the raw value of the extended identifier pub fn raw(&self) -> u32 { let mut raw_id: BitVec = BitVec::new(); - raw_id.append( - &mut (self.standard_id.priority() as u8) - .view_bits_mut::() - .to_bitvec(), - ); - raw_id.append(&mut self.pgn.raw()); - raw_id.append( - &mut self - .standard_id - .source_address() - .raw() - .view_bits::() - .to_bitvec(), - ); + raw_id.extend(self.standard_id.priority().raw_bits()); + raw_id.extend(self.pgn.raw_bits()); + raw_id.extend(self.standard_id.source_address().raw_bits()); + raw_id.reverse(); raw_id.load::() } - /// Get the PGN of the ID + /// Get the PGN of the identifier #[inline] pub fn pgn(&self) -> Pgn { self.pgn @@ -50,36 +43,7 @@ impl ExtendedId { impl From for EmbeddedId { fn from(id: ExtendedId) -> Self { - EmbeddedId::Extended(EmbeddedExtendedId::new(id.raw()).unwrap_or(EmbeddedExtendedId::ZERO)) - } -} - -impl TryFrom for ExtendedId { - type Error = ParseIdError; - - fn try_from(value: EmbeddedId) -> Result { - match value { - EmbeddedId::Standard(_) => Err(ParseIdError::StandardId), - EmbeddedId::Extended(id) => { - let bit_data = id.as_raw().view_bits::().to_bitvec(); - let priority = Priority::try_from(bit_data.load::()); - let pgn = Pgn::try_from(bit_data.load::()); - let source_address = Address::new(bit_data.load::()); - - if priority.is_err() { - return Err(ParseIdError::Priority); - } - - if pgn.is_err() { - return Err(ParseIdError::Pgn); - } - - Ok(ExtendedId::new( - StandardId::new(priority.unwrap(), source_address), - pgn.unwrap(), - )) - } - } + EmbeddedId::Extended(EmbeddedExtendedId::new(id.raw()).unwrap()) } } @@ -109,4 +73,16 @@ impl From for Id { } } -//TODO: tests -> especially for 'bit_data.load::()' +#[cfg(test)] +mod tests { + use crate::j1939::{Address, ExtendedId, PduFormat, PduSpecific, Pgn, Priority, StandardId}; + + #[test] + fn test_raw() { + let id = ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x25)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x0F)), + ); + assert_eq!(id.raw(), 0x18A0F25); + } +} diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs index 80ad49c..07195d5 100644 --- a/src/j1939/frame.rs +++ b/src/j1939/frame.rs @@ -1,7 +1,5 @@ // Copyright 2023 Raven Industries inc. use crate::j1939::id::Id; -use crate::j1939::standard_id::StandardId; -use crate::j1939::ExtendedId; use embedded_can::{Frame as EmbeddedFrame, Id as EmbeddedId}; #[derive(Debug, Default)] @@ -16,27 +14,14 @@ pub struct Frame { impl Frame { pub fn new(id: impl Into, data: Vec) -> Option { - let frame_id: Id = match id.into() { - EmbeddedId::Standard(id) => StandardId::try_from(EmbeddedId::Standard(id)) - .expect("Invalid standard ID") - .into(), - EmbeddedId::Extended(id) => ExtendedId::try_from(EmbeddedId::Extended(id)) - .expect("Invalid extended ID") - .into(), - }; - - if frame_id { - return None; - } - Some(Self { - id: parsed_id.unwrap(), + id: Id::try_from(id.into()).expect("Invalid J1939 ID"), data, }) } #[inline] - pub fn id(&self) -> ExtendedId { + pub fn id(&self) -> Id { self.id } @@ -51,23 +36,30 @@ impl EmbeddedFrame for Frame { Frame::new(id, data.to_vec()) } + /// create a new remote frame + ///
+ /// J1939 does not support remote frames (see J1939-21 5.4) so this is always [None] + ///
fn new_remote(_id: impl Into, _dlc: usize) -> Option { - //J1939 does not support remote frames None } fn is_extended(&self) -> bool { - // J1939 only supports extended frames - true + match self.id { + Id::Standard(_) => false, + Id::Extended(_) => true, + } } fn is_standard(&self) -> bool { - // J1939 only supports extended frames - false + match self.id { + Id::Standard(_) => true, + Id::Extended(_) => false, + } } fn is_remote_frame(&self) -> bool { - // J1939 does not support remote frames + // J1939 does not support remote frames (see J1939-21 5.4) false } @@ -77,7 +69,7 @@ impl EmbeddedFrame for Frame { } fn id(&self) -> EmbeddedId { - EmbeddedId::from(self.id) + self.id.into() } fn dlc(&self) -> usize { diff --git a/src/j1939/id.rs b/src/j1939/id.rs index 1ca4853..57257f2 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -1,5 +1,9 @@ use crate::j1939::standard_id::StandardId; -use crate::j1939::ExtendedId; +use crate::j1939::{Address, ExtendedId, Pgn, Priority}; +use bitvec::field::BitField; +use bitvec::order::Msb0; +use bitvec::view::BitView; +use embedded_can::Id as EmbeddedId; #[derive(Debug)] pub enum ParseIdError { @@ -10,6 +14,7 @@ pub enum ParseIdError { ExtendedId, } +/// Identifier for a J1939 message (standard or extended) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Id { Standard(StandardId), @@ -21,3 +26,53 @@ impl Default for Id { Id::Extended(ExtendedId::default()) } } + +impl From for EmbeddedId { + fn from(id: Id) -> Self { + match id { + Id::Standard(id) => id.into(), + Id::Extended(id) => id.into(), + } + } +} + +impl TryFrom for Id { + type Error = ParseIdError; + + fn try_from(value: EmbeddedId) -> Result { + match value { + EmbeddedId::Standard(id) => { + let bit_data = id.as_raw().view_bits::().to_bitvec(); + let priority_bits = + bit_data[StandardId::PRIORITY_START..StandardId::PRIORITY_END].to_bitvec(); + let source_address_bits = bit_data + [StandardId::SOURCE_ADDRESS_START..StandardId::SOURCE_ADDRESS_END] + .to_bitvec(); + let priority = + Priority::from([priority_bits[0], priority_bits[1], priority_bits[2]]); + let source_address = Address::new(source_address_bits.load()); + + Ok(Id::Standard(StandardId::new(priority, source_address))) + } + EmbeddedId::Extended(id) => { + let bit_data = id.as_raw().view_bits::().to_bitvec(); + let priority = Priority::try_from(bit_data.load::()); + let pgn = Pgn::try_from(bit_data.load::()); + let source_address = Address::new(bit_data.load::()); + + if priority.is_err() { + return Err(ParseIdError::Priority); + } + + if pgn.is_err() { + return Err(ParseIdError::Pgn); + } + + Ok(Id::Extended(ExtendedId::new( + StandardId::new(priority.unwrap(), source_address), + pgn.unwrap(), + ))) + } + } + } +} diff --git a/src/j1939/mod.rs b/src/j1939/mod.rs index 971bb5e..60b8f67 100644 --- a/src/j1939/mod.rs +++ b/src/j1939/mod.rs @@ -1,24 +1,35 @@ // Copyright 2023 Raven Industries inc. - -//! CAN Driver layer +//! J1939 layer //! //! This module defines: -//! 1. An abstract `Driver` trait for different CAN drivers to implement -//! 2. `Frame`, `Pgn`, `Address`, et al types +//! - The J1939 protocol data unit (PDU) format +//! - The J1939 protocol data unit (PDU) specific +//! - The J1939 parameter group number (PGN) +//! - The J1939 priority +//! - The J1939 standard and extended identifier mod address; +mod byte_field; mod driver; mod extended_id; mod frame; +mod id; mod page; +mod pdu_format; +mod pdu_specific; mod pgn; mod priority; mod standard_id; -mod id; pub use address::Address; +pub use byte_field::ByteField; pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; pub use extended_id::ExtendedId; pub use frame::{Channel, Frame}; +pub use id::{Id, ParseIdError}; +pub use page::{Page, ParsePageError}; +pub use pdu_format::PduFormat; +pub use pdu_specific::PduSpecific; pub use pgn::Pgn; pub use priority::Priority; +pub use standard_id::StandardId; diff --git a/src/j1939/page.rs b/src/j1939/page.rs index fd26e9a..1a369fa 100644 --- a/src/j1939/page.rs +++ b/src/j1939/page.rs @@ -2,40 +2,84 @@ use bitvec::field::BitField; use bitvec::order::Msb0; use bitvec::prelude::BitVec; -#[derive(Debug)] -enum ParsePageError { +#[derive(Debug, PartialEq)] +pub enum ParsePageError { InvalidPage(u8), } +/// Page definition (EDP & DP combination) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum Page { + /// J1939 page 0 J1939Page0 = 0, + /// J1939 page 1 J1939Page1 = 1, + /// J1939 page reserved J1939PageReserved = 2, - ISO11992_4 = 3, + /// ISO 11992-4 defined + ///
+ /// In this case the rest of the identifier is not defined by J1939! + /// Please refer to the ISO 11992-4 standard for more information. + ///
+ ISO11992_4Defined = 3, } -impl TryFrom for Page { - type Error = ParsePageError; +impl Page { + pub const BIT_LENGTH: usize = 2; - fn try_from(raw_page: u8) -> Result { + pub fn try_from_raw(raw_page: u8) -> Result { match raw_page { 0x0 => Ok(Page::J1939Page0), 0x1 => Ok(Page::J1939Page1), 0x2 => Ok(Page::J1939PageReserved), - 0x3 => Ok(Page::ISO11992_4), + 0x3 => Ok(Page::ISO11992_4Defined), _ => Err(ParsePageError::InvalidPage(raw_page)), } } + + pub fn from_raw_bits(raw_page: [bool; 2]) -> Self { + let mut page_vec: BitVec = BitVec::new(); + page_vec.extend(raw_page.iter()); + Page::try_from_raw(page_vec.load::()).unwrap() + } +} + +impl TryFrom for Page { + type Error = ParsePageError; + + fn try_from(raw_page: u8) -> Result { + Page::try_from_raw(raw_page) + } } impl From<[bool; 2]> for Page { fn from(value: [bool; 2]) -> Self { - let mut page_vec: BitVec = BitVec::new(); - page_vec.resize(8, false); - page_vec[0] = value[0]; - page_vec[1] = value[1]; - Page::try_from(page_vec.load::()).unwrap() + Page::from_raw_bits(value) + } +} + +#[cfg(test)] +mod tests { + use crate::j1939::{Page, ParsePageError}; + + #[test] + fn test_try_from_u8_for_page() { + assert_eq!(Page::try_from_raw(0x0).unwrap(), Page::J1939Page0); + assert_eq!(Page::try_from_raw(0x1).unwrap(), Page::J1939Page1); + assert_eq!(Page::try_from_raw(0x2).unwrap(), Page::J1939PageReserved); + assert_eq!(Page::try_from_raw(0x3).unwrap(), Page::ISO11992_4Defined); + assert_eq!( + Page::try_from_raw(0x4).unwrap_err(), + ParsePageError::InvalidPage(4) + ); + } + + #[test] + fn test_from_bool_array_for_page() { + assert_eq!(Page::from_raw_bits([false, false]), Page::J1939Page0); + assert_eq!(Page::from_raw_bits([false, true]), Page::J1939Page1); + assert_eq!(Page::from_raw_bits([true, false]), Page::J1939PageReserved); + assert_eq!(Page::from_raw_bits([true, true]), Page::ISO11992_4Defined); } } diff --git a/src/j1939/pdu_format.rs b/src/j1939/pdu_format.rs new file mode 100644 index 0000000..278b6cd --- /dev/null +++ b/src/j1939/pdu_format.rs @@ -0,0 +1,56 @@ +use crate::j1939::byte_field::ByteField; + +/// PDU format field defined in the PGN +#[derive(Debug, Default, PartialEq, Clone, Copy, Eq, Hash)] +#[repr(transparent)] +pub struct PduFormat(u8); + +impl PduFormat { + pub const PDU_1_START: u8 = 0x00; + pub const PDU_1_END: u8 = 0xEF; + pub const PDU_2_START: u8 = 0xF0; + pub const PDU_2_END: u8 = 0xFF; + + pub fn new(raw_pdu_format: u8) -> Self { + Self(raw_pdu_format) + } + + #[inline] + pub fn is_destination_specific(&self) -> bool { + self.0 <= Self::PDU_1_END + } + + #[inline] + pub fn is_group_extension(&self) -> bool { + self.0 >= Self::PDU_2_START + } +} + +impl ByteField for PduFormat { + fn raw(self) -> u8 { + self.0 + } +} + +#[cfg(test)] +mod tests { + use crate::j1939::pdu_format::PduFormat; + + #[test] + fn test_pdu_format() { + let pdu_format = PduFormat::new(0x00); + assert_eq!(pdu_format.is_destination_specific(), true); + assert_eq!(pdu_format.is_group_extension(), false); + + let pdu_format = PduFormat::new(0xEF); + assert_eq!(pdu_format.is_destination_specific(), true); + assert_eq!(pdu_format.is_group_extension(), false); + + let pdu_format = PduFormat::new(0xF0); + assert_eq!(pdu_format.is_destination_specific(), false); + assert_eq!(pdu_format.is_group_extension(), true); + + let pdu_format = PduFormat::new(0xFF); + assert_eq!(pdu_format.is_destination_specific(), false); + } +} diff --git a/src/j1939/pdu_specific.rs b/src/j1939/pdu_specific.rs new file mode 100644 index 0000000..1c7e0ed --- /dev/null +++ b/src/j1939/pdu_specific.rs @@ -0,0 +1,18 @@ +use crate::j1939::byte_field::ByteField; + +/// PDU specific field defined in the PGN +#[derive(Debug, Default, PartialEq, Clone, Copy, Eq, Hash)] +#[repr(transparent)] +pub struct PduSpecific(u8); + +impl PduSpecific { + pub fn new(raw_pdu_specific: u8) -> Self { + Self(raw_pdu_specific) + } +} + +impl ByteField for PduSpecific { + fn raw(self) -> u8 { + self.0 + } +} diff --git a/src/j1939/pgn.rs b/src/j1939/pgn.rs index a033e52..2bc647f 100644 --- a/src/j1939/pgn.rs +++ b/src/j1939/pgn.rs @@ -1,8 +1,11 @@ // Copyright 2023 Raven Industries inc. +use crate::j1939::byte_field::ByteField; use crate::j1939::page::Page; +use crate::j1939::pdu_format::PduFormat; +use crate::j1939::pdu_specific::PduSpecific; use crate::j1939::Address; use bitvec::field::BitField; -use bitvec::order::{Lsb0, Msb0}; +use bitvec::order::Lsb0; use bitvec::vec::BitVec; use bitvec::view::BitView; @@ -23,31 +26,49 @@ impl std::fmt::Display for ParsePgnError { } } +/// Parameter Group Number (PGN) #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct Pgn { extended_data_page: bool, data_page: bool, - pdu_format: u8, - pdu_specific: u8, + pdu_format: PduFormat, + pdu_specific: PduSpecific, } impl Pgn { - pub const PGN_LENGTH: usize = 18; - pub const MAX_PGN_VALUE: u32 = 0x3FFFF; - pub const PDU_1: u8 = 0xEF; - pub const PDU_2: u8 = 0xFF; - pub const EDP_START: u8 = 0; - pub const DP_START: u8 = 1; - pub const PDU_FORMAT_START: usize = 2; - pub const PDU_FORMAT_END: usize = 10; - pub const PDU_SPECIFIC_START: usize = 10; + /// The number of bits used to represent the PGN + pub const BIT_LENGTH: usize = 18; + /// The maximum value of the PGN + pub const MAX_VALUE: u32 = 0x3FFFF; + const EDP_START: u8 = 0; + const DP_START: u8 = 1; + const PDU_FORMAT_START: usize = 2; + const PDU_FORMAT_END: usize = 10; + const PDU_SPECIFIC_START: usize = 10; pub const PDU_SPECIFIC_END: usize = 18; + /// PDU 1 proprietary A parameter group + /// format: destination specific (not global) + /// data length: 0 - 1785 bytes (multi-packet support) + /// default priority: [`Priority::DEFAULT`](`crate::j1939::priority::Priority::DEFAULT) + pub const PDU_1_FORMAT_PROPRIETARY_A: u32 = 0xEF00; + /// PDU 1 proprietary A2 parameter group + /// format: destination specific (not global) + /// data length: 0 - 1785 bytes (multi-packet support) + /// default priority: [`DEFAULT`](`crate::j1939::priority::Priority::DEFAULT) + pub const PDU_1_FORMAT_PROPRIETARY_A2: u32 = 0x1EF00; + /// PDU 2 proprietary B parameter group + /// format: group extension + /// data length: 0 - 1785 bytes (multi-packet support) + /// default priority: [`Priority::DEFAULT`](`crate::j1939::priority::Priority::DEFAULT) + pub const PDU_2_FORMAT_PROPRIETARY_B: (std::ops::Range, std::ops::Range) = + (0x00FF00..0x00FFFF, 0x01FF00..0x01FFFF); + /// Create a new [Pgn] instance pub fn new( extended_data_page: bool, data_page: bool, - pdu_format: u8, - pdu_specific: u8, + pdu_format: PduFormat, + pdu_specific: PduSpecific, ) -> Self { Self { extended_data_page, @@ -57,70 +78,110 @@ impl Pgn { } } - pub fn raw(self) -> BitVec { + /// Get the raw bits of the [Pgn] + pub fn raw_bits(self) -> [bool; 18] { let mut raw_pgn: BitVec = BitVec::new(); - raw_pgn.append( - &mut (self.extended_data_page as u8) - .view_bits_mut::() - .to_bitvec(), - ); - raw_pgn.append(&mut (self.data_page as u8).view_bits_mut::().to_bitvec()); - raw_pgn.extend(self.pdu_format.view_bits::()); - raw_pgn.extend(self.pdu_specific.view_bits::()); - raw_pgn + raw_pgn.push(self.extended_data_page); + raw_pgn.push(self.data_page); + raw_pgn.extend(self.pdu_format.raw_bits()); + raw_pgn.extend(self.pdu_specific.raw_bits()); + [ + raw_pgn[0], + raw_pgn[1], + raw_pgn[2], + raw_pgn[3], + raw_pgn[4], + raw_pgn[5], + raw_pgn[6], + raw_pgn[7], + raw_pgn[8], + raw_pgn[9], + raw_pgn[10], + raw_pgn[11], + raw_pgn[12], + raw_pgn[13], + raw_pgn[14], + raw_pgn[15], + raw_pgn[16], + raw_pgn[17], + ] } - pub fn is_destination_specific(&self) -> bool { - self.pdu_format <= Self::PDU_1 + /// Get the raw value of the [Pgn] as a 32-bit integer + pub fn raw(self) -> u32 { + let mut raw_pgn: BitVec = BitVec::new(); + raw_pgn.extend(self.raw_bits()); + raw_pgn.reverse(); + raw_pgn.load() } + /// Get the destination address of the [Pgn] if it is destination specific pub fn get_destination_address(&self) -> Option
{ - if self.is_destination_specific() { - Some(Address::new(self.pdu_specific)) + if self.pdu_format().is_destination_specific() { + Some(Address::new(self.pdu_specific.raw())) } else { None } } + /// Set the destination [Address] of the [Pgn] if it is destination specific pub fn set_destination_address(&mut self, address: Address) { - if self.is_destination_specific() { - self.pdu_specific = address.raw(); + if self.pdu_format.is_destination_specific() { + self.pdu_specific = PduSpecific::new(address.raw()); } } - pub fn is_group_extension(&self) -> bool { - self.pdu_specific > Self::PDU_1 - } - + /// Get the group extension of the [Pgn] if it is a group extension pub fn get_group_extension(&self) -> Option { - if self.is_group_extension() { - Some(self.pdu_specific) + if self.pdu_format().is_group_extension() { + Some(self.pdu_specific.raw()) } else { None } } + /// Set the group extension of the [Pgn] if it is a group extension + pub fn set_group_extension(&mut self, group_extension: u8) { + if self.pdu_format.is_group_extension() { + self.pdu_specific = PduSpecific::new(group_extension); + } + } + + /// Returns if the [Pgn] is proprietary + pub fn is_proprietary(&self) -> bool { + self.raw() == Self::PDU_1_FORMAT_PROPRIETARY_A + || Self::PDU_2_FORMAT_PROPRIETARY_B.0.contains(&self.raw()) + || Self::PDU_2_FORMAT_PROPRIETARY_B.1.contains(&self.raw()) + || self.raw() == Self::PDU_1_FORMAT_PROPRIETARY_A2 + } + + /// Get the extended data page of the [Pgn] #[inline] pub fn extended_data_page(&self) -> bool { self.extended_data_page } + /// Get the data page of the [Pgn] #[inline] pub fn data_page(&self) -> bool { self.data_page } + /// Get the [Page] of the [Pgn] resulting from the extended data page and data page + #[inline] pub fn page(&self) -> Page { Page::from([self.extended_data_page, self.data_page]) } + /// Get the [PduFormat] of the [Pgn] #[inline] - pub fn pdu_format(&self) -> u8 { + pub fn pdu_format(&self) -> PduFormat { self.pdu_format } + /// Get the [PduSpecific] of the [Pgn] #[inline] - pub fn pdu_specific(&self) -> u8 { + pub fn pdu_specific(&self) -> PduSpecific { self.pdu_specific } } @@ -129,14 +190,14 @@ impl TryFrom for Pgn { type Error = ParsePgnError; fn try_from(raw_pgn: u32) -> Result { - if raw_pgn > Self::MAX_PGN_VALUE { + if raw_pgn > Self::MAX_VALUE { // raw value is too large to fit in PGN with 18 bits return Err(ParsePgnError::InvalidPgnLength(raw_pgn)); } let raw_pgn_be_bytes = raw_pgn.to_le_bytes(); let mut bit_data = raw_pgn_be_bytes.view_bits::().to_bitvec(); - bit_data.truncate(Pgn::PGN_LENGTH); + bit_data.truncate(Pgn::BIT_LENGTH); bit_data.reverse(); let mut pdu_format = bit_data[Self::PDU_FORMAT_START..Self::PDU_FORMAT_END].to_bitvec(); @@ -149,8 +210,8 @@ impl TryFrom for Pgn { let pgn = Self { extended_data_page: bit_data[Self::EDP_START as usize], data_page: bit_data[Self::DP_START as usize], - pdu_format: pdu_format.load(), - pdu_specific: pdu_specific.load(), + pdu_format: PduFormat::new(pdu_format.load()), + pdu_specific: PduSpecific::new(pdu_specific.load()), }; Ok(pgn) @@ -159,58 +220,163 @@ impl TryFrom for Pgn { #[cfg(test)] mod tests { - use crate::j1939::Pgn; + use crate::j1939::pdu_format::PduFormat; + use crate::j1939::pdu_specific::PduSpecific; + use crate::j1939::{Address, Page, Pgn}; #[test] - fn test_try_from() { - let pgn_parsed = Pgn::try_from(0x2E6BA).expect("Failed to parse PGN"); + fn test_raw_bits() { + let pgn = Pgn::new(true, false, PduFormat::new(0xE6), PduSpecific::new(0xBA)); + let raw_bits = pgn.raw_bits(); + assert_eq!( + raw_bits, + [ + true, false, true, true, true, false, false, true, true, false, true, false, true, + true, true, false, true, false + ] + ); - let pgn = Pgn::new(true, false, 0xE6, 0xBA); - assert_eq!(pgn, pgn_parsed); + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + let raw_bits = pgn.raw_bits(); + assert_eq!( + raw_bits, + [ + false, true, false, false, false, false, false, false, false, false, true, true, + true, true, true, true, true, true + ] + ); + + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + let raw_bits = pgn.raw_bits(); + assert_eq!( + raw_bits, + [ + false, true, false, false, false, true, false, false, true, false, false, false, + true, true, false, true, false, false + ] + ); + } + + #[test] + fn test_raw() { + let pgn = Pgn::new(true, false, PduFormat::new(0xE6), PduSpecific::new(0xBA)); + assert_eq!(pgn.raw(), 0x2E6BA); + + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert_eq!(pgn.raw(), 0x100FF); + + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn.raw(), 0x11234); + } + + #[test] + fn test_get_destination_address() { + let pgn = Pgn::new(true, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + assert_eq!(pgn.get_destination_address(), None); + + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert_eq!(pgn.get_destination_address(), Some(Address::BROADCAST)); + + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn.get_destination_address(), Some(Address::new(0x34))); + } + + #[test] + fn test_set_destination_address() { + let mut pgn = Pgn::new(true, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + pgn.set_destination_address(Address::new(0x34)); + assert_eq!(pgn.get_destination_address(), None); + + let mut pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + pgn.set_destination_address(Address::new(0x34)); + assert_eq!(pgn.get_destination_address(), Some(Address::new(0x34))); + + let mut pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + pgn.set_destination_address(Address::new(0x56)); + assert_eq!(pgn.get_destination_address(), Some(Address::new(0x56))); } #[test] - fn test_bitmath() { - let pgn = Pgn::try_from(0x30000).unwrap(); - assert_eq!(pgn.data_page, true); - assert_eq!(pgn.extended_data_page, true); + fn test_get_group_extension() { + let pgn = Pgn::new(true, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + assert_eq!(pgn.get_group_extension(), Some(0xBA)); - let pgn = Pgn::try_from(0x0FF00).unwrap(); - assert_eq!(pgn.pdu_format, 0xFF); - assert_eq!(pgn.pdu_specific, 0x00); + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert_eq!(pgn.get_group_extension(), None); - let pgn = Pgn::try_from(0x000FF).unwrap(); - assert_eq!(pgn.pdu_format, 0x00); - assert_eq!(pgn.pdu_specific, 0xFF); + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn.get_group_extension(), None); } #[test] - fn test_p2p() { - let pgn = Pgn::try_from(0x0EE00).unwrap(); - assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::try_from(0x0EF00).unwrap(); - assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::try_from(0x0F000).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x0FEFF).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x0FF00).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x0FFFF).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x10000).unwrap(); - assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::try_from(0x1EE00).unwrap(); - assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::try_from(0x1EF00).unwrap(); - assert_eq!(pgn.is_destination_specific(), true); - let pgn = Pgn::try_from(0x1F000).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x1FEFF).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x1FF00).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); - let pgn = Pgn::try_from(0x1FFFF).unwrap(); - assert_eq!(pgn.is_destination_specific(), false); + fn test_set_group_extension() { + let mut pgn = Pgn::new(true, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + pgn.set_group_extension(0x34); + assert_eq!(pgn.get_group_extension(), Some(0x34)); + + let mut pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + pgn.set_group_extension(0x34); + assert_eq!(pgn.get_group_extension(), None); + + let mut pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + pgn.set_group_extension(0x56); + assert_eq!(pgn.get_group_extension(), None); + } + + #[test] + fn test_is_proprietary() { + let pgn = Pgn::new(true, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + assert!(!pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert!(!pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert!(!pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0xEF), PduSpecific::new(0x00)); + assert!(pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0xFF), PduSpecific::new(0x00)); + assert!(pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0x1E), PduSpecific::new(0x00)); + assert!(!pgn.is_proprietary()); + + let pgn = Pgn::new(false, true, PduFormat::new(0x1F), PduSpecific::new(0x00)); + assert!(!pgn.is_proprietary()); + } + + #[test] + fn test_page() { + let pgn = Pgn::new(false, false, PduFormat::new(0xF3), PduSpecific::new(0xBA)); + assert_eq!(pgn.page(), Page::J1939Page0); + + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert_eq!(pgn.page(), Page::J1939Page1); + + let pgn = Pgn::new(true, false, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn.page(), Page::J1939PageReserved); + + let pgn = Pgn::new(true, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn.page(), Page::ISO11992_4Defined); + } + + #[test] + fn test_try_from() { + let pgn_parsed = Pgn::try_from(0x2E6BA).expect("Failed to parse PGN"); + + let pgn = Pgn::new(true, false, PduFormat::new(0xE6), PduSpecific::new(0xBA)); + assert_eq!(pgn, pgn_parsed); + + let pgn_parsed = Pgn::try_from(0x100FF).expect("Failed to parse PGN"); + + let pgn = Pgn::new(false, true, PduFormat::new(0x00), PduSpecific::new(0xFF)); + assert_eq!(pgn, pgn_parsed); + + let pgn_parsed = Pgn::try_from(0x11234).expect("Failed to parse PGN"); + + let pgn = Pgn::new(false, true, PduFormat::new(0x12), PduSpecific::new(0x34)); + assert_eq!(pgn, pgn_parsed); } } diff --git a/src/j1939/priority.rs b/src/j1939/priority.rs index cff6f0b..a2a3874 100644 --- a/src/j1939/priority.rs +++ b/src/j1939/priority.rs @@ -1,4 +1,9 @@ -#[derive(Debug)] +use bitvec::field::BitField; +use bitvec::order::Lsb0; +use bitvec::vec::BitVec; +use bitvec::view::BitView; + +#[derive(Debug, PartialEq, Eq)] pub struct ParsePriorityError(u8); impl std::fmt::Display for ParsePriorityError { @@ -11,6 +16,7 @@ impl std::fmt::Display for ParsePriorityError { } } +/// The priority of a J1939 message #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum Priority { @@ -28,24 +34,47 @@ pub enum Priority { } impl Priority { + /// The number of bits used to represent the priority + pub const BIT_LENGTH: u8 = 3; + /// The highest priority pub const HIGHEST: Priority = Priority::Zero; + /// The default priority pub const DEFAULT: Priority = Priority::Six; + /// The lowest priority pub const LOWEST: Priority = Priority::Seven; + /// Returns if the priority is the [Priority::HIGHEST] / [Priority::Zero] priority #[inline] pub fn is_highest(self) -> bool { self == Self::HIGHEST } + /// Returns if the priority is the [Priority::DEFAULT] / [Priority::Six] priority #[inline] pub fn is_default(self) -> bool { self == Self::DEFAULT } + /// Returns if the priority is the [Priority::LOWEST] / [Priority::Seven] priority #[inline] pub fn is_lowest(self) -> bool { self == Self::LOWEST } + + /// Get the raw value of the priority + #[inline] + pub fn raw(self) -> u8 { + self as u8 + } + + /// Get the raw bits of the priority + pub fn raw_bits(&self) -> [bool; 3] { + let priority_raw = self.raw(); + let mut priority_bits = priority_raw.view_bits::().to_bitvec(); + priority_bits.truncate(3); + priority_bits.reverse(); + [priority_bits[0], priority_bits[1], priority_bits[2]] + } } impl Default for Priority { @@ -54,6 +83,38 @@ impl Default for Priority { } } +impl From for u8 { + fn from(priority: Priority) -> Self { + priority.raw() + } +} + +impl From for [bool; 3] { + fn from(priority: Priority) -> Self { + priority.raw_bits() + } +} + +impl From<[bool; 3]> for Priority { + fn from(raw_priority: [bool; 3]) -> Self { + let mut priority_bits: BitVec = BitVec::new(); + priority_bits.extend(raw_priority.iter()); + priority_bits.reverse(); + let priority_raw = priority_bits.load_be::(); + match priority_raw { + 0x0 => Priority::Zero, + 0x1 => Priority::One, + 0x2 => Priority::Two, + 0x3 => Priority::Three, + 0x4 => Priority::Four, + 0x5 => Priority::Five, + 0x6 => Priority::Six, + 0x7 => Priority::Seven, + _ => unreachable!(), + } + } +} + impl TryFrom for Priority { type Error = ParsePriorityError; @@ -71,3 +132,67 @@ impl TryFrom for Priority { } } } + +#[cfg(test)] +mod tests { + use crate::j1939::priority::ParsePriorityError; + use crate::j1939::Priority; + + #[test] + fn test_try_from_u8_for_priority() { + assert_eq!(Priority::try_from(0x0).unwrap(), Priority::Zero); + assert_eq!(Priority::try_from(0x1).unwrap(), Priority::One); + assert_eq!(Priority::try_from(0x2).unwrap(), Priority::Two); + assert_eq!(Priority::try_from(0x3).unwrap(), Priority::Three); + assert_eq!(Priority::try_from(0x4).unwrap(), Priority::Four); + assert_eq!(Priority::try_from(0x5).unwrap(), Priority::Five); + assert_eq!(Priority::try_from(0x6).unwrap(), Priority::Six); + assert_eq!(Priority::try_from(0x7).unwrap(), Priority::Seven); + assert_eq!(Priority::try_from(0x8).unwrap_err(), ParsePriorityError(8)); + } + + #[test] + fn test_from_bool_array_for_priority() { + assert_eq!(Priority::from([false, false, false]), Priority::Zero); + assert_eq!(Priority::from([false, false, true]), Priority::One); + assert_eq!(Priority::from([false, true, false]), Priority::Two); + assert_eq!(Priority::from([false, true, true]), Priority::Three); + assert_eq!(Priority::from([true, false, false]), Priority::Four); + assert_eq!(Priority::from([true, false, true]), Priority::Five); + assert_eq!(Priority::from([true, true, false]), Priority::Six); + assert_eq!(Priority::from([true, true, true]), Priority::Seven); + } + + #[test] + fn test_priority() { + assert_eq!(Priority::HIGHEST, Priority::Zero); + assert_eq!(Priority::DEFAULT, Priority::Six); + assert_eq!(Priority::LOWEST, Priority::Seven); + + assert_eq!(Priority::HIGHEST.is_highest(), true); + assert_eq!(Priority::HIGHEST.is_default(), false); + assert_eq!(Priority::HIGHEST.is_lowest(), false); + + let priority = Priority::try_from(0x0).unwrap(); + assert_eq!(priority.raw(), 0x0); + assert_eq!(priority.raw_bits(), [false, false, false]); + } + + #[test] + fn test_raw_priority() { + let priority = Priority::Five; + assert_eq!(u8::from(priority), 0x5); + assert_eq!(priority.raw(), 0x5); + + let priority = Priority::One; + assert_eq!(u8::from(priority), 0x1); + assert_eq!(priority.raw(), 0x1); + } + + #[test] + fn test_raw_bits_priority() { + assert_eq!(Priority::Four.raw_bits(), [true, false, false]); + assert_eq!(Priority::Six.raw_bits(), [true, true, false]); + assert_eq!(Priority::One.raw_bits(), [false, false, true]); + } +} diff --git a/src/j1939/standard_id.rs b/src/j1939/standard_id.rs index 56382a3..5226f07 100644 --- a/src/j1939/standard_id.rs +++ b/src/j1939/standard_id.rs @@ -1,11 +1,12 @@ +use crate::j1939::byte_field::ByteField; use crate::j1939::id::Id; -use crate::j1939::id::ParseIdError; use crate::j1939::{Address, Priority}; use bitvec::field::BitField; use bitvec::order::Msb0; -use bitvec::view::BitView; -use embedded_can::Id as EmbeddedId; +use bitvec::vec::BitVec; +use embedded_can::{Id as EmbeddedId, StandardId as EmbeddedStandardId}; +/// Standard 11-bit J1939 identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct StandardId { priority: Priority, @@ -13,6 +14,12 @@ pub struct StandardId { } impl StandardId { + pub const SOURCE_ADDRESS_START: usize = 0; + pub const SOURCE_ADDRESS_END: usize = 8; + pub const PRIORITY_START: usize = 8; + pub const PRIORITY_END: usize = 11; + + /// Creates a new standard identifier out of a priority and source address pub fn new(priority: Priority, source_address: Address) -> Self { Self { priority, @@ -20,37 +27,45 @@ impl StandardId { } } - /// Get the priority of the ID + /// Get the priority of the identifier #[inline] pub fn priority(&self) -> Priority { self.priority } - /// Get the source address of the ID + /// Get the source address of the identifier #[inline] pub fn source_address(&self) -> Address { self.source_address } -} - -impl TryFrom for StandardId { - type Error = ParseIdError; - fn try_from(id: EmbeddedId) -> Result { - match id { - EmbeddedId::Standard(id) => { - let bit_data = id.as_raw().view_bits::().to_bitvec(); - let priority = Priority::try_from(bit_data.load::()); - let source_address = Address::new(bit_data.load::()); - - if priority.is_err() { - return Err(ParseIdError::Priority); - } + /// Get the raw value of the standard identifier + pub fn raw(&self) -> u16 { + let priority_bits: [bool; 3] = self.priority.into(); + let raw_source_address: [bool; 8] = self.source_address.raw_bits(); + let mut raw_id = BitVec::::new(); + raw_id.extend(priority_bits); + raw_id.extend(raw_source_address); + raw_id.load() + } - Ok(Self::new(priority.unwrap(), source_address)) - } - EmbeddedId::Extended(_) => Err(ParseIdError::ExtendedId), - } + /// Get the raw bits of the standard identifier + pub fn raw_bits(&self) -> [bool; 11] { + let priority_bits: [bool; 3] = self.priority.into(); + let raw_source_address: [bool; 8] = self.source_address.raw_bits(); + [ + priority_bits[0], + priority_bits[1], + priority_bits[2], + raw_source_address[0], + raw_source_address[1], + raw_source_address[2], + raw_source_address[3], + raw_source_address[4], + raw_source_address[5], + raw_source_address[6], + raw_source_address[7], + ] } } @@ -59,3 +74,32 @@ impl From for Id { Id::Standard(id) } } + +impl From for EmbeddedId { + fn from(id: StandardId) -> Self { + EmbeddedId::Standard(EmbeddedStandardId::new(id.raw()).unwrap()) + } +} + +impl From for [bool; 11] { + fn from(id: StandardId) -> Self { + id.raw_bits() + } +} + +#[cfg(test)] +mod tests { + use crate::j1939::{Address, Priority, StandardId}; + + #[test] + fn test_raw() { + let id = StandardId::new(Priority::Three, Address::new(0x0A)); + assert_eq!(id.raw(), 0x30A); + + let id = StandardId::new(Priority::Seven, Address::new(0x0F)); + assert_eq!(id.raw(), 0x70F); + + let id = StandardId::new(Priority::Zero, Address::new(0x00)); + assert_eq!(id.raw(), 0x000); + } +} diff --git a/src/network_management/common_parameter_group_numbers.rs b/src/network_management/common_parameter_group_numbers.rs index 700138d..05a6ec4 100644 --- a/src/network_management/common_parameter_group_numbers.rs +++ b/src/network_management/common_parameter_group_numbers.rs @@ -1,6 +1,6 @@ // Copyright 2023 Raven Industries inc. -use crate::j1939::Pgn; +use crate::j1939::{PduFormat, PduSpecific, Pgn}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum CommonParameterGroupNumbers { @@ -11,9 +11,11 @@ pub enum CommonParameterGroupNumbers { impl CommonParameterGroupNumbers { pub fn get_pgn(&self) -> Pgn { match self { - CommonParameterGroupNumbers::AddressClaim => Pgn::new(false, false, 0xEE, 0x00), + CommonParameterGroupNumbers::AddressClaim => { + Pgn::new(false, false, PduFormat::new(0xEE), PduSpecific::new(0x00)) + } CommonParameterGroupNumbers::ParameterGroupNumberRequest => { - Pgn::new(false, false, 0xEA, 0x00) + Pgn::new(false, false, PduFormat::new(0xEA), PduSpecific::new(0x00)) } } } diff --git a/src/network_management/network_manager.rs b/src/network_management/network_manager.rs index e4f8eb5..a43f47d 100644 --- a/src/network_management/network_manager.rs +++ b/src/network_management/network_manager.rs @@ -2,7 +2,7 @@ use std::time::Instant; use super::control_function::{AddressClaimingState, ControlFunction}; -use crate::j1939::{Address, ExtendedId, Frame, Pgn, Priority}; +use crate::j1939::{Address, ByteField, ExtendedId, Frame, Id, Pgn, Priority, StandardId}; use crate::network_management::common_parameter_group_numbers::CommonParameterGroupNumbers; use crate::network_management::name::NAME; use std::cell::RefCell; @@ -101,9 +101,8 @@ impl NetworkManager { let address_claim = >::into(name).to_le_bytes().to_vec(); let request_id = ExtendedId::new( - Priority::DEFAULT, + StandardId::new(Priority::Three, source_address), CommonParameterGroupNumbers::AddressClaim.get_pgn(), - source_address, ); Frame::new(request_id, address_claim).unwrap() } @@ -113,7 +112,7 @@ impl NetworkManager { let request = pgn_to_request.to_le_bytes().to_vec(); let mut pgn = CommonParameterGroupNumbers::ParameterGroupNumberRequest.get_pgn(); pgn.set_destination_address(Address::BROADCAST); - let request_id = ExtendedId::new(Priority::Three, pgn, Address::NULL); + let request_id = ExtendedId::new(StandardId::new(Priority::Three, Address::NULL), pgn); Frame::new(request_id, request).unwrap() } @@ -152,9 +151,11 @@ impl NetworkManager { self.get_control_function_address_by_name(destination.get_name()), ); let message_id = ExtendedId::new( - priority, + StandardId::new( + priority, + self.get_control_function_address_by_name(source.get_name()), + ), pgn, - self.get_control_function_address_by_name(source.get_name()), ); self.enqueue_can_message( @@ -244,55 +245,60 @@ impl NetworkManager { // Todo receive messages, need to generalize message handling let current_message = self.receive_message_queue.front().unwrap(); - // Process address claims and requests to claim - if NAME::default() == NAME::default() - /*TODO!: Replaced following code line by upper NAME::default(). There needs to be another abstraction of ISO 11783 specific frames which includes the NAME*/ - /*current_message.get_destination_name()*/ - { - // Broadcast Message - if current_message.id().pgn().pdu_format() - == CommonParameterGroupNumbers::AddressClaim - .get_pgn() - .pdu_format() - { - // Todo - } else if current_message.id().pgn().pdu_format() - == CommonParameterGroupNumbers::ParameterGroupNumberRequest - .get_pgn() - .pdu_format() - && current_message.clone().data().len() >= 3 - { - let message_data = current_message.clone().data(); - let requested_pgn: u32 = (message_data[0] as u32) - | ((message_data[1] as u32) << 8) - | ((message_data[2] as u32) << 16); - - if requested_pgn - == CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32 + match current_message.id() { + Id::Standard(_) => {} + Id::Extended(id) => { + // Process address claims and requests to claim + if NAME::default() == NAME::default() + /*TODO!: Replaced following code line by upper NAME::default(). There needs to be another abstraction of ISO 11783 specific frames which includes the NAME*/ + /*current_message.get_destination_name()*/ { - for internal_cf in &mut self.address_claim_state_machines { - let mut address_claimer = internal_cf.borrow_mut(); - match *address_claimer { - ControlFunction::Internal { - ref mut address_claim_data, - } => { - if address_claim_data.get_state() - == AddressClaimingState::AddressClaimingComplete - { - address_claim_data.set_state( - AddressClaimingState::SendReclaimAddressOnRequest, - ); + // Broadcast Message + if id.pgn().pdu_format() + == CommonParameterGroupNumbers::AddressClaim + .get_pgn() + .pdu_format() + { + // Todo + } else if id.pgn().pdu_format() + == CommonParameterGroupNumbers::ParameterGroupNumberRequest + .get_pgn() + .pdu_format() + && current_message.clone().data().len() >= 3 + { + let message_data = current_message.clone().data(); + let requested_pgn: u32 = (message_data[0] as u32) + | ((message_data[1] as u32) << 8) + | ((message_data[2] as u32) << 16); + + if requested_pgn + == CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32 + { + for internal_cf in &mut self.address_claim_state_machines { + let mut address_claimer = internal_cf.borrow_mut(); + match *address_claimer { + ControlFunction::Internal { + ref mut address_claim_data, + } => { + if address_claim_data.get_state() + == AddressClaimingState::AddressClaimingComplete + { + address_claim_data.set_state( + AddressClaimingState::SendReclaimAddressOnRequest, + ); + } + } + ControlFunction::External { name: _ } => {} } } - ControlFunction::External { name: _ } => {} } + } else { + // Destination specific } + + self.receive_message_queue.pop_front(); } - } else { - // Destination specific } - - self.receive_message_queue.pop_front(); } } } From 5b87c074c356305b85ac73c5b9a047b08afbf1c9 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Wed, 21 Feb 2024 23:00:24 +0100 Subject: [PATCH 15/29] fix unused import --- src/j1939/address.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index 9294ac6..ee4048e 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -53,6 +53,7 @@ impl ByteField for Address { } } +#[cfg(test)] mod tests { use crate::j1939::Address; From 6a98902e43f45f0979e504c69b2a5781573ef8a6 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:10:50 +0100 Subject: [PATCH 16/29] adding tests to standard id --- src/j1939/standard_id.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/j1939/standard_id.rs b/src/j1939/standard_id.rs index 5226f07..3622ffb 100644 --- a/src/j1939/standard_id.rs +++ b/src/j1939/standard_id.rs @@ -90,6 +90,7 @@ impl From for [bool; 11] { #[cfg(test)] mod tests { use crate::j1939::{Address, Priority, StandardId}; + use embedded_can::{Id as EmbeddedId, StandardId as EmbeddedStandardId}; #[test] fn test_raw() { @@ -102,4 +103,40 @@ mod tests { let id = StandardId::new(Priority::Zero, Address::new(0x00)); assert_eq!(id.raw(), 0x000); } + + #[test] + fn test_raw_bits() { + let id = StandardId::new(Priority::Three, Address::new(0x0A)); + assert_eq!( + id.raw_bits(), + [false, true, true, false, false, false, false, true, false, true, false] + ); + + let id = StandardId::new(Priority::Seven, Address::new(0x0F)); + assert_eq!( + id.raw_bits(), + [true, true, true, false, false, false, false, true, true, true, true] + ); + + let id = StandardId::new(Priority::Zero, Address::new(0x00)); + assert_eq!( + id.raw_bits(), + [false, false, false, false, false, false, false, false, false, false, false] + ); + } + + #[test] + fn test_from_standard_id_for_id() { + let id = StandardId::new(Priority::Three, Address::new(0x0A)); + assert_eq!( + EmbeddedId::from(id), + EmbeddedId::Standard(EmbeddedStandardId::new(0x30A).unwrap()) + ); + + let id = StandardId::new(Priority::Seven, Address::new(0x0F)); + assert_eq!( + EmbeddedId::from(id), + EmbeddedId::Standard(EmbeddedStandardId::new(0x70F).unwrap()) + ); + } } From 5a7da268c3f2e46da0c8d77756934ffd38ccea7d Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:11:01 +0100 Subject: [PATCH 17/29] adding doc --- src/j1939/frame.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs index 07195d5..3f80741 100644 --- a/src/j1939/frame.rs +++ b/src/j1939/frame.rs @@ -6,6 +6,7 @@ use embedded_can::{Frame as EmbeddedFrame, Id as EmbeddedId}; #[repr(transparent)] pub struct Channel(u8); +/// J1939 frame #[derive(Debug, Clone, Default)] pub struct Frame { id: Id, @@ -13,6 +14,7 @@ pub struct Frame { } impl Frame { + /// Creates a new J1939 data frame pub fn new(id: impl Into, data: Vec) -> Option { Some(Self { id: Id::try_from(id.into()).expect("Invalid J1939 ID"), @@ -20,11 +22,13 @@ impl Frame { }) } + /// Identifier of the frame #[inline] pub fn id(&self) -> Id { self.id } + /// Data of the frame #[inline] pub fn data(self) -> Vec { self.data @@ -32,18 +36,20 @@ impl Frame { } impl EmbeddedFrame for Frame { + /// Creates a new J1939 data frame from a standard CAN data frame fn new(id: impl Into, data: &[u8]) -> Option { Frame::new(id, data.to_vec()) } - /// create a new remote frame + /// Creates a new remote frame (only to satisfy the trait) ///
- /// J1939 does not support remote frames (see J1939-21 5.4) so this is always [None] + /// This will always return `None` as J1939 does not support remote frames ///
fn new_remote(_id: impl Into, _dlc: usize) -> Option { None } + /// Returns `true` if the frame is an extended frame fn is_extended(&self) -> bool { match self.id { Id::Standard(_) => false, @@ -51,6 +57,7 @@ impl EmbeddedFrame for Frame { } } + /// Returns `true` if the frame is a standard frame fn is_standard(&self) -> bool { match self.id { Id::Standard(_) => true, @@ -58,24 +65,27 @@ impl EmbeddedFrame for Frame { } } + /// returns always `false` as J1939 does not support remote frames fn is_remote_frame(&self) -> bool { - // J1939 does not support remote frames (see J1939-21 5.4) false } + /// returns always `true` as J1939 only supports data frames fn is_data_frame(&self) -> bool { - // J1939 only supports data frames true } + /// Identifier of the frame fn id(&self) -> EmbeddedId { self.id.into() } + /// Data length code of the frame fn dlc(&self) -> usize { self.data.len() } + /// Data of the frame fn data(&self) -> &[u8] { self.data.as_slice() } From 073356f1e0fc9adb1d5ee5252362836ae061acfd Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:11:25 +0100 Subject: [PATCH 18/29] add test for Default --- src/j1939/address.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index ee4048e..a8a60e4 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -69,5 +69,8 @@ mod tests { let address = Address::NULL; assert!(address.is_null()); + + let address = Address::default(); + assert!(address.is_null()); } } From 0c8d6831dbd4158e3a5a831aa3e2887e38b0455b Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:23:15 +0100 Subject: [PATCH 19/29] Adds tests to ext ID & fixes --- src/j1939/extended_id.rs | 104 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs index c5b5b8a..fb3de72 100644 --- a/src/j1939/extended_id.rs +++ b/src/j1939/extended_id.rs @@ -18,13 +18,20 @@ pub struct ExtendedId { } impl ExtendedId { + /// The number of bits in the extended identifier pub const BIT_LENGTH: u8 = 29; + const PRIORITY_START: usize = 0; + const PRIORITY_END: usize = 3; + const PGN_START: usize = 3; + const PGN_END: usize = 21; + const SOURCE_ADDRESS_START: usize = 21; + const SOURCE_ADDRESS_END: usize = 29; pub fn new(standard_id: StandardId, pgn: Pgn) -> Self { Self { standard_id, pgn } } - /// Get the raw value of the extended identifier + /// Raw value of the extended identifier pub fn raw(&self) -> u32 { let mut raw_id: BitVec = BitVec::new(); raw_id.extend(self.standard_id.priority().raw_bits()); @@ -34,7 +41,21 @@ impl ExtendedId { raw_id.load::() } - /// Get the PGN of the identifier + /// Raw bits of the extended identifier + pub fn raw_bits(&self) -> [bool; 29] { + let mut raw_id: BitVec = BitVec::new(); + raw_id.extend(self.standard_id.priority().raw_bits()); + raw_id.extend(self.pgn.raw_bits()); + raw_id.extend(self.standard_id.source_address().raw_bits()); + [ + raw_id[0], raw_id[1], raw_id[2], raw_id[3], raw_id[4], raw_id[5], raw_id[6], raw_id[7], + raw_id[8], raw_id[9], raw_id[10], raw_id[11], raw_id[12], raw_id[13], raw_id[14], + raw_id[15], raw_id[16], raw_id[17], raw_id[18], raw_id[19], raw_id[20], raw_id[21], + raw_id[22], raw_id[23], raw_id[24], raw_id[25], raw_id[26], raw_id[27], raw_id[28], + ] + } + + /// PGN of the identifier #[inline] pub fn pgn(&self) -> Pgn { self.pgn @@ -52,14 +73,28 @@ impl TryFrom for ExtendedId { fn try_from(raw_id: u32) -> Result { let bit_data = raw_id.view_bits::().to_bitvec(); - let priority = Priority::try_from(bit_data.load::()); - let pgn = Pgn::try_from(bit_data.load::()); - let source_address = Address::new(bit_data.load::()); + let mut priority_bits = + bit_data[ExtendedId::PRIORITY_START..ExtendedId::PRIORITY_END].to_bitvec(); + let mut pgn_bits = bit_data[ExtendedId::PGN_START..ExtendedId::PGN_END].to_bitvec(); + let mut source_address_bits = + bit_data[ExtendedId::SOURCE_ADDRESS_START..ExtendedId::SOURCE_ADDRESS_END].to_bitvec(); + + priority_bits.reverse(); + pgn_bits.reverse(); + source_address_bits.reverse(); + + let priority = Priority::try_from(priority_bits.load::()); + let pgn = Pgn::try_from(pgn_bits.load::()); + let source_address = Address::new(source_address_bits.load::()); - if priority.is_err() || pgn.is_err() { + if priority.is_err() { return Err(ParseIdError::Priority); } + if pgn.is_err() { + return Err(ParseIdError::Pgn); + } + Ok(ExtendedId::new( StandardId::new(priority.unwrap(), source_address), pgn.unwrap(), @@ -84,5 +119,62 @@ mod tests { Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x0F)), ); assert_eq!(id.raw(), 0x18A0F25); + + let id = ExtendedId::new( + StandardId::new(Priority::Seven, Address::new(0xAF)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x2F)), + ); + assert_eq!(id.raw(), 0x1D8A2FAF); + + let id = ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x00)), + Pgn::new(true, false, PduFormat::new(0x4C), PduSpecific::new(0x12)), + ); + assert_eq!(id.raw(), 0x24C1200); + } + + #[test] + fn test_raw_bits() { + let id = ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x25)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x0F)), + ); + assert_eq!( + id.raw_bits(), + [ + false, false, false, false, true, true, false, false, false, true, false, true, + false, false, false, false, false, true, true, true, true, false, false, true, + false, false, true, false, true + ] + ); + + let id = ExtendedId::new( + StandardId::new(Priority::Seven, Address::new(0xAF)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x2F)), + ); + + assert_eq!( + id.raw_bits(), + [ + true, true, true, false, true, true, false, false, false, true, false, true, false, + false, false, true, false, true, true, true, true, true, false, true, false, true, + true, true, true + ] + ); + } + + #[test] + fn test_from_extended_id_for_embedded_id() { + let id = ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x25)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x0F)), + ); + let embedded_id: embedded_can::Id = id.into(); + assert_eq!( + embedded_id, + embedded_can::Id::Extended(embedded_can::ExtendedId::new(0x18A0F25).unwrap()) + ); + } + } } From d5914ddfe65fda83c5f1b71dd2f31707333a76f0 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:27:41 +0100 Subject: [PATCH 20/29] add test --- src/j1939/extended_id.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs index fb3de72..7f628d8 100644 --- a/src/j1939/extended_id.rs +++ b/src/j1939/extended_id.rs @@ -176,5 +176,34 @@ mod tests { ); } + // not finished yet TODO! + //#[test] + fn test_try_from_u32_for_extended_id() { + let id = ExtendedId::try_from(0x18A0F25).unwrap(); + assert_eq!( + id, + ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x25)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x0F)), + ) + ); + + let id = ExtendedId::try_from(0x1D8A2FAF).unwrap(); + assert_eq!( + id, + ExtendedId::new( + StandardId::new(Priority::Seven, Address::new(0xAF)), + Pgn::new(false, true, PduFormat::new(0x8A), PduSpecific::new(0x2F)), + ) + ); + + let id = ExtendedId::try_from(0x24C1200).unwrap(); + assert_eq!( + id, + ExtendedId::new( + StandardId::new(Priority::Zero, Address::new(0x00)), + Pgn::new(true, false, PduFormat::new(0x4C), PduSpecific::new(0x12)), + ) + ); } } From 5010f2b8d6ac2d3d21fe9886898c7a83959c15f6 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 00:29:34 +0100 Subject: [PATCH 21/29] to be finished --- src/j1939/extended_id.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs index 7f628d8..8b8ab44 100644 --- a/src/j1939/extended_id.rs +++ b/src/j1939/extended_id.rs @@ -176,8 +176,8 @@ mod tests { ); } - // not finished yet TODO! - //#[test] + /* not finished yet TODO! + #[test] fn test_try_from_u32_for_extended_id() { let id = ExtendedId::try_from(0x18A0F25).unwrap(); assert_eq!( @@ -205,5 +205,5 @@ mod tests { Pgn::new(true, false, PduFormat::new(0x4C), PduSpecific::new(0x12)), ) ); - } + }*/ } From 25e97f3865c4eeb646ec3b71f5c1a668acdfe57f Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:00:59 +0100 Subject: [PATCH 22/29] add important addresses & addr ranges, documentation --- src/j1939/address.rs | 57 +++++++++++++++++++++++++++++++++++---- src/j1939/byte_field.rs | 7 +++++ src/j1939/extended_id.rs | 56 +++++++++++++++++++++++++++++++++----- src/j1939/frame.rs | 8 +++++- src/j1939/id.rs | 6 +++++ src/j1939/page.rs | 6 +++++ src/j1939/pdu_format.rs | 6 +++++ src/j1939/pdu_specific.rs | 6 +++++ src/j1939/pgn.rs | 8 +++++- src/j1939/priority.rs | 6 +++++ src/j1939/standard_id.rs | 6 +++++ 11 files changed, 159 insertions(+), 13 deletions(-) diff --git a/src/j1939/address.rs b/src/j1939/address.rs index a8a60e4..2131fff 100644 --- a/src/j1939/address.rs +++ b/src/j1939/address.rs @@ -1,8 +1,13 @@ -// Copyright 2023 Raven Industries inc. +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::byte_field::ByteField; -/// J1939 address (8-bits) used to identify ECUs on the network +/// J1939 address (8-bits) used to identify control applications on the network #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct Address(u8); @@ -10,11 +15,53 @@ pub struct Address(u8); impl Address { /// The number of bits in the address pub const BIT_LENGTH: u8 = 8; - /// Address representing broadcasts for destination specific PGNs + + /// Global Preferred Addresses + /// + /// only to be used by control applications that handles the given function and + /// function instance, if applicable, that is assigned to that address by SAE J1939. + /// + /// For more information see SAE J1939 4.6.1 + pub const GLOBAL_PREFERRED_ADDRESSES: (std::ops::Range, std::ops::Range) = + (0x00..0x7F, 0xF8..0xFD); + + /// Dynamic addresses + /// + /// any control application executing any system function can claim and use it. + /// The supplier of a control application can employ any strategy + /// to select the initial address within the range of 128 to 247. + /// + /// For more information see SAE J1939 4.6.2 + pub const DYNAMIC_ADDRESSES: std::ops::Range = 0x80..0xF7; + + /// Global Address + /// + /// The SAE J1939 source address 255 serves as the global destination address. + /// This global destination address is exclusively utilized as the destination + /// address in a D_PDU1 data frame to signify that the SAE J1939 data frame is + /// intended for all Control Applications (CAs) on the network. + /// + /// For more information see SAE J1939 4.6.3 pub const GLOBAL: Address = Self::BROADCAST; - /// Alias for the global address + /// Alias for the [Address::GLOBAL] pub const BROADCAST: Address = Address(0xFF); - /// The null address is used by ECUs without an address such as during address claiming + + /// Null Address + /// + /// The SAE J1939 source address 254 is designated as the Null address. + /// This Null address is specifically employed as the source (transmitter) address + /// within a D_PDU1 or D_PDU2 data frame. + /// + /// There are only two approved applications for the Null address: + /// + /// 1. The Null address can be utilized with an Address Claimed Parameter Group (PG) + /// when a Control Application (CA) reports its inability to claim an SAE J1939 Address. + /// + /// 2. the Null address can be employed with a Request PG soliciting + /// the Address Claimed PG when the Request PG is transmitted by a CA + /// prior to claiming a source address. + /// + /// For more information see SAE J1939 4.6.4 pub const NULL: Address = Address(0xFE); /// Create a new address diff --git a/src/j1939/byte_field.rs b/src/j1939/byte_field.rs index a6217ae..5caac1e 100644 --- a/src/j1939/byte_field.rs +++ b/src/j1939/byte_field.rs @@ -1,3 +1,10 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ + use bitvec::order::Msb0; use bitvec::view::BitView; diff --git a/src/j1939/extended_id.rs b/src/j1939/extended_id.rs index 8b8ab44..ab57518 100644 --- a/src/j1939/extended_id.rs +++ b/src/j1939/extended_id.rs @@ -1,4 +1,9 @@ -// Copyright 2023 Raven Industries inc. +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::byte_field::ByteField; use crate::j1939::id::{Id, ParseIdError}; use crate::j1939::priority::Priority; @@ -68,11 +73,12 @@ impl From for EmbeddedId { } } -impl TryFrom for ExtendedId { +impl TryFrom<[bool; 29]> for ExtendedId { type Error = ParseIdError; - fn try_from(raw_id: u32) -> Result { - let bit_data = raw_id.view_bits::().to_bitvec(); + fn try_from(raw_bits: [bool; 29]) -> Result { + let mut bit_data: BitVec = BitVec::new(); + bit_data.extend(raw_bits.iter()); let mut priority_bits = bit_data[ExtendedId::PRIORITY_START..ExtendedId::PRIORITY_END].to_bitvec(); let mut pgn_bits = bit_data[ExtendedId::PGN_START..ExtendedId::PGN_END].to_bitvec(); @@ -102,6 +108,45 @@ impl TryFrom for ExtendedId { } } +impl TryFrom for ExtendedId { + type Error = ParseIdError; + + fn try_from(raw_id: u32) -> Result { + let raw_id_bits = raw_id.view_bits::().to_bitvec(); + Self::try_from([ + raw_id_bits[3], + raw_id_bits[4], + raw_id_bits[5], + raw_id_bits[6], + raw_id_bits[7], + raw_id_bits[8], + raw_id_bits[9], + raw_id_bits[10], + raw_id_bits[11], + raw_id_bits[12], + raw_id_bits[13], + raw_id_bits[14], + raw_id_bits[15], + raw_id_bits[16], + raw_id_bits[17], + raw_id_bits[18], + raw_id_bits[19], + raw_id_bits[20], + raw_id_bits[21], + raw_id_bits[22], + raw_id_bits[23], + raw_id_bits[24], + raw_id_bits[25], + raw_id_bits[26], + raw_id_bits[27], + raw_id_bits[28], + raw_id_bits[29], + raw_id_bits[30], + raw_id_bits[31], + ]) + } +} + impl From for Id { fn from(id: ExtendedId) -> Self { Id::Extended(id) @@ -176,7 +221,6 @@ mod tests { ); } - /* not finished yet TODO! #[test] fn test_try_from_u32_for_extended_id() { let id = ExtendedId::try_from(0x18A0F25).unwrap(); @@ -205,5 +249,5 @@ mod tests { Pgn::new(true, false, PduFormat::new(0x4C), PduSpecific::new(0x12)), ) ); - }*/ + } } diff --git a/src/j1939/frame.rs b/src/j1939/frame.rs index 3f80741..d7b6658 100644 --- a/src/j1939/frame.rs +++ b/src/j1939/frame.rs @@ -1,4 +1,10 @@ -// Copyright 2023 Raven Industries inc. +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ + use crate::j1939::id::Id; use embedded_can::{Frame as EmbeddedFrame, Id as EmbeddedId}; diff --git a/src/j1939/id.rs b/src/j1939/id.rs index 57257f2..c69bd51 100644 --- a/src/j1939/id.rs +++ b/src/j1939/id.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::standard_id::StandardId; use crate::j1939::{Address, ExtendedId, Pgn, Priority}; use bitvec::field::BitField; diff --git a/src/j1939/page.rs b/src/j1939/page.rs index 1a369fa..4731adf 100644 --- a/src/j1939/page.rs +++ b/src/j1939/page.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use bitvec::field::BitField; use bitvec::order::Msb0; use bitvec::prelude::BitVec; diff --git a/src/j1939/pdu_format.rs b/src/j1939/pdu_format.rs index 278b6cd..134c7dc 100644 --- a/src/j1939/pdu_format.rs +++ b/src/j1939/pdu_format.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::byte_field::ByteField; /// PDU format field defined in the PGN diff --git a/src/j1939/pdu_specific.rs b/src/j1939/pdu_specific.rs index 1c7e0ed..c0e771b 100644 --- a/src/j1939/pdu_specific.rs +++ b/src/j1939/pdu_specific.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::byte_field::ByteField; /// PDU specific field defined in the PGN diff --git a/src/j1939/pgn.rs b/src/j1939/pgn.rs index 2bc647f..b996d49 100644 --- a/src/j1939/pgn.rs +++ b/src/j1939/pgn.rs @@ -1,4 +1,10 @@ -// Copyright 2023 Raven Industries inc. +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ + use crate::j1939::byte_field::ByteField; use crate::j1939::page::Page; use crate::j1939::pdu_format::PduFormat; diff --git a/src/j1939/priority.rs b/src/j1939/priority.rs index a2a3874..0f7c42c 100644 --- a/src/j1939/priority.rs +++ b/src/j1939/priority.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use bitvec::field::BitField; use bitvec::order::Lsb0; use bitvec::vec::BitVec; diff --git a/src/j1939/standard_id.rs b/src/j1939/standard_id.rs index 3622ffb..f0d1325 100644 --- a/src/j1939/standard_id.rs +++ b/src/j1939/standard_id.rs @@ -1,3 +1,9 @@ +/* +Copyright 2023 Raven Industries inc. + +@author Jannes Brands +@date 2024-02-22 +*/ use crate::j1939::byte_field::ByteField; use crate::j1939::id::Id; use crate::j1939::{Address, Priority}; From ff938943ee4c7a49b3af0172dfc344a1b1abbf7c Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:31:54 +0100 Subject: [PATCH 23/29] forward example only on linux (for this moment) --- examples/forward.rs | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index 90ce32f..463fb64 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -2,7 +2,7 @@ use clap::Parser; -#[cfg(target_os = "unix")] +#[cfg(target_os = "linux")] use socketcan::{CanSocket, Socket}; /// Forward CAN traffic from one interface to another @@ -26,7 +26,8 @@ struct Options { pub output_interface: String, } -fn open_socket_can_interface() -> (CanSocket, CanSocket) { +#[cfg(target_os = "linux")] +fn open_can_interface() -> (CanSocket, CanSocket) { let mut input = CanSocket::open(&opts.input_interface) .expect("The given input interface cannot be opened!"); @@ -52,23 +53,26 @@ fn main() { opts.output_interface ); - let (input, output) = open_can_interface(); + #[cfg(target_os = "linux")] + { + let (input, output) = open_can_interface(); - input - .set_nonblocking(true) - .expect("Could not set input bus to non-blocking!"); - output - .set_nonblocking(true) - .expect("Could not set output bus to non-blocking!"); + input + .set_nonblocking(true) + .expect("Could not set input bus to non-blocking!"); + output + .set_nonblocking(true) + .expect("Could not set output bus to non-blocking!"); - loop { - match input.receive() { - Ok(frame) => { - output - .transmit(&frame) - .expect("Could not forward received message!"); + loop { + match input.receive() { + Ok(frame) => { + output + .transmit(&frame) + .expect("Could not forward received message!"); + } + Err(_err) => continue, } - Err(_err) => continue, } } } From a8dc97cc6de4e0c492645e0161a13c593fd2182d Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:35:02 +0100 Subject: [PATCH 24/29] .. --- examples/forward.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/forward.rs b/examples/forward.rs index 463fb64..07ed21a 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -3,7 +3,7 @@ use clap::Parser; #[cfg(target_os = "linux")] -use socketcan::{CanSocket, Socket}; +use socketcan::{BlockingCan, CanSocket, NonBlockingCan, Socket}; /// Forward CAN traffic from one interface to another #[derive(Debug, Parser)] From 97d6ca0caa8d06917700c0509936fac6dab7fef9 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:45:00 +0100 Subject: [PATCH 25/29] fix --- examples/forward.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index 07ed21a..cd0ebd7 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -27,12 +27,12 @@ struct Options { } #[cfg(target_os = "linux")] -fn open_can_interface() -> (CanSocket, CanSocket) { - let mut input = CanSocket::open(&opts.input_interface) - .expect("The given input interface cannot be opened!"); +fn open_can_interface(input_name: &str, output_name: &str) -> (CanSocket, CanSocket) { + let mut input = + CanSocket::open(input_name).expect("The given input interface cannot be opened!"); - let mut output = CanSocket::open(&opts.output_interface) - .expect("The given output interface cannot be opened!"); + let mut output = + CanSocket::open(output_name).expect("The given output interface cannot be opened!"); (input, output) } @@ -54,9 +54,8 @@ fn main() { ); #[cfg(target_os = "linux")] - { - let (input, output) = open_can_interface(); - + |opts: Options| { + let (input, output) = open_can_interface(&opts.input_interface, &opts.output_interface); input .set_nonblocking(true) .expect("Could not set input bus to non-blocking!"); From d541b5e43f048668d0f4c568332cc6cc15ef4ba8 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:52:30 +0100 Subject: [PATCH 26/29] remove wrong import --- examples/forward.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/forward.rs b/examples/forward.rs index cd0ebd7..ada5200 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -3,7 +3,7 @@ use clap::Parser; #[cfg(target_os = "linux")] -use socketcan::{BlockingCan, CanSocket, NonBlockingCan, Socket}; +use socketcan::{BlockingCan, CanSocket, Socket}; /// Forward CAN traffic from one interface to another #[derive(Debug, Parser)] From 4b32adf172c4ef12d92f3add4d0152f4aaab7cc8 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:55:46 +0100 Subject: [PATCH 27/29] .. --- examples/forward.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index ada5200..0e9b876 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -55,7 +55,8 @@ fn main() { #[cfg(target_os = "linux")] |opts: Options| { - let (input, output) = open_can_interface(&opts.input_interface, &opts.output_interface); + let (mut input, mut output) = + open_can_interface(&opts.input_interface, &opts.output_interface); input .set_nonblocking(true) .expect("Could not set input bus to non-blocking!"); @@ -73,5 +74,5 @@ fn main() { Err(_err) => continue, } } - } + }; } From 6a51750fc962601088ed40bdbe49e9fda17d9e37 Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 22:58:45 +0100 Subject: [PATCH 28/29] remove confusing clippy hint --- examples/forward.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/forward.rs b/examples/forward.rs index 0e9b876..d2bf09e 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -55,8 +55,7 @@ fn main() { #[cfg(target_os = "linux")] |opts: Options| { - let (mut input, mut output) = - open_can_interface(&opts.input_interface, &opts.output_interface); + let (input, output) = open_can_interface(&opts.input_interface, &opts.output_interface); input .set_nonblocking(true) .expect("Could not set input bus to non-blocking!"); From 24f567a0a939559f4651135a3a456df1c7df440c Mon Sep 17 00:00:00 2001 From: Jannes Brands Date: Thu, 22 Feb 2024 23:01:07 +0100 Subject: [PATCH 29/29] borrow --- examples/forward.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/forward.rs b/examples/forward.rs index d2bf09e..e8f7295 100644 --- a/examples/forward.rs +++ b/examples/forward.rs @@ -64,9 +64,10 @@ fn main() { .expect("Could not set output bus to non-blocking!"); loop { - match input.receive() { + match input.borrow().receive() { Ok(frame) => { output + .borrow() .transmit(&frame) .expect("Could not forward received message!"); }