123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- #region Copyright
- ////////////////////////////////////////////////////////////////////////////////
- // The following FIT Protocol software provided may be used with FIT protocol
- // devices only and remains the copyrighted property of Dynastream Innovations Inc.
- // The software is being provided on an "as-is" basis and as an accommodation,
- // and therefore all warranties, representations, or guarantees of any kind
- // (whether express, implied or statutory) including, without limitation,
- // warranties of merchantability, non-infringement, or fitness for a particular
- // purpose, are specifically disclaimed.
- //
- // Copyright 2016 Dynastream Innovations Inc.
- ////////////////////////////////////////////////////////////////////////////////
- // ****WARNING**** This file is auto-generated! Do NOT edit this file.
- // Profile Version = 16.60Release
- // Tag = production-akw-16.60.00-0-g5d3d436
- ////////////////////////////////////////////////////////////////////////////////
- #endregion
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using System.IO;
- using System.Threading;
- namespace Dynastream.Fit
- {
- /// <summary>
- /// This class will decode a .fit file reading the file header and any definition or data messages.
- /// </summary>
- public class Decode
- {
- private const long CRCSIZE = 2;
- private const uint INVALID_DATA_SIZE = 0;
- #region Fields
- private MesgDefinition[] localMesgDefs = new MesgDefinition[Fit.MaxLocalMesgs];
- private Header fileHeader;
- private uint timestamp = 0;
- private int lastTimeOffset = 0;
- private bool invalidDataSize = false;
- private Accumulator accumulator = new Accumulator();
- #endregion
- #region Properties
- public bool InvalidDataSize
- {
- get
- {
- return invalidDataSize;
- }
- set
- {
- invalidDataSize = value;
- }
- }
- #endregion
- #region Constructors
- public Decode()
- {
- }
- #endregion
- #region Methods
- public event MesgEventHandler MesgEvent;
- public event MesgDefinitionEventHandler MesgDefinitionEvent;
- /// <summary>
- /// Reads the file header to check if the file is FIT.
- /// Does not check CRC.
- /// Returns true if file is FIT.
- /// </summary>
- /// <param name="fitStream"> Seekable (file)stream to parse</param>
- public bool IsFIT(Stream fitStream)
- {
- long position = fitStream.Position;
- bool status = false;
- try
- {
- // Does the header contain the flag string ".FIT"?
- Header header = new Header(fitStream);
- fitStream.Position = position;
- status = header.IsValid();
- }
- // If the header is malformed the ctor could throw an exception
- catch (FitException)
- {
- }
- fitStream.Position = position;
- return status;
- }
- /// <summary>
- /// Reads the FIT binary file header and crc to check compatibility and integrity.
- /// Also checks data reords size.
- /// Returns true if file is ok (not corrupt).
- ///</summary>
- /// <param name="fitStream">Seekable (file)stream to parse.</param>
- public bool CheckIntegrity(Stream fitStream)
- {
- bool isValid = true;
- long position = fitStream.Position;
- long fileSize = 0;
- try
- {
- while ((fitStream.Position < fitStream.Length) && (isValid == true))
- {
- // Is there a valid header?
- Header header = new Header(fitStream);
- isValid = header.IsValid();
- // Get the file size from the header
- // When the data size is 0 set flags, don't calculate CRC
- if (header.DataSize > INVALID_DATA_SIZE)
- {
- fileSize = header.Size + header.DataSize + CRCSIZE;
- // Is the file CRC ok?
- // Need to rewind the header size because the header is part of the CRC calculation.
- byte[] data = new byte[fileSize];
- fitStream.Position = fitStream.Position - header.Size;
- fitStream.Read(data, 0, data.Length);
- isValid &= (CRC.Calc16(data, data.Length) == 0x0000);
- }
- else
- {
- invalidDataSize = true;
- isValid = false;
- }
- }
- }
- catch (FitException)
- {
- isValid = false;
- }
- fitStream.Position = position;
- return isValid;
- }
- /// <summary>
- /// Reads a FIT binary file.
- /// </summary>
- /// <param name="fitStream">Seekable (file)stream to parse.</param>
- /// <returns>
- /// Returns true if reading finishes successfully.
- /// </returns>
- public bool Read(Stream fitStream)
- {
- bool status = true;
- while ((fitStream.Position < fitStream.Length) && (status == true))
- {
- status = Read(fitStream, false);
- }
- return status;
- }
- /// <summary>
- /// Reads a FIT binary file.
- /// </summary>
- /// <param name="fitStream">Seekable (file)stream to parse.</param>
- /// <param name="skipHeader">When true, skip file header. Also CRC will not be calculated.</param>
- /// <returns>
- /// Returns true if reading finishes successfully.
- /// </returns>
- public bool Read(Stream fitStream, bool skipHeader)
- {
- bool readOK = true;
- long fileSize = 0;
- long filePosition = fitStream.Position;
- try
- {
- // Attempt to read header
- if (!skipHeader)
- {
- fileHeader = new Header(fitStream);
- readOK &= fileHeader.IsValid();
- // Get the file size from the header
- // When the data size is invalid set the file size to the fitstream length
- if (!invalidDataSize)
- {
- fileSize = fileHeader.Size + fileHeader.DataSize + CRCSIZE;
- }
- else
- {
- fileSize = fitStream.Length;
- }
- if (!readOK)
- {
- throw new FitException("FIT decode error: File is not FIT format. Check file header data type. Error at stream position: " + fitStream.Position);
- }
- if ((fileHeader.ProtocolVersion & Fit.ProtocolVersionMajorMask) > (Fit.ProtocolMajorVersion << Fit.ProtocolVersionMajorShift))
- {
- // The decoder does not support decode accross protocol major revisions
- throw new FitException(String.Format("FIT decode error: Protocol Version {0}.X not supported by SDK Protocol Ver{1}.{2} ", (fileHeader.ProtocolVersion & Fit.ProtocolVersionMajorMask) >> Fit.ProtocolVersionMajorShift, Fit.ProtocolMajorVersion, Fit.ProtocolMinorVersion));
- }
- }
- else
- {
- // When skipping the header force the stream position to be at the beginning of the file
- // Also the fileSize is the length of the filestream.
- fitStream.Position = 0;
- fileSize = fitStream.Length;
- }
- // Read data messages and definitions
- while (fitStream.Position < (filePosition + fileSize - CRCSIZE))
- {
- DecodeNextMessage(fitStream);
- }
- // Is the file CRC ok?
- if (!skipHeader && !invalidDataSize)
- {
- byte[] data = new byte[fileSize];
- fitStream.Position = filePosition;
- fitStream.Read(data, 0, data.Length);
- readOK &= (CRC.Calc16(data, data.Length) == 0x0000);
- fitStream.Position = filePosition + fileSize;
- }
- }
- catch (EndOfStreamException e)
- {
- readOK = false;
- // Debug.Log(string.Format("{0} caught and ignored. ", e.GetType().Name));
- throw new FitException("Decode:Read - Unexpected End of File at stream position" + fitStream.Position, e);
- }
- catch (FitException e)
- {
- // When attempting to decode files with invalid data size this indicates the EOF.
- if (!invalidDataSize)
- {
- throw e;
- }
- }
- return readOK;
- }
- public void DecodeNextMessage(Stream fitStream)
- {
- BinaryReader br = new BinaryReader(fitStream);
- byte nextByte = br.ReadByte();
- // Is it a compressed timestamp mesg?
- if ((nextByte & Fit.CompressedHeaderMask) == Fit.CompressedHeaderMask)
- {
- MemoryStream mesgBuffer = new MemoryStream();
- int timeOffset = nextByte & Fit.CompressedTimeMask;
- timestamp += (uint)((timeOffset - lastTimeOffset) & Fit.CompressedTimeMask);
- lastTimeOffset = timeOffset;
- Field timestampField = new Field(Profile.GetMesg(MesgNum.Record).GetField("Timestamp"));
- timestampField.SetValue(timestamp);
- byte localMesgNum = (byte)((nextByte & Fit.CompressedLocalMesgNumMask) >> 5);
- mesgBuffer.WriteByte(localMesgNum);
- if (localMesgDefs[localMesgNum] == null)
- {
- throw new FitException("Decode:DecodeNextMessage - FIT decode error: Missing message definition for local message number " + localMesgNum + " at stream position " + fitStream.Position);
- }
- int fieldsSize = localMesgDefs[localMesgNum].GetMesgSize() - 1;
- try
- {
- mesgBuffer.Write(br.ReadBytes(fieldsSize), 0, fieldsSize);
- }
- catch (IOException e)
- {
- throw new FitException("Decode:DecodeNextMessage - Compressed Data Message unexpected end of file. Wanted " + fieldsSize + " bytes at stream position " + fitStream.Position, e);
- }
- Mesg newMesg = new Mesg(mesgBuffer, localMesgDefs[localMesgNum]);
- newMesg.InsertField(0, timestampField);
- if (MesgEvent != null)
- {
- MesgEvent(this, new MesgEventArgs(newMesg));
- }
- }
- // Is it a mesg def?
- else if ((nextByte & Fit.HeaderTypeMask) == Fit.MesgDefinitionMask)
- {
- MemoryStream mesgDefBuffer = new MemoryStream();
- // Figure out number of fields (length) of our defn and build buffer
- mesgDefBuffer.WriteByte(nextByte);
- mesgDefBuffer.Write(br.ReadBytes(4), 0, 4);
- byte numfields = br.ReadByte();
- mesgDefBuffer.WriteByte(numfields);
- try
- {
- mesgDefBuffer.Write(br.ReadBytes(numfields * 3), 0, numfields * 3);
- }
- catch (IOException e)
- {
- throw new FitException("Decode:DecodeNextMessage - Defn Message unexpected end of file. Wanted " + (numfields * 3) + " bytes at stream position " + fitStream.Position, e);
- }
- MesgDefinition newMesgDef = new MesgDefinition(mesgDefBuffer);
- localMesgDefs[newMesgDef.LocalMesgNum] = newMesgDef;
- if (MesgDefinitionEvent != null)
- {
- MesgDefinitionEvent(this, new MesgDefinitionEventArgs(newMesgDef));
- }
- }
- // Is it a data mesg?
- else if ((nextByte & Fit.HeaderTypeMask) == Fit.MesgHeaderMask)
- {
- MemoryStream mesgBuffer = new MemoryStream();
- byte localMesgNum = (byte)(nextByte & Fit.LocalMesgNumMask);
- mesgBuffer.WriteByte(localMesgNum);
- if (localMesgDefs[localMesgNum] == null)
- {
- throw new FitException("Decode:DecodeNextMessage - FIT decode error: Missing message definition for local message number " + localMesgNum + " at stream position " + fitStream.Position);
- }
- int fieldsSize = localMesgDefs[localMesgNum].GetMesgSize() - 1;
- try
- {
- mesgBuffer.Write(br.ReadBytes(fieldsSize), 0, fieldsSize);
- }
- catch (Exception e)
- {
- throw new FitException("Decode:DecodeNextMessage - Data Message unexpected end of file. Wanted " + fieldsSize + " bytes at stream position " + fitStream.Position, e);
- }
- Mesg newMesg = new Mesg(mesgBuffer, localMesgDefs[localMesgNum]);
- // If the new message contains a timestamp field, record the value to use as
- // a reference for compressed timestamp headers
- Field timestampField = newMesg.GetField("Timestamp");
- if (timestampField != null)
- {
- object tsValue = timestampField.GetValue();
- if (tsValue != null)
- {
- timestamp = (uint)tsValue;
- lastTimeOffset = (int)timestamp & Fit.CompressedTimeMask;
- }
- }
- foreach (Field field in newMesg.fields)
- {
- if (field.IsAccumulated)
- {
- int i;
- for (i = 0; i < field.GetNumValues(); i++)
- {
- long value = Convert.ToInt64(field.GetRawValue(i));
- accumulator.Set(newMesg.Num, field.Num, value);
- }
- }
- }
- // Now that the entire message is decoded we can evaluate subfields and expand any components
- newMesg.ExpandComponents(accumulator);
- if (MesgEvent != null)
- {
- MesgEvent(this, new MesgEventArgs(newMesg));
- }
- }
- else
- {
- throw new FitException("Decode:Read - FIT decode error: Unexpected Record Header Byte 0x" + nextByte.ToString("X") + " at stream position: " + fitStream.Position);
- }
- }
- #endregion
- } // class
- } // namespace
|