mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 00:13:53 +00:00
147 lines
4.3 KiB
TypeScript
147 lines
4.3 KiB
TypeScript
import { EndAction, OntimeEvent, Playback, TimerLifeCycle } from 'ontime-types';
|
|
|
|
import * as runtimeState from '../stores/runtimeState.js';
|
|
import { integrationService } from './integration-service/IntegrationService.js';
|
|
import { eventStore } from '../stores/EventStore.js';
|
|
import { restoreService } from './RestoreService.js';
|
|
import { runtimeService } from './runtime-service/RuntimeService.js';
|
|
import { EventLoader } from '../classes/event-loader/EventLoader.js';
|
|
|
|
/**
|
|
* Service manages Ontime's main timer
|
|
*/
|
|
export class TimerService {
|
|
private _interval: NodeJS.Timer;
|
|
private _updateInterval: number;
|
|
private _refreshInterval: number;
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {number} [timerConfig.refresh]
|
|
* @param {number} [timerConfig.updateInterval]
|
|
*/
|
|
constructor(timerConfig: { refresh: number; updateInterval: number }) {
|
|
this._refreshInterval = timerConfig.refresh;
|
|
this._updateInterval = timerConfig.updateInterval;
|
|
this._interval = setInterval(this.update, 32);
|
|
}
|
|
|
|
@broadcastResult
|
|
start() {
|
|
// TODO: when we start a timer, we schedule an update to its expected end - 16ms
|
|
// we need to cancel this timer on pause, stop and addTime
|
|
if (runtimeState.start()) {
|
|
integrationService.dispatch(TimerLifeCycle.onStart);
|
|
}
|
|
}
|
|
|
|
@broadcastResult
|
|
pause() {
|
|
if (runtimeState.pause()) {
|
|
integrationService.dispatch(TimerLifeCycle.onPause);
|
|
}
|
|
}
|
|
|
|
@broadcastResult
|
|
stop() {
|
|
if (runtimeState.stop()) {
|
|
integrationService.dispatch(TimerLifeCycle.onStop);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds time to running timer by given amount
|
|
* @param {number} amount
|
|
*/
|
|
@broadcastResult
|
|
addTime(amount: number): boolean {
|
|
return runtimeState.addTime(amount);
|
|
}
|
|
|
|
/**
|
|
* Update the app at regular intervals
|
|
* @param {boolean} force whether we should force a broadcast of state
|
|
*/
|
|
@broadcastResult
|
|
update(force = false) {
|
|
const { didUpdate, doRoll, isFinished, shouldNotify } = runtimeState.update(force, this._updateInterval);
|
|
if (didUpdate && shouldNotify) {
|
|
// TODO: can we distinguish between a clock update and a timer update?
|
|
integrationService.dispatch(TimerLifeCycle.onUpdate);
|
|
}
|
|
|
|
if (doRoll) {
|
|
const rundown = EventLoader.getPlayableEvents();
|
|
runtimeState.roll(rundown);
|
|
}
|
|
|
|
if (isFinished) {
|
|
integrationService.dispatch(TimerLifeCycle.onFinish);
|
|
const newState = runtimeState.getState();
|
|
|
|
// handle end action if there was a timer playing
|
|
if (newState.timer.playback === Playback.Play) {
|
|
if (newState.eventNow.endAction === EndAction.Stop) {
|
|
runtimeState.stop();
|
|
} else if (newState.eventNow.endAction === EndAction.LoadNext) {
|
|
// we need to delay here to put this action in the queue stack. otherwise it won't be executed properly
|
|
setTimeout(runtimeState.loadNext, 0);
|
|
} else if (newState.eventNow.endAction === EndAction.PlayNext) {
|
|
// TODO: avoid calling the runtime service here
|
|
runtimeService.startNext();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads roll information into timer service
|
|
* @throws {Error} if rundown is empty
|
|
* @param {OntimeEvent[]} rundown -- list of events to run
|
|
*/
|
|
@broadcastResult
|
|
roll(rundown: OntimeEvent[]) {
|
|
if (rundown.length === 0) {
|
|
throw new Error('No events found');
|
|
}
|
|
|
|
runtimeState.roll(rundown);
|
|
}
|
|
|
|
shutdown() {
|
|
clearInterval(this._interval);
|
|
}
|
|
}
|
|
|
|
function broadcastResult(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
const originalMethod = descriptor.value;
|
|
|
|
descriptor.value = function (...args: any[]) {
|
|
const result = originalMethod.apply(this, args);
|
|
const state = runtimeState.getState();
|
|
|
|
// TODO: compare datasets to see what needs to be emitted
|
|
eventStore.batchSet({
|
|
clock: state.clock,
|
|
eventNow: state.eventNow,
|
|
publicEventNow: state.publicEventNow,
|
|
eventNext: state.eventNext,
|
|
publicEventNext: state.publicEventNext,
|
|
runtime: state.runtime,
|
|
timer: state.timer,
|
|
});
|
|
|
|
// we write to restore service if the underlying data changes
|
|
restoreService.save({
|
|
playback: state.timer.playback,
|
|
selectedEventId: state.eventNow?.id ?? null,
|
|
startedAt: state.timer.startedAt,
|
|
addedTime: state.timer.addedTime,
|
|
pausedAt: state._timer.pausedAt,
|
|
});
|
|
return result;
|
|
};
|
|
|
|
return descriptor;
|
|
}
|