Compare commits

..

15 Commits

Author SHA1 Message Date
Henry
a0de23091b Added support for new style telemetry providers from old screens. Converted SWG to new style data adapter 2017-02-21 15:56:46 -08:00
Henry
722e384df4 [API] Renamed TypeRegistry function 'addType' to 'register' 2017-02-21 15:15:16 -08:00
Pete Richards
ab83beb17c Handle unspecified build environment 2017-02-21 10:20:13 -08:00
Henry
370421b4a5 Added options to specify default time span for conductor. Conductor also now defaults into realtime mode 2017-02-20 15:39:00 -08:00
Pete Richards
3e5f4c8b88 checkpoint agian 2017-02-16 16:57:37 -08:00
Pete Richards
b1f1568657 Proper cssClass capitalization 2017-02-16 14:41:52 -08:00
Pete Richards
33d24c9b60 checkpoint 2017-02-16 11:11:43 -08:00
Pete Richards
33c060473a Checkpoint at end of day, maybe questionable code 2017-02-15 17:17:34 -08:00
Pete Richards
3b01b9ee51 will it blend?
Squashed commit of the following:

commit 3d3baddd23
Author: Henry <akhenry@gmail.com>
Date:   Thu Feb 2 15:08:26 2017 -0800

    [Tables] Do not persist column configuration for non-editable objects
2017-02-15 16:08:56 -08:00
Pete Richards
26a0aed159 Merge new merged tables. YEA!
Squashed commit of the following:

commit 34dc457aff
Author: Henry <akhenry@gmail.com>
Date:   Fri Feb 10 15:35:17 2017 -0800

    [Tables] Restored telemetry datum field 'name'. Fixed bug with default sort not working

commit a3311e4c57
Author: Henry <akhenry@gmail.com>
Date:   Thu Jan 26 10:59:22 2017 -0800

    [Tables] Tests and style fixes

commit ef8efbd53d
Author: Henry <akhenry@gmail.com>
Date:   Wed Jan 25 15:41:08 2017 -0800

    [Tables] Default UTC time system if available and none others defined

commit 6cd99efbb9
Author: Henry <akhenry@gmail.com>
Date:   Mon Jan 23 12:43:59 2017 -0800

    [Tables] Added telemetry buffer so that subscription data is not discarded if it's beyond the end bounds

commit ae2b73a4f5
Author: Henry <akhenry@gmail.com>
Date:   Thu Jan 19 21:18:53 2017 -0800

    [Tables] Increase default table size

commit 0c3ff82cfe
Author: Henry <akhenry@gmail.com>
Date:   Tue Jan 17 14:44:09 2017 -0800

    [Table] Added ticking to combined historical/real-time table

    Don't add duplicate telemetry data

commit 50f303bbdc
Author: Henry <akhenry@gmail.com>
Date:   Sun Jan 15 10:59:28 2017 -0800

    [Tables] limit digests to increase performance

commit 2a4944d6ee
Author: Henry <akhenry@gmail.com>
Date:   Fri Dec 16 16:34:41 2016 -0800

    [Tables] Refactoring for consolidation of historical and real-time tables

    Added batch processing of large historical queries. #1077

commit 3544caf4be
Author: Henry <akhenry@gmail.com>
Date:   Thu Dec 15 15:21:45 2016 -0800

    [API] Observer path was accessing object key incorrectly

commit 976333d7f7
Author: Henry <akhenry@gmail.com>
Date:   Tue Dec 6 18:04:47 2016 -0800

    [Tables] Support for subscriptions from new Telemetry API

    Historical and real-time data flowing

    Added formatting, and limits. Support telemetry objects themselves and not just composition of telemetry objects

    Apply default time range if none supplied (15 minutes)

commit 6d5530ba9c
Author: Henry <akhenry@gmail.com>
Date:   Tue Dec 6 12:08:52 2016 -0800

    [Tables] Using new composition API to fetch all telemetry objects
