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