use super::parser::FitDataMessage;
use crate::error::{ErrorKind, Result};
use crate::profile::{
get_field_variant_as_string, ComponentFieldInfo, FieldDataType, FieldInfo, MesgNum, MessageInfo,
};
use crate::{FitDataField, FitDataRecord, Value};
use chrono::{DateTime, Duration, Local, NaiveDate, TimeZone};
use std::collections::HashMap;
use std::convert::{From, TryInto};
use std::f64::EPSILON;
use std::iter::IntoIterator;
impl Value {
fn to_ne_bytes(&self) -> Vec<u8> {
match self {
Value::Byte(val) => vec![*val as u8],
Value::Enum(val) => vec![*val as u8],
Value::SInt8(val) => vec![*val as u8],
Value::UInt8(val) => vec![*val as u8],
Value::SInt16(val) => val.to_ne_bytes().to_vec(),
Value::UInt16(val) => val.to_ne_bytes().to_vec(),
Value::SInt32(val) => val.to_ne_bytes().to_vec(),
Value::UInt32(val) => val.to_ne_bytes().to_vec(),
Value::String(val) => val.as_bytes().to_vec(),
Value::Timestamp(val) => val.timestamp().to_ne_bytes().to_vec(),
Value::Float32(val) => val.to_ne_bytes().to_vec(),
Value::Float64(val) => val.to_ne_bytes().to_vec(),
Value::UInt8z(val) => vec![*val as u8],
Value::UInt16z(val) => val.to_ne_bytes().to_vec(),
Value::UInt32z(val) => val.to_ne_bytes().to_vec(),
Value::SInt64(val) => val.to_ne_bytes().to_vec(),
Value::UInt64(val) => val.to_ne_bytes().to_vec(),
Value::UInt64z(val) => val.to_ne_bytes().to_vec(),
Value::Array(vals) => vals.iter().flat_map(|v| v.to_ne_bytes()).collect(),
}
}
}
#[derive(Debug, Copy, Clone)]
enum TimestampField {
Local(i64),
Utc(i64),
}
impl TimestampField {
fn as_i64(&self) -> i64 {
match self {
Self::Local(value) => *value,
Self::Utc(value) => *value,
}
}
fn to_date_time(self) -> DateTime<Local> {
let ref_date = NaiveDate::from_ymd(1989, 12, 31).and_hms(0, 0, 0);
match self {
Self::Local(value) => {
TimeZone::from_local_datetime(&Local, &ref_date).unwrap() + Duration::seconds(value)
}
Self::Utc(value) => {
TimeZone::from_utc_datetime(&Local, &ref_date) + Duration::seconds(value)
}
}
}
}
impl From<TimestampField> for Value {
fn from(timestamp: TimestampField) -> Value {
Value::Timestamp(timestamp.to_date_time())
}
}
pub struct Decoder {
base_timestamp: TimestampField,
accumulate_fields: HashMap<u32, Value>,
}
impl Decoder {
pub fn new() -> Self {
Decoder {
base_timestamp: TimestampField::Utc(0),
accumulate_fields: HashMap::new(),
}
}
pub fn reset(&mut self) {
self.base_timestamp = TimestampField::Utc(0);
self.accumulate_fields = HashMap::new();
}
pub fn decode_message(&mut self, message: FitDataMessage) -> Result<FitDataRecord> {
let mesg_num = MesgNum::from(message.global_message_number());
let mesg_info = mesg_num.message_info();
let mut record = FitDataRecord::new(mesg_num);
if let Some(Some(value)) = message.fields().get(&253) {
let value = value.clone();
self.base_timestamp =
if let Some(info) = get_message_field(&mesg_info, 253, &HashMap::new()) {
if let FieldDataType::LocalDateTime = info.field_type() {
TimestampField::Local(value.try_into().unwrap_or(0))
} else {
TimestampField::Utc(value.try_into().unwrap_or(0))
}
} else {
TimestampField::Utc(value.try_into().unwrap_or(0))
}
}
for field in self.build_data_fields_from_map(mesg_info, message.fields())? {
record.push(field);
}
if let Some(time_offset) = message.time_offset() {
record.push(FitDataField::new(
String::from("timestamp"),
253,
self.update_timestamp(time_offset),
String::new(),
));
}
Ok(record)
}
fn build_data_fields_from_map(
&mut self,
mesg_info: MessageInfo,
data_map: &HashMap<u8, Option<Value>>,
) -> Result<Vec<FitDataField>> {
let msg_num = mesg_info.global_message_number().as_u16();
let mut data_fields = Vec::new();
let mut data_map: HashMap<u8, Value> = data_map
.iter()
.filter_map(|(key, val)| val.as_ref().map(|v| (*key, v.clone())))
.collect();
let mut process_queue: Vec<(u8, Option<FieldInfo>)> = data_map
.keys()
.map(|k| (*k, get_message_field(&mesg_info, *k, &data_map).cloned()))
.collect();
while !process_queue.is_empty() {
let (def_num, field_info) = process_queue.remove(0);
let mut value = data_map[&def_num].clone();
if let Some(field_info) = field_info {
if field_info.components().is_empty() {
if field_info.accumulate() {
value = self.accumlate_value(msg_num, def_num, value)?;
}
data_fields.push(data_field_with_info(&field_info, value)?);
} else {
let (infos, values): (Vec<_>, Vec<_>) =
expand_components(&field_info, &value).into_iter().unzip();
for (comp_info, comp_value) in infos.iter().zip(values.into_iter()) {
data_map.insert(comp_info.dest_def_number(), comp_value);
}
for comp_info in infos {
let dest_def_number = comp_info.dest_def_number();
let old_field_info =
get_message_field(&mesg_info, comp_info.dest_def_number(), &data_map);
let new_field_info = old_field_info.map(|i| comp_info.to_field_info(i));
process_queue.push((dest_def_number, new_field_info));
}
}
} else {
data_fields.push(unknown_field(def_num, value));
}
}
data_fields.sort_by_key(|f| f.number());
Ok(data_fields)
}
fn accumlate_value(&mut self, msg_num: u16, def_num: u8, value: Value) -> Result<Value> {
macro_rules! only_add_like_values {
($key:ident, $val:ident, $stored_value:ident, $variant:ident) => {
if let Value::$variant(other) = value {
let value = Value::$variant($val + other);
self.accumulate_fields.insert($key, value.clone());
Ok(value)
} else {
Err(ErrorKind::ValueError(format!(
"Mixed type addition {} and {} cannot be combined",
$stored_value, value
))
.into())
}
};
}
let key = (msg_num as u32) << 8 | def_num as u32;
if let Some(stored_value) = self.accumulate_fields.get(&key) {
match stored_value {
Value::Timestamp(_) => Err(ErrorKind::ValueError(
"Cannot accumlate timestamp fields".to_string(),
)
.into()),
Value::Byte(val) => only_add_like_values!(key, val, stored_value, Byte),
Value::Enum(_) => {
Err(ErrorKind::ValueError("Cannot accumlate enum fields".to_string()).into())
}
Value::SInt8(val) => only_add_like_values!(key, val, stored_value, SInt8),
Value::UInt8(val) => only_add_like_values!(key, val, stored_value, UInt8),
Value::UInt8z(val) => only_add_like_values!(key, val, stored_value, UInt8z),
Value::SInt16(val) => only_add_like_values!(key, val, stored_value, SInt16),
Value::UInt16(val) => only_add_like_values!(key, val, stored_value, UInt16),
Value::UInt16z(val) => only_add_like_values!(key, val, stored_value, UInt16z),
Value::SInt32(val) => only_add_like_values!(key, val, stored_value, SInt32),
Value::UInt32(val) => only_add_like_values!(key, val, stored_value, UInt32),
Value::UInt32z(val) => only_add_like_values!(key, val, stored_value, UInt32z),
Value::SInt64(val) => only_add_like_values!(key, val, stored_value, SInt64),
Value::UInt64(val) => only_add_like_values!(key, val, stored_value, UInt64),
Value::UInt64z(val) => only_add_like_values!(key, val, stored_value, UInt64z),
Value::Float32(val) => only_add_like_values!(key, val, stored_value, Float32),
Value::Float64(val) => only_add_like_values!(key, val, stored_value, Float64),
Value::String(_) => {
Err(ErrorKind::ValueError("Cannot accumlate string fields".to_string()).into())
}
Value::Array(_) => {
Err(ErrorKind::ValueError("Cannot accumlate array fields".to_string()).into())
}
}
} else {
self.accumulate_fields.insert(key, value.clone());
Ok(value)
}
}
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)
}
}
fn get_message_field<'a>(
msg: &'a MessageInfo,
key: u8,
data_map: &HashMap<u8, Value>,
) -> Option<&'a FieldInfo> {
if let Some(field) = msg.fields().get(&key) {
for (num, val, sub_info) in field.subfields() {
if let Some(v) = data_map.get(num) {
if v.clone().try_into().map_or(false, |v: i64| v == *val) {
return Some(sub_info);
}
}
}
return Some(field);
}
None
}
fn expand_components<'a>(
field_info: &'a FieldInfo,
value: &Value,
) -> Vec<(&'a ComponentFieldInfo, Value)> {
let bit_mask = [1u8, 2u8, 4u8, 8u8, 16u8, 32u8, 64u8, 128u8];
let mut bytes = value.to_ne_bytes().into_iter();
let mut components = Vec::new();
let mut byte = bytes.next().unwrap_or(0);
let mut bit_pos = 0;
for comp_fld in field_info.components() {
let mut tmp: u64 = 0;
for pos in 0..comp_fld.bits() {
tmp |= (((byte & bit_mask[bit_pos]) >> bit_pos) as u64) << pos;
if bit_pos == 7 {
byte = bytes.next().unwrap_or(0);
bit_pos = 0;
} else {
bit_pos += 1;
}
}
components.push((comp_fld, Value::UInt64(tmp)));
}
components
}
fn convert_value(field_info: &FieldInfo, value: Value) -> Result<Value> {
if let Value::Array(vals) = value {
return Ok(Value::Array(vals));
}
match field_info.field_type() {
FieldDataType::DateTime => {
return Ok(Value::from(TimestampField::Utc(
value.try_into().unwrap_or(0),
)));
}
FieldDataType::LocalDateTime => {
return Ok(Value::from(TimestampField::Local(
value.try_into().unwrap_or(0),
)));
}
_ => (),
}
if field_info.field_type().is_enum_type() {
let val: i64 = value.try_into()?;
Ok(Value::String(get_field_variant_as_string(
field_info.field_type(),
val,
)))
} else if ((field_info.scale() - 1.0).abs() > EPSILON)
|| ((field_info.offset() - 0.0).abs() > EPSILON)
{
let val: f64 = value.try_into()?;
Ok(Value::Float64(
val / field_info.scale() - field_info.offset(),
))
} else {
Ok(value)
}
}
fn data_field_with_info(field_info: &FieldInfo, value: Value) -> Result<FitDataField> {
let value = convert_value(field_info, value)?;
Ok(FitDataField::new(
field_info.name().to_string(),
field_info.def_number(),
value,
field_info.units().to_string(),
))
}
fn unknown_field(field_def_num: u8, value: Value) -> FitDataField {
FitDataField::new(
format!("unknown_field_{}", field_def_num),
field_def_num,
value,
String::new(),
)
}