mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 21:24:11 +00:00
refactor: declare timer phase
This commit is contained in:
committed by
Carlos Valente
parent
a53d354b16
commit
fd2eee32aa
@@ -1,5 +1,5 @@
|
|||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { Playback, RuntimeStore, SimpleDirection, SimplePlayback } from 'ontime-types';
|
import { Playback, RuntimeStore, SimpleDirection, SimplePlayback, TimerPhase } from 'ontime-types';
|
||||||
import { createWithEqualityFn, useStoreWithEqualityFn } from 'zustand/traditional';
|
import { createWithEqualityFn, useStoreWithEqualityFn } from 'zustand/traditional';
|
||||||
|
|
||||||
export const runtimeStorePlaceholder: RuntimeStore = {
|
export const runtimeStorePlaceholder: RuntimeStore = {
|
||||||
@@ -11,6 +11,7 @@ export const runtimeStorePlaceholder: RuntimeStore = {
|
|||||||
elapsed: null,
|
elapsed: null,
|
||||||
expectedFinish: null,
|
expectedFinish: null,
|
||||||
finishedAt: null,
|
finishedAt: null,
|
||||||
|
phase: TimerPhase.None,
|
||||||
playback: Playback.Stop,
|
playback: Playback.Stop,
|
||||||
secondaryTimer: null,
|
secondaryTimer: null,
|
||||||
startedAt: null,
|
startedAt: null,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { MILLIS_PER_HOUR, dayInMs, millisToString } from 'ontime-utils';
|
import { MILLIS_PER_HOUR, dayInMs, millisToString } from 'ontime-utils';
|
||||||
import { EndAction, OntimeEvent, Playback, TimeStrategy, TimerType } from 'ontime-types';
|
import { EndAction, OntimeEvent, Playback, TimeStrategy, TimerPhase, TimerType } from 'ontime-types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getCurrent,
|
getCurrent,
|
||||||
getExpectedFinish,
|
getExpectedFinish,
|
||||||
getRollTimers,
|
getRollTimers,
|
||||||
getRuntimeOffset,
|
getRuntimeOffset,
|
||||||
|
getTimerPhase,
|
||||||
getTotalDuration,
|
getTotalDuration,
|
||||||
normaliseEndTime,
|
normaliseEndTime,
|
||||||
skippedOutOfEvent,
|
skippedOutOfEvent,
|
||||||
@@ -1756,3 +1757,97 @@ describe('getTotalDuration()', () => {
|
|||||||
expect(millisToString(duration)).toBe('62:00:00');
|
expect(millisToString(duration)).toBe('62:00:00');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getTimerPhase()', () => {
|
||||||
|
it('should be None if the timer is not running', () => {
|
||||||
|
const state = {
|
||||||
|
timer: {
|
||||||
|
addedTime: 0,
|
||||||
|
current: null,
|
||||||
|
duration: null,
|
||||||
|
elapsed: null,
|
||||||
|
expectedFinish: null,
|
||||||
|
finishedAt: null,
|
||||||
|
playback: Playback.Stop,
|
||||||
|
phase: TimerPhase.None,
|
||||||
|
secondaryTimer: null,
|
||||||
|
startedAt: null,
|
||||||
|
},
|
||||||
|
} as RuntimeState;
|
||||||
|
|
||||||
|
const phase = getTimerPhase(state);
|
||||||
|
expect(phase).toBe(TimerPhase.None);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be negative', () => {
|
||||||
|
const state = {
|
||||||
|
timer: {
|
||||||
|
addedTime: 0,
|
||||||
|
current: -50,
|
||||||
|
duration: 1000,
|
||||||
|
playback: Playback.Play,
|
||||||
|
},
|
||||||
|
eventNow: {
|
||||||
|
timeDanger: 100,
|
||||||
|
timeWarning: 200,
|
||||||
|
},
|
||||||
|
} as RuntimeState;
|
||||||
|
|
||||||
|
const phase = getTimerPhase(state);
|
||||||
|
expect(phase).toBe(TimerPhase.Negative);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be danger', () => {
|
||||||
|
const state = {
|
||||||
|
timer: {
|
||||||
|
addedTime: 0,
|
||||||
|
current: 0,
|
||||||
|
duration: 1000,
|
||||||
|
playback: Playback.Play,
|
||||||
|
},
|
||||||
|
eventNow: {
|
||||||
|
timeDanger: 100,
|
||||||
|
timeWarning: 200,
|
||||||
|
},
|
||||||
|
} as RuntimeState;
|
||||||
|
|
||||||
|
const phase = getTimerPhase(state);
|
||||||
|
expect(phase).toBe(TimerPhase.Danger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be warning', () => {
|
||||||
|
const state = {
|
||||||
|
timer: {
|
||||||
|
addedTime: 0,
|
||||||
|
current: 150,
|
||||||
|
duration: 1000,
|
||||||
|
playback: Playback.Play,
|
||||||
|
},
|
||||||
|
eventNow: {
|
||||||
|
timeDanger: 100,
|
||||||
|
timeWarning: 200,
|
||||||
|
},
|
||||||
|
} as RuntimeState;
|
||||||
|
|
||||||
|
const phase = getTimerPhase(state);
|
||||||
|
expect(phase).toBe(TimerPhase.Warning);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it default if the timer is playing and there is none of the above', () => {
|
||||||
|
const state = {
|
||||||
|
timer: {
|
||||||
|
addedTime: 0,
|
||||||
|
current: 250,
|
||||||
|
duration: 1000,
|
||||||
|
playback: Playback.Play,
|
||||||
|
},
|
||||||
|
eventNow: {
|
||||||
|
timeDanger: 100,
|
||||||
|
timeWarning: 200,
|
||||||
|
},
|
||||||
|
} as RuntimeState;
|
||||||
|
|
||||||
|
const phase = getTimerPhase(state);
|
||||||
|
expect(phase).toBe(TimerPhase.Default);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MaybeNumber, MaybeString, OntimeEvent, TimerType } from 'ontime-types';
|
import { MaybeNumber, MaybeString, OntimeEvent, Playback, TimerPhase, TimerType } from 'ontime-types';
|
||||||
import { dayInMs } from 'ontime-utils';
|
import { dayInMs } from 'ontime-utils';
|
||||||
import { RuntimeState } from '../stores/runtimeState.js';
|
import { RuntimeState } from '../stores/runtimeState.js';
|
||||||
import { timerConfig } from '../config/config.js';
|
import { timerConfig } from '../config/config.js';
|
||||||
@@ -361,3 +361,43 @@ export function getExpectedEnd(state: RuntimeState): MaybeNumber {
|
|||||||
}
|
}
|
||||||
return state.runtime.plannedEnd - state.runtime.offset + state._timer.totalDelay;
|
return state.runtime.plannedEnd - state.runtime.offset + state._timer.totalDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility checks whether the playback is considered to be active
|
||||||
|
* @param state
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function isPlaybackActive(state: RuntimeState): boolean {
|
||||||
|
return (
|
||||||
|
state.timer.playback === Playback.Play ||
|
||||||
|
state.timer.playback === Playback.Pause ||
|
||||||
|
state.timer.playback === Playback.Roll
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks running timer to see which phase it currently is in
|
||||||
|
* @param state
|
||||||
|
*/
|
||||||
|
export function getTimerPhase(state: RuntimeState): TimerPhase {
|
||||||
|
if (!isPlaybackActive(state)) {
|
||||||
|
return TimerPhase.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
const current = state.timer.current;
|
||||||
|
if (current < 0) {
|
||||||
|
return TimerPhase.Negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
const danger = state.eventNow.timeDanger;
|
||||||
|
if (current <= danger) {
|
||||||
|
return TimerPhase.Danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
const warning = state.eventNow.timeWarning;
|
||||||
|
if (current <= warning) {
|
||||||
|
return TimerPhase.Warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TimerPhase.Default;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MaybeNumber, OntimeEvent, Playback, Runtime, TimerState, TimerType } from 'ontime-types';
|
import { MaybeNumber, OntimeEvent, Playback, Runtime, TimerPhase, TimerState, TimerType } from 'ontime-types';
|
||||||
import { calculateDuration, dayInMs } from 'ontime-utils';
|
import { calculateDuration, dayInMs } from 'ontime-utils';
|
||||||
|
|
||||||
import { clock } from '../services/Clock.js';
|
import { clock } from '../services/Clock.js';
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getExpectedFinish,
|
getExpectedFinish,
|
||||||
getRollTimers,
|
getRollTimers,
|
||||||
getRuntimeOffset,
|
getRuntimeOffset,
|
||||||
|
getTimerPhase,
|
||||||
skippedOutOfEvent,
|
skippedOutOfEvent,
|
||||||
updateRoll,
|
updateRoll,
|
||||||
} from '../services/timerUtils.js';
|
} from '../services/timerUtils.js';
|
||||||
@@ -32,6 +33,7 @@ const initialTimer: TimerState = {
|
|||||||
elapsed: null,
|
elapsed: null,
|
||||||
expectedFinish: null, // TODO: expected finish could account for midnight, we cleanup in the clients
|
expectedFinish: null, // TODO: expected finish could account for midnight, we cleanup in the clients
|
||||||
finishedAt: null,
|
finishedAt: null,
|
||||||
|
phase: TimerPhase.None,
|
||||||
playback: Playback.Stop,
|
playback: Playback.Stop,
|
||||||
secondaryTimer: null,
|
secondaryTimer: null,
|
||||||
startedAt: null,
|
startedAt: null,
|
||||||
@@ -300,6 +302,10 @@ export function start(state: RuntimeState = runtimeState): boolean {
|
|||||||
state.runtime.actualStart = state.clock;
|
state.runtime.actualStart = state.clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update timer phase
|
||||||
|
runtimeState.timer.phase = getTimerPhase(runtimeState);
|
||||||
|
|
||||||
|
// update offset
|
||||||
state.runtime.offset = getRuntimeOffset(state);
|
state.runtime.offset = getRuntimeOffset(state);
|
||||||
state.runtime.expectedEnd = state.runtime.plannedEnd - state.runtime.offset;
|
state.runtime.expectedEnd = state.runtime.plannedEnd - state.runtime.offset;
|
||||||
|
|
||||||
@@ -387,6 +393,9 @@ export function update(): UpdateResult {
|
|||||||
runtimeState.timer.duration = runtimeState.timer.current;
|
runtimeState.timer.duration = runtimeState.timer.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update timer phase
|
||||||
|
runtimeState.timer.phase = getTimerPhase(runtimeState);
|
||||||
|
|
||||||
// update offset
|
// update offset
|
||||||
runtimeState.runtime.offset = getRuntimeOffset(runtimeState);
|
runtimeState.runtime.offset = getRuntimeOffset(runtimeState);
|
||||||
runtimeState.runtime.expectedEnd = getExpectedEnd(runtimeState);
|
runtimeState.runtime.expectedEnd = getExpectedEnd(runtimeState);
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import type { MaybeNumber } from '../../index.js';
|
import type { MaybeNumber } from '../../index.js';
|
||||||
import type { Playback } from './Playback.type.js';
|
import type { Playback } from './Playback.type.js';
|
||||||
|
|
||||||
|
export enum TimerPhase {
|
||||||
|
None = 'none',
|
||||||
|
Default = 'default',
|
||||||
|
Warning = 'warning',
|
||||||
|
Danger = 'danger',
|
||||||
|
Negative = 'negative',
|
||||||
|
}
|
||||||
|
|
||||||
export type TimerState = {
|
export type TimerState = {
|
||||||
addedTime: number; // time added by user, can be negative
|
addedTime: number; // time added by user, can be negative
|
||||||
current: MaybeNumber; // running countdown
|
current: MaybeNumber; // running countdown
|
||||||
@@ -8,6 +16,7 @@ export type TimerState = {
|
|||||||
elapsed: MaybeNumber; // elapsed time in current timer
|
elapsed: MaybeNumber; // elapsed time in current timer
|
||||||
expectedFinish: MaybeNumber; // time we expect timer to finish
|
expectedFinish: MaybeNumber; // time we expect timer to finish
|
||||||
finishedAt: MaybeNumber; // only if timer has already finished
|
finishedAt: MaybeNumber; // only if timer has already finished
|
||||||
|
phase: TimerPhase;
|
||||||
playback: Playback;
|
playback: Playback;
|
||||||
secondaryTimer: MaybeNumber; // used for roll mode
|
secondaryTimer: MaybeNumber; // used for roll mode
|
||||||
startedAt: MaybeNumber; // only if timer has already started
|
startedAt: MaybeNumber; // only if timer has already started
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export type { Message, TimerMessage, MessageState } from './definitions/runtime/
|
|||||||
|
|
||||||
export type { Runtime } from './definitions/runtime/Runtime.type.js';
|
export type { Runtime } from './definitions/runtime/Runtime.type.js';
|
||||||
export type { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js';
|
export type { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js';
|
||||||
export type { TimerState } from './definitions/runtime/TimerState.type.js';
|
export { type TimerState, TimerPhase } from './definitions/runtime/TimerState.type.js';
|
||||||
|
|
||||||
// ---> Extra Timer
|
// ---> Extra Timer
|
||||||
export { type SimpleTimerState, SimplePlayback, SimpleDirection } from './definitions/runtime/AuxTimer.type.js';
|
export { type SimpleTimerState, SimplePlayback, SimpleDirection } from './definitions/runtime/AuxTimer.type.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user