Skip to content

Commit

Permalink
Merge pull request #24 from maebli/feature/manufacturer-specific-data…
Browse files Browse the repository at this point in the history
…-record

Feature/manufacturer specific data record
  • Loading branch information
maebli authored Sep 23, 2024
2 parents cb02790 + 33494c7 commit 09d650a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 68 deletions.
32 changes: 20 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,19 +255,27 @@ fn parse_to_table(input: &str) -> std::string::String {

if let Some(data_records) = parsed_data.data_records {
for record in data_records.flatten() {
let value_information = match record
.data_record_header
.processed_data_record_header
.value_information
{
Some(x) => format!("{}", x),
None => "None".to_string(),
};

let data_information = match record
.data_record_header
.processed_data_record_header
.data_information
{
Some(x) => format!("{}", x),
None => "None".to_string(),
};

table.add_row(row![
format!(
"({}{}",
record.data,
record
.data_record_header
.processed_data_record_header
.value_information
),
record
.data_record_header
.processed_data_record_header
.data_information
format!("({}{:}", record.data, value_information),
format!("{}", data_information)
]);
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/user_data/data_information.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,13 @@ pub enum DataType<'a> {
SingleEveryOrInvalid<Minute>,
SingleEveryOrInvalid<Second>,
),
ManufacturerSpecific(&'a [u8]),
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(PartialEq, Debug)]
pub struct Data<'a> {
value: Option<DataType<'a>>,
size: usize,
pub value: Option<DataType<'a>>,
pub size: usize,
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -384,6 +385,10 @@ impl std::fmt::Display for Data<'_> {
DataType::Time(seconds, minutes, hours) => {
write!(f, "{}:{}:{}", hours, minutes, seconds)
}
DataType::Text(text_unit) => todo!(),
DataType::ManufacturerSpecific(data) => {
write!(f, "Manufacturer Specific: {:?}", data)
}
},
None => write!(f, "No Data"),
}
Expand Down
126 changes: 77 additions & 49 deletions src/user_data/data_record.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use super::{
data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock},
data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock, DataType},
value_information::{ValueInformation, ValueInformationBlock, ValueLabel},
variable_user_data::DataRecordError,
};
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct RawDataRecordHeader<'a> {
pub data_information_block: DataInformationBlock<'a>,
pub value_information_block: ValueInformationBlock,
pub value_information_block: Option<ValueInformationBlock>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, PartialEq)]
pub struct ProcessedDataRecordHeader {
pub data_information: DataInformation,
pub value_information: ValueInformation,
pub data_information: Option<DataInformation>,
pub value_information: Option<ValueInformation>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, PartialEq)]
Expand All @@ -28,6 +28,7 @@ impl DataRecord<'_> {
self.data_record_header.get_size() + self.data.get_size()
}
}

#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct DataRecordHeader<'a> {
Expand All @@ -38,13 +39,15 @@ pub struct DataRecordHeader<'a> {
impl DataRecordHeader<'_> {
#[must_use]
pub fn get_size(&self) -> usize {
self.raw_data_record_header
let s = self
.raw_data_record_header
.data_information_block
.get_size()
+ self
.raw_data_record_header
.value_information_block
.get_size()
.get_size();
if let Some(x) = &self.raw_data_record_header.value_information_block {
s + x.get_size()
} else {
s
}
}
}

