j1939 0.3.0

SAE J1939 is a set of standards that define how ECUs communicate via the CAN bus in heavy-duty vehicles.
Documentation
use crate::PDU_MAX_LENGTH;

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct Name {
    /// Identity number.
    pub identity_number: u32,
    /// Manufacturer code.
    pub manufacturer_code: u16,
    /// Function instance.
    pub function_instance: u8,
    /// ECU instance.
    pub ecu_instance: u8,
    /// Function.
    pub function: u8,
    /// Vehicle system.
    pub vehicle_system: u8,
    /// Vehicle system instance.
    pub vehicle_system_instance: u8,
    /// Industry group.
    pub industry_group: u8,
    /// Arbitrary address.
    pub arbitrary_address: bool,
}

impl Name {
    #[must_use]
    #[allow(clippy::cast_possible_truncation)]
    pub fn to_bytes(self) -> [u8; PDU_MAX_LENGTH] {
        let mut bytes = [0; PDU_MAX_LENGTH];

        // Mask fields to enforce bit width invariants per J1939 NAME field spec
        let identity_number = self.identity_number & 0x001f_ffff; // 21 bits
        let manufacturer_code = self.manufacturer_code & 0x7ff; // 11 bits
        let function_instance = self.function_instance & 0x1f; // 5 bits
        let ecu_instance = self.ecu_instance & 0x7; // 3 bits
        let vehicle_system = self.vehicle_system & 0x7f; // 7 bits
        let vehicle_system_instance = self.vehicle_system_instance & 0xf; // 4 bits
        let industry_group = self.industry_group & 0x7; // 3 bits

        bytes[0] = identity_number as u8;
        bytes[1] = (identity_number >> 8) as u8;
        bytes[2] = ((identity_number >> 16) as u8) | ((manufacturer_code << 5) as u8);
        bytes[3] = (manufacturer_code >> 3) as u8;
        bytes[4] = (function_instance << 3) | ecu_instance;
        bytes[5] = self.function;
        bytes[6] = vehicle_system << 1;
        bytes[7] = vehicle_system_instance
            | (industry_group << 4)
            | (u8::from(self.arbitrary_address) << 7);

        bytes
    }

    #[must_use]
    pub fn from_bytes(bytes: [u8; PDU_MAX_LENGTH]) -> Self {
        let identity_number =
            u32::from(bytes[0]) | (u32::from(bytes[1]) << 8) | (u32::from(bytes[2] & 0x1f) << 16);
        let manufacturer_code = u16::from(bytes[2] >> 5) | (u16::from(bytes[3]) << 3);
        let function_instance = bytes[4] >> 3;
        let ecu_instance = bytes[4] & 0x7;
        let function = bytes[5];
        let vehicle_system = bytes[6] >> 1;
        let vehicle_system_instance = bytes[7] & 0xf;
        let industry_group = (bytes[7] >> 4) & 0x7;
        let arbitrary_address = bytes[7] >> 7;

        Name {
            identity_number,
            manufacturer_code,
            function_instance,
            ecu_instance,
            function,
            vehicle_system,
            vehicle_system_instance,
            industry_group,
            arbitrary_address: arbitrary_address != 0,
        }
    }
}

impl core::fmt::Display for Name {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            f,
            "Identity number: 0x{:X}; Manufacturer code: 0x{:X}; Function instance: 0x{:X}; ECU instance: 0x{:X}; Function: 0x{:X}; Vehicle system: 0x{:X}; Vehicle system instance: 0x{:X}; Industry group: {:X}; Arbitrary address: {}",
            self.identity_number,
            self.manufacturer_code,
            self.function_instance,
            self.ecu_instance,
            self.function,
            self.vehicle_system,
            self.vehicle_system_instance,
            self.industry_group,
            self.arbitrary_address
        )
    }
}

#[derive(Default)]
pub struct NameBuilder {
    identity_number: u32,
    manufacturer_code: u16,
    function_instance: u8,
    ecu_instance: u8,
    function: u8,
    vehicle_system: u8,
    vehicle_system_instance: u8,
    industry_group: u8,
    arbitrary_address: bool,
}

impl NameBuilder {
    /// Set the identity number.
    #[inline]
    #[must_use]
    pub fn identity_number(mut self, identity_number: u32) -> Self {
        self.identity_number = identity_number & 0x001f_ffff;
        self
    }

    /// Set the manufacturer code.
    #[inline]
    #[must_use]
    pub fn manufacturer_code(mut self, manufacturer_code: u16) -> Self {
        self.manufacturer_code = manufacturer_code & 0x7ff;
        self
    }

