public static IDuration Create(IDuration.DurationType type, RegularStep parent)
        {
            IDuration newDuration;

            switch (type)
            {
                case IDuration.DurationType.LapButton:
                    {
                        newDuration = new LapButtonDuration(parent);
                        break;
                    }
                case IDuration.DurationType.Time:
                    {
                        newDuration = new TimeDuration(parent);
                        break;
                    }
                case IDuration.DurationType.Distance:
                    {
                        newDuration = new DistanceDuration(parent);
                        break;
                    }
                case IDuration.DurationType.HeartRateAbove:
                    {
                        newDuration = new HeartRateAboveDuration(parent);
                        break;
                    }
                case IDuration.DurationType.HeartRateBelow:
                    {
                        newDuration = new HeartRateBelowDuration(parent);
                        break;
                    }
                case IDuration.DurationType.Calories:
                    {
                        newDuration = new CaloriesDuration(parent);
                        break;
                    }
                case IDuration.DurationType.PowerAbove:
                    {
                        newDuration = new PowerAboveDuration(parent);
                        break;
                    }
                case IDuration.DurationType.PowerBelow:
                    {
                        newDuration = new PowerBelowDuration(parent);
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        newDuration = null;
                        break;
                    }
            }

            parent.Duration = newDuration;

            return newDuration;
        }
        public static ITarget Create(ITarget.TargetType type, Stream stream, DataVersion version, RegularStep parent)
        {
            ITarget newTarget;

            switch (type)
            {
                case ITarget.TargetType.Null:
                    {
                        newTarget = new NullTarget(stream, version, parent);
                        break;
                    }
                case ITarget.TargetType.Speed:
                    {
                        newTarget = new BaseSpeedTarget(stream, version, parent);
                        break;
                    }
                case ITarget.TargetType.HeartRate:
                    {
                        newTarget = new BaseHeartRateTarget(stream, version, parent);
                        break;
                    }
                case ITarget.TargetType.Cadence:
                    {
                        newTarget = new BaseCadenceTarget(stream, version, parent);
                        break;
                    }
                case ITarget.TargetType.Power:
                    {
                        newTarget = new BasePowerTarget(stream, version, parent);
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        newTarget = null;
                        break;
                    }
            }

            parent.Target = newTarget;

            return newTarget;
        }
        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 TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            IDuration deserializedDuration;

            serializedMessage.AddField(typeField);

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

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

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

            // Time duration
            TimeDuration timeDuration;

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

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

            // Distance duration
            DistanceDuration distanceDuration;

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

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

            // Calories duration
            CaloriesDuration caloriesDuration;

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

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

            // HR Above
            HeartRateAboveDuration hrAboveDuration;

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

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

            // HR Below
            HeartRateBelowDuration hrBelowDuration;

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

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

            // Power Above
            PowerAboveDuration powerAboveDuration;

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

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

            // Power Below
            PowerBelowDuration powerBelowDuration;

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

            valueField.SetUInt32(55);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is PowerBelowDuration, "PowerBelow duration not properly FIT deserialized");
            powerBelowDuration = deserializedDuration as PowerBelowDuration;
            Assert.AreEqual(true, powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT PowerBelow duration");
            Assert.AreEqual(55, powerBelowDuration.MinPower, "Invalid value deserialized for FIT PowerBelow duration");
        }
        public void TestRegularStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField nameField = new FITMessageField((Byte)FITWorkoutStepFieldIds.StepName);
            FITMessageField intensityField = new FITMessageField((Byte)FITWorkoutStepFieldIds.Intensity);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField targetTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField targetLowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField targetHighRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);

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

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

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

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

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

            // Duration & target test
            Assert.IsTrue(placeholderStep.Duration is CaloriesDuration, "Duration not properly FIT deserialized in regular step");
            Assert.IsTrue(placeholderStep.Target is BaseCadenceTarget, "Target not properly FIT deserialized in regular step");
        }
        public void TestTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ITarget loadedTarget = null;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            XmlAttribute attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // No target
            readNode.InnerXml = noTargetResult;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "No target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is NullTarget, "No target wasn't deserialized as proper type");

            // Cadence targets
            BaseCadenceTarget cadenceTarget;

            // Cadence range
            CadenceRangeTarget cadenceRangeTarget;

            readNode.InnerXml = cadenceRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(80, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(90, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

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

            // Speed targets
            BaseSpeedTarget speedTarget;

            // Speed range
            SpeedRangeTarget speedRangeTarget;
            double speed;

            readNode.InnerXml = speedRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(20, speed, STCommon.Data.Constants.Delta, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(30, speed, STCommon.Data.Constants.Delta, "SpeedRange max value wasn't properly deserialized");

            readNode.InnerXml = speedRangeTargetResult4;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(4, speed, STCommon.Data.Constants.Delta, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(8, speed, STCommon.Data.Constants.Delta, "SpeedRange max value wasn't properly deserialized");

            // Speed Garmin zone
            SpeedZoneGTCTarget speedGTCTarget;

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

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

            // Heart rate targets
            BaseHeartRateTarget hrTarget;

            // Heart rate range
            HeartRateRangeTarget hrRangeTarget;

            readNode.InnerXml = hrRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(false, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(130, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(170, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

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

            // Heart rate Garmin zone
            HeartRateZoneGTCTarget hrGTCTarget;

            readNode.InnerXml = hrGTCTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(1, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            readNode.InnerXml = hrGTCTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(3, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            // Power targets
            BasePowerTarget powerTarget;

            // Power range
            PowerRangeTarget powerRangeTarget;

            readNode.InnerXml = powerRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(false, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(150, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

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

            // Power Garmin zone
            PowerZoneGTCTarget powerGTCTarget;

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

            readNode.InnerXml = powerGTCTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(3, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");
        }
        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
        }
 void OnWorkoutStepDurationChanged(IWorkout modifiedWorkout, RegularStep modifiedStep, IDuration modifiedDuration, PropertyChangedEventArgs changedProperty)
 {
     UpdateWorkoutStepsCopy();
 }
        void OnWorkoutStepDurationChanged(IWorkout modifiedWorkout, RegularStep modifiedStep, IDuration modifiedDuration, PropertyChangedEventArgs changedProperty)
        {
            if (InvokeRequired)
            {
                Invoke(new WorkoutStepDurationChangedDelegate(OnWorkoutStepDurationChanged),
                       new object[] { modifiedWorkout, modifiedStep, modifiedDuration, changedProperty });
            }
            else
            {
                if (SelectedWorkout == modifiedWorkout)
                {
                    // Refresh the steps list so it updates the name/description
                    StepsList.Invalidate();

                    if (modifiedStep == SelectedStep)
                    {
                        UpdateUIFromDuration();
                    }
                }
            }
        }
        public static ITarget Create(XmlNode parentNode, RegularStep parent)
        {
            ITarget newTarget = null;

            if (parentNode.Attributes.Count == 1 && parentNode.Attributes[0].Name == Constants.XsiTypeTCXString)
            {
                string stepTypeString = parentNode.Attributes[0].Value;

                for (int i = 0; i < (int)ITarget.TargetType.TargetTypeCount; ++i)
                {
                    if (stepTypeString == Constants.TargetTypeTCXString[i])
                    {
                        newTarget = TargetFactory.Create((ITarget.TargetType)i, parent);
                        newTarget.Deserialize(parentNode);
                        parent.Target = newTarget;

                        break;
                    }
                }
            }

            return newTarget;
        }
        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;
        }
        private void UpdateSpeedTargetFromComboBox(RegularStep concreteStep, int selectedIndex)
        {
            BaseSpeedTarget baseTarget = (BaseSpeedTarget)concreteStep.Target;

            // We might have to change from one target type to the other
            if (baseTarget.ConcreteTarget.Type != BaseSpeedTarget.IConcreteSpeedTarget.SpeedTargetType.Range &&
                selectedIndex == 0)
            {
                // Custom range
                baseTarget.ConcreteTarget = new SpeedRangeTarget(baseTarget);
            }
            else if (baseTarget.ConcreteTarget.Type == BaseSpeedTarget.IConcreteSpeedTarget.SpeedTargetType.Range &&
                    selectedIndex != 0)
            {
                if (Options.Instance.UseSportTracksSpeedZones)
                {
                    baseTarget.ConcreteTarget = new SpeedZoneSTTarget(baseTarget.ParentStep.ParentConcreteWorkout.Category.SpeedZone.Zones[selectedIndex - 1], baseTarget);
                }
                else
                {
                    baseTarget.ConcreteTarget = new SpeedZoneGTCTarget((Byte)(selectedIndex), baseTarget);
                }
            }
            else if (baseTarget.ConcreteTarget.Type == BaseSpeedTarget.IConcreteSpeedTarget.SpeedTargetType.ZoneST)
            {
                SpeedZoneSTTarget concreteTarget = (SpeedZoneSTTarget)baseTarget.ConcreteTarget;

                concreteTarget.Zone = baseTarget.ParentStep.ParentConcreteWorkout.Category.SpeedZone.Zones[selectedIndex - 1];
            }
            else if (baseTarget.ConcreteTarget.Type == BaseSpeedTarget.IConcreteSpeedTarget.SpeedTargetType.ZoneGTC)
            {
                SpeedZoneGTCTarget concreteTarget = (SpeedZoneGTCTarget)baseTarget.ConcreteTarget;

                concreteTarget.Zone = (Byte)(selectedIndex);
            }
        }
Beispiel #13
0
        public override void Deserialize(XmlNode parentNode)
        {
            bool repeatsRead = false;
            List<IStep> stepsToRepeat = new List<IStep>();

            base.Deserialize(parentNode);

            for (int i = 0; i < parentNode.ChildNodes.Count; ++i)
            {
                XmlNode child = parentNode.ChildNodes[i];

                if (child.Name == "Repetitions")
                {
                    RepeatCountDuration repeatCountDuration = new RepeatCountDuration(this);

                    repeatCountDuration.Deserialize(child);
                    Duration = repeatCountDuration;
                    repeatsRead = true;
                }
                else if (child.Name == "Child")
                {
                    string stepTypeString = child.Attributes[0].Value;
                    IStep newStep = null;

                    if (stepTypeString == Constants.StepTypeTCXString[(int)IStep.StepType.Regular])
                    {
                        newStep = new RegularStep(ParentConcreteWorkout);
                    }
                    else if (stepTypeString == Constants.StepTypeTCXString[(int)IStep.StepType.Repeat])
                    {
                        newStep = new RepeatStep(ParentConcreteWorkout);
                    }
                    else
                    {
                        Debug.Assert(false);
                    }

                    stepsToRepeat.Add(newStep);
                    newStep.Deserialize(child);
                }
            }

            if (!repeatsRead || stepsToRepeat.Count == 0)
            {
                throw new GarminFitnessXmlDeserializationException("Information missing in the XML node", parentNode);
            }

            ParentConcreteWorkout.Steps.RemoveSteps(m_StepsToRepeat, false);
            // In case the repeat wasn't yet registered on the workout
            m_StepsToRepeat.Clear();
            for (int i = 0; i < stepsToRepeat.Count; ++i)
            {
                m_StepsToRepeat.Add(stepsToRepeat[i]);
            }
        }
        private void UpdatePowerTargetFromComboBox(RegularStep concreteStep, int selectedIndex)
        {
            BasePowerTarget baseTarget = (BasePowerTarget)concreteStep.Target;

            // We might have to change from one target type to the other
            if (baseTarget.ConcreteTarget.Type != BasePowerTarget.IConcretePowerTarget.PowerTargetType.Range &&
                selectedIndex == 0)
            {
                // Custom range
                baseTarget.ConcreteTarget = new PowerRangeTarget(baseTarget);
            }
            else if (baseTarget.ConcreteTarget.Type == BasePowerTarget.IConcretePowerTarget.PowerTargetType.Range &&
                     selectedIndex != 0)
            {
                // ST zone
                if (Options.Instance.UseSportTracksPowerZones)
                {
                    baseTarget.ConcreteTarget = new PowerZoneSTTarget(Options.Instance.PowerZoneCategory.Zones[selectedIndex - 1], baseTarget);
                }
                else
                {
                    baseTarget.ConcreteTarget = new PowerZoneGTCTarget((Byte)selectedIndex, baseTarget);
                }
            }
            else if (baseTarget.ConcreteTarget.Type == BasePowerTarget.IConcretePowerTarget.PowerTargetType.ZoneST)
            {
                PowerZoneSTTarget concreteTarget = (PowerZoneSTTarget)baseTarget.ConcreteTarget;

                concreteTarget.Zone = Options.Instance.PowerZoneCategory.Zones[selectedIndex - 1];
            }
            else if (baseTarget.ConcreteTarget.Type == BasePowerTarget.IConcretePowerTarget.PowerTargetType.ZoneGTC)
            {
                PowerZoneGTCTarget concreteTarget = (PowerZoneGTCTarget)baseTarget.ConcreteTarget;

                concreteTarget.Zone = (Byte)selectedIndex;
            }
        }
        private void UpdateCadenceTargetFromComboBox(RegularStep concreteStep, int selectedIndex)
        {
            BaseCadenceTarget baseTarget = (BaseCadenceTarget)concreteStep.Target;

            // We might have to change from one target type to the other
            if (baseTarget.ConcreteTarget.Type != BaseCadenceTarget.IConcreteCadenceTarget.CadenceTargetType.Range &&
                selectedIndex == 0)
            {
                // Custom range
                baseTarget.ConcreteTarget = new CadenceRangeTarget(baseTarget);
            }
            else if (baseTarget.ConcreteTarget.Type == BaseCadenceTarget.IConcreteCadenceTarget.CadenceTargetType.Range &&
                     selectedIndex != 0)
            {
                // ST zone
                baseTarget.ConcreteTarget = new CadenceZoneSTTarget(Options.Instance.CadenceZoneCategory.Zones[selectedIndex - 1], baseTarget);
            }
            else if (baseTarget.ConcreteTarget.Type == BaseCadenceTarget.IConcreteCadenceTarget.CadenceTargetType.ZoneST)
            {
                CadenceZoneSTTarget concreteTarget = (CadenceZoneSTTarget)baseTarget.ConcreteTarget;

                concreteTarget.Zone = Options.Instance.CadenceZoneCategory.Zones[selectedIndex - 1];
            }
        }
        void OnWorkoutStepTargetChanged(IWorkout modifiedWorkout, RegularStep modifiedStep, ITarget modifiedTarget, PropertyChangedEventArgs changedProperty)
        {
            if (InvokeRequired)
            {
                Invoke(new WorkoutStepTargetChangedDelegate(OnWorkoutStepTargetChanged),
                       new object[] {modifiedWorkout, modifiedStep, modifiedTarget, changedProperty});
            }
            else
            {
                if (SelectedWorkout == modifiedWorkout)
                {
                    // Refresh the steps list so it updates the name/description
                    StepsList.Invalidate();

                    if (changedProperty.PropertyName == "ConcreteTarget")
                    {
                        UpdateTargetPanelVisibility();
                    }

                    if (modifiedStep == SelectedStep)
                    {
                        UpdateUIFromTarget();
                    }
                }
            }
        }
        public void TestRegularStepTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep step = placeholderWorkout.Steps[0] as RegularStep;
            int resultPosition;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Active step
            step.Name = "StepTest1";
            step.Intensity = RegularStep.StepIntensity.Active;
            step.Serialize(database, "StepTest1", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult1);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for active step");

            // Rest step
            step.Name = "StepTest2";
            step.Intensity = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest2", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult2);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for rest step");

            // Warmup step
            step = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(step);
            Options.Instance.TCXExportWarmupAs = RegularStep.StepIntensity.Active;
            step.Name = "StepTest3";
            step.Intensity = RegularStep.StepIntensity.Warmup;
            step.Serialize(database, "StepTest3", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult3);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for warmup step");

            step.Name = "StepTest4";
            Options.Instance.TCXExportWarmupAs = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest4", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult4);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for warmup step");

            // Cooldown step
            Options.Instance.TCXExportCooldownAs = RegularStep.StepIntensity.Active;
            step.Name = "StepTest5";
            step.Intensity = RegularStep.StepIntensity.Cooldown;
            step.Serialize(database, "StepTest5", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult5);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for cooldown step");

            step.Name = "StepTest6";
            Options.Instance.TCXExportCooldownAs = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest6", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult6);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for cooldown step");
        }
        public void TestTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            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;
            int targetPosition1;
            int targetPosition2;

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

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // No target
            NullTarget noTarget = new NullTarget(placeholderStep);
            noTarget.Serialize(database, "NoTarget", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(noTargetResult);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid no target serialization");

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

            // Cadence range
            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(80, 90, cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(cadenceRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid CadenceRange target serialization");

            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(60, 120, cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(cadenceRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid CadenceRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "CadenceRange target serializations don't differ");

            // Cadence ST zone
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(cadenceSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid CadenceST target serialization");

            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[4], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(cadenceSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid CadenceST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "CadenceST target serializations don't differ");

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

            // Speed range
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRange target serialization");

            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Mile, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRange target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRange pace target serialization");

            speedTarget.ConcreteTarget = new SpeedRangeTarget(7.5, 15, Length.Units.Mile, Speed.Units.Pace, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRRange pace target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRange pace target serializations don't differ");

            // Speed Garmin zone
            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(1, speedTarget);
            speedTarget.Serialize(database, "SpeedGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedGTC target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(3, speedTarget);
            speedTarget.Serialize(database, "SpeedGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedGTC target serializations don't differ");

            // Speed ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRST target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST %Max target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST %Max target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "SpeedST %Max target serializations don't differ");

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

            // HR range
            hrTarget.ConcreteTarget = new HeartRateRangeTarget(130, 170, false, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRange target serialization");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(100, 190, false, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRange target serializations don't differ");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(50, 70, true, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRange %Max target serialization");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(75, 95, true, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRange %Max target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRange %Max target serializations don't differ");

            // HR Garmin zone
            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(1, hrTarget);
            hrTarget.Serialize(database, "HRGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRGTC target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(3, hrTarget);
            hrTarget.Serialize(database, "HRGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRGTC target serializations don't differ");

            // HR ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRST target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = true;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST %Max target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST %Max target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "HRST %Max target serializations don't differ");

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

            // Power range
            powerTarget.ConcreteTarget = new PowerRangeTarget(150, 200, false, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerRange target serialization");

            powerTarget.ConcreteTarget = new PowerRangeTarget(300, 400, false, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerRange target serializations don't differ");

            powerTarget.ConcreteTarget = new PowerRangeTarget(67, 80, true, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerRange %FTP target serialization");

            powerTarget.ConcreteTarget = new PowerRangeTarget(120, 160, true, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerRRange %FTP target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerRange %FTP target serializations don't differ");

            // Power Garmin zone
            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(1, powerTarget);
            powerTarget.Serialize(database, "PowerGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerGTC target serialization");

            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(3, powerTarget);
            powerTarget.Serialize(database, "PowerGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerGTC target serializations don't differ");

            // 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.Serialize(database, "PowerSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST target serialization");

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerST target serializations don't differ");

            Options.Instance.ExportSportTracksPowerAsPercentFTP = true;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST %FTP target serialization");

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST %FTP target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "PowerST %FTP target serializations don't differ");

            // Make sure to reset options to previous values
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = exportHRAsMax;
            Options.Instance.ExportSportTracksPowerAsPercentFTP = exportPowerAsFTP;
        }
        public void TestRepeatStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep regularStep;
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);

            regularStep = placeholderWorkout.Steps[0] as RegularStep;

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

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

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

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

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

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

            // At this point we have the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 2)
            //   - Regular step (id = 1)
            //  - Repeat step (id = 5)
            //   - Regular step (id = 4)
            //   - Regular step (id = 5)
            //  - Repeat step (not deserialized yet)
            // By deserializing the message we should get the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 6)
            //   - Repeat step (id = 2)
            //    - Regular step (id = 1)
            //   - Repeat step (id = 5)
            //    - Regular step (id = 3)
            //    - Regular step (id = 4)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(2, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(7, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RegularStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(regularStep, placeholderWorkout.Steps[0], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[1], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[1] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
        }
        public void 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;
        }
 void OnWorkoutStepTargetChanged(IWorkout modifiedWorkout, RegularStep modifiedStep, ITarget modifiedTarget, PropertyChangedEventArgs changedProperty)
 {
     UpdateWorkoutStepsCopy();
 }
        public void TestTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Lap button
            LapButtonDuration lapDuration = new LapButtonDuration(placeholderStep);
            lapDuration.Serialize(database, "LapDuration", testDocument);
            int durationPosition1 = testDocument.InnerXml.IndexOf(lapDurationResult);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid lap duration serialization");

            // Time
            TimeDuration timeDuration = new TimeDuration(500, placeholderStep);
            timeDuration.Serialize(database, "TimeDuration1", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(timeDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid time duration serialization");

            timeDuration.TimeInSeconds = 300;
            timeDuration.Serialize(database, "TimeDuration2", testDocument);
            int durationPosition2 = testDocument.InnerXml.IndexOf(timeDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid time duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "Time durations serialization don't differ");

            // Distance
            DistanceDuration distanceDuration = new DistanceDuration(1, Length.Units.Kilometer, placeholderStep);
            distanceDuration.Serialize(database, "DistanceDuration1", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(distanceDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid distance duration serialization");

            distanceDuration = new DistanceDuration(1, Length.Units.Mile, placeholderStep);
            distanceDuration.Serialize(database, "DistanceDuration2", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(distanceDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid distance duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "Distance durations serialization don't differ");

            // Calories
            CaloriesDuration caloriesDuration = new CaloriesDuration(550, placeholderStep);
            caloriesDuration.Serialize(database, "CaloriesDuration1", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(caloriesDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid calories duration serialization");

            caloriesDuration.CaloriesToSpend = 100;
            caloriesDuration.Serialize(database, "CaloriesDuration2", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(caloriesDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid calories duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "Calories durations serialization don't differ");

            // HR Above
            HeartRateAboveDuration hrAboveDuration = new HeartRateAboveDuration(160, false, placeholderStep);
            hrAboveDuration.Serialize(database, "HRAboveDuration1", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(hrAboveDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid HRAbove duration serialization");

            hrAboveDuration.MaxHeartRate = 130;
            hrAboveDuration.Serialize(database, "HRAboveDuration2", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(hrAboveDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid HRAbove duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "HRAbove durations serialization don't differ");

            hrAboveDuration.IsPercentageMaxHeartRate = true;
            hrAboveDuration.MaxHeartRate = 50;
            hrAboveDuration.Serialize(database, "HRAboveDuration3", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(hrAboveDurationResult3);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid HRAbove %Max duration serialization");

            hrAboveDuration.MaxHeartRate = 70;
            hrAboveDuration.Serialize(database, "HRAboveDuration4", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(hrAboveDurationResult4);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid HRAbove duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "HRAbove %Max durations serialization don't differ");

            // HR Below
            HeartRateBelowDuration hrBelowDuration = new HeartRateBelowDuration(160, false, placeholderStep);
            hrBelowDuration.Serialize(database, "HRBelowDuration1", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(hrBelowDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid HRBelow duration serialization");

            hrBelowDuration.MinHeartRate = 130;
            hrBelowDuration.Serialize(database, "HRBelowDuration2", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(hrBelowDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid HRBelow duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "HRBelow durations serialization don't differ");

            hrBelowDuration.IsPercentageMaxHeartRate = true;
            hrBelowDuration.MinHeartRate = 50;
            hrBelowDuration.Serialize(database, "HRBelowDuration3", testDocument);
            durationPosition1 = testDocument.InnerXml.IndexOf(hrBelowDurationResult3);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid HRBelow %Max duration serialization");

            hrBelowDuration.MinHeartRate = 70;
            hrBelowDuration.Serialize(database, "HRBelowDuration4", testDocument);
            durationPosition2 = testDocument.InnerXml.IndexOf(hrBelowDurationResult4);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid HRBelow duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "HRBelow %Max durations serialization don't differ");

            // Power Above
            try
            {
                PowerAboveDuration powerAboveDuration = new PowerAboveDuration(160, false, placeholderStep);
                powerAboveDuration.Serialize(database, "PowerAboveDuration1", testDocument);
                Assert.Fail("PowerAbove duration was serialized in TCX");
            }
            catch (NotSupportedException)
            {
            }

            // Power Below
            try
            {
                PowerBelowDuration powerBelowDuration = new PowerBelowDuration(160, false, placeholderStep);
                powerBelowDuration.Serialize(database, "PowerBelowDuration1", testDocument);
                Assert.Fail("PowerBelow duration was serialized in TCX");
            }
            catch (NotSupportedException)
            {
            }
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField lowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField highRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            ITarget loadedTarget = null;

            serializedMessage.AddField(typeField);

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

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

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

            // Cadence targets
            BaseCadenceTarget cadenceTarget;

            // Cadence range
            CadenceRangeTarget cadenceRangeTarget;

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

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

            // Speed targets
            BaseSpeedTarget speedTarget;

            // Speed range
            SpeedRangeTarget speedRangeTarget;
            double speed;

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

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

            // Speed Garmin zone
            SpeedZoneGTCTarget speedGTCTarget;

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

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

            // Heart rate targets
            BaseHeartRateTarget hrTarget;

            // Heart rate range
            HeartRateRangeTarget hrRangeTarget;

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

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

            // Heart rate Garmin zone
            HeartRateZoneGTCTarget hrGTCTarget;

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

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

            // Power targets
            BasePowerTarget powerTarget;

            // Power range
            PowerRangeTarget powerRangeTarget;

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

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

            // Power Garmin zone
            PowerZoneGTCTarget powerGTCTarget;

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

            valueField.SetUInt32(7);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(7, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");
        }
        public static IDuration Create(XmlNode parentNode, RegularStep parent)
        {
            IDuration newDuration = null;

            if (parentNode.Attributes.Count == 1 && parentNode.Attributes[0].Name == Constants.XsiTypeTCXString)
            {
                string stepTypeString = parentNode.Attributes[0].Value;

                // Power above & below are FIT only so stop the loop there
                for (int i = 0; i < (int)IDuration.DurationType.PowerAbove; ++i)
                {
                    if (stepTypeString == Constants.DurationTypeTCXString[i])
                    {
                        newDuration = DurationFactory.Create((IDuration.DurationType)i, parent);
                        newDuration.Deserialize(parentNode);
                        parent.Duration = newDuration;

                        break;
                    }
                }
            }

            return newDuration;
        }
        public void TestSerializeSTTargetExtensions()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;

            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Cadence ST zone
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for CadenceST target");
            Assert.AreEqual(cadenceSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid CadenceST target extension serialization");

            // Speed ST zone
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for SpeedST target");
            Assert.AreEqual(speedSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid SpeedST target extension serialization");

            // HR ST zone
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget1", testDocument);

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget3", testDocument);
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget4", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 3, "Missing step extension node for HRST target");
            Assert.AreEqual(hrSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 3].OuterXml,
                            "Invalid HRST target extension serialization");
            Assert.AreEqual(hrSTTargetExtensionResult2,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid HRST target extension serialization");
            Assert.AreEqual(placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].LastChild.FirstChild.InnerText,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 2].LastChild.FirstChild.InnerText,
                            "Matching HRST target extension are not identical");
            Assert.AreNotEqual(placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].LastChild.FirstChild.InnerText,
                               placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 3].LastChild.FirstChild.InnerText,
                               "Mismatching HRST target extension are identical");

            // Power ST zone
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for PowerST target");
            Assert.AreEqual(powerSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid PowerST target extension serialization");
        }
        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 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();
        }
        public static String GetStepIntensityText(RegularStep.StepIntensity intensity)
        {
            switch (intensity)
            {
                case RegularStep.StepIntensity.Active:
                    {
                        return GarminFitnessView.GetLocalizedString("ActiveText");
                    }
                case RegularStep.StepIntensity.Cooldown:
                    {
                        return GarminFitnessView.GetLocalizedString("CooldownText");
                    }
                case RegularStep.StepIntensity.Warmup:
                    {
                        return GarminFitnessView.GetLocalizedString("WarmupText");
                    }
                case RegularStep.StepIntensity.Rest:
                    {
                        return GarminFitnessView.GetLocalizedString("RestText");
                    }
            }

            return null;
        }
Beispiel #29
0
 void OnDurationChanged(RegularStep modifiedStep, IDuration durationChanged, PropertyChangedEventArgs changedProperty)
 {
     if (ConcreteWorkout.StepDurationChanged != null)
     {
         ConcreteWorkout.StepDurationChanged(this, modifiedStep, durationChanged, changedProperty);
     }
 }
        public void TestTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            IDuration loadedDuration = null;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            XmlAttribute attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // Lap button
            readNode.InnerXml = lapDurationResult;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Lap duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is LapButtonDuration, "Lap duration wasn't deserialized as proper type");

            // Time
            readNode.InnerXml = timeDurationResult1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Time duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is TimeDuration, "Time duration wasn't deserialized as proper type");
            TimeDuration timeDuration = loadedDuration as TimeDuration;
            Assert.AreEqual(500, timeDuration.TimeInSeconds, "Time duration didn't deserialize the proper time");

            readNode.InnerXml = timeDurationResult2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Time duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is TimeDuration, "Time duration wasn't deserialized as proper type");
            timeDuration = loadedDuration as TimeDuration;
            Assert.AreEqual(300, timeDuration.TimeInSeconds, "Time duration didn't deserialize the proper time");

            // Distance
            readNode.InnerXml = distanceDurationResult1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Distance duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is DistanceDuration, "Distance duration wasn't deserialized as proper type");
            DistanceDuration distanceDuration = loadedDuration as DistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Kilometer),
                            "Distance duration didn't deserialize the proper distance");

            readNode.InnerXml = distanceDurationResult2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Distance duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is DistanceDuration, "Distance duration wasn't deserialized as proper type");
            distanceDuration = loadedDuration as DistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Mile),
                            0.5,
                            "Distance duration didn't deserialize the proper distance");

            // Calories
            readNode.InnerXml = caloriesDurationResult1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Calories duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is CaloriesDuration, "Calories duration wasn't deserialized as proper type");
            CaloriesDuration caloriesDuration = loadedDuration as CaloriesDuration;
            Assert.AreEqual(550, caloriesDuration.CaloriesToSpend, "Calories duration didn't deserialize the proper calories");

            readNode.InnerXml = caloriesDurationResult2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "Calories duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is CaloriesDuration, "Calories duration wasn't deserialized as proper type");
            caloriesDuration = loadedDuration as CaloriesDuration;
            Assert.AreEqual(100, caloriesDuration.CaloriesToSpend, "Calories duration didn't deserialize the proper calories");

            // HR Above
            readNode.InnerXml = hrAboveDurationResult1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRAbove duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateAboveDuration, "HRAbove duration wasn't deserialized as proper type");
            HeartRateAboveDuration hrAboveDuration = loadedDuration as HeartRateAboveDuration;
            Assert.IsFalse(hrAboveDuration.IsPercentageMaxHeartRate, "HRAbove duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(160, hrAboveDuration.MaxHeartRate, "HRAbove duration didn't deserialize the proper hr");

            readNode.InnerXml = hrAboveDurationResult2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRAbove duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateAboveDuration, "HRAbove duration wasn't deserialized as proper type");
            hrAboveDuration = loadedDuration as HeartRateAboveDuration;
            Assert.IsFalse(hrAboveDuration.IsPercentageMaxHeartRate, "HRAbove duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(130, hrAboveDuration.MaxHeartRate, "HRAbove duration didn't deserialize the proper hr");

            readNode.InnerXml = hrAboveDurationResult3;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRAbove duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateAboveDuration, "HRAbove duration wasn't deserialized as proper type");
            hrAboveDuration = loadedDuration as HeartRateAboveDuration;
            Assert.IsTrue(hrAboveDuration.IsPercentageMaxHeartRate, "HRAbove duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(50, hrAboveDuration.MaxHeartRate, "HRAbove duration didn't deserialize the proper hr");

            readNode.InnerXml = hrAboveDurationResult4;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRAbove duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateAboveDuration, "HRAbove duration wasn't deserialized as proper type");
            hrAboveDuration = loadedDuration as HeartRateAboveDuration;
            Assert.IsTrue(hrAboveDuration.IsPercentageMaxHeartRate, "HRAbove duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(70, hrAboveDuration.MaxHeartRate, "HRAbove duration didn't deserialize the proper hr");

            // HR Below
            readNode.InnerXml = hrBelowDurationResult1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRBelow duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateBelowDuration, "HRBelow duration wasn't deserialized as proper type");
            HeartRateBelowDuration hrBelowDuration = loadedDuration as HeartRateBelowDuration;
            Assert.IsFalse(hrBelowDuration.IsPercentageMaxHeartRate, "HRBelow duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(160, hrBelowDuration.MinHeartRate, "HRBelow duration didn't deserialize the proper hr");

            readNode.InnerXml = hrBelowDurationResult2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRBelow duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateBelowDuration, "HRBelow duration wasn't deserialized as proper type");
            hrBelowDuration = loadedDuration as HeartRateBelowDuration;
            Assert.IsFalse(hrBelowDuration.IsPercentageMaxHeartRate, "HRBelow duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(130, hrBelowDuration.MinHeartRate, "HRBelow duration didn't deserialize the proper hr");

            readNode.InnerXml = hrBelowDurationResult3;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRBelow duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateBelowDuration, "HRBelow duration wasn't deserialized as proper type");
            hrBelowDuration = loadedDuration as HeartRateBelowDuration;
            Assert.IsTrue(hrBelowDuration.IsPercentageMaxHeartRate, "HRBelow duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(50, hrBelowDuration.MinHeartRate, "HRBelow duration didn't deserialize the proper hr");

            readNode.InnerXml = hrBelowDurationResult4;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedDuration, "HRBelow duration wasn't properly deserialized");
            Assert.IsTrue(loadedDuration is HeartRateBelowDuration, "HRBelow duration wasn't deserialized as proper type");
            hrBelowDuration = loadedDuration as HeartRateBelowDuration;
            Assert.IsTrue(hrBelowDuration.IsPercentageMaxHeartRate, "HRBelow duration didn't deserialize the proper hr %Max");
            Assert.AreEqual(70, hrBelowDuration.MinHeartRate, "HRBelow duration didn't deserialize the proper hr");

            // Invalid duration
            readNode.InnerXml = invalidDuration1;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNull(loadedDuration, "Invalid duration was properly deserialized");

            readNode.InnerXml = invalidDuration2;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNull(loadedDuration, "Empty type duration was properly deserialized");

            readNode.InnerXml = invalidDuration3;
            loadedDuration = DurationFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNull(loadedDuration, "No type duration was properly deserialized");
        }