mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-22 07:29:08 +00:00
refactor: finish of count-to-end is plan or start time
This commit is contained in:
@@ -42,7 +42,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('../../package.json').version) + ';'\" > src/ONTIME_VERSION.js",
|
"addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('../../package.json').version) + ';'\" > src/ONTIME_VERSION.js",
|
||||||
"postinstall": "pnpm addversion",
|
"postinstall": "pnpm addversion",
|
||||||
"dev": "cross-env NODE_ENV=development tsx watch --tsconfig tsconfig.app.json ./src/index.ts",
|
"dev": "cross-env PORT=4001 NODE_ENV=development tsx watch --tsconfig tsconfig.app.json ./src/index.ts",
|
||||||
"dev:electron": "pnpm dev",
|
"dev:electron": "pnpm dev",
|
||||||
"dev:inspect": "cross-env NODE_ENV=development tsx watch --tsconfig tsconfig.app.json --inspect ./src/index.ts",
|
"dev:inspect": "cross-env NODE_ENV=development tsx watch --tsconfig tsconfig.app.json --inspect ./src/index.ts",
|
||||||
"lint": "oxlint --quiet --type-aware",
|
"lint": "oxlint --quiet --type-aware",
|
||||||
|
|||||||
@@ -268,29 +268,31 @@ describe('getExpectedFinish()', () => {
|
|||||||
const calculatedFinish = getExpectedFinish(state);
|
const calculatedFinish = getExpectedFinish(state);
|
||||||
expect(calculatedFinish).toBe(30);
|
expect(calculatedFinish).toBe(30);
|
||||||
});
|
});
|
||||||
it('handles events that finish the day after', () => {
|
|
||||||
|
it('returns the start time for count to end times which started late', () => {
|
||||||
const state = {
|
const state = {
|
||||||
eventNow: {
|
eventNow: {
|
||||||
timeEnd: 600000, // 00:10:00
|
timeStart: 20 * MILLIS_PER_HOUR, // 20:00:00
|
||||||
|
timeEnd: 21 * MILLIS_PER_MINUTE, // 21:00:00
|
||||||
countToEnd: true,
|
countToEnd: true,
|
||||||
},
|
},
|
||||||
timer: {
|
timer: {
|
||||||
addedTime: 0,
|
addedTime: 0,
|
||||||
startedAt: 79200000, // 22:00:00
|
startedAt: 22 * MILLIS_PER_HOUR, // 22:00:00 <--------------
|
||||||
},
|
},
|
||||||
_timer: {
|
_timer: {
|
||||||
pausedAt: null,
|
pausedAt: null,
|
||||||
hasFinished: false,
|
hasFinished: false,
|
||||||
},
|
},
|
||||||
rundown: {
|
rundown: {
|
||||||
actualStart: 79200000,
|
actualStart: 20 * MILLIS_PER_HOUR, // 20:00:00
|
||||||
plannedEnd: 600000,
|
plannedEnd: 21 * MILLIS_PER_HOUR, // 21:00:00
|
||||||
},
|
},
|
||||||
} as RuntimeState;
|
} as RuntimeState;
|
||||||
|
|
||||||
const calculatedFinish = getExpectedFinish(state);
|
const calculatedFinish = getExpectedFinish(state);
|
||||||
// expected finish is not a duration but a point in time
|
// timeEnd is numerically before startedAt for overnight events, so expectedFinish is clamped to startedAt
|
||||||
expect(calculatedFinish).toBe(600000);
|
expect(calculatedFinish).toBe(22 * MILLIS_PER_HOUR);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Expose errors where we reach invalid states
|
||||||
|
* used mostly in shouldCrashDev patterns
|
||||||
|
*/
|
||||||
|
export class InvalidStateError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Day, MaybeNumber, TimeOfDay, TimerPhase } from 'ontime-types';
|
|||||||
import { MILLIS_PER_HOUR, checkIsNow, dayInMs, isPlaybackActive } from 'ontime-utils';
|
import { MILLIS_PER_HOUR, checkIsNow, dayInMs, isPlaybackActive } from 'ontime-utils';
|
||||||
|
|
||||||
import type { RuntimeState } from '../stores/runtimeState.js';
|
import type { RuntimeState } from '../stores/runtimeState.js';
|
||||||
|
import { InvalidStateError } from './generic.errors.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handle events that span over midnight
|
* handle events that span over midnight
|
||||||
@@ -24,37 +25,35 @@ export function hasCrossedMidnight(previous: TimeOfDay, current: TimeOfDay): boo
|
|||||||
* @returns {number | null} new current time or null if nothing is running
|
* @returns {number | null} new current time or null if nothing is running
|
||||||
*/
|
*/
|
||||||
export function getExpectedFinish(state: RuntimeState): MaybeNumber {
|
export function getExpectedFinish(state: RuntimeState): MaybeNumber {
|
||||||
const { startedAt, duration, addedTime } = state.timer;
|
// if there is a loaded event it must have started
|
||||||
|
// either way, we have no expected finish if nothing is playing
|
||||||
if (state.eventNow === null) {
|
if (state.eventNow === null || state.timer.startedAt === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { countToEnd, timeEnd } = state.eventNow;
|
if (state.eventNow.countToEnd) {
|
||||||
const { pausedAt } = state._timer;
|
|
||||||
const { clock } = state;
|
|
||||||
|
|
||||||
if (startedAt === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pausedTime = pausedAt != null ? clock - pausedAt : 0;
|
|
||||||
|
|
||||||
if (countToEnd) {
|
|
||||||
// count to end events are anchored to their fixed end: added time and pauses
|
// count to end events are anchored to their fixed end: added time and pauses
|
||||||
// do not move the end, they surface as offset instead (see getRuntimeOffset)
|
// do not move the end, they surface as offset instead (see getRuntimeOffset)
|
||||||
return timeEnd;
|
return Math.max(state.eventNow.timeEnd, state.timer.startedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pausedTime = state._timer.pausedAt != null ? state.clock - state._timer.pausedAt : 0;
|
||||||
|
|
||||||
|
DEV: {
|
||||||
|
if (state.timer.duration === null) {
|
||||||
|
throw new InvalidStateError('a running timer cannot have null duration');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle events that finish the day after
|
// handle events that finish the day after
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- duration exists if ther eis a timer
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- duration exists if ther eis a timer
|
||||||
const expectedFinish = startedAt + duration! + addedTime + pausedTime;
|
const expectedFinish = state.timer.startedAt + state.timer.duration + state.timer.addedTime + pausedTime;
|
||||||
if (expectedFinish > dayInMs) {
|
if (expectedFinish > dayInMs) {
|
||||||
return expectedFinish - dayInMs;
|
return expectedFinish - dayInMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an event cannot finish before it started (user added too much negative time)
|
// an event cannot finish before it started (user added too much negative time)
|
||||||
return Math.max(expectedFinish, startedAt);
|
return Math.max(expectedFinish, state.timer.startedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user