Expand All @@ -53,10 +56,16 @@ impl<'a> TryFrom<&'a [u8]> for RawDataRecordHeader<'a> {
fn try_from(data: &[u8]) -> Result<RawDataRecordHeader, DataRecordError> {
let difb = DataInformationBlock::try_from(data)?;
let offset = difb.get_size();
let vifb = ValueInformationBlock::try_from(
data.get(offset..)
.ok_or(DataRecordError::InsufficientData)?,
)?;

let mut vifb = None;

if difb.data_information_field.data != 0x0F {
vifb = Some(ValueInformationBlock::try_from(
data.get(offset..)
.ok_or(DataRecordError::InsufficientData)?,
)?);
}

Ok(RawDataRecordHeader {
data_information_block: difb,
value_information_block: vifb,
Expand All @@ -67,26 +76,31 @@ impl<'a> TryFrom<&'a [u8]> for RawDataRecordHeader<'a> {
impl<'a> TryFrom<&RawDataRecordHeader<'a>> for ProcessedDataRecordHeader {
type Error = DataRecordError;
fn try_from(raw_data_record_header: &RawDataRecordHeader) -> Result<Self, DataRecordError> {
let value_information =
ValueInformation::try_from(&raw_data_record_header.value_information_block)?;
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;
let mut value_information = None;
let mut data_information = None;

if let Some(x) = &raw_data_record_header.value_information_block {
let v = ValueInformation::try_from(x)?;

let mut d = 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 v.labels.contains(&ValueLabel::Date) {
d.data_field_coding = DataFieldCoding::DateTypeG;
} else if v.labels.contains(&ValueLabel::DateTime) {
d.data_field_coding = DataFieldCoding::DateTimeTypeF;
} else if v.labels.contains(&ValueLabel::Time) {
d.data_field_coding = DataFieldCoding::DateTimeTypeJ;
} else if v.labels.contains(&ValueLabel::DateTimeWithSeconds) {
d.data_field_coding = DataFieldCoding::DateTimeTypeI;
}

value_information = Some(v);
data_information = Some(d);
}

Ok(Self {
data_information,
value_information,
Expand All @@ -109,27 +123,34 @@ impl<'a> TryFrom<&'a [u8]> for DataRecordHeader<'a> {

impl<'a> TryFrom<&'a [u8]> for DataRecord<'a> {
type Error = DataRecordError;
fn try_from(data: &[u8]) -> Result<DataRecord, DataRecordError> {
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
let data_record_header = DataRecordHeader::try_from(data)?;
let offset = data_record_header
let mut offset = data_record_header
.raw_data_record_header
.data_information_block
.get_size()
+ data_record_header
.raw_data_record_header
.value_information_block
.get_size();
let data = data_record_header
.processed_data_record_header
.data_information
.data_field_coding
.parse(
data.get(offset..)
.ok_or(DataRecordError::InsufficientData)?,
)?;
.get_size();
let mut data_out = Data {
value: Some(DataType::ManufacturerSpecific(data)),
size: data.len(),
};
if let Some(x) = &data_record_header
.raw_data_record_header
.value_information_block
{
offset += x.get_size();
if let Some(data_info) = &data_record_header
.processed_data_record_header
.data_information
{
data_out = data_info.data_field_coding.parse(
data.get(offset..)
.ok_or(DataRecordError::InsufficientData)?,
)?;
}
}
Ok(DataRecord {
data_record_header,
data,
data: data_out,
})
}
}
Expand All @@ -143,4 +164,11 @@ mod tests {
let data = &[0x03, 0x13, 0x15, 0x31, 0x00];
let _result = DataRecordHeader::try_from(data.as_slice());
}
#[test]
#[cfg(feature = "std")]
fn test_manufacturer_specific_block() {
let data = [0x0F, 0x01, 0x02, 0x03, 0x04];
let result = DataRecord::try_from(data.as_slice());
println!("{:?}", result);
}
}
6 changes: 1 addition & 5 deletions src/user_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ impl<'a> Iterator for DataRecords<'a> {

while self.offset < self.data.len() {
match self.data.get(self.offset)? {
0x0F => {
/* TODO: parse manufacturer specific */
self.offset = self.data.len();
}
0x1F => {
/* TODO: parse manufacturer specific */
_more_records_follow = true;
Expand Down Expand Up @@ -126,7 +122,7 @@ pub enum Direction {
MasterToSlave,
}

// implement from trait for diirection
// implement from trait for direction
impl From<ControlInformation> for Direction {
fn from(single_byte: ControlInformation) -> Self {
match single_byte {
Expand Down

0 comments on commit 09d650a

Please sign in to comment.