Implement handling exercise timer and statistics.
[profile/iot/apps/dotnet/fitness.git] / Fitness / Services / Exercises / SquatService.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq;
5 using System.Timers;
6 using Tizen.Multimedia.Vision;
7
8 namespace Fitness.Services
9 {
10     /// <summary>
11     /// Class handling the squat exercise detection.
12     /// </summary>
13     public class SquatService : BaseExerciseService, IExerciseService
14     {
15         private const float SquatDetectedThreshold = 0.92f;
16         private const int HoldCount = 5;
17         private int hold;
18         private int count;
19         private int score;
20         private float currentSquatSimilarity = 0;
21         private int averageScore;
22         private Stopwatch stopwatch = new Stopwatch();
23         private System.Timers.Timer holdTimer;
24         private long holdTimeThreshold;
25         private List<int> allScores = new List<int>();
26
27         /// <inheritdoc />
28         public event EventHandler<ExerciseStateUpdatedEventArgs> ExerciseStateUpdated;
29
30         /// <inheritdoc />
31         public int AverageScore
32         {
33             get => averageScore;
34         }
35
36         /// <inheritdoc />
37         public int Count
38         {
39             get => count;
40         }
41
42         /// <inheritdoc />
43         public long HoldTimeThreshold
44         {
45             get => holdTimeThreshold;
46             set
47             {
48                 if (value != holdTimeThreshold)
49                 {
50                     holdTimeThreshold = value;
51                     InitializeTimer();
52                 }
53             }
54         }
55
56         /// <inheritdoc />
57         public void ResetWorkout()
58         {
59             TimerConfigured = false;
60             count = 0;
61             allScores.Clear();
62         }
63
64         /// <inheritdoc />
65         protected override void DetectExercise(Landmark[,] landmarks)
66         {
67             int numberOfBodyParts = landmarks.GetLength(1);
68             var range = Enumerable.Range(0, numberOfBodyParts);
69
70             var locations = range.Select(x => landmarks[0, x].Location).ToList();
71             var squatSimilarity = SquatDetector.Similarity(locations);
72             if (float.IsNaN(squatSimilarity))
73             {
74                 squatSimilarity = 0.0f;
75             }
76
77             DetectSquat(squatSimilarity);
78
79             NUIContext.InvokeOnMainThread(() =>
80             {
81                 ExerciseStateUpdated?.Invoke(this, new ExerciseStateUpdatedEventArgs()
82                 {
83                     PoseLandmarks = landmarks,
84                     Hold = hold,
85                     Count = count,
86                     Score = score,
87                 });
88                 averageScore = (int)System.Math.Round((float)(allScores.Count > 0 ? allScores.Average() : 0));
89             });
90         }
91
92         private void InitializeTimer()
93         {
94             long interval = holdTimeThreshold / HoldCount;
95             holdTimer = new System.Timers.Timer(interval);
96             holdTimer.Elapsed += HoldTimer_Elapsed;
97             holdTimer.AutoReset = true;
98             holdTimer.Enabled = true;
99         }
100
101         private void HoldTimer_Elapsed(object sender, ElapsedEventArgs e)
102         {
103             if (hold < HoldCount && currentSquatSimilarity >= SquatDetectedThreshold)
104             {
105                 hold++;
106             }
107         }
108
109         private void DetectSquat(float squatSimilarity)
110         {
111             var previousSquatSimilarity = currentSquatSimilarity;
112             currentSquatSimilarity = squatSimilarity;
113             score = (int)System.Math.Round((float)(100 * currentSquatSimilarity));
114
115             if (previousSquatSimilarity < SquatDetectedThreshold)
116             {
117                 if (currentSquatSimilarity >= SquatDetectedThreshold)
118                 {
119                     stopwatch.Start();
120                     holdTimer.Start();
121                 }
122             }
123             else if (currentSquatSimilarity < SquatDetectedThreshold)
124             {
125                 stopwatch.Stop();
126                 holdTimer.Stop();
127
128                 if (stopwatch.ElapsedMilliseconds >= holdTimeThreshold)
129                 {
130                     count++;
131                 }
132
133                 hold = 0;
134                 stopwatch.Reset();
135             }
136
137             if (currentSquatSimilarity >= SquatDetectedThreshold)
138             {
139                 allScores.Add(score);
140             }
141         }
142     }
143 }