diff --git a/display/include/timer.h b/display/include/timer.h index d00383f..9681037 100644 --- a/display/include/timer.h +++ b/display/include/timer.h @@ -14,7 +14,11 @@ struct TimerSegment { char label[32]; }; -enum class PreStartBehaviour { SHOW_ZERO, RUN_NORMALLY }; +enum class PreStartBehaviour { + SHOW_FIRST_SEGMENT, + SHOW_LAST_SEGMENT, + RUN_NORMALLY +}; struct DisplayOptions { PreStartBehaviour pre_start_behaviour; diff --git a/display/src/socket.cpp b/display/src/socket.cpp index 382e20b..6611895 100644 --- a/display/src/socket.cpp +++ b/display/src/socket.cpp @@ -109,12 +109,15 @@ uint32_t _parseHexColor(const char *c) { void _loadDisplayOptions(JsonObject &data) { String pre_start_behaviour = data["pre_start_behaviour"]; - if (pre_start_behaviour == "ShowZero") { + if (pre_start_behaviour == "ShowLastSegment") { _timerData->display_options.pre_start_behaviour = - timer::PreStartBehaviour::SHOW_ZERO; + timer::PreStartBehaviour::SHOW_LAST_SEGMENT; } else if (pre_start_behaviour == "RunNormally") { _timerData->display_options.pre_start_behaviour = timer::PreStartBehaviour::RUN_NORMALLY; + } else { + _timerData->display_options.pre_start_behaviour = + timer::PreStartBehaviour::SHOW_FIRST_SEGMENT; } _timerData->display_options.clock = data["clock"]; diff --git a/display/src/timer.cpp b/display/src/timer.cpp index 50e7e24..61ed8d0 100644 --- a/display/src/timer.cpp +++ b/display/src/timer.cpp @@ -8,16 +8,23 @@ TimerData _timerData; TimerData *timerData() { return &_timerData; } -ActiveSegment calculateCurrentSegment(TIME timeOffset) { - if (!_timerData.valid || timeOffset == 0) { - return {0, 0xfff, "", 0}; +TIME calculateTimeInCurrentRound(TIME currentTime) { + + if (currentTime < _timerData.start_at && + _timerData.display_options.pre_start_behaviour == + PreStartBehaviour::SHOW_FIRST_SEGMENT) { + return 1; + } + + long totalTimePerRound = 0; + for (int i = 0; i < 10 && _timerData.segments[i].valid; i++) { + totalTimePerRound += _timerData.segments[i].time; } - TIME currentTime = (TIME)millis() + timeOffset; if (currentTime < _timerData.start_at && _timerData.display_options.pre_start_behaviour == - PreStartBehaviour::SHOW_ZERO) { - return {0, 0xfff, "", currentTime}; + PreStartBehaviour::SHOW_LAST_SEGMENT) { + return totalTimePerRound; } long elapsedTime = currentTime - _timerData.start_at; @@ -26,16 +33,20 @@ ActiveSegment calculateCurrentSegment(TIME timeOffset) { elapsedTime = _timerData.stop_at - _timerData.start_at; } - long totalTimePerRound = 0; - for (int i = 0; i < 10 && _timerData.segments[i].valid; i++) { - totalTimePerRound += _timerData.segments[i].time; + if (!_timerData.repeat && elapsedTime > totalTimePerRound) { + return totalTimePerRound; } - if (!_timerData.repeat && elapsedTime > totalTimePerRound) { - return {0, 0xfff, "", currentTime}; + return elapsedTime % totalTimePerRound; +} + +ActiveSegment calculateCurrentSegment(TIME timeOffset) { + if (!_timerData.valid || timeOffset == 0) { + return {0, 0xfff, "", 0}; } - long timeInCurrentRound = elapsedTime % totalTimePerRound; + TIME currentTime = (TIME)millis() + timeOffset; + long timeInCurrentRound = calculateTimeInCurrentRound(currentTime); int currentSegmentIndex = 0; long timeInCurrentSegment = 0; diff --git a/src/redis_migrations/mod.rs b/src/redis_migrations/mod.rs index 2cd32c3..47254bd 100644 --- a/src/redis_migrations/mod.rs +++ b/src/redis_migrations/mod.rs @@ -1,6 +1,7 @@ mod display_options; mod pre_start_behaviour; mod segment; +mod sound; mod tests; mod timer; mod timer_metadata; diff --git a/src/redis_migrations/segment.rs b/src/redis_migrations/segment.rs index 34c53fa..5859549 100644 --- a/src/redis_migrations/segment.rs +++ b/src/redis_migrations/segment.rs @@ -1,10 +1,16 @@ use serde::Deserialize; -use crate::{color::Color, repository::Segment}; +use crate::{ + color::Color, + repository::{Segment, Sound}, +}; + +use super::sound::RedisSound; #[derive(Deserialize, Clone)] #[serde(untagged)] pub enum RedisSegment { + V1(SegmentV1), V0(SegmentV0), } @@ -12,6 +18,7 @@ impl Into for RedisSegment { fn into(self) -> Segment { match self { RedisSegment::V0(v0) => v0.into(), + RedisSegment::V1(v1) => v1.into(), } } } @@ -20,6 +27,32 @@ fn default_zero() -> u32 { 0 } +/// === V1 === + +#[derive(Deserialize, Clone)] +pub struct SegmentV1 { + label: String, + time: u32, + color: Option, + #[serde(default = "default_zero")] + count_to: u32, + sounds: Vec, +} + +impl Into for SegmentV1 { + fn into(self) -> Segment { + Segment { + label: self.label, + time: self.time, + color: self.color, + count_to: self.count_to, + sounds: self.sounds.into_iter().map(|s| s.into()).collect(), + } + } +} + +/// === V0 === + #[derive(Deserialize, Clone)] pub struct SegmentV0 { label: String, @@ -32,12 +65,25 @@ pub struct SegmentV0 { impl Into for SegmentV0 { fn into(self) -> Segment { + let mut sounds: Vec = Vec::new(); + + if self.sound { + sounds.push(Sound { + filename: "beep.mp3".to_string(), + trigger_time: 60, + }); + sounds.push(Sound { + filename: "countdown.mp3".to_string(), + trigger_time: 5, + }); + } + Segment { label: self.label, time: self.time, - sound: self.sound, color: self.color, count_to: self.count_to, + sounds: sounds, } } } diff --git a/src/redis_migrations/sound.rs b/src/redis_migrations/sound.rs new file mode 100644 index 0000000..3df8bcb --- /dev/null +++ b/src/redis_migrations/sound.rs @@ -0,0 +1,32 @@ +use serde::Deserialize; + +use crate::repository::Sound; + +#[derive(Deserialize, Clone)] +#[serde(untagged)] +pub enum RedisSound { + V0(SoundV0), +} + +impl Into for RedisSound { + fn into(self) -> Sound { + match self { + RedisSound::V0(v0) => v0.into(), + } + } +} + +#[derive(Deserialize, Clone)] +pub struct SoundV0 { + pub filename: String, + pub trigger_time: u32, +} + +impl Into for SoundV0 { + fn into(self) -> Sound { + Sound { + filename: self.filename, + trigger_time: self.trigger_time, + } + } +} diff --git a/src/redis_migrations/tests.rs b/src/redis_migrations/tests.rs index 10fd6c6..14c4817 100644 --- a/src/redis_migrations/tests.rs +++ b/src/redis_migrations/tests.rs @@ -32,6 +32,11 @@ fn test_v0() { let timer: Timer = timer.into(); assert_eq!(timer.segments.len(), 1); assert_eq!(timer.segments[0].label, "Boulder"); + assert_eq!(timer.segments[0].sounds.len(), 2); + assert_eq!(timer.segments[0].sounds[0].filename, "beep.mp3"); + assert_eq!(timer.segments[0].sounds[0].trigger_time, 60); + assert_eq!(timer.segments[0].sounds[1].filename, "countdown.mp3"); + assert_eq!(timer.segments[0].sounds[1].trigger_time, 5); assert_eq!(timer.metadata.delay_start_stop, 0); assert_eq!( timer.display_options.pre_start_behaviour, @@ -47,9 +52,14 @@ fn test_v1() { { "label":"Boulder", "time":230000, - "sound":true, "color":"#26A269", - "count_to":11000 + "count_to":11000, + "sounds": [ + { + "filename": "beep.mp3", + "trigger_time": 60 + } + ] } ], "id":"v0", @@ -71,6 +81,9 @@ fn test_v1() { let timer: Timer = timer.into(); assert_eq!(timer.segments.len(), 1); assert_eq!(timer.segments[0].label, "Boulder"); + assert_eq!(timer.segments[0].sounds.len(), 1); + assert_eq!(timer.segments[0].sounds[0].filename, "beep.mp3"); + assert_eq!(timer.segments[0].sounds[0].trigger_time, 60); assert_eq!(timer.metadata.delay_start_stop, 5); assert_eq!( timer.display_options.pre_start_behaviour, diff --git a/src/repository.rs b/src/repository.rs index 3210dc8..167b611 100644 --- a/src/repository.rs +++ b/src/repository.rs @@ -14,15 +14,15 @@ use tokio::{ pub struct Segment { pub label: String, pub time: u32, - pub sound: bool, pub color: Option, pub count_to: u32, + pub sounds: Vec, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Sound { pub filename: String, - pub play_at: u32, + pub trigger_time: u32, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] diff --git a/web/src/components/HelpPopup.svelte b/web/src/components/HelpPopup.svelte index d88f09e..400f9ab 100644 --- a/web/src/components/HelpPopup.svelte +++ b/web/src/components/HelpPopup.svelte @@ -4,10 +4,13 @@ import Fa from 'svelte-fa'; import newUniqueId from 'locally-unique-id-generator'; + let clazz = ''; + export { clazz as class }; + const id = newUniqueId(); - +
import Fa from 'svelte-fa'; import type { PageData } from './$types'; - import { - faChevronRight, - faCircleCheck, - faClose, - faEdit, - faForward, - faPause, - faPlay, - faRefresh - } from '@fortawesome/free-solid-svg-icons'; + import { faEdit, faForward, faPause, faPlay, faRefresh } from '@fortawesome/free-solid-svg-icons'; import { updateTimer, calculateStartTimeAfterResume, @@ -41,7 +32,7 @@ }; const restartTimer = () => { - _updateTimer(new Date().getTime()); + _updateTimer(new Date().getTime() + timerData.metadata.delay_start_stop); }; const stopTimer = () => { diff --git a/web/src/routes/manage/[id]/edit/+page.svelte b/web/src/routes/manage/[id]/edit/+page.svelte index 1ff43af..a3cb6af 100644 --- a/web/src/routes/manage/[id]/edit/+page.svelte +++ b/web/src/routes/manage/[id]/edit/+page.svelte @@ -1,7 +1,5 @@
@@ -32,9 +44,18 @@
- enable sound +