read method

void read(
  1. Uint8List data, {
  2. DecodeMode mode = DecodeMode.normal,
})

Implementation

void read(Uint8List data, {DecodeMode mode = DecodeMode.normal}) {
  EndianBinaryReader reader = EndianBinaryReader(data);

  if (mode == DecodeMode.normal || mode == DecodeMode.invalidHeader) {
    Header header = Header();
    try {
      header.read(reader);
    } catch (e) {
      if (mode == DecodeMode.normal) rethrow;
    }

    if (mode == DecodeMode.normal) {
      // CRC Check
      int calculatedCrc = Crc.calc16(data, data.length - 2);
      int fileCrc = data[data.length - 2] | (data[data.length - 1] << 8);
      if (calculatedCrc != fileCrc) {
        throw FitException('FIT decode error: File CRC mismatch');
      }
    }
  }

  while (reader.position < reader.length - 2) {
    int headerByte = reader.readByte();

    if ((headerByte & Fit.mesgDefinitionMask) == Fit.mesgDefinitionMask) {
      // Message Definition
      reader.position--; // Back up to read full definition
      MesgDefinition mesgDef = MesgDefinition();
      mesgDef.read(reader, lookup: _lookup);
      _localMesgDefinitions[mesgDef.localMesgNum] = mesgDef;
      onMesgDefinition?.call(mesgDef);
    } else if ((headerByte & Fit.compressedHeaderMask) ==
        Fit.compressedHeaderMask) {
      // Compressed Timestamp Header
      int timeOffset = headerByte & Fit.compressedTimeMask;
      _timestamp += (timeOffset - _lastTimeOffset) & Fit.compressedTimeMask;
      _lastTimeOffset = timeOffset;

      int localMesgNum = (headerByte & Fit.compressedLocalMesgNumMask) >> 5;
      MesgDefinition? def = _localMesgDefinitions[localMesgNum];
      if (def != null) {
        Mesg newMesg = Mesg.fromDefinition(def);
        newMesg.read(reader, def);

        // Add Timestamp Field
        Mesg recordMesg = Profile.getMesg(MesgNum.record);
        Field? timestampProfileField = recordMesg.getFieldByName('timestamp');
        if (timestampProfileField != null) {
          Field timestampField = Field.fromOther(timestampProfileField);
          timestampField.setValue(_timestamp);
          newMesg.insertField(0, timestampField);
        }

        newMesg.expandComponents(_accumulator);
        _handleMetaData(newMesg);
        onMesg?.call(newMesg);
      }
    } else {
      // Data Message
      int localMesgNum = headerByte & Fit.localMesgNumMask;
      MesgDefinition? def = _localMesgDefinitions[localMesgNum];
      if (def != null) {
        Mesg newMesg = Mesg.fromDefinition(def);
        newMesg.read(reader, def);

        Field? timestampField = newMesg.getFieldByName('timestamp');
        if (timestampField != null) {
          Object? val = timestampField.value;
          if (val is int) {
            _timestamp = val;
            _lastTimeOffset = _timestamp & Fit.compressedTimeMask;
          }
        }

        // Accumulate fields
        for (var field in newMesg.fields) {
          if (field.isAccumulated) {
            for (int i = 0; i < field.getNumValues(); i++) {
              var val = field.values[i];
              if (val is! num) continue;
              int value = val.toInt();

              for (var fieldIn in newMesg.fields) {
                for (var fc in fieldIn.components) {
                  if (fc.fieldNum == field.num && fc.accumulate) {
                    value = ((((value / field.scale) - field.offset) +
                                fc.offset) *
                            fc.scale)
                        .round();
                  }
                }
              }
              _accumulator.set(newMesg.num, field.num, value);
            }
          }
        }

        newMesg.expandComponents(_accumulator);
        _handleMetaData(newMesg);
        onMesg?.call(newMesg);
      } else {
        // Should not happen if FIT is valid
        break;
      }
    }
  }
}