From ec8952be56563188399734b141d69472873a76fd Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:26:37 +0100 Subject: [PATCH] chore: organise delay and rundown utils (#749) --- .../src/controllers/ontimeController.ts | 2 +- .../src/controllers/rundownController.ts | 2 +- apps/server/src/services/delayUtils.ts | 41 ------ .../rundown-service/RundownService.ts | 8 +- .../__tests__/delayUtils.test.ts | 14 +- ...own.utils.test.ts => rundownCache.test.ts} | 2 +- .../services/rundown-service/delayUtils.ts | 135 ++++++++++++++++++ ...elayedRundown.utils.ts => rundownCache.ts} | 95 +----------- apps/server/src/settings.ts | 1 - apps/server/src/utils/parserFunctions.ts | 7 - 10 files changed, 149 insertions(+), 158 deletions(-) delete mode 100644 apps/server/src/services/delayUtils.ts rename apps/server/src/services/{ => rundown-service}/__tests__/delayUtils.test.ts (94%) rename apps/server/src/services/rundown-service/__tests__/{delayedRundown.utils.test.ts => rundownCache.test.ts} (99%) create mode 100644 apps/server/src/services/rundown-service/delayUtils.ts rename apps/server/src/services/rundown-service/{delayedRundown.utils.ts => rundownCache.ts} (79%) delete mode 100644 apps/server/src/settings.ts diff --git a/apps/server/src/controllers/ontimeController.ts b/apps/server/src/controllers/ontimeController.ts index cec2325fd..ce1e1ec19 100644 --- a/apps/server/src/controllers/ontimeController.ts +++ b/apps/server/src/controllers/ontimeController.ts @@ -34,7 +34,7 @@ import { httpIntegration } from '../services/integration-service/HttpIntegration import { logger } from '../classes/Logger.js'; import { deleteAllEvents, notifyChanges } from '../services/rundown-service/RundownService.js'; import { runtimeCacheStore } from '../stores/cachingStore.js'; -import { delayedRundownCacheKey } from '../services/rundown-service/delayedRundown.utils.js'; +import { delayedRundownCacheKey } from '../services/rundown-service/rundownCache.js'; import { integrationService } from '../services/integration-service/IntegrationService.js'; import { getProjectFiles } from '../utils/getFileListFromFolder.js'; import { configService } from '../services/ConfigService.js'; diff --git a/apps/server/src/controllers/rundownController.ts b/apps/server/src/controllers/rundownController.ts index 157e81e82..dbb019453 100644 --- a/apps/server/src/controllers/rundownController.ts +++ b/apps/server/src/controllers/rundownController.ts @@ -13,7 +13,7 @@ import { reorderEvent, swapEvents, } from '../services/rundown-service/RundownService.js'; -import { getDelayedRundown, getRundownCache } from '../services/rundown-service/delayedRundown.utils.js'; +import { getDelayedRundown, getRundownCache } from '../services/rundown-service/rundownCache.js'; // Create controller for GET request to '/events' // Returns - diff --git a/apps/server/src/services/delayUtils.ts b/apps/server/src/services/delayUtils.ts deleted file mode 100644 index 992d917dc..000000000 --- a/apps/server/src/services/delayUtils.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundown } from 'ontime-types'; - -import { deleteAtIndex } from '../utils/arrayUtils.js'; - -export function _applyDelay(eventId: string, rundown: OntimeRundown): OntimeRundown { - const delayIndex = rundown.findIndex((event) => event.id === eventId); - const delayEvent = rundown.at(delayIndex); - - if (!delayEvent) { - throw new Error('Given event ID not found'); - } - - if (!isOntimeDelay(delayEvent)) { - throw new Error('Given event ID is not a delay'); - } - - const updatedRundown = [...rundown]; - const delayValue = delayEvent.duration; - - if (delayValue === 0 || delayIndex === rundown.length - 1) { - // nothing to apply - return updatedRundown; - } - - for (let i = delayIndex + 1; i < rundown.length; i++) { - const currentEvent = updatedRundown[i]; - - if (isOntimeBlock(currentEvent)) { - break; - } else if (isOntimeEvent(currentEvent)) { - currentEvent.timeStart = Math.max(0, currentEvent.timeStart + delayValue); - currentEvent.timeEnd = Math.max(currentEvent.duration, currentEvent.timeEnd + delayValue); - if (currentEvent.delay) { - currentEvent.delay = currentEvent.delay - delayValue; - } - currentEvent.revision += 1; - } - } - - return deleteAtIndex(delayIndex, updatedRundown); -} diff --git a/apps/server/src/services/rundown-service/RundownService.ts b/apps/server/src/services/rundown-service/RundownService.ts index 85c41ca08..b92366239 100644 --- a/apps/server/src/services/rundown-service/RundownService.ts +++ b/apps/server/src/services/rundown-service/RundownService.ts @@ -10,7 +10,6 @@ import { 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 } from '../../classes/event-loader/EventLoader.js'; import { sendRefetch } from '../../adapters/websocketAux.js'; import { runtimeCacheStore } from '../../stores/cachingStore.js'; @@ -24,7 +23,7 @@ import { cachedReorder, cachedSwap, delayedRundownCacheKey, -} from './delayedRundown.utils.js'; +} from './rundownCache.js'; import { logger } from '../../classes/Logger.js'; import { createEvent } from '../../utils/parser.js'; import { stateMutations } from '../../state.js'; @@ -44,11 +43,6 @@ export function forceReset() { * @return {unknown[]} */ export async function addEvent(eventData: Partial | Partial | Partial) { - const numEvents = DataProvider.getRundownLength(); - if (numEvents > MAX_EVENTS) { - throw new Error(`Reached limit number of ${MAX_EVENTS} events`); - } - let newEvent: Partial = {}; const id = generateId(); diff --git a/apps/server/src/services/__tests__/delayUtils.test.ts b/apps/server/src/services/rundown-service/__tests__/delayUtils.test.ts similarity index 94% rename from apps/server/src/services/__tests__/delayUtils.test.ts rename to apps/server/src/services/rundown-service/__tests__/delayUtils.test.ts index ee184724e..788bfebab 100644 --- a/apps/server/src/services/__tests__/delayUtils.test.ts +++ b/apps/server/src/services/rundown-service/__tests__/delayUtils.test.ts @@ -1,5 +1,5 @@ import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types'; -import { _applyDelay } from '../delayUtils.js'; +import { applyDelay } from '../delayUtils.js'; describe('_applyDelay() ', () => { describe('in a rundown without the delay field, persisted rundown', () => { @@ -20,7 +20,7 @@ describe('_applyDelay() ', () => { { id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); it('applies negative delays', () => { @@ -40,7 +40,7 @@ describe('_applyDelay() ', () => { { id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); it('maintains constant duration', () => { @@ -56,7 +56,7 @@ describe('_applyDelay() ', () => { { id: '3', type: SupportedEvent.Event, timeStart: 0, timeEnd: 20, duration: 20, revision: 2 } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); }); @@ -126,7 +126,7 @@ describe('_applyDelay() ', () => { } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); it('applies negative delays', () => { @@ -194,7 +194,7 @@ describe('_applyDelay() ', () => { } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); it('maintains constant duration', () => { @@ -242,7 +242,7 @@ describe('_applyDelay() ', () => { } as OntimeEvent, ]; - const updatedRundown = _applyDelay(delayId, testRundown); + const updatedRundown = applyDelay(delayId, testRundown); expect(updatedRundown).toStrictEqual(expected); }); }); diff --git a/apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts similarity index 99% rename from apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts rename to apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts index 80322ee45..180b8febf 100644 --- a/apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts +++ b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts @@ -1,6 +1,6 @@ import { EndAction, OntimeEvent, OntimeRundown, SupportedEvent, TimerType } from 'ontime-types'; -import { calculateRuntimeDelays, calculateRuntimeDelaysFrom, getDelayAt } from '../delayedRundown.utils.js'; +import { calculateRuntimeDelays, getDelayAt, calculateRuntimeDelaysFrom } from '../delayUtils.js'; describe('calculateRuntimeDelays', () => { it('calculates all delays in a given rundown', () => { diff --git a/apps/server/src/services/rundown-service/delayUtils.ts b/apps/server/src/services/rundown-service/delayUtils.ts new file mode 100644 index 000000000..f8ef23644 --- /dev/null +++ b/apps/server/src/services/rundown-service/delayUtils.ts @@ -0,0 +1,135 @@ +import { OntimeRundown, isOntimeDelay, isOntimeBlock, isOntimeEvent } from 'ontime-types'; + +import { deleteAtIndex } from '../../utils/arrayUtils.js'; + +/** + * Calculates all delays in a given rundown + * @param rundown + */ +export function calculateRuntimeDelays(rundown: OntimeRundown) { + let accumulatedDelay = 0; + const updatedRundown = [...rundown]; + + for (const [index, event] of updatedRundown.entries()) { + if (isOntimeDelay(event)) { + accumulatedDelay += event.duration; + } else if (isOntimeBlock(event)) { + accumulatedDelay = 0; + } else if (isOntimeEvent(event)) { + updatedRundown[index] = { + ...event, + delay: accumulatedDelay, + }; + } + } + return updatedRundown; +} +/** + * Calculate delays in rundown from a given index + * @param eventIndex + * @param rundown + */ +export function calculateRuntimeDelaysFromIndex(eventIndex: number, rundown: OntimeRundown) { + if (eventIndex === -1) { + throw new Error('ID not found at index'); + } + + let accumulatedDelay = getDelayAt(eventIndex, rundown); + const updatedRundown = [...rundown]; + + for (let i = eventIndex; i < rundown.length; i++) { + const event = rundown[i]; + if (isOntimeDelay(event)) { + accumulatedDelay += event.duration; + } else if (isOntimeBlock(event)) { + if (i === eventIndex) { + accumulatedDelay = 0; + } else { + break; + } + } else if (isOntimeEvent(event)) { + updatedRundown[i] = { + ...event, + delay: accumulatedDelay, + }; + } + } + return updatedRundown; +} + +/** + * Calculate delays in rundown from an event with given id + * @param eventId + * @param rundown + */ +export function calculateRuntimeDelaysFrom(eventId: string, rundown: OntimeRundown) { + const index = rundown.findIndex((event) => event.id === eventId); + return calculateRuntimeDelaysFromIndex(index, rundown); +} + +/** + * Calculates delay to an event at a given index + * @param eventIndex + * @param rundown + */ +export function getDelayAt(eventIndex: number, rundown: OntimeRundown): number { + if (eventIndex < 1) { + return 0; + } + + // we need to check the event before + const event = rundown[eventIndex - 1]; + + if (isOntimeDelay(event)) { + return event.duration + getDelayAt(eventIndex - 1, rundown); + } else if (isOntimeBlock(event)) { + return 0; + } else if (isOntimeEvent(event)) { + return event.delay ?? 0; + } + return 0; +} + +/** + * Applies delay from given event ID, deletes the delay event after + * @param eventId + * @param rundown + * @returns + */ +export function applyDelay(eventId: string, rundown: OntimeRundown): OntimeRundown { + const delayIndex = rundown.findIndex((event) => event.id === eventId); + const delayEvent = rundown.at(delayIndex); + + if (!delayEvent) { + throw new Error('Given event ID not found'); + } + + if (!isOntimeDelay(delayEvent)) { + throw new Error('Given event ID is not a delay'); + } + + const updatedRundown = [...rundown]; + const delayValue = delayEvent.duration; + + if (delayValue === 0 || delayIndex === rundown.length - 1) { + // nothing to apply + return updatedRundown; + } + + for (let i = delayIndex + 1; i < rundown.length; i++) { + const currentEvent = updatedRundown[i]; + + if (isOntimeBlock(currentEvent)) { + break; + } else if (isOntimeEvent(currentEvent)) { + currentEvent.timeStart = Math.max(0, currentEvent.timeStart + delayValue); + currentEvent.timeEnd = Math.max(currentEvent.duration, currentEvent.timeEnd + delayValue); + if (currentEvent.delay) { + currentEvent.delay = currentEvent.delay - delayValue; + } + currentEvent.revision += 1; + } + } + + return deleteAtIndex(delayIndex, updatedRundown); +} diff --git a/apps/server/src/services/rundown-service/delayedRundown.utils.ts b/apps/server/src/services/rundown-service/rundownCache.ts similarity index 79% rename from apps/server/src/services/rundown-service/delayedRundown.utils.ts rename to apps/server/src/services/rundown-service/rundownCache.ts index 69a19852f..06a92a0b4 100644 --- a/apps/server/src/services/rundown-service/delayedRundown.utils.ts +++ b/apps/server/src/services/rundown-service/rundownCache.ts @@ -15,8 +15,8 @@ import { DataProvider } from '../../classes/data-provider/DataProvider.js'; import { getCached, runtimeCacheStore } from '../../stores/cachingStore.js'; import { isProduction } from '../../setup.js'; import { deleteAtIndex, insertAtIndex, reorderArray } from '../../utils/arrayUtils.js'; -import { _applyDelay } from '../delayUtils.js'; import { createPatch } from '../../utils/parser.js'; +import { applyDelay, calculateRuntimeDelays, calculateRuntimeDelaysFromIndex, getDelayAt } from './delayUtils.js'; /** * Keep incremental revision number of rundown for runtime @@ -275,10 +275,10 @@ export async function cachedSwap(fromEventId: string, toEventId: string) { export async function cachedApplyDelay(eventId: string) { // update persisted rundown const rundown: OntimeRundown = DataProvider.getRundown(); - const persistedRundown = _applyDelay(eventId, rundown); + const persistedRundown = applyDelay(eventId, rundown); const delayedRundown = getDelayedRundown(); - const cachedRundown = _applyDelay(eventId, delayedRundown); + const cachedRundown = applyDelay(eventId, delayedRundown); // update runtimeCacheStore.setCached(delayedRundownCacheKey, cachedRundown); @@ -286,92 +286,3 @@ export async function cachedApplyDelay(eventId: string) { rundownRevision++; } - -/** - * Calculates all delays in a given rundown - * @param rundown - */ -export function calculateRuntimeDelays(rundown: OntimeRundown) { - let accumulatedDelay = 0; - const updatedRundown = [...rundown]; - - for (const [index, event] of updatedRundown.entries()) { - if (isOntimeDelay(event)) { - accumulatedDelay += event.duration; - } else if (isOntimeBlock(event)) { - accumulatedDelay = 0; - } else if (isOntimeEvent(event)) { - updatedRundown[index] = { - ...event, - delay: accumulatedDelay, - }; - } - } - return updatedRundown; -} - -/** - * Calculate delays in rundown from a given index - * @param eventIndex - * @param rundown - */ -export function calculateRuntimeDelaysFromIndex(eventIndex: number, rundown: OntimeRundown) { - if (eventIndex === -1) { - throw new Error('ID not found at index'); - } - - let accumulatedDelay = getDelayAt(eventIndex, rundown); - const updatedRundown = [...rundown]; - - for (let i = eventIndex; i < rundown.length; i++) { - const event = rundown[i]; - if (isOntimeDelay(event)) { - accumulatedDelay += event.duration; - } else if (isOntimeBlock(event)) { - if (i === eventIndex) { - accumulatedDelay = 0; - } else { - break; - } - } else if (isOntimeEvent(event)) { - updatedRundown[i] = { - ...event, - delay: accumulatedDelay, - }; - } - } - return updatedRundown; -} - -/** - * Calculate delays in rundown from an event with given id - * @param eventId - * @param rundown - */ -export function calculateRuntimeDelaysFrom(eventId: string, rundown: OntimeRundown) { - const index = rundown.findIndex((event) => event.id === eventId); - return calculateRuntimeDelaysFromIndex(index, rundown); -} - -/** - * Calculates delay to an event at a given index - * @param eventIndex - * @param rundown - */ -export function getDelayAt(eventIndex: number, rundown: OntimeRundown): number { - if (eventIndex < 1) { - return 0; - } - - // we need to check the event before - const event = rundown[eventIndex - 1]; - - if (isOntimeDelay(event)) { - return event.duration + getDelayAt(eventIndex - 1, rundown); - } else if (isOntimeBlock(event)) { - return 0; - } else if (isOntimeEvent(event)) { - return event.delay ?? 0; - } - return 0; -} diff --git a/apps/server/src/settings.ts b/apps/server/src/settings.ts deleted file mode 100644 index d4831d8c5..000000000 --- a/apps/server/src/settings.ts +++ /dev/null @@ -1 +0,0 @@ -export const MAX_EVENTS = 32768; diff --git a/apps/server/src/utils/parserFunctions.ts b/apps/server/src/utils/parserFunctions.ts index 9c7775b88..4170ad466 100644 --- a/apps/server/src/utils/parserFunctions.ts +++ b/apps/server/src/utils/parserFunctions.ts @@ -22,7 +22,6 @@ import { import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js'; import { dbModel } from '../models/dataModel.js'; import { createEvent } from './parser.js'; -import { MAX_EVENTS } from '../settings.js'; /** * Parse events array of an entry @@ -38,12 +37,6 @@ export const parseRundown = (data: Partial): OntimeRundown => { let eventIndex = 0; const ids = []; for (const event of data.rundown) { - // cap number of events - if (rundown.length >= MAX_EVENTS) { - console.log(`ERROR: Reached limit number of ${MAX_EVENTS} events`); - break; - } - // double check unique ids if (ids.includes(event?.id)) { console.log('ERROR: ID collision on import, skipping');