Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[213] uplift to latest FitSDK - meditation mapped to breathwork now #222

Merged
merged 3 commits into from
Feb 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@ configuration.local.json
*/**/output/**
/src/WebApp/WebApp.csproj.user
/Output
/src/PelotonToGarminConsole/data
/src/WebApp/data
2 changes: 1 addition & 1 deletion src/Common/FileHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void Cleanup(string dir)
try
{
using var trace2 = Tracing.Trace("DeleteDir", "io");
_logger.Debug("Deleting directory.");
_logger.Debug("Deleting directory: {@Directory}", dir);
Directory.Delete(dir, recursive: true);
}
catch (Exception e)
Expand Down
3 changes: 2 additions & 1 deletion src/Conversion/FitConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,9 @@ private SubSport GetGarminSubSport(Workout workout)
case FitnessDiscipline.Stretching:
return SubSport.FlexibilityTraining;
case FitnessDiscipline.Yoga:
case FitnessDiscipline.Meditation:
return SubSport.Yoga;
case FitnessDiscipline.Meditation:
return SubSport.Breathing;
default:
return SubSport.Generic;
}
Expand Down
786 changes: 398 additions & 388 deletions src/Conversion/FitEncodeExample.cs

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp

// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-dotnettools.csharp",
"mechatroner.rainbow-csv"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [

]
}
27 changes: 27 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/ActivityDecode.dll",
"args": ["${workspaceFolder}/../../../examples/activity.fit"],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
42 changes: 42 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ActivityDecode.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/ActivityDecode.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/ActivityDecode.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
12 changes: 12 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/ActivityDecode.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dynastream.Fit.Portable">
<HintPath>..\..\Dynastream.Fit.Portable.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
96 changes: 96 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/ActivityParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
////////////////////////////////////////////////////////////////////////////////
// The following FIT Protocol software provided may be used with FIT protocol
// devices only and remains the copyrighted property of Garmin Canada Inc.
// The software is being provided on an "as-is" basis and as an accommodation,
// and therefore all warranties, representations, or guarantees of any kind
// (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed.
//
// Copyright 2020 Garmin International, Inc.
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using Dynastream.Fit;
using Extensions;

public class ActivityParser
{
private FitMessages _messages;
public bool IsActivityFile => _messages?.FileId != null ? (_messages?.FileId?.GetType() ?? File.Invalid) == File.Activity : false;

public ActivityParser(FitMessages messages)
{
_messages = messages;
}

public List<SessionMessages> ParseSessions()
{
if (!IsActivityFile)
{
throw new Exception($"Expected FIT File Type: Activity, recieved File Type: {_messages?.FileId?.GetType()}");
}

// When there are no Sessions but there are Records create a Session message to recover as much data as possible
if (_messages.Sessions.Count == 0 && _messages.Records.Count > 0)
{
Dynastream.Fit.DateTime startTime = _messages.Records[0].GetTimestamp();
Dynastream.Fit.DateTime timestamp = _messages.Records[_messages.Records.Count - 1].GetTimestamp();

var session = new SessionMesg();
session.SetStartTime(startTime);
session.SetTimestamp(timestamp);
session.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp());
session.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp());

_messages.Sessions.Add(session);
}

int recordsTaken = 0;

var sessions = new List<SessionMessages>(_messages.Sessions.Count);
foreach (SessionMesg sessionMesg in _messages.Sessions)
{
var session = new SessionMessages(sessionMesg)
{
Laps = _messages.Laps.Skip(sessionMesg.GetFirstLapIndex() ?? 0).Take(sessionMesg.GetNumLaps() ?? 0).ToList(),

ClimbPros = _messages.ClimbPros.Where(climb => climb.Within(sessionMesg)).ToList(),
Events = _messages.Events.Where(evt => evt.Within(sessionMesg)).ToList(),
DeviceInfos = _messages.DeviceInfos.Where(deviceInfo => deviceInfo.Within(sessionMesg)).ToList(),
Lengths = _messages.Lengths.Where(length => length.Overlaps(sessionMesg)).ToList(),
Records = _messages.Records.Skip(recordsTaken).Where(record => record.Within(sessionMesg)).ToList(),
SegmentLaps = _messages.SegmentLaps.Where(segmentLap => segmentLap.Overlaps(sessionMesg)).ToList(),

TimerEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.Timer && evt.Within(sessionMesg)).ToList(),
FrontGearChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.FrontGearChange && evt.Within(sessionMesg)).ToList(),
RearGearChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.RearGearChange && evt.Within(sessionMesg)).ToList(),
RiderPositionChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.RiderPositionChange && evt.Within(sessionMesg)).ToList(),

Activity = _messages.Activity,
FileId = _messages.FileId,
RecordFieldNames = _messages.RecordFieldNames,
RecordDeveloperFieldNames = _messages.RecordDeveloperFieldNames,
UserProfile = _messages.UserProfile,
Workout = _messages.Workout,
WorkoutSteps = _messages.WorkoutSteps,
ZonesTarget = _messages.ZonesTarget,
};

recordsTaken += session.Records.Count;
sessions.Add(session);
}

return sessions;
}

public List<DeviceInfoMesg> DevicesWhereBatteryStatusIsLow()
{
var batteryStatus = new List<byte>() { BatteryStatus.Critical, BatteryStatus.Low };
var deviceInfos = new List<DeviceInfoMesg>();

deviceInfos = _messages.DeviceInfos.Where(info => batteryStatus.Contains(info.GetBatteryStatus() ?? BatteryStatus.Unknown)).ToList();
return deviceInfos;
}
}
139 changes: 139 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/Export.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
////////////////////////////////////////////////////////////////////////////////
// The following FIT Protocol software provided may be used with FIT protocol
// devices only and remains the copyrighted property of Garmin Canada Inc.
// The software is being provided on an "as-is" basis and as an accommodation,
// and therefore all warranties, representations, or guarantees of any kind
// (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed.
//
// Copyright 2020 Garmin International, Inc.
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dynastream.Fit;
using Extensions;

public class Export
{
private const string Unknown = "unknown";
private const double MetersToYards = 1.09361;
private const double DefaultPoolLength = 25.0;

static public string RecordsToCSV(SessionMessages session)
{
var stringBuilder = new StringBuilder();

// Add a comment row with: Sport Type, Sub Sport, Date/Time, Total Distance (meters), Calories, Duration (seconds)
stringBuilder.AppendLine($"#Records,{session.Session.GetSport().ToString()},{session.Session.GetSubSport().ToString()},{session.Session.GetStartTime().GetDateTime().ToString("yyyy-MM-dd HH:mm:ss")},{session.Session.GetTotalDistance()},{session.Session.GetTotalCalories() ?? 0},{session.Session.GetTotalElapsedTime() ?? 0}");

// Create the header row
stringBuilder.Append("Seconds,");
stringBuilder.Append($"{string.Join(",", session.RecordFieldNames)},");

if (session.RecordDeveloperFieldNames.Count > 0)
{
stringBuilder.Append($"developerdata_{string.Join(",developerdata_", session.RecordDeveloperFieldNames).Replace(" ","_")},");
}

stringBuilder.Append("TimerEvent,Lap");
stringBuilder.AppendLine();

var lapQueue = new Queue<LapMesg>(session.Laps);
var lap = lapQueue.Count > 0 ? lapQueue.Dequeue() : null;
var lapId = 1;

uint firstTimeStamp = session.Records[0].GetTimestamp().GetTimeStamp();

foreach (ExtendedRecordMesg record in session.Records)
{
while (lap != null && record.GetTimestamp().GetTimeStamp() > lap.GetTimestamp().GetTimeStamp())
{
lap = lapQueue.Count > 0 ? lapQueue.Dequeue() : null;
lapId++;
}

stringBuilder.Append($"{record.GetTimestamp().GetTimeStamp() - firstTimeStamp},");

foreach (string fieldName in session.RecordFieldNames)
{
var numFieldValues = record.GetNumFieldValues(fieldName);
if (numFieldValues > 1)
{
for (int i = 0; i < numFieldValues; i++)
{
stringBuilder.Append($"{record.GetFieldValue(fieldName, i)}|");
}
stringBuilder.Length--;
stringBuilder.Append($",");
}
else
{
stringBuilder.Append($"{record.GetFieldValue(fieldName)},");
}
}

foreach (string devFieldName in session.RecordDeveloperFieldNames)
{
DeveloperField devField = record.DeveloperFields.Where(f => f.Name == devFieldName).FirstOrDefault();
if (devField != null)
{
stringBuilder.Append($"{devField.GetValue(0)}");
}
stringBuilder.Append(",");
}

stringBuilder.Append($"{(record.EventType == EventType.Invalid ? "" : record.EventType.ToString())},");
stringBuilder.Append($"{lapId}");

stringBuilder.AppendLine();
}

return stringBuilder.ToString();
}

static public string LengthsToCSV(SessionMessages session)
{
var isMetric = session.Session.GetPoolLengthUnit() == DisplayMeasure.Metric;
var unitConversion = isMetric ? 1.0 : MetersToYards;
double poolLength = session.Session.GetPoolLength() ?? DefaultPoolLength;
var poolLengthString = $"{Math.Round(poolLength * unitConversion)}";
var totalDistance = Math.Round((session.Session.GetNumActiveLengths() ?? 0) * poolLength * unitConversion);

var stringBuilder = new StringBuilder();

// Add a comment row with: Sport Type, Sub Sport, Date/Time, Total Distance, Pool Length, Units, Calories, Duration (Seconds)
stringBuilder.AppendLine($"#Lengths,{session.Session.GetSport().ToString()},{session.Session.GetSubSport().ToString()},{session.Session.GetStartTime().GetDateTime().ToString("yyyy-MM-dd HH:mm:ss")},{totalDistance},{poolLengthString},{(isMetric ? "meters" : "yards")},{session.Session.GetTotalCalories() ?? 0},{session.Session.GetTotalElapsedTime() ?? 0}");

// Create the header row
stringBuilder.AppendLine($"LENGTH TYPE,DURATION (seconds),DISTANCE ({(isMetric ? "meters" : "yards")}),PACE,STOKE COUNT,SWOLF,DPS,STROKE RATE,STROKE TYPE");

foreach (LengthMesg length in session.Lengths)
{
var type = length.GetLengthType() ?? LengthType.Invalid;
float elapsedTime = length.GetTotalElapsedTime() ?? 0;
double speed = (length.GetAvgSpeed() ?? 0) * unitConversion;
ushort? totalStrokes = length.GetTotalStrokes();
var swolf = elapsedTime + (totalStrokes ?? 0);
double? distancePerStroke = totalStrokes.HasValue ? Math.Round(poolLength * unitConversion / totalStrokes ?? 1, 2) : (double?)null;

stringBuilder.Append($"{type.ToString()},");
stringBuilder.Append($"{elapsedTime},");
stringBuilder.Append($"{(type == LengthType.Active ? poolLengthString : "")},");
stringBuilder.Append($"{(type == LengthType.Active ? Math.Round(speed, 2).ToString() : "")},");
stringBuilder.Append($"{(type == LengthType.Active ? totalStrokes.ToString() : "")},");
stringBuilder.Append($"{(type == LengthType.Active ? swolf.ToString() : "")},");
stringBuilder.Append($"{(type == LengthType.Active ? distancePerStroke.ToString() : "")},");
stringBuilder.Append($"{length.GetAvgSwimmingCadence().ToString() ?? ""},");
stringBuilder.Append($"{length.GetSwimStroke().ToString() ?? ""}");

stringBuilder.AppendLine();
}

return stringBuilder.ToString();

}
}

27 changes: 27 additions & 0 deletions src/Garmin/FitSdk/Cookbook/ActivityDecode/ExtendedRecordMesg.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////////
// The following FIT Protocol software provided may be used with FIT protocol
// devices only and remains the copyrighted property of Garmin Canada Inc.
// The software is being provided on an "as-is" basis and as an accommodation,
// and therefore all warranties, representations, or guarantees of any kind
// (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed.
//
// Copyright 2020 Garmin International, Inc.
////////////////////////////////////////////////////////////////////////////////
using Dynastream.Fit;
public class ExtendedRecordMesg : RecordMesg
{
public EventType EventType {get; private set;}

public ExtendedRecordMesg(RecordMesg mesg) : base(mesg)
{
EventType = EventType.Invalid;
}

public ExtendedRecordMesg(EventMesg mesg)
{
SetTimestamp(mesg.GetTimestamp());
EventType = mesg.GetEventType() ?? EventType.Invalid;
}
}
Loading