Add event to exercise service raised when exercise status is updated.
authorPiotr Czaja/Advanced Frameworks (PLT) /SRPOL/Engineer/Samsung Electronics <p.czaja@samsung.com>
Mon, 26 Jul 2021 10:33:59 +0000 (12:33 +0200)
committerPiotr Czaja <p.czaja@samsung.com>
Tue, 14 Sep 2021 11:01:34 +0000 (13:01 +0200)
Fitness/Services/Exercises/BaseExerciseService.cs
Fitness/Services/Exercises/ExerciseEventArgs.cs [new file with mode: 0644]
Fitness/Services/Exercises/IExerciseService.cs
Fitness/Services/Exercises/SquatService.cs
Fitness/ViewModels/ExercisingViewModel.cs
Fitness/ViewModels/ScanningViewModel.cs
Fitness/res/layout/ExercisingView.xaml
Fitness/res/layout/PlayingView.xaml
Fitness/res/layout/ScanningView.xaml

index 8532c7a160da28493c106182241558cdb1b397a6..0c727532b7d7a5d5b210b9d014d27d7319bae5b2 100644 (file)
@@ -14,32 +14,10 @@ namespace Fitness.Services
     /// <summary>
     /// Common base class for all exercise services.
     /// </summary>
-    public abstract class BaseExerciseService : INotifyPropertyChanged
+    public abstract class BaseExerciseService
     {
         private readonly PoseDetector poseDetector = new PoseDetector();
         private int isInferencing = 0;
-        private Landmark[,] poseLandmarks;
-
-        /// <summary>
-        /// Occurs when a property value changes.
-        /// </summary>
-        public event PropertyChangedEventHandler PropertyChanged;
-
-        /// <summary>
-        /// Gets the pose landmark property.
-        /// </summary>
-        public Landmark[,] PoseLandmarks
-        {
-            get => poseLandmarks;
-            private set
-            {
-                if (value != poseLandmarks)
-                {
-                    poseLandmarks = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
 
         /// <summary>
         /// Detects performing the exercise based on given preview image data.
@@ -67,10 +45,6 @@ namespace Fitness.Services
                     var landmarks = await poseDetector.Detect(plane, height, width);
 
                     DetectExercise(landmarks);
-                    NUIContext.InvokeOnMainThread(() =>
-                    {
-                        PoseLandmarks = landmarks;
-                    });
                 }
             }
             catch (System.Exception exception)
@@ -91,14 +65,5 @@ namespace Fitness.Services
         /// </summary>
         /// <param name="landmarks">Body landmarks.</param>
         protected abstract void DetectExercise(Landmark[,] landmarks);
-
-        /// <summary>
-        /// Raises PropertyChanged event.
-        /// </summary>
-        /// <param name="propertyName">Property name.</param>
-        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
-        {
-            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-        }
     }
 }
