Compare commits

..

4 Commits

Author SHA1 Message Date
Joshi
5283d06e05 Save window location 2020-05-18 10:43:18 -07:00
Joshi
888695f636 Saves indicator buttons 2020-05-14 14:37:30 -07:00
Joshi
9f0af90663 WIP - Open 2 windows, close them and open the saved 2 windows 2020-05-14 11:53:53 -07:00
Joshi
e93d36ff19 WIP windowLayouts 2020-05-12 10:43:20 -07:00
26 changed files with 383 additions and 1158 deletions

4
API.md
View File

@@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints. Keys in this thi
Known hints:
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
* `range`: Indicates that the value is the "output" of a datum. Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
##### The Time Conductor and Telemetry

View File

@@ -103,7 +103,7 @@ the name chosen could not be mistaken for a topic or master branch.
### Merging
When development is complete on an issue, the first step toward merging it
back into the master branch is to file a Pull Request (PR). The contributions
back into the master branch is to file a Pull Request. The contributions
should meet code, test, and commit message standards as described below,
and the pull request should include a completed author checklist, also
as described below. Pull requests may be assigned to specific team
@@ -114,15 +114,6 @@ request. When the reviewer is satisfied, they should add a comment to
the pull request containing the reviewer checklist (from below) and complete
the merge back to the master branch.
Additionally:
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull requests __author__. If no issue exists, create one.
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull requests __author__.
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
* Every PR must have two reviewers assigned, though only one approval is necessary for merge.
* Changes to API require approval by a senior developer.
* When creating a PR, it is the author's responsibility to apply any priority label from the issue to the PR as well. This helps with prioritization.
## Standards
Contributions to Open MCT are expected to meet the following standards.
@@ -301,7 +292,6 @@ checklist).
2. Unit tests included and/or updated with changes?
3. Command line build passes?
4. Changes have been smoke-tested?
5. Testing instructions included?
### Reviewer Checklist
@@ -309,4 +299,3 @@ checklist).
2. Appropriate unit tests included?
3. Code style and in-line documentation are appropriate?
4. Commit messages meet standards?
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)

View File

