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

StopWatch: add persistence #2141

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
764a8fe
StopWatch: add persistence
codingjourney Oct 20, 2024
4db6a09
minor fixes:
codingjourney Oct 23, 2024
0a730c0
lap storage as CircularBuffer, minor fixes
codingjourney Oct 24, 2024
277b329
improved naming of lap-related fields and methods
codingjourney Oct 26, 2024
0fda703
removed superfluous default values in controller
codingjourney Oct 26, 2024
9a30b18
render accurate time at pause
codingjourney Oct 27, 2024
81dc7d6
fixed issues found by the test-format CI job
codingjourney Oct 28, 2024
8a64388
common method for entering the Paused state
codingjourney Oct 30, 2024
0dfae72
added missing newline
codingjourney Oct 30, 2024
0d0af6e
fixed an integer overflow bug in time rendering
codingjourney Nov 18, 2024
d3820f3
upper bound for lap numbers
codingjourney Nov 28, 2024
41da56e
upper bound for elapsed time
codingjourney Nov 28, 2024
a715ff3
fixed layout of lap data
codingjourney Nov 28, 2024
a6122a2
improved layout, improved re-alignment of time fields
codingjourney Nov 30, 2024
87be94e
length of lap list adapting to available space
codingjourney Nov 30, 2024
a6edd41
tweaked some margins to improve aesthetics
codingjourney Dec 4, 2024
b9def19
reduced heap size to fix a build error
codingjourney Dec 7, 2024
42c5913
fixed issues found by the test-format CI job
codingjourney Dec 7, 2024
1b665bf
elapsedTimeBoundary as constexpr
codingjourney Dec 12, 2024
42729f8
prevent unnecessary redrawing of the time label
codingjourney Dec 12, 2024
135e5f8
tightened declarations of integer fields
codingjourney Dec 14, 2024
5c2dba1
lap times without leading zeroes
codingjourney Dec 14, 2024
4aa51f3
Merge branch 'main' into stopwatch-persistence
codingjourney Dec 14, 2024
0f2d138
fixed issues found by the test-format CI job
codingjourney Dec 21, 2024
0c8f6f6
fixed a type declaration
codingjourney Jan 14, 2025
fa936ec
Merge branch 'main' into stopwatch-persistence
codingjourney Jan 14, 2025
45a9498
fixed irregular pause mode blinking at clock wraparound
codingjourney Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
minor fixes:
* more consistent function names
* lapCapacity as constexpr
* LastLap returns std::optional
* simplified handling of TickType_t values
* removed unused methods
* minor fix in lap rendering
  • Loading branch information
codingjourney committed Dec 7, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 4db6a09255734d4da5b6e1c0a5b7d598545d5051
43 changes: 11 additions & 32 deletions src/components/stopwatch/StopWatchController.cpp
Original file line number Diff line number Diff line change
@@ -2,20 +2,6 @@

using namespace Pinetime::Controllers;

namespace {
TickType_t CalculateDelta(const TickType_t startTime, const TickType_t currentTime) {
TickType_t delta = 0;
// Take care of overflow
if (startTime > currentTime) {
delta = 0xffffffff - startTime;
delta += (currentTime + 1);
} else {
delta = currentTime - startTime;
}
return delta;
}
}