diff --git a/Fitness/Services/Exercises/ExerciseEventArgs.cs b/Fitness/Services/Exercises/ExerciseEventArgs.cs
new file mode 100644 (file)
index 0000000..48b806b
--- /dev/null
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tizen.Multimedia.Vision;
+
+namespace Fitness.Services
+{
+    /// <summary>
+    /// Class holding exercise event data.
+    /// </summary>
+    public class ExerciseEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets the pose landmark property.
+        /// </summary>
+        public Landmark[,] PoseLandmarks { get; internal set; }
+
+        /// <summary>
+        /// Gets the property specifying the time of the exercise performed - values ranging from 0 to 5.
+        /// </summary>
+        public int Hold { get; internal set; }
+
+        /// <summary>
+        /// Gets the property specifying the number of repetitions of the exercise.
+        /// </summary>
+        public int Count { get; internal set; }
+
+        /// <summary>
+        /// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
+        /// </summary>
+        public int Score { get; internal set; }
+    }
+}
index a6dfe3d7b38411e6a2f7483c1a88af0af29a4bb7..146d6fdd2a45c17d41eb92834828bcf0a9bdce1c 100644 (file)
@@ -14,30 +14,15 @@ namespace Fitness.Services
     public interface IExerciseService
     {
         /// <summary>
-        /// Gets the pose landmark property.
+        /// Event raised when exercise status is updated.
         /// </summary>
-        Landmark[,] PoseLandmarks { get; }
+        event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
 
         /// <summary>
         /// Gets or sets the hold time threshold specifying the time of a single exercise.
         /// </summary>
         long HoldTimeThreshold { get; set; }
 
-        /// <summary>
-        /// Gets the property specifying the time of the exercise performed - values ranging from 0 to 5.
-        /// </summary>
-        int Hold { get; }
-
-        /// <summary>
-        /// Gets the property specifying the number of repetitions of the exercise.
-        /// </summary>
-        int Count { get; }
-
-        /// <summary>
-        /// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
-        /// </summary>
-        int Score { get; }
-
         /// <summary>
         /// Detects performing the exercise based on given preview image data.
         /// </summary>
index e43bafdd0904d47897bf850efc2ace19c854e0ea..084ebcc0a884b9c8ab4c34fec89a0d518c76b06a 100644 (file)
@@ -1,4 +1,5 @@
-using System.ComponentModel;
+using System;
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
 using System.Timers;
@@ -16,53 +17,15 @@ namespace Fitness.Services
         private int hold;
         private int count;
         private int score;
-        private int newHold;
-        private int newCount;
-        private int newScore;
-        private float fscore = 0;
+        private float currentSquatSimilarity = 0;
         private Stopwatch stopwatch = new Stopwatch();
         private System.Timers.Timer holdTimer;
         private long holdTimeThreshold;
 
-        public int Hold
-        {
-            get => hold;
-            set
-            {
-                if (value != hold)
-                {
-                    hold = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
-
-        public int Count
-        {
-            get => count;
-            set
-            {
-                if (value != count)
-                {
-                    count = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
-
-        public int Score
-        {
-            get => score;
-            set
-            {
-                if (value != score)
-                {
-                    score = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
+        /// <inheritdoc />
+        public event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
 
+        /// <inheritdoc />
         public long HoldTimeThreshold
         {
             get => holdTimeThreshold;
@@ -76,6 +39,7 @@ namespace Fitness.Services
             }
         }
 
+        /// <inheritdoc />
         protected override void DetectExercise(Landmark[,] landmarks)
         {
             int numberOfBodyParts = landmarks.GetLength(1);
@@ -92,9 +56,13 @@ namespace Fitness.Services
 
             NUIContext.InvokeOnMainThread(() =>
             {
-                Hold = newHold;
-                Count = newCount;
-                Score = newScore;
+                ExerciseStateUpdated.Invoke(this, new ExerciseEventArgs()
+                {
+                    PoseLandmarks = landmarks,
+                    Hold = hold,
+                    Count = count,
+                    Score = score,
+                });
             });
         }
 
@@ -109,37 +77,37 @@ namespace Fitness.Services
 
         private void HoldTimer_Elapsed(object sender, ElapsedEventArgs e)
         {
-            if (Hold < HoldCount && fscore >= SquatDetectedThreshold)
+            if (hold < HoldCount && currentSquatSimilarity >= SquatDetectedThreshold)
             {
-                newHold++;
+                hold++;
             }
         }
 
         private void DetectSquat(float squatSimilarity)
         {
-            var previousScore = fscore;
-            fscore = squatSimilarity;
-            newScore = (int)System.Math.Round((float)(100 * fscore));
+            var previousSquatSimilarity = currentSquatSimilarity;
+            currentSquatSimilarity = squatSimilarity;
+            score = (int)System.Math.Round((float)(100 * currentSquatSimilarity));
 
-            if (previousScore < SquatDetectedThreshold)
+            if (previousSquatSimilarity < SquatDetectedThreshold)
             {
-                if (fscore >= SquatDetectedThreshold)
+                if (currentSquatSimilarity >= SquatDetectedThreshold)
                 {
                     stopwatch.Start();
                     holdTimer.Start();
                 }
             }
-            else if (fscore < SquatDetectedThreshold)
+            else if (currentSquatSimilarity < SquatDetectedThreshold)
             {
                 stopwatch.Stop();
                 holdTimer.Stop();
 
                 if (stopwatch.ElapsedMilliseconds >= holdTimeThreshold)
                 {
-                    newCount++;
+                    count++;
                 }
 
-                newHold = 0;
+                hold = 0;
                 stopwatch.Reset();
             }
         }
index 56524b36a8ff67f5eebd447a1f591fff27a4a57b..100c931b07fad701f33515a28a2ebdde023b601b 100644 (file)
@@ -2,6 +2,7 @@ using System;
 using System.Windows.Input;
 using Fitness.Models;
 using Fitness.Services;
+using Tizen.Multimedia.Vision;
 using Tizen.NUI.Binding;
 
 namespace Fitness.ViewModels
@@ -10,6 +11,10 @@ namespace Fitness.ViewModels
     {
         private WorkoutState state;
         private IExerciseService squatService;
+        private int score;
+        private int hold;
+        private int repetitions;
+        private Landmark[,] poseLandmarks;
 
         public ExercisingViewModel(WorkoutViewModel workoutViewModel)
         {
@@ -22,22 +27,72 @@ namespace Fitness.ViewModels
             {
                 HoldTimeThreshold = 1200,
             };
+            squatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
         }
 
         /// <summary>
-        /// Current score
+        /// Gets the pose landmark property.
         /// </summary>
-        public int Score { get; private set; } = 95;
+        public Landmark[,] PoseLandmarks
+        {
+            get => poseLandmarks;
+            private set
+            {
+                if (value != poseLandmarks)
+                {
+                    poseLandmarks = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Current score.
+        /// </summary>
+        public int Score
+        {
+            get => score;
+            set
+            {
+                if (value != score)
+                {
+                    score = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
 
         /// <summary>
         /// Repetitions made in current Workout
         /// </summary>
-        public int Repetitions { get; private set; } = 3;
+        public int Repetitions
+        {
+            get => repetitions;
+            set
+            {
+                if (value != repetitions)
+                {
+                    repetitions = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
 
         /// <summary>
         /// Time left in current body pose to accept repetition.
         /// </summary>
-        public TimeSpan Hold { get; private set; } = TimeSpan.FromSeconds(5);
+        public int Hold
+        {
+            get => hold;
+            set
+            {
+                if (value != hold)
+                {
+                    hold = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
 
         /// <summary>
         /// TimeLeft in workout.
@@ -103,20 +158,9 @@ namespace Fitness.ViewModels
         public string PauseResumeLabel { get; private set; } = "Pause";
 
         /// <summary>
-        /// Gets or sets the <see cref="SquatService"/> property.
+        /// Gets the <see cref="SquatService"/> property.
         /// </summary>
-        public IExerciseService SquatService
-        {
-            get => squatService;
-            set
-            {
-                if (value != squatService)
-                {
-                    squatService = value;
-                    RaisePropertyChanged();
-                }
-            }
-        }
+        public IExerciseService SquatService { get; private set; }
 
         protected override void GoPrevious()
         {
@@ -138,6 +182,14 @@ namespace Fitness.ViewModels
             Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.ChangeToNextWorkout, CurrentWorkout);
         }
 
+        private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+        {
+            PoseLandmarks = e.PoseLandmarks;
+            Score = e.Score;
+            Repetitions = e.Count;
+            Hold = e.Hold;
+        }
+
         private void TriggerPauseResumeWorkout()
         {
             if (State == WorkoutState.Paused)
index b8109de7459ba18d5eacdd67dfc665721af882f5..deefdc56b3b7a0987cb46fa9bc97ded13de34cf5 100644 (file)
@@ -1,5 +1,6 @@
 using System.Windows.Input;
 using Fitness.Services;
+using Tizen.Multimedia.Vision;
 using Tizen.NUI.Binding;
 
 namespace Fitness.ViewModels
@@ -10,6 +11,10 @@ namespace Fitness.ViewModels
     public class ScanningViewModel : BaseViewModel
     {
         private IExerciseService squatService;
+        private int count;
+        private int hold;
+        private int score;
+        private Landmark[,] poseLandmarks;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ScanningViewModel"/> class.
@@ -24,32 +29,94 @@ namespace Fitness.ViewModels
             {
                 HoldTimeThreshold = 1200,
             };
+            squatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
         }
 
         /// <summary>
-        /// Gets title.
+        /// Gets the pose landmark property.
         /// </summary>
-        public string Title { get; private set; } = "ScanningView";
+        public Landmark[,] PoseLandmarks
+        {
+            get => poseLandmarks;
+            private set
+            {
+                if (value != poseLandmarks)
+                {
+                    poseLandmarks = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets  the property specifying the correctness of the exercise - values ranging from 0 to 100.
+        /// </summary>
+        public int Score
+        {
+            get => score;
+            set
+            {
+                if (value != score)
+                {
+                    score = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the property specifying the number of repetitions of the exercise.
+        /// </summary>
+        public int Count
+        {
+            get => count;
+            set
+            {
+                if (value != count)
+                {
+                    count = value;
+                    RaisePropertyChanged();
+                }
+            }
+        }
 
         /// <summary>
-        /// Gets or sets the <see cref="SquatService"/> property.
+        /// Gets or sets the property specifying the time of the exercise performed - values ranging from 0 to 5.
         /// </summary>
-        public IExerciseService SquatService
+        public int Hold
         {
-            get => squatService;
+            get => hold;
             set
             {
-                if (value != squatService)
+                if (value != hold)
                 {
-                    squatService = value;
+                    hold = value;
                     RaisePropertyChanged();
                 }
             }
         }
 
+        /// <summary>
+        /// Gets title.
+        /// </summary>
+        public string Title { get; private set; } = "ScanningView";
+
+        /// <summary>
+        /// Gets the <see cref="SquatService"/> property.
+        /// </summary>
+        public IExerciseService SquatService { get; private set; }
+
         /// <summary>
         /// Gets EndWorkout Command.
         /// </summary>
         public ICommand CloseScanningView { get; private set; }
+
+        private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+        {
+            PoseLandmarks = e.PoseLandmarks;
+            Score = e.Score;
+            Count = e.Count;
+            Hold = e.Hold;
+        }
     }
 }
index dcfd000478f650e50997cf0a2710ce99b5644317..2188c39e626f63d7f4b1d29fd69e39932a07ab42 100644 (file)
@@ -24,7 +24,7 @@
                  WidthResizePolicy="FillToParent"
                  HeightResizePolicy="FillToParent"
                  BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                 PoseLandmarks="{Binding SquatService.PoseLandmarks}">
+                 PoseLandmarks="{Binding PoseLandmarks}">
             <x:Arguments>
                 <Size2D>1124, 632</Size2D>
             </x:Arguments>
index 18c7789408676ce8c71459e5a4222da5b94e0614..15413262ef569854a6c4ee0022e8de755418efb2 100644 (file)
                 </View.Layout>
                 
                 <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                           Text="{Binding SquatService.Hold}"
+                           Text="{Binding Hold}"
                            TextColor="#000C2B"
                            PixelSize="40"
                            HorizontalAlignment="Center"
                            Size="{views:SizeInUnits Width=36, Height=16}"/>
                 
                 <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                           Text="{Binding SquatService.Count}"
+                           Text="{Binding Repetitions}"
                            TextColor="#000C2B"
                            PixelSize="40"
                            HorizontalAlignment="Center"
                            Size="{views:SizeInUnits Width=36, Height=16}"/>
                 
                 <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                           Text="{Binding SquatService.Score}"
+                           Text="{Binding Score}"
                            TextColor="#000C2B"
                            PixelSize="40"
                            HorizontalAlignment="Center"
index a55009c2f5a9d2bd1a1d316eac3e10d5cc138aad..654a114d41323ab30f9aa6c9af6f1ecd5fef8bb9 100644 (file)
@@ -29,7 +29,7 @@
         <ctrl:Overlay WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
                       HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
                       BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                      PoseLandmarks="{Binding SquatService.PoseLandmarks}">
+                      PoseLandmarks="{Binding PoseLandmarks}">
             <x:Arguments>
                 <Size2D>1280, 960</Size2D>
             </x:Arguments>
             </View.Layout>
             
             <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                       Text="{Binding SquatService.Hold, StringFormat='Hold: {0}'}"
+                       Text="{Binding Hold, StringFormat='Hold: {0}'}"
                        PixelSize="40"
                        TextColor="Black" />
             <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                       Text="{Binding SquatService.Count, StringFormat='Count: {0}'}"
+                       Text="{Binding Count, StringFormat='Count: {0}'}"
                        PixelSize="40"
                        TextColor="Black" />
             <TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
-                       Text="{Binding SquatService.Score, StringFormat='Score: {0}'}"
+                       Text="{Binding Score, StringFormat='Score: {0}'}"
                        PixelSize="40"
                        TextColor="Black"/>