Esempio n. 1
0
        public static void ExportSchedulesFITFile(Stream exportStream, MemoryStream dataStream)
        {
            MemoryStream tempDataStream = new MemoryStream();

            // Reserve size for header
            tempDataStream.Write(new Byte[12], 0, 12);

            // File id message
            FITMessage fileIdMessage = new FITMessage(FITGlobalMessageIds.FileId);
            FITMessageField fileType = new FITMessageField((Byte)FITFileIdFieldsIds.FileType);
            FITMessageField manufacturerId = new FITMessageField((Byte)FITFileIdFieldsIds.ManufacturerId);
            FITMessageField productId = new FITMessageField((Byte)FITFileIdFieldsIds.ProductId);
            FITMessageField serialNumber = new FITMessageField((Byte)FITFileIdFieldsIds.SerialNumber);
            FITMessageField exportDate = new FITMessageField((Byte)FITFileIdFieldsIds.ExportDate);
            FITMessageField number = new FITMessageField((Byte)FITFileIdFieldsIds.Number);

            manufacturerId.SetUInt16(1);
            fileType.SetEnum((Byte)FITFileTypes.Schedules);

            // Invalid fields
            productId.SetUInt16(0xFFFF);
            serialNumber.SetUInt32z(0);
            exportDate.SetUInt32(0xFFFFFFFF);
            number.SetUInt16(0xFFFF);

            fileIdMessage.AddField(serialNumber);
            fileIdMessage.AddField(exportDate);
            fileIdMessage.AddField(manufacturerId);
            fileIdMessage.AddField(productId);
            fileIdMessage.AddField(number);
            fileIdMessage.AddField(fileType);

            fileIdMessage.Serialize(tempDataStream);

            // Write all passed in data to output stream
            tempDataStream.Write(dataStream.GetBuffer(), 0, (int)dataStream.Length);

            // Write FIT header at the start of the stream
            GarminFitnessByteRange headerSize = new GarminFitnessByteRange(12);
            GarminFitnessByteRange protocolVersion = new GarminFitnessByteRange((Byte)((FITConstants.FITProtocolMajorVersion << 4) | FITConstants.FITProtocolMinorVersion));
            GarminFitnessUInt16Range profileVersion = new GarminFitnessUInt16Range((UInt16)((FITConstants.FITProfileMajorVersion * FITConstants.FITProfileMajorVersionMultiplier) + FITConstants.FITProfileMinorVersion));
            GarminFitnessInt32Range dataSize = new GarminFitnessInt32Range(0);

            tempDataStream.Seek(0, SeekOrigin.Begin);
            dataSize.Value = (int)tempDataStream.Length - 12;

            headerSize.Serialize(tempDataStream);
            protocolVersion.Serialize(tempDataStream);
            profileVersion.Serialize(tempDataStream);
            dataSize.Serialize(tempDataStream);
            tempDataStream.Write(Encoding.UTF8.GetBytes(FITConstants.FITFileDescriptor), 0, 4);

            // Write CRC
            GarminFitnessUInt16Range crc = new GarminFitnessUInt16Range(FITUtils.ComputeStreamCRC(tempDataStream));
            tempDataStream.Seek(0, SeekOrigin.End);
            crc.Serialize(tempDataStream);

            // Write all data to output stream
            exportStream.Write(tempDataStream.GetBuffer(), 0, (int)tempDataStream.Length);
        }
        public void TestFITScheduleDeserialization()
        {
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutSchedules);
            FITMessageField dateField = new FITMessageField((Byte)FITScheduleFieldIds.ScheduledDate);
            DateTime referenceDate = new DateTime(1989, 12, 31);
            DateTime resultDate;

            serializedMessage.AddField(dateField);

            DateTime currentDate = DateTime.Today;
            dateField.SetUInt32((UInt32)(currentDate - referenceDate).TotalSeconds);
            resultDate = WorkoutImporter.ImportWorkoutScheduleMessage(serializedMessage);
            Assert.AreEqual(currentDate, resultDate, "Deserialized date invalid for FIT format");

            currentDate = currentDate.AddDays(1);
            dateField.SetUInt32((UInt32)(currentDate - referenceDate).TotalSeconds);
            resultDate = WorkoutImporter.ImportWorkoutScheduleMessage(serializedMessage);
            Assert.AreEqual(currentDate, resultDate, "Deserialized date invalid for FIT format");

            dateField.SetUInt32(315576000);
            resultDate = WorkoutImporter.ImportWorkoutScheduleMessage(serializedMessage);
            Assert.AreEqual(new DateTime(1999, 12, 31, 12, 0, 0), resultDate, "Deserialized date invalid for FIT format");

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            bool res = Time.SetTimeZone("New Zealand Standard Time");
            dateField.SetUInt32(315576000);
            resultDate = WorkoutImporter.ImportWorkoutScheduleMessage(serializedMessage);
            res = Time.SetTimeZone(currentZoneName);
            Assert.AreEqual(new DateTime(1999, 12, 31, 12, 0, 0), resultDate, "Deserialized date invalid for FIT format");
        }
        public void TestFITDeserialization()
        {
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            Workout placeholderWorkout = null;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField nameField = new FITMessageField((Byte)FITWorkoutFieldIds.WorkoutName);
            FITMessageField numStepsField = new FITMessageField((Byte)FITWorkoutFieldIds.NumSteps);
            FITMessageField categoryField = new FITMessageField((Byte)FITWorkoutFieldIds.SportType);

            try
            {
                placeholderWorkout = WorkoutImporter.ImportWorkoutFromMessage(serializedMessage, logbook.ActivityCategories[0]);
                Assert.Fail("Workout without name was FIT deserialized");
            }
            catch (FITParserException)
            {
            }

            nameField.SetString(String.Empty);
            serializedMessage.AddField(nameField);

            try
            {
                placeholderWorkout = WorkoutImporter.ImportWorkoutFromMessage(serializedMessage, logbook.ActivityCategories[0]);
                Assert.Fail("Workout without step count was FIT deserialized");
            }
            catch (FITParserException)
            {
            }

            numStepsField.SetUInt16(0);
            serializedMessage.AddField(numStepsField);

            // Name
            nameField.SetString("TestName");
            placeholderWorkout = WorkoutImporter.ImportWorkoutFromMessage(serializedMessage, logbook.ActivityCategories[0]);
            Assert.AreEqual("TestName", placeholderWorkout.Name, "Workout name not properly FIT deserialized");
            GarminWorkoutManager.Instance.RemoveWorkout(placeholderWorkout);

            nameField.SetString("Workout2");
            placeholderWorkout = WorkoutImporter.ImportWorkoutFromMessage(serializedMessage, logbook.ActivityCategories[0]);
            Assert.AreEqual("Workout2", placeholderWorkout.Name, "Workout name not properly FIT deserialized");
            GarminWorkoutManager.Instance.RemoveWorkout(placeholderWorkout);

            // No need to test category, we ignore them in the application at this time (TODO)

            // Number of steps is tested in the steps list test since it requires multiple messages

            // Do not test steps deserialization here, it has it's own test
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField lowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField highRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            ITarget loadedTarget = null;

            serializedMessage.AddField(typeField);

            // No target
            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.NoTarget);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized without value fields");

            serializedMessage.AddField(valueField);
            serializedMessage.AddField(lowRangeField);
            serializedMessage.AddField(highRangeField);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized with unset value fields");

            valueField.SetUInt32(1);
            lowRangeField.SetUInt32(1);
            highRangeField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized with value fields");

            // Cadence targets
            BaseCadenceTarget cadenceTarget;

            // Cadence range
            CadenceRangeTarget cadenceRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Cadence);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(80);
            highRangeField.SetUInt32(90);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(80, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(90, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(60);
            highRangeField.SetUInt32(120);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(60, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(120, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            // Speed targets
            BaseSpeedTarget speedTarget;

            // Speed range
            SpeedRangeTarget speedRangeTarget;
            double speed;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Speed);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(5555);
            highRangeField.SetUInt32(8333);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(20, speed, 0.01, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(30, speed, 0.01, "SpeedRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(1788);
            highRangeField.SetUInt32(3576);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(4, speed, 0.01, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(8, speed, 0.01, "SpeedRange max value wasn't properly deserialized");

            // Speed Garmin zone
            SpeedZoneGTCTarget speedGTCTarget;

            valueField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(1, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(3);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(3, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            // Heart rate targets
            BaseHeartRateTarget hrTarget;

            // Heart rate range
            HeartRateRangeTarget hrRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.HeartRate);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(230);
            highRangeField.SetUInt32(270);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(false, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(130, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(170, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(50);
            highRangeField.SetUInt32(70);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(true, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(50, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(70, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            // Heart rate Garmin zone
            HeartRateZoneGTCTarget hrGTCTarget;

            valueField.SetUInt32(2);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(2, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(4);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(4, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            // Power targets
            BasePowerTarget powerTarget;

            // Power range
            PowerRangeTarget powerRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Power);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(1150);
            highRangeField.SetUInt32(1200);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(false, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(150, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(67);
            highRangeField.SetUInt32(200);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(true, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(67, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            // Power Garmin zone
            PowerZoneGTCTarget powerGTCTarget;

            valueField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(1, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(7);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(7, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            IRepeatDuration deserializedDuration;

            serializedMessage.AddField(typeField);
            serializedMessage.AddField(valueField);

            // Repeat count
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatCount);
            valueField.SetUInt32(6);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatCountDuration, "Repeat count duration not properly FIT deserialized");
            RepeatCountDuration repeatDuration = deserializedDuration as RepeatCountDuration;
            Assert.AreEqual(6, repeatDuration.RepetitionCount, "Invalid value deserialized for FIT repeat count duration");

            valueField.SetUInt32(2);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatCountDuration, "Repeat count duration not properly FIT deserialized");
            repeatDuration = deserializedDuration as RepeatCountDuration;
            Assert.AreEqual(2, repeatDuration.RepetitionCount, "Invalid value deserialized for FIT repeat count duration");

            // Repeat until calories
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilCalories);
            valueField.SetUInt32(666);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilCaloriesDuration, "Repeat until calories duration not properly FIT deserialized");
            RepeatUntilCaloriesDuration caloriesDuration = deserializedDuration as RepeatUntilCaloriesDuration;
            Assert.AreEqual(666, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT repeat until calories duration");

            valueField.SetUInt32(150);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilCaloriesDuration, "Repeat until calories duration not properly FIT deserialized");
            caloriesDuration = deserializedDuration as RepeatUntilCaloriesDuration;
            Assert.AreEqual(150, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT repeat until calories duration");

            // Repeat until distance
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilDistance);
            valueField.SetUInt32(100000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilDistanceDuration, "Repeat until distance duration not properly FIT deserialized");
            RepeatUntilDistanceDuration distanceDuration = deserializedDuration as RepeatUntilDistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Kilometer),
                            STCommon.Data.Constants.Delta,
                            "Invalid value deserialized for FIT repeat until distance duration");

            valueField.SetUInt32(160934);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilDistanceDuration, "Repeat until distance duration not properly FIT deserialized");
            distanceDuration = deserializedDuration as RepeatUntilDistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Mile),
                            STCommon.Data.Constants.Delta,
                            "Invalid value deserialized for FIT repeat until distance duration");

            // Repeat until HR Above
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan);
            valueField.SetUInt32(220);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateAboveDuration, "Repeat until HRAbove duration not properly FIT deserialized");
            RepeatUntilHeartRateAboveDuration hrAboveDuration = deserializedDuration as RepeatUntilHeartRateAboveDuration;
            Assert.IsFalse(hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");
            Assert.AreEqual(120, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");

            valueField.SetUInt32(80);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateAboveDuration, "Repeat until HRAbove duration not properly FIT deserialized");
            hrAboveDuration = deserializedDuration as RepeatUntilHeartRateAboveDuration;
            Assert.IsTrue(hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");
            Assert.AreEqual(80, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");

            // Repeat until HR Below
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan);
            valueField.SetUInt32(230);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateBelowDuration, "Repeat until HRBelow duration not properly FIT deserialized");
            RepeatUntilHeartRateBelowDuration hrBelowDuration = deserializedDuration as RepeatUntilHeartRateBelowDuration;
            Assert.IsFalse(hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");
            Assert.AreEqual(130, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");

            valueField.SetUInt32(65);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateBelowDuration, "Repeat until HRBelow duration not properly FIT deserialized");
            hrBelowDuration = deserializedDuration as RepeatUntilHeartRateBelowDuration;
            Assert.IsTrue(hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");
            Assert.AreEqual(65, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");

            // Repeat until Power Above
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan);
            valueField.SetUInt32(1220);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerAboveDuration, "Repeat until PowerAbove duration not properly FIT deserialized");
            RepeatUntilPowerAboveDuration powerAboveDuration = deserializedDuration as RepeatUntilPowerAboveDuration;
            Assert.IsFalse(powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerAbove duration");
            Assert.AreEqual(220, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT repeat until PowerAbove duration");

            valueField.SetUInt32(80);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerAboveDuration, "Repeat until PowerAbove duration not properly FIT deserialized");
            powerAboveDuration = deserializedDuration as RepeatUntilPowerAboveDuration;
            Assert.IsTrue(powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerAbove duration");
            Assert.AreEqual(80, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT repeat until PowerAbove duration");

            // Repeat until Power Below
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan);
            valueField.SetUInt32(1400);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerBelowDuration, "Repeat until PowerBelow duration not properly FIT deserialized");
            RepeatUntilPowerBelowDuration powerBelowDuration = deserializedDuration as RepeatUntilPowerBelowDuration;
            Assert.IsFalse(powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerBelow duration");
            Assert.AreEqual(400, powerBelowDuration.MinPower, "Invalid value deserialized for FIT repeat until PowerBelow duration");

            valueField.SetUInt32(125);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerBelowDuration, "Repeat until PowerBelow duration not properly FIT deserialized");
            powerBelowDuration = deserializedDuration as RepeatUntilPowerBelowDuration;
            Assert.IsTrue(powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerBelow duration");
            Assert.AreEqual(125, powerBelowDuration.MinPower, "Invalid value deserialized for FIT repeat until PowerBelow duration");

            // Repeat until time
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilTime);
            valueField.SetUInt32(60000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilTimeDuration, "Repeat until time duration not properly FIT deserialized");
            RepeatUntilTimeDuration timeDuration = deserializedDuration as RepeatUntilTimeDuration;
            Assert.AreEqual(60, timeDuration.TimeInSeconds, "Invalid value deserialized for FIT repeat until time duration");

            valueField.SetUInt32(600000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilTimeDuration, "Repeat until time duration not properly FIT deserialized");
            timeDuration = deserializedDuration as RepeatUntilTimeDuration;
            Assert.AreEqual(600, timeDuration.TimeInSeconds, "Invalid value deserialized for FIT repeat until time duration");
        }
        public void TestRepeatStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep regularStep;
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);

            regularStep = placeholderWorkout.Steps[0] as RegularStep;

            // Setup message
            serializedMessage.AddField(durationValueField);
            serializedMessage.AddField(durationTypeField);
            serializedMessage.AddField(targetValueField);
            durationValueField.SetUInt32(0); // Step id to start repeat at
            durationTypeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatCount);
            targetValueField.SetUInt32(2);

            // This test is a little hard to follow because repeat steps in FIT format regroup previously deserialized steps.
            //  We will illustrate the expected output throughout the test to ease validation.

            // At this point we have the following
            // - Root
            //  - Regular step (id = 0)
            // By deserializing the message we should get the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(1, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with single child");
            Assert.AreEqual(2, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with single child");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with single child");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[0], "Invalid workout structure after FIT deserialization of repeat step with single child");
            Assert.AreEqual(1, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with single child");
            Assert.AreEqual(regularStep, repeatStep.StepsToRepeat[0], "Invalid child in repeat after FIT deserialization of repeat step with single child");

            placeholderWorkout.Steps.AddStepToRoot(new RegularStep(placeholderWorkout));
            placeholderWorkout.Steps.AddStepToRoot(new RegularStep(placeholderWorkout));
            repeatStep = new RepeatStep(placeholderWorkout);
            durationValueField.SetUInt32(2); // Step id to start repeat at

            // At this point we have the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            //  - Regular step (id = 2)
            //  - Regular step (id = 3)
            // By deserializing the message we should get the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            //  - Repeat step (id = 4)
            //   - Regular step (id = 2)
            //   - Regular step (id = 3)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(2, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(5, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[1], "Invalid workout structure after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple children");

            regularStep = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.InsertStepBeforeStep(regularStep, placeholderWorkout.Steps[0]);
            repeatStep = new RepeatStep(placeholderWorkout);
            durationValueField.SetUInt32(1); // Step id to start repeat at

            // At this point we have the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 2)
            //   - Regular step (id = 1)
            //  - Repeat step (id = 5)
            //   - Regular step (id = 4)
            //   - Regular step (id = 5)
            //  - Repeat step (not deserialized yet)
            // By deserializing the message we should get the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 6)
            //   - Repeat step (id = 2)
            //    - Regular step (id = 1)
            //   - Repeat step (id = 5)
            //    - Regular step (id = 3)
            //    - Regular step (id = 4)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(2, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(7, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RegularStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(regularStep, placeholderWorkout.Steps[0], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[1], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[1] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            IDuration deserializedDuration;

            serializedMessage.AddField(typeField);

            // Lap button
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Open);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is LapButtonDuration, "Lap button duration not properly FIT deserialized without value field");

            serializedMessage.AddField(valueField);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is LapButtonDuration, "Lap button duration not properly FIT deserialized with unset value field");

            valueField.SetUInt32(1);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is LapButtonDuration, "Lap button duration not properly FIT deserialized with value field");

            // Time duration
            TimeDuration timeDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Time);
            valueField.SetUInt32(60000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is TimeDuration, "Time duration not properly FIT deserialized");
            timeDuration = deserializedDuration as TimeDuration;
            Assert.AreEqual(60, timeDuration.TimeInSeconds, "Invalid value deserialized for FIT time duration");

            valueField.SetUInt32(3600000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is TimeDuration, "Time duration not properly FIT deserialized");
            timeDuration = deserializedDuration as TimeDuration;
            Assert.AreEqual(1, timeDuration.Hours, "Invalid value deserialized for FIT time duration");
            Assert.AreEqual(0, timeDuration.Minutes, "Invalid value deserialized for FIT time duration");
            Assert.AreEqual(0, timeDuration.Seconds, "Invalid value deserialized for FIT time duration");

            // Distance duration
            DistanceDuration distanceDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Distance);
            valueField.SetUInt32(60000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is DistanceDuration, "Distance duration not properly FIT deserialized");
            distanceDuration = deserializedDuration as DistanceDuration;
            Assert.AreEqual(0.6,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Kilometer),
                            "Invalid value deserialized for FIT distance duration");

            valueField.SetUInt32(160934);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is DistanceDuration, "Distance duration not properly FIT deserialized");
            distanceDuration = deserializedDuration as DistanceDuration;
            Assert.AreEqual(1.0,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Mile),
                            STCommon.Data.Constants.Delta,
                            "Invalid value deserialized for FIT distance duration");

            // Calories duration
            CaloriesDuration caloriesDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Calories);
            valueField.SetUInt32(456);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is CaloriesDuration, "Calories duration not properly FIT deserialized");
            caloriesDuration = deserializedDuration as CaloriesDuration;
            Assert.AreEqual(456, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT calories duration");

            valueField.SetUInt32(100);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is CaloriesDuration, "Calories duration not properly FIT deserialized");
            caloriesDuration = deserializedDuration as CaloriesDuration;
            Assert.AreEqual(100, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT calories duration");

            // HR Above
            HeartRateAboveDuration hrAboveDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.HeartRateGreaterThan);
            valueField.SetUInt32(256);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is HeartRateAboveDuration, "HRAbove duration not properly FIT deserialized");
            hrAboveDuration = deserializedDuration as HeartRateAboveDuration;
            Assert.AreEqual(false, hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT HRAbove duration");
            Assert.AreEqual(156, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT HRAbove duration");

            valueField.SetUInt32(88);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is HeartRateAboveDuration, "HRAbove duration not properly FIT deserialized");
            hrAboveDuration = deserializedDuration as HeartRateAboveDuration;
            Assert.AreEqual(true, hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT HRAbove duration");
            Assert.AreEqual(88, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT HRAbove duration");

            // HR Below
            HeartRateBelowDuration hrBelowDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.HeartRateLessThan);
            valueField.SetUInt32(270);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is HeartRateBelowDuration, "HRBelow duration not properly FIT deserialized");
            hrBelowDuration = deserializedDuration as HeartRateBelowDuration;
            Assert.AreEqual(false, hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT HRBelow duration");
            Assert.AreEqual(170, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT HRBelow duration");

            valueField.SetUInt32(70);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is HeartRateBelowDuration, "HRBelow duration not properly FIT deserialized");
            hrBelowDuration = deserializedDuration as HeartRateBelowDuration;
            Assert.AreEqual(true, hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT HRBelow duration");
            Assert.AreEqual(70, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT HRBelow duration");

            // Power Above
            PowerAboveDuration powerAboveDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.PowerGreaterThan);
            valueField.SetUInt32(1256);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is PowerAboveDuration, "PowerAbove duration not properly FIT deserialized");
            powerAboveDuration = deserializedDuration as PowerAboveDuration;
            Assert.AreEqual(false, powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT PowerAbove duration");
            Assert.AreEqual(256, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT PowerAbove duration");

            valueField.SetUInt32(185);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is PowerAboveDuration, "PowerAbove duration not properly FIT deserialized");
            powerAboveDuration = deserializedDuration as PowerAboveDuration;
            Assert.AreEqual(true, powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT PowerAbove duration");
            Assert.AreEqual(185, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT PowerAbove duration");

            // Power Below
            PowerBelowDuration powerBelowDuration;

            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.PowerLessThan);
            valueField.SetUInt32(1300);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is PowerBelowDuration, "PowerBelow duration not properly FIT deserialized");
            powerBelowDuration = deserializedDuration as PowerBelowDuration;
            Assert.AreEqual(false, powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT PowerBelow duration");
            Assert.AreEqual(300, powerBelowDuration.MinPower, "Invalid value deserialized for FIT PowerBelow duration");

            valueField.SetUInt32(55);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is PowerBelowDuration, "PowerBelow duration not properly FIT deserialized");
            powerBelowDuration = deserializedDuration as PowerBelowDuration;
            Assert.AreEqual(true, powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT PowerBelow duration");
            Assert.AreEqual(55, powerBelowDuration.MinPower, "Invalid value deserialized for FIT PowerBelow duration");
        }
Esempio n. 8
0
        public void FillFITMessage(FITMessage workoutMessage)
        {
            FITMessageField capabilities = new FITMessageField((Byte)FITWorkoutFieldIds.Capabilities);
            FITMessageField sportType = new FITMessageField((Byte)FITWorkoutFieldIds.SportType);
            FITMessageField numValidSteps = new FITMessageField((Byte)FITWorkoutFieldIds.NumSteps);
            FITMessageField unknown = new FITMessageField((Byte)FITWorkoutFieldIds.Unknown);
            FITMessageField workoutName = new FITMessageField((Byte)FITWorkoutFieldIds.WorkoutName);

            sportType.SetEnum((Byte)Options.Instance.GetFITSport(Category));
            capabilities.SetUInt32z(32);
            numValidSteps.SetUInt16(StepCount);
            unknown.SetUInt16(0xFFFF);

            workoutMessage.AddField(capabilities);
            if (!String.IsNullOrEmpty(Name))
            {
                workoutName.SetString(Name, (Byte)(Constants.MaxNameLength + 1));
                workoutMessage.AddField(workoutName);
            }
            workoutMessage.AddField(numValidSteps);
            workoutMessage.AddField(unknown);
            workoutMessage.AddField(sportType);
        }
Esempio n. 9
0
        public void SerializeToFITSettings(Stream outputStream)
        {
            FITMessage userProfileMessage = new FITMessage(FITGlobalMessageIds.UserProfile);
            FITMessageField profileName = new FITMessageField((Byte)FITUserProfileFieldIds.FriendlyName);
            FITMessageField gender = new FITMessageField((Byte)FITUserProfileFieldIds.Gender);
            FITMessageField weight = new FITMessageField((Byte)FITUserProfileFieldIds.Weight);
            FITMessageField restingHR = new FITMessageField((Byte)FITUserProfileFieldIds.RestingHR);

            profileName.SetString(ProfileName, (Byte)(Constants.MaxNameLength + 1));
            userProfileMessage.AddField(profileName);

            gender.SetEnum(IsMale ? (Byte)FITGenders.Male : (Byte)FITGenders.Female);
            userProfileMessage.AddField(gender);

            weight.SetUInt16((UInt16)(WeightInKilos * 10));
            userProfileMessage.AddField(weight);

            restingHR.SetUInt8(RestingHeartRate);
            userProfileMessage.AddField(restingHR);

            userProfileMessage.Serialize(outputStream);

            (m_ActivityProfiles[(int)GarminCategories.Biking] as GarminBikingActivityProfile).SerializeBikeProfiles(outputStream);
        }
        public void SerializeToFITSport(Stream outputStream)
        {
            FITMessage sportMessage = new FITMessage(FITGlobalMessageIds.Sport);
            FITMessageField sport = new FITMessageField((Byte)FITSportFieldIds.Sport);
            FITMessageField subSport = new FITMessageField((Byte)FITSportFieldIds.SubSport);

            sport.SetEnum((Byte)Utils.GetFITSport(Category));
            subSport.SetEnum(0);

            sportMessage.AddField(sport);
            sportMessage.AddField(subSport);
            sportMessage.Serialize(outputStream);

            SerializeFITZonesTarget(outputStream);
            SerializeFITHRZones(outputStream);
            SerializeFITSpeedZones(outputStream);
            SerializeFITPowerZones(outputStream);
        }
        public virtual void SerializeFITZonesTarget(Stream outputStream)
        {
            FITMessage zonesTargetMessage = new FITMessage(FITGlobalMessageIds.ZonesTarget);
            FITMessageField maxHR = new FITMessageField((Byte)FITZonesTargetFieldIds.MaxHR);
            FITMessageField FTP = new FITMessageField((Byte)FITZonesTargetFieldIds.FTP);
            FITMessageField HRCalcType = new FITMessageField((Byte)FITZonesTargetFieldIds.HRCalcType);
            FITMessageField powerCalcType = new FITMessageField((Byte)FITZonesTargetFieldIds.PowerCalcType);

            maxHR.SetUInt8(MaximumHeartRate);
            FTP.SetUInt16(0);
            HRCalcType.SetEnum((Byte)FITHRCalcTypes.Custom);
            powerCalcType.SetEnum((Byte)FITPowerCalcTypes.Custom);

            zonesTargetMessage.AddField(maxHR);
            zonesTargetMessage.AddField(FTP);
            zonesTargetMessage.AddField(HRCalcType);
            zonesTargetMessage.AddField(powerCalcType);

            zonesTargetMessage.Serialize(outputStream);
        }
        public virtual void SerializeFITSpeedZones(Stream outputStream)
        {
            UInt16 i = 0;

            foreach (GarminFitnessNamedSpeedZone zone in m_SpeedZones)
            {
                FITMessage zonesTargetMessage = new FITMessage(FITGlobalMessageIds.SpeedZones);
                FITMessageField index = new FITMessageField((Byte)FITSpeedZonesFieldIds.MessageIndex);
                FITMessageField zoneName = new FITMessageField((Byte)FITSpeedZonesFieldIds.Name);
                FITMessageField highSpeed = new FITMessageField((Byte)FITSpeedZonesFieldIds.HighSpeed);

                if (i == 0)
                {
                    index.SetUInt16(i);
                    zoneName.SetString(zone.Name, (Byte)(Constants.MaxNameLength + 1));
                    highSpeed.SetUInt16((UInt16)(zone.Low * 1000));

                    zonesTargetMessage.AddField(index);
                    zonesTargetMessage.AddField(zoneName);
                    zonesTargetMessage.AddField(highSpeed);

                    zonesTargetMessage.Serialize(outputStream);
                    zonesTargetMessage.Clear();
                }

                index.SetUInt16((UInt16)(i + 1));
                zoneName.SetString(zone.Name, (Byte)(Constants.MaxNameLength + 1));
                highSpeed.SetUInt16((UInt16)(zone.High * 1000));

                zonesTargetMessage.AddField(index);
                zonesTargetMessage.AddField(zoneName);
                zonesTargetMessage.AddField(highSpeed);

                zonesTargetMessage.Serialize(outputStream, false);

                ++i;
            }
        }
        public virtual void SerializeFITHRZones(Stream outputStream)
        {
            UInt16 i = 0;

            foreach (GarminFitnessValueRange<GarminFitnessDoubleRange> zone in m_HeartRateZones)
            {
                FITMessage zonesTargetMessage = new FITMessage(FITGlobalMessageIds.HRZones);
                FITMessageField index = new FITMessageField((Byte)FITHRZonesFieldIds.MessageIndex);
                FITMessageField zoneName = new FITMessageField((Byte)FITHRZonesFieldIds.Name);
                FITMessageField highBPM = new FITMessageField((Byte)FITHRZonesFieldIds.HighBPM);

                if (i == 0)
                {
                    index.SetUInt16(i);
                    zoneName.SetString(String.Format("HR Zone {0}", i), (Byte)(Constants.MaxNameLength + 1));
                    highBPM.SetUInt8((Byte)(zone.Lower * MaximumHeartRate));

                    zonesTargetMessage.AddField(index);
                    zonesTargetMessage.AddField(zoneName);
                    zonesTargetMessage.AddField(highBPM);

                    zonesTargetMessage.Serialize(outputStream);
                    zonesTargetMessage.Clear();
                }

                index.SetUInt16((UInt16)(i + 1));
                zoneName.SetString(String.Format("HR Zone {0}", i + 1), (Byte)(Constants.MaxNameLength + 1));
                highBPM.SetUInt8((Byte)(zone.Upper * MaximumHeartRate));

                zonesTargetMessage.AddField(index);
                zonesTargetMessage.AddField(zoneName);
                zonesTargetMessage.AddField(highBPM);

                zonesTargetMessage.Serialize(outputStream, false);

                ++i;
            }
        }
Esempio n. 14
0
        private void ProcessMessageDefinition()
        {
            if (m_DataStream != null)
            {
                FITMessage definition = null;
                Byte[] temp;
                UInt16 globalMessageId = 0;
                Byte recordHeader = 0;
                Byte localDefinitionId = 0;
                Byte endianness = 0;
                Byte fieldCount = 0;
                bool isLittleEndian = true;

                // Record header
                recordHeader = (Byte)m_DataStream.ReadByte();
                Debug.Assert(IsDefinitionHeaderByte(recordHeader));
                localDefinitionId = (Byte)(recordHeader & 0xF);

                // Skip reserved byte
                m_DataStream.ReadByte();

                // Architecture byte
                endianness = (Byte)m_DataStream.ReadByte();
                isLittleEndian = (FITEndianness)endianness == FITEndianness.LittleEndian;

                // Global message id
                temp = new Byte[sizeof(UInt16)];
                m_DataStream.Read(temp, 0, 2);

                if (!isLittleEndian)
                {
                    Byte swapHolder = temp[0];

                    temp[0] = temp[1];
                    temp[1] = swapHolder;
                }
                globalMessageId = BitConverter.ToUInt16(temp, 0);

                // Field count
                fieldCount = (Byte)m_DataStream.ReadByte();

                try
                {
                    definition = new FITMessage((FITGlobalMessageIds)globalMessageId, isLittleEndian);
                }
                catch (Exception e)
                {
                    // Unsuported message type
                    throw e;
                }

                // Read fields
                for (int i = 0; i < fieldCount; ++i)
                {
                    Byte fieldId = 0;
                    Byte fieldSize = 0;
                    Byte fieldType = 0;

                    fieldId = (Byte)m_DataStream.ReadByte();
                    fieldSize = (Byte)m_DataStream.ReadByte();
                    fieldType = (Byte)m_DataStream.ReadByte();

                    definition.AddField(new FITMessageField(fieldId, fieldType, fieldSize));
                }

                // We might overwrite an old message, this is valid
                m_MessageDefinitions[localDefinitionId] = definition;
            }
        }
Esempio n. 15
0
        public static void ExportWorkoutToFIT(IWorkout workout, Stream exportStream, bool updateExportDate)
        {
            MemoryStream dataStream = new MemoryStream();

            // Reserve size for header
            dataStream.Write(new Byte[12], 0, 12);

            // File id message
            FITMessage fileIdMessage = new FITMessage(FITGlobalMessageIds.FileId);
            FITMessageField fileType = new FITMessageField((Byte)FITFileIdFieldsIds.FileType);
            FITMessageField manufacturerId = new FITMessageField((Byte)FITFileIdFieldsIds.ManufacturerId);
            FITMessageField productId = new FITMessageField((Byte)FITFileIdFieldsIds.ProductId);
            FITMessageField serialNumber = new FITMessageField((Byte)FITFileIdFieldsIds.SerialNumber);
            FITMessageField exportDate = new FITMessageField((Byte)FITFileIdFieldsIds.ExportDate);
            FITMessageField number = new FITMessageField((Byte)FITFileIdFieldsIds.Number);

            fileType.SetEnum((Byte)FITFileTypes.Workout);
            manufacturerId.SetUInt16(1);
            productId.SetUInt16(20119);
            serialNumber.SetUInt32z(0);
            exportDate.SetUInt32(workout.CreationTimestamp);
            number.SetUInt16(0xFFFF);

            fileIdMessage.AddField(serialNumber);
            fileIdMessage.AddField(exportDate);
            fileIdMessage.AddField(manufacturerId);
            fileIdMessage.AddField(productId);
            fileIdMessage.AddField(number);
            fileIdMessage.AddField(fileType);
            fileIdMessage.Serialize(dataStream);

            // File creator message
            FITMessage fileCreatorMessage = new FITMessage(FITGlobalMessageIds.FileCreator);
            FITMessageField software = new FITMessageField((Byte)FITFileCreatorFieldsIds.SoftwareVersion);
            FITMessageField hardware = new FITMessageField((Byte)FITFileCreatorFieldsIds.HardwareVersion);

            software.SetUInt16(3605);
            hardware.SetUInt8(0);

            fileCreatorMessage.AddField(software);
            fileCreatorMessage.AddField(hardware);
            fileCreatorMessage.Serialize(dataStream);

            // Write workout
            workout.SerializeToFIT(dataStream);

            // Write FIT header at the start of the stream
            GarminFitnessByteRange headerSize = new GarminFitnessByteRange(12);
            GarminFitnessByteRange protocolVersion = new GarminFitnessByteRange((Byte)((FITConstants.FITProtocolMajorVersion << 4) | FITConstants.FITProtocolMinorVersion));
            GarminFitnessUInt16Range profileVersion = new GarminFitnessUInt16Range((UInt16)((FITConstants.FITProfileMajorVersion * FITConstants.FITProfileMajorVersionMultiplier) + FITConstants.FITProfileMinorVersion));
            GarminFitnessInt32Range dataSize = new GarminFitnessInt32Range(0);

            dataStream.Seek(0, SeekOrigin.Begin);
            dataSize.Value = (int)dataStream.Length - 12;

            headerSize.Serialize(dataStream);
            protocolVersion.Serialize(dataStream);
            profileVersion.Serialize(dataStream);
            dataSize.Serialize(dataStream);
            dataStream.Write(Encoding.UTF8.GetBytes(FITConstants.FITFileDescriptor), 0, 4);

            // Write CRC
            GarminFitnessUInt16Range crc = new GarminFitnessUInt16Range(FITUtils.ComputeStreamCRC(dataStream));
            dataStream.Seek(0, SeekOrigin.End);
            crc.Serialize(dataStream);

            // Write all data to output stream
            exportStream.Write(dataStream.GetBuffer(), 0, (int)dataStream.Length);

            if (updateExportDate)
            {
                workout.LastExportDate = DateTime.Now;
            }
        }
Esempio n. 16
0
        public virtual void SerializeToFIT(Stream stream, bool serializeDefiniton)
        {
            FITMessage message = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField stepId = new FITMessageField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            FITMessageField durationType = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField durationValue = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField intensity = new FITMessageField((Byte)FITWorkoutStepFieldIds.Intensity);
            FITMessageField stepName = new FITMessageField((Byte)FITWorkoutStepFieldIds.StepName);
            FITMessageField targetHigh = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            FITMessageField targetLow = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField targetType = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField targetValue = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);

            // Fill in invalid data
            durationType.SetEnum((Byte)0xFF);
            durationValue.SetUInt32(0xFFFFFFFF);
            intensity.SetEnum((Byte)0xFF);
            stepName.SetString("", (Byte)(Constants.MaxNameLength + 1));
            targetHigh.SetUInt32(0xFFFFFFFF);
            targetLow.SetUInt32(0xFFFFFFFF);
            targetType.SetEnum((Byte)0xFF);
            targetValue.SetUInt32(0xFFFFFFFF);

            message.AddField(stepName);
            message.AddField(durationValue);
            message.AddField(targetValue);
            message.AddField(targetLow);
            message.AddField(targetHigh);
            message.AddField(stepId);
            message.AddField(durationType);
            message.AddField(targetType);
            message.AddField(intensity);

            FillFITStepMessage(message);

            message.Serialize(stream, serializeDefiniton);
        }
        public void TestRegularStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField nameField = new FITMessageField((Byte)FITWorkoutStepFieldIds.StepName);
            FITMessageField intensityField = new FITMessageField((Byte)FITWorkoutStepFieldIds.Intensity);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField targetTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField targetLowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField targetHighRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);

            // Setup message
            serializedMessage.AddField(nameField);
            serializedMessage.AddField(intensityField);
            serializedMessage.AddField(durationTypeField);
            serializedMessage.AddField(durationValueField);
            serializedMessage.AddField(targetTypeField);
            serializedMessage.AddField(targetValueField);
            serializedMessage.AddField(targetLowRangeField);
            serializedMessage.AddField(targetHighRangeField);
            durationTypeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Calories);
            durationValueField.SetUInt32(456);
            targetTypeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Cadence);
            targetValueField.SetUInt32(0);
            targetLowRangeField.SetUInt32(80);
            targetHighRangeField.SetUInt32(90);

            // Active step
            nameField.SetString("TestStep1");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Active);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep1", placeholderStep.Name, "Name not properly FIT deserialized for active step");
            Assert.AreEqual(RegularStep.StepIntensity.Active, placeholderStep.Intensity, "Intensity not properly FIT deserialized for active step");

            // Rest step
            nameField.SetString("TestStep2");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Rest);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep2", placeholderStep.Name, "Name not properly FIT deserialized for rest step");
            Assert.AreEqual(RegularStep.StepIntensity.Rest, placeholderStep.Intensity, "Intensity not properly FIT deserialized for rest step");

            // Warmup step
            nameField.SetString("TestStep3");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Warmup);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep3", placeholderStep.Name, "Name not properly FIT deserialized for warmup step");
            Assert.AreEqual(RegularStep.StepIntensity.Warmup, placeholderStep.Intensity, "Intensity not properly FIT deserialized for warmup step");

            // Cooldown step
            nameField.SetString("TestStep4");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Cooldown);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep4", placeholderStep.Name, "Name not properly FIT deserialized for cooldown step");
            Assert.AreEqual(RegularStep.StepIntensity.Cooldown, placeholderStep.Intensity, "Intensity not properly FIT deserialized for cooldown step");

            // Duration & target test
            Assert.IsTrue(placeholderStep.Duration is CaloriesDuration, "Duration not properly FIT deserialized in regular step");
            Assert.IsTrue(placeholderStep.Target is BaseCadenceTarget, "Target not properly FIT deserialized in regular step");
        }
