From d484262856b38df72372b0a87cdc7c5dab88c376 Mon Sep 17 00:00:00 2001 From: David Sferruzza Date: Sat, 9 Jan 2021 23:58:43 +0100 Subject: [PATCH] Add a new telemetry message for EOL tests Fix #34 --- src/cli/bin.rs | 5 ++ src/cli/convert.rs | 3 ++ src/lib.rs | 6 +++ src/parsers/v2.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++ src/structures.rs | 114 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+) diff --git a/src/cli/bin.rs b/src/cli/bin.rs index 697008fc08..813f15cab8 100644 --- a/src/cli/bin.rs +++ b/src/cli/bin.rs @@ -320,6 +320,7 @@ fn stats(cfg: Stats) { let mut nb_stopped_messages: u32 = 0; let mut nb_control_ack: u32 = 0; let mut nb_fatal_error: u32 = 0; + let mut nb_eol_test_snapshots: u32 = 0; loop { match rx.try_recv() { @@ -347,6 +348,9 @@ fn stats(cfg: Stats) { TelemetryMessage::FatalError(_) => { nb_fatal_error += 1; } + TelemetryMessage::EolTestSnapshot(_) => { + nb_eol_test_snapshots += 1; + } } telemetry_messages.push(message); } @@ -363,6 +367,7 @@ fn stats(cfg: Stats) { println!("Nb StoppedMessage: {}", nb_stopped_messages); println!("Nb ControlAck: {}", nb_control_ack); println!("Nb FatalError: {}", nb_fatal_error); + println!("Nb EolTestSnapshot: {}", nb_eol_test_snapshots); println!( "Estimated duration: {:.3} seconds", compute_duration(telemetry_messages) as f32 / 1000_f32 diff --git a/src/cli/convert.rs b/src/cli/convert.rs index 1f818b7d5a..e64dfc07c5 100644 --- a/src/cli/convert.rs +++ b/src/cli/convert.rs @@ -165,6 +165,9 @@ pub fn telemetry_to_gts(message: &TelemetryMessage, source_label: &Option { // Do nothing: we don't want this kind of messages } + TelemetryMessage::EolTestSnapshot(_) => { + // Do nothing: we don't want this kind of messages + } }; output.iter().fold(String::new(), |mut acc, cur| { acc.push_str(cur); diff --git a/src/lib.rs b/src/lib.rs index d2e35729c4..7923c3452f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,6 +287,12 @@ pub fn display_message(message: TelemetryChannelType) { }))) => { info!("***** FATAL ERROR ***** {:?}", &error); } + Ok(TelemetryMessageOrError::Message(TelemetryMessage::EolTestSnapshot(_))) => { + info!( + " {:?}", + &message.expect("failed unwrapping message for EOL test snapshot") + ); + } Ok(TelemetryMessageOrError::Error(e)) => { warn!("a high-level error occurred: {:?}", e); } diff --git a/src/parsers/v2.rs b/src/parsers/v2.rs index 842593b61e..b38a2f6d17 100644 --- a/src/parsers/v2.rs +++ b/src/parsers/v2.rs @@ -107,6 +107,30 @@ fn fatal_error_details(input: &[u8]) -> IResult<&[u8], FatalErrorDetails> { } } +named!( + eol_test_step, + map_res!(be_u8, |step| EolTestStep::try_from(step)) +); + +fn eol_test_snapshot_content(input: &[u8]) -> IResult<&[u8], EolTestSnapshotContent> { + use nom::error::{Error, ErrorKind}; + use nom::Err::Failure; + use EolTestSnapshotContent::*; + + let (input, content_type) = be_u8(input)?; + match content_type { + 0 => Ok((input, InProgress)), + 1 => { + do_parse!( + input, + sep >> error: u8_array >> (Error(String::from_utf8_lossy(&error).into_owned())) + ) + } + 2 => Ok((input, Success)), + _ => Err(Failure(Error::new(input, ErrorKind::Switch))), + } +} + named!( boot, do_parse!( @@ -579,6 +603,37 @@ named!( ) ); +named!( + eol_test_snapshot, + do_parse!( + tag!("L:") + >> tag!([VERSION]) + >> software_version_len: be_u8 + >> software_version: + map_res!(take!(software_version_len), |bytes| std::str::from_utf8( + bytes + )) + >> device_id1: be_u32 + >> device_id2: be_u32 + >> device_id3: be_u32 + >> sep + >> systick: be_u64 + >> sep + >> current_step: eol_test_step + >> sep + >> content: eol_test_snapshot_content + >> end + >> (TelemetryMessage::EolTestSnapshot(EolTestSnapshot { + telemetry_version: VERSION, + version: software_version.to_string(), + device_id: format!("{}-{}-{}", device_id1, device_id2, device_id3), + systick, + current_step, + content, + })) + ) +); + /// Transform bytes into a structured telemetry message /// /// * `input` - Bytes to parse. @@ -593,6 +648,7 @@ pub fn message(input: &[u8]) -> IResult<&[u8], TelemetryMessage, TelemetryError< alarm_trap, control_ack, fatal_error, + eol_test_snapshot, ))(input) .map_err(nom::Err::convert) } @@ -689,6 +745,26 @@ mod tests { } } + fn eol_test_step_strategy() -> impl Strategy { + proptest::num::u8::ANY + .prop_filter_map("Invalid test step", |n| EolTestStep::try_from(n).ok()) + } + + fn eol_test_snapshot_content_strategy() -> BoxedStrategy { + prop_oneof![ + Just(EolTestSnapshotContent::InProgress), + eol_test_snapshot_content_error_strategy(), + Just(EolTestSnapshotContent::Success), + ] + .boxed() + } + + prop_compose! { + fn eol_test_snapshot_content_error_strategy()(reason in ".+") -> EolTestSnapshotContent { + EolTestSnapshotContent::Error(reason) + } + } + proptest! { #[test] fn test_boot_message_parser( @@ -1326,4 +1402,57 @@ mod tests { assert_eq!(nom::dbg_dmp(fatal_error, "fatal_error")(input), Ok((&[][..], expected))); } } + + proptest! { + #[test] + fn test_eol_test_snapshot_message_parser( + version in ".*", + device_id1 in (0u32..), + device_id2 in (0u32..), + device_id3 in (0u32..), + systick in (0u64..), + current_step in eol_test_step_strategy(), + content in eol_test_snapshot_content_strategy(), + ) { + let msg = EolTestSnapshot { + telemetry_version: VERSION, + version, + device_id: format!("{}-{}-{}", device_id1, device_id2, device_id3), + systick, + current_step, + content, + }; + + let eol_test_snapshot_content: Vec = match msg.content { + EolTestSnapshotContent::InProgress => vec![0], + EolTestSnapshotContent::Error(ref reason) => flat(&[ + &[1], + b"\t", + &[reason.len() as u8], + &reason.as_bytes(), + ]), + EolTestSnapshotContent::Success => vec![2], + }; + + let input = &flat(&[ + b"L:", + &[VERSION], + &[msg.version.len() as u8], + &msg.version.as_bytes(), + &device_id1.to_be_bytes(), + &device_id2.to_be_bytes(), + &device_id3.to_be_bytes(), + b"\t", + &msg.systick.to_be_bytes(), + b"\t", + &[current_step as u8], + b"\t", + &eol_test_snapshot_content, + b"\n", + ]); + + let expected = TelemetryMessage::EolTestSnapshot(msg); + assert_eq!(nom::dbg_dmp(eol_test_snapshot, "eol_test_snapshot")(input), Ok((&[][..], expected))); + } + } } diff --git a/src/structures.rs b/src/structures.rs index a5ef2da604..fe6472b332 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -208,6 +208,94 @@ pub enum FatalErrorDetails { }, } +/// Step of the end of line test +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))] +#[allow(non_camel_case_types, missing_docs)] +pub enum EolTestStep { + START, + SUPPLY_TO_EXPANDER_NOT_CONNECTED, + CHECK_FAN, + TEST_BAT_DEAD, + BATTERY_DEEP_DISCHARGE, + DISCONNECT_MAINS, + CONNECT_MAINS, + CHECK_BUZZER, + CHECK_ALL_BUTTONS, + CHECK_UI_SCREEN, + PLUG_AIR_TEST_SYTEM, + REACH_MAX_PRESSURE, + MAX_PRESSURE_REACHED_OK, + MAX_PRESSURE_NOT_REACHED, + START_LEAK_MESURE, + LEAK_IS_TOO_HIGH, + REACH_NULL_PRESSURE, + MIN_PRESSURE_NOT_REACHED, + USER_CONFIRMATION_BEFORE_O2_TEST, + START_O2_TEST, + O2_PRESSURE_NOT_REACH, + WAIT_USER_BEFORE_LONG_RUN, + START_LONG_RUN_BLOWER, + PRESSURE_NOT_STABLE, + FLOW_NOT_STABLE, + END_SUCCESS, + DISPLAY_PRESSURE, + DISPLAY_FLOW, +} + +impl TryFrom for EolTestStep { + type Error = io::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::START), + 1 => Ok(Self::SUPPLY_TO_EXPANDER_NOT_CONNECTED), + 2 => Ok(Self::CHECK_FAN), + 3 => Ok(Self::TEST_BAT_DEAD), + 4 => Ok(Self::BATTERY_DEEP_DISCHARGE), + 5 => Ok(Self::DISCONNECT_MAINS), + 6 => Ok(Self::CONNECT_MAINS), + 7 => Ok(Self::CHECK_BUZZER), + 8 => Ok(Self::CHECK_ALL_BUTTONS), + 9 => Ok(Self::CHECK_UI_SCREEN), + 10 => Ok(Self::PLUG_AIR_TEST_SYTEM), + 11 => Ok(Self::REACH_MAX_PRESSURE), + 12 => Ok(Self::MAX_PRESSURE_REACHED_OK), + 13 => Ok(Self::MAX_PRESSURE_NOT_REACHED), + 14 => Ok(Self::START_LEAK_MESURE), + 15 => Ok(Self::LEAK_IS_TOO_HIGH), + 16 => Ok(Self::REACH_NULL_PRESSURE), + 17 => Ok(Self::MIN_PRESSURE_NOT_REACHED), + 18 => Ok(Self::USER_CONFIRMATION_BEFORE_O2_TEST), + 19 => Ok(Self::START_O2_TEST), + 20 => Ok(Self::O2_PRESSURE_NOT_REACH), + 21 => Ok(Self::WAIT_USER_BEFORE_LONG_RUN), + 22 => Ok(Self::START_LONG_RUN_BLOWER), + 23 => Ok(Self::PRESSURE_NOT_STABLE), + 24 => Ok(Self::FLOW_NOT_STABLE), + 25 => Ok(Self::END_SUCCESS), + 26 => Ok(Self::DISPLAY_PRESSURE), + 27 => Ok(Self::DISPLAY_FLOW), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("Invalid EOL test step {}", value), + )), + } + } +} + +/// Content of end of line test snapshots +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))] +pub enum EolTestSnapshotContent { + /// Test is in progress + InProgress, + /// There was an error during test + Error(String), + /// End of line test succeeded + Success, +} + /// A telemetry message that is sent once every time the MCU boots #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))] @@ -494,6 +582,24 @@ pub struct FatalError { pub error: FatalErrorDetails, } +/// [protocol v2] A message sent during end of line tests +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))] +pub struct EolTestSnapshot { + /// Version of the telemetry protocol + pub telemetry_version: u8, + /// Version of the MCU firmware + pub version: String, + /// Internal ID of the MCU + pub device_id: String, + /// Number of microseconds since the MCU booted + pub systick: u64, + /// Current step + pub current_step: EolTestStep, + /// Content of the snapshot + pub content: EolTestSnapshotContent, +} + /// Supported telemetry messages #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))] @@ -513,6 +619,8 @@ pub enum TelemetryMessage { ControlAck(ControlAck), /// [protocol v2] A message sent when a fatal error occurs FatalError(FatalError), + /// [protocol v2] A message sent during end of line tests + EolTestSnapshot(EolTestSnapshot), } impl TelemetryMessage { @@ -540,6 +648,9 @@ impl TelemetryMessage { Self::FatalError(FatalError { telemetry_version, .. }) => telemetry_version, + Self::EolTestSnapshot(EolTestSnapshot { + telemetry_version, .. + }) => telemetry_version, }; *val } @@ -554,6 +665,7 @@ impl TelemetryMessage { Self::AlarmTrap(AlarmTrap { version, .. }) => version, Self::ControlAck(ControlAck { version, .. }) => version, Self::FatalError(FatalError { version, .. }) => version, + Self::EolTestSnapshot(EolTestSnapshot { version, .. }) => version, }; val.clone() } @@ -568,6 +680,7 @@ impl TelemetryMessage { Self::AlarmTrap(AlarmTrap { device_id, .. }) => device_id, Self::ControlAck(ControlAck { device_id, .. }) => device_id, Self::FatalError(FatalError { device_id, .. }) => device_id, + Self::EolTestSnapshot(EolTestSnapshot { device_id, .. }) => device_id, }; val.clone() } @@ -582,6 +695,7 @@ impl TelemetryMessage { Self::AlarmTrap(AlarmTrap { systick, .. }) => systick, Self::ControlAck(ControlAck { systick, .. }) => systick, Self::FatalError(FatalError { systick, .. }) => systick, + Self::EolTestSnapshot(EolTestSnapshot { systick, .. }) => systick, }; *val }