protected override Tuple <string, ICollection <Mesg> > Convert(Workout workout, WorkoutSamples workoutSamples) { // MESSAGE ORDER MATTERS var messages = new List <Mesg>(); var startTime = GetStartTimeUtc(workout); var endTime = GetEndTimeUtc(workout); var title = WorkoutHelper.GetTitle(workout); var sport = GetGarminSport(workout); var subSport = GetGarminSubSport(workout); if (sport == Sport.Invalid) { Log.Warning("Unsupported Sport Type - Skipping {@Sport}", workout.Fitness_Discipline); return(new Tuple <string, ICollection <Mesg> >(string.Empty, null)); } var fileIdMesg = new FileIdMesg(); fileIdMesg.SetSerialNumber(_serialNumber); fileIdMesg.SetTimeCreated(startTime); fileIdMesg.SetManufacturer(_manufacturerId); fileIdMesg.SetProduct(_productId); fileIdMesg.SetType(Dynastream.Fit.File.Activity); messages.Add(fileIdMesg); var eventMesg = new EventMesg(); eventMesg.SetTimestamp(startTime); eventMesg.SetData(0); eventMesg.SetEvent(Event.Timer); eventMesg.SetEventType(EventType.Start); eventMesg.SetEventGroup(0); messages.Add(eventMesg); var deviceInfoMesg = new DeviceInfoMesg(); deviceInfoMesg.SetTimestamp(startTime); deviceInfoMesg.SetSerialNumber(_serialNumber); deviceInfoMesg.SetManufacturer(_manufacturerId); deviceInfoMesg.SetProduct(_productId); deviceInfoMesg.SetSoftwareVersion(_softwareVersion); deviceInfoMesg.SetDeviceIndex(0); deviceInfoMesg.SetSourceType(SourceType.Local); deviceInfoMesg.SetProductName("PelotonToGarmin"); // Max 20 Chars messages.Add(deviceInfoMesg); var userProfileMesg = new UserProfileMesg(); userProfileMesg.SetPowerSetting(DisplayPower.PercentFtp); messages.Add(userProfileMesg); var sportMesg = new SportMesg(); sportMesg.SetSport(sport); sportMesg.SetSubSport(subSport); messages.Add(sportMesg); var zoneTargetMesg = new ZonesTargetMesg(); zoneTargetMesg.SetFunctionalThresholdPower((ushort)workout.Ftp_Info.Ftp); zoneTargetMesg.SetPwrCalcType(PwrZoneCalc.PercentFtp); var maxHr = GetUserMaxHeartRate(workoutSamples); if (maxHr is object) { zoneTargetMesg.SetMaxHeartRate(maxHr.Value); zoneTargetMesg.SetHrCalcType(HrZoneCalc.PercentMaxHr); } messages.Add(zoneTargetMesg); var trainingMesg = new TrainingFileMesg(); trainingMesg.SetTimestamp(startTime); trainingMesg.SetTimeCreated(startTime); trainingMesg.SetSerialNumber(_serialNumber); trainingMesg.SetManufacturer(_manufacturerId); trainingMesg.SetProduct(_productId); trainingMesg.SetType(Dynastream.Fit.File.Workout); messages.Add(trainingMesg); AddMetrics(messages, workoutSamples, startTime); var workoutSteps = new List <WorkoutStepMesg>(); var laps = new List <LapMesg>(); if (workoutSamples.Target_Performance_Metrics?.Target_Graph_Metrics?.FirstOrDefault(w => w.Type == "cadence")?.Graph_Data is object) { var stepsAndLaps = GetWorkoutStepsAndLaps(workoutSamples, startTime, sport, subSport); workoutSteps = stepsAndLaps.Values.Select(v => v.Item1).ToList(); laps = stepsAndLaps.Values.Select(v => v.Item2).ToList(); } else { laps = GetWorkoutLaps(workoutSamples, startTime, sport, subSport).ToList(); } var workoutMesg = new WorkoutMesg(); workoutMesg.SetWktName(title.Replace(_spaceSeparator, " ")); workoutMesg.SetCapabilities(32); workoutMesg.SetSport(sport); workoutMesg.SetSubSport(subSport); workoutMesg.SetNumValidSteps((ushort)workoutSteps.Count); messages.Add(workoutMesg); // add steps in order foreach (var step in workoutSteps) { messages.Add(step); } // Add laps in order foreach (var lap in laps) { messages.Add(lap); } messages.Add(GetSessionMesg(workout, workoutSamples, startTime, endTime, (ushort)laps.Count)); var activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(endTime); activityMesg.SetTotalTimerTime(workoutSamples.Duration); activityMesg.SetNumSessions(1); activityMesg.SetType(Activity.Manual); activityMesg.SetEvent(Event.Activity); activityMesg.SetEventType(EventType.Stop); var timezoneOffset = (int)TimeZoneInfo.Local.GetUtcOffset(base.GetEndTimeUtc(workout)).TotalSeconds; var timeStamp = (uint)((int)endTime.GetTimeStamp() + timezoneOffset); activityMesg.SetLocalTimestamp(timeStamp); messages.Add(activityMesg); return(new Tuple <string, ICollection <Mesg> >(title, messages)); }