mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-18 13:44:12 +00:00
refactor: timer update
This commit is contained in:
committed by
Carlos Valente
parent
cb773ded9f
commit
edc7f20d7a
@@ -29,7 +29,9 @@ import {
|
||||
getEventWithId,
|
||||
getPlayableEvents,
|
||||
} from '../rundown-service/rundownUtils.js';
|
||||
import { skippedOutOfEvent } from '../timerUtils.js';
|
||||
import { integrationService } from '../integration-service/IntegrationService.js';
|
||||
|
||||
import { getForceUpdate, getShouldClockUpdate, getShouldTimerUpdate } from './rundownService.utils.js';
|
||||
|
||||
/**
|
||||
@@ -37,18 +39,21 @@ import { getForceUpdate, getShouldClockUpdate, getShouldTimerUpdate } from './ru
|
||||
* Coordinating with necessary services
|
||||
*/
|
||||
class RuntimeService {
|
||||
private eventTimer: TimerService | null = null;
|
||||
private eventTimer: TimerService;
|
||||
private lastIntegrationClockUpdate = -1;
|
||||
private lastIntegrationTimerValue = -1;
|
||||
|
||||
/** last time we updated the socket */
|
||||
static previousTimerUpdate: number;
|
||||
static previousTimerValue: MaybeNumber;
|
||||
static previousTimerValue: MaybeNumber; // previous timer value, could be null
|
||||
static previousClockUpdate: number;
|
||||
|
||||
/** last known state */
|
||||
static previousState: RuntimeState;
|
||||
|
||||
constructor() {
|
||||
constructor(timerService: TimerService) {
|
||||
this.eventTimer = timerService;
|
||||
|
||||
RuntimeService.previousTimerUpdate = -1;
|
||||
RuntimeService.previousTimerValue = -1;
|
||||
RuntimeService.previousClockUpdate = -1;
|
||||
@@ -59,7 +64,45 @@ class RuntimeService {
|
||||
@broadcastResult
|
||||
checkTimerUpdate({ shouldCallRoll, hasTimerFinished }: runtimeState.UpdateResult) {
|
||||
const newState = runtimeState.getState();
|
||||
if (hasTimerFinished) {
|
||||
|
||||
// 1. find if we need to dispatch integrations related to the phase
|
||||
const timerPhaseChanged = RuntimeService.previousState.timer?.phase !== newState.timer.phase;
|
||||
if (timerPhaseChanged) {
|
||||
if (newState.timer.phase === TimerPhase.Warning) {
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onWarning);
|
||||
});
|
||||
} else if (newState.timer.phase === TimerPhase.Danger) {
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onDanger);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. handle edge cases related to roll
|
||||
if (newState.timer.playback === Playback.Roll) {
|
||||
// check if we need to call roll again
|
||||
const needsEvent =
|
||||
newState.eventNow === null
|
||||
? true
|
||||
: skippedOutOfEvent(newState, this.lastIntegrationClockUpdate, timerConfig.skipLimit);
|
||||
const hasFinishedRoll = hasTimerFinished && shouldCallRoll;
|
||||
if (shouldCallRoll || needsEvent) {
|
||||
if (hasFinishedRoll) {
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onFinish);
|
||||
});
|
||||
}
|
||||
|
||||
// we dont call this.roll because we need to bypass the checks
|
||||
const rundown = getPlayableEvents();
|
||||
// TODO: by not calling roll, we dont get the events
|
||||
this.eventTimer.roll(rundown);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. find if we need to process actions related to the timer finishing
|
||||
if (newState.timer.playback !== Playback.Roll && hasTimerFinished) {
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onFinish);
|
||||
});
|
||||
@@ -77,10 +120,8 @@ class RuntimeService {
|
||||
}
|
||||
}
|
||||
|
||||
const hasRunningTimer = Boolean(newState.eventNow) && newState.timer.playback === Playback.Play;
|
||||
const shouldUpdateTimer =
|
||||
hasRunningTimer && getShouldTimerUpdate(this.lastIntegrationTimerValue, newState.timer.current);
|
||||
|
||||
// 4. find if we need to update the timer
|
||||
const shouldUpdateTimer = getShouldTimerUpdate(this.lastIntegrationTimerValue, newState.timer.current);
|
||||
if (shouldUpdateTimer) {
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onUpdate);
|
||||
@@ -89,6 +130,7 @@ class RuntimeService {
|
||||
this.lastIntegrationTimerValue = newState.timer.current ?? -1;
|
||||
}
|
||||
|
||||
// 5. find if we need to update the clock
|
||||
const shouldUpdateClock = getShouldClockUpdate(this.lastIntegrationClockUpdate, newState.clock);
|
||||
if (shouldUpdateClock) {
|
||||
process.nextTick(() => {
|
||||
@@ -97,42 +139,12 @@ class RuntimeService {
|
||||
|
||||
this.lastIntegrationClockUpdate = newState.clock;
|
||||
}
|
||||
|
||||
if (shouldCallRoll) {
|
||||
// we dont call this.roll because we need to bypass the checks
|
||||
const rundown = getPlayableEvents();
|
||||
this.eventTimer.roll(rundown);
|
||||
}
|
||||
|
||||
const timerPhaseChanged = RuntimeService.previousState.timer?.phase !== newState.timer.phase;
|
||||
|
||||
if (timerPhaseChanged) {
|
||||
switch (newState.timer.phase) {
|
||||
case TimerPhase.Warning:
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onWarning);
|
||||
});
|
||||
break;
|
||||
case TimerPhase.Danger:
|
||||
process.nextTick(() => {
|
||||
integrationService.dispatch(TimerLifeCycle.onDanger);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** delay initialisation until we have a restore point */
|
||||
init(resumable: RestorePoint | null) {
|
||||
logger.info(LogOrigin.Server, 'Runtime service started');
|
||||
// calculate at 30fps, refresh at 1fps
|
||||
this.eventTimer = new TimerService({
|
||||
refresh: timerConfig.updateRate,
|
||||
updateInterval: timerConfig.notificationRate,
|
||||
onUpdateCallback: (updateResult) => this.checkTimerUpdate(updateResult),
|
||||
});
|
||||
this.eventTimer.setOnUpdateCallback((updateResult) => this.checkTimerUpdate(updateResult));
|
||||
|
||||
if (resumable) {
|
||||
this.resume(resumable);
|
||||
@@ -553,8 +565,16 @@ class RuntimeService {
|
||||
}
|
||||
}
|
||||
|
||||
export const runtimeService = new RuntimeService();
|
||||
// calculate at 30fps, refresh at 1fps
|
||||
const eventTimer = new TimerService({
|
||||
refresh: timerConfig.updateRate,
|
||||
updateInterval: timerConfig.notificationRate,
|
||||
});
|
||||
export const runtimeService = new RuntimeService(eventTimer);
|
||||
|
||||
/**
|
||||
* Decorator manages side effects from updating the runtime
|
||||
*/
|
||||
function broadcastResult(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user