obd2_core/adapter/
detect.rs1use super::{AdapterInfo, Chipset, Capabilities};
4use crate::vehicle::Protocol;
5
6impl AdapterInfo {
7 pub fn detect(atz_response: &str, sti_response: Option<&str>) -> Self {
13 let (chipset, firmware) = if let Some(sti) = sti_response {
14 if sti.contains("STN") {
15 (Chipset::Stn, sti.trim().to_string())
16 } else {
17 detect_elm_version(atz_response)
18 }
19 } else {
20 detect_elm_version(atz_response)
21 };
22
23 let capabilities = match chipset {
24 Chipset::Stn => Capabilities {
25 can_clear_dtcs: true,
26 dual_can: true,
27 enhanced_diag: true,
28 battery_voltage: true,
29 adaptive_timing: true,
30 },
31 Chipset::Elm327Genuine => Capabilities {
32 can_clear_dtcs: true,
33 dual_can: false,
34 enhanced_diag: true,
35 battery_voltage: true,
36 adaptive_timing: true,
37 },
38 Chipset::Elm327Clone => Capabilities {
39 can_clear_dtcs: false,
40 dual_can: false,
41 enhanced_diag: false,
42 battery_voltage: true,
43 adaptive_timing: false,
44 },
45 Chipset::Unknown => Capabilities::default(),
46 };
47
48 Self {
49 chipset,
50 firmware,
51 protocol: Protocol::Auto,
52 capabilities,
53 }
54 }
55}
56
57fn detect_elm_version(response: &str) -> (Chipset, String) {
58 let response = response.trim();
59 if let Some(ver_start) = response.find("ELM327") {
60 let firmware = response[ver_start..].trim().to_string();
61 if let Some(v_pos) = firmware.find('v').or_else(|| firmware.find('V')) {
63 let ver_str = &firmware[v_pos + 1..];
64 let version: f32 = ver_str
65 .chars()
66 .take_while(|c| c.is_ascii_digit() || *c == '.')
67 .collect::<String>()
68 .parse()
69 .unwrap_or(0.0);
70
71 if version >= 2.0 {
72 (Chipset::Elm327Genuine, firmware)
73 } else {
74 (Chipset::Elm327Clone, firmware)
75 }
76 } else {
77 (Chipset::Elm327Clone, firmware)
78 }
79 } else {
80 (Chipset::Unknown, response.to_string())
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_detect_elm327_clone() {
90 let info = AdapterInfo::detect("ELM327 v1.5", None);
91 assert_eq!(info.chipset, Chipset::Elm327Clone);
92 assert!(!info.capabilities.can_clear_dtcs);
93 assert!(!info.capabilities.dual_can);
94 }
95
96 #[test]
97 fn test_detect_elm327_genuine() {
98 let info = AdapterInfo::detect("ELM327 v2.1", None);
99 assert_eq!(info.chipset, Chipset::Elm327Genuine);
100 assert!(info.capabilities.can_clear_dtcs);
101 assert!(info.capabilities.enhanced_diag);
102 }
103
104 #[test]
105 fn test_detect_stn() {
106 let info = AdapterInfo::detect("ELM327 v1.5", Some("STN2120"));
107 assert_eq!(info.chipset, Chipset::Stn);
108 assert!(info.capabilities.dual_can);
109 assert!(info.capabilities.enhanced_diag);
110 }
111
112 #[test]
113 fn test_detect_unknown() {
114 let info = AdapterInfo::detect("UNKNOWN DEVICE", None);
115 assert_eq!(info.chipset, Chipset::Unknown);
116 }
117
118 #[test]
119 fn test_detect_elm_with_garbage() {
120 let info = AdapterInfo::detect("\r\nELM327 v2.2\r\n>", None);
121 assert_eq!(info.chipset, Chipset::Elm327Genuine);
122 }
123
124 #[test]
125 fn test_capabilities_default() {
126 let caps = Capabilities::default();
127 assert!(!caps.can_clear_dtcs);
128 assert!(!caps.dual_can);
129 }
130
131 #[test]
132 fn test_stn_has_all_capabilities() {
133 let info = AdapterInfo::detect("ELM327 v1.5", Some("STN1110 v4.2"));
134 assert!(info.capabilities.can_clear_dtcs);
135 assert!(info.capabilities.dual_can);
136 assert!(info.capabilities.enhanced_diag);
137 assert!(info.capabilities.battery_voltage);
138 assert!(info.capabilities.adaptive_timing);
139 }
140}