mirror of
https://github.com/nasa/openmct.git
synced 2025-05-18 00:13:10 +00:00
Merge branch 'topic-conditionals' into dave/conditions-telemetry
This commit is contained in:
commit
3b82fd5d8b
@ -42,8 +42,28 @@
|
|||||||
|
|
||||||
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
||||||
<!-- TODO: description should be derived from criteria -->
|
<!-- TODO: description should be derived from criteria -->
|
||||||
<span class="c-condition__summary">
|
<span v-if="condition.isDefault"
|
||||||
Description/summary goes here {{ condition.configuration.description }}
|
class="c-condition__summary"
|
||||||
|
>
|
||||||
|
When all else fails
|
||||||
|
</span>
|
||||||
|
<span v-else
|
||||||
|
class="c-condition__summary"
|
||||||
|
>
|
||||||
|
<template v-if="!canEvaluateCriteria">
|
||||||
|
Define criteria
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
When
|
||||||
|
<span v-for="(criterion, index) in condition.configuration.criteria"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ getRule(criterion, index) }}
|
||||||
|
<template v-if="!isLastCriterion">
|
||||||
|
{{ getConjunction }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="c-condition__buttons">
|
<div class="c-condition__buttons">
|
||||||
@ -159,14 +179,35 @@
|
|||||||
Output: {{ condition.configuration.output }}
|
Output: {{ condition.configuration.output }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-condition__summary">
|
<div v-if="condition.isDefault"
|
||||||
Description/summary goes here {{ condition.configuration.description }}
|
class="c-condition__summary"
|
||||||
|
>
|
||||||
|
When all else fails
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
class="c-condition__summary"
|
||||||
|
>
|
||||||
|
<template v-if="!canEvaluateCriteria">
|
||||||
|
Define criteria
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
When
|
||||||
|
<span v-for="(criterion, index) in condition.configuration.criteria"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ getRule(criterion, index) }}
|
||||||
|
<template v-if="!isLastCriterion">
|
||||||
|
{{ getConjunction }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Criterion from './Criterion.vue';
|
import Criterion from './Criterion.vue';
|
||||||
|
import { OPERATIONS } from '../utils/operations';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -202,6 +243,24 @@ export default {
|
|||||||
criterionIndex: 0
|
criterionIndex: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
canEvaluateCriteria: function () {
|
||||||
|
let criteria = this.condition.configuration.criteria;
|
||||||
|
let lastCriterion = criteria[criteria.length - 1];
|
||||||
|
if (lastCriterion.telemetry &&
|
||||||
|
lastCriterion.operation &&
|
||||||
|
(lastCriterion.input.length ||
|
||||||
|
lastCriterion.operation === 'isDefined' ||
|
||||||
|
lastCriterion.operation === 'isUndefined')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getConjunction: function () {
|
||||||
|
return this.condition.configuration.trigger === 'all' ? 'and' : 'or';
|
||||||
|
}
|
||||||
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
@ -209,6 +268,20 @@ export default {
|
|||||||
this.setOutputSelection();
|
this.setOutputSelection();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getRule(criterion, index) {
|
||||||
|
return `${criterion.telemetry.name} ${criterion.telemetry.fieldName} ${this.findDescription(criterion.operation, criterion.input)}`;
|
||||||
|
},
|
||||||
|
isLastCriterion(index) {
|
||||||
|
return index === this.condition.configuration.criteria.length - 1;
|
||||||
|
},
|
||||||
|
findDescription(operation, values) {
|
||||||
|
for (let i=0, ii= OPERATIONS.length; i < ii; i++) {
|
||||||
|
if (operation === OPERATIONS[i].name) {
|
||||||
|
return OPERATIONS[i].getDescription(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
setOutputSelection() {
|
setOutputSelection() {
|
||||||
let conditionOutput = this.condition.configuration.output;
|
let conditionOutput = this.condition.configuration.output;
|
||||||
if (conditionOutput) {
|
if (conditionOutput) {
|
||||||
|
@ -34,8 +34,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<TestData :is-editing="isEditing" />
|
<TestData :is-editing="isEditing" />
|
||||||
<ConditionCollection :is-editing="isEditing"
|
<ConditionCollection
|
||||||
@conditionSetResultUpdated="updateCurrentOutput" />
|
:is-editing="isEditing"
|
||||||
|
@conditionSetResultUpdated="updateCurrentOutput"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -60,11 +62,6 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
|
||||||
if (this.stopProvidingTelemetry) {
|
|
||||||
this.stopProvidingTelemetry();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
updateCurrentOutput(currentConditionResult) {
|
updateCurrentOutput(currentConditionResult) {
|
||||||
this.currentConditionOutput = currentConditionResult.output;
|
this.currentConditionOutput = currentConditionResult.output;
|
||||||
|
@ -45,16 +45,32 @@
|
|||||||
{{ option.text }}
|
{{ option.text }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<span v-for="(item, inputIndex) in inputCount"
|
<span v-if="!enumerations.length">
|
||||||
:key="inputIndex"
|
<span v-for="(item, inputIndex) in inputCount"
|
||||||
class="c-cdef__control__inputs"
|
:key="inputIndex"
|
||||||
>
|
class="c-cdef__control__inputs"
|
||||||
<input v-model="criterion.input[inputIndex]"
|
|
||||||
class="c-cdef__control__input"
|
|
||||||
type="text"
|
|
||||||
@change="persist"
|
|
||||||
>
|
>
|
||||||
<span v-if="inputIndex < inputCount-1">and</span>
|
<input v-model="criterion.input[inputIndex]"
|
||||||
|
class="c-cdef__control__input"
|
||||||
|
:type="setInputType"
|
||||||
|
@blur="persist"
|
||||||
|
>
|
||||||
|
<span v-if="inputIndex < inputCount-1">and</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<span v-if="inputCount && criterion.operation"
|
||||||
|
class="c-cdef__control"
|
||||||
|
>
|
||||||
|
<select v-model="criterion.input[0]">
|
||||||
|
<option v-for="option in enumerations"
|
||||||
|
:key="option.string"
|
||||||
|
:value="option.value.toString()"
|
||||||
|
>
|
||||||
|
{{ option.string }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -92,7 +108,8 @@ export default {
|
|||||||
operations: OPERATIONS,
|
operations: OPERATIONS,
|
||||||
inputCount: 0,
|
inputCount: 0,
|
||||||
rowLabel: '',
|
rowLabel: '',
|
||||||
operationFormat: ''
|
operationFormat: '',
|
||||||
|
enumerations: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -102,6 +119,20 @@ export default {
|
|||||||
},
|
},
|
||||||
filteredOps: function () {
|
filteredOps: function () {
|
||||||
return [...this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1)];
|
return [...this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1)];
|
||||||
|
},
|
||||||
|
setInputType: function () {
|
||||||
|
let type = '';
|
||||||
|
for (let i = 0; i < this.filteredOps.length; i++) {
|
||||||
|
if (this.criterion.operation === this.filteredOps[i].name) {
|
||||||
|
if (this.filteredOps[i].appliesTo.length === 1) {
|
||||||
|
type = this.filteredOps[i].appliesTo[0];
|
||||||
|
} else {
|
||||||
|
type = 'string'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -109,11 +140,13 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getOperationFormat() {
|
getOperationFormat() {
|
||||||
|
this.enumerations = [];
|
||||||
this.telemetryMetadata.valueMetadatas.forEach((value, index) => {
|
this.telemetryMetadata.valueMetadatas.forEach((value, index) => {
|
||||||
if (value.key === this.criterion.metadata) {
|
if (value.key === this.criterion.metadata) {
|
||||||
let valueMetadata = this.telemetryMetadataOptions[index];
|
let valueMetadata = this.telemetryMetadataOptions[index];
|
||||||
if (valueMetadata.enumerations !== undefined) {
|
if (valueMetadata.enumerations !== undefined) {
|
||||||
this.operationFormat = 'enum';
|
this.operationFormat = 'enum';
|
||||||
|
this.enumerations = valueMetadata.enumerations;
|
||||||
} else if (valueMetadata.hints.hasOwnProperty('range')) {
|
} else if (valueMetadata.hints.hasOwnProperty('range')) {
|
||||||
this.operationFormat = 'number';
|
this.operationFormat = 'number';
|
||||||
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
|
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
|
||||||
@ -130,6 +163,7 @@ export default {
|
|||||||
if (ev) {this.clearInputs()}
|
if (ev) {this.clearInputs()}
|
||||||
if (this.criterion.telemetry) {
|
if (this.criterion.telemetry) {
|
||||||
this.openmct.objects.get(this.criterion.telemetry).then((telemetryObject) => {
|
this.openmct.objects.get(this.criterion.telemetry).then((telemetryObject) => {
|
||||||
|
this.criterion.telemetry.name = telemetryObject.name;
|
||||||
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
this.telemetryMetadataOptions = this.telemetryMetadata.values();
|
this.telemetryMetadataOptions = this.telemetryMetadata.values();
|
||||||
this.updateOperations();
|
this.updateOperations();
|
||||||
@ -140,13 +174,16 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateOperations(ev) {
|
updateOperations(ev) {
|
||||||
if (ev) {this.clearInputs()}
|
if (ev) {
|
||||||
|
this.criterion.telemetry.fieldName = ev.target.options[ev.target.selectedIndex].text;
|
||||||
|
this.clearInputs()
|
||||||
|
}
|
||||||
this.getOperationFormat();
|
this.getOperationFormat();
|
||||||
this.persist();
|
this.persist();
|
||||||
},
|
},
|
||||||
updateOperationInputVisibility(ev) {
|
updateOperationInputVisibility(ev) {
|
||||||
if (ev) {
|
if (ev) {
|
||||||
this.criterion.input = [];
|
this.criterion.input = this.enumerations.length ? [this.enumerations[0].value.toString()] : [];
|
||||||
this.inputCount = 0;
|
this.inputCount = 0;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this.filteredOps.length; i++) {
|
for (let i = 0; i < this.filteredOps.length; i++) {
|
||||||
|
@ -44,7 +44,6 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
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.subscription = null;
|
|
||||||
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(this.telemetry);
|
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(this.telemetry);
|
||||||
|
|
||||||
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
||||||
@ -53,18 +52,20 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
|
|
||||||
initialize(obj) {
|
initialize(obj) {
|
||||||
this.telemetryObject = obj;
|
this.telemetryObject = obj;
|
||||||
|
this.telemetryMetaData = this.openmct.telemetry.getMetadata(obj).valueMetadatas;
|
||||||
this.emitEvent('criterionUpdated', this);
|
this.emitEvent('criterionUpdated', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatData(data) {
|
formatData(data) {
|
||||||
|
const normalizedDatum = this.createNormalizedDatum(data);
|
||||||
const datum = {
|
const datum = {
|
||||||
result: this.computeResult(data)
|
result: this.computeResult(normalizedDatum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (normalizedDatum) {
|
||||||
// TODO check back to see if we should format times here
|
// TODO check back to see if we should format times here
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
||||||
datum[timeSystem.key] = data[timeSystem.key]
|
datum[timeSystem.key] = normalizedDatum[timeSystem.key]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return datum;
|
return datum;
|
||||||
@ -76,6 +77,13 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createNormalizedDatum(telemetryDatum) {
|
||||||
|
return Object.values(this.telemetryMetaData).reduce((normalizedDatum, metadatum) => {
|
||||||
|
normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source];
|
||||||
|
return normalizedDatum;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
findOperation(operation) {
|
findOperation(operation) {
|
||||||
for (let i=0, ii=OPERATIONS.length; i < ii; i++) {
|
for (let i=0, ii=OPERATIONS.length; i < ii; i++) {
|
||||||
if (operation === OPERATIONS[i].name) {
|
if (operation === OPERATIONS[i].name) {
|
||||||
@ -109,7 +117,7 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
isValid() {
|
||||||
return this.metadata && this.operation;
|
return this.telemetryObject && this.metadata && this.operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLAD(options) {
|
requestLAD(options) {
|
||||||
@ -140,8 +148,9 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.off('receivedTelemetry', this.handleSubscription);
|
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
||||||
this.emitEvent('criterionRemoved');
|
this.emitEvent('criterionRemoved');
|
||||||
delete this.telemetryObjectIdAsString;
|
delete this.telemetryObjectIdAsString;
|
||||||
|
delete this.telemetryObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,18 +37,25 @@ describe("The telemetry criterion", function () {
|
|||||||
type: "test-object",
|
type: "test-object",
|
||||||
name: "Test Object",
|
name: "Test Object",
|
||||||
telemetry: {
|
telemetry: {
|
||||||
values: [{
|
valueMetadatas: [{
|
||||||
key: "some-key",
|
key: "value",
|
||||||
name: "Some attribute",
|
name: "Value",
|
||||||
|
hints: {
|
||||||
|
range: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
name: "Time",
|
||||||
|
format: "utc",
|
||||||
hints: {
|
hints: {
|
||||||
domain: 1
|
domain: 1
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
key: "some-other-key",
|
key: "testSource",
|
||||||
name: "Another attribute",
|
source: "value",
|
||||||
hints: {
|
name: "Test",
|
||||||
range: 1
|
format: "enum"
|
||||||
}
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -60,7 +67,7 @@ describe("The telemetry criterion", function () {
|
|||||||
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata"]);
|
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata"]);
|
||||||
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
openmct.telemetry.subscribe.and.returnValue(function () {});
|
openmct.telemetry.subscribe.and.returnValue(function () {});
|
||||||
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
|
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
|
||||||
|
|
||||||
openmct.time = jasmine.createSpyObj('timeAPI',
|
openmct.time = jasmine.createSpyObj('timeAPI',
|
||||||
['timeSystem', 'bounds', 'getAllTimeSystems']
|
['timeSystem', 'bounds', 'getAllTimeSystems']
|
||||||
@ -101,11 +108,11 @@ describe("The telemetry criterion", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("emits update event on new data from telemetry providers", function () {
|
it("emits update event on new data from telemetry providers", function () {
|
||||||
|
telemetryCriterion.initialize(testTelemetryObject);
|
||||||
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
|
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
|
||||||
telemetryCriterion.handleSubscription({
|
telemetryCriterion.handleSubscription({
|
||||||
key: 'some-key',
|
value: 'Hello',
|
||||||
source: 'testSource',
|
utc: 'Hi'
|
||||||
testSource: 'Hello'
|
|
||||||
});
|
});
|
||||||
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
|
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
|
||||||
expect(mockListener).toHaveBeenCalled();
|
expect(mockListener).toHaveBeenCalled();
|
||||||
|
@ -8,7 +8,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' == ' + values[0];
|
return ' is ' + values[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -20,7 +20,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' != ' + values[0];
|
return ' is not ' + values[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -80,7 +80,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 2,
|
inputCount: 2,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' between ' + values[0] + ' and ' + values[1];
|
return ' is between ' + values[0] + ' and ' + values[1];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -92,7 +92,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 2,
|
inputCount: 2,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' not between ' + values[0] + ' and ' + values[1];
|
return ' is not between ' + values[0] + ' and ' + values[1];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -188,7 +188,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['enum'],
|
appliesTo: ['enum'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' == ' + values[0];
|
return ' is ' + values[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -200,7 +200,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['enum'],
|
appliesTo: ['enum'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' != ' + values[0];
|
return ' is not ' + values[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user