log parsing

This commit is contained in:
Will Greenberg
2023-12-04 16:47:42 -08:00
parent db18abae6f
commit e6fab5c27d
4 changed files with 338 additions and 44 deletions
+65 -13
View File
@@ -1,5 +1,6 @@
//! Diag protocol serialization/deserialization
use chrono::{DateTime, Local, FixedOffset};
use deku::prelude::*;
#[derive(Debug, Clone, DekuWrite)]
@@ -43,31 +44,66 @@ pub enum DataType {
}
#[derive(Debug, Clone, DekuRead)]
pub struct ResponseContainer {
pub struct MessagesContainer {
pub data_type: DataType,
pub num_responses: u32,
#[deku(count = "num_responses")]
pub responses: Vec<HdlcEncapsulatedResponse>,
pub messages: Vec<HdlcEncapsulatedMessage>,
}
#[derive(Debug, Clone, DekuRead)]
pub struct HdlcEncapsulatedResponse {
pub struct HdlcEncapsulatedMessage {
pub len: u32,
#[deku(count = "len")]
pub data: Vec<u8>,
}
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
// right before its data, but in this case, a status value comes between the
// variants and the data. so we need to use deku's context (ctx) feature to pass
// those opcodes down to their respective parsers.
#[derive(Debug, Clone, DekuRead)]
pub struct Response {
opcode: u32,
subopcode: u32,
pub status: u32,
#[deku(ctx = "*opcode, *subopcode")]
pub payload: ResponsePayload,
#[deku(type = "u8")]
pub enum Message {
#[deku(id = "16")]
Log {
pending_msgs: u8,
outer_length: u16,
inner_length: u16,
log_type: u16,
timestamp: Timestamp,
#[deku(count = "inner_length - 12")]
payload: Vec<u8>,
},
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
// right before its data, but in this case, a status value comes between the
// variants and the data. so we need to use deku's context (ctx) feature to
// pass those opcodes down to their respective parsers.
#[deku(id_pat = "_")]
Response {
opcode: u32,
subopcode: u32,
status: u32,
#[deku(ctx = "*opcode, *subopcode")]
payload: ResponsePayload,
},
}
#[derive(Debug, Clone, DekuRead)]
#[deku(endian = "little")]
pub struct Timestamp {
pub ts: u64,
}
impl Timestamp {
pub fn to_datetime(&self) -> DateTime<FixedOffset> {
// Upper 48 bits: epoch at 1980-01-06 00:00:00, incremented by 1 for 1/800s
// Lower 16 bits: time since last 1/800s tick in 1/32 chip units
let ts_upper = self.ts >> 16;
let ts_lower = self.ts & 0xffff;
let epoch = chrono::DateTime::parse_from_rfc3339("1980-01-06T00:00:00-00:00").unwrap();
let mut delta_seconds = ts_upper as f64 * 1.25;
delta_seconds += ts_lower as f64 / 40960.0;
let ts_delta = chrono::Duration::milliseconds(delta_seconds as i64);
epoch + ts_delta
}
}
#[derive(Debug, Clone, DekuRead)]
@@ -177,4 +213,20 @@ mod test {
1, 2, 3, 4,
]);
}
#[test]
fn test_message_parsing() {
let msg_bytes = vec![16, 0, 26, 0, 26, 0, 167, 24, 38, 161, 72, 107, 146, 30, 2, 1, 1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0];
match Message::from_bytes((msg_bytes.as_slice(), 0)) {
Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => {
assert_eq!(pending_msgs, 0);
assert_eq!(outer_length, 26);
assert_eq!(inner_length, 26);
assert_eq!(log_type, 6311);
assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap());
assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]);
},
_ => panic!("failed to parse message"),
}
}
}
+42 -31
View File
@@ -1,5 +1,5 @@
use crate::hdlc::{hdlc_encapsulate, hdlc_decapsulate, HdlcError};
use crate::diag::{Response, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, ResponseContainer};
use crate::diag::{Message, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, MessagesContainer};
use std::fs::File;
use std::io::{Cursor, Read, Write};
@@ -72,32 +72,35 @@ impl DiagDevice {
})
}
fn parse_response_container(&self, container: ResponseContainer) -> DiagResult<Vec<Response>> {
fn parse_response_container(&self, container: MessagesContainer) -> DiagResult<Vec<Message>> {
let mut result = Vec::new();
for msg in container.responses {
let data = hdlc_decapsulate(msg.data, &self.crc)?;
match Response::from_bytes((&data, 0)) {
Ok(((_, leftover_bytes), res)) => {
if leftover_bytes > 0 {
println!("warning: {} leftover bytes when Response", leftover_bytes);
}
result.push(res);
},
Err(e) => {
println!("{:?}", data);
println!("error parsing response: {:?}", e);
for msg in container.messages {
match hdlc_decapsulate(msg.data, &self.crc) {
Ok(data) => match Message::from_bytes((&data, 0)) {
Ok(((_, leftover_bytes), res)) => {
if leftover_bytes > 0 {
println!("warning: {} leftover bytes when Response", leftover_bytes);
}
result.push(res);
},
Err(e) => {
println!("error parsing response: {:?}", e);
},
},
Err(err) => {
println!("error decapsulating response: {:?}", err);
}
}
}
Ok(result)
}
pub fn read_response(&mut self) -> DiagResult<Vec<Response>> {
pub fn read_response(&mut self) -> DiagResult<Vec<Message>> {
let mut buf = vec![0; BUFFER_LEN];
loop {
let _ = self.file.read(&mut buf)?;
let ((_, leftover_bytes), res_container) = ResponseContainer::from_bytes((&buf, 0))?;
let ((_, leftover_bytes), res_container) = MessagesContainer::from_bytes((&buf, 0))?;
if leftover_bytes > 0 {
println!("warning: {} leftover bytes when parsing ResponseContainer", leftover_bytes);
}
@@ -124,7 +127,7 @@ impl DiagDevice {
let msg = format!("write failed with error code {}", ret);
return Err(DiagDeviceError::DeviceReadFailed(msg));
}
println!("{}. wrote {} bytes to device", buf.len(), ret);
println!("wrote {} bytes to device", ret);
}
Ok(())
}
@@ -133,15 +136,18 @@ impl DiagDevice {
let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges);
self.write_request(&req)?;
for res in self.read_response()? {
match res.payload {
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
if res.status != 0 {
return Err(DiagDeviceError::RequestFailed(res.status, req));
}
return Ok(log_mask_sizes);
for msg in self.read_response()? {
match msg {
Message::Log { .. } => println!("skipping log response..."),
Message::Response { payload, status, .. } => match payload {
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
if status != 0 {
return Err(DiagDeviceError::RequestFailed(status, req));
}
return Ok(log_mask_sizes);
},
_ => println!("skipping non-LogConfigResponse response..."),
},
_ => println!("skipping non-LogConfigResponse response..."),
}
}
@@ -153,12 +159,17 @@ impl DiagDevice {
let req = build_log_mask_request(log_type, log_mask_bitsize);
self.write_request(&req)?;
for res in self.read_response()? {
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = res.payload {
if res.status != 0 {
return Err(DiagDeviceError::RequestFailed(res.status, req));
}
return Ok(());
for msg in self.read_response()? {
match msg {
Message::Log { .. } => println!("skipping log response..."),
Message::Response { payload, status, .. } => {
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload {
if status != 0 {
return Err(DiagDeviceError::RequestFailed(status, req));
}
return Ok(());
}
},
}
}