fit-file-parser 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const binary_js_1 = require("./binary.js");
4
+ const helper_js_1 = require("./helper.js");
5
+ class FitParser {
6
+ constructor(options = {}) {
7
+ this.options = {
8
+ force: options.force != null ? options.force : true,
9
+ speedUnit: options.speedUnit || 'm/s',
10
+ lengthUnit: options.lengthUnit || 'm',
11
+ temperatureUnit: options.temperatureUnit || 'celsius',
12
+ elapsedRecordField: options.elapsedRecordField || false,
13
+ pressureUnit: options.pressureUnit || 'bar',
14
+ mode: options.mode || 'list',
15
+ };
16
+ }
17
+ parseAsync(content) {
18
+ return new Promise((resolve, reject) => {
19
+ this.parse(content, (error, data) => {
20
+ if (error) {
21
+ reject(error);
22
+ }
23
+ else if (data) {
24
+ resolve(data);
25
+ }
26
+ });
27
+ });
28
+ }
29
+ parse(content, callback) {
30
+ var _a;
31
+ const blob = new Uint8Array((0, binary_js_1.getArrayBuffer)(content));
32
+ if (blob.length < 12) {
33
+ callback('File to small to be a FIT file', undefined);
34
+ if (!this.options.force) {
35
+ return;
36
+ }
37
+ }
38
+ const headerLength = blob[0];
39
+ if (headerLength !== 14 && headerLength !== 12) {
40
+ callback('Incorrect header size', undefined);
41
+ if (!this.options.force) {
42
+ return;
43
+ }
44
+ }
45
+ let fileTypeString = '';
46
+ for (let i = 8; i < 12; i++) {
47
+ fileTypeString += String.fromCharCode(blob[i]);
48
+ }
49
+ if (fileTypeString !== '.FIT') {
50
+ callback('Missing \'.FIT\' in header', undefined);
51
+ if (!this.options.force) {
52
+ return;
53
+ }
54
+ }
55
+ if (headerLength === 14) {
56
+ const crcHeader = blob[12] + (blob[13] << 8);
57
+ const crcHeaderCalc = (0, binary_js_1.calculateCRC)(blob, 0, 12);
58
+ if (crcHeader !== crcHeaderCalc) {
59
+ // callback('Header CRC mismatch', {});
60
+ // TODO: fix Header CRC check
61
+ if (!this.options.force) {
62
+ return;
63
+ }
64
+ }
65
+ }
66
+ const protocolVersion = blob[1];
67
+ const profileVersion = blob[2] + (blob[3] << 8);
68
+ const dataLength = blob[4] + (blob[5] << 8) + (blob[6] << 16) + (blob[7] << 24);
69
+ const crcStart = dataLength + headerLength;
70
+ const crcFile = blob[crcStart] + (blob[crcStart + 1] << 8);
71
+ const crcFileCalc = (0, binary_js_1.calculateCRC)(blob, headerLength === 12 ? 0 : headerLength, crcStart);
72
+ if (crcFile !== crcFileCalc) {
73
+ // callback('File CRC mismatch', {});
74
+ // TODO: fix File CRC check
75
+ if (!this.options.force) {
76
+ return;
77
+ }
78
+ }
79
+ const fitObj = {
80
+ profileVersion,
81
+ protocolVersion,
82
+ };
83
+ let sessions = [];
84
+ let laps = [];
85
+ const records = [];
86
+ const events = [];
87
+ const hr_zone = [];
88
+ const power_zone = [];
89
+ const hrv = [];
90
+ const device_infos = [];
91
+ const applications = [];
92
+ const fieldDescriptions = [];
93
+ const dive_gases = [];
94
+ const course_points = [];
95
+ const sports = [];
96
+ const monitors = [];
97
+ const stress = [];
98
+ const definitions = [];
99
+ const file_ids = [];
100
+ const monitor_info = [];
101
+ const lengths = [];
102
+ const tank_updates = [];
103
+ const tank_summaries = [];
104
+ let loopIndex = headerLength;
105
+ const messageTypes = [];
106
+ const developerFields = [];
107
+ const isModeCascade = this.options.mode === 'cascade';
108
+ const isCascadeNeeded = isModeCascade || this.options.mode === 'both';
109
+ let startDate;
110
+ let lastStopTimestamp;
111
+ let pausedTime = 0;
112
+ while (loopIndex < crcStart) {
113
+ const { nextIndex, messageType, message } = (0, binary_js_1.readRecord)(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime);
114
+ loopIndex = nextIndex;
115
+ switch (messageType) {
116
+ case 'lap':
117
+ laps.push(message);
118
+ break;
119
+ case 'session':
120
+ sessions.push(message);
121
+ break;
122
+ case 'event':
123
+ if (message.event === 'timer') {
124
+ if (message.event_type === 'stop_all') {
125
+ lastStopTimestamp = message.timestamp;
126
+ }
127
+ else if (message.event_type === 'start' && lastStopTimestamp) {
128
+ pausedTime += (message.timestamp - lastStopTimestamp) / 1000;
129
+ }
130
+ }
131
+ events.push(message);
132
+ break;
133
+ case 'length':
134
+ lengths.push(message);
135
+ break;
136
+ case 'hrv':
137
+ hrv.push(message);
138
+ break;
139
+ case 'hr_zone':
140
+ hr_zone.push(message);
141
+ break;
142
+ case 'power_zone':
143
+ power_zone.push(message);
144
+ break;
145
+ case 'record':
146
+ if (!startDate) {
147
+ startDate = message.timestamp;
148
+ message.elapsed_time = 0;
149
+ message.timer_time = 0;
150
+ }
151
+ records.push(message);
152
+ break;
153
+ case 'field_description':
154
+ fieldDescriptions.push(message);
155
+ break;
156
+ case 'device_info':
157
+ device_infos.push(message);
158
+ break;
159
+ case 'developer_data_id':
160
+ applications.push(message);
161
+ break;
162
+ case 'dive_gas':
163
+ dive_gases.push(message);
164
+ break;
165
+ case 'course_point':
166
+ course_points.push(message);
167
+ break;
168
+ case 'sport':
169
+ sports.push(message);
170
+ break;
171
+ case 'file_id':
172
+ if (message) {
173
+ file_ids.push(message);
174
+ }
175
+ break;
176
+ case 'definition':
177
+ if (message) {
178
+ definitions.push(message);
179
+ }
180
+ break;
181
+ case 'monitoring':
182
+ monitors.push(message);
183
+ break;
184
+ case 'monitoring_info':
185
+ monitor_info.push(message);
186
+ break;
187
+ case 'stress_level':
188
+ stress.push(message);
189
+ break;
190
+ case 'software':
191
+ fitObj.software = message;
192
+ break;
193
+ case 'tank_update':
194
+ tank_updates.push(message);
195
+ break;
196
+ case 'tank_summary':
197
+ tank_summaries.push(message);
198
+ break;
199
+ default:
200
+ if (messageType !== '') {
201
+ fitObj[messageType] = message;
202
+ }
203
+ break;
204
+ }
205
+ }
206
+ fitObj.hr_zone = hr_zone;
207
+ fitObj.power_zone = power_zone;
208
+ fitObj.dive_gases = dive_gases;
209
+ fitObj.course_points = course_points;
210
+ fitObj.sports = sports;
211
+ fitObj.monitors = monitors;
212
+ fitObj.stress = stress;
213
+ fitObj.file_ids = file_ids;
214
+ fitObj.monitor_info = monitor_info;
215
+ fitObj.definitions = definitions;
216
+ fitObj.tank_updates = tank_updates;
217
+ fitObj.tank_summaries = tank_summaries;
218
+ if (isCascadeNeeded) {
219
+ laps = (0, helper_js_1.mapDataIntoLap)(laps, 'records', records);
220
+ laps = (0, helper_js_1.mapDataIntoLap)(laps, 'lengths', lengths);
221
+ sessions = (0, helper_js_1.mapDataIntoSession)(sessions, laps);
222
+ fitObj.activity = Object.assign(Object.assign({}, ((_a = fitObj.activity) !== null && _a !== void 0 ? _a : {})), { // ugly but we assume the activity was parsed correctly with all other members correctly
223
+ sessions,
224
+ events,
225
+ hrv,
226
+ device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports });
227
+ }
228
+ if (!isModeCascade) {
229
+ fitObj.sessions = sessions;
230
+ fitObj.laps = laps;
231
+ fitObj.lengths = lengths;
232
+ fitObj.records = records;
233
+ fitObj.events = events;
234
+ fitObj.device_infos = device_infos;
235
+ fitObj.developer_data_ids = applications;
236
+ fitObj.field_descriptions = fieldDescriptions;
237
+ fitObj.hrv = hrv;
238
+ }
239
+ callback(undefined, fitObj);
240
+ }
241
+ }
242
+ exports.default = FitParser;
@@ -0,0 +1,37 @@
1
+ import type { FitOptions, MesgNum } from './fit_types.js';
2
+ export interface FieldDefinition {
3
+ type: string | number;
4
+ fDefNo: number;
5
+ size: number;
6
+ endianAbility: boolean;
7
+ littleEndian: boolean;
8
+ baseTypeNo: number;
9
+ name: string;
10
+ dataType: string;
11
+ scale?: number;
12
+ offset?: number;
13
+ developerDataIndex?: number;
14
+ isDeveloperField?: boolean;
15
+ }
16
+ export interface MessageObject {
17
+ field: string;
18
+ type: string;
19
+ scale: number | null;
20
+ offset: number;
21
+ units: string;
22
+ }
23
+ export interface Message {
24
+ name: MesgNum;
25
+ [fieldId: number]: MessageObject;
26
+ }
27
+ export interface FitType {
28
+ scConst: number;
29
+ options: FitOptions;
30
+ messages: {
31
+ [messageId: number]: Message;
32
+ };
33
+ types: {
34
+ [typeName: string]: Record<number, string | number>;
35
+ };
36
+ }
37
+ export declare const FIT: FitType;