using System.Threading.Tasks;
using Tizen.Multimedia;
using Tizen.Multimedia.Vision;
+using Tizen.NUI;
namespace Fitness.Services
{
{
private readonly PoseDetector poseDetector = new PoseDetector();
private int isInferencing = 0;
+ private TimeSpan timeLeft;
+ private TimeSpan duration;
+ private Tizen.NUI.Timer timer;
+ private bool timerConfigured = false;
+ private bool workoutStopped = false;
+
+ /// <summary>
+ /// Event raised when workout time left has changed.
+ /// </summary>
+ public event EventHandler<TimeLeftChangedEventArgs> TimeLeftChanged;
+
+ /// <summary>
+ /// Event raised when workout time is up.
+ /// </summary>
+ public event EventHandler WorkoutTimeEnded;
+
+ /// <summary>
+ /// Gets or sets the duration of the workout.
+ /// </summary>
+ public TimeSpan Duration
+ {
+ get => duration;
+ set => duration = value;
+ }
+
+ /// <summary>
+ /// Gets the time remaining until the end of the exercise.
+ /// </summary>
+ public TimeSpan TimeLeft => timeLeft;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether timer has been configured.
+ /// </summary>
+ protected bool TimerConfigured
+ {
+ get => timerConfigured;
+ set => timerConfigured = value;
+ }
/// <summary>
/// Detects performing the exercise based on given preview image data.
var landmarks = await poseDetector.Detect(plane, height, width);
DetectExercise(landmarks);
+ if (workoutStopped)
+ {
+ return;
+ }
}
}
catch (System.Exception exception)
}
}
+ /// <summary>
+ /// Starts workout.
+ /// </summary>
+ public void StartWorkout()
+ {
+ if (!TimerConfigured)
+ {
+ ConfigureTimer();
+ }
+
+ timer?.Start();
+ workoutStopped = false;
+ }
+
+ /// <summary>
+ /// Stops workout.
+ /// </summary>
+ public void StopWorkout()
+ {
+ timer?.Stop();
+ workoutStopped = true;
+ }
+
/// <summary>
/// Detects performing the exercise based on given landmarks.
/// </summary>
/// <param name="landmarks">Body landmarks.</param>
protected abstract void DetectExercise(Landmark[,] landmarks);
+
+ private void ConfigureTimer()
+ {
+ timer = new Tizen.NUI.Timer(1000);
+ timeLeft = Duration;
+
+ timer.Tick += (sender, args) =>
+ {
+ if (timeLeft.CompareTo(TimeSpan.FromSeconds(0)) <= 0)
+ {
+ StopWorkout();
+
+ WorkoutTimeEnded?.Invoke(this, EventArgs.Empty);
+ return true;
+ }
+
+ timeLeft = timeLeft.Subtract(TimeSpan.FromSeconds(1));
+ TimeLeftChanged?.Invoke(this, new TimeLeftChangedEventArgs()
+ {
+ TimeLeft = timeLeft,
+ });
+ return true;
+ };
+ TimerConfigured = true;
+ }
}
}
+++ /dev/null
-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; }
- }
-}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tizen.Multimedia.Vision;
+
+namespace Fitness.Services
+{
+ /// <summary>
+ /// The class that stores the exercise updated event data.
+ /// </summary>
+ public class ExerciseStateUpdatedEventArgs : 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; }
+ }
+}
/// <summary>
/// Event raised when exercise status is updated.
/// </summary>
- event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
+ event EventHandler<ExerciseStateUpdatedEventArgs> ExerciseStateUpdated;
+
+ /// <summary>
+ /// Event raised when workout time left has changed.
+ /// </summary>
+ event EventHandler<TimeLeftChangedEventArgs> TimeLeftChanged;
+
+ /// <summary>
+ /// Event raised when workout time is up.
+ /// </summary>
+ event EventHandler WorkoutTimeEnded;
+
+ /// <summary>
+ /// Gets or sets the duration of the workout.
+ /// </summary>
+ TimeSpan Duration { get; set; }
/// <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 average score above the score threshold specified for the exercise.
+ /// </summary>
+ int AverageScore { get; }
+
+ /// <summary>
+ /// Gets the property specifying the number of repetitions of the exercise.
+ /// </summary>
+ int Count { get; }
+
+ /// <summary>
+ /// Gets the time remaining until the end of the exercise.
+ /// </summary>
+ TimeSpan TimeLeft { get; }
+
+ /// <summary>
+ /// Starts workout.
+ /// </summary>
+ void StartWorkout();
+
+ /// <summary>
+ /// Stops workout.
+ /// </summary>
+ void StopWorkout();
+
+ /// <summary>
+ /// Resets workout.
+ /// </summary>
+ void ResetWorkout();
+
/// <summary>
/// Detects performing the exercise based on given preview image data.
/// </summary>
-using System;
-using System.ComponentModel;
+using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Timers;
private int count;
private int score;
private float currentSquatSimilarity = 0;
+ private int averageScore;
private Stopwatch stopwatch = new Stopwatch();
private System.Timers.Timer holdTimer;
private long holdTimeThreshold;
+ private List<int> allScores = new List<int>();
/// <inheritdoc />
- public event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
+ public event EventHandler<ExerciseStateUpdatedEventArgs> ExerciseStateUpdated;
+
+ /// <inheritdoc />
+ public int AverageScore
+ {
+ get => averageScore;
+ }
+
+ /// <inheritdoc />
+ public int Count
+ {
+ get => count;
+ }
/// <inheritdoc />
public long HoldTimeThreshold
}
}
+ /// <inheritdoc />
+ public void ResetWorkout()
+ {
+ TimerConfigured = false;
+ count = 0;
+ allScores.Clear();
+ }
+
/// <inheritdoc />
protected override void DetectExercise(Landmark[,] landmarks)
{
NUIContext.InvokeOnMainThread(() =>
{
- ExerciseStateUpdated.Invoke(this, new ExerciseEventArgs()
+ ExerciseStateUpdated?.Invoke(this, new ExerciseStateUpdatedEventArgs()
{
PoseLandmarks = landmarks,
Hold = hold,
Count = count,
Score = score,
});
+ averageScore = (int)System.Math.Round((float)(allScores.Count > 0 ? allScores.Average() : 0));
});
}
hold = 0;
stopwatch.Reset();
}
+
+ if (currentSquatSimilarity >= SquatDetectedThreshold)
+ {
+ allScores.Add(score);
+ }
}
}
}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Fitness.Services
+{
+ /// <summary>
+ /// The class that stores the exercise time left data.
+ /// </summary>
+ public class TimeLeftChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the time remaining until the end of the exercise.
+ /// </summary>
+ public TimeSpan TimeLeft { get; internal set; }
+ }
+}
Title = "JOGA Workout 0",
Description = "1. Lie down on your back, keep your knees bent and your back and feet flat on the mat.\n2. Slowly lift your torso and situp.\n3. Return to the starting position by rolling down one vertebrae at a time.\n4. Repeat the exercise until set is complete.",
Difficulty = DifficultyLevel.Easy,
- Duration = new TimeSpan(0, 4, 30),
+ Duration = new TimeSpan(0, 0, 20),
Favourite = true,
VideoUrl = Application.Current.DirectoryInfo.Resource + "media/JOGA-0000.avi",
ThumbnailUrl = Application.Current.DirectoryInfo.Resource + "media/JOGA-0000.jpeg",
private ICommand summaryBackCommand;
private ICommand summaryOkCommand;
private string summaryTitle;
+ private TimeSpan timeLeft;
+ private int totatCount;
+ private TimeSpan totalTime;
+ private int avarageScore;
/// <summary>
/// Initializes a new instance of the <see cref="ExercisingViewModel"/> class.
TryAgain = new Command(ConfirmTryAgain);
EndWorkout = new Command(ConfirmEndWorkout);
CurrentWorkout = workoutViewModel;
+ TimeLeft = workoutViewModel.Duration;
SquatService = new SquatService()
{
HoldTimeThreshold = 1200,
+ Duration = CurrentWorkout.Duration,
};
- SquatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
+ SquatService.ExerciseStateUpdated += OnSquatServiceExerciseStateUpdated;
+ SquatService.WorkoutTimeEnded += OnSquatServiceWorkoutTimeEnded;
+ SquatService.TimeLeftChanged += OnSquatServiceTimeLeftChanged;
}
/// <summary>
/// <summary>
/// Gets AverageScore.
/// </summary>
- public int AverageScore { get; private set; } = 98;
+ public int AverageScore
+ {
+ get => avarageScore;
+ private set
+ {
+ if (value != avarageScore)
+ {
+ avarageScore = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets TotalTime.
+ /// </summary>
+ public TimeSpan TotalTime
+ {
+ get => totalTime;
+ private set
+ {
+ if (value != totalTime)
+ {
+ totalTime = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Gets TotalCount.
/// </summary>
- public int TotalCount { get; private set; } = 27;
+ public int TotalCount
+ {
+ get => totatCount;
+ private set
+ {
+ if (value != totatCount)
+ {
+ totatCount = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
}
/// <summary>
- /// Gets TotalTime.
+ /// Gets the time remaining until the end of the exercise.
/// </summary>
- public TimeSpan TotalTime { get; private set; } = new TimeSpan(0, 3, 39);
-
- /// <summary>
- /// Gets the TimeLeft in workout.
- /// </summary>
- public TimeSpan TimeLeft { get; private set; } = TimeSpan.FromSeconds(27);
+ public TimeSpan TimeLeft
+ {
+ get => timeLeft;
+ private set
+ {
+ if (timeLeft != value)
+ {
+ timeLeft = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Gets or sets the current workout state.
private void ConfirmChangeWorkout(int offset)
{
+ UpdateStatistics();
if (State == WorkoutState.Playing)
{
State = WorkoutState.OnHold;
private void StartNewWorkout()
{
+ TimeLeft = CurrentWorkout.Duration;
+ SquatService.Duration = CurrentWorkout.Duration;
+ SquatService.ResetWorkout();
State = WorkoutState.Loading;
IsSummaryVisible = false;
}
}
}
- private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+ private void OnSquatServiceExerciseStateUpdated(object sender, ExerciseStateUpdatedEventArgs e)
{
PoseLandmarks = e.PoseLandmarks;
Score = e.Score;
Hold = e.Hold;
}
+ private void OnSquatServiceWorkoutTimeEnded(object sender, EventArgs e)
+ {
+ ConfirmTimeIsUp();
+ }
+
+ private void OnSquatServiceTimeLeftChanged(object sender, TimeLeftChangedEventArgs e)
+ {
+ TimeLeft = e.TimeLeft;
+ }
+
private void TriggerPauseResumeWorkout()
{
if (State == WorkoutState.Paused)
private void ConfirmTryAgain()
{
+ UpdateStatistics();
SummaryBackCommand = new Command(ExecuteCloseSummary);
SummaryOkCommand = new Command(() => { ExecuteChangeWorkout(); });
SummaryTitle = GetSummaryTitle(SummaryType.TryAgain);
private void ConfirmEndWorkout()
{
+ UpdateStatistics();
SummaryBackCommand = new Command(ExecuteCloseSummary);
SummaryOkCommand = new Command(Services.NavigationService.Instance.PopToRoot);
SummaryTitle = GetSummaryTitle(SummaryType.EndWorkout);
IsSummaryVisible = true;
}
+
+ private void ConfirmTimeIsUp()
+ {
+ UpdateStatistics();
+ SummaryOkCommand = new Command(Services.NavigationService.Instance.PopToRoot);
+ SummaryTitle = GetSummaryTitle(SummaryType.TimeIsUp);
+ IsSummaryVisible = true;
+ }
+
+ private void UpdateStatistics()
+ {
+ TotalCount = SquatService.Count;
+ AverageScore = SquatService.AverageScore;
+ TotalTime = SquatService.Duration.Subtract(SquatService.TimeLeft);
+ }
}
}
/// </summary>
public ICommand CloseScanningView { get; private set; }
- private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+ private void SquatService_ExerciseStateUpdated(object sender, ExerciseStateUpdatedEventArgs e)
{
PoseLandmarks = e.PoseLandmarks;
Score = e.Score;
switch (oldState)
{
case WorkoutState.Loading:
+ break;
case WorkoutState.OnHold:
+ StartWorkout();
break;
case WorkoutState.Paused:
if (!source.IsCancellationRequested)
{
SetPositionAndSize(WorkoutState.Playing);
+ StartWorkout();
}
break;
{
case WorkoutState.Playing:
Preview.Pause();
+ StopWorkout();
break;
default:
Preview.Pause();
PlayingView.Hide();
PauseView.Show();
+ StopWorkout();
(Task scalePreview, Task movePreview) = Animate(Preview, playing.Preview, pause.Preview, source.Token);
(Task scaleCamera, Task moveCamera) = Animate(cameraOverlayView, playing.Camera, pause.Camera, source.Token);
}
LoadingView.Loaded -= OnLoaded;
+ StartWorkout();
+ }
+
+ private void StartWorkout()
+ {
+ if (BindingContext is ViewModels.ExercisingViewModel viewModel)
+ {
+ viewModel.SquatService.StartWorkout();
+ }
+ }
+
+ private void StopWorkout()
+ {
+ if (BindingContext is ViewModels.ExercisingViewModel viewModel)
+ {
+ viewModel.SquatService.StopWorkout();
+ }
}
private void SetPositionAndSize()