public static void Stop() { var now = new DateTime(System.DateTime.Now); TerminateLap(); sessionMesg.SetTimestamp(now); sessionMesg.SetSport(Sport.Cycling); sessionMesg.SetSubSport(SubSport.VirtualActivity); sessionMesg.SetTotalDistance(State.TripTotalKm * 1000); sessionMesg.SetTotalElapsedTime(State.TripTotalTime); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(numLaps); sessionMesg.SetEvent(Event.Session); sessionMesg.SetEventType(EventType.Stop); sessionMesg.SetEventGroup(0); activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(now); activityMesg.SetTotalTimerTime(State.TripTotalTime); activityMesg.SetNumSessions(1); activityMesg.SetType(Activity.Manual); activityMesg.SetEvent(Event.Activity); activityMesg.SetEventType(EventType.Stop); activityMesg.SetEventGroup(0); encoder.Write(sessionMesg); encoder.Write(activityMesg); encoder.Close(); fitDest.Close(); }
public static void Stop(DateTime?time = null) { Trace.TraceInformation("Stop()"); var now = time ?? DateTime.UtcNow; TerminateLap(time); if (!isPaused) { Pause(time); } sessionMesg.SetTimestamp(new Dynastream.Fit.DateTime(now)); sessionMesg.SetSport(Sport.Cycling); sessionMesg.SetSubSport(SubSport.VirtualActivity); sessionMesg.SetTotalDistance(totalDistance); sessionMesg.SetTotalElapsedTime((float)(now - startTime).TotalSeconds); sessionMesg.SetTotalTimerTime((float)totalTimerTime.TotalSeconds); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(numLaps); sessionMesg.SetEvent(Event.Session); sessionMesg.SetEventType(EventType.Stop); sessionMesg.SetEventGroup(0); sessionMesg.SetStartTime(new Dynastream.Fit.DateTime(startTime)); activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(new Dynastream.Fit.DateTime(now)); activityMesg.SetTotalTimerTime((float)totalTimerTime.TotalSeconds); activityMesg.SetNumSessions(1); activityMesg.SetType(Activity.Manual); activityMesg.SetEvent(Event.Activity); activityMesg.SetEventType(EventType.Stop); activityMesg.SetEventGroup(0); encoder.Write(sessionMesg); encoder.Write(activityMesg); encoder.Close(); fitDest.Close(); csvFile.Close(); }
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)); }
/// <summary> /// Encode to a fit file /// </summary> /// <param name="db"></param> /// <param name="outputFileName"></param> public static void EncodeActivityFile(TrainingCenterDatabase_t db, string outputFileName) { /* * string assemblyFilePath = Assembly.GetExecutingAssembly().GetName().CodeBase; * string assemblyPath = assemblyFilePath.Replace(SysPath.GetFileName(assemblyFilePath), ""); */ if (string.IsNullOrEmpty(outputFileName)) { outputFileName = "out"; } //outputFileName = assemblyPath + outputFileName; // write FIT file Encode encoder = new Encode(ProtocolVersion.V20); SysFileStream fitDest = new SysFileStream(outputFileName + ".fit", SysFileMode.Create, SysFileAccess.ReadWrite, SysFileShare.Read); // Write our header encoder.Open(fitDest); // FIT file_id message FileIdMesg fileIdMesg = new FileIdMesg(); fileIdMesg.SetType(File.Activity); // Activity File = 4 fileIdMesg.SetManufacturer(Manufacturer.Garmin); fileIdMesg.SetProduct(GarminProduct.Fr935); fileIdMesg.SetSerialNumber(3949668594); fileIdMesg.SetTimeCreated(new DateTime(db.Activities.Activity[0].Id)); // set from input file // Encode each message, a definition message is automatically generated and output if necessary encoder.OnMesgDefinition(new MesgDefinition(fileIdMesg)); encoder.OnMesg(fileIdMesg); Utils.LogMessage(LogType.Information, "Wrote FileID"); // FIT FileCreator FileCreatorMesg fileCreatorMesg = new FileCreatorMesg(); fileCreatorMesg.SetSoftwareVersion(600); // Garmin Connect encoder.OnMesgDefinition(new MesgDefinition(fileCreatorMesg)); encoder.OnMesg(fileCreatorMesg); Utils.LogMessage(LogType.Information, "Wrote FileCreatorMesg"); // FIT event message : not found in TCX EventMesg eventMesg = new EventMesg(); eventMesg.SetTimestamp(new DateTime(db.Activities.Activity[0].Id)); eventMesg.SetData(0); eventMesg.SetEvent(Event.Timer); eventMesg.SetEventType(EventType.Start); eventMesg.SetEventGroup(0); encoder.OnMesgDefinition(new MesgDefinition(eventMesg)); encoder.OnMesg(eventMesg); Utils.LogMessage(LogType.Information, "Wrote EventMesg"); // FIT deviceInfo message: not found in TCX DeviceInfoMesg devInfoMesg = new DeviceInfoMesg(); devInfoMesg.SetTimestamp(new DateTime(db.Activities.Activity[0].Id)); devInfoMesg.SetSerialNumber(3949668594); devInfoMesg.SetManufacturer(Manufacturer.Garmin); devInfoMesg.SetProduct(GarminProduct.Fr935); devInfoMesg.SetSoftwareVersion(6); devInfoMesg.SetDeviceIndex(0); devInfoMesg.SetSourceType(SourceType.Local); for (int i = 0; i < 4; i++) { encoder.OnMesgDefinition(new MesgDefinition(devInfoMesg)); encoder.OnMesg(devInfoMesg); Utils.LogMessage(LogType.Information, "Wrote DeviceInfoMesg"); } // FIT deviceSettings message: not found in TCX DeviceSettingsMesg devSettingsMesg = new DeviceSettingsMesg(); devSettingsMesg.SetUtcOffset(0); devSettingsMesg.SetTimeOffset(7, 0); devSettingsMesg.SetAutoActivityDetect(0); devSettingsMesg.SetAutosyncMinSteps(2000); devSettingsMesg.SetAutosyncMinTime(240); devSettingsMesg.SetActiveTimeZone(0); devSettingsMesg.SetActivityTrackerEnabled(Bool.True); devSettingsMesg.SetMountingSide(Side.Left); devSettingsMesg.SetTimeMode(1, TimeMode.Utc); encoder.OnMesgDefinition(new MesgDefinition(devSettingsMesg)); encoder.OnMesg(devSettingsMesg); Utils.LogMessage(LogType.Information, "Wrote DeviceSettingsMesg"); // FIT UserProfile message: : not found in TCX UserProfileMesg userProfileMesg = new UserProfileMesg(); userProfileMesg.SetActivityClass(ActivityClass.Level); encoder.OnMesgDefinition(new MesgDefinition(userProfileMesg)); encoder.OnMesg(userProfileMesg); Utils.LogMessage(LogType.Information, "Wrote UserProfileMesg"); // FIT Sport: SportMesg sportMesg = new SportMesg(); sportMesg.SetSport(Sport.Running); sportMesg.SetSubSport(SubSport.Road); // Encode each message, a definition message is automatically generated and output if necessary encoder.OnMesgDefinition(new MesgDefinition(sportMesg)); encoder.OnMesg(sportMesg); Utils.LogMessage(LogType.Information, "Wrote SportMesg"); // create FIT record and lap message double totalTime = 0; foreach (Activity_t act in db.Activities.Activity) { foreach (ActivityLap_t lap in act.Lap) { List <RecordMesg> records = new List <RecordMesg>(); // FIT Record message: foreach (Trackpoint_t trackPoint in lap.Track) { RecordMesg recMesg = new RecordMesg(); recMesg.SetTimestamp(new DateTime(trackPoint.Time)); recMesg.SetPositionLat(Utils.ConvertTcxLatLongToFit(trackPoint.Position.LatitudeDegrees)); recMesg.SetPositionLong(Utils.ConvertTcxLatLongToFit(trackPoint.Position.LongitudeDegrees)); recMesg.SetDistance((float)trackPoint.DistanceMeters); recMesg.SetAltitude((float)trackPoint.AltitudeMeters); //recMesg.SetSpeed((float)trackPoint.Extensions.Any["Speed"]); if (trackPoint.HeartRateBpm != null) { recMesg.SetHeartRate((byte)trackPoint.HeartRateBpm.Value); } // Extension if (trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:RunCadence") != null) { if (trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:RunCadence").Count == 1) { int cadence = 0; int.TryParse((trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:RunCadence")[0].InnerText), out cadence); recMesg.SetCadence((byte)cadence); } } if (trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:Speed") != null) { if (trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:Speed").Count == 1) { double speed = 0; double.TryParse((trackPoint.Extensions.Any[0].GetElementsByTagName("ns3:Speed")[0].InnerText), out speed); recMesg.SetSpeed((float)speed); } } encoder.OnMesgDefinition(new MesgDefinition(recMesg)); encoder.OnMesg(recMesg); Utils.LogMessage(LogType.Information, string.Format("Wrote RecMesg: {0}", recMesg.GetTimestamp().ToString())); } LapMesg lapMesg = new LapMesg(); lapMesg.SetTimestamp(new DateTime(lap.StartTime)); lapMesg.SetStartTime(new DateTime(lap.StartTime)); lapMesg.SetTotalMovingTime((float)lap.TotalTimeSeconds); lapMesg.SetStartPositionLat(Utils.ConvertTcxLatLongToFit(lap.Track[0].Position.LatitudeDegrees)); lapMesg.SetStartPositionLong(Utils.ConvertTcxLatLongToFit(lap.Track[0].Position.LongitudeDegrees)); lapMesg.SetSport(Sport.Running); lapMesg.SetSubSport(SubSport.Road); // TODO: EndPosition lapMesg.SetTotalElapsedTime((float)lap.TotalTimeSeconds); totalTime += lap.TotalTimeSeconds; // TODO: The rest encoder.OnMesgDefinition(new MesgDefinition(lapMesg)); encoder.OnMesg(lapMesg); Utils.LogMessage(LogType.Information, "Wrote LapMesg"); } } // FIT Session: not found in tcx // FIT Activity ActivityMesg activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(new DateTime(db.Activities.Activity[0].Id)); activityMesg.SetTotalTimerTime((float)totalTime); activityMesg.SetLocalTimestamp(new DateTime(db.Activities.Activity[0].Id).GetTimeStamp() + (uint)totalTime); activityMesg.SetNumSessions(1); activityMesg.SetType(Activity.Manual); activityMesg.SetEvent(Event.Activity); activityMesg.SetEventType(EventType.Stop); //activityMesg.SetTotalTimerTime(db.Activities.Activity[0].Training.); encoder.OnMesgDefinition(new MesgDefinition(activityMesg)); encoder.OnMesg(activityMesg); Utils.LogMessage(LogType.Information, "Wrote ActivityMesg"); // Update header datasize and file CRC encoder.Close(); fitDest.Close(); Console.WriteLine("Encoded FIT file " + outputFileName + ".fit"); }
static public void CreateTimeBasedActivity() { const double TwoPI = Math.PI * 2.0; const double SemicirclesPerMeter = 107.173; const string FileName = "ActivityEncodeRecipe.fit"; var messages = new List <Mesg>(); // The starting timestamp for the activity var startTime = new Dynastream.Fit.DateTime(System.DateTime.UtcNow); // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStart = new EventMesg(); eventMesgStart.SetTimestamp(startTime); eventMesgStart.SetEvent(Event.Timer); eventMesgStart.SetEventType(EventType.Start); messages.Add(eventMesgStart); // Create the Developer Id message for the developer data fields. var developerIdMesg = new DeveloperDataIdMesg(); // It is a BEST PRACTICE to reuse the same Guid for all FIT files created by your platform byte[] appId = new Guid("00010203-0405-0607-0809-0A0B0C0D0E0F").ToByteArray(); for (int i = 0; i < appId.Length; i++) { developerIdMesg.SetApplicationId(i, appId[i]); } developerIdMesg.SetDeveloperDataIndex(0); developerIdMesg.SetApplicationVersion(110); messages.Add(developerIdMesg); // Create the Developer Data Field Descriptions var doughnutsFieldDescMesg = new FieldDescriptionMesg(); doughnutsFieldDescMesg.SetDeveloperDataIndex(0); doughnutsFieldDescMesg.SetFieldDefinitionNumber(0); doughnutsFieldDescMesg.SetFitBaseTypeId(FitBaseType.Float32); doughnutsFieldDescMesg.SetFieldName(0, "Doughnuts Earned"); doughnutsFieldDescMesg.SetUnits(0, "doughnuts"); doughnutsFieldDescMesg.SetNativeMesgNum(MesgNum.Session); messages.Add(doughnutsFieldDescMesg); FieldDescriptionMesg hrFieldDescMesg = new FieldDescriptionMesg(); hrFieldDescMesg.SetDeveloperDataIndex(0); hrFieldDescMesg.SetFieldDefinitionNumber(1); hrFieldDescMesg.SetFitBaseTypeId(FitBaseType.Uint8); hrFieldDescMesg.SetFieldName(0, "Heart Rate"); hrFieldDescMesg.SetUnits(0, "bpm"); hrFieldDescMesg.SetNativeFieldNum(RecordMesg.FieldDefNum.HeartRate); hrFieldDescMesg.SetNativeMesgNum(MesgNum.Record); messages.Add(hrFieldDescMesg); // Every FIT ACTIVITY file MUST contain Record messages var timestamp = new Dynastream.Fit.DateTime(startTime); // Create one hour (3600 seconds) of Record data for (uint i = 0; i <= 3600; i++) { // Create a new Record message and set the timestamp var recordMesg = new RecordMesg(); recordMesg.SetTimestamp(timestamp); // Fake Record Data of Various Signal Patterns recordMesg.SetDistance(i); // Ramp recordMesg.SetSpeed(1); // Flatline recordMesg.SetHeartRate((byte)((Math.Sin(TwoPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine recordMesg.SetCadence((byte)(i % 255)); // Sawtooth recordMesg.SetPower((ushort)((i % 255) < 127 ? 150 : 250)); // Square recordMesg.SetAltitude((float)Math.Abs(((double)i % 255.0) - 127.0)); // Triangle recordMesg.SetPositionLat(0); recordMesg.SetPositionLong((int)Math.Round(i * SemicirclesPerMeter)); // Add a Developer Field to the Record Message var hrDevField = new DeveloperField(hrFieldDescMesg, developerIdMesg); recordMesg.SetDeveloperField(hrDevField); hrDevField.SetValue((byte)((Math.Sin(TwoPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine // Write the Rercord message to the output stream messages.Add(recordMesg); // Increment the timestamp by one second timestamp.Add(1); } // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStop = new EventMesg(); eventMesgStop.SetTimestamp(timestamp); eventMesgStop.SetEvent(Event.Timer); eventMesgStop.SetEventType(EventType.StopAll); messages.Add(eventMesgStop); // Every FIT ACTIVITY file MUST contain at least one Lap message var lapMesg = new LapMesg(); lapMesg.SetMessageIndex(0); lapMesg.SetTimestamp(timestamp); lapMesg.SetStartTime(startTime); lapMesg.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); lapMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); messages.Add(lapMesg); // Every FIT ACTIVITY file MUST contain at least one Session message var sessionMesg = new SessionMesg(); sessionMesg.SetMessageIndex(0); sessionMesg.SetTimestamp(timestamp); sessionMesg.SetStartTime(startTime); sessionMesg.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); sessionMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); sessionMesg.SetSport(Sport.StandUpPaddleboarding); sessionMesg.SetSubSport(SubSport.Generic); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(1); // Add a Developer Field to the Session message var doughnutsEarnedDevField = new DeveloperField(doughnutsFieldDescMesg, developerIdMesg); doughnutsEarnedDevField.SetValue(sessionMesg.GetTotalElapsedTime() / 1200.0f); sessionMesg.SetDeveloperField(doughnutsEarnedDevField); messages.Add(sessionMesg); // Every FIT ACTIVITY file MUST contain EXACTLY one Activity message var activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(timestamp); activityMesg.SetNumSessions(1); var timezoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalSeconds; activityMesg.SetLocalTimestamp((uint)((int)timestamp.GetTimeStamp() + timezoneOffset)); activityMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); messages.Add(activityMesg); CreateActivityFile(messages, FileName, startTime); }
static public void CreateLapSwimActivity() { // Example Swim Data representing a 500 yard pool swim using different strokes and drills. var swimData = new List <Dictionary <string, object> >() { new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Freestyle" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Freestyle" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Freestyle" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Freestyle" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Backstroke" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Backstroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Backstroke" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Backstroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Breaststroke" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Breaststroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Breaststroke" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Breaststroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Butterfly" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Butterfly" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Butterfly" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Butterfly" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Lap" } }, }; const string FileName = "ActivityEncodeRecipeLapSwim.fit"; var messages = new List <Mesg>(); // The starting timestamp for the activity var startTime = new Dynastream.Fit.DateTime(System.DateTime.UtcNow); // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStart = new EventMesg(); eventMesgStart.SetTimestamp(startTime); eventMesgStart.SetEvent(Event.Timer); eventMesgStart.SetEventType(EventType.Start); messages.Add(eventMesgStart); // // Create a Length or Lap message for each item in the sample swim data. Calculate // distance, duration, and stroke count for each lap and the overall session. // // Session Accumulators uint sessionTotalElapsedTime = 0; float sessionDistance = 0; ushort sessionNumLengths = 0; ushort sessionNumActiveLengths = 0; ushort sessionTotalStrokes = 0; ushort sessionNumLaps = 0; // Lap accumulators uint lapTotalElapsedTime = 0; float lapDistance = 0; ushort lapNumActiveLengths = 0; ushort lapNumLengths = 0; ushort lapFirstLengthIndex = 0; ushort lapTotalStrokes = 0; var lapStartTime = new Dynastream.Fit.DateTime(startTime); var poolLength = 22.86f; var poolLengthUnit = DisplayMeasure.Statute; var timestamp = new Dynastream.Fit.DateTime(startTime); ushort messageIndex = 0; foreach (var swimLength in swimData) { string type = (string)swimLength["type"]; if (type.Equals("Lap")) { // Create a Lap message, set its fields, and write it to the file var lapMesg = new LapMesg(); lapMesg.SetMessageIndex(sessionNumLaps); lapMesg.SetTimestamp(timestamp); lapMesg.SetStartTime(lapStartTime); lapMesg.SetTotalElapsedTime(lapTotalElapsedTime); lapMesg.SetTotalTimerTime(lapTotalElapsedTime); lapMesg.SetTotalDistance(lapDistance); lapMesg.SetFirstLengthIndex(lapFirstLengthIndex); lapMesg.SetNumActiveLengths(lapNumActiveLengths); lapMesg.SetNumLengths(lapNumLengths); lapMesg.SetTotalStrokes(lapTotalStrokes); lapMesg.SetAvgStrokeDistance(lapDistance / lapTotalStrokes); lapMesg.SetSport(Sport.Swimming); lapMesg.SetSubSport(SubSport.LapSwimming); messages.Add(lapMesg); sessionNumLaps++; // Reset the Lap accumulators lapFirstLengthIndex = messageIndex; lapNumActiveLengths = 0; lapNumLengths = 0; lapTotalElapsedTime = 0; lapDistance = 0; lapTotalStrokes = 0; lapStartTime = new Dynastream.Fit.DateTime(timestamp); } else { uint duration = (uint)swimLength["duration"]; var lengthType = (LengthType)Enum.Parse(typeof(LengthType), type); // Create a Length message and its fields var lengthMesg = new LengthMesg(); lengthMesg.SetMessageIndex(messageIndex++); lengthMesg.SetStartTime(timestamp); lengthMesg.SetTotalElapsedTime(duration); lengthMesg.SetTotalTimerTime(duration); lengthMesg.SetLengthType(lengthType); timestamp.Add(duration); lengthMesg.SetTimestamp(timestamp); // Create the Record message that pairs with the Length Message var recordMesg = new RecordMesg(); recordMesg.SetTimestamp(timestamp); recordMesg.SetDistance(sessionDistance + poolLength); // Is this an Active Length? if (lengthType == LengthType.Active) { // Get the Active data from the model string stroke = swimLength.ContainsKey("stroke") ? (String)swimLength["stroke"] : "Freestyle"; uint strokes = swimLength.ContainsKey("strokes") ? (uint)swimLength["strokes"] : 0; SwimStroke swimStroke = (SwimStroke)Enum.Parse(typeof(SwimStroke), stroke); // Set the Active data on the Length Message lengthMesg.SetAvgSpeed(poolLength / (float)duration); lengthMesg.SetSwimStroke(swimStroke); if (strokes > 0) { lengthMesg.SetTotalStrokes((ushort)strokes); lengthMesg.SetAvgSwimmingCadence((byte)(strokes * 60U / duration)); } // Set the Active data on the Record Message recordMesg.SetSpeed(poolLength / (float)duration); if (strokes > 0) { recordMesg.SetCadence((byte)((strokes * 60U) / duration)); } // Increment the "Active" accumulators sessionNumActiveLengths++; lapNumActiveLengths++; sessionDistance += poolLength; lapDistance += poolLength; sessionTotalStrokes += (ushort)strokes; lapTotalStrokes += (ushort)strokes; } // Write the messages to the file messages.Add(recordMesg); messages.Add(lengthMesg); // Increment the "Total" accumulators sessionTotalElapsedTime += duration; lapTotalElapsedTime += duration; sessionNumLengths++; lapNumLengths++; } } // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStop = new EventMesg(); eventMesgStop.SetTimestamp(timestamp); eventMesgStop.SetEvent(Event.Timer); eventMesgStop.SetEventType(EventType.StopAll); messages.Add(eventMesgStop); // Every FIT ACTIVITY file MUST contain at least one Session message var sessionMesg = new SessionMesg(); sessionMesg.SetMessageIndex(0); sessionMesg.SetTimestamp(timestamp); sessionMesg.SetStartTime(startTime); sessionMesg.SetTotalElapsedTime(sessionTotalElapsedTime); sessionMesg.SetTotalTimerTime(sessionTotalElapsedTime); sessionMesg.SetTotalDistance(sessionDistance); sessionMesg.SetSport(Sport.Swimming); sessionMesg.SetSubSport(SubSport.LapSwimming); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(sessionNumLaps); sessionMesg.SetPoolLength(poolLength); sessionMesg.SetPoolLengthUnit(poolLengthUnit); sessionMesg.SetNumLengths(sessionNumLengths); sessionMesg.SetNumActiveLengths(sessionNumActiveLengths); sessionMesg.SetTotalStrokes(sessionTotalStrokes); sessionMesg.SetAvgStrokeDistance(sessionDistance / sessionTotalStrokes); messages.Add(sessionMesg); // Every FIT ACTIVITY file MUST contain EXACTLY one Activity message var activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(timestamp); activityMesg.SetNumSessions(1); var timezoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalSeconds; activityMesg.SetLocalTimestamp((uint)((int)timestamp.GetTimeStamp() + timezoneOffset)); activityMesg.SetTotalTimerTime(sessionTotalElapsedTime); messages.Add(activityMesg); CreateActivityFile(messages, FileName, startTime); }
/// <summary> /// Writes the data to a FIT file. /// </summary> /// <param name="fileName">Name of the FIT file to write to.</param> /// <param name="start">Start date/time of the activity.</param> /// <param name="laps">Lap and record data to be written.</param> /// <param name="calories">Calories used for the activity.</param> /// <param name="work">Work done for the activity.</param> static void WriteFitFile(string fileName, System.DateTime start, LapsList laps, int calories, uint work) { // open the encoder and stream Encode encoder = new Encode(ProtocolVersion.V20); FileStream fitStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); encoder.Open(fitStream); // write the file ID message FileIdMesg fileIdMsg = new FileIdMesg(); fileIdMsg.SetType(Dynastream.Fit.File.Activity); fileIdMsg.SetManufacturer(Manufacturer.StagesCycling); fileIdMsg.SetProduct(3); fileIdMsg.SetSerialNumber(1); fileIdMsg.SetTimeCreated(new Dynastream.Fit.DateTime(start)); encoder.Write(fileIdMsg); // write the record and lap messages foreach (Lap lap in laps) { // write the record messages foreach (Record record in lap.Records) { RecordMesg recordMsg = new RecordMesg(); recordMsg.SetTimestamp(new Dynastream.Fit.DateTime(record.Time.ToUniversalTime())); recordMsg.SetHeartRate((byte)record.HeartRate); recordMsg.SetCadence((byte)record.Cadence); recordMsg.SetDistance((float)record.Distance * 1000); recordMsg.SetSpeed((float)(record.Speed / 3.6)); recordMsg.SetPower((ushort)record.Power); encoder.Write(recordMsg); } // write the lap message Record first = lap.Records[0]; Record last = lap.Records[lap.Records.Count - 1]; TimeSpan time = last.Time - first.Time; LapMesg lapMsg = new LapMesg(); lapMsg.SetTimestamp(new Dynastream.Fit.DateTime(last.Time.ToUniversalTime())); lapMsg.SetStartTime(new Dynastream.Fit.DateTime(first.Time.ToUniversalTime())); lapMsg.SetTotalElapsedTime((int)time.TotalSeconds); lapMsg.SetTotalTimerTime((int)time.TotalSeconds); lapMsg.SetTotalDistance((float)(last.Distance - first.Distance) * 1000); lapMsg.SetTotalCalories((ushort)lap.Calories); lapMsg.SetTotalWork(lap.Work * 1000); lapMsg.SetEvent(Event.Lap); lapMsg.SetEventType(EventType.Stop); lapMsg.SetIntensity(Intensity.Active); lapMsg.SetLapTrigger(LapTrigger.Manual); lapMsg.SetSport(Sport.Cycling); Summary lapSummary = GetLapSummary(lap); lapMsg.SetAvgCadence((byte)lapSummary.AveCadence); lapMsg.SetMaxCadence((byte)lapSummary.MaxCadence); lapMsg.SetAvgHeartRate((byte)lapSummary.AveHeartRate); lapMsg.SetMaxHeartRate((byte)lapSummary.MaxHeartRate); lapMsg.SetAvgPower((ushort)lapSummary.AvePower); lapMsg.SetMaxPower((ushort)lapSummary.MaxPower); lapMsg.SetAvgSpeed((float)lapSummary.AveSpeed / 3.6f); lapMsg.SetMaxSpeed((float)lapSummary.MaxSpeed / 3.6f); encoder.Write(lapMsg); } // get the first and last records Record firstRecord = laps[0].Records[0]; Lap lastLap = laps[laps.Count - 1]; Record lastRecord = lastLap.Records[lastLap.Records.Count - 1]; TimeSpan totalTime = lastRecord.Time - firstRecord.Time; // write the session message SessionMesg sessionMsg = new SessionMesg(); sessionMsg.SetTimestamp(new Dynastream.Fit.DateTime(lastRecord.Time.ToUniversalTime())); sessionMsg.SetStartTime(new Dynastream.Fit.DateTime(firstRecord.Time.ToUniversalTime())); sessionMsg.SetTotalElapsedTime((int)totalTime.TotalSeconds); sessionMsg.SetTotalTimerTime((int)totalTime.TotalSeconds); sessionMsg.SetTotalDistance((float)(lastRecord.Distance - firstRecord.Distance) * 1000); sessionMsg.SetTotalCalories((ushort)calories); sessionMsg.SetTotalWork(work * 1000); sessionMsg.SetFirstLapIndex(0); sessionMsg.SetNumLaps((ushort)laps.Count); sessionMsg.SetEvent(Event.Session); sessionMsg.SetEventType(EventType.Stop); sessionMsg.SetSport(Sport.Cycling); sessionMsg.SetSubSport(SubSport.Spin); Summary sessionSummary = GetSessionSummary(laps); sessionMsg.SetAvgCadence((byte)sessionSummary.AveCadence); sessionMsg.SetMaxCadence((byte)sessionSummary.MaxCadence); sessionMsg.SetAvgHeartRate((byte)sessionSummary.AveHeartRate); sessionMsg.SetMaxHeartRate((byte)sessionSummary.MaxHeartRate); sessionMsg.SetAvgPower((ushort)sessionSummary.AvePower); sessionMsg.SetMaxPower((ushort)sessionSummary.MaxPower); sessionMsg.SetAvgSpeed((float)sessionSummary.AveSpeed / 3.6f); sessionMsg.SetMaxSpeed((float)sessionSummary.MaxSpeed / 3.6f); encoder.Write(sessionMsg); // write the activity message ActivityMesg activityMsg = new ActivityMesg(); activityMsg.SetTimestamp(new Dynastream.Fit.DateTime(lastRecord.Time.ToUniversalTime())); activityMsg.SetTotalTimerTime((int)totalTime.TotalSeconds); activityMsg.SetNumSessions(1); activityMsg.SetType(Activity.Manual); activityMsg.SetEvent(Event.Activity); activityMsg.SetEventType(EventType.Stop); encoder.Write(activityMsg); // close the encoder and stream encoder.Close(); fitStream.Close(); }