fromBytes static method

FitFile fromBytes(
  1. Uint8List bytes, {
  2. dynamic checkCrc = true,
  3. dynamic checkRecords = false,
})

Implementation

static FitFile fromBytes(Uint8List bytes,
    {checkCrc = true, checkRecords = false}) {
  var byteOffset = 0;
  var crc = 0;

  final headerSize = bytes[0];
  final headerBytes =
      Uint8List.sublistView(bytes, byteOffset, byteOffset + headerSize);
  final header = FitFileHeader.fromBytes(headerBytes);
  crc = crc16(headerBytes, initial: crc);
  byteOffset += header.size;

  final records = <Record>[];
  final definitionMessageMap = <int, DefinitionMessage>{};
  final developerFieldsById = <int, Map<int, DeveloperField>>{};

  var recordIndex = 0;
  var recordBytesRemainingCount = header.recordsSize;
  while (recordBytesRemainingCount > 0) {
    // print('Record $recordIndex');
    final remainingBytes = Uint8List.sublistView(bytes, byteOffset);

    final record = Record.fromBytes(definitionMessageMap, remainingBytes,
        developerFieldsById: developerFieldsById);

    if (record.isDefinition) {
      definitionMessageMap[record.localId] =
          record.message as DefinitionMessage;
    } else if (record.message is FieldDescriptionMessage) {
      final message = record.message as FieldDescriptionMessage;

      //TODO(mkt): more work is needed here for more complicated dynamic fields.
      final developerField = DeveloperField(
        developerDataIndex: message.developerDataIndex ?? 0,
        id: message.fieldDefinitionNumber ?? 0,
        name: message.name,
        type: BaseTypeExtension.fromValue(message.fitBaseTypeId ?? 0),
        scale: message.scale?.toDouble(),
        offset: message.offset?.toDouble(),
        units: message.units ?? '',
      );
      if (developerFieldsById[developerField.developerDataIndex] == null) {
        developerFieldsById[developerField.developerDataIndex] = {};
      }
      developerFieldsById[developerField.developerDataIndex]![
          developerField.id] = developerField;
    }

    records.add(record);

    final definitionMessage = definitionMessageMap[record.localId];
    final recordSize = record.size;
    final definedSize = record.definedSize(definitionMessage!);

    crc = crc16(
        Uint8List.sublistView(bytes, byteOffset, byteOffset + definedSize),
        initial: crc);

    if (recordSize != definedSize) {
      logger.w(
          'Record $recordIndex, ${record.message}: size ($recordSize) != defined size ($definedSize). Some fields were not read correctly.');
    }

    if (checkRecords) {
      final actualBytes =
          Uint8List.sublistView(remainingBytes, 0, definedSize);
      final recordBytes = record.toBytes();

      if (!listEqual(actualBytes, recordBytes)) {
        logger.w(
            '- $recordIndex -\nactual: $actualBytes\nrecord: $recordBytes');
      }
    }

    recordBytesRemainingCount -= definedSize;
    byteOffset += definedSize;

    recordIndex++;
  }

  final byteData = ByteData.sublistView(bytes, byteOffset, byteOffset + 2);
  final fileCrc = byteData.getUint16(0, Endian.little);

  if (crc != fileCrc) {
    final message =
        'Calculated crc (0x${crc.toRadixString(16)}) does match crc in file (0x${fileCrc.toRadixString(16)}).';
    if (checkCrc) {
      throw Exception(message);
    } else {
      logger.w(message);
    }
  }

  return FitFile(header, records, crc);
}