aboutsummaryrefslogtreecommitdiff
path: root/src/models
diff options
context:
space:
mode:
authorflu0r1ne <flu0r1ne@flu0r1ne.net>2023-07-13 12:20:29 -0500
committerflu0r1ne <flu0r1ne@flu0r1ne.net>2023-07-13 12:20:29 -0500
commit2cb9b6fdc38a5df00ade737f90a9daf685ab89c3 (patch)
tree0a01a5fff4785520ae1a8d400b36f6bcdf84c532 /src/models
downloadfreqtimer-web-2cb9b6fdc38a5df00ade737f90a9daf685ab89c3.tar.xz
freqtimer-web-2cb9b6fdc38a5df00ade737f90a9daf685ab89c3.zip
Init commit
Diffstat (limited to 'src/models')
-rw-r--r--src/models/counter.cpp35
-rw-r--r--src/models/counter.hpp33
-rw-r--r--src/models/timer.cpp153
-rw-r--r--src/models/timer.hpp126
4 files changed, 347 insertions, 0 deletions
diff --git a/src/models/counter.cpp b/src/models/counter.cpp
new file mode 100644
index 0000000..4855c24
--- /dev/null
+++ b/src/models/counter.cpp
@@ -0,0 +1,35 @@
+#include "models/counter.hpp"
+
+double FreqCounter::rate() const {
+ uint64_t ms = _timer->total_duration_ms();
+
+ if(_timer->overtime()) {
+ ms = _timer->preset_ms();
+ }
+
+ return static_cast<double>(_cnt) / (ms / 60.0e3);
+}
+
+
+double FreqCounter::overtime_rate() const {
+ return static_cast<double>(_overtime_cnt) / (_timer->total_duration_ms() / 60.0e3);
+}
+
+std::string FreqCounter::display_value() const {
+ if(_cnt == UINT32_MAX) {
+ return "ERR";
+ }
+
+ return std::to_string(_cnt);
+}
+
+void FreqCounter::inc() {
+
+ if(_overtime_cnt != UINT32_MAX)
+ _overtime_cnt++;
+
+ if(!_timer->overtime()) {
+ _cnt = _overtime_cnt;
+ }
+
+}
diff --git a/src/models/counter.hpp b/src/models/counter.hpp
new file mode 100644
index 0000000..d1b68fc
--- /dev/null
+++ b/src/models/counter.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <inttypes.h>
+#include <string>
+#include "models/timer.hpp"
+
+class FreqCounter {
+
+ public:
+
+ FreqCounter(FreqTimer const & timer)
+ : _timer(&timer)
+ {}
+
+ void reset() { _cnt = _overtime_cnt = 0; }
+
+ void inc();
+
+ uint32_t cnt() const { return _cnt; }
+
+ [[nodiscard]] double rate() const;
+
+ [[nodiscard]] double overtime_rate() const;
+
+ [[nodiscard]] std::string display_value() const;
+
+
+ private:
+
+ uint32_t _cnt;
+ uint32_t _overtime_cnt;
+ FreqTimer const * _timer;
+};
diff --git a/src/models/timer.cpp b/src/models/timer.cpp
new file mode 100644
index 0000000..a05e8c1
--- /dev/null
+++ b/src/models/timer.cpp
@@ -0,0 +1,153 @@
+#include "timer.hpp"
+#include <cinttypes>
+
+#ifdef EMSCRIPTEN
+#include <emscripten/html5.h>
+#endif
+
+#ifdef TEST
+#define OVERRIDABLE_DURING_TESTING __attribute__((weak))
+#else
+#define OVERRIDABLE_DURING_TESTING static inline
+#endif
+
+
+OVERRIDABLE_DURING_TESTING uint64_t monotonic_timestamp_ms() {
+#ifdef EMSCRIPTEN
+ return emscripten_get_now();
+#else
+ return 0.0; // TODO: unimplemented
+#endif
+}
+
+namespace time_const {
+ static const int64_t MSEC = 1;
+ static const int64_t SEC = 1000 * MSEC;
+ static const int64_t MIN = 60 * SEC;
+ static const int64_t HOUR = 60 * MIN;
+};
+
+FreqTimer::FreqTimer() {
+ clear();
+}
+
+static void _append_ratio(std::string & display_val, int64_t & ms, int64_t ratio, bool two_digit = true) {
+ int64_t amount = ms / ratio;
+ ms -= amount * ratio;
+
+ char buf[3];
+
+ if(two_digit) {
+ snprintf(buf, sizeof(buf), "%02" PRIi64, amount);
+ } else {
+ snprintf(buf, sizeof(buf), "%1" PRIi64, amount);
+ }
+
+ display_val.append(buf);
+}
+
+std::string FreqTimer::counter_display_value() const {
+ using namespace time_const;
+
+ int64_t ms = time_ms();
+
+ std::string display_val;
+
+ if(ms < 0) {
+ display_val.append("+ ");
+ ms = -ms;
+ }
+
+ if(ms > 99 * HOUR) {
+ return "MAX TIME EXCEEDED";
+ }
+
+ _append_ratio(display_val, ms, HOUR);
+ display_val.append(" : ");
+ _append_ratio(display_val, ms, MIN);
+ display_val.append(" : ");
+ _append_ratio(display_val, ms, SEC);
+ display_val.append(".");
+ _append_ratio(display_val, ms, 100 * MSEC, false);
+
+ return display_val;
+}
+
+void FreqTimer::increment_preset_ms(uint64_t amount_ms) {
+ if(_state != State::SETTING) {
+ clear();
+ }
+
+ _preset_ms += amount_ms;
+}
+
+void FreqTimer::toggleStart() {
+ if(_state == State::RUNNING) {
+ _stop();
+ } else {
+ _start();
+ }
+}
+
+bool FreqTimer::has_preset() const {
+ return _preset_ms != 0;
+}
+
+void FreqTimer::reset() {
+ _time_offset_ms = 0;
+ _time_start_ms = monotonic_timestamp_ms();
+ _state = State::RESET;
+}
+
+void FreqTimer::clear() {
+ _time_offset_ms = 0;
+ _time_start_ms = monotonic_timestamp_ms();
+ _preset_ms = 0;
+ _state = State::SETTING;
+}
+
+double FreqTimer::record_floor() const {
+
+ int64_t ms;
+ if(has_preset()) {
+ ms = _preset_ms;
+ } else {
+ ms = total_duration_ms();
+ }
+
+ return 60.0e3 / ms;
+}
+
+double FreqTimer::overtime_record_floor() const {
+ return 60.0e3 / total_duration_ms();
+}
+
+uint64_t FreqTimer::_duration_since_last_stop_ms() const {
+ if(_state != State::RUNNING) {
+ return 0;
+ }
+
+ return monotonic_timestamp_ms() - _time_start_ms;
+}
+
+int64_t FreqTimer::time_ms() const {
+ if (_preset_ms == 0) {
+ return total_duration_ms();
+ }
+
+ return static_cast<int64_t>(_preset_ms) - total_duration_ms();
+}
+
+uint64_t FreqTimer::total_duration_ms() const {
+ return _time_offset_ms + _duration_since_last_stop_ms();
+}
+
+void FreqTimer::_stop() {
+ _time_offset_ms += _duration_since_last_stop_ms();
+ _state = State::STOPPED;
+}
+
+void FreqTimer::_start() {
+ _time_start_ms = monotonic_timestamp_ms();
+ _state = State::RUNNING;
+}
diff --git a/src/models/timer.hpp b/src/models/timer.hpp
new file mode 100644
index 0000000..0b0a978
--- /dev/null
+++ b/src/models/timer.hpp
@@ -0,0 +1,126 @@
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+class FreqTimer {
+ public:
+
+ FreqTimer();
+
+ /*
+ * Starts the timer if stopped
+ */
+ void toggleStart();
+
+ /*
+ * Timer is running
+ */
+ [[nodiscard]] bool running() const { return _state == State::RUNNING; }
+
+ /*
+ * Timer has been started
+ */
+ [[nodiscard]] bool started() const {
+ return _state == State::RUNNING or _state == State::STOPPED;
+ }
+
+ /*
+ * Current preset in ms
+ */
+ [[nodiscard]] int64_t preset_ms() const noexcept { return _preset_ms; }
+
+ /*
+ * Whether the preset time has been exceeded
+ */
+ [[nodiscard]] bool overtime() const { return time_ms() < 0; }
+
+ /*
+ * Reset the time
+ */
+ void reset();
+
+ /*
+ * Reset the time and preset
+ */
+ void clear();
+
+ [[nodiscard]] std::string counter_display_value() const;
+
+ /*
+ * Lowest frequency which can be measured
+ * in the interval (as measured in events/min
+ * or multiples of 60 Hz)
+ */
+ [[nodiscard]] double record_floor() const;
+
+
+ [[nodiscard]] double overtime_record_floor() const;
+
+ /*
+ * Without a preset, the timer acts like a
+ * stopwatch and counts freely from zero.
+ * With a preset, the timer acts like a timer
+ * counting down to zero, then counts up to
+ * measure the number of seconds since the
+ * timing has ended.
+ */
+ [[nodiscard]] bool has_preset() const;
+
+ /*
+ * The current duration in milliseconds.
+ *
+ * If the timer has exceeded the preset,
+ * this time will be negative.
+ */
+ [[nodiscard]] int64_t time_ms() const;
+
+ [[nodiscard]] uint64_t total_duration_ms() const;
+
+
+ /*
+ * Increment the timer's preset by the specified
+ * amount (in milisec.) Subsequent calls between
+ * timings increment the time. Incrementing a
+ * timing after a timing has been restarted will
+ * reset the timing and start from zero.
+ */
+ void increment_preset_ms(uint64_t amount_ms);
+
+ private:
+
+ enum class State {
+ // The timer initially starts in
+ // "setting" mode and the preset
+ // can be modified by incrementing
+ // it by values (measured in seconds)
+ //
+ // Once it is started, this preset
+ // is "sticky" and a reset returns
+ // the timer to the preset value.
+ // Incrementing the preset or clearing
+ // the timer moves the timer back
+ // into setting mode.
+ SETTING,
+ // Timer is active
+ RUNNING,
+ // Timer is temporarily stopped
+ STOPPED,
+ // Reset
+ RESET,
+ };
+
+ State _state;
+
+ uint64_t _time_offset_ms;
+ uint64_t _time_start_ms;
+
+ int64_t _preset_ms;
+
+ [[nodiscard]] uint64_t _duration_since_last_stop_ms() const;
+
+
+ void _stop();
+ void _start();
+
+};