public Field(Field field)
        {
            if (field == null)
             {
            this.name = "unknown";
            this.num = Fit.FieldNumInvalid;
            this.type = 0;
            this.scale = 1f;
            this.offset = 0f;
            this.units = "";
            return;
             }

             this.name = field.Name;
             this.num = field.Num;
             this.type = field.Type;
             this.scale = field.Scale;
             this.offset = field.Offset;
             this.units = field.units;

             foreach (object obj in field.values)
             {
            this.values.Add(obj);
             }
             foreach (Subfield subfield in field.subfields)
             {
            this.subfields.Add(new Subfield(subfield));
             }
             foreach (FieldComponent component in field.components)
             {
            this.components.Add(new FieldComponent(component));
             }
        }
        public void SetFieldValue(String name, int fieldArrayIndex, Object value)
        {
            Field testField =  new Field(this.GetField(name));
             Subfield subfield = testField.GetSubfield(name);

             if ((subfield != null) && !(subfield.CanMesgSupport(this)))
             {
            return;
             }

             Field field = GetField(name, false);

             if (field == null)
             {
            field = new Field(Profile.GetMesg(this.Num).GetField(name));
            SetField(field);
             }

             field.SetValue(fieldArrayIndex, value, name);
        }
        public void SetFieldValue(byte fieldNum, int fieldArrayIndex, Object value, String subfieldName)
        {
            Field testField =  new Field(this.GetField(fieldNum));
             Subfield subfield = testField.GetSubfield(subfieldName);

             if ((subfield != null) && !(subfield.CanMesgSupport(this)))
             {
            return;
             }

             Field field = GetField(fieldNum);

             if (field == null)
             {
            // We normally won't have fields attached to our skeleton message,
            // as we add values we need to add the fields too based on the mesg,field
            // combo in the profile.
            field = new Field(Profile.GetMesg(this.Num).GetField(fieldNum));
            if (field.Num == Fit.FieldNumInvalid)
            {
               // If there was no info in the profile our FieldNum will get set to invalid,
               // at least preserve FieldNum while we know it
               field.Num = fieldNum;
            }
            SetField(field);
             }
             field.SetValue(fieldArrayIndex, value, subfieldName);
        }
 /// <summary>
 /// Replace an existing field, otherwise add a reference to fields list
 /// </summary>
 /// <param name="field">Caller allocated field</param>
 public void SetField(Field field)
 {
     for (int i = 0; i < fields.Count; i++)
      {
     if (fields[i].Num == field.Num)
     {
        fields[i] = field;
        return;
     }
      }
      fields.Add(field);
 }
        public void Read(Stream inStream, MesgDefinition defnMesg)
        {
            inStream.Position = 1;
             EndianBinaryReader mesgReader = new EndianBinaryReader(inStream, defnMesg.IsBigEndian);

             LocalNum = defnMesg.LocalMesgNum;

             foreach (FieldDefinition fieldDef in defnMesg.GetFields())
             {
            // It's possible the field type found in the field definition may
            // not agree with the type defined in the profile.  The profile
            // type will be preferred for decode.
            Field field = GetField(fieldDef.Num);
            if (field == null)
            {
               // We normally won't have fields attached to our skeleton message,
               // as we add values we need to add the fields too based on the mesg,field
               // combo in the profile.  Must derive from the profile so the scale etc
               // is correct
               field = new Field(Profile.GetMesg(this.Num).GetField(fieldDef.Num));
               if (field.Num == Fit.FieldNumInvalid)
               {
                  // If there was no info in the profile the FieldNum will get set to invalid
                  // so preserve the unknown fields info while we know it
                  field.Num = fieldDef.Num;
                  field.Type = fieldDef.Type;
               }
               SetField(field);
            }

            object value;

            // strings may be an array and are of variable length
            if ((field.Type & Fit.BaseTypeNumMask) == Fit.String)
            {
               List<byte> utf8Bytes = new List<byte>();
               byte b = new byte();

               for (int i=0; i<fieldDef.Size; i++)
               {
                  b = mesgReader.ReadByte();
                  if (b == 0x00)
                  {
                     field.AddValue(utf8Bytes.ToArray());
                     utf8Bytes.Clear();
                  }
                  else
                  {
                     utf8Bytes.Add(b);
                  }
               }
               if (utf8Bytes.Count != 0)
               {
                  field.AddValue(utf8Bytes.ToArray());
                  utf8Bytes.Clear();
               }
            }
            else
            {
               int numElements = (int)fieldDef.Size / Fit.BaseType[field.Type & Fit.BaseTypeNumMask].size;
               for (int i=0; i < numElements; i++)
               {
                  switch (field.Type & Fit.BaseTypeNumMask)
                  {
                     case Fit.Enum:
                     case Fit.Byte:
                     case Fit.UInt8:
                     case Fit.UInt8z:
                        value = mesgReader.ReadByte();
                        break;

                     case Fit.SInt8:
                        value = mesgReader.ReadSByte();
                        break;

                     case Fit.SInt16:
                        value = mesgReader.ReadInt16();
                        break;

                     case Fit.UInt16:
                     case Fit.UInt16z:
                        value = mesgReader.ReadUInt16();
                        break;

                     case Fit.SInt32:
                        value = mesgReader.ReadInt32();
                        break;

                     case Fit.UInt32:
                     case Fit.UInt32z:
                        value = mesgReader.ReadUInt32();
                        break;

                     case Fit.Float32:
                        value = mesgReader.ReadSingle();
                        break;

                     case Fit.Float64:
                        value = mesgReader.ReadDouble();
                        break;

                     default:
                        value = mesgReader.ReadBytes(fieldDef.Size);
                        break;
                  }
                  field.SetRawValue(i, value);
               }
            }
             }
        }
 /// <summary>
 /// Insert a field at the desired index.  If the field already exists in the mesg it is first removed.
 /// </summary>
 /// <param name="index">Index to insert the field, if index is out of range, the field is added to the end of the list</param>
 /// <param name="field">Caller allocated field</param>
 public void InsertField(int index, Field field)
 {
     // if message already contains this field, remove it
      for (int i = 0; i < fields.Count; i++)
      {
     if (fields[i].Num == field.Num)
     {
        fields.RemoveAt(i);
     }
      }
      // if the index is out of range, add to the end
      if (index < 0 || index > fields.Count)
      {
     fields.Add(field);
      }
      // insert the new field at desired index
      else
      {
     fields.Insert(index, field);
      }
 }
        public string GetActiveSubFieldName(byte fieldNum)
        {
            Field testField = new Field(this.GetField(fieldNum));

             if (testField == null)
             {
            return Fit.SubfieldNameMainField;
             }

             foreach (Subfield subfield in testField.subfields)
             {
            if (subfield.CanMesgSupport(this))
            {
               return subfield.Name;
            }
             }
             return Fit.SubfieldNameMainField;
        }
        public ushort GetActiveSubFieldIndex(byte fieldNum)
        {
            Field testField = new Field(this.GetField(fieldNum));

             if (testField == null)
             {
            return Fit.SubfieldIndexMainField;
             }

             for (ushort i = 0; i < testField.subfields.Count; i++)
             {
            if (testField.subfields[i].CanMesgSupport(this))
            {
               return i;
            }
             }
             return Fit.SubfieldIndexMainField;
        }
        public void ExpandComponents()
        {
            // We can't modify the fields collection as we are traversing it, save new fields
             // to add after we complete expansion
             List<Field> newFields = new List<Field>();

             foreach (Field myField in fields)
             {
            List<FieldComponent> compsToExpand = null;
            Field containingField = null;

            // Determine the active subfield and expand if it has any components
            ushort activeSubfield = GetActiveSubFieldIndex(myField.Num);

            if (activeSubfield == Fit.SubfieldIndexMainField)
            {
               // There are main field components, expand
               if (myField.components.Count > 0)
               {
                  compsToExpand = myField.components;
                  containingField = myField;
               }
            }
            else
            {
               // There are subfield components, expand
               if (myField.GetSubfield(activeSubfield).Components.Count > 0)
               {
                  compsToExpand = myField.GetSubfield(activeSubfield).Components;
                  containingField = myField;
               }
            }
            if (compsToExpand != null)
            {
               // Comp Decode
               if (containingField.values.Count > 0)
               {
                  int offset = 0;

                  foreach (FieldComponent component in compsToExpand)
                  {
                     if (component.fieldNum != Fit.FieldNumInvalid)
                     {
                        Field destinationField = new Field(Profile.GetMesg(this.Num).GetField(component.fieldNum));

                        // GetBitsValue will not return more bits than the componentField type can hold.
                        // This means strings are built one letter at a time when using components
                        // which is a bad idea to start with)
                        long? bitsValue = containingField.GetBitsValue(offset, component.bits, destinationField.Type);
                        if (bitsValue == null)
                        {
                           break;
                        }

                        if (component.accumulate == true)
                        {
                           bitsValue = component.Accumulate(bitsValue.Value);
                        }

                        if (destinationField.IsNumeric())
                        {
                           float fbitsValue = Convert.ToSingle(bitsValue);
                           fbitsValue = ((float)fbitsValue / component.scale) - component.offset;

                           if (this.HasField(destinationField.Num) == true)
                           {
                              this.GetField(destinationField.Num).SetValue(this.GetField(destinationField.Num).values.Count, fbitsValue);
                           }
                           else
                           {
                              destinationField.SetValue(fbitsValue);
                              newFields.Add(destinationField);
                           }
                        }
                        // Shouldn't apply scale/offset to string or enum
                        else
                        {
                           object nonNumericBitsValue;
                           // Ensure strings are added as byte[]
                           if ((destinationField.Type & Fit.BaseTypeNumMask) == Fit.String)
                           {
                              nonNumericBitsValue = new byte[] { (byte)bitsValue };
                           }
                           else
                           {
                              nonNumericBitsValue = bitsValue;
                           }
                           if (this.HasField(destinationField.Num) == true)
                           {
                              this.GetField(destinationField.Num).SetValue(this.GetField(destinationField.Num).values.Count, nonNumericBitsValue);
                           }
                           else
                           {
                              destinationField.SetValue(nonNumericBitsValue);
                              newFields.Add(destinationField);
                           }
                        }
                     }
                     offset += component.bits;
                  }
               }
            }
             }
             foreach (Field newField in newFields)
             {
            // Another component added this field during expansion, append the additional values
            if (this.HasField(newField.Num) == true)
            {
               foreach (object newValue in newField.values)
               {
                  this.GetField(newField.Num).SetValue(this.GetField(newField.Num).values.Count, newValue);
               }
            }
            // Add the new field
            else
            {
               this.SetField(newField);
            }
             }
        }
        public static Field GetField(ushort globalMesgNum, byte fieldNum)
        {
            Field returnField = null;

             foreach (Mesg mesg in mesgs)
             {
            if (mesg.Num == globalMesgNum)
            {
               returnField = mesg.GetField(fieldNum);
               break;
            }
             }
             if (returnField == null)
             {
            returnField = new Field("unknown", fieldNum, 0, 1, 0, "");
             }
             return returnField;
        }
        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.mesgs[Profile.RecordIndex].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 + ".");
            }
            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 + ".");
            }
            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)
            {
               timestamp = (uint)timestampField.GetValue();
               lastTimeOffset = (int)timestamp & Fit.CompressedTimeMask;
            }
            // Now that the entire message is decoded we can evaluate subfields and expand any components
            newMesg.ExpandComponents();

            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"));
             }
        }
 public FieldDefinition(Field field)
 {
     Num = field.Num;
      Size = field.GetSize();
      Type = field.Type;
 }