use std::{cell::RefCell, time::Duration};
use tokio::sync::{Mutex, Notify};
use super::DiagError;
#[derive(Debug, Clone, PartialEq)]
pub enum UdsResponse {
Single(u8, u8, u8), First(u16, u8, u8, u8, Vec<u8>), Consecutive(u8, Vec<u8>), FlowControl, }
#[derive(Debug, Clone, PartialEq)]
pub enum Response {
Ok(UdsResponse),
Error,
}
pub struct ResponseSlot(pub Mutex<RefCell<Response>>, pub Notify);
impl Default for ResponseSlot {
fn default() -> Self {
Self::new()
}
}
impl ResponseSlot {
const TIMEOUT: u64 = 1000;
pub fn new() -> Self {
Self(Mutex::new(RefCell::new(Response::Error)), Notify::new())
}
pub async fn get(&self) -> Result<Response, DiagError> {
self.1.notified().await;
let res = self.0.try_lock().unwrap().to_owned().into_inner();
Ok(res)
}
pub async fn wait_for_response(&self) -> Response {
tokio::select! {
_ = self.1.notified() => {
let data = self.0.lock().await;
data.borrow().clone()
}
_ = tokio::time::sleep(Duration::from_millis(Self::TIMEOUT)) => {
Response::Error
}
}
}
pub async fn update_response(&self, new_data: Vec<u8>) -> Result<(), DiagError> {
let res = self.process_response(new_data)?;
self.0.lock().await.replace(Response::Ok(res)); self.1.notify_one(); Ok(())
}
fn process_response(&self, res: Vec<u8>) -> Result<UdsResponse, DiagError> {
match res[0] & 0xF0 {
0x00 => {
Ok(UdsResponse::Single(res[1], res[2], res[3]))
}
0x10 => {
let size = (((res[0] & 0x0f) as u16) << 8) + res[1] as u16;
Ok(UdsResponse::First(
size,
res[2],
res[3],
res[4],
res[5..].to_vec(),
))
}
0x20 => {
let idx = res[0] & 0x0F;
Ok(UdsResponse::Consecutive(idx, res[1..].to_vec()))
}
0x30 => {
Ok(UdsResponse::FlowControl)
}
_ => Err(DiagError::NotSupported),
}
}
}