1pub mod protocol;
2
3use crate::protocol::io::{skip_bytes, write_bin};
4use crate::protocol::macros::get_field_value;
5use crate::protocol::message_type::MessageType;
6use crate::protocol::value::Value;
7use crate::protocol::{
8 calculate_fit_crc, DataMessage, DefinitionMessage, FitDataMessage, FitDefinitionMessage,
9 FitHeader, FitMessage, FitMessageHeader,
10};
11use binrw::{BinReaderExt, BinResult, BinWrite, Endian, Error};
12use std::collections::VecDeque;
13use std::fmt;
14use std::fmt::{Debug, Formatter};
15use std::fs::{read, write};
16use std::io::{Cursor, Seek, SeekFrom, Write};
17use std::ops::Div;
18use std::path::Path;
19
20#[derive(Clone)]
21pub struct Fit {
22 pub header: FitHeader,
23
24 pub data: Vec<FitMessage>,
25}
26
27impl Debug for Fit {
28 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
29 f.debug_struct("Fit").field("header", &self.header).finish()
30 }
31}
32
33impl Fit {
34 pub fn read(buf: Vec<u8>) -> BinResult<Self> {
35 let mut cursor = Cursor::new(buf);
36 let header: FitHeader = cursor.read_ne()?;
37 let mut queue: VecDeque<(u8, FitDefinitionMessage)> = VecDeque::new();
38
39 let mut data: Vec<FitMessage> = Vec::new();
40 loop {
41 let message_header: FitMessageHeader = cursor.read_ne()?;
42 match message_header.definition {
43 true => {
44 if message_header.dev_fields {
45 unimplemented!("message_header.dev_fields is unimplemented");
46 }
47 let definition_message: DefinitionMessage =
48 cursor.read_ne_args((message_header.dev_fields,))?;
49
50 let local_num = message_header.local_num;
51 let def = FitDefinitionMessage {
52 header: message_header,
53 data: definition_message,
54 };
55 data.push(FitMessage::Definition(def.clone()));
56 queue.push_front((local_num, def));
57 }
58 false => {
59 let definition = match queue.iter().find(|x| x.0 == message_header.local_num) {
60 None => continue,
61 Some((_, def)) => def,
62 };
63 let data_message: DataMessage = cursor.read_ne_args((definition,))?;
64 if data_message.message_type == MessageType::None {
65 continue;
66 }
67 data.push(FitMessage::Data(FitDataMessage {
68 header: message_header,
69 data: data_message,
70 }));
71 if cursor.position() >= (header.data_size + header.header_size as u32) as u64 {
72 break;
73 }
74 }
75 }
76 }
77 Ok(Fit { header, data })
78 }
79
80 pub fn write<P: AsRef<Path>>(&self, file: P) -> BinResult<()> {
81 let mut buf = Vec::with_capacity(
82 (self.header.data_size + self.header.header_size as u32 + 2) as usize,
83 );
84 let header = self.write_buf(&mut buf)?;
85 Fit::write_crc(header, &mut buf)?;
86 write(file, &buf)?;
87 Ok(())
88 }
89
90 fn write_crc(header: FitHeader, buf: &mut Vec<u8>) -> BinResult<()> {
91 let mut header_crc: Option<u16> = None;
92 if header.crc.is_some() {
93 let header = &buf[0..(header.header_size - 2) as usize];
94 header_crc = Some(calculate_fit_crc(&header));
95 }
96 let end_byte = header.header_size as u32 + header.data_size;
97 let body = &buf[header.header_size as usize..end_byte as usize];
98 let body_crc = calculate_fit_crc(&body);
99 let mut writer = Cursor::new(buf);
100 match header_crc {
101 None => {}
102 Some(crc) => {
103 writer.seek(SeekFrom::Start(header.header_size as u64 - 2))?;
104 write_bin(&mut writer, crc, Endian::Little)?;
105 }
106 }
107 writer.seek(SeekFrom::End(0))?;
108 write_bin(&mut writer, body_crc, Endian::Little)?;
109 writer.flush()?;
110 Ok(())
111 }
112
113 pub(crate) fn write_buf(&self, buf: &mut Vec<u8>) -> BinResult<FitHeader> {
114 let mut queue: VecDeque<(u8, FitDefinitionMessage)> = VecDeque::new();
115 let mut writer = Cursor::new(buf);
116 skip_bytes(&mut writer, self.header.header_size);
117 for massage in &self.data {
118 match massage {
119 FitMessage::Definition(msg) => {
120 msg.header.write(&mut writer)?;
121 msg.data.write(&mut writer)?;
122 let local_num = msg.header.local_num;
123 queue.push_front((local_num, msg.clone()));
124 }
125 FitMessage::Data(msg) => {
126 let message_header = &msg.header;
127 let definition = match queue.iter().find(|x| x.0 == message_header.local_num) {
128 None => None,
129 Some((_, def)) => Some(def),
130 };
131 match definition {
132 None => {}
133 Some(def) => {
134 if &msg.data.message_type != &MessageType::None {
135 msg.header.write(&mut writer)?;
136 msg.data.write(&mut writer, &def.data)?;
137 }
138 }
139 }
140 }
141 }
142 }
143 let mut header = self.header.clone();
144 header.data_size = writer.position() as u32 - header.header_size as u32;
145 writer.seek(SeekFrom::Start(0))?;
146 header.write(&mut writer)?;
147 writer.flush()?;
148 Ok(header)
149 }
150}
151
152impl Fit {
153 #[allow(unused)]
154 pub fn merge<P: AsRef<Path>>(files: Vec<P>, path: P) -> BinResult<()> {
155 if files.is_empty() || files.len() <= 1 {
156 eprintln!("Error files is empty: {:?}", files.len());
157 return Err(Error::Io(binrw::io::Error::new(
158 binrw::io::ErrorKind::UnexpectedEof,
159 "Error files is empty!",
160 )));
161 }
162 let file = read(files.get(0).unwrap()).unwrap();
163 let mut fit: Fit = Fit::read(file)?;
164 let session: Option<(usize, FitDataMessage)> = fit.get_session();
166 let mut sessions: Vec<Option<(usize, FitDataMessage)>> = vec![session];
167 for i in 1..=files.len() - 1 {
168 let f = files.get(i).unwrap();
169 let f = read(f).unwrap();
170 let mut tmp = Fit::read(f)?;
171 sessions.push(tmp.get_session());
172
173 let to_move: Vec<_> = tmp
174 .data
175 .iter()
176 .enumerate()
177 .filter(|(_, message)| match message {
178 FitMessage::Definition(_) => false,
179 FitMessage::Data(msg) => {
180 return matches!(msg.data.message_type, MessageType::Record);
181 }
182 })
183 .map(|(i, _)| i)
184 .collect();
185
186 for i in to_move.into_iter().rev() {
187 let message = tmp.data.swap_remove(i);
189 fit.data.push(message);
190 }
191 }
192
193 fit.replace_session(sessions);
194 fit.write(path)
195 }
196
197 fn replace_session(&mut self, sessions: Vec<Option<(usize, FitDataMessage)>>) {
198 let mut index = 0;
199 let mut session_vec = vec![];
200 for session in sessions {
201 match session {
202 None => {}
203 Some((i, s)) => {
204 if index == 0 {
205 index = i;
206 }
207 session_vec.push(s);
208 }
209 }
210 }
211
212 let session = Fit::merge_sessions(session_vec);
213 match session {
214 None => {}
215 Some(session) => {
216 self.data[index] = FitMessage::Data(session);
217 }
218 }
219 }
220
221 fn merge_sessions(mut sessions: Vec<FitDataMessage>) -> Option<FitDataMessage> {
222 if sessions.is_empty() {
223 return None;
224 }
225 let mut merged_session = sessions.remove(0);
226 let mut max_stop_timestamp = Value::Time(u32::MIN);
228 let mut max_speed = Value::U16(u16::MIN);
229 let mut max_power = Value::U16(u16::MIN);
230 let mut max_altitude = Value::U16(u16::MIN);
231 let mut max_pos_grade = Value::I16(i16::MIN);
232 let mut max_neg_grade = Value::I16(i16::MIN);
233 let mut max_heart_rate = Value::U8(u8::MIN);
234 let mut max_cadence = Value::U8(u8::MIN);
235 let mut max_temperature = Value::U8(u8::MIN);
236 let mut min_start_timestamp = Value::Time(u32::MAX);
238 let mut min_altitude = Value::U16(u16::MAX);
239 let mut min_heart_rate = Value::U8(u8::MAX);
240 let mut total_elapsed_time = Value::U32(0_u32);
242 let mut total_timer_time = Value::U32(0_u32);
243 let mut total_distance = Value::U32(0_u32);
244 let mut total_moving_time = Value::U32(0_u32);
245 let mut total_calories = Value::U16(0_u16);
246 let mut total_ascent = Value::U16(0_u16);
247 let mut total_descent = Value::U16(0_u16);
248 let mut avg_speed = Value::I32(0_i32);
250 let mut avg_speed_count = 0_i32;
251 let mut avg_power = Value::I32(0_i32);
252 let mut avg_power_count = 0_i32;
253 let mut avg_altitude = Value::I32(0_i32);
254 let mut avg_altitude_count = 0_i32;
255 let mut avg_grade = Value::I32(0_i32);
256 let mut avg_grade_count = 0_i32;
257 let mut avg_pos_grade = Value::I32(0_i32);
258 let mut avg_pos_grade_count = 0_i32;
259 let mut avg_neg_grade = Value::I32(0_i32);
260 let mut avg_neg_grade_count = 0_i32;
261 let mut avg_pos_vertical_speed = Value::I32(0_i32);
262 let mut avg_pos_vertical_speed_count = 0_i32;
263 let mut avg_neg_vertical_speed = Value::I32(0_i32);
264 let mut avg_neg_vertical_speed_count = 0_i32;
265 let mut avg_heart_rate = Value::I32(0_i32);
266 let mut avg_heart_rate_count = 0_i32;
267 let mut avg_cadence = Value::I32(0_i32);
268 let mut avg_cadence_count = 0_i32;
269 let mut avg_temperature = Value::I32(0_i32);
270 let mut avg_temperature_count = 0_i32;
271
272 for session in sessions {
273 merge_stats!(
274 max 253, max_stop_timestamp, session,
276 max 15, max_speed, session,
277 max 21, max_power, session,
278 max 50, max_altitude, session,
279 max 55, max_pos_grade, session,
280 max 56, max_neg_grade, session,
281 max 17, max_heart_rate, session,
282 max 19, max_cadence, session,
283 max 58, max_temperature, session,
284 min 2, min_start_timestamp, session,
286 min 71, min_altitude, session,
287 min 64, min_heart_rate, session,
288 sum 7, total_elapsed_time, session,
290 sum 8, total_timer_time, session,
291 sum 9, total_distance, session,
292 sum 59, total_moving_time, session,
293 sum 11, total_calories, session,
294 sum 22, total_ascent, session,
295 sum 23, total_descent, session,
296 sum 23, total_descent, session,
297 avg 14, avg_speed, avg_speed_count, session,
299 avg 20, avg_power, avg_power_count, session,
300 avg 49, avg_altitude, avg_altitude_count, session,
301 avg 52, avg_grade, avg_grade_count, session,
302 avg 53, avg_pos_grade, avg_pos_grade_count, session,
303 avg 54, avg_neg_grade, avg_neg_grade_count, session,
304 avg 60, avg_pos_vertical_speed, avg_pos_vertical_speed_count, session,
305 avg 61, avg_neg_vertical_speed, avg_neg_vertical_speed_count, session,
306 avg 16, avg_heart_rate, avg_heart_rate_count, session,
307 avg 18, avg_cadence, avg_cadence_count, session,
308 avg 57, avg_temperature, avg_temperature_count, session,
309 );
310 }
311
312 update_field!(merged_session.data.values, 253, max_stop_timestamp);
315 update_field!(merged_session.data.values, 15, max_speed);
316 update_field!(merged_session.data.values, 21, max_power);
317 update_field!(merged_session.data.values, 50, max_altitude);
318 update_field!(merged_session.data.values, 55, max_pos_grade);
319 update_field!(merged_session.data.values, 56, max_neg_grade);
320 update_field!(merged_session.data.values, 17, max_heart_rate);
321 update_field!(merged_session.data.values, 19, max_cadence);
322 update_field!(merged_session.data.values, 58, max_temperature);
323 update_field!(merged_session.data.values, 2, min_start_timestamp);
325 update_field!(merged_session.data.values, 71, min_altitude);
326 update_field!(merged_session.data.values, 64, min_heart_rate);
327 update_field!(merged_session.data.values, 7, total_elapsed_time);
329 update_field!(merged_session.data.values, 8, total_timer_time);
330 update_field!(merged_session.data.values, 9, total_distance);
331 update_field!(merged_session.data.values, 59, total_moving_time);
332 update_field!(merged_session.data.values, 11, total_calories);
333 update_field!(merged_session.data.values, 22, total_ascent);
334 update_field!(merged_session.data.values, 23, total_descent);
335 if avg_speed_count > 0 {
337 let avg_speed = <Value as Into<i32>>::into(avg_speed).div(avg_speed_count);
338 update_field!(merged_session.data.values, 14, Value::U16(avg_speed as u16));
339 }
340 if avg_power_count > 0 {
341 let avg_power = <Value as Into<i32>>::into(avg_power).div(avg_power_count);
342 update_field!(merged_session.data.values, 20, Value::U16(avg_power as u16));
343 }
344 if avg_altitude_count > 0 {
345 let avg_altitude = <Value as Into<i32>>::into(avg_altitude).div(avg_altitude_count);
346 update_field!(
347 merged_session.data.values,
348 49,
349 Value::U16(avg_altitude as u16)
350 );
351 }
352 if avg_grade_count > 0 {
353 let avg_grade = <Value as Into<i32>>::into(avg_grade).div(avg_grade_count);
354 update_field!(merged_session.data.values, 52, Value::I16(avg_grade as i16));
355 }
356 if avg_pos_grade_count > 0 {
357 let avg_pos_grade = <Value as Into<i32>>::into(avg_pos_grade).div(avg_pos_grade_count);
358 update_field!(
359 merged_session.data.values,
360 53,
361 Value::I16(avg_pos_grade as i16)
362 );
363 }
364 if avg_neg_grade_count > 0 {
365 let avg_neg_grade = <Value as Into<i32>>::into(avg_neg_grade).div(avg_neg_grade_count);
366 update_field!(
367 merged_session.data.values,
368 54,
369 Value::I16(avg_neg_grade as i16)
370 );
371 }
372 if avg_pos_vertical_speed_count > 0 {
373 let avg_pos_vertical_speed = <Value as Into<i32>>::into(avg_pos_vertical_speed)
374 .div(avg_pos_vertical_speed_count);
375 update_field!(
376 merged_session.data.values,
377 60,
378 Value::I16(avg_pos_vertical_speed as i16)
379 );
380 }
381 if avg_neg_vertical_speed_count > 0 {
382 let avg_neg_vertical_speed = <Value as Into<i32>>::into(avg_neg_vertical_speed)
383 .div(avg_neg_vertical_speed_count);
384 update_field!(
385 merged_session.data.values,
386 61,
387 Value::I16(avg_neg_vertical_speed as i16)
388 );
389 }
390 if avg_heart_rate_count > 0 {
391 let avg_heart_rate =
392 <Value as Into<i32>>::into(avg_heart_rate).div(avg_heart_rate_count);
393 update_field!(
394 merged_session.data.values,
395 16,
396 Value::U8(avg_heart_rate as u8)
397 );
398 }
399 if avg_cadence_count > 0 {
400 let avg_cadence = <Value as Into<i32>>::into(avg_cadence).div(avg_cadence_count);
401 update_field!(merged_session.data.values, 18, Value::U8(avg_cadence as u8));
402 }
403 if avg_temperature_count > 0 {
404 let avg_temperature =
405 <Value as Into<i32>>::into(avg_temperature).div(avg_temperature_count);
406 update_field!(
407 merged_session.data.values,
408 57,
409 Value::I8(avg_temperature as i8)
410 );
411 }
412
413 Some(merged_session)
414 }
415
416 pub fn get_session(&self) -> Option<(usize, FitDataMessage)> {
417 for (index, message) in self.data.iter().enumerate() {
418 match message {
419 FitMessage::Definition(_) => {}
420 FitMessage::Data(msg) => match msg.data.message_type {
421 MessageType::Session => {
422 return Some((index, msg.clone()));
423 }
424 _ => {}
425 },
426 };
427 }
428 None
429 }
430}