    /// Set the function instance.
    #[inline]
    #[must_use]
    pub fn function_instance(mut self, function_instance: u8) -> Self {
        self.function_instance = function_instance & 0x1f;
        self
    }

    /// Set the ECU instance.
    #[inline]
    #[must_use]
    pub fn ecu_instance(mut self, ecu_instance: u8) -> Self {
        self.ecu_instance = ecu_instance & 0x7;
        self
    }

    /// Set the function.
    #[inline]
    #[must_use]
    pub fn function(mut self, function: u8) -> Self {
        self.function = function;
        self
    }

    /// Set the vehicle system.
    #[inline]
    #[must_use]
    pub fn vehicle_system(mut self, vehicle_system: u8) -> Self {
        self.vehicle_system = vehicle_system & 0x7f;
        self
    }

    /// Set the vehicle system instance.
    #[inline]
    #[must_use]
    pub fn vehicle_system_instance(mut self, vehicle_system_instance: u8) -> Self {
        self.vehicle_system_instance = vehicle_system_instance & 0xf;
        self
    }

    /// Set the industry group.
    #[inline]
    #[must_use]
    pub fn industry_group(mut self, industry_group: u8) -> Self {
        self.industry_group = industry_group & 0x7;
        self
    }

    /// Set the arbitrary address.
    #[inline]
    #[must_use]
    pub fn arbitrary_address(mut self, arbitrary_address: bool) -> Self {
        self.arbitrary_address = arbitrary_address;
        self
    }

    /// Construct name.
    #[must_use]
    pub fn build(self) -> Name {
        Name {
            identity_number: self.identity_number,
            manufacturer_code: self.manufacturer_code,
            function_instance: self.function_instance,
            ecu_instance: self.ecu_instance,
            function: self.function,
            vehicle_system: self.vehicle_system,
            vehicle_system_instance: self.vehicle_system_instance,
            industry_group: self.industry_group,
            arbitrary_address: self.arbitrary_address,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_name_to_name() {
        let name = NameBuilder::default()
            .identity_number(0xB5D15)
            .manufacturer_code(0x623)
            .function_instance(30)
            .ecu_instance(0x7)
            .function(0xE3)
            .vehicle_system(126)
            .vehicle_system_instance(15)
            .industry_group(5)
            .arbitrary_address(true)
            .build();

        let name2 = Name::from_bytes(name.to_bytes());

        assert_eq!(name, name2);
        assert_eq!(name2.identity_number, 0xB5D15);
        assert_eq!(name2.manufacturer_code, 0x623);
        assert_eq!(name2.function_instance, 30);
        assert_eq!(name2.ecu_instance, 0x7);
        assert_eq!(name2.function, 0xE3);
        assert_eq!(name2.vehicle_system, 126);
        assert_eq!(name2.vehicle_system_instance, 15);
        assert_eq!(name2.industry_group, 5);
        assert!(name2.arbitrary_address);
    }

    #[test]
    fn test_to_bytes() {
        let name = NameBuilder::default()
            .identity_number(0xB0309)
            .manufacturer_code(0x122)
            .function_instance(0x2)
            .ecu_instance(0x1)
            .function(0x5)
            .vehicle_system(0x6)
            .vehicle_system_instance(0x5)
            .arbitrary_address(true)
            .build();

        let bytes = name.to_bytes();

        assert_eq!(bytes, [0x09, 0x03, 0x4B, 0x24, 0x11, 0x05, 0x0C, 0x85]);
    }

    #[test]
    fn test_from_bytes() {
        let bytes = [0x19, 0xA4, 0x49, 0x24, 0x11, 0x05, 0x0C, 0x85];

        let name = Name::from_bytes(bytes);

        assert_eq!(
            name,
            NameBuilder::default()
                .identity_number(0x9A419)
                .manufacturer_code(0x122)
                .function_instance(0x2)
                .ecu_instance(0x1)
                .function(0x5)
                .vehicle_system(0x6)
                .vehicle_system_instance(0x5)
                .arbitrary_address(true)
                .build()
        );
    }

    #[test]
    fn test_name_builder() {
        let name = NameBuilder::default()
            .identity_number(0x1)
            .manufacturer_code(0x717)
            .function_instance(1)
            .ecu_instance(1)
            .function(0x3A)
            .vehicle_system(9)
            .build();

        let bytes = name.to_bytes();

        assert_eq!(bytes, [0x01, 0x00, 0xE0, 0xE2, 0x09, 0x3A, 0x12, 0x00]);
    }
}