refactor: account for overlapping in rundown

This commit is contained in:
Carlos Valente
2024-08-26 10:49:17 +02:00
committed by Carlos Valente
parent c43b6f37f9
commit 25c7915cf7
8 changed files with 149 additions and 82 deletions
@@ -10,7 +10,7 @@ import {
TimeStrategy,
TimerType,
} from 'ontime-types';
import { MILLIS_PER_HOUR, dayInMs, millisToString } from 'ontime-utils';
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, dayInMs } from 'ontime-utils';
import { calculateRuntimeDelays, getDelayAt, calculateRuntimeDelaysFrom } from '../delayUtils.js';
import {
@@ -89,6 +89,65 @@ describe('generate()', () => {
expect(initResult.totalDuration).toBe(700 - 100);
});
it('accounts for overlaps in rundown', () => {
const testRundown: OntimeRundown = [
{ type: SupportedEvent.Event, id: '1', timeStart: 9000, timeEnd: 10000, duration: 1000 } as OntimeEvent,
{ type: SupportedEvent.Event, id: '2', timeStart: 9250, timeEnd: 9500, duration: 250 } as OntimeEvent,
{ type: SupportedEvent.Event, id: '3', timeStart: 9500, timeEnd: 10500, duration: 1000 } as OntimeEvent,
];
const initResult = generate(testRundown);
expect(initResult.totalDuration).toBe(10500 - 9000); // last end - first start
});
it('accounts for overlaps in rundown (with added gap)', () => {
const testRundown: OntimeRundown = [
{ type: SupportedEvent.Event, id: '1', timeStart: 9000, timeEnd: 10000, duration: 1000 } as OntimeEvent,
{ type: SupportedEvent.Event, id: '2', timeStart: 9250, timeEnd: 9500, duration: 250 } as OntimeEvent,
{ type: SupportedEvent.Event, id: '3', timeStart: 9500, timeEnd: 10500, duration: 1000 } as OntimeEvent,
{ type: SupportedEvent.Event, id: '4', timeStart: 15000, timeEnd: 20000, duration: 5000 } as OntimeEvent,
];
const initResult = generate(testRundown);
expect(initResult.totalDuration).toBe(20000 - 9000); // last end - first start
});
it('accounts for overlaps in rundown (with multiple days)', () => {
const testRundown: OntimeRundown = [
{
type: SupportedEvent.Event,
id: '1',
timeStart: 9 * MILLIS_PER_HOUR,
timeEnd: 10 * MILLIS_PER_HOUR,
duration: MILLIS_PER_HOUR,
} as OntimeEvent,
{
type: SupportedEvent.Event,
id: '2',
timeStart: 9 * MILLIS_PER_HOUR + 15 * MILLIS_PER_MINUTE,
timeEnd: 9 * MILLIS_PER_HOUR + 45 * MILLIS_PER_MINUTE,
duration: 30 * MILLIS_PER_MINUTE,
} as OntimeEvent,
{
type: SupportedEvent.Event,
id: '3',
timeStart: 9 * MILLIS_PER_HOUR + 30 * MILLIS_PER_MINUTE,
timeEnd: 10 * MILLIS_PER_HOUR + 30 * MILLIS_PER_MINUTE,
duration: MILLIS_PER_HOUR,
} as OntimeEvent,
{
type: SupportedEvent.Event,
id: '4',
timeStart: 9 * MILLIS_PER_HOUR,
timeEnd: 10 * MILLIS_PER_HOUR,
duration: MILLIS_PER_HOUR,
} as OntimeEvent,
];
const initResult = generate(testRundown);
expect(initResult.totalDuration).toBe(dayInMs + MILLIS_PER_HOUR); // day + last end - first start
});
it('handles negative delays', () => {
const testRundown: OntimeRundown = [
{ type: SupportedEvent.Event, id: '1', timeStart: 100, timeEnd: 200, duration: 100 } as OntimeEvent,
@@ -362,7 +421,7 @@ describe('generate()', () => {
];
const initResult = generate(testRundown, customProperties);
expect(initResult.order.length).toBe(2);
expect(initResult.assignedCustomProperties).toMatchObject({
expect(initResult.assignedCustomFields).toMatchObject({
lighting: ['1', '2'],
sound: ['2'],
});
@@ -498,20 +557,6 @@ describe('swap() mutation', () => {
});
});
/**
*
*
*
*
*
*
*
*
*
*
*
*/
describe('calculateRuntimeDelays', () => {
it('calculates all delays in a given rundown', () => {
const rundown: OntimeRundown = [
@@ -11,7 +11,14 @@ import {
OntimeRundownEntry,
PlayableEvent,
} from 'ontime-types';
import { generateId, insertAtIndex, reorderArray, swapEventData, getTimeFromPrevious } from 'ontime-utils';
import {
generateId,
insertAtIndex,
reorderArray,
swapEventData,
getTimeFromPrevious,
checkIsNextDay,
} from 'ontime-utils';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { createPatch } from '../../utils/parser.js';
import { apply } from './delayUtils.js';
@@ -83,7 +90,6 @@ export function generate(
totalDuration = 0;
totalDelay = 0;
let previousEntry: PlayableEvent | null = null;
let lastEntry: PlayableEvent | null = null;
for (let i = 0; i < initialRundown.length; i++) {
@@ -100,31 +106,48 @@ export function generate(
// update rundown metadata, it only concerns playable events
if (isPlayableEvent(currentEntry)) {
// fist start is always the first event
if (firstStart === null) {
firstStart = currentEntry.timeStart;
}
// TODO: carry on last event
lastEnd = currentEntry.timeEnd;
const timeFromPrevious: number = getTimeFromPrevious(
currentEntry.timeStart,
currentEntry.timeEnd,
previousEntry?.timeStart,
previousEntry?.timeEnd,
previousEntry?.duration,
lastEntry?.timeStart,
lastEntry?.timeEnd,
lastEntry?.duration,
);
totalDuration += timeFromPrevious + currentEntry.duration;
if (timeFromPrevious === 0) {
// event starts on previous finish, we add its duration
totalDuration += currentEntry.duration;
} else if (timeFromPrevious > 0) {
// event has a gap, we add the gap and the duration
totalDuration += timeFromPrevious + currentEntry.duration;
} else if (timeFromPrevious < 0) {
// there is an overlap, we remove the overlap from the duration
// ensuring that the sum is not negative (ie: fully overlapped events)
// NOTE: we add the gap since it is a negative number
totalDuration += Math.max(currentEntry.duration + timeFromPrevious, 0);
}
// remove eventual gaps from the accumulated delay
// we only affect positive delays (time forwards)
if (totalDelay > 0 && previousEntry) {
const gap = Math.max(currentEntry.timeStart - previousEntry.timeEnd, 0);
totalDelay = Math.max(totalDelay - gap, 0);
if (totalDelay > 0 && timeFromPrevious > 0) {
totalDelay = Math.max(totalDelay - timeFromPrevious, 0);
}
// current event delay is the current accumulated delay
currentEntry.delay = totalDelay;
// keep copy of event
previousEntry = currentEntry;
// lastEntry is the event with the latest end time
if (
lastEntry === null ||
currentEntry.timeEnd > lastEntry.timeEnd ||
checkIsNextDay(lastEntry.timeStart, currentEntry.timeStart, lastEntry.duration)
) {
lastEntry = currentEntry;
}
}
}
@@ -147,9 +170,10 @@ export function generate(
rundown[currentEntry.id] = currentEntry;
}
lastEnd = lastEntry?.timeEnd ?? null;
isStale = false;
customFieldChangelog.clear();
return { rundown, order, links, totalDelay, totalDuration, assignedCustomProperties: assignedCustomFields };
return { rundown, order, links, totalDelay, totalDuration, assignedCustomFields };
}
/** Returns an ID guaranteed to be unique */