public void TestRegularStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = placeholderWorkout.Steps[0] as RegularStep;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Active step
            placeholderStep.Name = "TestStep1";
            placeholderStep.Intensity = RegularStep.StepIntensity.Active;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for regular step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep1", messageField.GetString(), "Invalid name in field for active step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Active, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for active step");
            serializedMessage.Clear();

            // Rest step
            placeholderStep.Name = "TestStep2";
            placeholderStep.Intensity = RegularStep.StepIntensity.Rest;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep2", messageField.GetString(), "Invalid name in field for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Rest, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for rest step");
            serializedMessage.Clear();

            // Warmup step
            placeholderStep = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);
            placeholderStep.Name = "TestStep3";
            placeholderStep.Intensity = RegularStep.StepIntensity.Warmup;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep3", messageField.GetString(), "Invalid name in field for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Warmup, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for warmup step");
            serializedMessage.Clear();

            // Cooldown step
            placeholderStep.Name = "TestStep4";
            placeholderStep.Intensity = RegularStep.StepIntensity.Cooldown;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for cooldwon step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep4", messageField.GetString(), "Invalid name in field for cooldown step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Cooldown, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for cooldown step");
            serializedMessage.Clear();
        }
        public void TestRepeatStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            placeholderWorkout.Steps.RemoveStep(placeholderWorkout.Steps[0]);

            // Single child
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Multiple children
            // - Root
            //  - Repeat step (id = 2)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            repeatStep.StepsToRepeat.Add(new RegularStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeat steps
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            repeatStep.StepsToRepeat.Add(new RepeatStep(placeholderWorkout));
            repeatStep.StepsToRepeat[2].FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(2, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeats with 2nd repeat as first nested step
            // - Root
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            //  - Repeat step (id = 8)
            //   - Repeat step (id = 6)
            //    - Regular step (id = 5)
            //   - Regular step (id = 7)
            repeatStep = new RepeatStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            repeatStep.StepsToRepeat.Insert(0, new RepeatStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(5, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();
        }
        public static IDuration Create(FITMessage stepMessage, RegularStep parent)
        {
            FITMessageField durationTypeField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField durationField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            IDuration newDuration = new LapButtonDuration(parent);

            if (durationTypeField == null)
            {
                throw new FITParserException("Missing duration type field");
            }
            else if ((FITWorkoutStepDurationTypes)durationTypeField.GetEnum() != FITWorkoutStepDurationTypes.Open &&
                     durationField == null)
            {
                throw new FITParserException("Missing duration value field");
            }
            else
            {
                switch ((FITWorkoutStepDurationTypes)durationTypeField.GetEnum())
                {
                    case FITWorkoutStepDurationTypes.Time:
                        {
                            TimeDuration timeDuration = (TimeDuration)Create(IDuration.DurationType.Time, parent);
                            timeDuration.TimeInSeconds = (UInt16)(durationField.GetUInt32() / 1000.0f);
                            newDuration = timeDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.Distance:
                        {
                            DistanceDuration distanceDuration = (DistanceDuration)Create(IDuration.DurationType.Distance, parent);
                            distanceDuration.SetDistanceInBaseUnit(Length.Convert(durationField.GetUInt32(), Length.Units.Centimeter, distanceDuration.BaseUnit));
                            newDuration = distanceDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.HeartRateGreaterThan:
                        {
                            HeartRateAboveDuration hrDuration = (HeartRateAboveDuration)Create(IDuration.DurationType.HeartRateAbove, parent);
                            UInt32 hrValue = durationField.GetUInt32();

                            if (hrValue >= 100)
                            {
                                hrDuration.IsPercentageMaxHeartRate = false;
                                hrDuration.MaxHeartRate = (Byte)(hrValue - 100);
                            }
                            else
                            {
                                hrDuration.IsPercentageMaxHeartRate = true;
                                hrDuration.MaxHeartRate = (Byte)hrValue;
                            }
                            newDuration = hrDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.HeartRateLessThan:
                        {
                            HeartRateBelowDuration hrDuration = (HeartRateBelowDuration)Create(IDuration.DurationType.HeartRateBelow, parent);
                            UInt32 hrValue = durationField.GetUInt32();

                            if (hrValue >= 100)
                            {
                                hrDuration.IsPercentageMaxHeartRate = false;
                                hrDuration.MinHeartRate = (Byte)(hrValue - 100);
                            }
                            else
                            {
                                hrDuration.IsPercentageMaxHeartRate = true;
                                hrDuration.MinHeartRate = (Byte)hrValue;
                            }
                            newDuration = hrDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.Calories:
                        {
                            CaloriesDuration caloriesDuration = (CaloriesDuration)Create(IDuration.DurationType.Calories, parent);
                            caloriesDuration.CaloriesToSpend = (UInt16)durationField.GetUInt32();
                            newDuration = caloriesDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.PowerGreaterThan:
                        {
                            PowerAboveDuration powerDuration = (PowerAboveDuration)Create(IDuration.DurationType.PowerAbove, parent);
                            UInt32 powerValue = durationField.GetUInt32();

                            if (powerValue >= 1000)
                            {
                                powerDuration.IsPercentFTP = false;
                                powerDuration.MaxPower = (UInt16)(powerValue - 1000);
                            }
                            else
                            {
                                powerDuration.IsPercentFTP = true;
                                powerDuration.MaxPower = (UInt16)powerValue;
                            }
                            newDuration = powerDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.PowerLessThan:
                        {
                            PowerBelowDuration powerDuration = (PowerBelowDuration)Create(IDuration.DurationType.PowerBelow, parent);
                            UInt32 powerValue = durationField.GetUInt32();

                            if (powerValue >= 1000)
                            {
                                powerDuration.IsPercentFTP = false;
                                powerDuration.MinPower = (UInt16)(powerValue - 1000);
                            }
                            else
                            {
                                powerDuration.IsPercentFTP = true;
                                powerDuration.MinPower = (UInt16)powerValue;
                            }
                            newDuration = powerDuration;
                            break;
                        }
                }
            }

            parent.Duration = newDuration;

            return newDuration;
        }
        public static IRepeatDuration Create(FITMessage stepMessage, RepeatStep parent)
        {
            FITMessageField durationTypeField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField targetField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            IRepeatDuration newDuration = new RepeatCountDuration(parent);

            if (durationTypeField == null)
            {
                throw new FITParserException("Missing repeat duration type field");
            }
            else if (targetField == null)
            {
                throw new FITParserException("Missing repeat target value field");
            }
            else
            {
                switch ((FITWorkoutStepDurationTypes)durationTypeField.GetEnum())
                {
                    case FITWorkoutStepDurationTypes.RepeatCount:
                        {
                            RepeatCountDuration repeatDuration = (RepeatCountDuration)Create(IRepeatDuration.RepeatDurationType.RepeatCount, parent);
                            repeatDuration.RepetitionCount = (Byte)targetField.GetUInt32();
                            newDuration = repeatDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilTime:
                        {
                            RepeatUntilTimeDuration timeDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilTime, parent) as RepeatUntilTimeDuration;
                            timeDuration.TimeInSeconds = (UInt16)(targetField.GetUInt32() / 1000.0f);
                            newDuration = timeDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilDistance:
                        {
                            RepeatUntilDistanceDuration distanceDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilDistance, parent) as RepeatUntilDistanceDuration;
                            distanceDuration.SetDistanceInBaseUnit(Length.Convert(targetField.GetUInt32(), Length.Units.Centimeter, distanceDuration.BaseUnit));
                            newDuration = distanceDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan:
                        {
                            RepeatUntilHeartRateAboveDuration hrDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilHeartRateAbove, parent) as RepeatUntilHeartRateAboveDuration;
                            UInt32 hrValue = targetField.GetUInt32();

                            if (hrValue >= 100)
                            {
                                hrDuration.IsPercentageMaxHeartRate = false;
                                hrDuration.MaxHeartRate = (Byte)(hrValue - 100);
                            }
                            else
                            {
                                hrDuration.IsPercentageMaxHeartRate = true;
                                hrDuration.MaxHeartRate = (Byte)hrValue;
                            }
                            newDuration = hrDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan:
                        {
                            RepeatUntilHeartRateBelowDuration hrDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilHeartRateBelow, parent) as RepeatUntilHeartRateBelowDuration;
                            UInt32 hrValue = targetField.GetUInt32();

                            if (hrValue >= 100)
                            {
                                hrDuration.IsPercentageMaxHeartRate = false;
                                hrDuration.MinHeartRate = (Byte)(hrValue - 100);
                            }
                            else
                            {
                                hrDuration.IsPercentageMaxHeartRate = true;
                                hrDuration.MinHeartRate = (Byte)hrValue;
                            }
                            newDuration = hrDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilCalories:
                        {
                            RepeatUntilCaloriesDuration caloriesDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilCalories, parent) as RepeatUntilCaloriesDuration;
                            caloriesDuration.CaloriesToSpend = (UInt16)targetField.GetUInt32();
                            newDuration = caloriesDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan:
                        {
                            RepeatUntilPowerAboveDuration powerDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilPowerAbove, parent) as RepeatUntilPowerAboveDuration;
                            UInt32 powerValue = targetField.GetUInt32();

                            if (powerValue >= 1000)
                            {
                                powerDuration.IsPercentFTP = false;
                                powerDuration.MaxPower = (UInt16)(powerValue - 1000);
                            }
                            else
                            {
                                powerDuration.IsPercentFTP = true;
                                powerDuration.MaxPower = (UInt16)powerValue;
                            }
                            newDuration = powerDuration;
                            break;
                        }
                    case FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan:
                        {
                            RepeatUntilPowerBelowDuration powerDuration = Create(IRepeatDuration.RepeatDurationType.RepeatUntilPowerBelow, parent) as RepeatUntilPowerBelowDuration;
                            UInt32 powerValue = targetField.GetUInt32();

                            if (powerValue >= 1000)
                            {
                                powerDuration.IsPercentFTP = false;
                                powerDuration.MinPower = (UInt16)(powerValue - 1000);
                            }
                            else
                            {
                                powerDuration.IsPercentFTP = true;
                                powerDuration.MinPower = (UInt16)powerValue;
                            }
                            newDuration = powerDuration;
                            break;
                        }
                }
            }

            parent.Duration = newDuration;

            return newDuration;
        }
        public static Workout ImportWorkoutFromMessage(FITMessage workoutMessage, IActivityCategory category)
        {
            // Peek name
            FITMessageField nameField = workoutMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);

            if (nameField != null)
            {
                GarminFitnessView pluginView = PluginMain.GetApplication().ActiveView as GarminFitnessView;
                String workoutName = nameField.GetString();

                if (category == null && pluginView != null)
                {
                    GarminWorkoutControl workoutControl = pluginView.GetCurrentView() as GarminWorkoutControl;

                    if (workoutControl != null)
                    {
                        workoutControl.GetNewWorkoutNameAndCategory(ref workoutName, ref category);
                    }
                }

                return GarminWorkoutManager.Instance.CreateWorkout(workoutName, workoutMessage, category);
            }
            else
            {
                throw new FITParserException("No name for workout");
            }
        }
        public override void DeserializePowerZonesFromFIT(FITMessage powerZonesMessage)
        {
            base.DeserializePowerZonesFromFIT(powerZonesMessage);

            FITMessageField zoneIndex = powerZonesMessage.GetField((Byte)FITPowerZonesFieldIds.MessageIndex);
            FITMessageField zoneUpperValue = powerZonesMessage.GetField((Byte)FITPowerZonesFieldIds.HighWatts);

            if (zoneIndex != null && zoneUpperValue != null)
            {
                bool currentPercentFTP = PowerZonesInPercentFTP;
                UInt16 index = zoneIndex.GetUInt16();

                PowerZonesInPercentFTP = false;

                if (index > Constants.GarminPowerZoneCount)
                {
                    throw new FITParserException("Invalid index for power zone");
                }

                if (index == 0)
                {
                    SetPowerLowLimit(index, zoneUpperValue.GetUInt16());
                }
                else
                {
                    SetPowerHighLimit(index - 1, zoneUpperValue.GetUInt16());

                    if (index < Constants.GarminPowerZoneCount)
                    {
                        SetPowerLowLimit(index, zoneUpperValue.GetUInt16());
                    }
                }

                PowerZonesInPercentFTP = currentPercentFTP;
            }
            else
            {
                throw new FITParserException("Missing fields for power zone");
            }
        }
        public virtual void DeserializeSpeedZonesFromFIT(FITMessage speedZonesMessage)
        {
            FITMessageField zoneIndex = speedZonesMessage.GetField((Byte)FITSpeedZonesFieldIds.MessageIndex);
            FITMessageField zoneUpperValue = speedZonesMessage.GetField((Byte)FITSpeedZonesFieldIds.HighSpeed);
            FITMessageField zoneName = speedZonesMessage.GetField((Byte)FITSpeedZonesFieldIds.Name);

            if (zoneIndex != null &&
                zoneUpperValue != null &&
                zoneName != null)
            {
                UInt16 index = zoneIndex.GetUInt16();

                if (index > Constants.GarminSpeedZoneCount)
                {
                    throw new FITParserException("Invalid index for for speed zone");
                }

                if (index == 0)
                {
                    SetSpeedLowLimitInMetersPerSecond(index, zoneUpperValue.GetUInt16() / 1000.0);
                    SetSpeedName(index, zoneName.GetString());
                }
                else
                {
                    SetSpeedName(index - 1, zoneName.GetString());
                    SetSpeedHighLimitInMetersPerSecond(index - 1, zoneUpperValue.GetUInt16() / 1000.0);

                    if (index < Constants.GarminSpeedZoneCount)
                    {
                        SetSpeedLowLimitInMetersPerSecond(index, zoneUpperValue.GetUInt16() / 1000.0);
                    }
                }
            }
            else
            {
                throw new FITParserException("Missing fields for speed zone");
            }
        }
        public void TestFITScheduleSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;
            DateTime serializedDate;
            DateTime referenceDate = new DateTime(1989, 12, 31);

            // Scheduled dates
            placeholderWorkout.Name = "WorkoutTest5";
            serializedDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            serializedDate.AddDays(1);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // Test specific date with hardcoded result
            placeholderWorkout.Name = "WorkoutTest6b";
            serializedDate = new DateTime(1999, 12, 31, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            placeholderWorkout.Name = "WorkoutTest7";
            bool res = Time.SetTimeZone("New Zealand Standard Time");
            serializedDate = new DateTime(1999, 12, 31, 0, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            res = Time.SetTimeZone(currentZoneName);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();
        }
Beispiel #9
0
        public override void DeserializeFromFIT(FITMessage stepMessage)
        {
            FITMessageField stepsToRepeatField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);

            if (stepsToRepeatField != null)
            {
                Int32 precedingStepsToRepeat = (Int32)(ParentWorkout.StepCount - stepsToRepeatField.GetUInt32());
                List<IStep> stepsToRepeat = new List<IStep>();

                while (precedingStepsToRepeat > 0)
                {
                    Int32 precedingStepIndex = ParentWorkout.Steps.Count - 1;
                    Int32 precedingStepCounter = ParentWorkout.Steps[precedingStepIndex].StepCount;

                    while (precedingStepCounter < precedingStepsToRepeat)
                    {
                        precedingStepIndex--;
                        precedingStepCounter += ParentWorkout.Steps[precedingStepIndex].StepCount;
                    }

                    IStep precedingStep = ParentWorkout.Steps[precedingStepIndex];

                    stepsToRepeat.Add(precedingStep);

                    precedingStepsToRepeat -= precedingStep.StepCount;
                }

                // Officialize result in workout
                ParentConcreteWorkout.Steps.RemoveSteps(stepsToRepeat, false);
                // In case the repeat wasn't yet registered on the workout
                StepsToRepeat.Clear();
                foreach (IStep currentStep in stepsToRepeat)
                {
                    StepsToRepeat.Add(currentStep);
                }

                DurationFactory.Create(stepMessage, this);
            }
            else
            {
                throw new FITParserException("Missing steps to repeat");
            }
        }
        public virtual void DeserializeHRZonesFromFIT(FITMessage HRZonesMessage)
        {
            FITMessageField zoneIndex = HRZonesMessage.GetField((Byte)FITHRZonesFieldIds.MessageIndex);
            FITMessageField zoneUpperValue = HRZonesMessage.GetField((Byte)FITHRZonesFieldIds.HighBPM);

            if (zoneIndex != null && zoneUpperValue != null)
            {
                HRReferential currentReferential = HRZonesReferential;
                UInt16 index = zoneIndex.GetUInt16();

                // Always deserialize in BPM
                HRZonesReferential = HRReferential.HRReferential_BPM;

                if (index > Constants.GarminHRZoneCount)
                {
                    throw new FITParserException("Invalid index for HR zone");
                }

                if (index == 0)
                {
                    SetHeartRateLowLimit(0, zoneUpperValue.GetUInt8());
                }
                else
                {
                    SetHeartRateHighLimit(index - 1, zoneUpperValue.GetUInt8());

                    if (index < Constants.GarminHRZoneCount)
                    {
                        SetHeartRateLowLimit(index, zoneUpperValue.GetUInt8());
                    }
                }

                HRZonesReferential = currentReferential;
            }
            else
            {
                throw new FITParserException("Missing fields for HR zone");
            }
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Lap button
            LapButtonDuration lapDuration = new LapButtonDuration(placeholderStep);
            lapDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for lap button duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Open, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for lap button duration");
            serializedMessage.Clear();

            // Time duration
            TimeDuration timeDuration = new TimeDuration(500, placeholderStep);
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Time, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(500000, messageField.GetUInt32(), "Invalid duration type in field for time duration");
            serializedMessage.Clear();

            timeDuration.TimeInSeconds = 15;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Time, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(15000, messageField.GetUInt32(), "Invalid duration type in field for time duration");
            serializedMessage.Clear();

            // Distance duration
            DistanceDuration distanceDuration = new DistanceDuration(1000, Length.Units.Centimeter, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Distance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for distance duration");
            Assert.AreEqual(1000, messageField.GetUInt32(), "Invalid duration value in field for distance duration");
            serializedMessage.Clear();

            distanceDuration = new DistanceDuration(15, Length.Units.NauticalMile, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Distance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for distance duration");
            Assert.AreEqual(2778000, messageField.GetUInt32(), "Invalid duration value in field for distance duration");
            serializedMessage.Clear();

            // Calories duration
            CaloriesDuration caloriesDuration = new CaloriesDuration(1000, placeholderStep);
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Calories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for calories duration");
            Assert.AreEqual(1000, messageField.GetUInt32(), "Invalid duration value in field for calories duration");
            serializedMessage.Clear();

            caloriesDuration.CaloriesToSpend = 452;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.Calories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for calories duration");
            Assert.AreEqual(452, messageField.GetUInt32(), "Invalid duration value in field for calories duration");
            serializedMessage.Clear();

            // HR Above
            HeartRateAboveDuration hrAboveDuration = new HeartRateAboveDuration(145, false, placeholderStep);
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(245, messageField.GetUInt32(), "Invalid duration value in field for HRAbove duration");
            serializedMessage.Clear();

            hrAboveDuration = new HeartRateAboveDuration(75, true, placeholderStep);
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRAbove duration");
            Assert.AreEqual(75, messageField.GetUInt32(), "Invalid duration value in field for HRAbove duration");
            serializedMessage.Clear();

            // HR Below
            HeartRateBelowDuration hrBelowDuration = new HeartRateBelowDuration(145, false, placeholderStep);
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(245, messageField.GetUInt32(), "Invalid duration value in field for HRBelow duration");
            serializedMessage.Clear();

            hrBelowDuration = new HeartRateBelowDuration(55, true, placeholderStep);
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.HeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for HRBelow duration");
            Assert.AreEqual(55, messageField.GetUInt32(), "Invalid duration value in field for HRBelow duration");
            serializedMessage.Clear();

            // Power Above
            PowerAboveDuration powerAboveDuration = new PowerAboveDuration(150, false, placeholderStep);
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid duration value in field for PowerAbove duration");
            serializedMessage.Clear();

            powerAboveDuration = new PowerAboveDuration(150, true, placeholderStep);
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerAbove duration");
            Assert.AreEqual(150, messageField.GetUInt32(), "Invalid duration value in field for PowerAbove duration");
            serializedMessage.Clear();

            // Power Below
            PowerBelowDuration powerBelowDuration = new PowerBelowDuration(175, false, placeholderStep);
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(1175, messageField.GetUInt32(), "Invalid duration value in field for PowerBelow duration");
            serializedMessage.Clear();

            powerBelowDuration = new PowerBelowDuration(60, true, placeholderStep);
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.PowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration type field not serialized for PowerBelow duration");
            Assert.AreEqual(60, messageField.GetUInt32(), "Invalid duration value in field for PowerBelow duration");
            serializedMessage.Clear();
        }
Beispiel #12
0
        public override void DeserializeFromFIT(FITMessage workoutMessage)
        {
            FITMessage stepMessage;
            FITMessageField numStepsField = workoutMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);

            if (numStepsField != null)
            {
                UInt16 numSteps = numStepsField.GetUInt16();

                FITParser.Instance.RestartParsing();
                m_Steps.Clear();

                do
                {
                    stepMessage = FITParser.Instance.ReadNextMessage();

                    if (stepMessage != null)
                    {
                        switch (stepMessage.GlobalMessageType)
                        {
                            case FITGlobalMessageIds.WorkoutStep:
                            {
                                FITMessageField stepTypeField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);

                                if (stepTypeField != null)
                                {
                                    FITWorkoutStepDurationTypes durationType = (FITWorkoutStepDurationTypes)stepTypeField.GetEnum();
                                    IStep newStep = null;

                                    switch (durationType)
                                    {
                                        case FITWorkoutStepDurationTypes.Calories:
                                        case FITWorkoutStepDurationTypes.Distance:
                                        case FITWorkoutStepDurationTypes.HeartRateGreaterThan:
                                        case FITWorkoutStepDurationTypes.HeartRateLessThan:
                                        case FITWorkoutStepDurationTypes.Open:
                                        case FITWorkoutStepDurationTypes.Time:
                                        case FITWorkoutStepDurationTypes.PowerGreaterThan:
                                        case FITWorkoutStepDurationTypes.PowerLessThan:
                                            {
                                                newStep = new RegularStep(this);
                                                break;
                                            }
                                        case FITWorkoutStepDurationTypes.RepeatCount:
                                        case FITWorkoutStepDurationTypes.RepeatUntilCalories:
                                        case FITWorkoutStepDurationTypes.RepeatUntilDistance:
                                        case FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan:
                                        case FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan:
                                        case FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan:
                                        case FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan:
                                        case FITWorkoutStepDurationTypes.RepeatUntilTime:
                                            {
                                                newStep = new RepeatStep(this);
                                                break;
                                            }
                                    }

                                    newStep.DeserializeFromFIT(stepMessage);
                                    m_Steps.AddStepToRoot(newStep);
                                }
                                else
                                {
                                    throw new FITParserException("Missing duration type field");
                                }

                                break;
                            }
                            default:
                            {
                                // Nothing to do
                                break;
                            }
                        }
                    }
                }
                while (stepMessage != null && m_Steps.StepCount < numSteps);

                if (m_Steps.StepCount < numSteps)
                {
                    throw new FITParserException("Unable to deserialize all steps");
                }
            }
            else
            {
                throw new FITParserException("No step count field");
            }
        }
        public override void DeserializeZonesTargetFromFIT(FITMessage zonesTargetMessage)
        {
            base.DeserializeZonesTargetFromFIT(zonesTargetMessage);

            FITMessageField FTPField = zonesTargetMessage.GetField((Byte)FITZonesTargetFieldIds.FTP);

            if (FTPField != null)
            {
                FTP = FTPField.GetUInt16();
            }
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Repeat count
            RepeatCountDuration repeatDuration = new RepeatCountDuration(placeholderStep);
            repeatDuration.RepetitionCount = 3;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            repeatDuration.RepetitionCount = 7;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(7, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            // Repeat until calories
            RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
            caloriesDuration.CaloriesToSpend = 550;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(550, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            caloriesDuration.CaloriesToSpend = 750;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(750, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            // Repeat until distance
            RepeatUntilDistanceDuration distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Kilometer, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(100000, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Mile, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(160934, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            // Repeat until HRAbove
            RepeatUntilHeartRateAboveDuration hrAboveDuration = new RepeatUntilHeartRateAboveDuration(placeholderStep);
            hrAboveDuration.IsPercentageMaxHeartRate = false;
            hrAboveDuration.MaxHeartRate = 160;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            hrAboveDuration.IsPercentageMaxHeartRate = true;
            hrAboveDuration.MaxHeartRate = 80;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilHeartRateBelowDuration hrBelowDuration = new RepeatUntilHeartRateBelowDuration(placeholderStep);
            hrBelowDuration.IsPercentageMaxHeartRate = false;
            hrBelowDuration.MinHeartRate = 150;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(250, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            hrBelowDuration.IsPercentageMaxHeartRate = true;
            hrBelowDuration.MinHeartRate = 70;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            // Repeat until PowerAbove
            RepeatUntilPowerAboveDuration powerAboveDuration = new RepeatUntilPowerAboveDuration(placeholderStep);
            powerAboveDuration.IsPercentFTP = false;
            powerAboveDuration.MaxPower = 250;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(1250, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            powerAboveDuration.IsPercentFTP = true;
            powerAboveDuration.MaxPower = 100;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilPowerBelowDuration powerBelowDuration = new RepeatUntilPowerBelowDuration(placeholderStep);
            powerBelowDuration.IsPercentFTP = false;
            powerBelowDuration.MinPower = 150;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            powerBelowDuration.IsPercentFTP = true;
            powerBelowDuration.MinPower = 270;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            // Repeat until time
            RepeatUntilTimeDuration timeDuration = new RepeatUntilTimeDuration(placeholderStep);
            timeDuration.TimeInSeconds = 600;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(600000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();

            timeDuration.TimeInSeconds = 350;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(350000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();
        }
        public virtual void DeserializeZonesTargetFromFIT(FITMessage zonesTargetMessage)
        {
            FITMessageField maxHR = zonesTargetMessage.GetField((Byte)FITZonesTargetFieldIds.MaxHR);

            if (maxHR != null)
            {
                MaximumHeartRate = maxHR.GetUInt8();
            }
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // This is required to determine the step's id in the workout during serialization
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // No target
            NullTarget noTarget = new NullTarget(placeholderStep);
            noTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid no target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.NoTarget, messageField.GetEnum(), "Invalid target type in field for target");
            serializedMessage.Clear();

            // Cadence targets
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);

            // Cadence range
            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(80, 90, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(90, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(60, 120, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(60, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Cadence ST zone
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(85, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[4], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Speed targets
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);

            // Speed range
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Mile, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Speed Garmin zone
            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(1, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(3, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            // Speed ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(2777, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Heart rate targets
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);

            // HR range
            hrTarget.ConcreteTarget = new HeartRateRangeTarget(130, 170, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(230, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(100, 190, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(200, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(290, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(50, 70, true, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(50, messageField.GetUInt32(), "Invalid lower value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid upper value in field for HRRange %Max target");
            serializedMessage.Clear();

            // HR Garmin zone
            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(1, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(3, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            // HR ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(240, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[4], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(280, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(340, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = true;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(68, messageField.GetUInt32(), "Invalid lower value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(82, messageField.GetUInt32(), "Invalid upper value in field for HRST %Max target");
            serializedMessage.Clear();

            // Power targets
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);

            // Power range
            powerTarget.ConcreteTarget = new PowerRangeTarget(150, 200, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(300, 400, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(67, 80, true, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(67, messageField.GetUInt32(), "Invalid lower value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid upper value in field for PowerRange %FTP target");
            serializedMessage.Clear();

            // Power Garmin zone
            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(1, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(3, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            // Power ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[4];
            Options.Instance.ExportSportTracksPowerAsPercentFTP = false;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            Options.Instance.ExportSportTracksPowerAsPercentFTP = true;
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid lower value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(160, messageField.GetUInt32(), "Invalid upper value in field for PowerST %FTP target");
            serializedMessage.Clear();

            // Make sure to reset options to previous values
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = exportHRAsMax;
            Options.Instance.ExportSportTracksPowerAsPercentFTP = exportPowerAsFTP;
        }
Beispiel #17
0
        public override void DeserializeFromFIT(FITMessage stepMessage)
        {
            FITMessageField nameField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            FITMessageField intensityField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);

            if (nameField != null)
            {
                Name = nameField.GetString();
            }

            if (intensityField != null)
            {
                Intensity = (StepIntensity)intensityField.GetEnum();
            }

            Duration = DurationFactory.Create(stepMessage, this);
            Target = TargetFactory.Create(stepMessage, this);
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;

            // Name
            placeholderWorkout.Name = "WorkoutTest1";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest1", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest2";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest2", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            // Category/Sport
            placeholderWorkout.Name = "WorkoutTest3";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[5];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Cycling, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest4";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[6];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Running, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            // Number of steps
            placeholderWorkout.Name = "WorkoutTest5";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            placeholderWorkout.Steps.AddStepToRoot(new RepeatStep(placeholderWorkout));
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(3, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            // Do not test steps serialization here, it has it's own test
        }
        public static ITarget Create(FITMessage stepMessage, RegularStep parent)
        {
            FITMessageField targetTypeField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField targetField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField targetCustomLowField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField targetCustomHighField = stepMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            ITarget result = new NullTarget(parent);

            if(targetTypeField != null)
            {
                switch((FITWorkoutStepTargetTypes)targetTypeField.GetEnum())
                {
                    case FITWorkoutStepTargetTypes.HeartRate:
                        {
                            if(targetField != null)
                            {
                                BaseHeartRateTarget hrTarget = (BaseHeartRateTarget)Create(ITarget.TargetType.HeartRate, parent);
                                UInt32 hrZone = targetField.GetUInt32();

                                if (hrZone == 0)
                                {
                                    if (targetCustomLowField != null &&
                                        targetCustomHighField != null)
                                    {
                                        HeartRateRangeTarget concreteTarget = new HeartRateRangeTarget(hrTarget);

                                        if (targetCustomLowField.GetUInt32() > 100)
                                        {
                                            concreteTarget.SetValues((Byte)(targetCustomLowField.GetUInt32() - 100),
                                                                     (Byte)(targetCustomHighField.GetUInt32() - 100),
                                                                     false);
                                        }
                                        else
                                        {
                                            concreteTarget.SetValues((Byte)targetCustomLowField.GetUInt32(),
                                                                     (Byte)targetCustomHighField.GetUInt32(),
                                                                     true);
                                        }

                                        hrTarget.ConcreteTarget = concreteTarget;
                                    }
                                    else
                                    {
                                        throw new FITParserException("Missing target custom value field");
                                    }
                                }
                                else
                                {
                                    HeartRateZoneGTCTarget concreteTarget = new HeartRateZoneGTCTarget(hrTarget);

                                    concreteTarget.Zone = (Byte)hrZone;
                                    hrTarget.ConcreteTarget = concreteTarget;
                                }

                                result = hrTarget;
                            }
                            else
                            {
                                throw new FITParserException("Missing target value field");
                            }
                            break;
                        }
                    case FITWorkoutStepTargetTypes.Cadence:
                        {
                            if (targetField != null &&
                                targetCustomLowField != null &&
                                targetCustomHighField != null)
                            {
                                BaseCadenceTarget cadenceTarget = (BaseCadenceTarget)Create(ITarget.TargetType.Cadence, parent);
                                CadenceRangeTarget concreteTarget = new CadenceRangeTarget(cadenceTarget);
                                UInt32 cadenceZone = targetField.GetUInt32();

                                Debug.Assert(cadenceZone == 0);

                                concreteTarget.SetValues((Byte)targetCustomLowField.GetUInt32(),
                                                         (Byte)targetCustomHighField.GetUInt32());
                                cadenceTarget.ConcreteTarget = concreteTarget;

                                result = cadenceTarget;
                            }
                            else
                            {
                                throw new FITParserException("Missing target or custom values field");
                            }

                            break;
                        }
                    case FITWorkoutStepTargetTypes.Speed:
                        {
                            if(targetField != null)
                            {
                                BaseSpeedTarget speedTarget = (BaseSpeedTarget)Create(ITarget.TargetType.Speed, parent);
                                UInt32 speedZone = targetField.GetUInt32();

                                if (speedZone == 0)
                                {
                                    if (targetCustomLowField != null &&
                                        targetCustomHighField != null)
                                    {
                                        SpeedRangeTarget concreteTarget = new SpeedRangeTarget(speedTarget);

                                        concreteTarget.SetRangeInBaseUnitsPerHour(Length.Convert(targetCustomLowField.GetUInt32() * 3.6, Length.Units.Meter, concreteTarget.BaseUnit),
                                                                                  Length.Convert(targetCustomHighField.GetUInt32() * 3.6, Length.Units.Meter, concreteTarget.BaseUnit));
                                        speedTarget.ConcreteTarget = concreteTarget;
                                    }
                                    else
                                    {
                                        throw new FITParserException("Missing target custom value field");
                                    }
                                }
                                else
                                {
                                    SpeedZoneGTCTarget concreteTarget = new SpeedZoneGTCTarget(speedTarget);

                                    concreteTarget.Zone = (Byte)speedZone;
                                    speedTarget.ConcreteTarget = concreteTarget;
                                }

                                result = speedTarget;
                            }
                            else
                            {
                                throw new FITParserException("Missing target value field");
                            }
                            break;
                        }
                    case FITWorkoutStepTargetTypes.Power:
                        {
                            if(targetField != null)
                            {
                                BasePowerTarget powerTarget = (BasePowerTarget)Create(ITarget.TargetType.Power, parent);
                                UInt32 powerZone = targetField.GetUInt32();

                                if (powerZone == 0)
                                {
                                    if (targetCustomLowField != null &&
                                        targetCustomHighField != null)
                                    {
                                        PowerRangeTarget concreteTarget = new PowerRangeTarget(powerTarget);

                                        if (targetCustomLowField.GetUInt32() > 1000)
                                        {
                                            concreteTarget.SetValues((UInt16)(targetCustomLowField.GetUInt32() - 1000),
                                                                     (UInt16)(targetCustomHighField.GetUInt32() - 1000),
                                                                     false);
                                        }
                                        else
                                        {
                                            concreteTarget.SetValues((UInt16)(targetCustomLowField.GetUInt32()),
                                                                     (UInt16)(targetCustomHighField.GetUInt32()),
                                                                     true);
                                        }
                                        powerTarget.ConcreteTarget = concreteTarget;
                                    }
                                    else
                                    {
                                        throw new FITParserException("Missing target custom value field");
                                    }
                                }
                                else
                                {
                                    PowerZoneGTCTarget concreteTarget = new PowerZoneGTCTarget(powerTarget);

                                    concreteTarget.Zone = (Byte)powerZone;
                                    powerTarget.ConcreteTarget = concreteTarget;
                                }

                                result = powerTarget;
                            }
                            else
                            {
                                throw new FITParserException("Missing target value field");
                            }

                            break;
                        }
                }
            }
            else
            {
                throw new FITParserException("Missing target type field");
            }

            return result;
        }
        public static DateTime ImportWorkoutScheduleMessage(FITMessage scheduleMessage)
        {
            // Make sure we have a workout file
            FITMessageField scheduledDate = scheduleMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            UInt32 secondsSinceReference = scheduledDate.GetUInt32();

            return new DateTime(1989, 12, 31) + new TimeSpan(0, 0, (int)secondsSinceReference);
        }
        public void DeserializeBikeProfile(FITMessage bikeProfileMessage)
        {
            FITMessageField bikeIndex = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.MessageIndex);
            FITMessageField bikeName = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.Name);
            FITMessageField odometer = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.Odometer);
            FITMessageField customWheelSize = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.CustomWheelSize);
            FITMessageField autoWheelSize = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.AutoWheelSize);
            FITMessageField weight = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.Weight);
            FITMessageField autoWheelSetting = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.AutoWheelSetting);
            FITMessageField speedCadenceSensorEnabled = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.SpeedCadenceSensorEnabled);
            FITMessageField powerSensorEnabled = bikeProfileMessage.GetField((Byte)FITBikeProfileFieldIds.PowerSensorEnabled);

            if (bikeIndex != null)
            {
                GarminBikeProfile profile = m_Bikes[bikeIndex.GetUInt16()];

                if (bikeName != null)
                {
                    profile.Name = bikeName.GetString();
                }

                if (odometer != null)
                {
                    profile.OdometerInMeters = Math.Min(odometer.GetUInt32() / 100.0, Constants.MaxOdometerMeters);
                }

                if (weight != null)
                {
                    profile.WeightInPounds = Weight.Convert(weight.GetUInt16() / 10.0, Weight.Units.Kilogram, Weight.Units.Pound);
                }

                if (autoWheelSetting != null)
                {
                    bool useAutoWheelSize = (autoWheelSetting.GetEnum() == (Byte)FITBoolean.True);

                    if (useAutoWheelSize && autoWheelSize != null)
                    {
                        profile.AutoWheelSize = true;
                        profile.WheelSize = autoWheelSize.GetUInt16();
                    }
                    else if (!useAutoWheelSize && customWheelSize != null)
                    {
                        profile.AutoWheelSize = false;
                        profile.WheelSize = customWheelSize.GetUInt16();
                    }
                }

                if (speedCadenceSensorEnabled != null)
                {
                    profile.HasCadenceSensor = (speedCadenceSensorEnabled.GetEnum() == (Byte)FITBoolean.True);
                }

                if (powerSensorEnabled != null)
                {
                    profile.HasPowerSensor = (powerSensorEnabled.GetEnum() == (Byte)FITBoolean.True);
                }
            }
            else
            {
                throw new FITParserException("Invalid bike profile index");
            }
        }