Esempio n. 18
0
        public virtual void FillFITMessageForScheduledDate(FITMessage scheduleMessage, DateTime scheduledDate)
        {
            FITMessageField workoutManufacturer = new FITMessageField((Byte)FITScheduleFieldIds.WorkoutManufacturer);
            FITMessageField workoutProduct = new FITMessageField((Byte)FITScheduleFieldIds.WorkoutProduct);
            FITMessageField workoutSN = new FITMessageField((Byte)FITScheduleFieldIds.WorkoutSN);
            FITMessageField scheduleType = new FITMessageField((Byte)FITScheduleFieldIds.ScheduleType);
            FITMessageField workoutCompleted = new FITMessageField((Byte)FITScheduleFieldIds.WorkoutCompleted);
            FITMessageField workoutId = new FITMessageField((Byte)FITScheduleFieldIds.WorkoutId);
            FITMessageField scheduledField = new FITMessageField((Byte)FITScheduleFieldIds.ScheduledDate);
            DateTime midDaySchedule = new DateTime(scheduledDate.Date.Year, scheduledDate.Date.Month, scheduledDate.Day, 12, 0, 0);
            TimeSpan timeSinceReference = midDaySchedule - new DateTime(1989, 12, 31);

            // Hardcoded fields from the schedule file
            workoutManufacturer.SetUInt16(1);           // Always 1
            workoutProduct.SetUInt16(20119);            // Always 20119
            workoutSN.SetUInt32z(0);                    // Invalid
            workoutCompleted.SetEnum((Byte)0xFF);       // Invalid/Not completed

            // Real data
            scheduleType.SetEnum((Byte)FITScheduleType.Workout);
            workoutId.SetUInt32(CreationTimestamp);

            scheduleMessage.AddField(workoutSN);
            scheduleMessage.AddField(workoutId);
            scheduleMessage.AddField(scheduledField);
            scheduleMessage.AddField(workoutManufacturer);
            scheduleMessage.AddField(workoutProduct);
            scheduleMessage.AddField(workoutCompleted);
            scheduleMessage.AddField(scheduleType);

            scheduledField.SetUInt32((UInt32)timeSinceReference.TotalSeconds);
        }
        public void SerializeBikeProfiles(Stream outputStream)
        {
            UInt16 i = 0;

            foreach (GarminBikeProfile bike in m_Bikes)
            {
                FITMessage bikeProfileMessage = new FITMessage(FITGlobalMessageIds.BikeProfile);
                FITMessageField index = new FITMessageField((Byte)FITBikeProfileFieldIds.MessageIndex);
                FITMessageField name = new FITMessageField((Byte)FITBikeProfileFieldIds.Name);
                FITMessageField odometer = new FITMessageField((Byte)FITBikeProfileFieldIds.Odometer);
                FITMessageField customWheelSize = new FITMessageField((Byte)FITBikeProfileFieldIds.CustomWheelSize);
                FITMessageField autoWheelSize = new FITMessageField((Byte)FITBikeProfileFieldIds.AutoWheelSize);
                FITMessageField weight = new FITMessageField((Byte)FITBikeProfileFieldIds.Weight);
                FITMessageField useAutoWheelSize = new FITMessageField((Byte)FITBikeProfileFieldIds.AutoWheelSetting);
                FITMessageField cadenceSensor = new FITMessageField((Byte)FITBikeProfileFieldIds.SpeedCadenceSensorEnabled);
                FITMessageField powerSensor = new FITMessageField((Byte)FITBikeProfileFieldIds.PowerSensorEnabled);

                index.SetUInt16(i);
                name.SetString(bike.Name, (Byte)(Constants.MaxNameLength + 1));
                odometer.SetUInt32((UInt32)(bike.OdometerInMeters * 100));
                customWheelSize.SetUInt16(bike.WheelSize);
                autoWheelSize.SetUInt16(bike.WheelSize);
                weight.SetUInt16((UInt16)Math.Round(Weight.Convert(bike.WeightInPounds, Weight.Units.Pound, Weight.Units.Kilogram) * 10, 0));
                useAutoWheelSize.SetEnum(bike.AutoWheelSize ? (Byte)FITBoolean.True : (Byte)FITBoolean.False);
                cadenceSensor.SetEnum(bike.HasCadenceSensor ? (Byte)FITBoolean.True : (Byte)FITBoolean.False);
                powerSensor.SetEnum(bike.HasPowerSensor ? (Byte)FITBoolean.True : (Byte)FITBoolean.False);

                bikeProfileMessage.AddField(index);
                bikeProfileMessage.AddField(name);
                bikeProfileMessage.AddField(odometer);
                bikeProfileMessage.AddField(customWheelSize);
                bikeProfileMessage.AddField(autoWheelSize);
                bikeProfileMessage.AddField(weight);
                bikeProfileMessage.AddField(useAutoWheelSize);
                bikeProfileMessage.AddField(cadenceSensor);
                bikeProfileMessage.AddField(powerSensor);

                bikeProfileMessage.Serialize(outputStream, i == 0);

                ++i;
            }
        }