mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-07 23:39:09 +00:00
Automation should use event store for data (#1962)
* fix: automation should use RuntimeStore for data * add: aux timer template help
This commit is contained in:
committed by
GitHub
parent
c1fcdf7065
commit
358ad79ae4
+10
@@ -54,6 +54,13 @@ const eventStaticPropertiesNext = [
|
|||||||
'{{eventNext.delay}}',
|
'{{eventNext.delay}}',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const staticAuxProperties = (index: 1 | 2 | 3) => [
|
||||||
|
`{{auxtimer${index}.current}}`,
|
||||||
|
`{{auxtimer${index}.duration}}`,
|
||||||
|
`{{auxtimer${index}.playback}}`,
|
||||||
|
`{{auxtimer${index}.direction}}`,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a it of possible autocomplete suggestions
|
* Creates a it of possible autocomplete suggestions
|
||||||
* Based on RuntimeState
|
* Based on RuntimeState
|
||||||
@@ -68,6 +75,9 @@ export function makeAutoCompleteList(customFields: CustomFields): string[] {
|
|||||||
...Object.entries(customFields).map(([key]) => `{{eventNow.custom.${key}}}`),
|
...Object.entries(customFields).map(([key]) => `{{eventNow.custom.${key}}}`),
|
||||||
...eventStaticPropertiesNext,
|
...eventStaticPropertiesNext,
|
||||||
...Object.entries(customFields).map(([key]) => `{{eventNext.custom.${key}}}`),
|
...Object.entries(customFields).map(([key]) => `{{eventNext.custom.${key}}}`),
|
||||||
|
...staticAuxProperties(1),
|
||||||
|
...staticAuxProperties(2),
|
||||||
|
...staticAuxProperties(3),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { PlayableEvent, TimerLifeCycle } from 'ontime-types';
|
import { PlayableEvent, TimerLifeCycle } from 'ontime-types';
|
||||||
|
|
||||||
import { makeRuntimeStateData } from '../../../stores/__mocks__/runtimeState.mocks.js';
|
import { makeRuntimeStoreData } from '../../../stores/__mocks__/runtimeStore.mocks.js';
|
||||||
import { makeOntimeEvent } from '../../rundown/__mocks__/rundown.mocks.js';
|
|
||||||
|
|
||||||
import { deleteAllTriggers, addTrigger, addAutomation } from '../automation.dao.js';
|
import { deleteAllTriggers, addTrigger, addAutomation } from '../automation.dao.js';
|
||||||
import { testConditions, triggerAutomations } from '../automation.service.js';
|
import { testConditions, triggerAutomations } from '../automation.service.js';
|
||||||
@@ -10,6 +9,7 @@ import * as httpClient from '../clients/http.client.js';
|
|||||||
|
|
||||||
import { makeOSCAction, makeHTTPAction } from './testUtils.js';
|
import { makeOSCAction, makeHTTPAction } from './testUtils.js';
|
||||||
import { RuntimeState } from '../../../stores/runtimeState.js';
|
import { RuntimeState } from '../../../stores/runtimeState.js';
|
||||||
|
import { makeOntimeEvent } from '../../rundown/__mocks__/rundown.mocks.js';
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
|
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
|
||||||
@@ -40,9 +40,20 @@ describe('triggerAction()', () => {
|
|||||||
let oscSpy = vi.spyOn(oscClient, 'emitOSC');
|
let oscSpy = vi.spyOn(oscClient, 'emitOSC');
|
||||||
let httpSpy = vi.spyOn(httpClient, 'emitHTTP');
|
let httpSpy = vi.spyOn(httpClient, 'emitHTTP');
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
vi.mock('../../../stores/EventStore.js', () => {
|
||||||
|
// Create a small mock store
|
||||||
|
return {
|
||||||
|
eventStore: {
|
||||||
|
poll: vi.fn().mockImplementation(() => makeRuntimeStoreData()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
oscSpy = vi.spyOn(oscClient, 'emitOSC').mockImplementation(() => {});
|
oscSpy = vi.spyOn(oscClient, 'emitOSC').mockImplementation(() => { });
|
||||||
httpSpy = vi.spyOn(httpClient, 'emitHTTP').mockImplementation(() => {});
|
httpSpy = vi.spyOn(httpClient, 'emitHTTP').mockImplementation(() => { });
|
||||||
|
|
||||||
await deleteAllTriggers();
|
await deleteAllTriggers();
|
||||||
const oscAutomation = await addAutomation({
|
const oscAutomation = await addAutomation({
|
||||||
@@ -70,26 +81,25 @@ describe('triggerAction()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger automations for a given action', () => {
|
it('should trigger automations for a given action', () => {
|
||||||
const state = makeRuntimeStateData();
|
triggerAutomations(TimerLifeCycle.onLoad);
|
||||||
triggerAutomations(TimerLifeCycle.onLoad, state);
|
|
||||||
expect(oscSpy).toHaveBeenCalledTimes(1);
|
expect(oscSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(httpSpy).not.toBeCalled();
|
expect(httpSpy).not.toBeCalled();
|
||||||
oscSpy.mockReset();
|
oscSpy.mockReset();
|
||||||
httpSpy.mockReset();
|
httpSpy.mockReset();
|
||||||
|
|
||||||
triggerAutomations(TimerLifeCycle.onStart, state);
|
triggerAutomations(TimerLifeCycle.onStart);
|
||||||
expect(oscClient.emitOSC).not.toBeCalled();
|
expect(oscClient.emitOSC).not.toBeCalled();
|
||||||
expect(httpSpy).not.toBeCalled();
|
expect(httpSpy).not.toBeCalled();
|
||||||
oscSpy.mockReset();
|
oscSpy.mockReset();
|
||||||
httpSpy.mockReset();
|
httpSpy.mockReset();
|
||||||
|
|
||||||
triggerAutomations(TimerLifeCycle.onFinish, state);
|
triggerAutomations(TimerLifeCycle.onFinish);
|
||||||
expect(oscSpy).not.toBeCalled();
|
expect(oscSpy).not.toBeCalled();
|
||||||
expect(httpSpy).toHaveBeenCalledTimes(1);
|
expect(httpSpy).toHaveBeenCalledTimes(1);
|
||||||
oscSpy.mockReset();
|
oscSpy.mockReset();
|
||||||
httpSpy.mockReset();
|
httpSpy.mockReset();
|
||||||
|
|
||||||
triggerAutomations(TimerLifeCycle.onStop, state);
|
triggerAutomations(TimerLifeCycle.onStop);
|
||||||
expect(oscSpy).not.toBeCalled();
|
expect(oscSpy).not.toBeCalled();
|
||||||
expect(httpSpy).not.toBeCalled();
|
expect(httpSpy).not.toBeCalled();
|
||||||
});
|
});
|
||||||
@@ -540,7 +550,7 @@ describe('testConditions()', () => {
|
|||||||
|
|
||||||
describe('for all filter rule', () => {
|
describe('for all filter rule', () => {
|
||||||
it('should return true when all filters are true', () => {
|
it('should return true when all filters are true', () => {
|
||||||
const mockStore = makeRuntimeStateData({
|
const mockStore = makeRuntimeStoreData({
|
||||||
clock: 10,
|
clock: 10,
|
||||||
eventNow: makeOntimeEvent({
|
eventNow: makeOntimeEvent({
|
||||||
title: 'test',
|
title: 'test',
|
||||||
@@ -560,7 +570,7 @@ describe('testConditions()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if any filters are false', () => {
|
it('should return false if any filters are false', () => {
|
||||||
const mockStore = makeRuntimeStateData({
|
const mockStore = makeRuntimeStoreData({
|
||||||
clock: 10,
|
clock: 10,
|
||||||
eventNow: makeOntimeEvent({
|
eventNow: makeOntimeEvent({
|
||||||
title: 'test',
|
title: 'test',
|
||||||
@@ -582,7 +592,7 @@ describe('testConditions()', () => {
|
|||||||
|
|
||||||
describe('for any filter rule', () => {
|
describe('for any filter rule', () => {
|
||||||
it('should return true when all filters are true', () => {
|
it('should return true when all filters are true', () => {
|
||||||
const mockStore = makeRuntimeStateData({
|
const mockStore = makeRuntimeStoreData({
|
||||||
clock: 10,
|
clock: 10,
|
||||||
eventNow: makeOntimeEvent({
|
eventNow: makeOntimeEvent({
|
||||||
title: 'test',
|
title: 'test',
|
||||||
@@ -602,7 +612,7 @@ describe('testConditions()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if any filters are true', () => {
|
it('should return true if any filters are true', () => {
|
||||||
const mockStore = makeRuntimeStateData({
|
const mockStore = makeRuntimeStoreData({
|
||||||
clock: 10,
|
clock: 10,
|
||||||
eventNow: makeOntimeEvent({
|
eventNow: makeOntimeEvent({
|
||||||
title: 'not-test',
|
title: 'not-test',
|
||||||
@@ -622,7 +632,7 @@ describe('testConditions()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if all filters are false', () => {
|
it('should return false if all filters are false', () => {
|
||||||
const mockStore = makeRuntimeStateData({
|
const mockStore = makeRuntimeStoreData({
|
||||||
clock: 10,
|
clock: 10,
|
||||||
eventNow: makeOntimeEvent({ title: 'test' }) as PlayableEvent,
|
eventNow: makeOntimeEvent({ title: 'test' }) as PlayableEvent,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
isOntimeAction,
|
isOntimeAction,
|
||||||
isOSCOutput,
|
isOSCOutput,
|
||||||
LogOrigin,
|
LogOrigin,
|
||||||
|
RuntimeStore,
|
||||||
TimerLifeCycle,
|
TimerLifeCycle,
|
||||||
type AutomationFilter,
|
type AutomationFilter,
|
||||||
type AutomationOutput,
|
type AutomationOutput,
|
||||||
@@ -10,29 +11,30 @@ import {
|
|||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
import { getPropertyFromPath } from 'ontime-utils';
|
import { getPropertyFromPath } from 'ontime-utils';
|
||||||
|
|
||||||
import { logger } from '../../classes/Logger.js';
|
|
||||||
import { getState, type RuntimeState } from '../../stores/runtimeState.js';
|
|
||||||
import { isOntimeCloud } from '../../setup/environment.js';
|
|
||||||
|
|
||||||
import { emitOSC } from './clients/osc.client.js';
|
import { emitOSC } from './clients/osc.client.js';
|
||||||
import { emitHTTP } from './clients/http.client.js';
|
import { emitHTTP } from './clients/http.client.js';
|
||||||
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
|
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
|
||||||
import { isContained, isEquivalent, isGreaterThan, isLessThan } from './automation.utils.js';
|
import { isContained, isEquivalent, isGreaterThan, isLessThan } from './automation.utils.js';
|
||||||
import { toOntimeAction } from './clients/ontime.client.js';
|
import { toOntimeAction } from './clients/ontime.client.js';
|
||||||
|
|
||||||
|
import { logger } from '../../classes/Logger.js';
|
||||||
|
import { isOntimeCloud } from '../../setup/environment.js';
|
||||||
|
import { eventStore } from '../../stores/EventStore.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes a method for triggering actions based on a TimerLifeCycle event
|
* Exposes a method for triggering actions based on a TimerLifeCycle event
|
||||||
*/
|
*/
|
||||||
export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
|
export function triggerAutomations(cycle: TimerLifeCycle) {
|
||||||
if (!getAutomationsEnabled()) {
|
if (!getAutomationsEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const store = eventStore.poll();
|
||||||
|
|
||||||
let triggers = getAutomationTriggers();
|
let triggers = getAutomationTriggers();
|
||||||
|
|
||||||
// get triggers from event
|
// get triggers from event
|
||||||
if (state.eventNow?.triggers) {
|
if (store.eventNow?.triggers) {
|
||||||
triggers = triggers.concat(state.eventNow.triggers);
|
triggers = triggers.concat(store.eventNow.triggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no onStop triggers in event
|
// note: there are no onStop triggers in event
|
||||||
@@ -51,9 +53,9 @@ export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
|
|||||||
if (!automation || automation.outputs.length === 0) {
|
if (!automation || automation.outputs.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const shouldSend = testConditions(automation.filters, automation.filterRule, state);
|
const shouldSend = testConditions(automation.filters, automation.filterRule, store);
|
||||||
if (shouldSend) {
|
if (shouldSend) {
|
||||||
send(automation.outputs, state);
|
send(automation.outputs, store);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,8 @@ export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
|
|||||||
* Exposes a method for bypassing the condition check and testing the sending of an output
|
* Exposes a method for bypassing the condition check and testing the sending of an output
|
||||||
*/
|
*/
|
||||||
export function testOutput(payload: AutomationOutput) {
|
export function testOutput(payload: AutomationOutput) {
|
||||||
send([payload]);
|
const store = eventStore.poll();
|
||||||
|
send([payload], store);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +74,7 @@ export function testOutput(payload: AutomationOutput) {
|
|||||||
export function testConditions(
|
export function testConditions(
|
||||||
filters: AutomationFilter[],
|
filters: AutomationFilter[],
|
||||||
filterRule: FilterRule,
|
filterRule: FilterRule,
|
||||||
state: Partial<RuntimeState>,
|
store: Partial<RuntimeStore>,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (filters.length === 0) {
|
if (filters.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
@@ -86,7 +89,7 @@ export function testConditions(
|
|||||||
function evaluateCondition(filter: AutomationFilter): boolean {
|
function evaluateCondition(filter: AutomationFilter): boolean {
|
||||||
const { field, operator, value } = filter;
|
const { field, operator, value } = filter;
|
||||||
const lowerCasedValue = value.toLowerCase();
|
const lowerCasedValue = value.toLowerCase();
|
||||||
const fieldValue = getPropertyFromPath(field, state);
|
const fieldValue = getPropertyFromPath(field, store);
|
||||||
|
|
||||||
// if value is empty string, the user could be meaning to check if the value does not exist
|
// if value is empty string, the user could be meaning to check if the value does not exist
|
||||||
// we use loose equality to be able to check for converted values (eg '10' == 10)
|
// we use loose equality to be able to check for converted values (eg '10' == 10)
|
||||||
@@ -115,13 +118,12 @@ export function testConditions(
|
|||||||
* Handles preparing and sending of the data
|
* Handles preparing and sending of the data
|
||||||
* Returns a boolean indicating whether a message was sent
|
* Returns a boolean indicating whether a message was sent
|
||||||
*/
|
*/
|
||||||
function send(output: AutomationOutput[], state?: RuntimeState) {
|
function send(output: AutomationOutput[], store: RuntimeStore) {
|
||||||
const stateSnapshot = state ?? getState();
|
|
||||||
output.forEach((payload) => {
|
output.forEach((payload) => {
|
||||||
if (isOSCOutput(payload) && !isOntimeCloud) {
|
if (isOSCOutput(payload) && !isOntimeCloud) {
|
||||||
emitOSC(payload, stateSnapshot);
|
emitOSC(payload, store);
|
||||||
} else if (isHTTPOutput(payload)) {
|
} else if (isHTTPOutput(payload)) {
|
||||||
emitHTTP(payload, stateSnapshot);
|
emitHTTP(payload, store);
|
||||||
} else if (isOntimeAction(payload)) {
|
} else if (isOntimeAction(payload)) {
|
||||||
toOntimeAction(payload);
|
toOntimeAction(payload);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { HTTPOutput, LogOrigin } from 'ontime-types';
|
import { DeepReadonly } from 'ts-essentials';
|
||||||
|
import { HTTPOutput, LogOrigin, RuntimeStore } from 'ontime-types';
|
||||||
|
|
||||||
import { logger } from '../../../classes/Logger.js';
|
import { logger } from '../../../classes/Logger.js';
|
||||||
import type { RuntimeState } from '../../../stores/runtimeState.js';
|
|
||||||
|
|
||||||
import { parseTemplateNested } from '../automation.utils.js';
|
import { parseTemplateNested } from '../automation.utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose possibility to send a message using HTTP protocol
|
* Expose possibility to send a message using HTTP protocol
|
||||||
*/
|
*/
|
||||||
export function emitHTTP(output: HTTPOutput, state: RuntimeState) {
|
export function emitHTTP(output: HTTPOutput, store: DeepReadonly<RuntimeStore>) {
|
||||||
const url = preparePayload(output, state);
|
const url = preparePayload(output, store);
|
||||||
emit(url);
|
emit(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses the state and prepares payload to be emitted */
|
/** Parses the state and prepares payload to be emitted */
|
||||||
function preparePayload(output: HTTPOutput, state: RuntimeState): string {
|
function preparePayload(output: HTTPOutput, state: DeepReadonly<RuntimeStore>): string {
|
||||||
const parsedUrl = parseTemplateNested(output.url, state);
|
const parsedUrl = parseTemplateNested(output.url, state);
|
||||||
return parsedUrl;
|
return parsedUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { LogOrigin, OSCOutput } from 'ontime-types';
|
import { LogOrigin, OSCOutput, RuntimeStore } from 'ontime-types';
|
||||||
|
|
||||||
import { type OscPacketInput, toBuffer as oscPacketToBuffer } from 'osc-min';
|
import { type OscPacketInput, toBuffer as oscPacketToBuffer } from 'osc-min';
|
||||||
import * as dgram from 'node:dgram';
|
import * as dgram from 'node:dgram';
|
||||||
|
|
||||||
import { logger } from '../../../classes/Logger.js';
|
import { logger } from '../../../classes/Logger.js';
|
||||||
import { type RuntimeState } from '../../../stores/runtimeState.js';
|
|
||||||
import { parseTemplateNested, stringToOSCArgs } from '../automation.utils.js';
|
import { parseTemplateNested, stringToOSCArgs } from '../automation.utils.js';
|
||||||
|
import { DeepReadonly } from 'ts-essentials';
|
||||||
|
|
||||||
const udpClient = dgram.createSocket('udp4');
|
const udpClient = dgram.createSocket('udp4');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose possibility to send a message using OSC protocol
|
* Expose possibility to send a message using OSC protocol
|
||||||
*/
|
*/
|
||||||
export function emitOSC(output: OSCOutput, state: RuntimeState) {
|
export function emitOSC(output: OSCOutput, store: DeepReadonly<RuntimeStore>) {
|
||||||
const message = preparePayload(output, state);
|
const message = preparePayload(output, store);
|
||||||
emit(output.targetIP, output.targetPort, message);
|
emit(output.targetIP, output.targetPort, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses the state and prepares payload to be emitted */
|
/** Parses the state and prepares payload to be emitted */
|
||||||
function preparePayload(output: OSCOutput, state: RuntimeState): OscPacketInput {
|
function preparePayload(output: OSCOutput, store: DeepReadonly<RuntimeStore>): OscPacketInput {
|
||||||
// check for templates in the address
|
// check for templates in the address
|
||||||
const parsedAddress = parseTemplateNested(output.address, state);
|
const parsedAddress = parseTemplateNested(output.address, store);
|
||||||
|
|
||||||
// check for templates in the arguments
|
// check for templates in the arguments
|
||||||
const parsedArguments = output.args ? parseTemplateNested(output.args, state) : undefined;
|
const parsedArguments = output.args ? parseTemplateNested(output.args, store) : undefined;
|
||||||
// check we have the correct type
|
// check we have the correct type
|
||||||
const oscArguments = stringToOSCArgs(parsedArguments);
|
const oscArguments = stringToOSCArgs(parsedArguments);
|
||||||
return { address: parsedAddress, args: oscArguments };
|
return { address: parsedAddress, args: oscArguments };
|
||||||
|
|||||||
@@ -77,11 +77,11 @@ class RuntimeService {
|
|||||||
if (timerPhaseChanged) {
|
if (timerPhaseChanged) {
|
||||||
if (newState.timer.phase === TimerPhase.Warning) {
|
if (newState.timer.phase === TimerPhase.Warning) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onWarning, newState);
|
triggerAutomations(TimerLifeCycle.onWarning);
|
||||||
});
|
});
|
||||||
} else if (newState.timer.phase === TimerPhase.Danger) {
|
} else if (newState.timer.phase === TimerPhase.Danger) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onDanger, newState);
|
triggerAutomations(TimerLifeCycle.onDanger);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ class RuntimeService {
|
|||||||
} else if (hasTimerFinished) {
|
} else if (hasTimerFinished) {
|
||||||
// if the timer has finished, we need to load next and keep rolling
|
// if the timer has finished, we need to load next and keep rolling
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onFinish, newState);
|
triggerAutomations(TimerLifeCycle.onFinish);
|
||||||
});
|
});
|
||||||
this.handleLoadNext();
|
this.handleLoadNext();
|
||||||
this.rollLoaded(newState.offset);
|
this.rollLoaded(newState.offset);
|
||||||
@@ -116,7 +116,7 @@ class RuntimeService {
|
|||||||
// 3. find if we need to process actions related to the timer finishing
|
// 3. find if we need to process actions related to the timer finishing
|
||||||
if (newState.timer.playback === Playback.Play && hasTimerFinished) {
|
if (newState.timer.playback === Playback.Play && hasTimerFinished) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onFinish, newState);
|
triggerAutomations(TimerLifeCycle.onFinish);
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle end action if there was a timer playing
|
// handle end action if there was a timer playing
|
||||||
@@ -134,7 +134,7 @@ class RuntimeService {
|
|||||||
const shouldUpdateTimer = isNewSecond(this.lastIntegrationTimerValue, newState.timer.current);
|
const shouldUpdateTimer = isNewSecond(this.lastIntegrationTimerValue, newState.timer.current);
|
||||||
if (shouldUpdateTimer) {
|
if (shouldUpdateTimer) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onUpdate, newState);
|
triggerAutomations(TimerLifeCycle.onUpdate);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lastIntegrationTimerValue = newState.timer.current ?? -1;
|
this.lastIntegrationTimerValue = newState.timer.current ?? -1;
|
||||||
@@ -144,7 +144,7 @@ class RuntimeService {
|
|||||||
const shouldUpdateClock = getShouldClockUpdate(this.lastIntegrationClockUpdate, newState.clock);
|
const shouldUpdateClock = getShouldClockUpdate(this.lastIntegrationClockUpdate, newState.clock);
|
||||||
if (shouldUpdateClock) {
|
if (shouldUpdateClock) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onClock, newState);
|
triggerAutomations(TimerLifeCycle.onClock);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lastIntegrationClockUpdate = newState.clock;
|
this.lastIntegrationClockUpdate = newState.clock;
|
||||||
@@ -209,10 +209,9 @@ class RuntimeService {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
||||||
const newState = runtimeState.getState();
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
||||||
triggerAutomations(TimerLifeCycle.onLoad, newState);
|
triggerAutomations(TimerLifeCycle.onLoad);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
@@ -447,7 +446,7 @@ class RuntimeService {
|
|||||||
if (didStart) {
|
if (didStart) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
||||||
triggerAutomations(TimerLifeCycle.onStart, newState);
|
triggerAutomations(TimerLifeCycle.onStart);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return didStart;
|
return didStart;
|
||||||
@@ -500,7 +499,7 @@ class RuntimeService {
|
|||||||
const newState = runtimeState.getState();
|
const newState = runtimeState.getState();
|
||||||
logger.info(LogOrigin.Playback, `Play Mode ${newState.timer.playback.toUpperCase()}`);
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.timer.playback.toUpperCase()}`);
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerAutomations(TimerLifeCycle.onPause, newState);
|
triggerAutomations(TimerLifeCycle.onPause);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +519,7 @@ class RuntimeService {
|
|||||||
logger.info(LogOrigin.Playback, `Play Mode ${newState.timer.playback.toUpperCase()}`);
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.timer.playback.toUpperCase()}`);
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
||||||
triggerAutomations(TimerLifeCycle.onStop, newState);
|
triggerAutomations(TimerLifeCycle.onStop);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -553,7 +552,7 @@ class RuntimeService {
|
|||||||
const newState = runtimeState.getState();
|
const newState = runtimeState.getState();
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
||||||
triggerAutomations(TimerLifeCycle.onStart, newState);
|
triggerAutomations(TimerLifeCycle.onStart);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -584,14 +583,14 @@ class RuntimeService {
|
|||||||
logger.info(LogOrigin.Playback, `Loaded event with ID ${result.eventId}`);
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${result.eventId}`);
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
triggerReportEntry(TimerLifeCycle.onStop, previousState);
|
||||||
triggerAutomations(TimerLifeCycle.onLoad, newState);
|
triggerAutomations(TimerLifeCycle.onLoad);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.didStart) {
|
if (result.didStart) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
triggerReportEntry(TimerLifeCycle.onStart, newState);
|
||||||
triggerAutomations(TimerLifeCycle.onStart, newState);
|
triggerAutomations(TimerLifeCycle.onStart);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { RuntimeStore, runtimeStorePlaceholder } from 'ontime-types';
|
||||||
|
import { deepmerge } from 'ontime-utils';
|
||||||
|
|
||||||
|
const baseStore: RuntimeStore = {
|
||||||
|
...runtimeStorePlaceholder
|
||||||
|
};
|
||||||
|
|
||||||
|
export function makeRuntimeStoreData(patch?: Partial<RuntimeStore>): RuntimeStore {
|
||||||
|
return deepmerge(baseStore, patch) as RuntimeStore;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user