use super::parser::FitDataMessage;
use super::DecodeOption;
use crate::error::Result;
use crate::profile::{data_field_with_info, FieldDataType, MesgNum, TimestampField};
use crate::{DeveloperFieldDescription, ErrorKind, FitDataField, FitDataRecord, Value};
use std::collections::{HashMap, HashSet, VecDeque};
use std::convert::{From, TryInto};
pub struct Decoder {
base_timestamp: TimestampField,
accumulate_fields: HashMap<u32, Value>,
developer_field_descriptions: HashMap<(u8, u8), DeveloperFieldDescription>,
}
impl Decoder {
pub fn new() -> Self {
Decoder {
base_timestamp: TimestampField::Utc(0),
accumulate_fields: HashMap::new(),
developer_field_descriptions: HashMap::new(),
}
}
pub fn reset(&mut self) {
self.base_timestamp = TimestampField::Utc(0);
self.accumulate_fields = HashMap::new();
self.developer_field_descriptions = HashMap::new();
}
pub fn decode_message(
&mut self,
mut message: FitDataMessage,
options: &HashSet<DecodeOption>,
) -> Result<FitDataRecord> {
let mesg_num = MesgNum::from(message.global_message_number());
let mut record = FitDataRecord::new(mesg_num);
if let Some(value) = message.fields().get(&253) {
self.base_timestamp = TimestampField::Utc(value.clone().try_into().unwrap_or(0));
}
let mut fields =
mesg_num.decode_message(&mut message.fields, &mut self.accumulate_fields, options)?;
fields.sort_by_key(|f| f.number());
if mesg_num == MesgNum::FieldDescription {
let description = DeveloperFieldDescription::try_from(&fields)?;
self.developer_field_descriptions.insert(
(
description.developer_data_index,
description.field_definition_number,
),
description,
);
}
record.extend(fields);
self.decode_developer_fields(&mut record, &message.developer_fields, options)?;
if let Some(time_offset) = message.time_offset() {
record.push(FitDataField::new(
String::from("timestamp"),
253,
None,
self.update_timestamp(time_offset),
String::new(),
));
}
Ok(record)
}
fn update_timestamp(&mut self, offset: u8) -> Value {
let offset: i64 = offset as i64;
let mask: i64 = 31; let mut value = offset + (self.base_timestamp.as_i64() & !mask);
if offset < (self.base_timestamp.as_i64() & mask) {
value += 32;
}
match self.base_timestamp {
TimestampField::Local(_) => {
self.base_timestamp = TimestampField::Local(value);
}
TimestampField::Utc(_) => {
self.base_timestamp = TimestampField::Utc(value);
}
}
Value::from(self.base_timestamp)
}
pub fn developer_field_descriptions(&self) -> &HashMap<(u8, u8), DeveloperFieldDescription> {
&self.developer_field_descriptions
}
fn decode_developer_fields(
&self,
record: &mut FitDataRecord,
developer_data_map: &HashMap<(u8, u8), Value>,
options: &HashSet<DecodeOption>,
) -> Result<()> {
let mut entries: VecDeque<((u8, u8), Value)> = developer_data_map
.iter()
.map(|(k, v)| (*k, v.clone()))
.collect();
while let Some(((dev_data_idx, field_nr), value)) = entries.pop_front() {
let dev_definition = self
.developer_field_descriptions
.get(&(dev_data_idx, field_nr))
.ok_or(ErrorKind::MissingDeveloperDefinitionMessage())?;
record.push(data_field_with_info(
dev_definition.field_definition_number,
Some(dev_definition.developer_data_index),
&dev_definition.field_name,
FieldDataType::Byte,
dev_definition.scale,
dev_definition.offset,
&dev_definition.units,
value,
options,
)?);
}
Ok(())
}
}