Skip to content

Commit 876c2cc

Browse files
authored
Merge pull request #16 from garmin/production/akw/21.158.0_54
Garmin FIT SDK 21.158.0
2 parents 16c3eff + 65e44ec commit 876c2cc

File tree

15 files changed

+387
-76
lines changed

15 files changed

+387
-76
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@garmin/fitsdk",
3-
"version": "21.141.0",
3+
"version": "21.158.0",
44
"description": "FIT JavaScript SDK",
55
"main": "src/index.js",
66
"type": "module",

src/accumulator.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

@@ -32,16 +32,26 @@ class AccumulatedField {
3232
class Accumulator {
3333
#messages = {};
3434

35-
add(mesgNum, fieldNum, value) {
35+
createAccumulatedField(mesgNum, fieldNum, value) {
36+
const accumualtedField = new AccumulatedField(value);
37+
3638
if (this.#messages[mesgNum] == null) {
3739
this.#messages[mesgNum] = {};
3840
}
3941

40-
this.#messages[mesgNum][fieldNum] = new AccumulatedField(value);
42+
this.#messages[mesgNum][fieldNum] = accumualtedField;
43+
44+
return accumualtedField;
4145
}
4246

4347
accumulate(mesgNum, fieldNum, value, bits) {
44-
return this.#messages[mesgNum]?.[fieldNum]?.accumulate(value, bits) ?? value;
48+
let accumualtedField = this.#messages[mesgNum]?.[fieldNum];
49+
50+
if(accumualtedField == null) {
51+
accumualtedField = this.createAccumulatedField(mesgNum, fieldNum, value);
52+
}
53+
54+
return accumualtedField.accumulate(value, bits);
4555
}
4656
}
4757

src/bit-stream.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

src/crc-calculator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

src/decoder.js

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

@@ -25,8 +25,17 @@ const MESG_DEFINITION_MASK = 0x40;
2525
const DEV_DATA_MASK = 0x20;
2626
const MESG_HEADER_MASK = 0x00;
2727
const LOCAL_MESG_NUM_MASK = 0x0F;
28+
29+
const HEADER_WITH_CRC_SIZE = 14;
30+
const HEADER_WITHOUT_CRC_SIZE = 12;
2831
const CRC_SIZE = 2;
2932

33+
const DecodeMode = Object.freeze({
34+
NORMAL: "normal",
35+
SKIP_HEADER: "skipHeader",
36+
DATA_ONLY: "dataOnly"
37+
});
38+
3039
class Decoder {
3140
#localMessageDefinitions = [];
3241
#developerDataDefinitions = {};
@@ -36,6 +45,8 @@ class Decoder {
3645
#fieldsWithSubFields = [];
3746
#fieldsToExpand = [];
3847

48+
#decodeMode = DecodeMode.NORMAL;
49+
3950
#mesgListener = null;
4051
#optExpandSubFields = true;
4152
#optExpandComponents = true;
@@ -67,15 +78,15 @@ class Decoder {
6778
static isFIT(stream) {
6879
try {
6980
const fileHeaderSize = stream.peekByte();
70-
if ([14, 12].includes(fileHeaderSize) != true) {
81+
if ([HEADER_WITH_CRC_SIZE, HEADER_WITHOUT_CRC_SIZE].includes(fileHeaderSize) != true) {
7182
return false;
7283
}
7384

7485
if (stream.length < fileHeaderSize + CRC_SIZE) {
7586
return false;
7687
}
7788

78-
const fileHeader = Decoder.#readFileHeader(stream, true);
89+
const fileHeader = Decoder.#readFileHeader(stream, { resetPosition: true, });
7990
if (fileHeader.dataType !== ".FIT") {
8091
return false;
8192
}
@@ -105,15 +116,15 @@ class Decoder {
105116
return false;
106117
}
107118

108-
const fileHeader = Decoder.#readFileHeader(this.#stream, true);
119+
const fileHeader = Decoder.#readFileHeader(this.#stream, { resetPosition: true, });
109120

110121
if (this.#stream.length < fileHeader.headerSize + fileHeader.dataSize + CRC_SIZE) {
111122
return false;
112123
}
113124

114125
const buf = new Uint8Array(this.#stream.slice(0, this.#stream.length))
115126

116-
if (fileHeader.headerSize === 14 && fileHeader.headerCRC !== 0x0000
127+
if (fileHeader.headerSize === HEADER_WITH_CRC_SIZE && fileHeader.headerCRC !== 0x0000
117128
&& fileHeader.headerCRC != CrcCalculator.calculateCRC(buf, 0, 12)) {
118129
return false;
119130
}
@@ -150,6 +161,8 @@ class Decoder {
150161
* @param {boolean} [options.convertDateTimesToDates=true] - (optional, default true)
151162
* @param {Boolean} [options.includeUnknownData=false] - (optional, default false)
152163
* @param {boolean} [options.mergeHeartRates=true] - (optional, default false)
164+
* @param {boolean} [options.skipHeader=false] - (optional, default false)
165+
* @param {boolean} [options.dataOnly=false] - (optional, default false)
153166
* @return {Object} result - {messages:Array, errors:Array}
154167
*/
155168
read({
@@ -160,7 +173,9 @@ class Decoder {
160173
convertTypesToStrings = true,
161174
convertDateTimesToDates = true,
162175
includeUnknownData = false,
163-
mergeHeartRates = true } = {}) {
176+
mergeHeartRates = true,
177+
skipHeader = false,
178+
dataOnly = false,} = {}) {
164179

165180
this.#mesgListener = mesgListener;
166181
this.#optExpandSubFields = expandSubFields
@@ -182,7 +197,11 @@ class Decoder {
182197
this.#throwError("mergeHeartRates requires applyScaleAndOffset and expandComponents to be enabled");
183198
}
184199

185-
this.#stream.reset();
200+
if (dataOnly && skipHeader) {
201+
this.#throwError("dataOnly and skipHeader cannot both be enabled")
202+
}
203+
204+
this.#decodeMode = skipHeader ? DecodeMode.SKIP_HEADER : dataOnly ? DecodeMode.DATA_ONLY : DecodeMode.NORMAL;
186205

187206
while (this.#stream.position < this.#stream.length) {
188207
this.#decodeNextFile();
@@ -203,23 +222,23 @@ class Decoder {
203222
#decodeNextFile() {
204223
const position = this.#stream.position;
205224

206-
if (!this.isFIT()) {
225+
if (this.#decodeMode === DecodeMode.NORMAL && !this.isFIT()) {
207226
this.#throwError("input is not a FIT file");
208227
}
209228

210229
this.#stream.crcCalculator = new CrcCalculator();
211230

212-
const fileHeader = Decoder.#readFileHeader(this.#stream);
231+
const { headerSize, dataSize } = Decoder.#readFileHeader(this.#stream, { decodeMode: this.#decodeMode });
213232

214233
// Read data messages and definitions
215-
while (this.#stream.position < (position + fileHeader.headerSize + fileHeader.dataSize)) {
234+
while (this.#stream.position < (position + headerSize + dataSize)) {
216235
this.#decodeNextRecord();
217236
}
218237

219238
// Check the CRC
220239
const calculatedCrc = this.#stream.crcCalculator.crc;
221240
const crc = this.#stream.readUInt16();
222-
if (crc !== calculatedCrc) {
241+
if (this.#decodeMode === DecodeMode.NORMAL && crc !== calculatedCrc) {
223242
this.#throwError("CRC error");
224243
}
225244
}
@@ -341,7 +360,7 @@ class Decoder {
341360
}
342361

343362
if (field?.isAccumulated) {
344-
this.#accumulator.add(mesgNum, fieldDefinition.fieldDefinitionNumber, rawFieldValue);
363+
this.#setAccumulatedField(messageDefinition, message, field, rawFieldValue);
345364
}
346365
}
347366
});
@@ -530,7 +549,7 @@ class Decoder {
530549
while (this.#fieldsToExpand.length > 0) {
531550
const name = this.#fieldsToExpand.shift();
532551

533-
const { rawFieldValue, fieldDefinitionNumber, isSubField } = message[name];
552+
const { rawFieldValue, fieldDefinitionNumber, isSubField } = message[name] ?? mesg[name];
534553
let field = Profile.messages[mesgNum].fields[fieldDefinitionNumber];
535554
field = isSubField ? this.#lookupSubfield(field, name) : field;
536555
const baseType = FIT.FieldTypeToBaseType[field.type];
@@ -546,6 +565,10 @@ class Decoder {
546565
const bitStream = new BitStream(rawFieldValue, baseType);
547566

548567
for (let j = 0; j < field.components.length; j++) {
568+
if (bitStream.bitsAvailable < field.bits[j]) {
569+
break;
570+
}
571+
549572
const targetField = fields[field.components[j]];
550573
if (mesg[targetField.name] == null) {
551574
const baseType = FIT.FieldTypeToBaseType[targetField.type];
@@ -560,22 +583,22 @@ class Decoder {
560583
};
561584
}
562585

563-
if (bitStream.bitsAvailable < field.bits[j]) {
564-
break;
565-
}
566-
567586
let value = bitStream.readBits(field.bits[j]);
568587

569-
value = this.#accumulator.accumulate(mesgNum, targetField.num, value, field.bits[j]) ?? value;
588+
if (targetField.isAccumulated) {
589+
value = this.#accumulator.accumulate(mesgNum, targetField.num, value, field.bits[j]);
590+
}
591+
592+
// Undo component scale and offset before applying the destination field's scale and offset
593+
value = (value / field.scale[j] - field.offset[j]);
570594

571-
mesg[targetField.name].rawFieldValue.push(value);
595+
const rawValue = (value + targetField.offset) * targetField.scale;
596+
mesg[targetField.name].rawFieldValue.push(rawValue);
572597

573-
if (value === mesg[targetField.name].invalidValue) {
598+
if (rawValue === mesg[targetField.name].invalidValue) {
574599
mesg[targetField.name].fieldValue.push(null);
575600
}
576601
else {
577-
value = value / field.scale[j] - field.offset[j];
578-
579602
if (this.#optConvertTypesToStrings) {
580603
value = this.#convertTypeToString(mesg, targetField, value);
581604
}
@@ -681,6 +704,26 @@ class Decoder {
681704
}
682705
}
683706

707+
#setAccumulatedField(messageDefinition, message, field, rawFieldValue) {
708+
const rawFieldValues = Array.isArray(rawFieldValue) ? rawFieldValue : [rawFieldValue];
709+
710+
rawFieldValues.forEach((value) => {
711+
Object.values(message).forEach((containingField) => {
712+
let components = messageDefinition.fields[containingField.fieldDefinitionNumber].components ?? []
713+
714+
components.forEach((componentFieldNum, i) => {
715+
const targetField = messageDefinition.fields[componentFieldNum];
716+
717+
if(targetField?.num == field.num && targetField?.isAccumulated) {
718+
value = (((value / field.scale) - field.offset) + containingField.offset[i]) * containingField.scale[i];
719+
}
720+
});
721+
});
722+
723+
this.#accumulator.createAccumulatedField(messageDefinition.num, field.num, value);
724+
});
725+
}
726+
684727
#convertTypeToString(messageDefinition, field, rawFieldValue) {
685728
if ([Profile.MesgNum.DEVELOPER_DATA_ID, Profile.MesgNum.FIELD_DESCRIPTION].includes(messageDefinition.globalMessageNumber)) {
686729
return rawFieldValue;
@@ -711,9 +754,22 @@ class Decoder {
711754
return subField != null ? subField : {};
712755
}
713756

714-
static #readFileHeader(stream, resetPosition = false) {
757+
static #readFileHeader(stream, { resetPosition = false, decodeMode = DecodeMode.NORMAL }) {
715758
const position = stream.position;
716759

760+
if(decodeMode !== DecodeMode.NORMAL) {
761+
if(decodeMode === DecodeMode.SKIP_HEADER) {
762+
stream.seek(HEADER_WITH_CRC_SIZE);
763+
}
764+
765+
const headerSize = decodeMode === DecodeMode.SKIP_HEADER ? HEADER_WITH_CRC_SIZE : 0;
766+
767+
return {
768+
headerSize,
769+
dataSize: stream.length - headerSize - CRC_SIZE,
770+
};
771+
}
772+
717773
const fileHeader = {
718774
headerSize: stream.readByte(),
719775
protocolVersion: stream.readByte(),

src/fit.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// Transfer (FIT) Protocol License.
66
/////////////////////////////////////////////////////////////////////////////////////////////
77
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
8-
// Profile Version = 21.141.0Release
9-
// Tag = production/release/21.141.0-0-g2aa27e1
8+
// Profile Version = 21.158.0Release
9+
// Tag = production/release/21.158.0-0-gc9428aa
1010
/////////////////////////////////////////////////////////////////////////////////////////////
1111

1212

0 commit comments

Comments
 (0)