mirror of
https://github.com/nasa/openmct.git
synced 2025-05-07 02:58:30 +00:00
Ensures correct results are returned for conditions and criteria for a given telemetry datapoint (#2904)
* Ensures that results for a specific datapoint are evaluated atomically. * Remove generating timestamp for telemetry data * Get results directly instead of using events * Refactor all/any telemetry criterion to use new evaluator * Use current timesystem to compare latest * AllTelemetryCriterion extends TelemetryCriterion Co-authored-by: David Tsay <david.e.tsay@nasa.gov> Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
11f2c35bb2
commit
26838635b6
@ -23,8 +23,7 @@
|
|||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
import { TRIGGER } from "./utils/constants";
|
import { evaluateResults } from './utils/evaluator';
|
||||||
import {computeCondition, computeConditionByLimit} from "./utils/evaluator";
|
|
||||||
import { getLatestTimestamp } from './utils/time';
|
import { getLatestTimestamp } from './utils/time';
|
||||||
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
||||||
|
|
||||||
@ -57,29 +56,41 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
this.conditionManager = conditionManager;
|
this.conditionManager = conditionManager;
|
||||||
this.id = conditionConfiguration.id;
|
this.id = conditionConfiguration.id;
|
||||||
this.criteria = [];
|
this.criteria = [];
|
||||||
this.criteriaResults = {};
|
|
||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
this.latestTimestamp = {};
|
|
||||||
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
||||||
if (conditionConfiguration.configuration.criteria) {
|
if (conditionConfiguration.configuration.criteria) {
|
||||||
this.createCriteria(conditionConfiguration.configuration.criteria);
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
||||||
}
|
}
|
||||||
this.trigger = conditionConfiguration.configuration.trigger;
|
this.trigger = conditionConfiguration.configuration.trigger;
|
||||||
this.conditionManager.on('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBroadcastTelemetry(datum) {
|
getResult(datum) {
|
||||||
if (!datum || !datum.id) {
|
if (!datum || !datum.id) {
|
||||||
console.log('no data received');
|
console.log('no data received');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.isTelemetryUsed(datum.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.criteria.forEach(criterion => {
|
this.criteria.forEach(criterion => {
|
||||||
if (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any')) {
|
if (this.isAnyOrAllTelemetry(criterion)) {
|
||||||
criterion.handleSubscription(datum, this.conditionManager.telemetryObjects);
|
criterion.getResult(datum, this.conditionManager.telemetryObjects);
|
||||||
} else {
|
} else {
|
||||||
criterion.emit(`subscription:${datum.id}`, datum);
|
criterion.getResult(datum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnyOrAllTelemetry(criterion) {
|
||||||
|
return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any'));
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelemetryUsed(id) {
|
||||||
|
return this.criteria.some(criterion => {
|
||||||
|
return this.isAnyOrAllTelemetry(criterion) || criterion.telemetryObjectIdAsString === id;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(conditionConfiguration) {
|
update(conditionConfiguration) {
|
||||||
@ -90,7 +101,6 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
updateTrigger(trigger) {
|
updateTrigger(trigger) {
|
||||||
if (this.trigger !== trigger) {
|
if (this.trigger !== trigger) {
|
||||||
this.trigger = trigger;
|
this.trigger = trigger;
|
||||||
this.handleConditionUpdated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +144,6 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
||||||
}
|
}
|
||||||
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
if (!this.criteria) {
|
if (!this.criteria) {
|
||||||
this.criteria = [];
|
this.criteria = [];
|
||||||
}
|
}
|
||||||
@ -163,20 +172,11 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
||||||
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
||||||
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
|
|
||||||
let criterion = found.item;
|
let criterion = found.item;
|
||||||
criterion.unsubscribe();
|
criterion.unsubscribe();
|
||||||
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
this.criteria.splice(found.index, 1, newCriterion);
|
this.criteria.splice(found.index, 1, newCriterion);
|
||||||
delete this.criteriaResults[criterion.id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCriterion(id) {
|
|
||||||
if (this.destroyCriterion(id)) {
|
|
||||||
this.handleConditionUpdated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +189,6 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
});
|
});
|
||||||
criterion.destroy();
|
criterion.destroy();
|
||||||
this.criteria.splice(found.index, 1);
|
this.criteria.splice(found.index, 1);
|
||||||
delete this.criteriaResults[criterion.id];
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -200,27 +199,9 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
let found = this.findCriterion(criterion.id);
|
let found = this.findCriterion(criterion.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
this.criteria[found.index] = criterion.data;
|
this.criteria[found.index] = criterion.data;
|
||||||
// TODO nothing is listening to this
|
|
||||||
this.emitEvent('conditionUpdated', {
|
|
||||||
trigger: this.trigger,
|
|
||||||
criteria: this.criteria
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCriteriaResults(eventData) {
|
|
||||||
const id = eventData.id;
|
|
||||||
|
|
||||||
if (this.findCriterion(id)) {
|
|
||||||
this.criteriaResults[id] = !!eventData.data.result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCriterionResult(eventData) {
|
|
||||||
this.updateCriteriaResults(eventData);
|
|
||||||
this.handleConditionUpdated(eventData.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLADConditionResult() {
|
requestLADConditionResult() {
|
||||||
let latestTimestamp;
|
let latestTimestamp;
|
||||||
let criteriaResults = {};
|
let criteriaResults = {};
|
||||||
@ -237,28 +218,21 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
latestTimestamp = getLatestTimestamp(
|
latestTimestamp = getLatestTimestamp(
|
||||||
latestTimestamp,
|
latestTimestamp,
|
||||||
data,
|
data,
|
||||||
this.timeSystems
|
this.timeSystems,
|
||||||
|
this.openmct.time.timeSystem()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
data: Object.assign({}, latestTimestamp, { result: this.evaluate(criteriaResults) })
|
data: Object.assign(
|
||||||
}
|
{},
|
||||||
|
latestTimestamp,
|
||||||
|
{ result: evaluateResults(Object.values(criteriaResults), this.trigger) }
|
||||||
|
)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTelemetrySubscriptions() {
|
|
||||||
return this.criteria.map(criterion => criterion.telemetryObjectIdAsString);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConditionUpdated(datum) {
|
|
||||||
// trigger an updated event so that consumers can react accordingly
|
|
||||||
this.result = this.evaluate(this.criteriaResults);
|
|
||||||
this.emitEvent('conditionResultUpdated',
|
|
||||||
Object.assign({}, datum, { result: this.result })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCriteria() {
|
getCriteria() {
|
||||||
return this.criteria;
|
return this.criteria;
|
||||||
}
|
}
|
||||||
@ -272,28 +246,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate(results) {
|
|
||||||
if (this.trigger && this.trigger === TRIGGER.XOR) {
|
|
||||||
return computeConditionByLimit(results, 1);
|
|
||||||
} else if (this.trigger && this.trigger === TRIGGER.NOT) {
|
|
||||||
return computeConditionByLimit(results, 0);
|
|
||||||
} else {
|
|
||||||
return computeCondition(results, this.trigger === TRIGGER.ALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitEvent(eventName, data) {
|
|
||||||
this.emit(eventName, {
|
|
||||||
id: this.id,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.conditionManager.off('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
|
||||||
if (typeof this.stopObservingForChanges === 'function') {
|
|
||||||
this.stopObservingForChanges();
|
|
||||||
}
|
|
||||||
this.destroyCriteria();
|
this.destroyCriteria();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.conditionSetDomainObject = conditionSetDomainObject;
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
this.timeAPI = this.openmct.time;
|
|
||||||
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
||||||
this.composition = this.openmct.composition.get(conditionSetDomainObject);
|
this.composition = this.openmct.composition.get(conditionSetDomainObject);
|
||||||
this.composition.on('add', this.subscribeToTelemetry, this);
|
this.composition.on('add', this.subscribeToTelemetry, this);
|
||||||
@ -56,8 +55,9 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
|
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
|
||||||
this.subscriptions[id] = this.openmct.telemetry.subscribe(
|
this.subscriptions[id] = this.openmct.telemetry.subscribe(
|
||||||
endpoint,
|
endpoint,
|
||||||
this.broadcastTelemetry.bind(this, id)
|
this.telemetryReceived.bind(this, id)
|
||||||
);
|
);
|
||||||
|
// TODO check if this is needed
|
||||||
this.updateConditionTelemetry();
|
this.updateConditionTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.conditionResults = {};
|
|
||||||
this.conditionClassCollection = [];
|
this.conditionClassCollection = [];
|
||||||
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
@ -96,7 +95,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
|
|
||||||
initCondition(conditionConfiguration, index) {
|
initCondition(conditionConfiguration, index) {
|
||||||
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
||||||
condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
|
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
this.conditionClassCollection.splice(index + 1, 0, condition);
|
this.conditionClassCollection.splice(index + 1, 0, condition);
|
||||||
} else {
|
} else {
|
||||||
@ -160,13 +158,10 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
|
|
||||||
removeCondition(index) {
|
removeCondition(index) {
|
||||||
let condition = this.conditionClassCollection[index];
|
let condition = this.conditionClassCollection[index];
|
||||||
condition.destroyCriteria();
|
condition.destroy();
|
||||||
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
|
|
||||||
this.conditionClassCollection.splice(index, 1);
|
this.conditionClassCollection.splice(index, 1);
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
||||||
delete this.conditionResults[condition.id];
|
|
||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
this.handleConditionResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findConditionById(id) {
|
findConditionById(id) {
|
||||||
@ -184,7 +179,23 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentCondition(conditionResults) {
|
getCurrentCondition() {
|
||||||
|
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
|
||||||
|
let currentCondition = conditionCollection[conditionCollection.length-1];
|
||||||
|
|
||||||
|
for (let i = 0; i < conditionCollection.length - 1; i++) {
|
||||||
|
const condition = this.findConditionById(conditionCollection[i].id)
|
||||||
|
if (condition.result) {
|
||||||
|
//first condition to be true wins
|
||||||
|
currentCondition = conditionCollection[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentConditionLAD(conditionResults) {
|
||||||
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
|
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
|
||||||
let currentCondition = conditionCollection[conditionCollection.length-1];
|
let currentCondition = conditionCollection[conditionCollection.length-1];
|
||||||
|
|
||||||
@ -198,36 +209,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
return currentCondition;
|
return currentCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConditionResults(resultObj) {
|
|
||||||
if (!resultObj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = resultObj.id;
|
|
||||||
|
|
||||||
if (this.findConditionById(id)) {
|
|
||||||
this.conditionResults[id] = resultObj.data.result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConditionResult(resultObj) {
|
|
||||||
this.updateConditionResults(resultObj);
|
|
||||||
const currentCondition = this.getCurrentCondition(this.conditionResults);
|
|
||||||
const timestamp = JSON.parse(JSON.stringify(resultObj.data))
|
|
||||||
delete timestamp.result
|
|
||||||
|
|
||||||
this.emit('conditionSetResultUpdated',
|
|
||||||
Object.assign(
|
|
||||||
{
|
|
||||||
output: currentCondition.configuration.output,
|
|
||||||
id: this.conditionSetDomainObject.identifier,
|
|
||||||
conditionId: currentCondition.id
|
|
||||||
},
|
|
||||||
timestamp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLADConditionSetOutput() {
|
requestLADConditionSetOutput() {
|
||||||
if (!this.conditionClassCollection.length) {
|
if (!this.conditionClassCollection.length) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
@ -249,10 +230,11 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
latestTimestamp = getLatestTimestamp(
|
latestTimestamp = getLatestTimestamp(
|
||||||
latestTimestamp,
|
latestTimestamp,
|
||||||
data,
|
data,
|
||||||
this.timeSystems
|
this.timeSystems,
|
||||||
|
this.openmct.time.timeSystem()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const currentCondition = this.getCurrentCondition(conditionResults);
|
const currentCondition = this.getCurrentConditionLAD(conditionResults);
|
||||||
|
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
@ -266,8 +248,28 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastTelemetry(id, datum) {
|
telemetryReceived(id, datum) {
|
||||||
this.emit(`broadcastTelemetry`, Object.assign({}, this.createNormalizedDatum(datum, id), {id: id}));
|
const normalizedDatum = this.createNormalizedDatum(datum, id);
|
||||||
|
const timeSystemKey = this.openmct.time.timeSystem().key;
|
||||||
|
let timestamp = {};
|
||||||
|
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
|
||||||
|
|
||||||
|
this.conditionClassCollection.forEach(condition => {
|
||||||
|
condition.getResult(normalizedDatum);
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentCondition = this.getCurrentCondition();
|
||||||
|
|
||||||
|
this.emit('conditionSetResultUpdated',
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
output: currentCondition.configuration.output,
|
||||||
|
id: this.conditionSetDomainObject.identifier,
|
||||||
|
conditionId: currentCondition.id
|
||||||
|
},
|
||||||
|
timestamp
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTestData(metadatum) {
|
getTestData(metadatum) {
|
||||||
@ -282,12 +284,16 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createNormalizedDatum(telemetryDatum, id) {
|
createNormalizedDatum(telemetryDatum, id) {
|
||||||
return Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((normalizedDatum, metadatum) => {
|
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => {
|
||||||
const testValue = this.getTestData(metadatum);
|
const testValue = this.getTestData(metadatum);
|
||||||
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
||||||
normalizedDatum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
|
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
|
||||||
return normalizedDatum;
|
return datum;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
normalizedDatum.id = id;
|
||||||
|
|
||||||
|
return normalizedDatum;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTestData(testData) {
|
updateTestData(testData) {
|
||||||
@ -310,7 +316,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.conditionClassCollection.forEach((condition) => {
|
this.conditionClassCollection.forEach((condition) => {
|
||||||
condition.off('conditionResultUpdated', this.handleConditionResult);
|
|
||||||
condition.destroy();
|
condition.destroy();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ describe('ConditionManager', () => {
|
|||||||
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
||||||
|
|
||||||
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
||||||
conditionMgr.on('broadcastTelemetry', mockListener);
|
conditionMgr.on('telemetryReceived', mockListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a conditionCollection with a default condition', function () {
|
it('creates a conditionCollection with a default condition', function () {
|
||||||
|
@ -25,12 +25,11 @@ import {TRIGGER} from "./utils/constants";
|
|||||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
|
|
||||||
let openmct = {},
|
let openmct = {},
|
||||||
mockListener,
|
|
||||||
testConditionDefinition,
|
testConditionDefinition,
|
||||||
testTelemetryObject,
|
testTelemetryObject,
|
||||||
conditionObj,
|
conditionObj,
|
||||||
conditionManager,
|
conditionManager,
|
||||||
mockBroadcastTelemetry,
|
mockTelemetryReceived,
|
||||||
mockTimeSystems;
|
mockTimeSystems;
|
||||||
|
|
||||||
describe("The condition", function () {
|
describe("The condition", function () {
|
||||||
@ -39,10 +38,9 @@ describe("The condition", function () {
|
|||||||
conditionManager = jasmine.createSpyObj('conditionManager',
|
conditionManager = jasmine.createSpyObj('conditionManager',
|
||||||
['on']
|
['on']
|
||||||
);
|
);
|
||||||
mockBroadcastTelemetry = jasmine.createSpy('listener');
|
mockTelemetryReceived = jasmine.createSpy('listener');
|
||||||
conditionManager.on('broadcastTelemetry', mockBroadcastTelemetry);
|
conditionManager.on('telemetryReceived', mockTelemetryReceived);
|
||||||
|
|
||||||
mockListener = jasmine.createSpy('listener');
|
|
||||||
testTelemetryObject = {
|
testTelemetryObject = {
|
||||||
identifier:{ namespace: "", key: "test-object"},
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
type: "test-object",
|
type: "test-object",
|
||||||
@ -104,8 +102,6 @@ describe("The condition", function () {
|
|||||||
openmct,
|
openmct,
|
||||||
conditionManager
|
conditionManager
|
||||||
);
|
);
|
||||||
|
|
||||||
conditionObj.on('conditionUpdated', mockListener);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("generates criteria with the correct properties", function () {
|
it("generates criteria with the correct properties", function () {
|
||||||
|
@ -20,11 +20,10 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import EventEmitter from 'EventEmitter';
|
import TelemetryCriterion from './TelemetryCriterion';
|
||||||
import {OPERATIONS} from '../utils/operations';
|
import { evaluateResults } from "../utils/evaluator";
|
||||||
import {computeCondition} from "@/plugins/condition/utils/evaluator";
|
|
||||||
|
|
||||||
export default class TelemetryCriterion extends EventEmitter {
|
export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes/Unsubscribes to telemetry and emits the result
|
* Subscribes/Unsubscribes to telemetry and emits the result
|
||||||
@ -34,23 +33,20 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
* @param openmct
|
* @param openmct
|
||||||
*/
|
*/
|
||||||
constructor(telemetryDomainObjectDefinition, openmct) {
|
constructor(telemetryDomainObjectDefinition, openmct) {
|
||||||
super();
|
super(telemetryDomainObjectDefinition, openmct);
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct = openmct;
|
initialize() {
|
||||||
this.objectAPI = this.openmct.objects;
|
this.telemetryObjects = { ...this.telemetryDomainObjectDefinition.telemetryObjects };
|
||||||
this.telemetryAPI = this.openmct.telemetry;
|
|
||||||
this.timeAPI = this.openmct.time;
|
|
||||||
this.id = telemetryDomainObjectDefinition.id;
|
|
||||||
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
|
||||||
this.operation = telemetryDomainObjectDefinition.operation;
|
|
||||||
this.telemetryObjects = Object.assign({}, telemetryDomainObjectDefinition.telemetryObjects);
|
|
||||||
this.input = telemetryDomainObjectDefinition.input;
|
|
||||||
this.metadata = telemetryDomainObjectDefinition.metadata;
|
|
||||||
this.telemetryDataCache = {};
|
this.telemetryDataCache = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetry(telemetryObjects) {
|
||||||
this.telemetryObjects = Object.assign({}, telemetryObjects);
|
this.telemetryObjects = { ...telemetryObjects };
|
||||||
}
|
}
|
||||||
|
|
||||||
formatData(data, telemetryObjects) {
|
formatData(data, telemetryObjects) {
|
||||||
@ -68,60 +64,32 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const datum = {
|
const datum = {
|
||||||
result: computeCondition(this.telemetryDataCache, this.telemetry === 'all')
|
result: evaluateResults(Object.values(this.telemetryDataCache), this.telemetry)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// TODO check back to see if we should format times here
|
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
datum[timeSystem.key] = data[timeSystem.key]
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return datum;
|
return datum;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscription(data, telemetryObjects) {
|
getResult(data, telemetryObjects) {
|
||||||
if(this.isValid()) {
|
const validatedData = this.isValid() ? data : {};
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData(data, telemetryObjects));
|
|
||||||
} else {
|
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData({}, telemetryObjects));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findOperation(operation) {
|
if (validatedData) {
|
||||||
for (let i=0; i < OPERATIONS.length; i++) {
|
this.telemetryDataCache[validatedData.id] = this.computeResult(validatedData);
|
||||||
if (operation === OPERATIONS[i].name) {
|
|
||||||
return OPERATIONS[i].operation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
computeResult(data) {
|
Object.values(telemetryObjects).forEach(telemetryObject => {
|
||||||
let result = false;
|
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
if (data) {
|
if (this.telemetryDataCache[id] === undefined) {
|
||||||
let comparator = this.findOperation(this.operation);
|
this.telemetryDataCache[id] = false;
|
||||||
let params = [];
|
|
||||||
params.push(data[this.metadata]);
|
|
||||||
if (this.input instanceof Array && this.input.length) {
|
|
||||||
this.input.forEach(input => params.push(input));
|
|
||||||
}
|
}
|
||||||
if (typeof comparator === 'function') {
|
|
||||||
result = comparator(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
emitEvent(eventName, data) {
|
|
||||||
this.emit(eventName, {
|
|
||||||
id: this.id,
|
|
||||||
data: data
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
isValid() {
|
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||||
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLAD(options) {
|
requestLAD(options) {
|
||||||
@ -139,7 +107,7 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
|
|
||||||
let keys = Object.keys(Object.assign({}, options.telemetryObjects));
|
let keys = Object.keys(Object.assign({}, options.telemetryObjects));
|
||||||
const telemetryRequests = keys
|
const telemetryRequests = keys
|
||||||
.map(key => this.telemetryAPI.request(
|
.map(key => this.openmct.telemetry.request(
|
||||||
options.telemetryObjects[key],
|
options.telemetryObjects[key],
|
||||||
options
|
options
|
||||||
));
|
));
|
||||||
@ -163,10 +131,7 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.emitEvent('criterionRemoved');
|
|
||||||
delete this.telemetryObjects;
|
delete this.telemetryObjects;
|
||||||
delete this.telemetryDataCache;
|
delete this.telemetryDataCache;
|
||||||
delete this.telemetryObjectIdAsString;
|
|
||||||
delete this.telemetryObject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
import {OPERATIONS} from '../utils/operations';
|
import { OPERATIONS } from '../utils/operations';
|
||||||
|
|
||||||
export default class TelemetryCriterion extends EventEmitter {
|
export default class TelemetryCriterion extends EventEmitter {
|
||||||
|
|
||||||
@ -36,20 +36,27 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.objectAPI = this.openmct.objects;
|
this.telemetryDomainObjectDefinition = telemetryDomainObjectDefinition;
|
||||||
this.telemetryAPI = this.openmct.telemetry;
|
|
||||||
this.timeAPI = this.openmct.time;
|
|
||||||
this.id = telemetryDomainObjectDefinition.id;
|
this.id = telemetryDomainObjectDefinition.id;
|
||||||
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
||||||
this.operation = telemetryDomainObjectDefinition.operation;
|
this.operation = telemetryDomainObjectDefinition.operation;
|
||||||
this.input = telemetryDomainObjectDefinition.input;
|
this.input = telemetryDomainObjectDefinition.input;
|
||||||
this.metadata = telemetryDomainObjectDefinition.metadata;
|
this.metadata = telemetryDomainObjectDefinition.metadata;
|
||||||
this.telemetryObject = telemetryDomainObjectDefinition.telemetryObject;
|
this.result = undefined;
|
||||||
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(telemetryDomainObjectDefinition.telemetry);
|
|
||||||
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
this.initialize();
|
||||||
this.emitEvent('criterionUpdated', this);
|
this.emitEvent('criterionUpdated', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
|
||||||
|
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return this.telemetryObject && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetry(telemetryObjects) {
|
||||||
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
||||||
}
|
}
|
||||||
@ -60,20 +67,44 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// TODO check back to see if we should format times here
|
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
datum[timeSystem.key] = data[timeSystem.key]
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return datum;
|
return datum;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscription(data) {
|
getResult(data) {
|
||||||
if(this.isValid()) {
|
const validatedData = this.isValid() ? data : {};
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData(data));
|
this.result = this.computeResult(validatedData);
|
||||||
} else {
|
}
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData({}));
|
|
||||||
|
requestLAD(options) {
|
||||||
|
options = Object.assign({},
|
||||||
|
options,
|
||||||
|
{
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.isValid()) {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData({})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.openmct.telemetry.request(
|
||||||
|
this.telemetryObject,
|
||||||
|
options
|
||||||
|
).then(results => {
|
||||||
|
const latestDatum = results.length ? results[results.length - 1] : {};
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData(latestDatum)
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findOperation(operation) {
|
findOperation(operation) {
|
||||||
@ -95,7 +126,7 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
this.input.forEach(input => params.push(input));
|
this.input.forEach(input => params.push(input));
|
||||||
}
|
}
|
||||||
if (typeof comparator === 'function') {
|
if (typeof comparator === 'function') {
|
||||||
result = comparator(params);
|
result = !!comparator(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -108,42 +139,9 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
|
||||||
return this.telemetryObject && this.metadata && this.operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLAD(options) {
|
|
||||||
options = Object.assign({},
|
|
||||||
options,
|
|
||||||
{
|
|
||||||
strategy: 'latest',
|
|
||||||
size: 1
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!this.isValid()) {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
data: this.formatData({})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.telemetryAPI.request(
|
|
||||||
this.telemetryObject,
|
|
||||||
options
|
|
||||||
).then(results => {
|
|
||||||
const latestDatum = results.length ? results[results.length - 1] : {};
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
data: this.formatData(latestDatum)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.off(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
|
||||||
this.emitEvent('criterionRemoved');
|
|
||||||
delete this.telemetryObjectIdAsString;
|
|
||||||
delete this.telemetryObject;
|
delete this.telemetryObject;
|
||||||
|
delete this.telemetryObjectIdAsString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ describe("The telemetry criterion", function () {
|
|||||||
key: "testSource",
|
key: "testSource",
|
||||||
source: "value",
|
source: "value",
|
||||||
name: "Test",
|
name: "Test",
|
||||||
format: "enum"
|
format: "string"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -80,8 +80,9 @@ describe("The telemetry criterion", function () {
|
|||||||
testCriterionDefinition = {
|
testCriterionDefinition = {
|
||||||
id: 'test-criterion-id',
|
id: 'test-criterion-id',
|
||||||
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
|
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
|
||||||
operation: 'lessThan',
|
operation: 'textContains',
|
||||||
metadata: 'sin',
|
metadata: 'value',
|
||||||
|
input: ['Hell'],
|
||||||
telemetryObject: testTelemetryObject
|
telemetryObject: testTelemetryObject
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,12 +101,21 @@ describe("The telemetry criterion", function () {
|
|||||||
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
|
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates and emits event on new data from telemetry providers", function () {
|
it("returns a result on new data from relevant telemetry providers", function () {
|
||||||
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
|
telemetryCriterion.getResult({
|
||||||
telemetryCriterion.handleSubscription({
|
|
||||||
value: 'Hello',
|
value: 'Hello',
|
||||||
utc: 'Hi'
|
utc: 'Hi',
|
||||||
|
id: testTelemetryObject.identifier.key
|
||||||
});
|
});
|
||||||
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
|
expect(telemetryCriterion.result).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// it("does not return a result on new data from irrelavant telemetry providers", function () {
|
||||||
|
// telemetryCriterion.getResult({
|
||||||
|
// value: 'Hello',
|
||||||
|
// utc: 'Hi',
|
||||||
|
// id: '1234'
|
||||||
|
// });
|
||||||
|
// expect(telemetryCriterion.result).toBeFalse();
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
|
@ -19,36 +19,50 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
import { TRIGGER } from "./constants";
|
||||||
|
|
||||||
export const computeCondition = (resultMap, allMustBeTrue) => {
|
export const evaluateResults = (results, trigger) => {
|
||||||
let result = false;
|
if (trigger && trigger === TRIGGER.XOR) {
|
||||||
for (let key in resultMap) {
|
return matchExact(results, 1);
|
||||||
if (resultMap.hasOwnProperty(key)) {
|
} else if (trigger && trigger === TRIGGER.NOT) {
|
||||||
result = resultMap[key];
|
return matchExact(results, 0);
|
||||||
if (allMustBeTrue && !result) {
|
} else if (trigger && trigger === TRIGGER.ALL) {
|
||||||
//If we want all conditions to be true, then even one negative result should break.
|
return matchAll(results);
|
||||||
break;
|
} else {
|
||||||
} else if (!allMustBeTrue && result) {
|
return matchAny(results);
|
||||||
//If we want at least one condition to be true, then even one positive result should break.
|
}
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
function matchAll(results) {
|
||||||
|
for (const result of results) {
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Returns true only if limit number of results are satisfied
|
return true;
|
||||||
export const computeConditionByLimit = (resultMap, limit) => {
|
}
|
||||||
let trueCount = 0;
|
|
||||||
for (let key in resultMap) {
|
function matchAny(results) {
|
||||||
if (resultMap.hasOwnProperty(key)) {
|
for (const result of results) {
|
||||||
if (resultMap[key]) {
|
if (result) {
|
||||||
trueCount++;
|
return true;
|
||||||
}
|
|
||||||
if (trueCount > limit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return trueCount === limit;
|
|
||||||
};
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchExact(results, target) {
|
||||||
|
let matches = 0;
|
||||||
|
for (const result of results) {
|
||||||
|
if (result) {
|
||||||
|
matches++;
|
||||||
|
}
|
||||||
|
if (matches > target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches === target;
|
||||||
|
}
|
||||||
|
@ -20,47 +20,185 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { computeConditionByLimit } from "./evaluator";
|
import { evaluateResults } from './evaluator';
|
||||||
|
import { TRIGGER } from './constants';
|
||||||
|
|
||||||
describe('evaluate results based on trigger', function () {
|
describe('evaluate results', () => {
|
||||||
|
// const allTrue = [true, true, true, true, true];
|
||||||
|
// const oneTrue = [false, false, false, false, true];
|
||||||
|
// const multipleTrue = [false, true, false, true, false];
|
||||||
|
// const noneTrue = [false, false, false, false, false];
|
||||||
|
// const allTrueWithUndefined = [true, true, true, undefined, true];
|
||||||
|
// const oneTrueWithUndefined = [undefined, undefined, undefined, undefined, true];
|
||||||
|
// const multipleTrueWithUndefined = [true, undefined, true, undefined, true];
|
||||||
|
// const allUndefined = [undefined, undefined, undefined, undefined, undefined];
|
||||||
|
// const singleTrue = [true];
|
||||||
|
// const singleFalse = [false];
|
||||||
|
// const singleUndefined = [undefined];
|
||||||
|
// const empty = [];
|
||||||
|
|
||||||
it('should evaluate to true if trigger is NOT', () => {
|
const tests = [
|
||||||
const results = {
|
{
|
||||||
result: false,
|
name: 'allTrue',
|
||||||
result1: false,
|
values: [true, true, true, true, true],
|
||||||
result2: false
|
any: true,
|
||||||
};
|
all: true,
|
||||||
const result = computeConditionByLimit(results, 0);
|
not: false,
|
||||||
expect(result).toBeTrue();
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'oneTrue',
|
||||||
|
values: [false, false, false, false, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'multipleTrue',
|
||||||
|
values: [false, true, false, true, false],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'noneTrue',
|
||||||
|
values: [false, false, false, false, false],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'allTrueWithUndefined',
|
||||||
|
values: [true, true, true, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'oneTrueWithUndefined',
|
||||||
|
values: [undefined, undefined, undefined, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'multipleTrueWithUndefined',
|
||||||
|
values: [true, undefined, true, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'allUndefined',
|
||||||
|
values: [undefined, undefined, undefined, undefined, undefined],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'singleTrue',
|
||||||
|
values: [true],
|
||||||
|
any: true,
|
||||||
|
all: true,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'singleFalse',
|
||||||
|
values: [false],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'singleUndefined',
|
||||||
|
values: [undefined],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}
|
||||||
|
// , {
|
||||||
|
// name: 'empty',
|
||||||
|
// values: [],
|
||||||
|
// any: false,
|
||||||
|
// all: false,
|
||||||
|
// not: true,
|
||||||
|
// xor: false
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
describe(`based on trigger ${TRIGGER.ANY}`, () => {
|
||||||
|
it('should evaluate to expected result', () => {
|
||||||
|
tests.forEach(test => {
|
||||||
|
const result = evaluateResults(test.values, TRIGGER.ANY);
|
||||||
|
expect(result).toEqual(test[TRIGGER.ANY])
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate to false if trigger is NOT', () => {
|
describe(`based on trigger ${TRIGGER.ALL}`, () => {
|
||||||
const results = {
|
it('should evaluate to expected result', () => {
|
||||||
result: true,
|
tests.forEach(test => {
|
||||||
result1: false,
|
const result = evaluateResults(test.values, TRIGGER.ALL);
|
||||||
result2: false
|
expect(result).toEqual(test[TRIGGER.ALL])
|
||||||
};
|
});
|
||||||
const result = computeConditionByLimit(results, 0);
|
});
|
||||||
expect(result).toBeFalse();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate to true if trigger is XOR', () => {
|
describe(`based on trigger ${TRIGGER.NOT}`, () => {
|
||||||
const results = {
|
it('should evaluate to expected result', () => {
|
||||||
result: false,
|
tests.forEach(test => {
|
||||||
result1: true,
|
const result = evaluateResults(test.values, TRIGGER.NOT);
|
||||||
result2: false
|
expect(result).toEqual(test[TRIGGER.NOT])
|
||||||
};
|
});
|
||||||
const result = computeConditionByLimit(results, 1);
|
});
|
||||||
expect(result).toBeTrue();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate to false if trigger is XOR', () => {
|
describe(`based on trigger ${TRIGGER.XOR}`, () => {
|
||||||
const results = {
|
it('should evaluate to expected result', () => {
|
||||||
result: false,
|
tests.forEach(test => {
|
||||||
result1: true,
|
const result = evaluateResults(test.values, TRIGGER.XOR);
|
||||||
result2: true
|
expect(result).toEqual(test[TRIGGER.XOR])
|
||||||
};
|
});
|
||||||
const result = computeConditionByLimit(results, 1);
|
});
|
||||||
expect(result).toBeFalse();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// it('should evaluate to true if trigger is NOT', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: false,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 0);
|
||||||
|
// expect(result).toBeTrue();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to false if trigger is NOT', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: true,
|
||||||
|
// result1: false,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 0);
|
||||||
|
// expect(result).toBeFalse();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to true if trigger is XOR', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: true,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 1);
|
||||||
|
// expect(result).toBeTrue();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to false if trigger is XOR', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: true,
|
||||||
|
// result2: true
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 1);
|
||||||
|
// expect(result).toBeFalse();
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
|
@ -20,15 +20,33 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
export const getLatestTimestamp = (current, compare, timeSystems) => {
|
export const getLatestTimestamp = (
|
||||||
const timestamp = Object.assign({}, current);
|
currentTimestamp,
|
||||||
|
compareTimestamp,
|
||||||
|
timeSystems,
|
||||||
|
currentTimeSystem
|
||||||
|
) => {
|
||||||
|
let latest = { ...currentTimestamp };
|
||||||
|
const compare = { ...compareTimestamp };
|
||||||
|
const key = currentTimeSystem.key;
|
||||||
|
|
||||||
|
if (!latest || !latest[key]) {
|
||||||
|
latest = updateLatestTimeStamp(compare, timeSystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compare[key] > latest[key]) {
|
||||||
|
latest = updateLatestTimeStamp(compare, timeSystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLatestTimeStamp(timestamp, timeSystems) {
|
||||||
|
let latest = {};
|
||||||
|
|
||||||
timeSystems.forEach(timeSystem => {
|
timeSystems.forEach(timeSystem => {
|
||||||
if (!timestamp[timeSystem.key]
|
latest[timeSystem.key] = timestamp[timeSystem.key];
|
||||||
|| compare[timeSystem.key] > timestamp[timeSystem.key]
|
|
||||||
) {
|
|
||||||
timestamp[timeSystem.key] = compare[timeSystem.key];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return timestamp;
|
|
||||||
|
return latest;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user