Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various additions #35

Merged
merged 8 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/cli/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ fn stats(cfg: Stats) {
let mut nb_machine_state_snapshots: u32 = 0;
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() {
Expand All @@ -343,6 +345,12 @@ fn stats(cfg: Stats) {
TelemetryMessage::ControlAck(_) => {
nb_control_ack += 1;
}
TelemetryMessage::FatalError(_) => {
nb_fatal_error += 1;
}
TelemetryMessage::EolTestSnapshot(_) => {
nb_eol_test_snapshots += 1;
}
}
telemetry_messages.push(message);
}
Expand All @@ -358,6 +366,8 @@ fn stats(cfg: Stats) {
println!("Nb MachineStateSnapshot: {}", nb_machine_state_snapshots);
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
Expand Down
6 changes: 6 additions & 0 deletions src/cli/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ pub fn telemetry_to_gts(message: &TelemetryMessage, source_label: &Option<String
TelemetryMessage::ControlAck(_) => {
// Do nothing: we don't want this kind of messages
}
TelemetryMessage::FatalError(_) => {
// 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);
Expand Down
8 changes: 8 additions & 0 deletions src/cli/statistics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ mod tests {
inspiratory_duration_command: None,
previous_inspiratory_duration: None,
battery_level: None,
patient_height: None,
locale: None,
},
)];

Expand Down Expand Up @@ -180,6 +182,8 @@ mod tests {
inspiratory_duration_command: None,
battery_level: None,
current_alarm_codes: None,
patient_height: None,
locale: None,
}));

assert_eq!(compute_duration(vect), 100);
Expand Down Expand Up @@ -275,6 +279,8 @@ mod tests {
inspiratory_duration_command: None,
previous_inspiratory_duration: None,
battery_level: None,
patient_height: None,
locale: None,
},
));

Expand Down Expand Up @@ -312,6 +318,8 @@ mod tests {
inspiratory_duration_command: None,
battery_level: None,
current_alarm_codes: None,
patient_height: None,
locale: None,
}));

assert_eq!(compute_duration(vect), 110);
Expand Down
12 changes: 12 additions & 0 deletions src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

use std::ops::RangeInclusive;

use crate::locale::Locale;

/// Special value that can be used in a heartbeat control message to disable RPi watchdog
pub const DISABLE_RPI_WATCHDOG: u16 = 43_690;

Expand Down Expand Up @@ -71,6 +73,10 @@ pub enum ControlSetting {
TargetInspiratoryFlow = 25,
/// Duration of inspiration in ms (value bounds must be between 200 and 3000)
InspiratoryDuration = 26,
/// Language of the system; this should be two letters (see [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1)) in ASCII representation as two u8
Locale = 27,
/// Patient's height in centimeters
PatientHeight = 28,
}

impl ControlSetting {
Expand Down Expand Up @@ -105,6 +111,8 @@ impl ControlSetting {
Self::LeakAlarmThreshold => 200,
Self::TargetInspiratoryFlow => 40,
Self::InspiratoryDuration => 800,
Self::Locale => Locale::default().as_usize(),
Self::PatientHeight => 160,
}
}

Expand Down Expand Up @@ -139,6 +147,8 @@ impl ControlSetting {
Self::LeakAlarmThreshold => RangeInclusive::new(0, 10_000),
Self::TargetInspiratoryFlow => RangeInclusive::new(5, 80),
Self::InspiratoryDuration => RangeInclusive::new(200, 3_000),
Self::Locale => Locale::bounds(),
Self::PatientHeight => RangeInclusive::new(100, 250),
}
}
}
Expand Down Expand Up @@ -175,6 +185,8 @@ impl std::convert::TryFrom<u8> for ControlSetting {
24 => Ok(ControlSetting::LeakAlarmThreshold),
25 => Ok(ControlSetting::TargetInspiratoryFlow),
26 => Ok(ControlSetting::InspiratoryDuration),
27 => Ok(ControlSetting::Locale),
28 => Ok(ControlSetting::PatientHeight),
_ => Err("Invalid setting number"),
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
pub mod alarm;
/// Structures to represent control messages
pub mod control;
/// Tools to manipulate ISO 639-1 language codes to be used in the control protocol
pub mod locale;
/// Underlying parsers for telemetry messages
pub mod parsers;
/// Structures to represent telemetry messages
Expand Down Expand Up @@ -279,6 +281,18 @@ pub fn display_message(message: TelemetryChannelType) {
}))) => {
info!("← {:?} = {}", &setting, &value);
}
Ok(TelemetryMessageOrError::Message(TelemetryMessage::FatalError(FatalError {
error,
..
}))) => {
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);
}
Expand Down
117 changes: 117 additions & 0 deletions src/locale.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// MakAir Telemetry
//
// Copyright: 2020, Makers For Life
// License: Public Domain License

