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 = {
'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': {
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,
yesterday: nextStep - 60*60*24*1000,
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;
@@ -112,7 +113,8 @@
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
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({
@@ -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) {
return amplitude *
Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;

View File

@@ -25,11 +25,13 @@ define([
"./GeneratorProvider",
"./SinewaveLimitProvider",
"./StateGeneratorProvider",
"./SquareWaveGeneratorProvider",
"./GeneratorMetadataProvider"
], function (
GeneratorProvider,
SinewaveLimitProvider,
StateGeneratorProvider,
SquareWaveGeneratorProvider,
GeneratorMetadataProvider
) {
@@ -62,6 +64,80 @@ define([
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", {
name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",

View File

@@ -1,14 +1,14 @@
<template>
<div class="example">{{ msg }}</div>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
data() {
return {
msg: 'Hello world!'
}
}
}
}
</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
Administration. All rights reserved.
@@ -43,9 +43,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -69,9 +69,7 @@ export default class ConditionClass extends EventEmitter {
console.log('no data received');
return;
}
if (!this.isTelemetryUsed(datum.id)) {
return;
}
this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects);
@@ -206,7 +204,7 @@ export default class ConditionClass extends EventEmitter {
let latestTimestamp;
let criteriaResults = {};
const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects}));
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests)
.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.subscriptions[id] = this.openmct.telemetry.subscribe(
endpoint,
this.telemetryReceived.bind(this, id)
this.telemetryReceived.bind(this, endpoint)
);
// TODO check if this is needed
this.updateConditionTelemetry();
}
@@ -71,6 +70,7 @@ export default class ConditionManager extends EventEmitter {
this.subscriptions[id]();
delete this.subscriptions[id];
delete this.telemetryObjects[id];
this.removeConditionTelemetry();
}
initialize() {
@@ -86,6 +86,30 @@ export default class ConditionManager extends EventEmitter {
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) {
let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration);
@@ -234,9 +258,13 @@ export default class ConditionManager extends EventEmitter {
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,
id: this.conditionSetDomainObject.identifier,
@@ -244,12 +272,30 @@ export default class ConditionManager extends EventEmitter {
},
latestTimestamp
);
return [currentOutput];
});
});
}
telemetryReceived(id, datum) {
const normalizedDatum = this.createNormalizedDatum(datum, id);
isTelemetryUsed(endpoint) {
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;
let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
@@ -283,8 +329,11 @@ export default class ConditionManager extends EventEmitter {
return data;
}
createNormalizedDatum(telemetryDatum, id) {
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => {
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 testValue = this.getTestData(metadatum);
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);

View File

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

View File

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

View File

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

View File

@@ -34,6 +34,10 @@ const convertToStrings = (input) => {
return stringInputs;
};
const joinValues = (values, length) => {
return values.slice(0, length).join(', ');
};
export const OPERATIONS = [
{
name: 'equalTo',
@@ -44,7 +48,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' is ' + values.join(', ');
return ' is ' + joinValues(values, 1);
}
},
{
@@ -56,7 +60,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' is not ' + values.join(', ');
return ' is not ' + joinValues(values, 1);
}
},
{
@@ -68,7 +72,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' > ' + values.join(', ');
return ' > ' + joinValues(values, 1);
}
},
{
@@ -80,7 +84,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' < ' + values.join(', ');
return ' < ' + joinValues(values, 1);
}
},
{
@@ -92,7 +96,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' >= ' + values.join(', ');
return ' >= ' + joinValues(values, 1);
}
},
{
@@ -104,7 +108,7 @@ export const OPERATIONS = [
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' <= ' + values.join(', ');
return ' <= ' + joinValues(values, 1);
}
},
{
@@ -146,7 +150,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' contains ' + values.join(', ');
return ' contains ' + joinValues(values, 1);
}
},
{
@@ -158,7 +162,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
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'],
inputCount: 1,
getDescription: function (values) {
return ' starts with ' + values.join(', ');
return ' starts with ' + joinValues(values, 1);
}
},
{
@@ -182,7 +186,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' ends with ' + values.join(', ');
return ' ends with ' + joinValues(values, 1);
}
},
{
@@ -194,7 +198,7 @@ export const OPERATIONS = [
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' is exactly ' + values.join(', ');
return ' is exactly ' + joinValues(values, 1);
}
},
{
@@ -231,7 +235,7 @@ export const OPERATIONS = [
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' is ' + values.join(', ');
return ' is ' + joinValues(values, 1);
}
},
{
@@ -244,7 +248,7 @@ export const OPERATIONS = [
appliesTo: ['enum'],
inputCount: 1,
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);
},
changeLocation() {
this.openmct.time.stopClock();
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
const link = this.embed.historicLink;
if (!link) {
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;
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);
},
formatTime(unixTime, timeFormat) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -108,7 +108,7 @@ export default {
let object = selection[0][0].context.item;
if (object) {
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))
{
@@ -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) {
this.currentTabbedView = view;
},

View File

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

View File

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