From 55a3700abb02873eda2d0cee2e7c93f48d7e3fd1 Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 30 Jun 2024 22:30:04 +0200 Subject: [PATCH 01/10] splitting up time and date time --- src/user_data/value_information.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/user_data/value_information.rs b/src/user_data/value_information.rs index fff5af4..93371b6 100644 --- a/src/user_data/value_information.rs +++ b/src/user_data/value_information.rs @@ -267,7 +267,8 @@ impl TryFrom<&ValueInformationBlock> for ValueInformation { decimal_scale_exponent += (value_information_block.value_information.data & 0b11) as isize - 3; } - 0x6C..=0x6D => labels.push(ValueLabel::TimePoint), + 0x6C => labels.push(ValueLabel::Date), + 0x6D => labels.push(ValueLabel::DateTime), 0x6E => labels.push(ValueLabel::DimensionlessHCA), 0x70..=0x73 => labels.push(ValueLabel::AveragingDuration), 0x74..=0x77 => labels.push(ValueLabel::ActualityDuration), @@ -880,7 +881,8 @@ pub enum ValueLabel { CompactProfile, ActualityDuration, AveragingDuration, - TimePoint, + Date, + DateTime, FabricationNumber, EnhancedIdentification, Address, From 845aaef4cce6038738e9300ca56c7419d06e84ca Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 30 Jun 2024 22:56:26 +0200 Subject: [PATCH 02/10] boilerplate for date time to be parsed --- src/user_data/data_information.rs | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 03e45c7..f625f25 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -519,6 +519,21 @@ impl DataFieldCoding { // Special functions parsing based on the code todo!() } + + DataFieldCoding::DateTypeG => { + todo!(); + } + DataFieldCoding::DateTimeTypeF => { + todo!() + } + + DataFieldCoding::DateTimeTypeJ => { + todo!() + } + + DataFieldCoding::DateTimeTypeI => { + todo!() + } } } } @@ -707,6 +722,22 @@ impl DataFieldCoding { data: 0.0, byte_size: 0, }, + DataFieldCoding::DateTypeG => Value { + data: 0.0, + byte_size: 0, + }, + DataFieldCoding::DateTimeTypeF => Value { + data: 0.0, + byte_size: 0, + }, + DataFieldCoding::DateTimeTypeJ => Value { + data: 0.0, + byte_size: 0, + }, + DataFieldCoding::DateTimeTypeI => Value { + data: 0.0, + byte_size: 0, + }, } } } @@ -729,6 +760,10 @@ pub enum DataFieldCoding { VariableLength, BCDDigit12, SpecialFunctions(SpecialFunctions), + DateTypeG, + DateTimeTypeF, + DateTimeTypeJ, + DateTimeTypeI, } #[cfg(feature = "std")] @@ -750,6 +785,10 @@ impl std::fmt::Display for DataFieldCoding { DataFieldCoding::BCD8Digit => write!(f, "BCD 8-digit"), DataFieldCoding::VariableLength => write!(f, "Variable Length"), DataFieldCoding::BCDDigit12 => write!(f, "BCD 12-digit"), + DataFieldCoding::DateTypeG => write!(f, "Date Type G"), + DataFieldCoding::DateTimeTypeF => write!(f, "Date Time Type F"), + DataFieldCoding::DateTimeTypeJ => write!(f, "Date Time Type J"), + DataFieldCoding::DateTimeTypeI => write!(f, "Date Time Type I"), DataFieldCoding::SpecialFunctions(code) => write!(f, "Special Functions ({:?})", code), } } From 70836de6b6a739040c407f58020d5162a41f8a95 Mon Sep 17 00:00:00 2001 From: heroichornet Date: Tue, 2 Jul 2024 23:09:58 +0200 Subject: [PATCH 03/10] WIP first format implemented 3 to go --- src/user_data/data_information.rs | 156 ++++++++++++++++++++++++++-- src/user_data/data_record.rs | 22 +++- src/user_data/value_information.rs | 2 + src/user_data/variable_user_data.rs | 1 + 4 files changed, 172 insertions(+), 9 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index f625f25..d11bf20 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -255,11 +255,74 @@ impl From> for String { } } +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, PartialEq)] +pub enum Month { + January, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December, +} + +#[cfg(feature = "std")] +impl std::fmt::Display for Month { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Month::January => write!(f, "Jan"), + Month::February => write!(f, "Feb"), + Month::March => write!(f, "Mar"), + Month::April => write!(f, "Apr"), + Month::May => write!(f, "May"), + Month::June => write!(f, "Jun"), + Month::July => write!(f, "Jul"), + Month::August => write!(f, "Aug"), + Month::September => write!(f, "Sep"), + Month::October => write!(f, "Oct"), + Month::November => write!(f, "Nov"), + Month::December => write!(f, "Dec"), + } + } +} + +pub type Year = u16; +pub type DayOfMonth = u8; +pub type Hour = u8; +pub type Minute = u8; +pub type Second = u8; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, PartialEq)] +pub enum SingleOrEvery { + Single(T), + Every(), +} + #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, PartialEq)] pub enum DataType<'a> { Text(TextUnit<'a>), Number(f64), + Date( + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + ), + DateTime( + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + ), } #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(PartialEq, Debug)] @@ -267,6 +330,17 @@ pub struct Data<'a> { value: Option>, size: usize, } + +#[cfg(feature = "std")] +impl std::fmt::Display for SingleOrEvery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SingleOrEvery::Single(value) => write!(f, "{}", value), + SingleOrEvery::Every() => write!(f, "Every"), + } + } +} + #[cfg(feature = "std")] impl std::fmt::Display for Data<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -277,6 +351,14 @@ impl std::fmt::Display for Data<'_> { write!(f, "{}", text) } DataType::Number(value) => write!(f, "{}", value), + DataType::Date(day, month, year) => write!(f, "{}/{}/{}", day, month, year), + DataType::DateTime(day, month, year, hour, minute, second) => { + write!( + f, + "{}/{}/{} {}:{}:{}", + day, month, year, hour, minute, second + ) + } }, None => write!(f, "No Data"), } @@ -521,18 +603,80 @@ impl DataFieldCoding { } DataFieldCoding::DateTypeG => { - todo!(); + if input.len() < 2 { + return Err(DataRecordError::InsufficientData); + } + // lowest five bits of first byte is day, 0 means every day + let day = if input[0] & 0x1F == 0 { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[0] & 0x1F) + }; + + // the first four bits of the second byte is month, 15 means every month + let month = if input[1] & 0xF0 == 0xF0 { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(match input[1] & 0xF { + 0x0 => Month::January, + 0x1 => Month::February, + 0x2 => Month::March, + 0x3 => Month::April, + 0x4 => Month::May, + 0x5 => Month::June, + 0x6 => Month::July, + 0x7 => Month::August, + 0x8 => Month::September, + 0x9 => Month::October, + 0xA => Month::November, + 0xB => Month::December, + _ => return { Err(DataRecordError::InvalidData) }, + }) + }; + let year = u16::from(input[1] & 0x0F) | (u16::from(input[0] & 0xE0) << 1); + let year = if year == 127 { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(year) + }; + + Ok(Data { + value: Some(DataType::Date(day, month, year)), + size: 2, + }) } DataFieldCoding::DateTimeTypeF => { - todo!() + todo!(); + Ok(Data { + value: Some(DataType::Date( + SingleOrEvery::Every(), + SingleOrEvery::Every(), + SingleOrEvery::Every(), + )), + size: 4, + }) } - DataFieldCoding::DateTimeTypeJ => { - todo!() + todo!(); + Ok(Data { + value: Some(DataType::Date( + SingleOrEvery::Every(), + SingleOrEvery::Every(), + SingleOrEvery::Every(), + )), + size: 4, + }) } - DataFieldCoding::DateTimeTypeI => { - todo!() + todo!(); + Ok(Data { + value: Some(DataType::Date( + SingleOrEvery::Every(), + SingleOrEvery::Every(), + SingleOrEvery::Every(), + )), + size: 4, + }) } } } diff --git a/src/user_data/data_record.rs b/src/user_data/data_record.rs index b5c114d..e2d04d4 100644 --- a/src/user_data/data_record.rs +++ b/src/user_data/data_record.rs @@ -1,6 +1,6 @@ use super::{ - data_information::{Data, DataInformation, DataInformationBlock}, - value_information::{ValueInformation, ValueInformationBlock}, + data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock}, + value_information::{ValueInformation, ValueInformationBlock, ValueLabel}, variable_user_data::DataRecordError, }; #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -68,8 +68,24 @@ impl<'a> TryFrom<&RawDataRecordHeader<'a>> for ProcessedDataRecordHeader { ) -> Result { let value_information = ValueInformation::try_from(&raw_data_record_header.value_information_block)?; - let data_information = + let mut data_information = DataInformation::try_from(&raw_data_record_header.data_information_block)?; + + // unfortunately, the data field coding is not always set in the data information block + // so we must do some additional checks to determine the correct data field coding + + if value_information.labels.contains(&ValueLabel::Date) { + data_information.data_field_coding = DataFieldCoding::DateTypeG; + } else if value_information.labels.contains(&ValueLabel::DateTime) { + data_information.data_field_coding = DataFieldCoding::DateTimeTypeF; + } else if value_information.labels.contains(&ValueLabel::Time) { + data_information.data_field_coding = DataFieldCoding::DateTimeTypeJ; + } else if value_information + .labels + .contains(&ValueLabel::DateTimeWithSeconds) + { + data_information.data_field_coding = DataFieldCoding::DateTimeTypeI; + } Ok(ProcessedDataRecordHeader { data_information, value_information, diff --git a/src/user_data/value_information.rs b/src/user_data/value_information.rs index 93371b6..e46f618 100644 --- a/src/user_data/value_information.rs +++ b/src/user_data/value_information.rs @@ -882,7 +882,9 @@ pub enum ValueLabel { ActualityDuration, AveragingDuration, Date, + Time, DateTime, + DateTimeWithSeconds, FabricationNumber, EnhancedIdentification, Address, diff --git a/src/user_data/variable_user_data.rs b/src/user_data/variable_user_data.rs index 9e89580..387312c 100644 --- a/src/user_data/variable_user_data.rs +++ b/src/user_data/variable_user_data.rs @@ -5,6 +5,7 @@ use super::DataRecords; pub enum DataRecordError { DataInformationError(data_information::DataInformationError), InsufficientData, + InvalidData, } #[derive(Debug, PartialEq)] From daea52d1da9b6602d8a54fbbdadc8afd5f16ce58 Mon Sep 17 00:00:00 2001 From: heroichornet Date: Wed, 3 Jul 2024 21:40:41 +0200 Subject: [PATCH 04/10] adding further date time --- src/user_data/data_information.rs | 73 ++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index d11bf20..956fd06 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -321,6 +321,13 @@ pub enum DataType<'a> { SingleOrEvery, SingleOrEvery, SingleOrEvery, + ), + DateTimeWithSeconds( + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, SingleOrEvery, ), } @@ -352,7 +359,10 @@ impl std::fmt::Display for Data<'_> { } DataType::Number(value) => write!(f, "{}", value), DataType::Date(day, month, year) => write!(f, "{}/{}/{}", day, month, year), - DataType::DateTime(day, month, year, hour, minute, second) => { + DataType::DateTime(day, month, year, hour, minute) => { + write!(f, "{}/{}/{} {}:{}:00", day, month, year, hour, minute) + } + DataType::DateTimeWithSeconds(day, month, year, hour, minute, second) => { write!( f, "{}/{}/{} {}:{}:{}", @@ -630,11 +640,11 @@ impl DataFieldCoding { 0x9 => Month::October, 0xA => Month::November, 0xB => Month::December, - _ => return { Err(DataRecordError::InvalidData) }, + _ => return Err(DataRecordError::InvalidData), }) }; - let year = u16::from(input[1] & 0x0F) | (u16::from(input[0] & 0xE0) << 1); - let year = if year == 127 { + let year = u16::from(input[1] & 0xF0) | (u16::from(input[0] & 0xE0) << 1); + let year = if year == 0x7F { SingleOrEvery::Every() } else { SingleOrEvery::Single(year) @@ -646,13 +656,56 @@ impl DataFieldCoding { }) } DataFieldCoding::DateTimeTypeF => { - todo!(); + if input.len() < 4 { + return Err(DataRecordError::InsufficientData); + } + let minutes = if input[0] == 0x3F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[0] & 0x3F) + }; + + let hour = if input[1] == 0x1F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[1] & 0x1F) + }; + + let day = if input[2] == 0x1F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[2] & 0x1F) + }; + + let month = if input[3] == 0x0F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(match input[3] & 0x0F { + 0x0 => Month::January, + 0x1 => Month::February, + 0x2 => Month::March, + 0x3 => Month::April, + 0x4 => Month::May, + 0x5 => Month::June, + 0x6 => Month::July, + 0x7 => Month::August, + 0x8 => Month::September, + 0x9 => Month::October, + 0xA => Month::November, + 0xB => Month::December, + _ => return Err(DataRecordError::InvalidData), + }) + }; + + // year [21 to 23; 28 to 31] + let year = u16::from(input[3] & 0xF0) | (u16::from(input[2] & 0xE0) << 1); + let year = if year == 0x7F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(year) + }; Ok(Data { - value: Some(DataType::Date( - SingleOrEvery::Every(), - SingleOrEvery::Every(), - SingleOrEvery::Every(), - )), + value: Some(DataType::DateTime(day, month, year, hour, minutes)), size: 4, }) } From 4a613a24dca46e0abb28ea00620426eaddd8f1da Mon Sep 17 00:00:00 2001 From: heroichornet Date: Wed, 3 Jul 2024 21:59:27 +0200 Subject: [PATCH 05/10] fixing date time --- src/user_data/data_information.rs | 56 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 956fd06..62625db 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -628,22 +628,24 @@ impl DataFieldCoding { SingleOrEvery::Every() } else { SingleOrEvery::Single(match input[1] & 0xF { - 0x0 => Month::January, - 0x1 => Month::February, - 0x2 => Month::March, - 0x3 => Month::April, - 0x4 => Month::May, - 0x5 => Month::June, - 0x6 => Month::July, - 0x7 => Month::August, - 0x8 => Month::September, - 0x9 => Month::October, - 0xA => Month::November, - 0xB => Month::December, + // warning: 0 is actually invalid, but used, to prevent an invalid data + // this is a work around needs to be improved + 0 | 0x1 => Month::January, + 0x2 => Month::February, + 0x3 => Month::March, + 0x4 => Month::April, + 0x5 => Month::May, + 0x6 => Month::June, + 0x7 => Month::July, + 0x8 => Month::August, + 0x9 => Month::September, + 0xA => Month::October, + 0xB => Month::November, + 0xC => Month::December, _ => return Err(DataRecordError::InvalidData), }) }; - let year = u16::from(input[1] & 0xF0) | (u16::from(input[0] & 0xE0) << 1); + let year = (u16::from(input[1] & 0xF0) >> 1) | (u16::from(input[0] & 0xE0) >> 5); let year = if year == 0x7F { SingleOrEvery::Every() } else { @@ -681,24 +683,26 @@ impl DataFieldCoding { SingleOrEvery::Every() } else { SingleOrEvery::Single(match input[3] & 0x0F { - 0x0 => Month::January, - 0x1 => Month::February, - 0x2 => Month::March, - 0x3 => Month::April, - 0x4 => Month::May, - 0x5 => Month::June, - 0x6 => Month::July, - 0x7 => Month::August, - 0x8 => Month::September, - 0x9 => Month::October, - 0xA => Month::November, - 0xB => Month::December, + // warning: 0 is actually invalid, but used, to prevent an invalid data + // this is a work around needs to be improved + 0 | 0x1 => Month::January, + 0x2 => Month::February, + 0x3 => Month::March, + 0x4 => Month::April, + 0x5 => Month::May, + 0x6 => Month::June, + 0x7 => Month::July, + 0x8 => Month::August, + 0x9 => Month::September, + 0xA => Month::October, + 0xB => Month::November, + 0xC => Month::December, _ => return Err(DataRecordError::InvalidData), }) }; // year [21 to 23; 28 to 31] - let year = u16::from(input[3] & 0xF0) | (u16::from(input[2] & 0xE0) << 1); + let year = (u16::from(input[3] & 0xF0) >> 1) | (u16::from(input[2] & 0xE0) >> 5); let year = if year == 0x7F { SingleOrEvery::Every() } else { From 3605978fe238e4302941f051c77bc61915b4d71e Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sat, 6 Jul 2024 16:54:26 +0200 Subject: [PATCH 06/10] adding time data format J --- src/user_data/data_information.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 62625db..ff4beef 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -310,11 +310,16 @@ pub enum SingleOrEvery { pub enum DataType<'a> { Text(TextUnit<'a>), Number(f64), - Date( + Date( SingleOrEvery, SingleOrEvery, SingleOrEvery, ), + Time( + SingleOrEvery, + SingleOrEvery, + SingleOrEvery, + ), DateTime( SingleOrEvery, SingleOrEvery, @@ -714,13 +719,24 @@ impl DataFieldCoding { }) } DataFieldCoding::DateTimeTypeJ => { - todo!(); + let seconds = if input[0] == 0x3F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[0] & 0x3F) + }; + let minutes = if input[1] == 0x3F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[1] & 0x3F) + }; + let hours = if input[2] == 0x1F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[2] & 0x1F) + }; + Ok(Data { - value: Some(DataType::Date( - SingleOrEvery::Every(), - SingleOrEvery::Every(), - SingleOrEvery::Every(), - )), + value: Some(DataType::Time(seconds, minutes, hours)), size: 4, }) } From a5040ab8d578cf67f3788996038dd86174085f4a Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 7 Jul 2024 11:28:36 +0200 Subject: [PATCH 07/10] adding further data --- src/user_data/data_information.rs | 68 ++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index ff4beef..00f58e4 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -719,6 +719,9 @@ impl DataFieldCoding { }) } DataFieldCoding::DateTimeTypeJ => { + if input.len() < 2 { + return Err(DataRecordError::InsufficientData); + } let seconds = if input[0] == 0x3F { SingleOrEvery::Every() } else { @@ -741,14 +744,67 @@ impl DataFieldCoding { }) } DataFieldCoding::DateTimeTypeI => { - todo!(); + // note: more information can be extracted from the data, + // however, because this data can be derived from the other data that is + // that is extracted, it is not necessary to extract it. + + if input.len() < 6 { + return Err(DataRecordError::InsufficientData); + } + let seconds = if input[0] == 0x3F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[0] & 0x3F) + }; + let minutes = if input[1] == 0x3F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[1] & 0x3F) + }; + let hours = if input[2] == 0x1F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[2] & 0x1F) + }; + let days = if input[3] == 0x1F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(input[3] & 0x1F) + }; + let months = if input[4] == 0x0F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single(match input[4] & 0x0F { + // warning: 0 is actually invalid, but used, to prevent an invalid data + // this is a work around needs to be improved + 0 | 0x1 => Month::January, + 0x2 => Month::February, + 0x3 => Month::March, + 0x4 => Month::April, + 0x5 => Month::May, + 0x6 => Month::June, + 0x7 => Month::July, + 0x8 => Month::August, + 0x9 => Month::September, + 0xA => Month::October, + 0xB => Month::November, + 0xC => Month::December, + _ => return Err(DataRecordError::InvalidData), + }) + }; + let year = if input[5] == 0x7F { + SingleOrEvery::Every() + } else { + SingleOrEvery::Single( + (u16::from(input[5] & 0xF0) >> 1) | (u16::from(input[4] & 0xE0) >> 5), + ) + }; + Ok(Data { - value: Some(DataType::Date( - SingleOrEvery::Every(), - SingleOrEvery::Every(), - SingleOrEvery::Every(), + value: Some(DataType::DateTimeWithSeconds( + days, months, year, hours, minutes, seconds, )), - size: 4, + size: 6, }) } } From 4ff222dc1d47b6309e7757983ba9033482adce1c Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 7 Jul 2024 11:37:11 +0200 Subject: [PATCH 08/10] introducing invalid type, should be maybe done with error thrown instead. But because invalid seems common this will be avoided for now --- src/user_data/data_information.rs | 205 +++++++++++++++--------------- 1 file changed, 102 insertions(+), 103 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 00f58e4..fb82f97 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -300,9 +300,10 @@ pub type Second = u8; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq)] -pub enum SingleOrEvery { +pub enum SingleEveryOrInvalid { Single(T), Every(), + Invalid(), } #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -311,29 +312,29 @@ pub enum DataType<'a> { Text(TextUnit<'a>), Number(f64), Date( - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, ), Time( - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, ), DateTime( - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, ), DateTimeWithSeconds( - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, - SingleOrEvery, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, + SingleEveryOrInvalid, ), } #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -344,11 +345,12 @@ pub struct Data<'a> { } #[cfg(feature = "std")] -impl std::fmt::Display for SingleOrEvery { +impl std::fmt::Display for SingleEveryOrInvalid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - SingleOrEvery::Single(value) => write!(f, "{}", value), - SingleOrEvery::Every() => write!(f, "Every"), + SingleEveryOrInvalid::Single(value) => write!(f, "{}", value), + SingleEveryOrInvalid::Every() => write!(f, "Every"), + SingleEveryOrInvalid::Invalid() => write!(f, "Invalid"), } } } @@ -374,6 +376,9 @@ impl std::fmt::Display for Data<'_> { day, month, year, hour, minute, second ) } + DataType::Time(seconds, minutes, hours) => { + write!(f, "{}:{}:{}", hours, minutes, seconds) + } }, None => write!(f, "No Data"), } @@ -623,38 +628,36 @@ impl DataFieldCoding { } // lowest five bits of first byte is day, 0 means every day let day = if input[0] & 0x1F == 0 { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[0] & 0x1F) + SingleEveryOrInvalid::Single(input[0] & 0x1F) }; // the first four bits of the second byte is month, 15 means every month let month = if input[1] & 0xF0 == 0xF0 { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(match input[1] & 0xF { - // warning: 0 is actually invalid, but used, to prevent an invalid data - // this is a work around needs to be improved - 0 | 0x1 => Month::January, - 0x2 => Month::February, - 0x3 => Month::March, - 0x4 => Month::April, - 0x5 => Month::May, - 0x6 => Month::June, - 0x7 => Month::July, - 0x8 => Month::August, - 0x9 => Month::September, - 0xA => Month::October, - 0xB => Month::November, - 0xC => Month::December, - _ => return Err(DataRecordError::InvalidData), - }) + match input[1] & 0xF { + 0x1 => SingleEveryOrInvalid::Single(Month::January), + 0x2 => SingleEveryOrInvalid::Single(Month::February), + 0x3 => SingleEveryOrInvalid::Single(Month::March), + 0x4 => SingleEveryOrInvalid::Single(Month::April), + 0x5 => SingleEveryOrInvalid::Single(Month::May), + 0x6 => SingleEveryOrInvalid::Single(Month::June), + 0x7 => SingleEveryOrInvalid::Single(Month::July), + 0x8 => SingleEveryOrInvalid::Single(Month::August), + 0x9 => SingleEveryOrInvalid::Single(Month::September), + 0xA => SingleEveryOrInvalid::Single(Month::October), + 0xB => SingleEveryOrInvalid::Single(Month::November), + 0xC => SingleEveryOrInvalid::Single(Month::December), + _ => SingleEveryOrInvalid::Invalid(), + } }; let year = (u16::from(input[1] & 0xF0) >> 1) | (u16::from(input[0] & 0xE0) >> 5); let year = if year == 0x7F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(year) + SingleEveryOrInvalid::Single(year) }; Ok(Data { @@ -667,51 +670,49 @@ impl DataFieldCoding { return Err(DataRecordError::InsufficientData); } let minutes = if input[0] == 0x3F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[0] & 0x3F) + SingleEveryOrInvalid::Single(input[0] & 0x3F) }; let hour = if input[1] == 0x1F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[1] & 0x1F) + SingleEveryOrInvalid::Single(input[1] & 0x1F) }; let day = if input[2] == 0x1F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[2] & 0x1F) + SingleEveryOrInvalid::Single(input[2] & 0x1F) }; let month = if input[3] == 0x0F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(match input[3] & 0x0F { - // warning: 0 is actually invalid, but used, to prevent an invalid data - // this is a work around needs to be improved - 0 | 0x1 => Month::January, - 0x2 => Month::February, - 0x3 => Month::March, - 0x4 => Month::April, - 0x5 => Month::May, - 0x6 => Month::June, - 0x7 => Month::July, - 0x8 => Month::August, - 0x9 => Month::September, - 0xA => Month::October, - 0xB => Month::November, - 0xC => Month::December, - _ => return Err(DataRecordError::InvalidData), - }) + match input[1] & 0xF { + 0x1 => SingleEveryOrInvalid::Single(Month::January), + 0x2 => SingleEveryOrInvalid::Single(Month::February), + 0x3 => SingleEveryOrInvalid::Single(Month::March), + 0x4 => SingleEveryOrInvalid::Single(Month::April), + 0x5 => SingleEveryOrInvalid::Single(Month::May), + 0x6 => SingleEveryOrInvalid::Single(Month::June), + 0x7 => SingleEveryOrInvalid::Single(Month::July), + 0x8 => SingleEveryOrInvalid::Single(Month::August), + 0x9 => SingleEveryOrInvalid::Single(Month::September), + 0xA => SingleEveryOrInvalid::Single(Month::October), + 0xB => SingleEveryOrInvalid::Single(Month::November), + 0xC => SingleEveryOrInvalid::Single(Month::December), + _ => SingleEveryOrInvalid::Invalid(), + } }; // year [21 to 23; 28 to 31] let year = (u16::from(input[3] & 0xF0) >> 1) | (u16::from(input[2] & 0xE0) >> 5); let year = if year == 0x7F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(year) + SingleEveryOrInvalid::Single(year) }; Ok(Data { value: Some(DataType::DateTime(day, month, year, hour, minutes)), @@ -723,19 +724,19 @@ impl DataFieldCoding { return Err(DataRecordError::InsufficientData); } let seconds = if input[0] == 0x3F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[0] & 0x3F) + SingleEveryOrInvalid::Single(input[0] & 0x3F) }; let minutes = if input[1] == 0x3F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[1] & 0x3F) + SingleEveryOrInvalid::Single(input[1] & 0x3F) }; let hours = if input[2] == 0x1F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[2] & 0x1F) + SingleEveryOrInvalid::Single(input[2] & 0x1F) }; Ok(Data { @@ -752,50 +753,48 @@ impl DataFieldCoding { return Err(DataRecordError::InsufficientData); } let seconds = if input[0] == 0x3F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[0] & 0x3F) + SingleEveryOrInvalid::Single(input[0] & 0x3F) }; let minutes = if input[1] == 0x3F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[1] & 0x3F) + SingleEveryOrInvalid::Single(input[1] & 0x3F) }; let hours = if input[2] == 0x1F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[2] & 0x1F) + SingleEveryOrInvalid::Single(input[2] & 0x1F) }; let days = if input[3] == 0x1F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(input[3] & 0x1F) + SingleEveryOrInvalid::Single(input[3] & 0x1F) }; let months = if input[4] == 0x0F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single(match input[4] & 0x0F { - // warning: 0 is actually invalid, but used, to prevent an invalid data - // this is a work around needs to be improved - 0 | 0x1 => Month::January, - 0x2 => Month::February, - 0x3 => Month::March, - 0x4 => Month::April, - 0x5 => Month::May, - 0x6 => Month::June, - 0x7 => Month::July, - 0x8 => Month::August, - 0x9 => Month::September, - 0xA => Month::October, - 0xB => Month::November, - 0xC => Month::December, - _ => return Err(DataRecordError::InvalidData), - }) + match input[1] & 0xF { + 0x1 => SingleEveryOrInvalid::Single(Month::January), + 0x2 => SingleEveryOrInvalid::Single(Month::February), + 0x3 => SingleEveryOrInvalid::Single(Month::March), + 0x4 => SingleEveryOrInvalid::Single(Month::April), + 0x5 => SingleEveryOrInvalid::Single(Month::May), + 0x6 => SingleEveryOrInvalid::Single(Month::June), + 0x7 => SingleEveryOrInvalid::Single(Month::July), + 0x8 => SingleEveryOrInvalid::Single(Month::August), + 0x9 => SingleEveryOrInvalid::Single(Month::September), + 0xA => SingleEveryOrInvalid::Single(Month::October), + 0xB => SingleEveryOrInvalid::Single(Month::November), + 0xC => SingleEveryOrInvalid::Single(Month::December), + _ => SingleEveryOrInvalid::Invalid(), + } }; let year = if input[5] == 0x7F { - SingleOrEvery::Every() + SingleEveryOrInvalid::Every() } else { - SingleOrEvery::Single( + SingleEveryOrInvalid::Single( (u16::from(input[5] & 0xF0) >> 1) | (u16::from(input[4] & 0xE0) >> 5), ) }; From cdafbb8e4094fe6406d09057e6e130ba297ddc10 Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 7 Jul 2024 12:49:10 +0200 Subject: [PATCH 09/10] using macros to reduce redundancy and make the code a bit more readable --- src/user_data/data_information.rs | 202 ++++++++++-------------------- 1 file changed, 63 insertions(+), 139 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index fb82f97..9175e21 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -392,6 +392,51 @@ impl Data<'_> { } } +#[macro_use] +macro_rules! parse_single_or_every { + ($input:expr, $mask:expr, $all_value:expr, $shift:expr) => { + if $input & $mask == $all_value { + SingleEveryOrInvalid::Every() + } else { + SingleEveryOrInvalid::Single(($input & $mask) >> $shift) + } + }; +} + +#[macro_use] +macro_rules! parse_month { + ($input:expr) => { + match $input & 0xF { + 0x1 => SingleEveryOrInvalid::Single(Month::January), + 0x2 => SingleEveryOrInvalid::Single(Month::February), + 0x3 => SingleEveryOrInvalid::Single(Month::March), + 0x4 => SingleEveryOrInvalid::Single(Month::April), + 0x5 => SingleEveryOrInvalid::Single(Month::May), + 0x6 => SingleEveryOrInvalid::Single(Month::June), + 0x7 => SingleEveryOrInvalid::Single(Month::July), + 0x8 => SingleEveryOrInvalid::Single(Month::August), + 0x9 => SingleEveryOrInvalid::Single(Month::September), + 0xA => SingleEveryOrInvalid::Single(Month::October), + 0xB => SingleEveryOrInvalid::Single(Month::November), + 0xC => SingleEveryOrInvalid::Single(Month::December), + _ => SingleEveryOrInvalid::Invalid(), + } + }; +} + +#[macro_use] +macro_rules! parse_year { + ($input:expr, $mask_byte1:expr, $mask_byte2:expr, $all_value:expr) => {{ + let year = ((u16::from($input[1] & $mask_byte1) >> 1) + | (u16::from($input[0] & $mask_byte2) >> 5)) as u16; + if year == $all_value { + SingleEveryOrInvalid::Every() + } else { + SingleEveryOrInvalid::Single(year) + } + }}; +} + impl DataFieldCoding { pub fn parse<'a>(&self, input: &'a [u8]) -> Result, DataRecordError> { match self { @@ -626,39 +671,9 @@ impl DataFieldCoding { if input.len() < 2 { return Err(DataRecordError::InsufficientData); } - // lowest five bits of first byte is day, 0 means every day - let day = if input[0] & 0x1F == 0 { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[0] & 0x1F) - }; - - // the first four bits of the second byte is month, 15 means every month - let month = if input[1] & 0xF0 == 0xF0 { - SingleEveryOrInvalid::Every() - } else { - match input[1] & 0xF { - 0x1 => SingleEveryOrInvalid::Single(Month::January), - 0x2 => SingleEveryOrInvalid::Single(Month::February), - 0x3 => SingleEveryOrInvalid::Single(Month::March), - 0x4 => SingleEveryOrInvalid::Single(Month::April), - 0x5 => SingleEveryOrInvalid::Single(Month::May), - 0x6 => SingleEveryOrInvalid::Single(Month::June), - 0x7 => SingleEveryOrInvalid::Single(Month::July), - 0x8 => SingleEveryOrInvalid::Single(Month::August), - 0x9 => SingleEveryOrInvalid::Single(Month::September), - 0xA => SingleEveryOrInvalid::Single(Month::October), - 0xB => SingleEveryOrInvalid::Single(Month::November), - 0xC => SingleEveryOrInvalid::Single(Month::December), - _ => SingleEveryOrInvalid::Invalid(), - } - }; - let year = (u16::from(input[1] & 0xF0) >> 1) | (u16::from(input[0] & 0xE0) >> 5); - let year = if year == 0x7F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(year) - }; + let day = parse_single_or_every!(input[0], 0x1F, 0, 0); + let month = parse_month!(input[1]); + let year = parse_year!(input, 0xF0, 0xE0, 0x7F); Ok(Data { value: Some(DataType::Date(day, month, year)), @@ -669,51 +684,12 @@ impl DataFieldCoding { if input.len() < 4 { return Err(DataRecordError::InsufficientData); } - let minutes = if input[0] == 0x3F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[0] & 0x3F) - }; - - let hour = if input[1] == 0x1F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[1] & 0x1F) - }; - - let day = if input[2] == 0x1F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[2] & 0x1F) - }; - - let month = if input[3] == 0x0F { - SingleEveryOrInvalid::Every() - } else { - match input[1] & 0xF { - 0x1 => SingleEveryOrInvalid::Single(Month::January), - 0x2 => SingleEveryOrInvalid::Single(Month::February), - 0x3 => SingleEveryOrInvalid::Single(Month::March), - 0x4 => SingleEveryOrInvalid::Single(Month::April), - 0x5 => SingleEveryOrInvalid::Single(Month::May), - 0x6 => SingleEveryOrInvalid::Single(Month::June), - 0x7 => SingleEveryOrInvalid::Single(Month::July), - 0x8 => SingleEveryOrInvalid::Single(Month::August), - 0x9 => SingleEveryOrInvalid::Single(Month::September), - 0xA => SingleEveryOrInvalid::Single(Month::October), - 0xB => SingleEveryOrInvalid::Single(Month::November), - 0xC => SingleEveryOrInvalid::Single(Month::December), - _ => SingleEveryOrInvalid::Invalid(), - } - }; - - // year [21 to 23; 28 to 31] - let year = (u16::from(input[3] & 0xF0) >> 1) | (u16::from(input[2] & 0xE0) >> 5); - let year = if year == 0x7F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(year) - }; + let minutes = parse_single_or_every!(input[0], 0x3F, 0x3F, 0); + let hour = parse_single_or_every!(input[1], 0x1F, 0x1F, 0); + let day = parse_single_or_every!(input[2], 0x1F, 0x1F, 0); + let month = parse_month!(input[3]); + let year = parse_year!(input, 0xF0, 0xE0, 0x7F); + Ok(Data { value: Some(DataType::DateTime(day, month, year, hour, minutes)), size: 4, @@ -723,21 +699,9 @@ impl DataFieldCoding { if input.len() < 2 { return Err(DataRecordError::InsufficientData); } - let seconds = if input[0] == 0x3F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[0] & 0x3F) - }; - let minutes = if input[1] == 0x3F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[1] & 0x3F) - }; - let hours = if input[2] == 0x1F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[2] & 0x1F) - }; + let seconds = parse_single_or_every!(input[0], 0x3F, 0x3F, 0); + let minutes = parse_single_or_every!(input[1], 0x3F, 0x3F, 0); + let hours = parse_single_or_every!(input[2], 0x1F, 0x1F, 0); Ok(Data { value: Some(DataType::Time(seconds, minutes, hours)), @@ -752,52 +716,12 @@ impl DataFieldCoding { if input.len() < 6 { return Err(DataRecordError::InsufficientData); } - let seconds = if input[0] == 0x3F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[0] & 0x3F) - }; - let minutes = if input[1] == 0x3F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[1] & 0x3F) - }; - let hours = if input[2] == 0x1F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[2] & 0x1F) - }; - let days = if input[3] == 0x1F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single(input[3] & 0x1F) - }; - let months = if input[4] == 0x0F { - SingleEveryOrInvalid::Every() - } else { - match input[1] & 0xF { - 0x1 => SingleEveryOrInvalid::Single(Month::January), - 0x2 => SingleEveryOrInvalid::Single(Month::February), - 0x3 => SingleEveryOrInvalid::Single(Month::March), - 0x4 => SingleEveryOrInvalid::Single(Month::April), - 0x5 => SingleEveryOrInvalid::Single(Month::May), - 0x6 => SingleEveryOrInvalid::Single(Month::June), - 0x7 => SingleEveryOrInvalid::Single(Month::July), - 0x8 => SingleEveryOrInvalid::Single(Month::August), - 0x9 => SingleEveryOrInvalid::Single(Month::September), - 0xA => SingleEveryOrInvalid::Single(Month::October), - 0xB => SingleEveryOrInvalid::Single(Month::November), - 0xC => SingleEveryOrInvalid::Single(Month::December), - _ => SingleEveryOrInvalid::Invalid(), - } - }; - let year = if input[5] == 0x7F { - SingleEveryOrInvalid::Every() - } else { - SingleEveryOrInvalid::Single( - (u16::from(input[5] & 0xF0) >> 1) | (u16::from(input[4] & 0xE0) >> 5), - ) - }; + let seconds = parse_single_or_every!(input[0], 0x3F, 0x3F, 0); + let minutes = parse_single_or_every!(input[1], 0x3F, 0x3F, 0); + let hours = parse_single_or_every!(input[2], 0x1F, 0x1F, 0); + let days = parse_single_or_every!(input[3], 0x1F, 0x1F, 0); + let months = parse_month!(input[4]); + let year = parse_year!(input, 0xF0, 0xE0, 0x7F); Ok(Data { value: Some(DataType::DateTimeWithSeconds( From d5711bffc613ba2de95c1847e54580d13cf2600d Mon Sep 17 00:00:00 2001 From: heroichornet Date: Sun, 7 Jul 2024 12:58:30 +0200 Subject: [PATCH 10/10] fixing cargo fmt --- src/user_data/data_information.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 9175e21..703307c 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -392,7 +392,6 @@ impl Data<'_> { } } -#[macro_use] macro_rules! parse_single_or_every { ($input:expr, $mask:expr, $all_value:expr, $shift:expr) => { if $input & $mask == $all_value { @@ -403,7 +402,6 @@ macro_rules! parse_single_or_every { }; } -#[macro_use] macro_rules! parse_month { ($input:expr) => { match $input & 0xF { @@ -424,7 +422,6 @@ macro_rules! parse_month { }; } -#[macro_use] macro_rules! parse_year { ($input:expr, $mask_byte1:expr, $mask_byte2:expr, $all_value:expr) => {{ let year = ((u16::from($input[1] & $mask_byte1) >> 1)