use std::convert::TryFrom;
use std::ops::RangeInclusive;

/// An ISO 639-1 language code to be used to choose language for the whole system
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize-messages", derive(serde::Serialize))]
pub struct Locale(u16);

impl Locale {
/// Create a locale from a u16
pub fn try_from_u16(num: u16) -> Option<Self> {
match Self::try_from(Self(num).to_string().as_str()) {
Ok(locale) => Some(locale),
Err(_) => None,
}
}

/// Language code as a u16
pub fn as_u16(&self) -> u16 {
self.0
}

/// Language code as a usize
pub fn as_usize(&self) -> usize {
self.0.into()
}

/// Allowed value bounds (this is not really correct/useful)
pub fn bounds() -> RangeInclusive<usize> {
RangeInclusive::new(
Self::try_from("aa").unwrap().as_usize(),
Self::try_from("zz").unwrap().as_usize(),
)
}
}

impl TryFrom<&str> for Locale {
type Error = &'static str;

fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.len() == 2 {
let bytes = value.as_bytes();
let w = ((bytes[0] as u16) << 8) | bytes[1] as u16;
Ok(Locale(w))
} else {
Err("language code must be exactly 2 characters, according to ISO 639-1")
}
}
}

impl Default for Locale {
fn default() -> Self {
Locale::try_from("en").unwrap()
}
}

impl std::fmt::Display for Locale {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self.0.to_be_bytes();
let str = String::from_utf8_lossy(&bytes);
f.write_str(&str)
}
}

#[cfg(test)]
mod tests {
use super::Locale;

use proptest::prelude::*;
use std::convert::TryFrom;

const FR: u16 = 0x6672;

fn ui_locale_strategy() -> impl Strategy<Value = Locale> {
proptest::num::u16::ANY.prop_filter_map("Invalid UI locale code", |code| {
let ui_locale = Locale(code);
if ui_locale.to_string().is_ascii() {
Some(ui_locale)
} else {
None
}
})
}

#[test]
fn from_str_fr() {
assert_eq!(Locale::try_from("fr").map(|code| code.as_u16()), Ok(FR));
}

#[test]
fn from_str_empty() {
assert!(Locale::try_from("").is_err())
}

#[test]
fn from_str_too_long() {
assert!(Locale::try_from("fra").is_err())
}

#[test]
fn to_str() {
assert_eq!(Locale(FR).to_string().as_str(), "fr")
}

proptest! {
#[test]
fn back_and_forth(ui_locale in ui_locale_strategy()) {
let str = ui_locale.to_string();
assert_eq!(Locale::try_from(str.as_str()).map(|code| code.as_u16()), Ok(ui_locale.as_u16()))
}
}
}
8 changes: 8 additions & 0 deletions src/parsers/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ named!(
inspiratory_duration_command: None,
battery_level: None,
current_alarm_codes: None,
patient_height: None,
locale: None,
})
})
)
Expand Down Expand Up @@ -280,6 +282,8 @@ named!(
inspiratory_duration_command: None,
previous_inspiratory_duration: None,
battery_level: None,
patient_height: None,
locale: None,
}))
)
);
Expand Down Expand Up @@ -522,6 +526,8 @@ mod tests {
inspiratory_duration_command: None,
battery_level: None,
current_alarm_codes: None,
patient_height: None,
locale: None,
};

// This needs to be consistent with sendStoppedMessage() defined in src/software/firmware/srcs/telemetry.cpp
Expand Down Expand Up @@ -671,6 +677,8 @@ mod tests {
inspiratory_duration_command: None,
previous_inspiratory_duration: None,
battery_level: None,
patient_height: None,
locale: None,
};

// This needs to be consistent with sendMachineStateSnapshot() defined in makair-firmware/srcs/telemetry.cpp
Expand Down
Loading