iso13400_2/
common.rs

1use crate::{constants::*, error::Error, request, response, utils, PayloadType};
2use getset::{CopyGetters, Getters};
3use std::fmt::{Display, Formatter};
4
5/// Table 16 — Generic DoIP header structure at line #48(ISO 13400-2-2019)
6#[repr(u8)]
7#[derive(Debug, Copy, Clone, Eq, Default, PartialEq)]
8pub enum Version {
9    ISO13400_2_2010 = 0x01,
10    ISO13400_2_2012 = 0x02,
11    ISO13400_2_2019 = 0x03,
12    Reserved(u8),
13    #[default]
14    Default = 0xFF,
15}
16
17impl From<Version> for u8 {
18    fn from(val: Version) -> Self {
19        match val {
20            Version::ISO13400_2_2010 => 0x01,
21            Version::ISO13400_2_2012 => 0x02,
22            Version::ISO13400_2_2019 => 0x03,
23            Version::Default => 0xFF,
24            Version::Reserved(v) => v,
25        }
26    }
27}
28
29impl From<Version> for Vec<u8> {
30    fn from(val: Version) -> Self {
31        let version: u8 = val.into();
32        vec![version, !version]
33    }
34}
35
36impl From<u8> for Version {
37    fn from(version: u8) -> Self {
38        match version {
39            0x01 => Self::ISO13400_2_2010,
40            0x02 => Self::ISO13400_2_2012,
41            0x03 => Self::ISO13400_2_2019,
42            0xFF => Self::Default,
43            _ => {
44                rsutil::warn!("ISO 13400-2 - used reserved version: {}", version);
45                Self::Reserved(version)
46            }
47        }
48    }
49}
50
51impl TryFrom<&[u8]> for Version {
52    type Error = Error;
53    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
54        let data_len = data.len();
55        if data_len < SIZE_OF_VERSION {
56            return Err(Error::InvalidLength {
57                actual: data_len,
58                expected: SIZE_OF_VERSION,
59            });
60        }
61
62        let version = data[0];
63        let reverse = data[1];
64        if !version != reverse {
65            return Err(Error::InvalidVersion { version, reverse });
66        }
67
68        Ok(Self::from(version))
69    }
70}
71
72/// Table 19 — Generic DoIP header NACK codes at line #52(ISO 13400-2-2019)
73#[repr(u8)]
74#[derive(Debug, Copy, Clone, Eq, PartialEq)]
75pub enum HeaderNegativeCode {
76    IncorrectPatternFormat = 0x00, // close socket
77    UnknownPayloadTYpe = 0x01,
78    MessageTooLarge = 0x02,
79    OutOfMemory = 0x03,
80    InvalidPayloadLength = 0x04, // close socket
81    Reserved(u8),
82}
83
84impl From<HeaderNegativeCode> for u8 {
85    fn from(val: HeaderNegativeCode) -> Self {
86        match val {
87            HeaderNegativeCode::IncorrectPatternFormat => 0x00,
88            HeaderNegativeCode::UnknownPayloadTYpe => 0x01,
89            HeaderNegativeCode::MessageTooLarge => 0x02,
90            HeaderNegativeCode::OutOfMemory => 0x03,
91            HeaderNegativeCode::InvalidPayloadLength => 0x04,
92            HeaderNegativeCode::Reserved(code) => code,
93        }
94    }
95}
96
97impl From<u8> for HeaderNegativeCode {
98    fn from(code: u8) -> Self {
99        match code {
100            0x00 => Self::IncorrectPatternFormat,
101            0x01 => Self::UnknownPayloadTYpe,
102            0x02 => Self::MessageTooLarge,
103            0x03 => Self::OutOfMemory,
104            0x04 => Self::InvalidPayloadLength,
105            _ => {
106                rsutil::warn!("ISO 13400-2 - used reserved header negative code: {}", code);
107                Self::Reserved(code)
108            }
109        }
110    }
111}
112
113/// Table 13 — Logical address overview at line #37(ISO 13400-2-2019)
114///
115/// 0000         ISO/SAE reserved
116///
117/// 0001 to 0DFF VM specific
118/// 0E00 to 0FFF reserved for addresses of client
119///
120/// 0E00 to 0E7F external legislated diagnostics test equipment (e.g. for emissions external test equipment)
121///              When using these addresses in the routing activation request other ongoing diagnostic communication in the vehicle may be interrupted and other normal functionality may be impaired (e.g. return to a failsafe behaviour).
122///
123/// 0E80 to 0EFF external vehicle-manufacturer-/aftermarket-enhanced diagnostics test equipment
124///              When using these addresses in the routing activation request and diagnostic messages the routing activation may be delayed initially due to other ongoing diagnostic communication, which may then be interrupted and other normal functionality may also be impaired (e.g. return to a failsafe behaviour).
125///
126/// 0F00 to 0F7F internal data collection/on-board diagnostic equipment (for vehicle-manufacturer use only)
127///              These addresses should not be used by client DoIP entity that is not designed as an integral part of the vehicle. This includes any plug-in equipment that performs diagnostic communication through the diagnostic connector.
128///
129/// 0F80 to 0FFF external prolonged data collection equipment (vehicle data recorders and loggers, e.g. used by insurance companies or to collect vehicle fleet data)
130///              These addresses should be used by equipment that is installed in the vehicle and remains in the vehicle for periodic data retrieval by means of diagnostic communication. The DoIP entities may deny/delay accepting a routing activation request from this type of equipment in order to complete ongoing vehicle internal communication to avoid that normal operation of the vehicle may be impaired.
131///
132/// 1000 to 7FFF VM specific
133///
134/// 8000 to CFFF ISO/SAE reserved
135///
136/// D000 to DFFF Reserved for SAE Truck & Bus Control and Communication Committee
137///
138/// E000 to E3FF Definition of logical address is specified in use case-specific standard(e.g. ISO 27145-1, ISO 20730-1).
139///
140/// E400 to EFFF vehicle-manufacturer-defined functional group logical addresses
141///
142/// F000 to FFFF ISO/SAE reserved
143#[repr(u8)]
144#[derive(Debug, Copy, Clone, Eq, PartialEq)]
145pub enum LogicAddress {
146    VMSpecific(u16),           // 0x0001 ~ 0x0DFF | 0x1000 ~ 0x7FFF
147    Client(u16),               // 0x0E00 ~ 0x0FFF
148    VMSpecificFunctional(u16), // 0xE400 ~ 0xEFFF (functional group logical addresses)
149    Reserved(u16),             // 0x0000 | 0xE000 ~ 0xE3FF | 0xF000 ~ 0xFFFF
150}
151
152impl From<u16> for LogicAddress {
153    fn from(v: u16) -> Self {
154        match v {
155            0x0001..=0x0DFF | 0x1000..=0x7FFF => Self::VMSpecific(v),
156            0x0E00..=0x0FFF => Self::Client(v),
157            0xE400..=0xEFFF => Self::VMSpecificFunctional(v),
158            _ => {
159                rsutil::warn!("ISO 13400-2 - used reserved logic address: {}", v);
160                Self::Reserved(v)
161            }
162        }
163    }
164}
165
166impl From<LogicAddress> for u16 {
167    fn from(val: LogicAddress) -> Self {
168        match val {
169            LogicAddress::VMSpecific(v) => v,
170            LogicAddress::Client(v) => v,
171            LogicAddress::VMSpecificFunctional(v) => v,
172            LogicAddress::Reserved(v) => v,
173        }
174    }
175}
176
177impl Display for LogicAddress {
178    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
179        let value: u16 = (*self).into();
180        write!(f, "{:#X}", value)
181    }
182}
183
184/// Table 11 — DoIP entity status response
185#[repr(u8)]
186#[derive(Debug, Copy, Clone, Eq, PartialEq)]
187pub enum NodeType {
188    Gateway = 0x00,
189    Node = 0x01,
190    Reserved(u8),
191}
192
193impl From<NodeType> for u8 {
194    fn from(val: NodeType) -> Self {
195        match val {
196            NodeType::Gateway => 0x00,
197            NodeType::Node => 0x01,
198            NodeType::Reserved(v) => v,
199        }
200    }
201}
202
203impl From<u8> for NodeType {
204    fn from(v: u8) -> Self {
205        match v {
206            0x00 => Self::Gateway,
207            0x01 => Self::Node,
208            _ => {
209                rsutil::warn!("ISO 13400-2 - used reserved entity: {}", v);
210                Self::Reserved(v)
211            }
212        }
213    }
214}
215
216impl Display for NodeType {
217    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
218        match self {
219            NodeType::Gateway => write!(f, "Gateway"),
220            NodeType::Node => write!(f, "Node"),
221            NodeType::Reserved(v) => write!(f, "{}", format_args!("Unknown({:#X})", *v)),
222        }
223    }
224}
225
226/// Table 6 — Definition of further action code values
227#[repr(u8)]
228#[derive(Debug, Copy, Clone, Eq, PartialEq)]
229pub enum FurtherAction {
230    NoAction = 0x00,
231    Reserved(u8), // 0x01 ~ 0x0f
232    CentralSecurity = 0x10,
233    VMSpecific(u8), // 0x11 ~ 0xFF
234}
235
236impl From<FurtherAction> for u8 {
237    fn from(val: FurtherAction) -> Self {
238        match val {
239            FurtherAction::NoAction => 0x00,
240            FurtherAction::Reserved(v) => v,
241            FurtherAction::CentralSecurity => 0x10,
242            FurtherAction::VMSpecific(v) => v,
243        }
244    }
245}
246
247impl From<u8> for FurtherAction {
248    fn from(v: u8) -> Self {
249        match v {
250            0x00 => Self::NoAction,
251            0x01..=0x0F => {
252                rsutil::warn!("ISO 13400-2 - used reserved further action: {}", v);
253                Self::Reserved(v)
254            }
255            0x10 => Self::CentralSecurity,
256            _ => Self::VMSpecific(v),
257        }
258    }
259}
260
261/// Table 7 — Definition of VIN/GID synchronization status code values
262#[repr(u8)]
263#[derive(Debug, Copy, Clone, Eq, PartialEq)]
264pub enum SyncStatus {
265    VINorGIDSync = 0x00,
266    VINorGIDNotSync = 0x10,
267    Reserved(u8),
268}
269
270impl From<SyncStatus> for u8 {
271    fn from(val: SyncStatus) -> Self {
272        match val {
273            SyncStatus::VINorGIDSync => 0x00,
274            SyncStatus::VINorGIDNotSync => 0x10,
275            SyncStatus::Reserved(v) => v,
276        }
277    }
278}
279
280impl From<u8> for SyncStatus {
281    fn from(v: u8) -> Self {
282        match v {
283            0x00 => Self::VINorGIDSync,
284            0x10 => Self::VINorGIDNotSync,
285            _ => {
286                rsutil::warn!("ISO 13400-2 - used reserved VIN/GID sync. status: {}", v);
287                Self::Reserved(v)
288            }
289        }
290    }
291}
292
293/// Table 49 — Routing activation response code values
294#[repr(u8)]
295#[derive(Debug, Copy, Clone, Eq, PartialEq)]
296pub enum ActiveCode {
297    SourceAddressUnknown = 0x00, // close TCP
298    Activated = 0x01,            // close TCP
299    SourceAddressInvalid = 0x02, // close TCP
300    SocketInvalid = 0x03,        // close TCP
301    WithoutAuth = 0x04,
302    VehicleRefused = 0x05, // close TCP
303    Unsupported = 0x06,    // close TCP
304    /// ISO 14300-2:2019
305    TLSRequired = 0x07,
306    Success = 0x10,
307    NeedConfirm = 0x11,
308    VMSpecific(u8), // 0xE0 ~ 0xFE
309    Reserved(u8),   // 0x07 ~ 0x0F | 0x12 ~ 0xDF | 0xFF
310}
311
312impl From<ActiveCode> for u8 {
313    fn from(val: ActiveCode) -> Self {
314        match val {
315            ActiveCode::SourceAddressUnknown => 0x00,
316            ActiveCode::Activated => 0x01,
317            ActiveCode::SourceAddressInvalid => 0x02,
318            ActiveCode::SocketInvalid => 0x03,
319            ActiveCode::WithoutAuth => 0x04,
320            ActiveCode::VehicleRefused => 0x05,
321            ActiveCode::Unsupported => 0x06,
322            ActiveCode::TLSRequired => 0x07,
323            ActiveCode::Success => 0x10,
324            ActiveCode::NeedConfirm => 0x11,
325            ActiveCode::VMSpecific(v) => v,
326            ActiveCode::Reserved(v) => v,
327        }
328    }
329}
330
331impl From<u8> for ActiveCode {
332    fn from(v: u8) -> Self {
333        match v {
334            0x00 => Self::SourceAddressUnknown,
335            0x01 => Self::Activated,
336            0x02 => Self::SourceAddressInvalid,
337            0x03 => Self::SocketInvalid,
338            0x04 => Self::WithoutAuth,
339            0x05 => Self::VehicleRefused,
340            0x06 => Self::Unsupported,
341            0x07 => Self::TLSRequired,
342            0x10 => Self::Success,
343            0x11 => Self::NeedConfirm,
344            0xE0..=0xFE => Self::VMSpecific(v),
345            _ => {
346                rsutil::warn!("ISO 13400-2 - used reserved active code: {}", v);
347                Self::Reserved(v)
348            }
349        }
350    }
351}
352
353/// Table 9 — Diagnostic power mode information response
354#[repr(u8)]
355#[derive(Debug, Copy, Clone, Eq, PartialEq)]
356pub enum PowerMode {
357    NotReady = 0x00,
358    Ready = 0x01,
359    NotSupported = 0x02,
360    Reserved(u8),
361}
362
363impl From<PowerMode> for u8 {
364    fn from(val: PowerMode) -> Self {
365        match val {
366            PowerMode::NotReady => 0x00,
367            PowerMode::Ready => 0x01,
368            PowerMode::NotSupported => 0x02,
369            PowerMode::Reserved(v) => v,
370        }
371    }
372}
373
374impl From<u8> for PowerMode {
375    fn from(v: u8) -> Self {
376        match v {
377            0x00 => Self::NotReady,
378            0x01 => Self::Ready,
379            0x02 => Self::NotSupported,
380            _ => {
381                rsutil::warn!("ISO 13400-2 - used reserved power mode: {}", v);
382                Self::Reserved(v)
383            }
384        }
385    }
386}
387
388/// Table 47 — Routing activation request activation types
389#[repr(u8)]
390#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
391pub enum RoutingActiveType {
392    #[default]
393    Default = 0x00,
394    WWHODB = 0x01,
395    Reserved(u8), // 0x02 ~ 0xDF
396    CentralSecurity = 0xE0,
397    VMSpecific(u8), // 0xE1 ~ 0xFF
398}
399
400impl From<RoutingActiveType> for u8 {
401    fn from(val: RoutingActiveType) -> Self {
402        match val {
403            RoutingActiveType::Default => 0x00,
404            RoutingActiveType::WWHODB => 0x01,
405            RoutingActiveType::Reserved(v) => v,
406            RoutingActiveType::CentralSecurity => 0xE0,
407            RoutingActiveType::VMSpecific(v) => v,
408        }
409    }
410}
411
412impl From<u8> for RoutingActiveType {
413    fn from(v: u8) -> Self {
414        match v {
415            0x00 => Self::Default,
416            0x01 => Self::WWHODB,
417            0x02..=0xDF => {
418                rsutil::warn!("ISO 13400-2 - used reserved routing active type: {}", v);
419                Self::Reserved(v)
420            }
421            0xE0 => Self::CentralSecurity,
422            _ => Self::VMSpecific(v),
423        }
424    }
425}
426
427/// Table 24 — Diagnostic message positive acknowledge codes
428#[repr(u8)]
429#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
430pub enum DiagnosticPositiveCode {
431    #[default]
432    Confirm = 0x00,
433    Reserved(u8),
434}
435
436impl From<DiagnosticPositiveCode> for u8 {
437    fn from(val: DiagnosticPositiveCode) -> Self {
438        match val {
439            DiagnosticPositiveCode::Confirm => 0x00,
440            DiagnosticPositiveCode::Reserved(v) => v,
441        }
442    }
443}
444
445impl From<u8> for DiagnosticPositiveCode {
446    fn from(v: u8) -> Self {
447        match v {
448            0x00 => Self::Confirm,
449            _ => {
450                rsutil::warn!(
451                    "ISO 13400-2 - used reserved diagnostic positive code: {}",
452                    v
453                );
454                Self::Reserved(v)
455            }
456        }
457    }
458}
459
460impl Display for DiagnosticPositiveCode {
461    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
462        match self {
463            Self::Confirm => write!(f, "Diagnostic Positive Confirm"),
464            Self::Reserved(v) => write!(f, "Diagnostic Positive Reserved({})", v),
465        }
466    }
467}
468
469/// Table 26 — Diagnostic message negative acknowledge codes
470#[repr(u8)]
471#[derive(Debug, Copy, Clone, Eq, PartialEq)]
472pub enum DiagnosticNegativeCode {
473    InvalidSourceAddress = 0x02,
474    UnknownTargetAddress = 0x03,
475    DiagnosticMessageTooLarge = 0x04,
476    OutOfMemory = 0x05,
477    TargetUnreachable = 0x06,
478    UnknownNetwork = 0x07,
479    TransportProtocolError = 0x08,
480    Reserved(u8),
481}
482
483impl From<DiagnosticNegativeCode> for u8 {
484    fn from(val: DiagnosticNegativeCode) -> Self {
485        match val {
486            DiagnosticNegativeCode::InvalidSourceAddress => 0x02,
487            DiagnosticNegativeCode::UnknownTargetAddress => 0x03,
488            DiagnosticNegativeCode::DiagnosticMessageTooLarge => 0x04,
489            DiagnosticNegativeCode::OutOfMemory => 0x05,
490            DiagnosticNegativeCode::TargetUnreachable => 0x06,
491            DiagnosticNegativeCode::UnknownNetwork => 0x07,
492            DiagnosticNegativeCode::TransportProtocolError => 0x08,
493            DiagnosticNegativeCode::Reserved(v) => v,
494        }
495    }
496}
497
498impl From<u8> for DiagnosticNegativeCode {
499    fn from(v: u8) -> Self {
500        match v {
501            0x02 => Self::InvalidSourceAddress,
502            0x03 => Self::UnknownTargetAddress,
503            0x04 => Self::DiagnosticMessageTooLarge,
504            0x05 => Self::OutOfMemory,
505            0x06 => Self::TargetUnreachable,
506            0x07 => Self::UnknownNetwork,
507            0x08 => Self::TransportProtocolError,
508            _ => {
509                rsutil::warn!(
510                    "ISO 13400-2 - used reserved diagnostic negative code: {}",
511                    v
512                );
513                Self::Reserved(v)
514            }
515        }
516    }
517}
518
519impl Display for DiagnosticNegativeCode {
520    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
521        match self {
522            Self::InvalidSourceAddress => write!(f, "Diagnostic Negative Source Address"),
523            Self::UnknownTargetAddress => write!(f, "Diagnostic Negative Target Address"),
524            Self::DiagnosticMessageTooLarge => {
525                write!(f, "Diagnostic Negative Diagnostic Message Too Large")
526            }
527            Self::OutOfMemory => write!(f, "Diagnostic Negative Target Address"),
528            Self::TargetUnreachable => write!(f, "Diagnostic Negative Target Address"),
529            Self::UnknownNetwork => write!(f, "Diagnostic Negative Target Address"),
530            Self::TransportProtocolError => {
531                write!(f, "Diagnostic Negative Transport Protocol Error")
532            }
533            Self::Reserved(v) => write!(f, "Diagnostic Negative Reserved({})", v),
534        }
535    }
536}
537
538/// The first response is 0x8002 if diagnostic is positive,
539/// that means diagnostic request was received,
540/// then send 0x8001 response with UDS data.
541/// Otherwise, send 0x8003 response with UDS NRC data.
542#[derive(Debug, Clone, Eq, PartialEq, Getters, CopyGetters)]
543pub struct Diagnostic {
544    // 0x8001
545    #[getset(get_copy = "pub")]
546    pub(crate) dst_addr: LogicAddress,
547    #[getset(get_copy = "pub")]
548    pub(crate) src_addr: LogicAddress,
549    #[getset(get = "pub")]
550    pub data: Vec<u8>,
551}
552
553impl Diagnostic {
554    pub fn new(dst_addr: LogicAddress, src_addr: LogicAddress, data: Vec<u8>) -> Self {
555        Self {
556            dst_addr,
557            src_addr,
558            data,
559        }
560    }
561
562    /// min length
563    #[inline]
564    const fn length() -> usize {
565        SIZE_OF_ADDRESS + SIZE_OF_ADDRESS
566    }
567}
568
569impl TryFrom<&[u8]> for Diagnostic {
570    type Error = Error;
571    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
572        let (_, mut offset) = utils::data_len_check(data, Self::length(), false)?;
573        let dst_addr =
574            u16::from_be_bytes(data[offset..offset + SIZE_OF_ADDRESS].try_into().unwrap());
575        offset += SIZE_OF_ADDRESS;
576        let dst_addr = LogicAddress::from(dst_addr);
577        let src_addr =
578            u16::from_be_bytes(data[offset..offset + SIZE_OF_ADDRESS].try_into().unwrap());
579        offset += SIZE_OF_ADDRESS;
580        let src_addr = LogicAddress::from(src_addr);
581        let data = data[offset..].to_vec();
582
583        Ok(Self::new(dst_addr, src_addr, data))
584    }
585}
586
587impl From<Diagnostic> for Vec<u8> {
588    fn from(mut val: Diagnostic) -> Self {
589        let mut result = TCP_DIAGNOSTIC.to_be_bytes().to_vec();
590        let length = (Diagnostic::length() + val.data.len()) as u32;
591        result.extend(length.to_be_bytes());
592        let dst_addr: u16 = val.dst_addr.into();
593        result.extend(dst_addr.to_be_bytes());
594        let src_addr: u16 = val.src_addr.into();
595        result.extend(src_addr.to_be_bytes());
596        result.append(&mut val.data);
597
598        result
599    }
600}
601
602/// Table 17 — Overview of DoIP payload types at line #49(ISO 13400-2-2019)
603#[derive(Debug, Clone)]
604pub enum Payload {
605    RespHeaderNegative(response::HeaderNegative), // UDP/TCP 0x0000
606    ReqVehicleId(request::VehicleID),             // UDP 0x0001
607    ReqVehicleWithEid(request::VehicleIDWithEID), // UDP 0x0002
608    ReqVehicleWithVIN(request::VehicleIDWithVIN), // UDP 0x0003
609    RespVehicleId(response::VehicleID),           // UDP 0x0004
610    ReqRoutingActive(request::RoutingActive),     // TCP 0x0005
611    RespRoutingActive(response::RoutingActive),   // TCP 0x0006
612    ReqAliveCheck(request::AliveCheck),           // TCP 0x0007
613    RespAliveCheck(response::AliveCheck),         // TCP 0x0008
614    ReqEntityStatus(request::EntityStatus),       // UDP 0x4001
615    ReqDiagPowerMode(request::DiagnosticPowerMode), // UDP 0x4003
616    RespEntityStatus(response::EntityStatus),     // UDP 0x4002
617    RespDiagPowerMode(response::DiagnosticPowerMode), // UDP 0x4004
618    Diagnostic(Diagnostic),                       // TCP 0x8001
619    RespDiagPositive(response::DiagnosticPositive), // TCP 0x8002
620    RespDiagNegative(response::DiagnosticNegative), // TCP 0x8003
621}
622
623impl Payload {
624    pub fn payload_type(&self) -> PayloadType {
625        match &self {
626            Payload::RespHeaderNegative(_) => PayloadType::RespHeaderNegative,
627            Payload::ReqVehicleId(_) => PayloadType::ReqVehicleId,
628            Payload::ReqVehicleWithEid(_) => PayloadType::ReqVehicleWithEid,
629            Payload::ReqVehicleWithVIN(_) => PayloadType::ReqVehicleWithVIN,
630            Payload::RespVehicleId(_) => PayloadType::RespVehicleId,
631            Payload::ReqRoutingActive(_) => PayloadType::ReqRoutingActive,
632            Payload::RespRoutingActive(_) => PayloadType::RespRoutingActive,
633            Payload::ReqAliveCheck(_) => PayloadType::ReqAliveCheck,
634            Payload::RespAliveCheck(_) => PayloadType::RespAliveCheck,
635            Payload::ReqEntityStatus(_) => PayloadType::ReqEntityStatus,
636            Payload::ReqDiagPowerMode(_) => PayloadType::ReqDiagPowerMode,
637            Payload::RespEntityStatus(_) => PayloadType::RespEntityStatus,
638            Payload::RespDiagPowerMode(_) => PayloadType::RespDiagPowerMode,
639            Payload::Diagnostic(_) => PayloadType::Diagnostic,
640            Payload::RespDiagPositive(_) => PayloadType::RespDiagPositive,
641            Payload::RespDiagNegative(_) => PayloadType::RespDiagNegative,
642        }
643    }
644}
645
646#[derive(Debug, Clone)]
647pub struct Message {
648    pub version: Version,
649    pub payload: Payload,
650}
651
652impl From<Message> for Vec<u8> {
653    fn from(val: Message) -> Self {
654        let mut result: Vec<_> = val.version.into();
655        match val.payload {
656            Payload::RespHeaderNegative(v) => result.append(&mut v.into()),
657            Payload::ReqVehicleId(v) => result.append(&mut v.into()),
658            Payload::ReqVehicleWithEid(v) => result.append(&mut v.into()),
659            Payload::ReqVehicleWithVIN(v) => result.append(&mut v.into()),
660            Payload::RespVehicleId(v) => result.append(&mut v.into()),
661            Payload::ReqRoutingActive(v) => result.append(&mut v.into()),
662            Payload::RespRoutingActive(v) => result.append(&mut v.into()),
663            Payload::ReqAliveCheck(v) => result.append(&mut v.into()),
664            Payload::RespAliveCheck(v) => result.append(&mut v.into()),
665            Payload::ReqEntityStatus(v) => result.append(&mut v.into()),
666            Payload::ReqDiagPowerMode(v) => result.append(&mut v.into()),
667            Payload::RespEntityStatus(v) => result.append(&mut v.into()),
668            Payload::RespDiagPowerMode(v) => result.append(&mut v.into()),
669            Payload::Diagnostic(v) => result.append(&mut v.into()),
670            Payload::RespDiagPositive(v) => result.append(&mut v.into()),
671            Payload::RespDiagNegative(v) => result.append(&mut v.into()),
672        }
673
674        result
675    }
676}
677
678impl TryFrom<&[u8]> for Message {
679    type Error = Error;
680    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
681        rsutil::debug!("ISO 13400-2 - parsing data: {}", hex::encode(data));
682        let data_len = data.len();
683        let expected = SIZE_OF_VERSION + SIZE_OF_DATA_TYPE + SIZE_OF_LENGTH;
684        if data_len < expected {
685            return Err(Error::InvalidLength {
686                actual: data_len,
687                expected,
688            });
689        }
690
691        let mut offset = 0;
692        let version = Version::try_from(data)?;
693        offset += SIZE_OF_VERSION;
694        let payload_type =
695            u16::from_be_bytes(data[offset..offset + SIZE_OF_DATA_TYPE].try_into().unwrap());
696        offset += SIZE_OF_DATA_TYPE;
697        let payload_len =
698            u32::from_be_bytes(data[offset..offset + SIZE_OF_LENGTH].try_into().unwrap());
699        offset += SIZE_OF_LENGTH;
700        let expected = data_len - offset;
701        if (payload_len as usize) != expected {
702            return Err(Error::InvalidPayloadLength {
703                actual: payload_len as usize,
704                expected,
705            });
706        }
707        let payload = match PayloadType::try_from(payload_type)? {
708            PayloadType::RespHeaderNegative => {
709                Payload::RespHeaderNegative(response::HeaderNegative::try_from(&data[offset..])?)
710            }
711            PayloadType::ReqVehicleId => {
712                Payload::ReqVehicleId(request::VehicleID::try_from(&data[offset..])?)
713            }
714            PayloadType::ReqVehicleWithEid => {
715                Payload::ReqVehicleWithEid(request::VehicleIDWithEID::try_from(&data[offset..])?)
716            }
717            PayloadType::ReqVehicleWithVIN => {
718                Payload::ReqVehicleWithVIN(request::VehicleIDWithVIN::try_from(&data[offset..])?)
719            }
720            PayloadType::RespVehicleId => {
721                Payload::RespVehicleId(response::VehicleID::try_from(&data[offset..])?)
722            }
723            PayloadType::ReqRoutingActive => {
724                Payload::ReqRoutingActive(request::RoutingActive::try_from(&data[offset..])?)
725            }
726            PayloadType::RespRoutingActive => {
727                Payload::RespRoutingActive(response::RoutingActive::try_from(&data[offset..])?)
728            }
729            PayloadType::ReqAliveCheck => {
730                Payload::ReqAliveCheck(request::AliveCheck::try_from(&data[offset..])?)
731            }
732            PayloadType::RespAliveCheck => {
733                Payload::RespAliveCheck(response::AliveCheck::try_from(&data[offset..])?)
734            }
735            PayloadType::ReqEntityStatus => {
736                Payload::ReqEntityStatus(request::EntityStatus::try_from(&data[offset..])?)
737            }
738            PayloadType::RespEntityStatus => {
739                Payload::RespEntityStatus(response::EntityStatus::try_from(&data[offset..])?)
740            }
741            PayloadType::ReqDiagPowerMode => {
742                Payload::ReqDiagPowerMode(request::DiagnosticPowerMode::try_from(&data[offset..])?)
743            }
744            PayloadType::RespDiagPowerMode => Payload::RespDiagPowerMode(
745                response::DiagnosticPowerMode::try_from(&data[offset..])?,
746            ),
747            PayloadType::Diagnostic => Payload::Diagnostic(Diagnostic::try_from(&data[offset..])?),
748            PayloadType::RespDiagPositive => {
749                Payload::RespDiagPositive(response::DiagnosticPositive::try_from(&data[offset..])?)
750            }
751            PayloadType::RespDiagNegative => {
752                Payload::RespDiagNegative(response::DiagnosticNegative::try_from(&data[offset..])?)
753            }
754        };
755
756        Ok(Self { version, payload })
757    }
758}