diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index f764afc40..bea2b4663 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -20,7 +20,7 @@ import { getPlayableEvents, } from '../rundown-service/rundownUtils.js'; import { integrationService } from '../integration-service/IntegrationService.js'; -import { getShouldClockUpdate, getShouldTimerUpdate } from './rundownService.utils.js'; +import { getForceUpdate, getShouldClockUpdate, getShouldTimerUpdate } from './rundownService.utils.js'; /** * Service manages runtime status of app @@ -28,7 +28,8 @@ import { getShouldClockUpdate, getShouldTimerUpdate } from './rundownService.uti */ class RuntimeService { private eventTimer: TimerService | null = null; - private lastOnUpdate = -1; + private lastIntegrationClockUpdate = -1; + private lastIntegrationTimerValue = -1; /** last time we updated the socket */ static previousTimerUpdate: number; @@ -66,19 +67,25 @@ class RuntimeService { } } - // update normal cycle - if (newState.clock - this.lastOnUpdate >= timerConfig.notificationRate) { - const hasRunningTimer = Boolean(newState.eventNow) && newState.timer.playback === Playback.Play; - if (hasRunningTimer) { - process.nextTick(() => { - integrationService.dispatch(TimerLifeCycle.onUpdate); - }); - } + const hasRunningTimer = Boolean(newState.eventNow) && newState.timer.playback === Playback.Play; + const shouldUpdateTimer = + hasRunningTimer && getShouldTimerUpdate(this.lastIntegrationTimerValue, newState.timer.current); + + if (shouldUpdateTimer) { + process.nextTick(() => { + integrationService.dispatch(TimerLifeCycle.onUpdate); + }); + + this.lastIntegrationTimerValue = newState.timer.current; + } + + const shouldUpdateClock = getShouldClockUpdate(this.lastIntegrationClockUpdate, newState.clock); + if (shouldUpdateClock) { process.nextTick(() => { integrationService.dispatch(TimerLifeCycle.onClock); }); - this.lastOnUpdate = newState.clock; + this.lastIntegrationClockUpdate = newState.clock; } if (shouldCallRoll) { @@ -522,12 +529,9 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert // to apply custom logic for different datasets const shouldUpdateClock = getShouldClockUpdate(RuntimeService.previousClockUpdate, state.clock); - const shouldUpdateTimer = getShouldTimerUpdate( - RuntimeService.previousTimerValue, - state.timer.current, - RuntimeService.previousTimerUpdate, - state.clock, - ); + const shouldForceTimerUpdate = getForceUpdate(RuntimeService.previousTimerUpdate, state.clock); + const shouldUpdateTimer = + shouldForceTimerUpdate || getShouldTimerUpdate(RuntimeService.previousTimerValue, state.timer.current); // some changes need an immediate update const hasNewLoaded = state.eventNow?.id !== RuntimeService.previousState?.eventNow?.id; diff --git a/apps/server/src/services/runtime-service/__tests__/rundownService.utils.test.ts b/apps/server/src/services/runtime-service/__tests__/rundownService.utils.test.ts index 41910f8d1..a6aa79c31 100644 --- a/apps/server/src/services/runtime-service/__tests__/rundownService.utils.test.ts +++ b/apps/server/src/services/runtime-service/__tests__/rundownService.utils.test.ts @@ -35,27 +35,28 @@ describe('getShouldTimerUpdate', () => { it('should return false when currentValue is null', () => { const previousValue = 0; const currentValue = null; - const previousUpdate = 0; - const now = 0; - const result = getShouldTimerUpdate(previousValue, currentValue, previousUpdate, now); + const result = getShouldTimerUpdate(previousValue, currentValue); expect(result).toBe(false); }); it('should return true when timer is a second ahead', () => { - const previousValue = 5000; - const currentValue = 6500; - const previousUpdate = Date.now(); - const now = Date.now(); - const result = getShouldTimerUpdate(previousValue, currentValue, previousUpdate, now); + const previousValue = 6500; + const currentValue = 5000; + const result = getShouldTimerUpdate(previousValue, currentValue); expect(result).toBe(true); }); - it('should return false when timer is not a second ahead and force update is not required', () => { - const previousValue = 5000; - const currentValue = 5500; - const previousUpdate = Date.now(); - const now = Date.now(); - const result = getShouldTimerUpdate(previousValue, currentValue, previousUpdate, now); + it('should return false when timer is not a second ahead', () => { + const previousValue = 5500; + const currentValue = 5200; + const result = getShouldTimerUpdate(previousValue, currentValue); expect(result).toBe(false); }); + + it('timer value is ceiled', () => { + const previousValue = 5001; // 6 + const currentValue = 4999; // 5 + const result = getShouldTimerUpdate(previousValue, currentValue); + expect(result).toBe(true); + }); }); diff --git a/apps/server/src/services/runtime-service/rundownService.utils.ts b/apps/server/src/services/runtime-service/rundownService.utils.ts index b8760f9a9..6c257ffe0 100644 --- a/apps/server/src/services/runtime-service/rundownService.utils.ts +++ b/apps/server/src/services/runtime-service/rundownService.utils.ts @@ -19,26 +19,15 @@ export function getShouldClockUpdate(previousUpdate: number, now: number): boole /** * Checks whether we should update the timer value - * - timer has slid (?) * - we have rolled into a new seconds unit */ -export function getShouldTimerUpdate( - previousValue: number, - currentValue: MaybeNumber, - previousUpdate: number, - now: number, -): boolean { +export function getShouldTimerUpdate(previousValue: number, currentValue: MaybeNumber): boolean { if (currentValue === null) { return false; } - - const shouldForceUpdate = getForceUpdate(previousUpdate, now); - if (shouldForceUpdate) { - return true; - } - - const shouldUpdateTimer = millisToSeconds(currentValue) !== millisToSeconds(previousValue + timerConfig.triggerAhead); - + // we avoid trigger ahead since it can cause duplicate triggers + // we force the timer value to be negative because we need a ceiling reduction + const shouldUpdateTimer = millisToSeconds(-currentValue) !== millisToSeconds(-previousValue); return shouldUpdateTimer; } @@ -47,7 +36,7 @@ export function getShouldTimerUpdate( * - if the clock has slid back * - if we have escaped the update rate (clock slid forward) */ -function getForceUpdate(previousUpdate: number, now: number): boolean { +export function getForceUpdate(previousUpdate: number, now: number): boolean { const isClockBehind = now < previousUpdate; const hasExceededRate = now - previousUpdate >= timerConfig.notificationRate; return isClockBehind || hasExceededRate;