use crate::{
error::Error,
request::{Request, SubFunction},
utils, EventType, Configuration, RequestData, ResponseOnEventType, Service,
};
use bitfield_struct::bitfield;
rsutil::enum_extend!(
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ComparisonLogicID {
LessThan = 0x01,
LargerThan = 0x02,
Equal = 0x03,
NotEqual = 0x04,
},
u8,
Error,
ReservedError
);
#[bitfield(u16, order = Msb)]
#[derive(Eq, PartialEq)]
pub struct Localization {
pub sign: bool,
#[bits(5)]
pub length: u8,
#[bits(10)]
pub offset: u16,
}
impl Localization {
#[inline]
pub const fn is_sign(&self) -> bool {
self.sign()
}
#[inline]
pub fn sign_set(&mut self, value: bool) -> &mut Self {
self.set_sign(value);
self
}
#[inline]
pub const fn length_value(&self) -> u8 {
self.length()
}
#[inline]
pub fn length_set(&mut self, value: u8) -> &mut Self {
self.set_length(value);
self
}
#[inline]
pub const fn offset_value(&self) -> u16 {
self.offset()
}
#[inline]
pub fn offset_set(&mut self, value: u16) -> &mut Self {
self.set_offset(value);
self
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum EventTypeParameter {
StopResponseOnEvent = 0x00,
OnDTCStatusChange {
test_failed: u8,
service: Service,
sub_func: u8,
dtc_status_mask: u8,
} = 0x01, OnChangeOfDataIdentifier {
did: u16,
service: Service,
} = 0x03, ReportActivatedEvents = 0x04,
StartResponseOnEvent = 0x05, ClearResponseOnEvent = 0x06,
OnComparisonOfValues {
did: u16,
logic_id: ComparisonLogicID,
comparison_ref: u32,
hysteresis_value: u8,
localization: Localization,
service: Service,
response_did: u16, } = 0x07, ReportMostRecentDtcOnStatusChange {
report_type: u8,
} = 0x08, ReportDTCRecordInformationOnDtcStatusChange {
dtc_status_mask: u8,
dtc_sub_func: u8,
dtc_ext_data_record_num: u8,
} = 0x09, }
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResponseOnEvent {
pub window_time: u8, pub param: EventTypeParameter,
}
#[allow(unused)]
impl From<ResponseOnEvent> for Vec<u8> {
fn from(v: ResponseOnEvent) -> Self {
let mut result = Vec::new();
match v.param {
EventTypeParameter::StopResponseOnEvent => {
result.push(EventType::new(false, ResponseOnEventType::StopResponseOnEvent).into());
result.push(v.window_time);
}
EventTypeParameter::OnDTCStatusChange {
test_failed,
service,
sub_func,
dtc_status_mask,
} => {
result.push(EventType::new(false, ResponseOnEventType::OnDTCStatusChange).into());
result.push(v.window_time);
result.push(test_failed);
result.push(service.into());
result.push(sub_func);
result.push(dtc_status_mask);
}
EventTypeParameter::OnChangeOfDataIdentifier { did, service } => {
result.push(
EventType::new(false, ResponseOnEventType::OnChangeOfDataIdentifier).into(),
);
result.push(v.window_time);
result.extend(did.to_be_bytes());
result.push(service.into());
}
EventTypeParameter::ReportActivatedEvents => {
result
.push(EventType::new(false, ResponseOnEventType::ReportActivatedEvents).into());
result.push(v.window_time);
}
EventTypeParameter::StartResponseOnEvent => {
result
.push(EventType::new(false, ResponseOnEventType::StartResponseOnEvent).into());
result.push(v.window_time);
}
EventTypeParameter::ClearResponseOnEvent => {
result
.push(EventType::new(false, ResponseOnEventType::ClearResponseOnEvent).into());
result.push(v.window_time);
}
EventTypeParameter::OnComparisonOfValues {
did,
logic_id,
comparison_ref,
hysteresis_value,
localization,
service,
response_did,
} => {
result
.push(EventType::new(false, ResponseOnEventType::OnComparisonOfValues).into());
result.push(v.window_time);
result.extend(did.to_be_bytes());
result.push(logic_id.into());
result.extend(comparison_ref.to_be_bytes());
result.push(hysteresis_value);
let localization: u16 = localization.into();
result.extend(localization.to_be_bytes());
result.push(service.into());
result.extend(response_did.to_be_bytes());
}
EventTypeParameter::ReportMostRecentDtcOnStatusChange { report_type } => {
result.push(
EventType::new(
false,
ResponseOnEventType::ReportMostRecentDtcOnStatusChange,
)
.into(),
);
result.push(v.window_time);
result.push(report_type);
}
EventTypeParameter::ReportDTCRecordInformationOnDtcStatusChange {
dtc_status_mask,
dtc_sub_func,
dtc_ext_data_record_num,
} => {
result.push(
EventType::new(
false,
ResponseOnEventType::ReportDTCRecordInformationOnDtcStatusChange,
)
.into(),
);
result.push(v.window_time);
result.push(dtc_status_mask);
result.push(dtc_sub_func);
result.push(dtc_ext_data_record_num);
}
}
result
}
}
impl RequestData for ResponseOnEvent {
fn new_request<T: AsRef<[u8]>>(
data: T,
sub_func: Option<u8>,
_: &Configuration,
) -> Result<Request, Error> {
let data = data.as_ref();
match sub_func {
Some(_) => Err(Error::SubFunctionError(Service::ResponseOnEvent)),
None => Ok(Request {
service: Service::ResponseOnEvent,
sub_func: None,
data: data.to_vec(),
}),
}
}
}
impl TryFrom<(&Request, &Configuration)> for ResponseOnEvent {
type Error = Error;
fn try_from((req, _): (&Request, &Configuration)) -> Result<Self, Self::Error> {
let service = req.service();
if service != Service::ResponseOnEvent || req.sub_func.is_some() {
return Err(Error::ServiceError(service));
}
let data = req.raw_data();
if data.len() < 2 {
return Err(Error::InvalidDataLength {
expect: 2,
actual: data.len(),
});
}
let mut offset = 0;
let event_type = EventType::try_from(data[offset])?;
offset += 1;
let window_time = data[offset];
offset += 1;
let param = match event_type.event_type() {
ResponseOnEventType::StopResponseOnEvent => {
if data.len() != offset {
return Err(Error::InvalidData(hex::encode(data)));
}
EventTypeParameter::StopResponseOnEvent
}
ResponseOnEventType::OnDTCStatusChange => {
utils::data_length_check(data.len(), offset + 4, true)?;
EventTypeParameter::OnDTCStatusChange {
test_failed: data[offset],
service: Service::try_from(data[offset + 1])?,
sub_func: data[offset + 2],
dtc_status_mask: data[offset + 3],
}
}
ResponseOnEventType::OnTimerInterrupt => return Err(Error::NotImplement),
ResponseOnEventType::OnChangeOfDataIdentifier => {
utils::data_length_check(data.len(), offset + 3, true)?;
EventTypeParameter::OnChangeOfDataIdentifier {
did: u16::from_be_bytes([data[offset], data[offset + 1]]),
service: Service::try_from(data[offset + 2])?,
}
}
ResponseOnEventType::ReportActivatedEvents => {
if data.len() != offset {
return Err(Error::InvalidData(hex::encode(data)));
}
EventTypeParameter::ReportActivatedEvents
}
ResponseOnEventType::StartResponseOnEvent => {
if data.len() != offset {
return Err(Error::InvalidData(hex::encode(data)));
}
EventTypeParameter::StartResponseOnEvent
}
ResponseOnEventType::ClearResponseOnEvent => {
if data.len() != offset {
return Err(Error::InvalidData(hex::encode(data)));
}
EventTypeParameter::ClearResponseOnEvent
}
ResponseOnEventType::OnComparisonOfValues => {
utils::data_length_check(data.len(), offset + 13, true)?;
EventTypeParameter::OnComparisonOfValues {
did: u16::from_be_bytes([data[offset], data[offset + 1]]),
logic_id: ComparisonLogicID::try_from(data[offset + 2])?,
comparison_ref: u32::from_be_bytes([
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
]),
hysteresis_value: data[offset + 7],
localization: Localization::from(u16::from_be_bytes([
data[offset + 8],
data[offset + 9],
])),
service: Service::try_from(data[offset + 10])?,
response_did: u16::from_be_bytes([data[offset + 11], data[offset + 12]]),
}
}
ResponseOnEventType::ReportMostRecentDtcOnStatusChange => {
utils::data_length_check(data.len(), offset + 1, true)?;
EventTypeParameter::ReportMostRecentDtcOnStatusChange {
report_type: data[offset],
}
}
ResponseOnEventType::ReportDTCRecordInformationOnDtcStatusChange => {
utils::data_length_check(data.len(), offset + 3, true)?;
EventTypeParameter::ReportDTCRecordInformationOnDtcStatusChange {
dtc_status_mask: data[offset],
dtc_sub_func: data[offset + 1],
dtc_ext_data_record_num: data[offset + 2],
}
}
};
Ok(Self { window_time, param })
}
}