use core::fmt::Debug;
use automotive_wire_codec::{
read_u8, read_u16_be, read_u32_be, write_u8, write_u16_be, write_u32_be,
};
use super::message_error::MessageError;
use super::traits::{Decode, Encode};
#[derive(Clone, Copy, strum::Display, Eq, PartialEq)]
#[repr(u8)]
pub enum ProtocolVersion {
Reserved = 0x00,
V2010 = 0x01,
V2012 = 0x02,
V2019 = 0x03,
ReservedFuture(u8),
VehicleIdentificationRequest = 0xFF,
}
impl ProtocolVersion {
#[must_use]
pub fn inverse(&self) -> u8 {
match self {
ProtocolVersion::Reserved => 0xFF,
ProtocolVersion::V2010 => 0xFE,
ProtocolVersion::V2012 => 0xFD,
ProtocolVersion::V2019 => 0xFC,
ProtocolVersion::ReservedFuture(value) => !value,
ProtocolVersion::VehicleIdentificationRequest => 0x00,
}
}
}
impl From<u8> for ProtocolVersion {
fn from(value: u8) -> Self {
match value {
0x00 => ProtocolVersion::Reserved,
0x01 => ProtocolVersion::V2010,
0x02 => ProtocolVersion::V2012,
0x03 => ProtocolVersion::V2019,
0x04..=0xFE => ProtocolVersion::ReservedFuture(value),
0xFF => ProtocolVersion::VehicleIdentificationRequest,
}
}
}
impl From<ProtocolVersion> for u8 {
fn from(value: ProtocolVersion) -> Self {
match value {
ProtocolVersion::Reserved => 0x00,
ProtocolVersion::V2010 => 0x01,
ProtocolVersion::V2012 => 0x02,
ProtocolVersion::V2019 => 0x03,
ProtocolVersion::ReservedFuture(value) => value,
ProtocolVersion::VehicleIdentificationRequest => 0xFF,
}
}
}
impl Debug for ProtocolVersion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let val: u8 = (*self).into();
write!(f, "{self} ({val:#04X})")
}
}
#[derive(Clone, Copy, strum::Display, Eq, PartialEq)]
#[repr(u16)]
pub enum PayloadType {
NegativeAcknowledge = 0x0000,
VehicleIdentificationRequest = 0x0001,
VehicleIdentificationRequestWithEID = 0x0002,
VehicleIdentificationRequestWithVIN = 0x0003,
VehicleAnnouncement = 0x0004,
RoutingActivationRequest = 0x0005,
RoutingActivationResponse = 0x0006,
AliveCheckRequest = 0x0007,
AliveCheckResponse = 0x0008,
DoIPEntityStatusRequest = 0x4001,
DoIPEntityStatusResponse = 0x4002,
DiagnosticPowerModeInfoRequest = 0x4003,
DiagnosticPowerModeInfoResponse = 0x4004,
DiagnosticMessage = 0x8001,
DiagnosticMessagePositiveAcknowledge = 0x8002,
DiagnosticMessageNegativeAcknowledge = 0x8003,
Reserved(u16),
ReservedVehicleManufacturer(u16),
}
impl From<u16> for PayloadType {
fn from(value: u16) -> Self {
match value {
0x0000 => PayloadType::NegativeAcknowledge,
0x0001 => PayloadType::VehicleIdentificationRequest,
0x0002 => PayloadType::VehicleIdentificationRequestWithEID,
0x0003 => PayloadType::VehicleIdentificationRequestWithVIN,
0x0004 => PayloadType::VehicleAnnouncement,
0x0005 => PayloadType::RoutingActivationRequest,
0x0006 => PayloadType::RoutingActivationResponse,
0x0007 => PayloadType::AliveCheckRequest,
0x0008 => PayloadType::AliveCheckResponse,
0x4001 => PayloadType::DoIPEntityStatusRequest,
0x4002 => PayloadType::DoIPEntityStatusResponse,
0x4003 => PayloadType::DiagnosticPowerModeInfoRequest,
0x4004 => PayloadType::DiagnosticPowerModeInfoResponse,
0x8001 => PayloadType::DiagnosticMessage,
0x8002 => PayloadType::DiagnosticMessagePositiveAcknowledge,
0x8003 => PayloadType::DiagnosticMessageNegativeAcknowledge,
0xF000..=0xFFFF => PayloadType::ReservedVehicleManufacturer(value),
_ => PayloadType::Reserved(value),
}
}
}
impl From<PayloadType> for u16 {
fn from(value: PayloadType) -> Self {
match value {
PayloadType::NegativeAcknowledge => 0x0000,
PayloadType::VehicleIdentificationRequest => 0x0001,
PayloadType::VehicleIdentificationRequestWithEID => 0x0002,
PayloadType::VehicleIdentificationRequestWithVIN => 0x0003,
PayloadType::VehicleAnnouncement => 0x0004,
PayloadType::RoutingActivationRequest => 0x0005,
PayloadType::RoutingActivationResponse => 0x0006,
PayloadType::AliveCheckRequest => 0x0007,
PayloadType::AliveCheckResponse => 0x0008,
PayloadType::DoIPEntityStatusRequest => 0x4001,
PayloadType::DoIPEntityStatusResponse => 0x4002,
PayloadType::DiagnosticPowerModeInfoRequest => 0x4003,
PayloadType::DiagnosticPowerModeInfoResponse => 0x4004,
PayloadType::DiagnosticMessage => 0x8001,
PayloadType::DiagnosticMessagePositiveAcknowledge => 0x8002,
PayloadType::DiagnosticMessageNegativeAcknowledge => 0x8003,
PayloadType::Reserved(value) | PayloadType::ReservedVehicleManufacturer(value) => value,
}
}
}
impl Debug for PayloadType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let val: u16 = (*self).into();
write!(f, "{self} ({val:#04X})")
}
}
#[derive(Clone, PartialEq)]
pub struct Header {
pub protocol_version: ProtocolVersion,
pub inverse_protocol_version: u8,
pub payload_type: PayloadType,
pub payload_length: u32,
}
impl Header {
pub const SIZE: usize = 8;
#[must_use]
pub fn new(
protocol_version: ProtocolVersion,
payload_type: PayloadType,
payload_length: u32,
) -> Self {
let inverse_protocol_version = protocol_version.inverse();
Header {
protocol_version,
inverse_protocol_version,
payload_type,
payload_length,
}
}
pub(crate) fn version_inverse_correct(&self) -> Result<(), MessageError> {
let expected = self.protocol_version.inverse();
if expected == self.inverse_protocol_version {
Ok(())
} else {
Err(MessageError::VersionInverseIncorrect {
expected,
value: self.inverse_protocol_version,
})
}
}
}
impl<'a> Decode<'a> for Header {
type Error = MessageError;
fn decode(buf: &'a [u8]) -> Result<(Self, &'a [u8]), MessageError> {
let (protocol_version, rest) = read_u8(buf)?;
let protocol_version = protocol_version.into();
let (inverse_protocol_version, rest) = read_u8(rest)?;
let (payload_type, rest) = read_u16_be(rest)?;
let payload_type = payload_type.into();
let (payload_length, rest) = read_u32_be(rest)?;
let header = Header {
protocol_version,
inverse_protocol_version,
payload_type,
payload_length,
};
header.version_inverse_correct()?;
Ok((header, rest))
}
}
impl Encode for Header {
type Error = MessageError;
fn encoded_size(&self) -> Result<usize, MessageError> {
Ok(Header::SIZE)
}
fn encode(&self, writer: &mut impl embedded_io::Write) -> Result<usize, MessageError> {
write_u8(writer, self.protocol_version.into())?;
write_u8(writer, self.inverse_protocol_version)?;
write_u16_be(writer, self.payload_type.into())?;
write_u32_be(writer, self.payload_length)?;
Ok(Header::SIZE)
}
}
impl Debug for Header {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Header")
.field("Version", &self.protocol_version)
.field(
"inverse_protocol_version",
&format_args!("{:#X}", self.inverse_protocol_version),
)
.field("payload_type", &self.payload_type)
.field("payload_length", &self.payload_length)
.finish()
}
}