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

Feature/manufacturer specific data record #24

Merged
merged 5 commits into from
Sep 23, 2024
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
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