StopWatchController::StopWatchController() {
Clear();
}
@@ -29,14 +15,14 @@ void StopWatchController::Start() {

void StopWatchController::Pause() {
currentState = StopWatchStates::Paused;
timeElapsedPreviously += CalculateDelta(startTime, xTaskGetTickCount());
timeElapsedPreviously += xTaskGetTickCount() - startTime;
}

void StopWatchController::Clear() {
currentState = StopWatchStates::Cleared;
timeElapsedPreviously = 0;

for (int i = 0; i < LAP_CAPACITY; i++) {
for (int i = 0; i < lapCapacity; i++) {
laps[i].count = 0;
laps[i].time = 0;
}
@@ -51,32 +37,25 @@ void StopWatchController::PushLap() {
laps[lapHead].time = lapEnd;
laps[lapHead].count = lapCount + 1;
lapCount += 1;
lapHead = lapCount % LAP_CAPACITY;
}

int StopWatchController::GetLapNum() {
if (lapCount < LAP_CAPACITY)
return lapCount;
else
return LAP_CAPACITY;
lapHead = lapCount % lapCapacity;
}

int StopWatchController::GetLapCount() {
return lapCount;
}

int Wrap(int index) {
return ((index % LAP_CAPACITY) + LAP_CAPACITY) % LAP_CAPACITY;
return ((index % lapCapacity) + lapCapacity) % lapCapacity;
}

LapInfo* StopWatchController::LastLap(int lap) {
if (lap >= LAP_CAPACITY || lap > lapCount || lapCount == 0) {
// Return "empty" LapInfo_t
return &emptyLapInfo;
std::optional<LapInfo> StopWatchController::LastLap(int lap) {
if (lap >= lapCapacity || lap >= lapCount) {
return {};
}
// Index backwards
int index = Wrap(lapHead - lap);
return &laps[index];
int mostRecentLap = lapHead - 1;
int index = Wrap(mostRecentLap - lap);
return laps[index];
}

// Data / State acess
@@ -85,7 +64,7 @@ TickType_t StopWatchController::GetElapsedTime() {
if (!IsRunning()) {
return timeElapsedPreviously;
}
return timeElapsedPreviously + CalculateDelta(startTime, xTaskGetTickCount());
return timeElapsedPreviously + (xTaskGetTickCount() - startTime);
}

bool StopWatchController::IsRunning() {
19 changes: 8 additions & 11 deletions src/components/stopwatch/StopWatchController.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#pragma once

#include <FreeRTOS.h>
#include <optional>
#include <timers.h>

#define LAP_CAPACITY 2

namespace Pinetime {
namespace System {
class SystemTask;
@@ -18,6 +17,8 @@ namespace Pinetime {
TickType_t time = 0; // Delta time from beginning of stopwatch
};

constexpr int lapCapacity = 2;

class StopWatchController {
public:
StopWatchController();
@@ -34,32 +35,28 @@ namespace Pinetime {
/// Only the latest laps are stored, the lap count is saved until reset
void PushLap();

/// Returns actual count of stored laps
int GetLapNum();

/// Returns lapCount
int GetLapCount();

/// Indexes into lap history, with 0 being the latest lap.
/// If the lap is unavailable, count and time will be 0. If there is a
/// real value, count should be above 0
LapInfo* LastLap(int lap = 0);
std::optional<LapInfo> LastLap(int lap = 0);

bool IsRunning();
bool IsCleared();
bool IsPaused();

private:
// private:
// Current state of stopwatch
StopWatchStates currentState = StopWatchStates::Cleared;
// Start time of current duration
TickType_t startTime = 0;
// How much time was elapsed before current duration
TickType_t timeElapsedPreviously = 0;
// Stores lap times
LapInfo laps[LAP_CAPACITY];
LapInfo emptyLapInfo = {.count = 0, .time = 0};
LapInfo laps[lapCapacity];
// Total lap count; may exceed lapCapacity
int lapCount = 0;
// Index for next lap time; must be lower than lapCapacity
int lapHead = 0;
};
}
22 changes: 12 additions & 10 deletions src/displayapp/screens/StopWatch.cpp
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ using namespace Pinetime::Applications::Screens;
using namespace Pinetime::Controllers;

namespace {
TimeSeparated convertTicksToTimeSegments(const TickType_t timeElapsed) {
TimeSeparated ConvertTicksToTimeSegments(const TickType_t timeElapsed) {
// Centiseconds
const int timeElapsedCentis = timeElapsed * 100 / configTICK_RATE_HZ;

@@ -18,14 +18,14 @@ namespace {
return TimeSeparated {hours, mins, secs, hundredths};
}

void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) {
void PlayPauseEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* stopWatch = static_cast<StopWatch*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
stopWatch->PlayPauseBtnEventHandler();
}
}

void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) {
void StopLapEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* stopWatch = static_cast<StopWatch*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
stopWatch->StopLapBtnEventHandler();
@@ -42,14 +42,14 @@ StopWatch::StopWatch(System::SystemTask& systemTask,
static constexpr uint8_t btnHeight = 80;
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
btnPlayPause->user_data = this;
lv_obj_set_event_cb(btnPlayPause, play_pause_event_handler);
lv_obj_set_event_cb(btnPlayPause, PlayPauseEventHandler);
lv_obj_set_size(btnPlayPause, btnWidth, btnHeight);
lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
txtPlayPause = lv_label_create(btnPlayPause, nullptr);

btnStopLap = lv_btn_create(lv_scr_act(), nullptr);
btnStopLap->user_data = this;
lv_obj_set_event_cb(btnStopLap, stop_lap_event_handler);
lv_obj_set_event_cb(btnStopLap, StopLapEventHandler);
lv_obj_set_size(btnStopLap, btnWidth, btnHeight);
lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
txtStopLap = lv_label_create(btnStopLap, nullptr);
@@ -146,7 +146,7 @@ void StopWatch::DisplayCleared() {
}

void StopWatch::RenderTime() {
TimeSeparated currentTimeSeparated = convertTicksToTimeSegments(stopWatchController.GetElapsedTime());
TimeSeparated currentTimeSeparated = ConvertTicksToTimeSegments(stopWatchController.GetElapsedTime());
if (currentTimeSeparated.hours == 0) {
lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
} else {
@@ -176,14 +176,16 @@ void StopWatch::RenderPause() {

void StopWatch::RenderLaps() {
lv_label_set_text(lapText, "");
for (int i = 0; i < displayedLaps; i++) {
LapInfo* lap = stopWatchController.LastLap(i);
for (int i = displayedLaps - 1; i >= 0; i--) {
std::optional<LapInfo> lap = stopWatchController.LastLap(i);

if (lap->count != 0) {
TimeSeparated laptime = convertTicksToTimeSegments(lap->time);
if (lap) {
TimeSeparated laptime = ConvertTicksToTimeSegments(lap->time);
char buffer[16];
sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->count, laptime.mins, laptime.secs, laptime.hundredths);
lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer);
} else {
lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n");
}
}
}