refactor: runtime service (#715)

* refactor: rename PlaybackService > RuntimeService

* refactor: stateful runtime service

* refactor: merge event loading

* refactor: roll is part of state mutation
This commit is contained in:
Carlos Valente
2024-01-16 19:54:40 +01:00
committed by GitHub
parent ee121bcfcb
commit a2950442d8
27 changed files with 2198 additions and 2467 deletions
@@ -1,18 +1,9 @@
import {
LogOrigin,
OntimeBaseEvent,
OntimeBlock,
OntimeDelay,
OntimeEvent,
Playback,
SupportedEvent,
} from 'ontime-types';
import { LogOrigin, OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent, SupportedEvent } from 'ontime-types';
import { generateId, getCueCandidate } from 'ontime-utils';
import { DataProvider } from '../../classes/data-provider/DataProvider.js';
import { block as blockDef, delay as delayDef } from '../../models/eventsDefinition.js';
import { MAX_EVENTS } from '../../settings.js';
import { EventLoader, eventLoader } from '../../classes/event-loader/EventLoader.js';
import { eventTimer } from '../TimerService.js';
import { EventLoader } from '../../classes/event-loader/EventLoader.js';
import { sendRefetch } from '../../adapters/websocketAux.js';
import { runtimeCacheStore } from '../../stores/cachingStore.js';
import {
@@ -28,130 +19,17 @@ import {
} from './delayedRundown.utils.js';
import { logger } from '../../classes/Logger.js';
import { validateEvent } from '../../utils/parser.js';
import { clock } from '../Clock.js';
import { state } from '../../state.js';
import { stateMutations } from '../../state.js';
import { runtimeService } from '../runtime-service/RuntimeService.js';
/**
* Forces rundown to be recalculated
* To be used when we know the rundown has changed completely
*/
export function forceReset() {
eventLoader.reset();
runtimeService.reset();
runtimeCacheStore.invalidate(delayedRundownCacheKey);
}
/**
* Checks if a list of IDs is in the current selection
*/
const affectedLoaded = (affectedIds: string[]) => {
const now = eventLoader.loaded.selectedEventId;
const nowPublic = eventLoader.loaded.selectedPublicEventId;
const next = eventLoader.loaded.nextEventId;
const nextPublic = eventLoader.loaded.nextPublicEventId;
return (
affectedIds.includes(now) ||
affectedIds.includes(nowPublic) ||
affectedIds.includes(next) ||
affectedIds.includes(nextPublic)
);
};
/**
* Checks if timer replaces the loaded next
*/
const isNewNext = () => {
const timedEvents = EventLoader.getTimedEvents();
const now = eventLoader.loaded.selectedEventId;
const next = eventLoader.loaded.nextEventId;
// check whether the index of now and next are consecutive
const indexNow = timedEvents.findIndex((event) => event.id === now);
const indexNext = timedEvents.findIndex((event) => event.id === next);
if (indexNext - indexNow !== 1) {
return true;
}
// iterate through timed events and see if there are public events between nowPublic and nextPublic
const nowPublic = eventLoader.loaded.selectedPublicEventId;
const nextPublic = eventLoader.loaded.nextPublicEventId;
let foundNew = false;
let isAfter = false;
for (const event of timedEvents) {
if (!isAfter) {
if (event.id === nowPublic) {
isAfter = true;
}
} else {
if (event.id === nextPublic) {
break;
}
if (event.isPublic) {
foundNew = true;
break;
}
}
}
return foundNew;
};
/**
* Updates timer service when a relevant piece of data changes
*/
export function updateTimer(affectedIds?: string[]) {
const runningEventId = eventLoader.loaded.selectedEventId;
const nextEventId = eventLoader.loaded.nextEventId;
if (runningEventId === null && nextEventId === null) {
return false;
}
// we need to reload in a few scenarios:
// 1. we are not confident that changes do not affect running event
const safeOption = typeof affectedIds === 'undefined';
// 2. the edited event is in memory (now or next) running
const eventInMemory = safeOption ? false : affectedLoaded(affectedIds);
// 3. the edited event replaces next event
const isNext = isNewNext();
if (safeOption) {
eventLoader.reset();
const { eventNow } = eventLoader.loadById(runningEventId) || {};
eventTimer.hotReload(eventNow);
return true;
}
if (eventInMemory) {
eventLoader.reset();
if (state.playback === Playback.Roll) {
const rollTimers = eventLoader.findRoll(clock.timeNow());
if (rollTimers === null) {
eventTimer.stop();
} else {
const { currentEvent, nextEvent } = rollTimers;
eventTimer.roll(currentEvent, nextEvent);
}
} else {
const { eventNow } = eventLoader.loadById(runningEventId) || {};
if (eventNow) {
eventTimer.hotReload(eventNow);
} else {
eventTimer.stop();
}
}
return true;
}
if (isNext) {
const { eventNow } = eventLoader.loadById(runningEventId) || {};
eventTimer.hotReload(eventNow);
return true;
}
return false;
}
/**
* @description creates a new event with given data
* @param {object} eventData
@@ -216,8 +94,8 @@ export async function editEvent(eventData: Partial<OntimeEvent> | Partial<Ontime
export async function batchEditEvents(ids: string[], data: Partial<OntimeEvent>) {
await cachedBatchEdit(ids, data);
// notify timer service of changed events
updateTimer(ids);
// notify runtime service of changed events
runtimeService.update(ids);
// advice socket subscribers of change
sendRefetch();
@@ -243,7 +121,8 @@ export async function deleteEvent(eventId) {
export async function deleteAllEvents() {
await cachedClear();
notifyChanges({ timer: true, external: true, reset: true });
// no need to modify timer since we will reset
notifyChanges({ external: true, reset: true });
}
/**
@@ -284,7 +163,8 @@ export async function swapEvents(from: string, to: string) {
* Called when we make changes to the rundown object
*/
function updateChangeNumEvents() {
eventLoader.updateNumEvents();
const numEvents = EventLoader.getPlayableEvents().length;
stateMutations.updateNumEvents(numEvents);
}
/**
@@ -293,10 +173,11 @@ function updateChangeNumEvents() {
export function notifyChanges(options: { timer?: boolean | string[]; external?: boolean; reset?: boolean }) {
if (options.timer) {
// notify timer service of changed events
// timer can be true or an array of changed IDs
if (Array.isArray(options.timer)) {
updateTimer(options.timer);
runtimeService.update(options.timer);
}
updateTimer();
runtimeService.update();
}
if (options.reset) {