@@ -50,8 +50,7 @@ define([
values: [
{
key: "name",
name: "Name",
format: "string"
name: "Name"
},
{
key: "utc",

View File

@@ -82,6 +82,7 @@
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
openmct.install(openmct.plugins.WindowLayout());
openmct.start();
</script>
</html>

View File

@@ -2,9 +2,7 @@
"name": "openmct",
"version": "1.0.0-snapshot",
"description": "The Open MCT core platform",
"dependencies": {
"plotly.js-dist": "^1.54.1"
},
"dependencies": {},
"devDependencies": {
"angular": "1.7.9",
"angular-route": "1.4.14",

View File

@@ -252,7 +252,6 @@ define([
// Plugin's that are installed by default
this.install(this.plugins.Plot());
this.install(this.plugins.PlotlyPlot());
this.install(this.plugins.TelemetryTable());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());

View File

@@ -19,46 +19,53 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTableSet from './components/LadTableSet.vue';
import Vue from 'vue';
export default function LADTableSetViewProvider(openmct) {
return {
key: 'LadTableSet',
name: 'LAD Table Set',
cssClass: 'icon-tabular-lad-set',
canView: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject, objectPath) {
let component;
define([
'./components/LadTableSet.vue',
'vue'
], function (
LadTableSet,
Vue
) {
function LADTableSetViewProvider(openmct) {
return {
key: 'LadTableSet',
name: 'LAD Table Set',
cssClass: 'icon-tabular-lad-set',
canView: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableSet: LadTableSet
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-set></lad-table-set>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableSet: LadTableSet.default
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-set></lad-table-set>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return LADTableSetViewProvider;
});

View File

@@ -19,46 +19,53 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTable from './components/LADTable.vue';
import Vue from 'vue';
export default function LADTableViewProvider(openmct) {
return {
key: 'LadTable',
name: 'LAD Table',
cssClass: 'icon-tabular-lad',
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject, objectPath) {
let component;
define([
'./components/LADTable.vue',
'vue'
], function (
LadTableComponent,
Vue
) {
function LADTableViewProvider(openmct) {
return {
key: 'LadTable',
name: 'LAD Table',
cssClass: 'icon-tabular-lad',
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableComponent: LadTable
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-component></lad-table-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableComponent: LadTableComponent.default
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-component></lad-table-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return LADTableViewProvider;
});

View File

@@ -1,6 +1,6 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,8 +24,10 @@
<template>
<tr @contextmenu.prevent="showContextMenu">
<td>{{ name }}</td>
<td>{{ formattedTimestamp }}</td>
<td :class="valueClass">{{ value }}</td>
<td>{{ timestamp }}</td>
<td :class="valueClass">
{{ value }}
</td>
</tr>
</template>
@@ -56,16 +58,10 @@ export default {
currentObjectPath
}
},
computed: {
formattedTimestamp() {
return this.timestamp !== '---' ? this.formats[this.timestampKey].format(this.timestamp) : this.timestamp;
}
},
mounted() {
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.bounds = this.openmct.time.bounds();
this.limitEvaluator = this.openmct
.telemetry
@@ -80,7 +76,6 @@ export default {
);
this.openmct.time.on('timeSystem', this.updateTimeSystem);
this.openmct.time.on('bounds', this.updateBounds);
this.timestampKey = this.openmct.time.timeSystem().key;
@@ -94,64 +89,43 @@ export default {
.telemetry
.subscribe(this.domainObject, this.updateValues);
this.requestHistory();
this.openmct
.telemetry
.request(this.domainObject, {strategy: 'latest'})
.then((array) => this.updateValues(array[array.length - 1]));
},
destroyed() {
this.stopWatchingMutation();
this.unsubscribe();
this.openmct.time.off('timeSystem', this.updateTimeSystem);
this.openmct.time.off('bounds', this.updateBounds);
this.openmct.off('timeSystem', this.updateTimeSystem);
},
methods: {
updateValues(datum) {
let newTimestamp = this.formats[this.timestampKey].parse(datum),
shouldUpdate = this.timestamp === '---' || newTimestamp >= this.timestamp,
limit;
this.timestamp = this.formats[this.timestampKey].format(datum);
this.value = this.formats[this.valueKey].format(datum);
if(!this.inBounds(newTimestamp)) {
return;
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
if (limit) {
this.valueClass = limit.cssClass;
} else {
this.valueClass = '';
}
if(shouldUpdate) {
this.timestamp = this.formats[this.timestampKey].parse(datum);
this.value = this.formats[this.valueKey].format(datum);
limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
if (limit) {
this.valueClass = limit.cssClass;
} else {
this.valueClass = '';
}
}
},
requestHistory() {
this.timestamp = '---';
this.openmct
.telemetry
.request(this.domainObject, {
start: this.bounds.start,
end: this.bounds.end,
strategy: 'latest'
})
.then((data) => this.updateValues(data[data.length - 1]));
},
updateName(name) {
this.name = name;
},
updateBounds(bounds, isTick) {
this.bounds = bounds;
if(!isTick) {
this.requestHistory();
}
},
inBounds(timestamp) {
return timestamp >= this.bounds.start && timestamp <= this.bounds.end;
},
updateTimeSystem(timeSystem) {
this.value = '---';
this.timestamp = '---';
this.valueClass = '';
this.timestampKey = timeSystem.key;
this.openmct
.telemetry
.request(this.domainObject, {strategy: 'latest'})
.then((array) => this.updateValues(array[array.length - 1]));
},
showContextMenu(event) {
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);

View File

@@ -88,3 +88,4 @@ export default {
}
}
</script>

View File

@@ -19,36 +19,38 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LADTableViewProvider from './LADTableViewProvider';
import LADTableSetViewProvider from './LADTableSetViewProvider';
import LADTableCompositionPolicy from './LADTableCompositionPolicy';
export default function plugin() {
return function install(openmct) {
define([
'./LADTableViewProvider',
'./LADTableSetViewProvider'
], function (
LADTableViewProvider,
LADTableSetViewProvider
) {
return function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
openmct.types.addType('LadTable', {
name: "LAD Table",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.types.addType('LadTable', {
name: "LAD Table",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.types.addType('LadTableSet', {
name: "LAD Table Set",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad-set',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.composition.addPolicy(new LADTableCompositionPolicy(openmct));
openmct.types.addType('LadTableSet', {
name: "LAD Table Set",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad-set',
initialize(domainObject) {
domainObject.composition = [];
}
});
};
};
}
});

View File

@@ -1,356 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadPlugin from './plugin.js';
import Vue from 'vue';
import {
createOpenMct,
getMockObjects,
getMockTelemetry,
getLatestTelemetry
} from 'testTools';
let openmct,
ladPlugin,
parent,
child;
let selectors = {};
selectors.ladTableClass = '.c-table.c-lad-table';
selectors.ladTableBodyRows = selectors.ladTableClass + ' tbody tr';
selectors.ladTableBodyRowsFirstData = selectors.ladTableBodyRows + ' td:first-child';
selectors.ladTableBodyRowsSecondtData = selectors.ladTableBodyRows + ' td:nth-child(2)';
selectors.ladTableBodyRowsThirdData = selectors.ladTableBodyRows + ' td:nth-child(3)';
selectors.ladTableFirstBodyRow = selectors.ladTableClass + ' tbody tr:first-child';
selectors.ladTableFirstRowFirstData = selectors.ladTableBodyRows + ' td:first-child';
selectors.ladTableFirstRowSecondData = selectors.ladTableBodyRows + ' td:nth-child(2)';
selectors.ladTableFirstRowThirdData = selectors.ladTableBodyRows + ' td:nth-child(3)';
selectors.ladTableSetTableHeaders = selectors.ladTableClass + ' .c-table__group-header';
function utcTimeFormat(value) {
return new Date(value).toISOString().replace('T', ' ')
}
describe("The LAD Table", () => {
const ladTableKey = 'LadTable';
let telemetryCount = 3,
timeFormat = 'utc',
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
mockObj = getMockObjects({
objectKeyStrings: ['ladTable', 'telemetry'],
format: timeFormat
}),
bounds = {
start: 0,
end: 4
};
// add telemetry object as composition in lad table
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
// this setups up the app
beforeEach((done) => {
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
parent = document.createElement('div');
child = document.createElement('div');
parent.appendChild(child);
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
ladPlugin = new LadPlugin();
openmct.install(ladPlugin);
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
openmct.time.bounds({ start: bounds.start, end: bounds.end });
openmct.on('start', done);
openmct.start(appHolder);
});
it("should provide a table view only for lad table objects", () => {
let applicableViews = openmct.objectViews.get(mockObj.ladTable),
ladTableView = applicableViews.find(
(viewProvider) => viewProvider.key === ladTableKey
);
expect(applicableViews.length).toEqual(1);
expect(ladTableView).toBeDefined();
});
describe('composition', () => {
let ladTableCompositionCollection;
beforeEach(() => {
ladTableCompositionCollection = openmct.composition.get(mockObj.ladTable);
ladTableCompositionCollection.load();
});
it("should accept telemetry producing objects", () => {
expect(() => {
ladTableCompositionCollection.add(mockObj.telemetry);
}).not.toThrow();
});
it("should reject non-telemtry producing objects", () => {
expect(()=> {
ladTableCompositionCollection.add(mockObj.ladTable);
}).toThrow();
});
});
describe("table view", () => {
let applicableViews,
ladTableViewProvider,
ladTableView,
anotherTelemetryObj = getMockObjects({
objectKeyStrings: ['telemetry'],
overwrite: {
telemetry: {
name: "New Telemetry Object",
identifier: { namespace: "", key: "another-telemetry-object" }
}
}
}).telemetry;
// add another telemetry object as composition in lad table to test multi rows
mockObj.ladTable.composition.push(anotherTelemetryObj.identifier);
beforeEach(async () => {
let telemetryRequestResolve,
telemetryObjectResolve,
anotherTelemetryObjectResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
}),
telemetryObjectPromise = new Promise((resolve) => {
telemetryObjectResolve = resolve;
}),
anotherTelemetryObjectPromise = new Promise((resolve) => {
anotherTelemetryObjectResolve = resolve;
})
openmct.telemetry.request.and.callFake(() => {
telemetryRequestResolve(mockTelemetry);
return telemetryRequestPromise;
});
openmct.objects.get.and.callFake((obj) => {
if(obj.key === 'telemetry-object') {
telemetryObjectResolve(mockObj.telemetry);
return telemetryObjectPromise;
} else {
anotherTelemetryObjectResolve(anotherTelemetryObj);
return anotherTelemetryObjectPromise;
}
});
openmct.time.bounds({ start: bounds.start, end: bounds.end });
applicableViews = openmct.objectViews.get(mockObj.ladTable);
ladTableViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableKey);
ladTableView = ladTableViewProvider.view(mockObj.ladTable, [mockObj.ladTable]);
ladTableView.show(child, true);
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise]);
return await Vue.nextTick();
});
it("should show one row per object in the composition", () => {
const rowCount = parent.querySelectorAll(selectors.ladTableBodyRows).length;
expect(rowCount).toBe(mockObj.ladTable.composition.length);
});
it("should show the most recent datum from the telemetry producing object", async () => {
const latestDatum = getLatestTelemetry(mockTelemetry, { timeFormat });
const expectedDate = utcTimeFormat(latestDatum[timeFormat]);
await Vue.nextTick();
const latestDate = parent.querySelector(selectors.ladTableFirstRowSecondData).innerText;
expect(latestDate).toBe(expectedDate);
});
it("should show the name provided for the the telemetry producing object", () => {
const rowName = parent.querySelector(selectors.ladTableFirstRowFirstData).innerText,
expectedName = mockObj.telemetry.name;
expect(rowName).toBe(expectedName);
});
it("should show the correct values for the datum based on domain and range hints", async () => {
const range = mockObj.telemetry.telemetry.values.find((val) => {
return val.hints && val.hints.range !== undefined;
}).key;
const domain = mockObj.telemetry.telemetry.values.find((val) => {
return val.hints && val.hints.domain !== undefined;
}).key;
const mostRecentTelemetry = getLatestTelemetry(mockTelemetry, { timeFormat });
const rangeValue = mostRecentTelemetry[range];
const domainValue = utcTimeFormat(mostRecentTelemetry[domain]);
await Vue.nextTick();
const actualDomainValue = parent.querySelector(selectors.ladTableFirstRowSecondData).innerText;
const actualRangeValue = parent.querySelector(selectors.ladTableFirstRowThirdData).innerText;
expect(actualRangeValue).toBe(rangeValue);
expect(actualDomainValue).toBe(domainValue);
});
});
});
describe("The LAD Table Set", () => {
const ladTableSetKey = 'LadTableSet';
let telemetryCount = 3,
timeFormat = 'utc',
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
mockObj = getMockObjects({
objectKeyStrings: ['ladTable', 'ladTableSet', 'telemetry']
}),
bounds = {
start: 0,
end: 4
};
// add mock telemetry to lad table and lad table to lad table set (composition)
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
mockObj.ladTableSet.composition.push(mockObj.ladTable.identifier);
beforeEach((done) => {
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
parent = document.createElement('div');
child = document.createElement('div');
parent.appendChild(child);
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
ladPlugin = new LadPlugin();
openmct.install(ladPlugin);
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
openmct.time.bounds({ start: bounds.start, end: bounds.end });
openmct.on('start', done);
openmct.start(appHolder);
});
it("should provide a lad table set view only for lad table set objects", () => {
let applicableViews = openmct.objectViews.get(mockObj.ladTableSet),
ladTableSetView = applicableViews.find(
(viewProvider) => viewProvider.key === ladTableSetKey
);
expect(applicableViews.length).toEqual(1);
expect(ladTableSetView).toBeDefined();
});
describe('composition', () => {
let ladTableSetCompositionCollection;
beforeEach(() => {
ladTableSetCompositionCollection = openmct.composition.get(mockObj.ladTableSet);
ladTableSetCompositionCollection.load();
});
it("should accept lad table objects", () => {
expect(() => {
ladTableSetCompositionCollection.add(mockObj.ladTable);
}).not.toThrow();
});
it("should reject non lad table objects", () => {
expect(()=> {
ladTableSetCompositionCollection.add(mockObj.telemetry);
}).toThrow();
});
});
describe("table view", () => {
let applicableViews,
ladTableSetViewProvider,
ladTableSetView,
otherObj = getMockObjects({
objectKeyStrings: ['ladTable'],
overwrite: {
ladTable: {
name: "New LAD Table Object",
identifier: { namespace: "", key: "another-lad-object" }
}
}
});
// add another lad table (with telemetry object) object to the lad table set for multi row test
otherObj.ladTable.composition.push(mockObj.telemetry.identifier);
mockObj.ladTableSet.composition.push(otherObj.ladTable.identifier);
beforeEach(async () => {
let telemetryRequestResolve,
ladObjectResolve,
anotherLadObjectResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
}),
ladObjectPromise = new Promise((resolve) => {
ladObjectResolve = resolve;
}),
anotherLadObjectPromise = new Promise((resolve) => {
anotherLadObjectResolve = resolve;
})
openmct.telemetry.request.and.callFake(() => {
telemetryRequestResolve(mockTelemetry);
return telemetryRequestPromise;
});
openmct.objects.get.and.callFake((obj) => {
if(obj.key === 'lad-object') {
ladObjectResolve(mockObj.ladObject);
return ladObjectPromise;
} else if(obj.key === 'another-lad-object') {
anotherLadObjectResolve(otherObj.ladObject);
return anotherLadObjectPromise;
}
return Promise.resolve({});
});
openmct.time.bounds({ start: bounds.start, end: bounds.end });
applicableViews = openmct.objectViews.get(mockObj.ladTableSet);
ladTableSetViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableSetKey);
ladTableSetView = ladTableSetViewProvider.view(mockObj.ladTableSet, [mockObj.ladTableSet]);
ladTableSetView.show(child, true);
await Promise.all([telemetryRequestPromise, ladObjectPromise, anotherLadObjectPromise]);
return await Vue.nextTick();
});
it("should show one row per lad table object in the composition", () => {
const rowCount = parent.querySelectorAll(selectors.ladTableSetTableHeaders).length;
expect(rowCount).toBe(mockObj.ladTableSet.composition.length);
pending();
});
});
});

View File

@@ -70,18 +70,15 @@ export default class ConditionClass extends EventEmitter {
return;
}
if (this.isTelemetryUsed(datum.id)) {
this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects);
} else {
criterion.getResult(datum);
}
});
this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects);
} else {
criterion.getResult(datum);
}
});
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
}
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
}
isAnyOrAllTelemetry(criterion) {

View File

@@ -57,7 +57,6 @@ export default class ConditionManager extends EventEmitter {
endpoint,
this.telemetryReceived.bind(this, endpoint)
);
// TODO check if this is needed
this.updateConditionTelemetry();
}

View File

@@ -47,24 +47,17 @@ describe("The condition", function () {
name: "Test Object",
telemetry: {
values: [{
key: "value",
name: "Value",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
key: "some-key",
name: "Some attribute",
hints: {
domain: 1
}
}, {
key: "testSource",
source: "value",
name: "Test",
format: "string"
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
}]
}
};
@@ -143,38 +136,4 @@ describe("The condition", function () {
expect(result).toBeTrue();
expect(conditionObj.criteria.length).toEqual(0);
});
it("gets the result of a condition when new telemetry data is received", function () {
conditionObj.getResult({
value: '0',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeTrue();
});
it("gets the result of a condition when new telemetry data is received", function () {
conditionObj.getResult({
value: '1',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeFalse();
});
it("keeps the old result new telemetry data is not used by it", function () {
conditionObj.getResult({
value: '0',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(conditionObj.result).toBeTrue();
conditionObj.getResult({
value: '1',
utc: 'Hi',
id: '1234'
});
expect(conditionObj.result).toBeTrue();
});
});

View File

@@ -172,7 +172,6 @@ export default {
&& this.embed.bounds.end !== bounds.end;
const isFixedTimespanMode = !this.openmct.time.clock();
this.openmct.time.stopClock();
window.location.href = link;
let message = '';

View File

@@ -325,7 +325,7 @@ define([
} else {
// A history entry is created by startMarquee, need to remove
// if marquee zoom doesn't occur.
this.plotHistory.pop();
this.back();
}
this.$scope.rectangles = [];
this.marquee = undefined;

View File

@@ -1,72 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import PlotlyViewLayout from './components/PlotlyViewLayout.vue';
import Vue from 'vue';
export default function PlotlyViewProvider(openmct) {
return {
key: 'plotlyPlot',
name: 'Plotly Plot',
cssClass: 'icon-plot-overlay',
canView: function (domainObject) {
return domainObject.type === 'plotlyPlot';
},
canEdit: function (domainObject) {
return domainObject.type === 'plotlyPlot';
},
view: function (domainObject) {
let component;
return {
show: function (element, isEditing) {
component = new Vue({
provide: {
openmct,
domainObject
},
el: element,
components: {
PlotlyViewLayout
},
data() {
return {
isEditing
}
},
template: '<plotly-view-layout :isEditing="isEditing"></plotly-view-layout>'
});
},
onEditModeChange: function (isEditing) {
component.isEditing = isEditing;
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}

View File

@@ -1,221 +0,0 @@
<template>
<div class="l-view-section"></div>
</template>
<script>
import Plotly from 'plotly.js-dist';
import moment from 'moment'
export default {
inject: ['openmct', 'domainObject'],
data: function () {
return {
telemetryObjects: [],
bounds: this.openmct.time.bounds(),
timeRange: 0,
plotData: {},
subscriptions: {}
}
},
mounted() {
this.plotElement = document.querySelector('.l-view-section');
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addTelemetry);
this.composition.on('remove', this.removeTelemetry);
this.composition.load();
this.openmct.time.on('bounds', this.refreshData);
this.openmct.time.on('clock', this.changeClock);
},
destroyed() {
this.unsubscribe();
},
methods: {
changeClock() {
if (this.openmct.time.clock()) {
Plotly.purge(this.plotElement);
this.telemetryObjects.forEach((telemetryObject, index) => {
this.subscribeTo(telemetryObject, index);
});
}
},
addTelemetry(telemetryObject) {
this.telemetryObjects.push(telemetryObject);
const index = this.telemetryObjects.findIndex(obj => obj === telemetryObject);
this.requestHistory(telemetryObject, index, true);
this.subscribeTo(telemetryObject, index);
},
subscribeTo(telemetryObject, index) {
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
//Check that telemetry object has not been removed since telemetry was requested.
if (!this.telemetryObjects.includes(telemetryObject)) {
return;
}
const length = this.plotData[telemetryObject.identifier.key].x.length;
this.updateData(datum, index, length);
});
},
unsubscribe(keyString) {
this.subscriptions[keyString]();
delete this.subscriptions[keyString];
},
refreshData(bounds, isTick) {
this.bounds = bounds;
this.telemetryObjects.forEach((telemetryObject, index) => {
if(!isTick) {
this.requestHistory(telemetryObject, index, false);
} else {
if (this.timeRange === 0 || this.timeRange !== this.bounds.end - this.bounds.start) {
this.timeRange = this.bounds.end - this.bounds.start;
this.requestHistory(telemetryObject, index, false);
}
}
});
},
requestHistory(telemetryObject, index, isAdd) {
this.openmct
.telemetry
.request(telemetryObject, {
start: this.bounds.start,
end: this.bounds.end
})
.then((telemetryData) => {
this.addTrace(telemetryData, telemetryObject, index, isAdd);
});
},
getLayout(telemetryObject, isFixed) {
return {
hovermode: 'compare',
hoverdistance: -1,
autosize: "true",
showlegend: false,
font: {
family: "'Helvetica Neue', Helvetica, Arial, sans-serif",
size: "12px",
color: "#666"
},
xaxis: { // hardcoded as UTC for now
title: 'UTC',
zeroline: false,
range: isFixed ? 'undefined' : [
this.formatDatumX({utc: this.bounds.start}),
this.formatDatumX({utc: this.bounds.start})
]
},
yaxis: {
title: this.getYAxisLabel(telemetryObject),
zeroline: false
},
margin: {
l: 40,
r: 10,
b: 40,
t: 10
},
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent'
}
},
removeTelemetry(identifier) {
let keyString = this.openmct.objects.makeKeyString(identifier);
this.unsubscribe(keyString);
this.telemetryObjects = this.telemetryObjects.filter((object) => !_.eq(identifier, object.identifier));
if (!this.domainObject.composition.length) {
Plotly.purge(this.plotElement);
} else {
Plotly.deleteTraces(this.plotElement, this.domainObject.composition.length);
}
},
getYAxisLabel(telemetryObject) {
this.setYAxisProp(telemetryObject);
const valueMetadatas = this.openmct.telemetry.getMetadata(telemetryObject).values();
const index = valueMetadatas.findIndex(value => value.key === this.yAxisProp);
const yLabel = valueMetadatas[index].name;
return yLabel;
},
setYAxisProp(telemetryObject) {
if (telemetryObject.type === 'generator') {
this.yAxisProp = 'sin';
} else if (telemetryObject.type === 'example.state-generator') {
this.yAxisProp = 'state';
} else if (telemetryObject.type === 'conditionSet') {
this.yAxisProp = 'output';
}
},
formatDatumX(datum) {
let timestamp = moment.utc(datum.utc).format('YYYY-MM-DDTHH:mm:ss[Z]');
return timestamp;
},
formatDatumY(datum) {
return datum.sin;
},
addTrace(telemetryData, telemetryObject, index, isAdd) {
let x = [];
let y = [];
const colors = ['red', 'green', 'blue'];
telemetryData.forEach((datum) => {
x.push(this.formatDatumX(datum));
y.push(this.formatDatumY(datum));
})
let traceData = [{ // trace configuration
x,
y,
type: 'scattergl',
mode: 'lines+markers',
line: {
color: colors[index], // to set new color for each trace
shape: 'linear'
}
}];
this.plotData[telemetryObject.identifier.key] = traceData[0];
if (!this.plotElement.childNodes.length) { // not traces yet, so create new plot
Plotly.newPlot(
this.plotElement,
traceData,
this.getLayout(telemetryObject, true),
{
displayModeBar: false, // turns off hover-activated toolbar
staticPlot: true // turns off hover effects on datapoints
}
);
} else {
if (isAdd) { // add a new trace to existing plot
Plotly.addTraces(this.plotElement, traceData);
} else { // update existing trace with new data (bounds change)
Plotly.react(this.plotElement, Object.values(this.plotData), this.getLayout(telemetryObject, false));
}
}
},
updateData(datum, index, length) {
// plot all datapoints within bounds
if (datum.utc <= this.bounds.end && this.openmct.time.clock()) {
Plotly.extendTraces(
this.plotElement,
{
x: [[this.formatDatumX(datum)]],
y: [[this.formatDatumY(datum)]]
},
[index], // apply changes to particular trace
length // set the fixed number of points (will drop points from beginning as new points are added)
);
let newRange = {
'xaxis.range': [this.formatDatumX({utc: this.bounds.start}),this.formatDatumX({utc: this.bounds.end})]
};
Plotly.relayout(this.plotElement, newRange);
}
}
}
}
</script>

View File

@@ -1,2 +0,0 @@
.plot svg {
}

View File

@@ -1,18 +0,0 @@
import PlotlyViewProvider from './PlotlyViewProvider.js';
export default function () {
return function install(openmct) {
openmct.objectViews.addProvider(new PlotlyViewProvider(openmct));
openmct.types.addType('plotlyPlot', {
name: "Plotly Plot",
description: "Simple plot rendered by plotly.js",
creatable: true,
cssClass: 'icon-plot-overlay',
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.telemetry = {};
}
});
};
}

View File

@@ -34,7 +34,6 @@ define([
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin',
'./plotlyPlot/plugin',
'./telemetryTable/plugin',
'./staticRootPlugin/plugin',
'./notebook/plugin',
@@ -52,7 +51,8 @@ define([
'./conditionWidget/plugin',
'./themes/espresso',
'./themes/maelstrom',
'./themes/snow'
'./themes/snow',
'./windowLayout/plugin'
], function (
_,
UTCTimeSystem,
@@ -67,7 +67,6 @@ define([
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin,
PlotlyPlotPlugin,
TelemetryTablePlugin,
StaticRootPlugin,
Notebook,
@@ -85,7 +84,8 @@ define([
ConditionWidgetPlugin,
Espresso,
Maelstrom,
Snow
Snow,
WindowLayout
) {
var bundleMap = {
LocalStorage: 'platform/persistence/local',
@@ -173,8 +173,8 @@ define([
plugins.ExampleImagery = ExampleImagery;
plugins.ImageryPlugin = ImageryPlugin;
plugins.Plot = PlotPlugin;
plugins.PlotlyPlot = PlotlyPlotPlugin.default;
plugins.TelemetryTable = TelemetryTablePlugin;
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin;
@@ -183,7 +183,7 @@ define([
plugins.FolderView = FolderView;
plugins.Tabs = Tabs;
plugins.FlexibleLayout = FlexibleLayout;
plugins.LADTable = LADTable.default;
plugins.LADTable = LADTable;
plugins.Filters = Filters;
plugins.ObjectMigration = ObjectMigration.default;
plugins.GoToOriginalAction = GoToOriginalAction.default;
@@ -194,6 +194,7 @@ define([
plugins.Snow = Snow.default;
plugins.Condition = ConditionPlugin.default;
plugins.ConditionWidget = ConditionWidgetPlugin.default;
plugins.WindowLayout = WindowLayout.default;
return plugins;
});

View File

@@ -0,0 +1,183 @@
<template>
<div v-if="!isOpener"
class="c-indicator c-indicator--clickable icon-save s-status-caution"
>
<span class="label c-indicator__label">
<button @click="open2Windows">Open 2 Windows</button>
<button @click="closeAllWindows">Close Windows</button>
<button @click="openSavedWindows">Open Saved Windows</button>
</span>
</div>
</template>
<script>
export default {
inject: ['openmct'],
computed: {
isOpener() {
return window.opener;
}
},
mounted() {
this.openWindows = {};
this.windowFeatures = "menubar=yes,location=yes,tabs=yes, resizable=yes,scrollbars=yes, innerHeight=480,innerWidth=640,screenY=100";
window.addEventListener("message", this.receiveMessage, false);
if (window.opener) {
window.opener.postMessage({
status: 'READY',
name: window.name
},'*');
window.addEventListener('beforeunload', () => {
window.opener.postMessage({
status: 'CLOSED',
name: window.name
},'*');
});
window.addEventListener('blur', () => {
window.opener.postMessage({
query: 'QUERY__SIZE',
info: {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth,
screenLeft: window.screenLeft,
screenTop: window.screenTop,
screenWidth: screen.width,
screenHeight: screen.height,
screenAvailableWidth: screen.availWidth,
screenAvailHeight: screen.availHeight,
devicePixelRatio: window.devicePixelRatio,
url: window.location.href
},
name: window.name
},'*');
});
}
},
methods: {
persistWindowInformation() {
window.localStorage.setItem('openmct-windowlayout-items',
JSON.stringify(this.getOpenWindowSpecifications()));
},
getScreenX() {
return !Object.keys(this.openWindows).length ? 100: (screen.width - (Object.keys(this.openWindows).length*640));
},
open2Windows() {
this.openWindow(`${this.windowFeatures},screenX=${this.getScreenX()}`);
this.openWindow(`${this.windowFeatures},screenX=${this.getScreenX()}`);
},
openWindow(windowFeatures) {
let newWindowName = `Open MCT Window ${Object.keys(this.openWindows).length+1}`;
this.openWindows[newWindowName] = { windowReference: window.open(window.location.href, newWindowName, windowFeatures)};
},
moveWindow() {
const key = Object.keys(this.openWindows)[0];
this.openWindows[key].postMessage({
command: 'moveTo',
params: [window.screenLeft + 40, window.screenTop + 40]
}, 'http://localhost:8080');
},
openSavedWindows() {
const persistedWindowObjs = window.localStorage.getItem('openmct-windowlayout-items');
if (persistedWindowObjs) {
const windowObjs = JSON.parse(persistedWindowObjs);
windowObjs.forEach(windowObj => {
let newWindowName = windowObj.name;
const features = `menubar=yes,location=yes,resizable=yes,scrollbars=yes,innerHeight=${windowObj.info.innerHeight || 480},innerWidth=${windowObj.info.innerWidth || 640},screenX=${windowObj.info.screenLeft || this.getScreenX()},screenY=${windowObj.info.screenTop || 100}`;
const windowReference = window.open((windowObj.info.url || window.location.href), newWindowName, features);
this.openWindows[newWindowName] = {
windowReference,
name: newWindowName,
info: windowObj.info
}
});
}
},
closeAllWindows() {
this.persistWindowInformation();
Object.keys(this.openWindows).forEach((windowName => {
const windowObj = this.openWindows[windowName];
windowObj.closedByOpener = true;
windowObj.windowReference.close();
}));
},
receiveMessage(event) {
const { data, origin, source} = event;
switch(origin) {
case 'http://localhost:8080':
if (data) {
if (data.status === 'READY') {
let newWindowReference = this.openWindows[data.name].windowReference;
newWindowReference.postMessage({
command: 'QUERY__SIZE'
}, 'http://localhost:8080');
} else if (data.status === 'CLOSED') {
const closedByOpener = this.openWindows[data.name].closedByOpener;
this.openWindows[data.name] = undefined;
delete this.openWindows[data.name];
if (!closedByOpener) {
this.persistWindowInformation();
}
} else if (data.command) {
switch(data.command) {
case 'QUERY__SIZE':
source.postMessage({
query: data.command,
info: {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth,
screenLeft: window.screenLeft,
screenTop: window.screenTop,
screenWidth: screen.width,
screenHeight: screen.height,
screenAvailableWidth: screen.availWidth,
screenAvailHeight: screen.availHeight,
devicePixelRatio: window.devicePixelRatio,
url: window.location.href
},
name: window.name
}, origin);
break;
default:
window[data.command](...data.params);
this.$nextTick(() => {
source.postMessage({
query: data.command,
name: window.name
}, origin);
});
break;
}
} else if (data.query) {
if (data.info) {
if (!this.openWindows[data.name]) {
return;
}
this.openWindows[data.name].info = data.info;
this.persistWindowInformation();
} else {
if (!this.openWindows[data.name]) {
return;
}
let newWindowReference = this.openWindows[data.name].windowReference;
newWindowReference.postMessage({
command: 'QUERY__SIZE'
}, 'http://localhost:8080');
}
}
}
break;
default:
break;
}
},
getOpenWindowSpecifications() {
return Object.keys(this.openWindows).map(key => {
return {
name: key,
info: this.openWindows[key].info
}
});
}
}
}
</script>

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,21 +19,25 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Vue from 'vue';
import WindowLayout from "./components/WindowLayout.vue";
export default class LADTableCompositionPolicy {
export default function plugin() {
constructor(openmct) {
this.openmct = openmct;
return this.allow.bind(this);
}
return function install(openmct) {
let component = new Vue ({
provide: {
openmct
},
components: {
WindowLayout: WindowLayout
},
template: '<WindowLayout></WindowLayout>'
}),
indicator = {
element: component.$mount().$el
};
allow(parent, child) {
if(parent.type === 'LadTable') {
return this.openmct.telemetry.isTelemetryObject(child);
} else if(parent.type === 'LadTableSet') {
return child.type === 'LadTable';
}
return true;
}
openmct.indicators.add(indicator);
};
}

View File

@@ -18,7 +18,6 @@
@import "../plugins/folderView/components/list-item.scss";
@import "../plugins/folderView/components/list-view.scss";
@import "../plugins/imagery/components/imagery-view-layout.scss";
@import "../plugins/plotlyPlot/components/plotly.scss";
@import "../plugins/telemetryTable/components/table-row.scss";
@import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss";
@import "../plugins/tabs/components/tabs.scss";

View File

@@ -1,6 +1,4 @@
import MCT from 'MCT';
let nativeFunctions = [],
mockObjects = setMockObjects();
export function createOpenMct() {
const openmct = new MCT();
@@ -18,225 +16,3 @@ export function createMouseEvent(eventName) {
view: window
});
}
export const spyOnBuiltins = (functionNames, object = window) => {
functionNames.forEach(functionName => {
if (nativeFunctions[functionName]) {
throw `Builtin spy function already defined for ${functionName}`;
}
nativeFunctions.push({functionName, object, nativeFunction: object[functionName]});
spyOn(object, functionName);
});
};
export const clearBuiltinSpies = () => {
nativeFunctions.forEach(clearBuiltinSpy);
nativeFunctions = [];
};
function clearBuiltinSpy(funcDefinition) {
funcDefinition.object[funcDefinition.functionName] = funcDefinition.nativeFunction;
}
export const getLatestTelemetry = (telemetry = [], opts = {}) => {
let latest = [],
timeFormat = opts.timeFormat || 'utc';
if(telemetry.length) {
latest = telemetry.reduce((prev, cur) => {
return prev[timeFormat] > cur[timeFormat] ? prev : cur;
});
}
return latest;
};
// EXAMPLE:
// getMockObjects({
// name: 'Jamie Telemetry',
// keys: ['test','other','yeah','sup'],
// format: 'local',
// telemetryConfig: {
// hints: {
// test: {
// domain: 1
// },
// other: {
// range: 2
// }
// }
// }
// })
export const getMockObjects = (opts = {}) => {
opts.type = opts.type || 'default';
if(opts.objectKeyStrings && !Array.isArray(opts.objectKeyStrings)) {
throw `"getMockObjects" optional parameter "objectKeyStrings" must be an array of string object keys`;
}
let requestedMocks = {};
if (!opts.objectKeyStrings) {
requestedMocks = copyObj(mockObjects[opts.type]);
} else {
opts.objectKeyStrings.forEach(objKey => {
if(mockObjects[opts.type] && mockObjects[opts.type][objKey]) {
requestedMocks[objKey] = copyObj(mockObjects[opts.type][objKey]);
} else {
throw `No mock object for object key "${objKey}" of type "${opts.type}"`;
}
});
}
// build out custom telemetry mappings if necessary
if(requestedMocks.telemetry && opts.telemetryConfig) {
let keys = opts.telemetryConfig.keys,
format = opts.telemetryConfig.format || 'utc',
hints = opts.telemetryConfig.hints,
values;
// if utc, keep default
if(format === 'utc') {
// save for later if new keys
if(keys) {
format = requestedMocks.telemetry
.telemetry.values.find((vals) => vals.key === 'utc');
}
} else {
format = {
key: format,
name: "Time",
format: format === 'local' ? 'local-format' : format,
hints: {
domain: 1
}
}
}
if(keys) {
values = keys.map((key) => ({ key, name: key + ' attribute' }));
values.push(format); // add time format back in
} else {
values = requestedMocks.telemetry.telemetry.values;
}
if(hints) {
for(let val of values) {
if(hints[val.key]) {
val.hints = hints[val.key];
}
}
}
requestedMocks.telemetry.telemetry.values = values;
}
// overwrite any field keys
if(opts.overwrite) {
for(let mock in requestedMocks) {
if(opts.overwrite[mock]) {
for(let key in opts.overwrite[mock]) {
if (Object.prototype.hasOwnProperty.call(opts.overwrite[mock], key)) {
requestedMocks[mock][key] = opts.overwrite[mock][key];
}
}
}
}
}
return requestedMocks;
}
// EXAMPLE:
// getMockTelemetry({
// name: 'My Telemetry',
// keys: ['test','other','yeah','sup'],
// count: 8,
// format: 'local'
// })
export const getMockTelemetry = (opts = {}) => {
let count = opts.count || 2,
format = opts.format || 'utc',
name = opts.name || 'Mock Telemetry Datum',
keyCount = 2,
keys = false,
telemetry = [];
if(opts.keys && Array.isArray(opts.keys)) {
keyCount = opts.keys.length;
keys = opts.keys;
} else if(opts.keyCount) {
keyCount = opts.keyCount;
}
for(let i = 1; i < count + 1; i++) {
let datum = {
[format]: i,
name
}
for(let k = 1; k < keyCount + 1; k++) {
let key = keys ? keys[k - 1] : 'some-key-' + k,
value = keys ? keys[k - 1] + ' value ' + i : 'some value ' + i + '-' + k;
datum[key] = value;
}
telemetry.push(datum);
}
return telemetry;
}
// copy objects a bit more easily
function copyObj(obj) {
return JSON.parse(JSON.stringify(obj));
}
// add any other necessary types to this mockObjects object
function setMockObjects() {
return {
default: {
ladTable: {
identifier: { namespace: "", key: "lad-object"},
type: 'LadTable',
composition: []
},
ladTableSet: {
identifier: { namespace: "", key: "lad-set-object"},
type: 'LadTableSet',
composition: []
},
telemetry: {
identifier: { namespace: "", key: "telemetry-object"},
type: "test-telemetry-object",
name: "Test Telemetry Object",
telemetry: {
values: [{
key: "name",
name: "Name",
format: "string"
},{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},{
name: "Some attribute 1",
key: "some-key-1",
hints: {
range: 1
}
}, {
name: "Some attribute 2",
key: "some-key-2"
}]
}
}
},
otherType: {
example: {}
}
}
}