2017-02-15 16:05:50 -08:00
Pete Richards
1c4a67ebd1 change orphan check behavior 2017-02-15 15:39:36 -08:00
Pete Richards
f6fd572e4f cssclass is now cssClass 2017-02-15 15:02:39 -08:00
Pete Richards
2ec1f76fc5 Skip optimze in dev environment 2017-02-15 12:15:49 -08:00
Pete Richards
8f618a1f35 Ensure composition providers get new format objects 2017-02-15 12:15:34 -08:00
Pete Richards
6a89e6da50 Stop loading old-style bundles.json 2017-02-15 10:51:36 -08:00
43 changed files with 458 additions and 431 deletions

View File

@@ -1,6 +1,6 @@
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Open MCT is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)

View File

@@ -131,7 +131,7 @@ Keeping that in mind, there are a few useful patterns supported by the
framework that are useful to keep in mind.
The specific service infrastructure provided by the platform is described
in the [Platform Architecture](platform.md).
in the [Platform Architecture](Platform.md).
## Extension Categories

View File

@@ -2261,7 +2261,10 @@ The platform understands the following policy categories (specifiable as the
* `action`: Determines whether or not a given action is allowable. The candidate
argument here is an Action; the context is its action context object.
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
* `composition`: Determines whether or not domain objects of a given type are
allowed to contain domain objects of another type. The candidate argument here
is the container's `Type`; the context argument is the `Type` of the object to be
contained.
* `view`: Determines whether or not a view is applicable for a domain object.
The candidate argument is the view's extension definition; the context argument
is the `DomainObject` to be viewed.

View File

@@ -1,184 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, 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.
*****************************************************************************/
/*global define*/
define([
"./src/SinewaveTelemetryProvider",
"./src/SinewaveLimitCapability",
"./src/SinewaveDeltaFormat",
'legacyRegistry'
], function (
SinewaveTelemetryProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/generator", {
"name": "Sine Wave Generator",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"extensions": {
"components": [
{
"implementation": SinewaveTelemetryProvider,
"type": "provider",
"provides": "telemetryService",
"depends": [
"$q",
"$timeout"
]
}
],
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta"
}
],
"priority": -1
}
],
"types": [
{
"key": "generator",
"name": "Sine Wave Generator",
"cssClass": "icon-telemetry",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {
"period": 10,
"amplitude": 1,
"offset": 0,
"dataRateInHz": 1
}
},
"telemetry": {
"source": "generator",
"domains": [
{
"key": "utc",
"name": "Time",
"format": "utc"
},
{
"key": "yesterday",
"name": "Yesterday",
"format": "utc"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Amplitude",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "amplitude",
"required": true,
"property": [
"telemetry",
"amplitude"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Offset",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "offset",
"required": true,
"property": [
"telemetry",
"offset"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Data Rate (hz)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "dataRateInHz",
"required": true,
"property": [
"telemetry",
"dataRateInHz"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@@ -28,11 +28,10 @@
<script src="bower_components/requirejs/require.js">
</script>
<script>
require(['openmct'], function (openmct) {
require(['openmct'], function (openmct, generatorPlugin) {
[
'example/imagery',
'example/eventGenerator',
'example/generator'
'example/eventGenerator'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
@@ -40,11 +39,12 @@
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.Conductor({
defaultTimeSystem: 'utc',
defaultTimespan: 30 * 60 * 1000,
showConductor: false
showConductor: true
}));
openmct.start();

View File

@@ -48,7 +48,7 @@ define([], function () {
parent.useCapability('composition')
.then(function (composees) {
var isOrphan = composees.every(function (c) {
return c.getId() !== domainObject.getId();
return c.getId() !== domainObject.getId()
});
if (isOrphan) {
parent.getCapability('action').perform('navigate');

View File

@@ -33,7 +33,7 @@ define([
mockContext,
mockActionCapability,
mockEditor,
testParentComposition,
testParentModel,
testId,
mockThrottledFns;
@@ -41,6 +41,7 @@ define([
testId = 'some-identifier';
mockThrottledFns = [];
testParentModel = {};
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
@@ -54,12 +55,14 @@ define([
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'useCapability'
'getModel',
'hasCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
@@ -72,7 +75,9 @@ define([
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.andReturn(mockMutationTopic);
mockTopic.andCallFake(function (k) {
return k === 'mutation' && mockMutationTopic;
});
mockDomainObject.getId.andReturn(testId);
mockDomainObject.getCapability.andCallFake(function (c) {
return {
@@ -83,13 +88,12 @@ define([
mockDomainObject.hasCapability.andCallFake(function (c) {
return !!mockDomainObject.getCapability(c);
});
mockParentObject.getModel.andReturn(testParentModel);
mockParentObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability
}[c];
});
testParentComposition = [];
mockParentObject.useCapability.andReturn(Promise.resolve(testParentComposition));
mockContext.getParent.andReturn(mockParentObject);
mockNavigationService.getNavigation.andReturn(mockDomainObject);
mockEditor.isEditContextRoot.andReturn(false);
@@ -122,9 +126,7 @@ define([
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
if (!isOrphan) {
testParentComposition.push(mockDomainObject);
}
testParentModel.composition = isOrphan ? [] : [testId];
});
[false, true].forEach(function (isEditRoot) {
@@ -134,31 +136,13 @@ define([
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
} else {
it("does nothing", function () {
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
}
}
@@ -173,6 +157,7 @@ define([
mockNavigationService.addListener.mostRecentCall
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});

View File

@@ -54,7 +54,8 @@ define(
AddActionProvider.prototype.getActions = function (actionContext) {
var context = actionContext || {},
key = context.key,
destination = context.domainObject;
destination = context.domainObject,
self = this;
// We only provide Add actions, and we need a
// domain object to serve as the container for the
@@ -65,16 +66,16 @@ define(
}
// Introduce one create action per type
return ['timeline', 'activity'].map(function (type) {
['timeline', 'activity'].map(function (type) {
return new AddAction(
this.typeService.getType(type),
type,
destination,
context,
this.$q,
this.dialogService,
this.policyService
self.$q,
self.dialogService,
self.policyService
);
}, this);
});
};
return AddActionProvider;

View File

@@ -59,12 +59,12 @@ define(
domainObject = this.domainObject,
policyService = this.policyService;
function validateLocation(parent) {
var parentType = parent &&
parent.getCapability('type');
return parentType && policyService.allow(
function validateLocation(locatingObject) {
var locatingType = locatingObject &&
locatingObject.getCapability('type');
return locatingType && policyService.allow(
"composition",
parentType,
locatingType,
domainObject
);
}
@@ -118,7 +118,7 @@ define(
formModel = this.createModel(formValue);
formModel.location = parent.getId();
this.domainObject.useCapability("mutation", function () {
this.domainObject.useCapability("mutation", function (model) {
return formModel;
});
return this.domainObject;

View File

@@ -31,7 +31,9 @@ define(
var mockTypeService,
mockDialogService,
mockPolicyService,
mockTypeMap,
mockCreationPolicy,
mockCompositionPolicy,
mockPolicyMap = {},
mockTypes,
mockDomainObject,
mockQ,
@@ -53,33 +55,49 @@ define(
);
mockType.hasFeature.andReturn(true);
mockType.getName.andReturn(name);
mockType.getKey.andReturn(name);
return mockType;
}
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
"typeService",
["getType"]
["listTypes"]
);
mockDialogService = jasmine.createSpyObj(
"dialogService",
["getUserInput"]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
["allow"]
);
mockDialogService = {};
mockPolicyService = {};
mockDomainObject = {};
mockTypes = [
"timeline",
"activity",
"other"
].map(createMockType);
mockTypeMap = {};
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getCapability"]
);
//Mocking getCapability because AddActionProvider uses the
// type capability of the destination object.
mockDomainObject.getCapability.andReturn({});
mockTypes = ["A", "B", "C"].map(createMockType);
mockTypes.forEach(function (type) {
mockTypeMap[type.getKey()] = type;
mockPolicyMap[type.getName()] = true;
});
mockTypeService.getType.andCallFake(function (key) {
return mockTypeMap[key];
});
mockCreationPolicy = function (type) {
return mockPolicyMap[type.getName()];
};
mockCompositionPolicy = function () {
return true;
};
mockPolicyService.allow.andReturn(true);
mockTypeService.listTypes.andReturn(mockTypes);
provider = new AddActionProvider(
mockQ,
@@ -89,16 +107,29 @@ define(
);
});
it("provides actions for timeline and activity", function () {
var actions = provider.getActions({
it("checks for creatability", function () {
provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(actions.length).toBe(2);
expect(actions[0].metadata.type).toBe('timeline');
expect(actions[1].metadata.type).toBe('activity');
// Make sure it was creation which was used to check
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation", mockTypes[0]);
});
it("checks for composability of type", function () {
provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
jasmine.any(Object),
jasmine.any(Object)
);
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type');
});
});
}

View File

@@ -175,7 +175,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockOtherType,
mockDomainObject
mockType
);
});

View File

@@ -38,20 +38,22 @@ define(
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (parentType, child) {
var parentDef = parentType.getDefinition();
// A parent without containment rules can contain anything.
if (!parentDef.contains) {
CompositionPolicy.prototype.allow = function (candidate, context) {
var type = context.getCapability('type');
var typeKey = type.getKey();
var candidateDef = candidate.getDefinition();
// A candidate without containment rules can contain anything.
if (!candidateDef.contains) {
return true;
}
// If any containment rule matches context type, the candidate
// can contain this type.
return parentDef.contains.some(function (c) {
return candidateDef.contains.some(function (c) {
// Simple containment rules are supported typeKeys.
if (typeof c === 'string') {
return c === child.getCapability('type').getKey();
return c === typeKey;
}
// More complicated rules require context to have all specified
// capabilities.
@@ -59,7 +61,7 @@ define(
c.has = [c.has];
}
return c.has.every(function (capability) {
return child.hasCapability(capability);
return context.hasCapability(capability);
});
});
};

View File

@@ -79,7 +79,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockTypes[0],
mockDomainObjects[1]
mockTypes[1]
);
});

View File

@@ -24,94 +24,60 @@ define(
["../src/CompositionPolicy"],
function (CompositionPolicy) {
describe("Composition policy", function () {
var typeA,
typeB,
typeC,
mockChildObject,
var mockInjector,
mockTypeService,
mockCapabilityService,
mockTypes,
policy;
beforeEach(function () {
typeA = jasmine.createSpyObj(
'type A-- the particular kind',
['getKey', 'getDefinition']
mockInjector = jasmine.createSpyObj('$injector', ['get']);
mockTypeService = jasmine.createSpyObj(
'typeService',
['listTypes']
);
typeA.getKey.andReturn('a');
typeA.getDefinition.andReturn({
contains: ['a']
mockCapabilityService = jasmine.createSpyObj(
'capabilityService',
['getCapabilities']
);
// Both types can only contain b, let's say
mockTypes = ['a', 'b'].map(function (type) {
var mockType = jasmine.createSpyObj(
'type-' + type,
['getKey', 'getDefinition', 'getInitialModel']
);
mockType.getKey.andReturn(type);
mockType.getDefinition.andReturn({
contains: ['b']
});
mockType.getInitialModel.andReturn({});
return mockType;
});
typeB = jasmine.createSpyObj(
'type B-- anything goes',
['getKey', 'getDefinition']
);
typeB.getKey.andReturn('b');
typeB.getDefinition.andReturn({
contains: ['a', 'b']
mockInjector.get.andCallFake(function (name) {
return {
typeService: mockTypeService,
capabilityService: mockCapabilityService
}[name];
});
typeC = jasmine.createSpyObj(
'type C-- distinguishing and interested in telemetry',
['getKey', 'getDefinition']
);
typeC.getKey.andReturn('c');
typeC.getDefinition.andReturn({
contains: [{has: 'telemetry'}]
});
mockTypeService.listTypes.andReturn(mockTypes);
mockCapabilityService.getCapabilities.andReturn({});
mockChildObject = jasmine.createSpyObj(
'childObject',
['getCapability', 'hasCapability']
);
policy = new CompositionPolicy();
policy = new CompositionPolicy(mockInjector);
});
describe('enforces simple containment rules', function () {
it('allows when type matches', function () {
mockChildObject.getCapability.andReturn(typeA);
expect(policy.allow(typeA, mockChildObject))
.toBeTruthy();
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
});
it('disallows when type doesn\'t match', function () {
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
mockChildObject.getCapability.andReturn(typeC);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
});
});
describe('enforces capability-based containment rules', function () {
it('allows when object has capability', function () {
mockChildObject.hasCapability.andReturn(true);
expect(policy.allow(typeC, mockChildObject))
.toBeTruthy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
it('skips when object doesn\'t have capability', function () {
mockChildObject.hasCapability.andReturn(false);
expect(policy.allow(typeC, mockChildObject))
.toBeFalsy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
// Test basic composition policy here; test more closely at
// the unit level in ContainmentTable for 'has' support, et al
it("enforces containment rules defined by types", function () {
expect(policy.allow(mockTypes[0], mockTypes[1]))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[1]))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[0]))
.toBeFalsy();
expect(policy.allow(mockTypes[0], mockTypes[0]))
.toBeFalsy();
});
});

View File

@@ -44,16 +44,16 @@ define(
var model = parentObject && parentObject.getModel(),
composition = (model || {}).composition || [];
if (composition.indexOf(id) === -1) {
$log.warn([
"Attempted to contextualize",
id,
"in",
parentObject && parentObject.getId(),
"but that object does not contain",
id,
"in its composition.",
"Unexpected behavior may follow."
].join(" "));
// $log.warn([
// "Attempted to contextualize",
// id,
// "in",
// parentObject && parentObject.getId(),
// "but that object does not contain",
// id,
// "in its composition.",
// "Unexpected behavior may follow."
// ].join(" "));
}
}

View File

@@ -104,7 +104,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object
object.capabilities.type
);
});

View File

@@ -114,7 +114,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object
object.capabilities.type
);
});

View File

@@ -124,7 +124,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object
object.capabilities.type
);
});

View File

@@ -34,14 +34,13 @@ define(
function LayoutCompositionPolicy() {
}
LayoutCompositionPolicy.prototype.allow = function (parentType, child) {
if (parentType.instanceOf('layout') &&
child.getCapability('type').instanceOf('folder')) {
return false;
}
return true;
LayoutCompositionPolicy.prototype.allow = function (candidate, context) {
var isFolderInLayout =
candidate &&
context &&
candidate.instanceOf('layout') &&
context.getCapability('type').instanceOf('folder');
return !isFolderInLayout;
};
return LayoutCompositionPolicy;

View File

@@ -24,25 +24,18 @@ define(
["../src/LayoutCompositionPolicy"],
function (LayoutCompositionPolicy) {
describe("Layout's composition policy", function () {
var mockChild,
mockCandidate,
var mockCandidate,
mockContext,
candidateType,
contextType,
policy;
beforeEach(function () {
mockChild = jasmine.createSpyObj(
'childObject',
['getCapability']
);
mockCandidate =
jasmine.createSpyObj('candidateType', ['instanceOf']);
mockContext =
jasmine.createSpyObj('contextType', ['instanceOf']);
mockChild.getCapability.andReturn(mockContext);
mockCandidate.instanceOf.andCallFake(function (t) {
return t === candidateType;
});
@@ -56,19 +49,19 @@ define(
it("disallows folders in layouts", function () {
candidateType = 'layout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockChild)).toBe(false);
expect(policy.allow(mockCandidate, mockContext)).toBe(false);
});
it("does not disallow folders elsewhere", function () {
candidateType = 'nonlayout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
});
it("allows things other than folders in layouts", function () {
candidateType = 'layout';
contextType = 'nonfolder';
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
});
});

View File

@@ -60,7 +60,7 @@
</tr>
<tr ng-repeat-end
ng-style="{ top: visibleRow.offsetY + 'px' }"
ng-click="table.onRowClick($event, visibleRow.rowIndex)">
ng-click="table.onRowClick($event, visibleRow.rowIndex) ">
<td ng-repeat="header in displayHeaders"
ng-style="{
width: columnWidths[$index] + 'px',

View File

@@ -66,8 +66,7 @@ define(
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getModel",
"getId",
"useCapability",
"hasCapability"
"useCapability"
]);
mockDomainObject.getModel.andReturn({});
mockDomainObject.getId.andReturn("mockId");

View File

@@ -166,13 +166,13 @@ define(
// Aggregators need other services to aggregate, otherwise they
// do nothing.
if (!latest[service]) {
return warn(
aggregator,
"aggregator",
"No services to aggregate for"
);
}
// if (!latest[service]) {
// return warn(
// aggregator,
// "aggregator",
// "No services to aggregate for"
// );
// }
dependencies = dependencies.concat([providerSetName]);
latest[service] = name;

View File

@@ -32,7 +32,6 @@ define(
mockPersistence,
mockDomainObject,
testModel,
testId,
decorator;
beforeEach(function () {
@@ -42,7 +41,6 @@ define(
['getCapabilities']
);
testModel = { someKey: "some value" };
testId = 'someId';
mockPersistence = jasmine.createSpyObj(
'persistence',
['persist', 'refresh']
@@ -69,9 +67,9 @@ define(
// QueuingPersistenceCapability itself, which has its own tests.
it("delegates to its wrapped service", function () {
decorator.getCapabilities(testModel, testId);
decorator.getCapabilities(testModel);
expect(mockCapabilityService.getCapabilities)
.toHaveBeenCalledWith(testModel, testId);
.toHaveBeenCalledWith(testModel);
});
it("wraps its persistence capability's constructor", function () {

View File

@@ -18,14 +18,14 @@
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.
-->
-->
<div class="search-result-item l-flex-row flex-elem grows"
ng-class="{selected: ngModel.selectedObject.getId() === domainObject.getId()}">
<mct-representation key="'label'"
mct-object="domainObject"
ng-model="ngModel"
ng-click="ngModel.allowSelection(domainObject) && ngModel.onSelection(domainObject)"
ng-click="ngModel.selectedObject = domainObject"
class="l-flex-row flex-elem grows">
</mct-representation>
</div>
</div>

View File

@@ -79,6 +79,7 @@ define([
"key": "telemetry",
"implementation": TelemetryCapability,
"depends": [
"openmct",
"$injector",
"$q",
"$log"

View File

@@ -24,8 +24,12 @@
* Module defining TelemetryCapability. Created by vwoeltje on 11/12/14.
*/
define(
[],
function () {
[
'../../../src/api/objects/object-utils'
],
function (
objectUtils
) {
var ZERO = function () {
return 0;
@@ -103,7 +107,7 @@ define(
* @implements {Capability}
* @constructor
*/
function TelemetryCapability($injector, $q, $log, domainObject) {
function TelemetryCapability(openmct, $injector, $q, $log, domainObject) {
// We could depend on telemetryService directly, but
// there isn't a platform implementation of this.
this.initializeTelemetryService = function () {
@@ -118,7 +122,7 @@ define(
}
};
this.openmct = openmct;
this.$q = $q;
this.$log = $log;
this.domainObject = domainObject;
@@ -160,6 +164,20 @@ define(
};
function asSeries(telemetry, defaultDomain, defaultRange) {
return {
getRangeValue: function (index, range) {
return telemetry[index][range || defaultRange];
},
getDomainValue: function (index, domain) {
return telemetry[index][domain || defaultDomain];
},
getPointCount: function () {
return telemetry.length;
}
}
}
/**
* Request telemetry data for this specific domain object.
* @param {TelemetryRequest} [request] parameters for this
@@ -169,11 +187,21 @@ define(
*/
TelemetryCapability.prototype.requestData = function requestTelemetry(request) {
// Bring in any defaults from the object model
var fullRequest = this.buildRequest(request || {}),
source = fullRequest.source,
key = fullRequest.key,
telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var fullRequest = this.buildRequest(request || {});
var source = fullRequest.source;
var key = fullRequest.key;
var telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = (metadata.valuesForHints(['domain'])[0] || {}).key;
var defaultRange = (metadata.valuesForHints(['range'])[0] || {}).key;
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject)
=== telemetryAPI.legacyProvider;
// Pull out the relevant field from the larger,
// structured response.
@@ -187,11 +215,18 @@ define(
return telemetryService.requestTelemetry([fullRequest]);
}
// If a telemetryService is not available,
// getTelemetryService() should reject, and this should
// bubble through subsequent then calls.
return telemetryService &&
requestTelemetryFromService().then(getRelevantResponse);
// TODO: Adapt request / options?
if (isLegacyProvider) {
// If a telemetryService is not available,
// getTelemetryService() should reject, and this should
// bubble through subsequent then calls.
return telemetryService &&
requestTelemetryFromService().then(getRelevantResponse);
} else {
return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
return asSeries(telemetry, defaultDomain, defaultRange);
});
}
};
/**
@@ -217,12 +252,26 @@ define(
* subscription request
*/
TelemetryCapability.prototype.subscribe = function subscribe(callback, request) {
var fullRequest = this.buildRequest(request || {}),
telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var fullRequest = this.buildRequest(request || {});
var telemetryService = this.telemetryService ||
this.initializeTelemetryService(); // Lazy initialization
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
var telemetryAPI = this.openmct.telemetry;
var metadata = telemetryAPI.getMetadata(domainObject);
var defaultDomain = (metadata.valuesForHints(['domain'])[0] || {}).key;
var defaultRange = (metadata.valuesForHints(['range'])[0] || {}).key;
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject)
=== telemetryAPI.legacyProvider;
function update(telemetry) {
callback(asSeries([telemetry], defaultDomain, defaultRange));
}
// Unpack the relevant telemetry series
function update(telemetries) {
function updateLegacy(telemetries) {
var source = fullRequest.source,
key = fullRequest.key,
result = ((telemetries || {})[source] || {})[key];
@@ -231,8 +280,13 @@ define(
}
}
return telemetryService &&
telemetryService.subscribe(update, [fullRequest]);
// Avoid a loop here...
if (isLegacyProvider) {
return telemetryService &&
telemetryService.subscribe(updateLegacy, [fullRequest]);
} else {
return telemetryAPI.subscribe(domainObject, update, fullRequest);
}
};
/**

View File

@@ -26,14 +26,14 @@ define([], function () {
}
AdapterCompositionPolicy.prototype.allow = function (
parentType,
child
containerType,
childObject
) {
var container = parentType.getInitialModel();
var containerObject = containerType.getInitialModel();
return this.openmct.composition.checkPolicy(
container,
child
containerObject,
childObject
);
};

View File

@@ -146,7 +146,7 @@ define([
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier));
var limitEvaluator = oldObject.getCapability("limit");
if (!limitEvaluator) {
return;
}
@@ -159,7 +159,8 @@ define([
};
return function (openmct, instantiate) {
// Legacy provider should always be the fallback.
// Push onto the start of the default providers array so that it's
// always the last resort
var provider = new LegacyTelemetryProvider(openmct, instantiate);
openmct.telemetry.legacyProvider = provider;
openmct.telemetry.requestProviders.push(provider);

View File

@@ -176,9 +176,8 @@ define([
* @private
*/
TelemetryAPI.prototype.findSubscriptionProvider = function (domainObject) {
var args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsSubscribe.apply(provider, args);
return provider.supportsSubscribe(domainObject);
}
return this.subscriptionProviders.filter(supportsDomainObject)[0];
@@ -188,9 +187,8 @@ define([
* @private
*/
TelemetryAPI.prototype.findRequestProvider = function (domainObject, options) {
var args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsRequest.apply(provider, args);
return provider.supportsRequest(domainObject);
}
return this.requestProviders.filter(supportsDomainObject)[0];
@@ -332,7 +330,8 @@ define([
*/
TelemetryAPI.prototype.limitEvaluator = function () {
return this.legacyProvider.limitEvaluator.apply(this.legacyProvider, arguments);
};
}
return TelemetryAPI;
});

View File

@@ -105,7 +105,6 @@ define([
this.valueMetadatas = this.valueMetadatas.map(applyReasonableDefaults);
}
/**
* Get value metadata for a single key.
*/

View File

@@ -51,7 +51,7 @@ define(['./Type'], function (Type) {
* @method addType
* @memberof module:openmct.TypeRegistry#
*/
TypeRegistry.prototype.addType = function (typeKey, typeDef) {
TypeRegistry.prototype.register = function (typeKey, typeDef) {
this.types[typeKey] = new Type(typeDef);
};

View File

@@ -33,7 +33,6 @@ define([
'../example/export/bundle',
'../example/extensions/bundle',
'../example/forms/bundle',
'../example/generator/bundle',
'../example/identity/bundle',
'../example/imagery/bundle',
'../example/mobile/bundle',

View File

@@ -41,6 +41,10 @@ define([
return domainObject.type === 'generator';
};
GeneratorProvider.prototype.supportsRequest =
GeneratorProvider.prototype.supportsSubscribe =
GeneratorProvider.prototype.canProvideTelemetry;
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',

View File

@@ -0,0 +1,171 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, 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.
*****************************************************************************/
/*global define*/
define([
"./GeneratorProvider",
"./SinewaveLimitCapability",
"./SinewaveDeltaFormat"
], function (
GeneratorProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat
) {
var legacyExtensions = {
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta"
}
],
"priority": -1
}
]
}
return function(openmct){
//Register legacy extensions for things not yet supported by the new API
Object.keys(legacyExtensions).forEach(function (type){
var extensionsOfType = legacyExtensions[type];
extensionsOfType.forEach(function (extension) {
openmct.legacyExtension(type, extension)
})
});
openmct.types.register("generator", {
label: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
cssClass: "icon-telemetry",
creatable: true,
form: [
{
name: "Period",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "period",
required: true,
property: [
"telemetry",
"period"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Amplitude",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "amplitude",
required: true,
property: [
"telemetry",
"amplitude"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Offset",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "offset",
required: true,
property: [
"telemetry",
"offset"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Data Rate (hz)",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "dataRateInHz",
required: true,
property: [
"telemetry",
"dataRateInHz"
],
pattern: "^\\d*(\\.\\d*)?$"
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
domains: [
{
key: "utc",
name: "Time",
format: "utc"
},
{
key: "yesterday",
name: "Yesterday",
format: "utc"
},
{
key: "delta",
name: "Delta",
format: "example.delta"
}
],
ranges: [
{
key: "sin",
name: "Sine"
},
{
key: "cos",
name: "Cosine"
}
]
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
};
});

View File

@@ -22,10 +22,12 @@
define([
'lodash',
'../../platform/features/conductor/utcTimeSystem/src/UTCTimeSystem'
'../../platform/features/conductor/utcTimeSystem/src/UTCTimeSystem',
'./generator/plugin'
], function (
_,
UTCTimeSystem
UTCTimeSystem,
GeneratorPlugin
) {
var bundleMap = {
couchDB: 'platform/persistence/couch',
@@ -136,5 +138,9 @@ define([
};
};
plugins.Generator = function () {
return GeneratorPlugin;
};
return plugins;
});