Compare commits

...

22 Commits

Author SHA1 Message Date
Nikhil Mandlik
344cbbb92d square wave generator 2020-04-30 12:46:59 -07:00
Nikhil
ff7debfb81 [Notebook] Entries and Embeds need to use the same timesystem #2920 (#2923)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:56:08 -07:00
Joel McKinnon
92ba103f45 modified eslint script and fixed errors found (#2905)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:53:07 -07:00
Nikhil
2c2d8d6b56 [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843 (#2866)
* [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843

* removed stopclock call.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-30 10:47:49 -07:00
Charles Hacskaylo
cfadb9f4fd Fixes #2901 (#2902)
- Mod PreviewAction to prevent folders from being previewed;

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 09:48:16 -07:00
David Tsay
396817b2d1 handle non-valid requests (#2984)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 15:24:12 -07:00
David Tsay
96eb6d6b74 [Conditionals] evaluation fixes (#2981)
* change single output to state and value

* do not send telemetryObjects to telemetry api request cal

* normalize data on requests

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 14:56:07 -07:00
Shefali Joshi
cb5d47f66f Use only the values required for description (#2919)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-28 16:58:58 -07:00
Shefali Joshi
ea90d02d66 Show the Styles tab for non creatable layout objects including condition sets (#2975) 2020-04-28 13:10:29 -07:00
David Tsay
95f73d8eb8 [Conditionals] fix #2961 in master (#2969)
* use correct id for telemetry requests

* request and subscription data cache should be mutually exclusive

use latest timestamp for any/all requests

* do not add prop to datum

remove unnecessary if check

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-28 13:02:23 -07:00
Joel McKinnon
a37c686993 Merge pull request #2968 from nasa/revert-2885-lodash-upgrade-test
Revert "Lodash upgrade"
2020-04-24 11:55:44 -07:00
Deep Tailor
f12166097c Revert "Lodash upgrade (#2885)"
This reverts commit d103a22fa0.
2020-04-24 11:53:31 -07:00
Joel McKinnon
d103a22fa0 Lodash upgrade (#2885)
* upgraded lodash, changed method names
* native implementations as requested
2020-04-23 10:38:44 -07:00
Deep Tailor
04a60cfcbb fixes #2713 (#2928)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-22 15:22:11 -07:00
Joel McKinnon
8d723960f4 removed acorn from package.json (#2906)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:50:45 -07:00
Joel McKinnon
6d3cd2c699 Upgrade angular from 1.4.14 to 1.7.9 (#2955)
* successfully upgraded to v1.6 with $compileProvider.preAssignBindingsEnabled(true)
* removed $compileProvider.preAssignBindingsEnabled(true), wrapped constructors for plot and chart inside onInit function
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:34:12 -07:00
Andrew Henry
87bf94fe0a Updated year in index.html (#2930) 2020-04-21 15:28:10 -07:00
Joel McKinnon
af93823b6f Elasticsearch support for change to typeless API (#2941)
* added elasticsearch to bundlemap
2020-04-21 15:23:43 -07:00
Shefali Joshi
4a39ddf425 Check for any and all criteria (#2948) 2020-04-16 15:01:14 -07:00
Shefali Joshi
83c273b976 Remove telemetry from criteria when not editing a condition set (#2933) 2020-04-16 12:32:32 -07:00
Shefali Joshi
7dd81beb03 Remove telemetry data cache if a telemetry endpoint is removed (#2916) 2020-04-10 16:49:29 -07:00
David Tsay
1842d3923c [Conditionals] Only provide telemetry for incoming telemetry that is used (#2914)
* Ensures that results for a specific datapoint are evaluated atomically.

* Removes timestamp based evalutation from conditionManager

* Remove unused code

* remove generating timestamp for telemetry data

* get results directly instead of using events

* remove unused listeners, events, and helpers

* linting

* remove commented code

* telemetry criterion stores its own result

* refactor all/any telemetry criterion to use new evaluator

* tie in requests and eliminate unused code

* use current timesystem to compare latest

* scope function names

* AllTelemetryCriterion extends TelemetryCriterion

* fix telemetrycriterion and unit testing

* fix unit tests

* check if telemetry is used at condition manager level

* move check to condition manager

* remove whitespace

Co-authored-by: Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 16:45:09 -07:00
27 changed files with 464 additions and 142 deletions

View File

@@ -5,6 +5,38 @@ define([
) { ) {
var METADATA_BY_TYPE = { var METADATA_BY_TYPE = {
'squareWaveGenerator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "square",
name: "Square",
formatString: '%d',
hints: {
range: 1
}
}
]
},
'generator': { 'generator': {
values: [ values: [
{ {

View File

@@ -0,0 +1,68 @@
define([
'./WorkerInterface'
], function (
WorkerInterface
) {
var REQUEST_DEFAULTS = {
amplitude: 1,
period: 10,
offset: 0,
dataRateInHz: 1,
phase: 0
};
function SquareWaveGeneratorProvider() {
this.workerInterface = new WorkerInterface();
}
SquareWaveGeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
return domainObject.type === 'squareWaveGenerator';
};
SquareWaveGeneratorProvider.prototype.supportsRequest =
SquareWaveGeneratorProvider.prototype.supportsSubscribe =
SquareWaveGeneratorProvider.prototype.canProvideTelemetry;
SquareWaveGeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',
'period',
'offset',
'dataRateInHz',
'phase',
];
request = request || {};
var workerRequest = {};
props.forEach(function (prop) {
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
workerRequest[prop] = domainObject.telemetry[prop];
}
if (request && request.hasOwnProperty(prop)) {
workerRequest[prop] = request[prop];
}
if (!workerRequest.hasOwnProperty(prop)) {
workerRequest[prop] = REQUEST_DEFAULTS[prop];
}
workerRequest[prop] = Number(workerRequest[prop]);
});
workerRequest.name = domainObject.name;
return workerRequest;
};
SquareWaveGeneratorProvider.prototype.request = function (domainObject, request) {
var workerRequest = this.makeWorkerRequest(domainObject, request);
workerRequest.start = request.start;
workerRequest.end = request.end;
return this.workerInterface.request(workerRequest);
};
SquareWaveGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
var workerRequest = this.makeWorkerRequest(domainObject, {});
return this.workerInterface.subscribe(workerRequest, callback);
};
return SquareWaveGeneratorProvider;
});

View File

@@ -66,7 +66,8 @@
utc: nextStep, utc: nextStep,
yesterday: nextStep - 60*60*24*1000, yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness), sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness) cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
square: square(nextStep, data.period, data.amplitude, data.offset, data.phase)
} }
}); });
nextStep += step; nextStep += step;
@@ -112,7 +113,8 @@
utc: nextStep, utc: nextStep,
yesterday: nextStep - 60*60*24*1000, yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness), sin: sin(nextStep, period, amplitude, offset, phase, randomness),
cos: cos(nextStep, period, amplitude, offset, phase, randomness) cos: cos(nextStep, period, amplitude, offset, phase, randomness),
square: square(nextStep, period, amplitude, offset, phase)
}); });
} }
self.postMessage({ self.postMessage({
@@ -121,6 +123,12 @@
}); });
} }
function square(timestamp, period, amplitude, offset, phase) {
var sinValue = Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
var value = sinValue > 0 ? amplitude : -amplitude;
return value;
}
function cos(timestamp, period, amplitude, offset, phase, randomness) { function cos(timestamp, period, amplitude, offset, phase, randomness) {
return amplitude * return amplitude *
Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset; Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;

View File

@@ -25,11 +25,13 @@ define([
"./GeneratorProvider", "./GeneratorProvider",
"./SinewaveLimitProvider", "./SinewaveLimitProvider",
"./StateGeneratorProvider", "./StateGeneratorProvider",
"./SquareWaveGeneratorProvider",
"./GeneratorMetadataProvider" "./GeneratorMetadataProvider"
], function ( ], function (
GeneratorProvider, GeneratorProvider,
SinewaveLimitProvider, SinewaveLimitProvider,
StateGeneratorProvider, StateGeneratorProvider,
SquareWaveGeneratorProvider,
GeneratorMetadataProvider GeneratorMetadataProvider
) { ) {
@@ -62,6 +64,80 @@ define([
openmct.telemetry.addProvider(new StateGeneratorProvider()); openmct.telemetry.addProvider(new StateGeneratorProvider());
openmct.types.addType("squareWaveGenerator", {
name: "Square Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sqaure wave algorithm.",
cssClass: "icon-generator-telemetry",
creatable: true,
form: [
{
name: "Period",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "period",
required: true,
property: [
"telemetry",
"period"
]
},
{
name: "Amplitude",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "amplitude",
required: true,
property: [
"telemetry",
"amplitude"
]
},
{
name: "Offset",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "offset",
required: true,
property: [
"telemetry",
"offset"
]
},
{
name: "Data Rate (hz)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "dataRateInHz",
required: true,
property: [
"telemetry",
"dataRateInHz"
]
},
{
name: "Phase (radians)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "phase",
required: true,
property: [
"telemetry",
"phase"
]
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 5,
offset: 0,
dataRateInHz: 10,
phase: 0
};
}
});
openmct.telemetry.addProvider(new SquareWaveGeneratorProvider());
openmct.types.addType("generator", { openmct.types.addType("generator", {
name: "Sine Wave Generator", name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",

View File

@@ -1,14 +1,14 @@
<template> <template>
<div class="example">{{ msg }}</div> <div class="example">{{ msg }}</div>
</template> </template>
<script> <script>
export default { export default {
data () { data() {
return { return {
msg: 'Hello world!' msg: 'Hello world!'
}
} }
}
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
<!-- <!--
Open MCT, Copyright (c) 2014-2017, United States Government Open MCT, Copyright (c) 2014-2020, United States Government
as represented by the Administrator of the National Aeronautics and Space as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved. Administration. All rights reserved.
@@ -43,9 +43,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
); );
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso()); openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems()); openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator()); openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.UTCTimeSystem());

View File

@@ -4,8 +4,7 @@
"description": "The Open MCT core platform", "description": "The Open MCT core platform",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"acorn": "6.2.0", "angular": "1.7.9",
"angular": "1.4.14",
"angular-route": "1.4.14", "angular-route": "1.4.14",
"babel-eslint": "8.2.6", "babel-eslint": "8.2.6",
"comma-separated-values": "^3.6.4", "comma-separated-values": "^3.6.4",
@@ -77,8 +76,8 @@
}, },
"scripts": { "scripts": {
"start": "node app.js", "start": "node app.js",
"lint": "eslint platform example src/**/*.{js,vue} openmct.js", "lint": "eslint platform example src --ext .js,.vue openmct.js",
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix", "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
"build:prod": "cross-env NODE_ENV=production webpack", "build:prod": "cross-env NODE_ENV=production webpack",
"build:dev": "webpack", "build:dev": "webpack",
"build:watch": "webpack --watch", "build:watch": "webpack --watch",

View File

@@ -87,6 +87,11 @@ define([
bootstrapper bootstrapper
); );
// Override of angular1.6 ! hashPrefix
app.config(['$locationProvider', function ($locationProvider) {
$locationProvider.hashPrefix('');
}]);
// Apply logging levels; this must be done now, before the // Apply logging levels; this must be done now, before the
// first log statement. // first log statement.
new LogLevel(logLevel).configure(app, $log); new LogLevel(logLevel).configure(app, $log);

View File

@@ -71,7 +71,7 @@ define([
}, },
{ {
"key": "ELASTIC_PATH", "key": "ELASTIC_PATH",
"value": "mct/domain_object", "value": "mct/_doc",
"priority": "fallback" "priority": "fallback"
}, },
{ {

View File

@@ -32,9 +32,9 @@ define(
// JSLint doesn't like underscore-prefixed properties, // JSLint doesn't like underscore-prefixed properties,
// so hide them here. // so hide them here.
var SRC = "_source", var SRC = "_source",
REV = "_version", CONFLICT = 409,
ID = "_id", SEQ_NO = "_seq_no",
CONFLICT = 409; PRIMARY_TERM = "_primary_term";
/** /**
* The ElasticPersistenceProvider reads and writes JSON documents * The ElasticPersistenceProvider reads and writes JSON documents
@@ -104,7 +104,8 @@ define(
// Get a domain object model out of ElasticSearch's response // Get a domain object model out of ElasticSearch's response
ElasticPersistenceProvider.prototype.getModel = function (response) { ElasticPersistenceProvider.prototype.getModel = function (response) {
if (response && response[SRC]) { if (response && response[SRC]) {
this.revs[response[ID]] = response[REV]; this.revs[response[SEQ_NO]] = response[SEQ_NO];
this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
return response[SRC]; return response[SRC];
} else { } else {
return undefined; return undefined;
@@ -116,7 +117,8 @@ define(
// indicate that the request failed. // indicate that the request failed.
ElasticPersistenceProvider.prototype.checkResponse = function (response, key) { ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
if (response && !response.error) { if (response && !response.error) {
this.revs[key] = response[REV]; this.revs[SEQ_NO] = response[SEQ_NO];
this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
return response; return response;
} else { } else {
return this.handleError(response, key); return this.handleError(response, key);
@@ -147,7 +149,7 @@ define(
function checkUpdate(response) { function checkUpdate(response) {
return self.checkResponse(response, key); return self.checkResponse(response, key);
} }
return this.put(key, value, { version: this.revs[key] }) return this.put(key, value)
.then(checkUpdate); .then(checkUpdate);
}; };

View File

@@ -85,7 +85,7 @@ define(
it("allows object creation", function () { it("allows object creation", function () {
var model = { someKey: "some value" }; var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1 } data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
})); }));
provider.createObject("testSpace", "abc", model).then(capture); provider.createObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -100,7 +100,7 @@ define(
it("allows object models to be read back", function () { it("allows object models to be read back", function () {
var model = { someKey: "some value" }; var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1, "_source": model } data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
})); }));
provider.readObject("testSpace", "abc").then(capture); provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -117,19 +117,19 @@ define(
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
// Now perform an update // Now perform an update
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 43, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.updateObject("testSpace", "abc", model).then(capture); provider.updateObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc", url: "/test/db/abc",
method: "PUT", method: "PUT",
params: { version: 42 }, params: undefined,
data: model data: model
}); });
expect(capture.calls.mostRecent().args[0]).toBeTruthy(); expect(capture.calls.mostRecent().args[0]).toBeTruthy();
@@ -138,13 +138,13 @@ define(
it("allows object deletion", function () { it("allows object deletion", function () {
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
// Now perform an update // Now perform an update
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.deleteObject("testSpace", "abc", {}).then(capture); provider.deleteObject("testSpace", "abc", {}).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -167,13 +167,13 @@ define(
expect(capture).toHaveBeenCalledWith(undefined); expect(capture).toHaveBeenCalledWith(undefined);
}); });
it("handles rejection due to version", function () { it("handles rejection due to _seq_no", function () {
var model = { someKey: "some value" }, var model = { someKey: "some value" },
mockErrorCallback = jasmine.createSpy('error'); mockErrorCallback = jasmine.createSpy('error');
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
@@ -196,7 +196,7 @@ define(
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");

View File

@@ -69,9 +69,7 @@ export default class ConditionClass extends EventEmitter {
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 (this.isAnyOrAllTelemetry(criterion)) { if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects); criterion.getResult(datum, this.conditionManager.telemetryObjects);
@@ -206,7 +204,7 @@ export default class ConditionClass extends EventEmitter {
let latestTimestamp; let latestTimestamp;
let criteriaResults = {}; let criteriaResults = {};
const criteriaRequests = this.criteria const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects})); .map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests) return Promise.all(criteriaRequests)
.then(results => { .then(results => {

View File

@@ -55,9 +55,8 @@ 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.telemetryReceived.bind(this, id) this.telemetryReceived.bind(this, endpoint)
); );
// TODO check if this is needed
this.updateConditionTelemetry(); this.updateConditionTelemetry();
} }
@@ -71,6 +70,7 @@ export default class ConditionManager extends EventEmitter {
this.subscriptions[id](); this.subscriptions[id]();
delete this.subscriptions[id]; delete this.subscriptions[id];
delete this.telemetryObjects[id]; delete this.telemetryObjects[id];
this.removeConditionTelemetry();
} }
initialize() { initialize() {
@@ -86,6 +86,30 @@ export default class ConditionManager extends EventEmitter {
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry()); this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
} }
removeConditionTelemetry() {
let conditionsChanged = false;
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
if (!isAnyAllTelemetry) {
const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
});
if (!found) {
criterion.telemetry = '';
criterion.metadata = '';
criterion.input = [];
criterion.operation = '';
conditionsChanged = true;
}
}
});
});
if (conditionsChanged) {
this.persistConditions();
}
}
updateCondition(conditionConfiguration, index) { updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index]; let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration); condition.update(conditionConfiguration);
@@ -234,9 +258,13 @@ export default class ConditionManager extends EventEmitter {
this.openmct.time.timeSystem() this.openmct.time.timeSystem()
); );
}); });
const currentCondition = this.getCurrentConditionLAD(conditionResults);
return Object.assign( if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
return [];
}
const currentCondition = this.getCurrentConditionLAD(conditionResults);
const currentOutput = Object.assign(
{ {
output: currentCondition.configuration.output, output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier, id: this.conditionSetDomainObject.identifier,
@@ -244,12 +272,30 @@ export default class ConditionManager extends EventEmitter {
}, },
latestTimestamp latestTimestamp
); );
return [currentOutput];
}); });
}); });
} }
telemetryReceived(id, datum) { isTelemetryUsed(endpoint) {
const normalizedDatum = this.createNormalizedDatum(datum, id); const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditionClassCollection) {
if (condition.isTelemetryUsed(id)) {
return true;
}
}
return false;
}
telemetryReceived(endpoint, datum) {
if (!this.isTelemetryUsed(endpoint)) {
return;
}
const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
const timeSystemKey = this.openmct.time.timeSystem().key; const timeSystemKey = this.openmct.time.timeSystem().key;
let timestamp = {}; let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey]; timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
@@ -283,8 +329,11 @@ export default class ConditionManager extends EventEmitter {
return data; return data;
} }
createNormalizedDatum(telemetryDatum, id) { createNormalizedDatum(telemetryDatum, endpoint) {
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => { const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).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);
datum[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]);

View File

@@ -54,13 +54,22 @@ export default class ConditionSetMetadataProvider {
return { return {
values: this.getDomains().concat([ values: this.getDomains().concat([
{ {
name: 'Output', key: "state",
key: 'output', source: "output",
format: 'enum', name: "State",
format: "enum",
enumerations: enumerations, enumerations: enumerations,
hints: { hints: {
range: 1 range: 1
} }
},
{
key: "output",
name: "Value",
format: "string",
hints: {
range: 2
}
} }
]) ])
}; };

View File

@@ -45,7 +45,7 @@ export default class ConditionSetTelemetryProvider {
return conditionManager.requestLADConditionSetOutput() return conditionManager.requestLADConditionSetOutput()
.then(latestOutput => { .then(latestOutput => {
return latestOutput ? [latestOutput] : []; return latestOutput;
}); });
} }

View File

@@ -22,6 +22,7 @@
import TelemetryCriterion from './TelemetryCriterion'; import TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator"; import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time';
export default class AllTelemetryCriterion extends TelemetryCriterion { export default class AllTelemetryCriterion extends TelemetryCriterion {
@@ -47,6 +48,21 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
updateTelemetry(telemetryObjects) { updateTelemetry(telemetryObjects) {
this.telemetryObjects = { ...telemetryObjects }; this.telemetryObjects = { ...telemetryObjects };
this.removeTelemetryDataCache();
}
removeTelemetryDataCache() {
const telemetryCacheIds = Object.keys(this.telemetryDataCache);
Object.values(this.telemetryObjects).forEach(telemetryObject => {
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
const foundIndex = telemetryCacheIds.indexOf(id);
if (foundIndex > -1) {
telemetryCacheIds.splice(foundIndex, 1);
}
});
telemetryCacheIds.forEach(id => {
delete (this.telemetryDataCache[id]);
});
} }
formatData(data, telemetryObjects) { formatData(data, telemetryObjects) {
@@ -92,40 +108,53 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry); this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
} }
requestLAD(options) { requestLAD(telemetryObjects) {
options = Object.assign({}, const options = {
options, strategy: 'latest',
{ size: 1
strategy: 'latest', };
size: 1
}
);
if (!this.isValid()) { if (!this.isValid()) {
return this.formatData({}, options.telemetryObjects); return this.formatData({}, telemetryObjects);
} }
let keys = Object.keys(Object.assign({}, options.telemetryObjects)); let keys = Object.keys(Object.assign({}, telemetryObjects));
const telemetryRequests = keys const telemetryRequests = keys
.map(key => this.openmct.telemetry.request( .map(key => this.openmct.telemetry.request(
options.telemetryObjects[key], telemetryObjects[key],
options options
)); ));
let telemetryDataCache = {};
return Promise.all(telemetryRequests) return Promise.all(telemetryRequests)
.then(telemetryRequestsResults => { .then(telemetryRequestsResults => {
let latestDatum; let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => { telemetryRequestsResults.forEach((results, index) => {
latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
if (index < telemetryRequestsResults.length-1) { const datumId = keys[index];
if (latestDatum) { const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
} telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
}
latestTimestamp = getLatestTimestamp(
latestTimestamp,
normalizedDatum,
timeSystems,
timeSystem
);
}); });
const datum = {
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
...latestTimestamp
};
return { return {
id: this.id, id: this.id,
data: this.formatData(latestDatum, options.telemetryObjects) data: datum
}; };
}); });
} }

View File

@@ -61,6 +61,21 @@ export default class TelemetryCriterion extends EventEmitter {
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString]; this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
} }
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
return datum;
}, {});
normalizedDatum.id = id;
return normalizedDatum;
}
formatData(data) { formatData(data) {
const datum = { const datum = {
result: this.computeResult(data) result: this.computeResult(data)
@@ -79,14 +94,11 @@ export default class TelemetryCriterion extends EventEmitter {
this.result = this.computeResult(validatedData); this.result = this.computeResult(validatedData);
} }
requestLAD(options) { requestLAD() {
options = Object.assign({}, const options = {
options, strategy: 'latest',
{ size: 1
strategy: 'latest', };
size: 1
}
);
if (!this.isValid()) { if (!this.isValid()) {
return { return {
@@ -100,9 +112,11 @@ export default class TelemetryCriterion extends EventEmitter {
options options
).then(results => { ).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
return { return {
id: this.id, id: this.id,
data: this.formatData(latestDatum) data: this.formatData(normalizedDatum)
}; };
}); });
} }

View File

@@ -34,6 +34,10 @@ const convertToStrings = (input) => {
return stringInputs; return stringInputs;
}; };
const joinValues = (values, length) => {
return values.slice(0, length).join(', ');
};
export const OPERATIONS = [ export const OPERATIONS = [
{ {
name: 'equalTo', name: 'equalTo',
@@ -44,7 +48,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is ' + values.join(', '); return ' is ' + joinValues(values, 1);
} }
}, },
{ {
@@ -56,7 +60,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is not ' + values.join(', '); return ' is not ' + joinValues(values, 1);
} }
}, },
{ {
@@ -68,7 +72,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' > ' + values.join(', '); return ' > ' + joinValues(values, 1);
} }
}, },
{ {
@@ -80,7 +84,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' < ' + values.join(', '); return ' < ' + joinValues(values, 1);
} }
}, },
{ {
@@ -92,7 +96,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' >= ' + values.join(', '); return ' >= ' + joinValues(values, 1);
} }
}, },
{ {
@@ -104,7 +108,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' <= ' + values.join(', '); return ' <= ' + joinValues(values, 1);
} }
}, },
{ {
@@ -146,7 +150,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' contains ' + values.join(', '); return ' contains ' + joinValues(values, 1);
} }
}, },
{ {
@@ -158,7 +162,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' does not contain ' + values.join(', '); return ' does not contain ' + joinValues(values, 1);
} }
}, },
{ {
@@ -170,7 +174,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' starts with ' + values.join(', '); return ' starts with ' + joinValues(values, 1);
} }
}, },
{ {
@@ -182,7 +186,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' ends with ' + values.join(', '); return ' ends with ' + joinValues(values, 1);
} }
}, },
{ {
@@ -194,7 +198,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is exactly ' + values.join(', '); return ' is exactly ' + joinValues(values, 1);
} }
}, },
{ {
@@ -231,7 +235,7 @@ export const OPERATIONS = [
appliesTo: ['enum'], appliesTo: ['enum'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is ' + values.join(', '); return ' is ' + joinValues(values, 1);
} }
}, },
{ {
@@ -244,7 +248,7 @@ export const OPERATIONS = [
appliesTo: ['enum'], appliesTo: ['enum'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is not ' + values.join(', '); return ' is not ' + joinValues(values, 1);
} }
}, },
{ {

View File

@@ -162,19 +162,31 @@ export default {
}).show(this.embed.snapshot.src); }).show(this.embed.snapshot.src);
}, },
changeLocation() { changeLocation() {
this.openmct.time.stopClock();
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
const link = this.embed.historicLink; const link = this.embed.historicLink;
if (!link) { if (!link) {
return; return;
} }
const bounds = this.openmct.time.bounds();
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
&& this.embed.bounds.end !== bounds.end;
const isFixedTimespanMode = !this.openmct.time.clock();
window.location.href = link; window.location.href = link;
const message = 'Time bounds changed to fixed timespan mode';
let message = '';
if (isTimeBoundChanged) {
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
message = 'Time bound values changed';
}
if (!isFixedTimespanMode) {
message = 'Time bound values changed to fixed timespan mode';
}
this.openmct.notifications.alert(message); this.openmct.notifications.alert(message);
}, },
formatTime(unixTime, timeFormat) { formatTime(unixTime, timeFormat) {

View File

@@ -221,7 +221,7 @@ export default {
return position; return position;
}, },
formatTime(unixTime, timeFormat) { formatTime(unixTime, timeFormat) {
return Moment(unixTime).format(timeFormat); return Moment.utc(unixTime).format(timeFormat);
}, },
moveSnapshot(snapshotId) { moveSnapshot(snapshotId) {
const snapshot = this.snapshotContainer.getSnapshot(snapshotId); const snapshot = this.snapshotContainer.getSnapshot(snapshotId);

View File

@@ -54,27 +54,29 @@ function (
* @constructor * @constructor
*/ */
function MCTChartController($scope) { function MCTChartController($scope) {
this.$scope = $scope; this.$onInit = () => {
this.isDestroyed = false; this.$scope = $scope;
this.lines = []; this.isDestroyed = false;
this.pointSets = []; this.lines = [];
this.alarmSets = []; this.pointSets = [];
this.offset = {}; this.alarmSets = [];
this.config = $scope.config; this.offset = {};
this.listenTo(this.$scope, '$destroy', this.destroy, this); this.config = $scope.config;
this.draw = this.draw.bind(this); this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.scheduleDraw = this.scheduleDraw.bind(this); this.draw = this.draw.bind(this);
this.seriesElements = new WeakMap(); this.scheduleDraw = this.scheduleDraw.bind(this);
this.seriesElements = new WeakMap();
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this); this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this); this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this); this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this); this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw); this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw); this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.$scope.$watch('highlights', this.scheduleDraw); this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw); this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this); this.config.series.forEach(this.onSeriesAdd, this);
}
} }
eventHelpers.extend(MCTChartController.prototype); eventHelpers.extend(MCTChartController.prototype);

View File

@@ -34,25 +34,27 @@ define([
* values near the cursor. * values near the cursor.
*/ */
function MCTPlotController($scope, $element, $window) { function MCTPlotController($scope, $element, $window) {
this.$scope = $scope; this.$onInit = () => {
this.$scope.config = this.config; this.$scope = $scope;
this.$scope.plot = this; this.$scope.config = this.config;
this.$element = $element; this.$scope.plot = this;
this.$window = $window; this.$element = $element;
this.$window = $window;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange')); this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange')); this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
this.pan = undefined; this.pan = undefined;
this.marquee = undefined; this.marquee = undefined;
this.chartElementBounds = undefined; this.chartElementBounds = undefined;
this.tickUpdate = false; this.tickUpdate = false;
this.$scope.plotHistory = this.plotHistory = []; this.$scope.plotHistory = this.plotHistory = [];
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this); this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize(); this.initialize();
}
} }
MCTPlotController.$inject = ['$scope', '$element', '$window']; MCTPlotController.$inject = ['$scope', '$element', '$window'];

View File

@@ -114,15 +114,17 @@ define([
} }
function MCTTicksController($scope, $element) { function MCTTicksController($scope, $element) {
this.$scope = $scope; this.$onInit = () => {
this.$element = $element; this.$scope = $scope;
this.$element = $element;
this.tickCount = 4; this.tickCount = 4;
this.tickUpdate = false; this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this); this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
this.listenTo(this.axis, 'change:format', this.updateTicks, this); this.listenTo(this.axis, 'change:format', this.updateTicks, this);
this.listenTo(this.$scope, '$destroy', this.stopListening, this); this.listenTo(this.$scope, '$destroy', this.stopListening, this);
this.updateTicks(); this.updateTicks();
}
} }
MCTTicksController.$inject = ['$scope', '$element']; MCTTicksController.$inject = ['$scope', '$element'];

View File

@@ -88,7 +88,8 @@ define([
var bundleMap = { var bundleMap = {
LocalStorage: 'platform/persistence/local', LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items', MyItems: 'platform/features/my-items',
CouchDB: 'platform/persistence/couch' CouchDB: 'platform/persistence/couch',
Elasticsearch: 'platform/persistence/elastic'
}; };
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) { var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {

View File

@@ -108,7 +108,7 @@ export default {
let object = selection[0][0].context.item; let object = selection[0][0].context.item;
if (object) { if (object) {
let type = this.openmct.types.get(object.type); let type = this.openmct.types.get(object.type);
this.showStyles = (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable; this.showStyles = this.isLayoutObject(selection[0], object.type) || this.isCreatableObject(object, type);
} }
if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key)) if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
{ {
@@ -116,6 +116,14 @@ export default {
} }
} }
}, },
isLayoutObject(selection, objectType) {
//we allow conditionSets to be styled if they're part of a layout
return selection.length > 1 &&
((objectType === 'conditionSet') || (this.excludeObjectTypes.indexOf(objectType) < 0));
},
isCreatableObject(object, type) {
return (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
},
updateCurrentTab(view) { updateCurrentTab(view) {
this.currentTabbedView = view; this.currentTabbedView = view;
}, },

View File

@@ -198,8 +198,6 @@ export default {
updateName(event) { updateName(event) {
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) { if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText); this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
} else {
event.target.innerText = this.domainObject.name;
} }
}, },
updateNameOnEnterKeyPress(event) { updateNameOnEnterKeyPress(event) {

View File

@@ -63,7 +63,7 @@ export default class PreviewAction {
}); });
} }
appliesTo(objectPath) { appliesTo(objectPath) {
return !this._isNavigatedObject(objectPath) return !this._isNavigatedObject(objectPath) && !this._preventPreview(objectPath);
} }
_isNavigatedObject(objectPath) { _isNavigatedObject(objectPath) {
let targetObject = objectPath[0]; let targetObject = objectPath[0];
@@ -71,4 +71,8 @@ export default class PreviewAction {
return targetObject.identifier.namespace === navigatedObject.identifier.namespace && return targetObject.identifier.namespace === navigatedObject.identifier.namespace &&
targetObject.identifier.key === navigatedObject.identifier.key; targetObject.identifier.key === navigatedObject.identifier.key;
} }
_preventPreview(objectPath) {
const noPreviewTypes = ['folder'];
return noPreviewTypes.includes(objectPath[0].type);
}
} }