1use std::collections::HashSet;
4use async_trait::async_trait;
5use crate::error::Obd2Error;
6use crate::protocol::pid::Pid;
7use crate::protocol::dtc::Dtc;
8use crate::protocol::service::ServiceRequest;
9use crate::vehicle::Protocol;
10use super::{Adapter, AdapterInfo, Chipset, Capabilities};
11
12#[derive(Debug)]
16pub struct MockAdapter {
17 info: AdapterInfo,
18 vin: String,
19 dtcs: Vec<Dtc>,
20 initialized: bool,
21 supported: HashSet<Pid>,
22}
23
24impl MockAdapter {
25 pub fn new() -> Self {
27 Self::with_vin("1GCHK23224F000001") }
29
30 pub fn with_vin(vin: &str) -> Self {
32 let mut supported = HashSet::new();
33 for pid in &[
35 0x00u8, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x0B, 0x0C, 0x0D,
36 0x0E, 0x0F, 0x10, 0x11, 0x1C, 0x1F, 0x20, 0x21, 0x2C, 0x2F,
37 0x31, 0x33, 0x40, 0x42, 0x43, 0x44, 0x46, 0x5C, 0x5E,
38 0x60, 0x61, 0x62, 0x63,
39 ] {
40 supported.insert(Pid(*pid));
41 }
42
43 Self {
44 info: AdapterInfo {
45 chipset: Chipset::Elm327Genuine,
46 firmware: "MockAdapter v1.0".to_string(),
47 protocol: Protocol::Can11Bit500,
48 capabilities: Capabilities {
49 can_clear_dtcs: true,
50 dual_can: false,
51 enhanced_diag: true,
52 battery_voltage: true,
53 adaptive_timing: true,
54 },
55 },
56 vin: vin.to_string(),
57 dtcs: Vec::new(),
58 initialized: false,
59 supported,
60 }
61 }
62
63 pub fn set_dtcs(&mut self, dtcs: Vec<Dtc>) {
65 self.dtcs = dtcs;
66 }
67
68 fn mock_pid_response(&self, pid: u8) -> Vec<u8> {
70 match pid {
71 0x04 => vec![0x40], 0x05 => vec![0x5A], 0x06 => vec![0x80], 0x07 => vec![0x80], 0x0B => vec![0x65], 0x0C => vec![0x0A, 0xA0], 0x0D => vec![0x00], 0x0E => vec![0x8C], 0x0F => vec![0x41], 0x10 => vec![0x00, 0xFA], 0x11 => vec![0x26], 0x1C => vec![0x06], 0x1F => vec![0x00, 0x3C], 0x2C => vec![0x1A], 0x2F => vec![0xB3], 0x33 => vec![0x65], 0x42 => vec![0x38, 0x5C], 0x46 => vec![0x41], 0x5C => vec![0x78], 0x5E => vec![0x00, 0x64], 0x61 => vec![0x8D], 0x62 => vec![0x8D], 0x63 => vec![0x03, 0x7F], 0x00 => vec![0xBE, 0x3E, 0xB8, 0x11], 0x01 => vec![0x00, 0x07, 0x65, 0x00], 0x20 => vec![0x80, 0x12, 0xA0, 0x13], 0x40 => vec![0xFA, 0xDC, 0x80, 0x00], 0x60 => vec![0xE0, 0x00, 0x00, 0x00], _ => vec![0x00], }
102 }
103
104 fn mock_j1939_response(&self, pgn: u32) -> Vec<u8> {
106 match pgn {
107 61444 => {
109 let rpm_raw = (680.0_f64 / 0.125) as u16; vec![
111 0x00, 155, 155, (rpm_raw & 0xFF) as u8, (rpm_raw >> 8) as u8, 0xFF, 0xFF, 0xFF, ]
118 }
119 65265 => vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
121 65262 => vec![90, 60, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF],
123 65263 => vec![0xFF, 50, 0xFF, 100, 0xFF, 0xFF, 0xFF, 0xFF],
125 65266 => {
127 let rate_raw = (5.0_f64 / 0.05) as u16; vec![
129 (rate_raw & 0xFF) as u8,
130 (rate_raw >> 8) as u8,
131 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF,
133 ]
134 }
135 65226 => vec![0x00, 0x00], _ => vec![0xFF; 8],
139 }
140 }
141}
142
143impl Default for MockAdapter {
144 fn default() -> Self { Self::new() }
145}
146
147#[async_trait]
148impl Adapter for MockAdapter {
149 async fn initialize(&mut self) -> Result<AdapterInfo, Obd2Error> {
150 self.initialized = true;
151 Ok(self.info.clone())
152 }
153
154 async fn request(&mut self, req: &ServiceRequest) -> Result<Vec<u8>, Obd2Error> {
155 match req.service_id {
156 0x01 => {
158 if let Some(&pid_code) = req.data.first() {
159 if self.supported.contains(&Pid(pid_code)) {
160 Ok(self.mock_pid_response(pid_code))
161 } else {
162 Err(Obd2Error::NoData)
163 }
164 } else {
165 Err(Obd2Error::ParseError("no PID in request".into()))
166 }
167 }
168
169 0x03 => {
171 let mut result = Vec::new();
172 for dtc in &self.dtcs {
173 if dtc.code.len() == 5 {
175 let prefix = match dtc.code.chars().next() {
176 Some('P') => 0x00u8,
177 Some('C') => 0x40,
178 Some('B') => 0x80,
179 Some('U') => 0xC0,
180 _ => 0x00,
181 };
182 if let Ok(num) = u16::from_str_radix(&dtc.code[1..], 16) {
183 let a = prefix | ((num >> 8) as u8 & 0x3F);
184 let b = (num & 0xFF) as u8;
185 result.push(a);
186 result.push(b);
187 }
188 }
189 }
190 Ok(result)
191 }
192
193 0x04 => {
195 self.dtcs.clear();
196 Ok(vec![])
197 }
198
199 0x09 => {
201 match req.data.first() {
202 Some(0x02) => Ok(self.vin.as_bytes().to_vec()), _ => Ok(vec![]),
204 }
205 }
206
207 0x05 => {
209 match (req.data.first(), req.data.get(1)) {
210 (Some(_tid), Some(&sensor)) if sensor <= 0x02 => {
212 Ok(vec![0x00, 0x5A])
214 }
215 _ => Err(Obd2Error::NoData),
217 }
218 }
219
220 0x21 | 0x22 => Ok(vec![0x80, 0x00]),
222
223 0x10 => Ok(vec![]),
225
226 0x27 => {
228 match req.data.first() {
229 Some(0x01) => Ok(vec![0xAA, 0xBB, 0xCC, 0xDD]), Some(0x02) => Ok(vec![]), _ => Ok(vec![]),
232 }
233 }
234
235 0x2F => Ok(vec![]),
237
238 0x3E => Ok(vec![]),
240
241 0xEA => {
243 let pgn = match (req.data.first(), req.data.get(1), req.data.get(2)) {
244 (Some(&lo), Some(&mid), Some(&hi)) => {
245 (lo as u32) | ((mid as u32) << 8) | ((hi as u32) << 16)
246 }
247 _ => return Err(Obd2Error::NoData),
248 };
249 Ok(self.mock_j1939_response(pgn))
250 }
251
252 _ => Err(Obd2Error::NoData),
253 }
254 }
255
256 async fn supported_pids(&mut self) -> Result<HashSet<Pid>, Obd2Error> {
257 Ok(self.supported.clone())
258 }
259
260 async fn battery_voltage(&mut self) -> Result<Option<f64>, Obd2Error> {
261 Ok(Some(14.4))
262 }
263
264 fn info(&self) -> &AdapterInfo {
265 &self.info
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[tokio::test]
274 async fn test_mock_adapter_initialize() {
275 let mut adapter = MockAdapter::new();
276 let info = adapter.initialize().await.unwrap();
277 assert_eq!(info.chipset, Chipset::Elm327Genuine);
278 }
279
280 #[tokio::test]
281 async fn test_mock_adapter_supported_pids() {
282 let mut adapter = MockAdapter::new();
283 let pids = adapter.supported_pids().await.unwrap();
284 assert!(pids.contains(&Pid::ENGINE_RPM));
285 assert!(pids.contains(&Pid::VEHICLE_SPEED));
286 assert!(pids.contains(&Pid::COOLANT_TEMP));
287 }
288
289 #[tokio::test]
290 async fn test_mock_adapter_read_pid() {
291 let mut adapter = MockAdapter::new();
292 adapter.initialize().await.unwrap();
293 let req = ServiceRequest::read_pid(Pid::ENGINE_RPM);
294 let response = adapter.request(&req).await.unwrap();
295 assert_eq!(response.len(), 2); }
297
298 #[tokio::test]
299 async fn test_mock_adapter_read_vin() {
300 let mut adapter = MockAdapter::with_vin("1GCHK23224F000001");
301 let req = ServiceRequest::read_vin();
302 let response = adapter.request(&req).await.unwrap();
303 let vin = String::from_utf8_lossy(&response);
304 assert_eq!(vin.len(), 17);
305 assert_eq!(vin, "1GCHK23224F000001");
306 }
307
308 #[tokio::test]
309 async fn test_mock_adapter_read_dtcs() {
310 let mut adapter = MockAdapter::new();
311 adapter.set_dtcs(vec![
312 Dtc::from_code("P0420"),
313 Dtc::from_code("P0171"),
314 ]);
315 let req = ServiceRequest::read_dtcs();
316 let response = adapter.request(&req).await.unwrap();
317 assert_eq!(response.len(), 4); }
319
320 #[tokio::test]
321 async fn test_mock_adapter_clear_dtcs() {
322 let mut adapter = MockAdapter::new();
323 adapter.set_dtcs(vec![Dtc::from_code("P0420")]);
324
325 let req = ServiceRequest {
327 service_id: 0x04,
328 data: vec![],
329 target: crate::protocol::service::Target::Broadcast,
330 };
331 adapter.request(&req).await.unwrap();
332
333 let req = ServiceRequest::read_dtcs();
335 let response = adapter.request(&req).await.unwrap();
336 assert!(response.is_empty());
337 }
338
339 #[tokio::test]
340 async fn test_mock_adapter_unsupported_pid() {
341 let mut adapter = MockAdapter::new();
342 let req = ServiceRequest::read_pid(Pid(0xFF));
344 let result = adapter.request(&req).await;
345 assert!(result.is_err());
346 }
347
348 #[tokio::test]
349 async fn test_mock_adapter_battery_voltage() {
350 let mut adapter = MockAdapter::new();
351 let voltage = adapter.battery_voltage().await.unwrap();
352 assert_eq!(voltage, Some(14.4));
353 }
354}