fit-file-parser 4.0.2 → 5.0.1
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.
- package/CHANGELOG.md +91 -0
- package/README.md +273 -117
- package/dist/binary.d.ts +7 -4
- package/dist/binary.js +207 -163
- package/dist/cjs/binary.d.ts +7 -4
- package/dist/cjs/binary.js +207 -163
- package/dist/cjs/fit-parser.js +41 -21
- package/dist/cjs/fit.d.ts +15 -7
- package/dist/cjs/fit.js +137 -9078
- package/dist/cjs/fit_types.d.ts +1384 -298
- package/dist/cjs/garmin_profile.generated.d.ts +9 -0
- package/dist/cjs/garmin_profile.generated.js +17838 -0
- package/dist/cjs/helper.js +22 -12
- package/dist/cjs/messages.d.ts +2 -3
- package/dist/cjs/type_generator.d.ts +3 -0
- package/dist/cjs/type_generator.js +56 -27
- package/dist/fit-parser.js +41 -21
- package/dist/fit.d.ts +15 -7
- package/dist/fit.js +136 -9077
- package/dist/fit_types.d.ts +1384 -298
- package/dist/garmin_profile.generated.d.ts +9 -0
- package/dist/garmin_profile.generated.js +17835 -0
- package/dist/helper.js +22 -12
- package/dist/messages.d.ts +2 -3
- package/dist/type_generator.d.ts +3 -0
- package/dist/type_generator.js +55 -27
- package/package.json +9 -6
package/dist/cjs/helper.js
CHANGED
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mapDataIntoLap = mapDataIntoLap;
|
|
4
4
|
exports.mapDataIntoSession = mapDataIntoSession;
|
|
5
|
+
function timestampValue(value) {
|
|
6
|
+
if (value === undefined || value === null) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const timestamp = value instanceof Date
|
|
10
|
+
? value.getTime()
|
|
11
|
+
: new Date(value).getTime();
|
|
12
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
13
|
+
}
|
|
5
14
|
function mapDataIntoLap(inputLaps, lapKey, data) {
|
|
15
|
+
var _a;
|
|
6
16
|
const laps = [...inputLaps];
|
|
7
17
|
let index = 0;
|
|
8
18
|
for (let i = 0; i < laps.length; i++) {
|
|
9
19
|
const nextLap = laps[i + 1];
|
|
10
20
|
const tempData = [];
|
|
11
|
-
const nextLapStartTime = nextLap
|
|
12
|
-
? new Date(nextLap.start_time).getTime()
|
|
13
|
-
: null;
|
|
21
|
+
const nextLapStartTime = timestampValue(nextLap === null || nextLap === void 0 ? void 0 : nextLap.start_time);
|
|
14
22
|
for (let j = index; j < data.length; j++) {
|
|
15
23
|
const row = data[j];
|
|
16
|
-
if (nextLap && nextLapStartTime) {
|
|
17
|
-
const timestamp =
|
|
18
|
-
if (nextLapStartTime > timestamp) {
|
|
24
|
+
if (nextLap && nextLapStartTime !== undefined) {
|
|
25
|
+
const timestamp = timestampValue((_a = row.timestamp) !== null && _a !== void 0 ? _a : row.start_time);
|
|
26
|
+
if (timestamp === undefined || nextLapStartTime > timestamp) {
|
|
19
27
|
tempData.push(row);
|
|
28
|
+
index = j + 1;
|
|
20
29
|
}
|
|
21
30
|
else if (nextLapStartTime <= timestamp) {
|
|
22
31
|
index = j;
|
|
@@ -25,6 +34,7 @@ function mapDataIntoLap(inputLaps, lapKey, data) {
|
|
|
25
34
|
}
|
|
26
35
|
else {
|
|
27
36
|
tempData.push(row);
|
|
37
|
+
index = j + 1;
|
|
28
38
|
}
|
|
29
39
|
}
|
|
30
40
|
if (!laps[i][lapKey]) {
|
|
@@ -39,15 +49,14 @@ function mapDataIntoSession(inputSessions, laps) {
|
|
|
39
49
|
for (let i = 0; i < sessions.length; i++) {
|
|
40
50
|
const nextSession = sessions[i + 1];
|
|
41
51
|
const tempLaps = [];
|
|
42
|
-
const nextSessionStartTime = nextSession
|
|
43
|
-
? new Date(nextSession.start_time).getTime()
|
|
44
|
-
: null;
|
|
52
|
+
const nextSessionStartTime = timestampValue(nextSession === null || nextSession === void 0 ? void 0 : nextSession.start_time);
|
|
45
53
|
for (let j = lapIndex; j < laps.length; j++) {
|
|
46
54
|
const lap = laps[j];
|
|
47
|
-
if (nextSession && nextSessionStartTime) {
|
|
48
|
-
const lapStartTime =
|
|
49
|
-
if (nextSessionStartTime > lapStartTime) {
|
|
55
|
+
if (nextSession && nextSessionStartTime !== undefined) {
|
|
56
|
+
const lapStartTime = timestampValue(lap.start_time);
|
|
57
|
+
if (lapStartTime === undefined || nextSessionStartTime > lapStartTime) {
|
|
50
58
|
tempLaps.push(lap);
|
|
59
|
+
lapIndex = j + 1;
|
|
51
60
|
}
|
|
52
61
|
else if (nextSessionStartTime <= lapStartTime) {
|
|
53
62
|
lapIndex = j;
|
|
@@ -56,6 +65,7 @@ function mapDataIntoSession(inputSessions, laps) {
|
|
|
56
65
|
}
|
|
57
66
|
else {
|
|
58
67
|
tempLaps.push(lap);
|
|
68
|
+
lapIndex = j + 1;
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
if (!sessions[i].laps) {
|
package/dist/cjs/messages.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { MessageObject } from './fit.js';
|
|
2
|
-
import type { MesgNum } from './fit_types.js';
|
|
1
|
+
import type { MessageName, MessageObject } from './fit.js';
|
|
3
2
|
export declare function getFitMessage(messageNum: number): {
|
|
4
|
-
name:
|
|
3
|
+
name: MessageName | '';
|
|
5
4
|
getAttributes: (fieldNum: number) => MessageObject;
|
|
6
5
|
};
|
|
7
6
|
export declare function getFitMessageBaseType(inp: any): any;
|
|
@@ -19,6 +19,9 @@ export declare function generateOptions(options: {
|
|
|
19
19
|
}): ts.Node[];
|
|
20
20
|
export declare function generateUtilities(): Statement[];
|
|
21
21
|
export declare function generateFitType(): Statement;
|
|
22
|
+
export declare function generateParsedMessages(messages: {
|
|
23
|
+
[messageId: number]: Message;
|
|
24
|
+
}): Statement;
|
|
22
25
|
export declare function generateMessages(messages: {
|
|
23
26
|
[messageId: number]: Message;
|
|
24
27
|
}): Statement[];
|
|
@@ -16,6 +16,7 @@ exports.generateTypes = generateTypes;
|
|
|
16
16
|
exports.generateOptions = generateOptions;
|
|
17
17
|
exports.generateUtilities = generateUtilities;
|
|
18
18
|
exports.generateFitType = generateFitType;
|
|
19
|
+
exports.generateParsedMessages = generateParsedMessages;
|
|
19
20
|
exports.generateMessages = generateMessages;
|
|
20
21
|
exports.main = main;
|
|
21
22
|
const typescript_1 = __importDefault(require("typescript"));
|
|
@@ -58,7 +59,16 @@ function generateProperty(name, type, optional = true) {
|
|
|
58
59
|
function generateArrayProperty(name, type, optional = true) {
|
|
59
60
|
return generateProperty(name, typescript_1.default.factory.createArrayTypeNode(type), optional);
|
|
60
61
|
}
|
|
62
|
+
function generateNullableArrayType(type) {
|
|
63
|
+
return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createParenthesizedType(typescript_1.default.factory.createUnionTypeNode([
|
|
64
|
+
type,
|
|
65
|
+
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createNull()),
|
|
66
|
+
])));
|
|
67
|
+
}
|
|
61
68
|
function generateTypeFromField(def) {
|
|
69
|
+
if (def.array === true) {
|
|
70
|
+
return generateNullableArrayType(generateTypeFromField(Object.assign(Object.assign({}, def), { array: false })));
|
|
71
|
+
}
|
|
62
72
|
switch (def.type) {
|
|
63
73
|
case 'uint32_array':
|
|
64
74
|
case 'uint16_array':
|
|
@@ -67,12 +77,15 @@ function generateTypeFromField(def) {
|
|
|
67
77
|
case 'sint16_array':
|
|
68
78
|
case 'sint8_array':
|
|
69
79
|
case 'byte_array':
|
|
70
|
-
return
|
|
71
|
-
case '
|
|
72
|
-
return typescript_1.default.factory.
|
|
80
|
+
return generateNullableArrayType(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
|
|
81
|
+
case 'exercise_category_array':
|
|
82
|
+
return generateNullableArrayType(typescript_1.default.factory.createTypeReferenceNode('ExerciseCategory'));
|
|
73
83
|
case 'date_time':
|
|
84
|
+
case 'local_date_time':
|
|
85
|
+
return typescript_1.default.factory.createTypeReferenceNode('Date');
|
|
74
86
|
case 'string':
|
|
75
87
|
return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword);
|
|
88
|
+
case 'bool':
|
|
76
89
|
case 'uint32':
|
|
77
90
|
case 'uint64':
|
|
78
91
|
case 'uint16':
|
|
@@ -102,18 +115,30 @@ function generateTypes(types) {
|
|
|
102
115
|
const nodes = [];
|
|
103
116
|
const typeNames = Object.keys(types);
|
|
104
117
|
typeNames.forEach((name) => {
|
|
105
|
-
if (name === 'message_index') {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
118
|
const type = types[name];
|
|
109
|
-
const names = Object.values(type);
|
|
119
|
+
const names = [...new Set(Object.values(type))];
|
|
120
|
+
const isMask = names.includes('mask');
|
|
121
|
+
const typeNode = isMask
|
|
122
|
+
? typescript_1.default.factory.createTypeLiteralNode([
|
|
123
|
+
generateProperty('value', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword), false),
|
|
124
|
+
...names
|
|
125
|
+
.filter(value => value !== 'mask')
|
|
126
|
+
.map(value => generateProperty(String(value), typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false)),
|
|
127
|
+
])
|
|
128
|
+
: names.length === 0
|
|
129
|
+
? typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)
|
|
130
|
+
: typescript_1.default.factory.createUnionTypeNode([
|
|
131
|
+
...names.map(n => typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral(String(n)))),
|
|
132
|
+
...(name === 'mesg_num'
|
|
133
|
+
? [
|
|
134
|
+
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
|
|
135
|
+
]
|
|
136
|
+
: []),
|
|
137
|
+
typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword),
|
|
138
|
+
]);
|
|
110
139
|
const typeAlias = typescript_1.default.factory.createTypeAliasDeclaration([
|
|
111
140
|
typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
|
|
112
|
-
], snakeToCamel(name), undefined,
|
|
113
|
-
? [
|
|
114
|
-
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
|
|
115
|
-
]
|
|
116
|
-
: [])]));
|
|
141
|
+
], snakeToCamel(name), undefined, typeNode);
|
|
117
142
|
nodes.push(typeAlias);
|
|
118
143
|
});
|
|
119
144
|
return nodes;
|
|
@@ -150,13 +175,6 @@ function generateUtilities() {
|
|
|
150
175
|
]),
|
|
151
176
|
]));
|
|
152
177
|
nodes.push(unitType);
|
|
153
|
-
const messageIndex = typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'MessageIndex', undefined, undefined, [
|
|
154
|
-
generateProperty('0', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
|
|
155
|
-
generateProperty('value', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword), false),
|
|
156
|
-
generateProperty('reserved', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
|
|
157
|
-
generateProperty('selected', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
|
|
158
|
-
]);
|
|
159
|
-
nodes.push(messageIndex);
|
|
160
178
|
return nodes;
|
|
161
179
|
}
|
|
162
180
|
function generateAdditionalFields(msg) {
|
|
@@ -180,6 +198,7 @@ function generateAdditionalFields(msg) {
|
|
|
180
198
|
developer_data_ids: 'parsed_developer_data_id',
|
|
181
199
|
field_descriptions: 'parsed_field_description',
|
|
182
200
|
sports: 'parsed_sport',
|
|
201
|
+
sets: 'parsed_set',
|
|
183
202
|
splits: 'parsed_split',
|
|
184
203
|
split_summaries: 'parsed_split_summary',
|
|
185
204
|
};
|
|
@@ -211,6 +230,7 @@ function generateFitType() {
|
|
|
211
230
|
tank_updates: 'parsed_tank_update',
|
|
212
231
|
tank_summaries: 'parsed_tank_summary',
|
|
213
232
|
jumps: 'parsed_jump',
|
|
233
|
+
sets: 'parsed_set',
|
|
214
234
|
splits: 'parsed_split',
|
|
215
235
|
split_summaries: 'parsed_split_summary',
|
|
216
236
|
time_in_zone: 'parsed_time_in_zone',
|
|
@@ -227,31 +247,38 @@ function generateFitType() {
|
|
|
227
247
|
activity: 'parsed_activity',
|
|
228
248
|
zones_target: '?parsed_zones_target',
|
|
229
249
|
};
|
|
250
|
+
const existingMessageNames = new Set([
|
|
251
|
+
...Object.values(collectionProperties),
|
|
252
|
+
...Object.values(referenceProperties),
|
|
253
|
+
].map(name => name.replace('?', '').replace(/^parsed_/, '')));
|
|
254
|
+
const additionalReferenceProperties = Object.values(fit_js_1.FIT.messages)
|
|
255
|
+
.filter(message => !existingMessageNames.has(message.name))
|
|
256
|
+
.map(message => generateProperty(message.name, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`))));
|
|
230
257
|
return typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'ParsedFit', undefined, undefined, [
|
|
231
258
|
generateProperty('protocolVersion', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)),
|
|
232
259
|
generateProperty('profileVersion', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)),
|
|
260
|
+
generateProperty('messages', typescript_1.default.factory.createTypeReferenceNode('ParsedMessages')),
|
|
233
261
|
...Object.keys(referenceProperties).map(prop => generateProperty(prop, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(referenceProperties[prop].replace('?', ''))), referenceProperties[prop].startsWith('?'))),
|
|
234
262
|
...Object.keys(collectionProperties).map(prop => generateArrayProperty(prop, collectionProperties[prop] === 'unknown'
|
|
235
263
|
? typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UnknownKeyword)
|
|
236
264
|
: typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(collectionProperties[prop])))),
|
|
265
|
+
...additionalReferenceProperties,
|
|
237
266
|
]);
|
|
238
267
|
}
|
|
268
|
+
function generateParsedMessages(messages) {
|
|
269
|
+
return typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'ParsedMessages', undefined, undefined, Object.values(messages).map(message => generateArrayProperty(message.name, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`)))));
|
|
270
|
+
}
|
|
239
271
|
function generateMessages(messages) {
|
|
240
272
|
const nodes = [];
|
|
241
273
|
Object.keys(messages).forEach((name) => {
|
|
242
274
|
const msg = fit_js_1.FIT.messages[Number(name)];
|
|
243
|
-
const usedFields = new Set();
|
|
244
275
|
const messageType = typescript_1.default.factory.createInterfaceDeclaration([
|
|
245
276
|
typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
|
|
246
277
|
], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
|
|
247
|
-
...Object.keys(msg).filter(n => n !== 'name').
|
|
278
|
+
...Object.keys(msg).filter(n => n !== 'name').map((id) => {
|
|
248
279
|
const def = msg[Number(id)];
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
acc.push(generateProperty(def.field, generateTypeFromField(def), !['start_time', 'timestamp'].includes(def.field)));
|
|
252
|
-
}
|
|
253
|
-
return acc;
|
|
254
|
-
}, []),
|
|
280
|
+
return generateProperty(def.field, generateTypeFromField(def));
|
|
281
|
+
}),
|
|
255
282
|
...generateAdditionalFields(msg),
|
|
256
283
|
]);
|
|
257
284
|
nodes.push(messageType);
|
|
@@ -271,6 +298,8 @@ function main() {
|
|
|
271
298
|
nodes.push(...generateOptions(fit_js_1.FIT.options));
|
|
272
299
|
nodes.push(...comment('parsed from Fit.messages'));
|
|
273
300
|
nodes.push(...generateMessages(fit_js_1.FIT.messages));
|
|
301
|
+
nodes.push(...comment('all recognized messages, retained in file order'));
|
|
302
|
+
nodes.push(generateParsedMessages(fit_js_1.FIT.messages));
|
|
274
303
|
nodes.push(...comment('the returned type after parsing a .fit file'));
|
|
275
304
|
nodes.push(generateFitType());
|
|
276
305
|
const nodesArray = typescript_1.default.factory.createNodeArray(nodes);
|
package/dist/fit-parser.js
CHANGED
|
@@ -26,23 +26,23 @@ export default class FitParser {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
parse(content, callback) {
|
|
29
|
-
var _a;
|
|
29
|
+
var _a, _b;
|
|
30
30
|
const blob = content instanceof ArrayBuffer
|
|
31
31
|
? new Uint8Array(content)
|
|
32
32
|
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
33
33
|
const dataView = new DataView(blob.buffer, blob.byteOffset, blob.byteLength);
|
|
34
34
|
if (blob.length < 12) {
|
|
35
35
|
callback('File to small to be a FIT file', undefined);
|
|
36
|
-
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
36
|
+
return;
|
|
39
37
|
}
|
|
40
38
|
const headerLength = blob[0];
|
|
41
39
|
if (headerLength !== 14 && headerLength !== 12) {
|
|
42
40
|
callback('Incorrect header size', undefined);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (blob.length < headerLength) {
|
|
44
|
+
callback('File to small to be a FIT file', undefined);
|
|
45
|
+
return;
|
|
46
46
|
}
|
|
47
47
|
let fileTypeString = '';
|
|
48
48
|
for (let i = 8; i < 12; i++) {
|
|
@@ -50,29 +50,33 @@ export default class FitParser {
|
|
|
50
50
|
}
|
|
51
51
|
if (fileTypeString !== '.FIT') {
|
|
52
52
|
callback('Missing \'.FIT\' in header', undefined);
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
53
|
+
return;
|
|
56
54
|
}
|
|
57
55
|
if (headerLength === 14 && !this.options.force) {
|
|
58
56
|
const crcHeader = blob[12] + (blob[13] << 8);
|
|
59
57
|
const crcHeaderCalc = calculateCRC(blob, 0, 12);
|
|
60
|
-
if (crcHeader !== crcHeaderCalc) {
|
|
61
|
-
|
|
62
|
-
// TODO: fix Header CRC check
|
|
58
|
+
if (crcHeader !== 0 && crcHeader !== crcHeaderCalc) {
|
|
59
|
+
callback('Header CRC mismatch', undefined);
|
|
63
60
|
return;
|
|
64
61
|
}
|
|
65
62
|
}
|
|
66
63
|
const protocolVersion = blob[1];
|
|
67
64
|
const profileVersion = blob[2] + (blob[3] << 8);
|
|
68
|
-
const dataLength =
|
|
65
|
+
const dataLength = dataView.getUint32(4, true);
|
|
69
66
|
const crcStart = dataLength + headerLength;
|
|
67
|
+
if (crcStart > blob.length) {
|
|
68
|
+
callback('File data exceeds input length', undefined);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!this.options.force && crcStart + 2 > blob.length) {
|
|
72
|
+
callback('File CRC missing', undefined);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
70
75
|
if (!this.options.force) {
|
|
71
76
|
const crcFile = blob[crcStart] + (blob[crcStart + 1] << 8);
|
|
72
|
-
const crcFileCalc = calculateCRC(blob,
|
|
77
|
+
const crcFileCalc = calculateCRC(blob, 0, crcStart);
|
|
73
78
|
if (crcFile !== crcFileCalc) {
|
|
74
|
-
|
|
75
|
-
// TODO: fix File CRC check
|
|
79
|
+
callback('File CRC mismatch', undefined);
|
|
76
80
|
return;
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -80,6 +84,7 @@ export default class FitParser {
|
|
|
80
84
|
profileVersion,
|
|
81
85
|
protocolVersion,
|
|
82
86
|
};
|
|
87
|
+
const messages = {};
|
|
83
88
|
let sessions = [];
|
|
84
89
|
let laps = [];
|
|
85
90
|
const records = [];
|
|
@@ -102,6 +107,7 @@ export default class FitParser {
|
|
|
102
107
|
const tank_updates = [];
|
|
103
108
|
const tank_summaries = [];
|
|
104
109
|
const jumps = [];
|
|
110
|
+
const sets = [];
|
|
105
111
|
const splits = [];
|
|
106
112
|
const split_summaries = [];
|
|
107
113
|
const time_in_zone = [];
|
|
@@ -110,14 +116,20 @@ export default class FitParser {
|
|
|
110
116
|
let loopIndex = headerLength;
|
|
111
117
|
const messageTypes = [];
|
|
112
118
|
const developerFields = [];
|
|
119
|
+
const decoderState = {};
|
|
113
120
|
const isModeCascade = this.options.mode === 'cascade';
|
|
114
121
|
const isCascadeNeeded = isModeCascade || this.options.mode === 'both';
|
|
115
122
|
let startDate;
|
|
116
123
|
let lastStopTimestamp;
|
|
117
124
|
let pausedTime = 0;
|
|
118
125
|
while (loopIndex < crcStart) {
|
|
119
|
-
const { nextIndex, messageType, message } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView);
|
|
126
|
+
const { nextIndex, messageType, message } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView, decoderState);
|
|
120
127
|
loopIndex = nextIndex;
|
|
128
|
+
if (messageType !== ''
|
|
129
|
+
&& messageType !== 'definition'
|
|
130
|
+
&& message !== undefined) {
|
|
131
|
+
((_a = messages[messageType]) !== null && _a !== void 0 ? _a : (messages[messageType] = [])).push(message);
|
|
132
|
+
}
|
|
121
133
|
switch (messageType) {
|
|
122
134
|
case 'lap':
|
|
123
135
|
laps.push(message);
|
|
@@ -151,8 +163,10 @@ export default class FitParser {
|
|
|
151
163
|
case 'record':
|
|
152
164
|
if (!startDate) {
|
|
153
165
|
startDate = message.timestamp;
|
|
154
|
-
|
|
155
|
-
|
|
166
|
+
if (this.options.elapsedRecordField) {
|
|
167
|
+
message.elapsed_time = 0;
|
|
168
|
+
message.timer_time = 0;
|
|
169
|
+
}
|
|
156
170
|
}
|
|
157
171
|
records.push(message);
|
|
158
172
|
break;
|
|
@@ -205,6 +219,9 @@ export default class FitParser {
|
|
|
205
219
|
case 'jump':
|
|
206
220
|
jumps.push(message);
|
|
207
221
|
break;
|
|
222
|
+
case 'set':
|
|
223
|
+
sets.push(message);
|
|
224
|
+
break;
|
|
208
225
|
case 'split':
|
|
209
226
|
splits.push(message);
|
|
210
227
|
break;
|
|
@@ -245,15 +262,17 @@ export default class FitParser {
|
|
|
245
262
|
fitObj.time_in_zone = time_in_zone;
|
|
246
263
|
fitObj.activity_metrics = activity_metrics;
|
|
247
264
|
fitObj.user_metrics = user_metrics;
|
|
265
|
+
fitObj.messages = messages;
|
|
248
266
|
if (isCascadeNeeded) {
|
|
249
267
|
laps = mapDataIntoLap(laps, 'records', records);
|
|
250
268
|
laps = mapDataIntoLap(laps, 'lengths', lengths);
|
|
251
269
|
sessions = mapDataIntoSession(sessions, laps);
|
|
252
|
-
fitObj.activity = Object.assign(Object.assign({}, ((
|
|
270
|
+
fitObj.activity = Object.assign(Object.assign({}, ((_b = fitObj.activity) !== null && _b !== void 0 ? _b : {})), { // ugly but we assume the activity was parsed correctly with all other members correctly
|
|
253
271
|
sessions,
|
|
254
272
|
events,
|
|
255
273
|
hrv,
|
|
256
274
|
device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports,
|
|
275
|
+
sets,
|
|
257
276
|
splits,
|
|
258
277
|
split_summaries });
|
|
259
278
|
}
|
|
@@ -267,6 +286,7 @@ export default class FitParser {
|
|
|
267
286
|
fitObj.developer_data_ids = applications;
|
|
268
287
|
fitObj.field_descriptions = fieldDescriptions;
|
|
269
288
|
fitObj.hrv = hrv;
|
|
289
|
+
fitObj.sets = sets;
|
|
270
290
|
}
|
|
271
291
|
callback(undefined, fitObj);
|
|
272
292
|
}
|
package/dist/fit.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { FitOptions, MesgNum } from './fit_types.js';
|
|
2
|
+
export type MessageName = Exclude<MesgNum, number | 'definition'>;
|
|
2
3
|
export interface FieldDefinition {
|
|
3
4
|
type: string | number;
|
|
5
|
+
rawType?: string | number;
|
|
4
6
|
fDefNo: number;
|
|
5
7
|
size: number;
|
|
8
|
+
array?: boolean;
|
|
6
9
|
endianAbility: boolean;
|
|
7
10
|
littleEndian: boolean;
|
|
8
11
|
baseTypeNo: number;
|
|
@@ -10,6 +13,7 @@ export interface FieldDefinition {
|
|
|
10
13
|
dataType: string;
|
|
11
14
|
scale?: number | null;
|
|
12
15
|
offset?: number;
|
|
16
|
+
units?: string;
|
|
13
17
|
requiresBoundedDataView?: boolean;
|
|
14
18
|
developerDataIndex?: number;
|
|
15
19
|
isDeveloperField?: boolean;
|
|
@@ -17,22 +21,26 @@ export interface FieldDefinition {
|
|
|
17
21
|
export interface MessageObject {
|
|
18
22
|
field: string;
|
|
19
23
|
type: string;
|
|
24
|
+
baseType?: string;
|
|
25
|
+
array?: boolean;
|
|
20
26
|
scale: number | null;
|
|
21
27
|
offset: number;
|
|
22
28
|
units: string;
|
|
23
29
|
}
|
|
24
30
|
export interface Message {
|
|
25
|
-
name:
|
|
31
|
+
name: MessageName;
|
|
26
32
|
[fieldId: number]: MessageObject;
|
|
27
33
|
}
|
|
28
34
|
export interface FitType {
|
|
29
35
|
scConst: number;
|
|
30
36
|
options: FitOptions;
|
|
31
|
-
messages:
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
types: {
|
|
35
|
-
[typeName: string]: Record<number, string | number>;
|
|
36
|
-
};
|
|
37
|
+
messages: Record<number, Message>;
|
|
38
|
+
types: Record<string, Record<number, string | number>>;
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Garmin fields observed in the external FIT corpus but absent from the pinned
|
|
42
|
+
* public SDK profile. These additions may not replace standard SDK fields.
|
|
43
|
+
*/
|
|
44
|
+
export declare const FIT_VENDOR_MESSAGE_EXTENSIONS: Record<number, Message>;
|
|
45
|
+
export declare const FIT_VENDOR_TYPE_EXTENSIONS: Record<string, Record<number, string>>;
|
|
38
46
|
export declare const FIT: FitType;
|