diff --git a/static_src/timer.js b/static_src/timer.js index 9ddf460b4341c3a2bcc01d4a7a099cc58930ebf5..31525f5ba020bd33cad17e167cc51aca33d756c0 100644 --- a/static_src/timer.js +++ b/static_src/timer.js @@ -12,8 +12,8 @@ const enableInputs = () => { $("#pause_play,#minutes,#seconds,#update_time").prop("disabled", false) } -const updateTimeText = (timer) => { - const timeValues = timer.getTimeValues() +const updateTimeText = () => { + const timeValues = window.timer.getTimeValues() const hours = String(timeValues.minutes + timeValues.hours * 60 + timeValues.days * 1440).padStart(2, '0') const seconds = String(timeValues.seconds).padStart(2, '0') @@ -21,15 +21,15 @@ const updateTimeText = (timer) => { $('#timer .timer-values').html(`${hours}:${seconds}`) } -const assignEventListeners = (timer) => { - timer.addEventListener( +const assignEventListeners = () => { + window.timer.addEventListener( 'secondsUpdated', (event) => { - updateTimeText(timer) + updateTimeText() } ) - timer.addEventListener( + window.timer.addEventListener( 'targetAchieved', (event) => { $("#is_counting").prop("checked", false) @@ -37,20 +37,41 @@ const assignEventListeners = (timer) => { } ) - updateTimeText(timer) + updateTimeText() } -const updateTimer = (timer, data, continuePlaying) => { - console.info(`Updating timer: ${JSON.stringify(data.sync_time)}`) +const updateTimer = (data, options) => { + const timerValues = timer.getTimeValues() + const minutes = data["sync_time"]["minutes"] + const seconds = data["sync_time"]["seconds"] + + if (window.timerIsRunning && (minutes === 0 && seconds <= 5)) { + // Don't update the time if we're running in the final 5 seconds. + return + } + + if (options.minuteTolerance !== undefined && options.secondTolerance !== undefined) { + if ( + (Math.abs(timerValues.minutes - minutes) < options.minuteTolerance) + && (Math.abs(timerValues.seconds - seconds) < options.secondTolerance) + ) { + // Don't annoy the user with time changes when there is only a 1-2 second difference. + return + } else { + console.warn("Timer out of sync!") + } + } + + console.info(`Updating timer: ${minutes}:${seconds}, used to be ${timerValues.minutes}:${timerValues.seconds}`) window.startingTime = { - minutes: data["sync_time"]["minutes"], - seconds: data["sync_time"]["seconds"] + minutes: minutes, + seconds: seconds } - timer.removeAllEventListeners() + window.timer.removeAllEventListeners() - timer = new Timer({ + window.timer = new Timer({ countdown: true, startValues: { minutes: window.startingTime.minutes, @@ -58,18 +79,16 @@ const updateTimer = (timer, data, continuePlaying) => { } }) - assignEventListeners(timer) + assignEventListeners() if (window.timerIsRunning) { - timer.start() + window.timer.start() } - - return timer } -const syncTime = (timerSocket, timer) => { +const syncTime = (timerSocket) => { timerSocket.send(JSON.stringify({ - "sync": timer.getTimeValues() + "sync": window.timer.getTimeValues() })) } @@ -80,8 +99,7 @@ $(window).ready( // --- BEGIN Timer --- window.timerIsRunning = false - - let timer = new Timer({ + window.timer = new Timer({ countdown: true, startValues: { minutes: window.startingTime.minutes, @@ -118,25 +136,22 @@ $(window).ready( const data = JSON.parse(event.data) if ("sync_time" in data) { - // Only update if there is any real difference. - - const remainingTime = timer.getTimeValues() - - if ( - data.sync_time.minutes !== remainingTime.minutes || - data.sync_time.seconds !== remainingTime.seconds - ) { - timer = updateTimer(timer, data) - } + updateTimer( + data, + { + minuteTolerance: 1, + secondTolerance: 2 + } + ) } if ("is_running" in data) { if (data["is_running"]) { // Reset if we are playing again. - const remainingTime = timer.getTimeValues() + const remainingTime = window.timer.getTimeValues() if (remainingTime.seconds === 0 && remainingTime.minutes === 0) { - timer = new Timer({ + window.timer = new Timer({ countdown: true, startValues: { minutes: window.startingTime.minutes, @@ -144,16 +159,16 @@ $(window).ready( } }) - assignEventListeners(timer) + assignEventListeners() } - timer.start() + window.timer.start() $("#is_counting").prop("checked", true) $("#pause_play > .btn__body").html("⏸︎") window.timerIsRunning = true } else { - timer.pause() + window.timer.pause() $("#is_counting").prop("checked", false) $("#pause_play > .btn__body").html("⏵︎") @@ -165,15 +180,23 @@ $(window).ready( let interval = null timerSocket.onopen = () => { - syncTime(timerSocket, timer) + syncTime(timerSocket) - interval = setInterval(syncTime, 5000, timerSocket, timer) + interval = setInterval(syncTime, 1000, timerSocket) // --- BEGIN Controls --- $("#pause_play").on( "click", (event) => { + const timeValues = window.timer.getTimeValues() + + if (timeValues.minutes == 0 && timeValues.seconds == 0) { + alertify.error("ProsĂm, nastav ÄŤas.") + + return + } + $("#is_counting").click() } ) @@ -206,7 +229,7 @@ $(window).ready( (event) => { disableInputs() - timer.pause() + window.timer.pause() let minutes = Number($("#minutes").val()) let seconds = Number($("#seconds").val()) @@ -231,14 +254,14 @@ $(window).ready( alertify.error("Ztráta spojenĂ, pokoušĂme se o zpÄ›tnĂ© pĹ™ipojenĂ.") - setTimeout(connectToSocket, 5000) + setTimeout(connectToSocket, 1000) clearInterval(interval) $("#is_counting,#pause_play,#update_time").unbind("click") } } connectToSocket() - assignEventListeners(timer) + assignEventListeners() // --- END Timer --- } diff --git a/timer/consumers.py b/timer/consumers.py index c750a381a37b918770c7abdf1ac79edef47167a5..0aac79dde09ca4f4452c41b01529d391411853fb 100644 --- a/timer/consumers.py +++ b/timer/consumers.py @@ -3,7 +3,7 @@ import datetime import json import threading import time -from timeit import default_timer +import timeit from asgiref.sync import sync_to_async from channels.generic.websocket import AsyncWebsocketConsumer @@ -12,16 +12,20 @@ running_timer_threads = [] def tick_timer(timer, iteration: int, total_seconds: int) -> None: + print("ticking", timer) + second_compensation = 0 - start_time = default_timer() + start_time = timeit.default_timer() timer.refresh_from_db() - end_time = default_timer() + end_time = timeit.default_timer() second_compensation = end_time - start_time while not timer.is_running: + print("waiting to run ...") + time.sleep(0.05) timer.refresh_from_db() @@ -32,15 +36,30 @@ def tick_timer(timer, iteration: int, total_seconds: int) -> None: running_timer_threads.remove(timer.id) return - start_time = default_timer() + if timer.minutes == 0 and timer.seconds == 0: + # Stop the thread if we have reached the end - if iteration != timer.iteration: - # Stop the thread if there is a new timer + running_timer_threads.remove(timer.id) + return + + start_time = timeit.default_timer() + + print(timer.minutes, timer.seconds) + + if timer.minutes == 0 and timer.seconds == 0: + # Stop the thread if we have reached the end + + timer.is_running = False + timer.save() running_timer_threads.remove(timer.id) + return - if total_seconds < 1: + if iteration != timer.iteration: + # Stop the thread if there is a new timer + + running_timer_threads.remove(timer.id) return total_seconds -= 1 @@ -53,11 +72,11 @@ def tick_timer(timer, iteration: int, total_seconds: int) -> None: timer.save() - end_time = default_timer() + end_time = timeit.default_timer() second_compensation += end_time - start_time threading.Timer( - interval=1 - second_compensation, + interval=max(0, 1 - second_compensation), function=tick_timer, args=( timer, @@ -118,6 +137,8 @@ class TimerConsumer(AsyncWebsocketConsumer): ).start() async def receive(self, text_data: str) -> None: + await sync_to_async(self.timer.refresh_from_db)() + json_data = json.loads(text_data) response = {} @@ -141,8 +162,6 @@ class TimerConsumer(AsyncWebsocketConsumer): if reset_timer or (self.timer.id not in running_timer_threads and self.timer.is_running): await self.run_timer() - await sync_to_async(self.timer.refresh_from_db)() - response.update( { "sync_time": {