Skip to main content

obd2_core/protocol/
enhanced.rs

1//! Enhanced manufacturer-specific PID types.
2
3use std::time::Instant;
4use crate::error::Obd2Error;
5
6/// Confidence level of diagnostic data from a vehicle spec.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize)]
8pub enum Confidence {
9    Verified,
10    Community,
11    Inferred,
12    Unverified,
13}
14
15/// How to decode raw response bytes into a Value.
16#[derive(Debug, Clone, serde::Deserialize)]
17#[non_exhaustive]
18pub enum Formula {
19    Linear { scale: f64, offset: f64 },
20    TwoByte { scale: f64, offset: f64 },
21    Centered { center: f64, divisor: f64 },
22    Bitmask { bits: Vec<(u8, String)> },
23    Enumerated { values: Vec<(u8, String)> },
24    Expression(String),
25}
26
27/// Enhanced manufacturer-specific PID (Mode 21/22).
28#[derive(Debug, Clone, serde::Deserialize)]
29pub struct EnhancedPid {
30    pub service_id: u8,
31    pub did: u16,
32    pub name: String,
33    pub unit: String,
34    pub formula: Formula,
35    pub bytes: u8,
36    pub module: String,
37    pub value_type: super::pid::ValueType,
38    pub confidence: Confidence,
39    pub command_suffix: Option<Vec<u8>>,
40}
41
42/// A decoded value from an OBD-II response.
43#[derive(Debug, Clone)]
44#[non_exhaustive]
45pub enum Value {
46    Scalar(f64),
47    Bitfield(Bitfield),
48    State(String),
49    Raw(Vec<u8>),
50}
51
52impl Value {
53    pub fn as_f64(&self) -> Result<f64, Obd2Error> {
54        match self {
55            Value::Scalar(v) => Ok(*v),
56            _ => Err(Obd2Error::ParseError("expected scalar value".into())),
57        }
58    }
59
60    pub fn as_bitfield(&self) -> Result<&Bitfield, Obd2Error> {
61        match self {
62            Value::Bitfield(b) => Ok(b),
63            _ => Err(Obd2Error::ParseError("expected bitfield value".into())),
64        }
65    }
66}
67
68/// A set of named boolean flags decoded from raw bytes.
69#[derive(Debug, Clone)]
70pub struct Bitfield {
71    pub raw: u32,
72    pub flags: Vec<(String, bool)>,
73}
74
75/// Source of a reading.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum ReadingSource {
78    Live,
79    FreezeFrame,
80    Replay,
81}
82
83/// A single reading from the vehicle.
84#[derive(Debug, Clone)]
85pub struct Reading {
86    pub value: Value,
87    pub unit: &'static str,
88    pub timestamp: Instant,
89    pub raw_bytes: Vec<u8>,
90    pub source: ReadingSource,
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_value_as_f64() {
99        let v = Value::Scalar(42.0);
100        assert_eq!(v.as_f64().unwrap(), 42.0);
101    }
102
103    #[test]
104    fn test_value_as_f64_error_on_bitfield() {
105        let v = Value::Bitfield(Bitfield { raw: 0xFF, flags: vec![] });
106        assert!(v.as_f64().is_err());
107    }
108
109    #[test]
110    fn test_value_as_bitfield() {
111        let bf = Bitfield { raw: 0xAB, flags: vec![("test".into(), true)] };
112        let v = Value::Bitfield(bf);
113        let result = v.as_bitfield().unwrap();
114        assert_eq!(result.raw, 0xAB);
115    }
116
117    #[test]
118    fn test_reading_source_equality() {
119        assert_ne!(ReadingSource::Live, ReadingSource::FreezeFrame);
120        assert_ne!(ReadingSource::Live, ReadingSource::Replay);
121    }
122}