Merge branch 'open-master' into open1317limits2

WTD-1405

Conflicts:
	platform/features/layout/res/templates/elements/telemetry.html
This commit is contained in:
Victor Woeltjen
2015-06-29 17:25:17 -07:00
114 changed files with 4376 additions and 917 deletions

View File

@@ -0,0 +1,37 @@
{
"name": "Event Messages",
"description": "List of time-ordered event messages",
"extensions": {
"views": [
{
"key": "messages",
"name": "Messages",
"glyph": "5",
"description": "Scrolling list of messages.",
"templateUrl": "templates/messages.html",
"needs": [ "telemetry" ],
"delegation": true
}
],
"controllers": [
{
"key": "EventListController",
"implementation": "EventListController.js",
"depends": [ "$scope", "telemetryFormatter" ]
}
],
"directives": [
{
"key": "mctDataTable",
"implementation": "directives/MCTDataTable.js",
"depends": [ "$window" ]
}
],
"policies": [
{
"category": "view",
"implementation": "policies/MessagesViewPolicy.js"
}
]
}
}

View File

@@ -0,0 +1,37 @@
<!--
Open MCT Web, Copyright (c) 2014-2015, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT Web 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 Web 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.
-->
<table class="tabular">
<thead>
<tr>
<th ng-repeat="header in headers">
{{header}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="cell in row">
{{cell}}
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,29 @@
<!--
Open MCT Web, Copyright (c) 2014-2015, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT Web 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 Web 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.
-->
<div class="w1" ng-controller="TelemetryController as telemetry">
<div class="w2"
ng-controller="EventListController">
<mct-data-table headers="headers" rows="rows" ascending-scroll="true"></mct-data-table>
</div>
</div>

View File

@@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,moment*/
/**
* Module defining DomainColumn. Created by vwoeltje on 11/18/14.
*/
define(
[],
function () {
"use strict";
/**
* A column which will report telemetry domain values
* (typically, timestamps.) Used by the ScrollingListController.
*
* @constructor
* @param domainMetadata an object with the machine- and human-
* readable names for this domain (in `key` and `name`
* fields, respectively.)
* @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable.
*/
function DomainColumn(domainMetadata, telemetryFormatter) {
return {
/**
* Get the title to display in this column's header.
* @returns {string} the title to display
*/
getTitle: function () {
return domainMetadata.name;
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
*/
getValue: function (domainObject, data, index) {
return telemetryFormatter.formatDomainValue(
data.getDomainValue(index, domainMetadata.key)
);
}
};
}
return DomainColumn;
}
);

View File

@@ -0,0 +1,133 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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*/
/**
* Module defining EventListController.
* Created by chacskaylo on 06/18/2015.
* Modified by shale on 06/23/2015.
*/
define(
["./DomainColumn", "./RangeColumn", "./EventListPopulator"],
function (DomainColumn, RangeColumn, EventListPopulator) {
"use strict";
var ROW_COUNT = 100;
/**
* The EventListController is responsible for populating
* the contents of the event list view.
* @constructor
*/
function EventListController($scope, formatter) {
var populator;
// Get a set of populated, ready-to-display rows for the
// latest data values.
function getRows(telemetry) {
var datas = telemetry.getResponse(),
objects = telemetry.getTelemetryObjects();
return populator.getRows(datas, objects, ROW_COUNT);
}
// Update the contents
function updateRows() {
var telemetry = $scope.telemetry;
$scope.rows = telemetry ? getRows(telemetry) : [];
}
// Set up columns based on telemetry metadata. This will
// include one column for each domain and range type, as
// well as a column for the domain object name.
function setupColumns(telemetry) {
var domainKeys = {},
rangeKeys = {},
columns = [],
metadata;
// Add a domain to the set of columns, if a domain
// with the same key has not yet been inclued.
function addDomain(domain) {
var key = domain.key;
if (key && !domainKeys[key]) {
domainKeys[key] = true;
columns.push(new DomainColumn(domain, formatter));
}
}
// Add a range col to the set of columns, if a range
// with the same key has not yet been included.
function addRange(range) {
var key = range.key;
if (key && !rangeKeys[key]) {
rangeKeys[key] = true;
columns.push(new RangeColumn(range, formatter));
}
}
// We cannot proceed if the telemetry controller
// is not available; clear all rows/columns.
if (!telemetry) {
columns = [];
$scope.rows = [];
$scope.headers = [];
return;
}
// Add domain, range, event msg columns
metadata = telemetry.getMetadata();
(metadata || []).forEach(function (metadata) {
(metadata.domains || []).forEach(addDomain);
});
(metadata || []).forEach(function (metadata) {
(metadata.ranges || []).forEach(addRange);
});
// Add default domain and range columns if none
// were described in metadata.
if (Object.keys(domainKeys).length < 1) {
columns.push(new DomainColumn({name: "Time"}, formatter));
}
if (Object.keys(rangeKeys).length < 1) {
columns.push(new RangeColumn({name: "Message"}, formatter));
}
// We have all columns now; use them to initializer
// the populator, which will use them to generate
// actual rows and headers.
populator = new EventListPopulator(columns);
// Initialize headers
$scope.headers = populator.getHeaders();
// Fill in the contents of the rows.
updateRows();
}
$scope.$on("telemetryUpdate", updateRows);
$scope.$watch("telemetry", setupColumns);
}
return EventListController;
}
);

View File

@@ -0,0 +1,161 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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(
[],
function () {
"use strict";
/**
* The EventListPopulator is responsible for filling in the
* values which should appear within columns of a event list
* view, based on received telemetry data.
* @constructor
* @param {Column[]} columns the columns to be populated
*/
function EventListPopulator(columns) {
/**
* Look up the most recent values from a set of data objects.
* Returns an array of objects in the order in which data
* should be displayed; each element is an object with
* two properties:
*
* * objectIndex: The index of the domain object associated
* with the data point to be displayed in that
* row.
* * pointIndex: The index of the data point itself, within
* its data set.
*
* @param {Array<Telemetry>} datas an array of the most recent
* data objects; expected to be in the same order
* as the domain objects provided at constructor
* @param {number} count the number of rows to provide
*/
function getLatestDataValues(datas, count) {
var latest = [],
candidate,
candidateTime,
used = datas.map(function () { return 0; });
// This algorithm is O(nk) for n rows and k telemetry elements;
// one O(k) linear search for a max is made for each of n rows.
// This could be done in O(n lg k + k lg k), using a priority
// queue (where priority is max-finding) containing k initial
// values. For n rows, pop the max from the queue and replenish
// the queue with a value from the data at the same
// objectIndex, if available.
// But k is small, so this might not give an observable
// improvement in performance.
// Find the most recent unused data point (this will be used
// in a loop to find and the N most recent data points)
function findCandidate(data, i) {
var nextTime,
pointCount = data.getPointCount(),
pointIndex = pointCount - used[i] - 1;
if (data && pointIndex >= 0) {
nextTime = data.getDomainValue(pointIndex);
if (nextTime > candidateTime) {
candidateTime = nextTime;
candidate = {
objectIndex: i,
pointIndex: pointIndex
};
}
}
}
// Assemble a list of the most recent data points
while (latest.length < count) {
// Reset variables pre-search
candidateTime = Number.NEGATIVE_INFINITY;
candidate = undefined;
// Linear search for most recent
datas.forEach(findCandidate);
if (candidate) {
// Record this data point - it is the most recent
latest.push(candidate);
// Track the data points used so we can look farther back
// in the data set on the next iteration
used[candidate.objectIndex] = used[candidate.objectIndex] + 1;
} else {
// Ran out of candidates; not enough data points
// available to fill all rows.
break;
}
}
return latest;
}
return {
/**
* Get the text which should appear in headers for the
* provided columns.
* @returns {string[]} column headers
*/
getHeaders: function () {
return columns.map(function (column) {
return column.getTitle();
});
},
/**
* Get the contents of rows for the event list view.
* @param {TelemetrySeries[]} datas the data sets
* @param {DomainObject[]} objects the domain objects which
* provided the data sets; these should match
* index-to-index with the `datas` argument
* @param {number} count the number of rows to populate
* @returns {string[][]} an array of rows, each of which
* is an array of values which should appear
* in that row
*/
getRows: function (datas, objects, count) {
var values = getLatestDataValues(datas, count);
// Each value will become a row, which will contain
// some value in each column (rendering by the
// column object itself)
// Additionally, we want to display the rows in reverse
// order. (i.e. from the top to the bottom of the page)
return values.map(function (value) {
return columns.map(function (column) {
return column.getValue(
objects[value.objectIndex],
datas[value.objectIndex],
value.pointIndex
);
});
}).reverse();
}
};
}
return EventListPopulator;
}
);

View File

@@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,Promise*/
/**
* Module defining DomainColumn. Created by vwoeltje on 11/18/14.
*/
define(
[],
function () {
"use strict";
/**
* A column which will report telemetry range values
* (typically, measurements.) Used by the ScrollingListController.
*
* @constructor
* @param rangeMetadata an object with the machine- and human-
* readable names for this range (in `key` and `name`
* fields, respectively.)
* @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable.
*/
function RangeColumn(rangeMetadata, telemetryFormatter) {
return {
/**
* Get the title to display in this column's header.
* @returns {string} the title to display
*/
getTitle: function () {
return rangeMetadata.name;
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
*/
getValue: function (domainObject, data, index) {
return telemetryFormatter.formatRangeValue(
data.getRangeValue(index, rangeMetadata.key)
);
}
};
}
return RangeColumn;
}
);

View File

@@ -0,0 +1,74 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,Promise*/
/**
* Module defining MCTDataTable. Created by shale on 06/22/2015.
*/
define(
[],
function () {
"use strict";
function MCTDataTable($window) {
return {
restrict: "E",
templateUrl: "platform/features/events/res/templates/mct-data-table.html",
scope: {
headers: "=",
rows: "=",
ascendingScroll: "="
},
link: function ($scope, $element) {
var currentHeight,
previousHeight,
scrollParent;
// If the scroll is set to ascending, we want to
// check when elements are added to the table, and move the scroll
// bar accordingly.
// (When viewing at the bottom of the page, the scroll bar will
// stay at the bottom despite additions to the table)
if ($scope.ascendingScroll) {
$scope.$watch("rows", function () {
// Wait until the page as been repainted (otherwise the
// height will always be zero)
$window.requestAnimationFrame(function () {
previousHeight = currentHeight;
// The height of the table body
currentHeight = $element[0].firstElementChild.firstElementChild.nextElementSibling.clientHeight;
// One of the parents is a div that has vscroll
scrollParent = $element[0].parentElement.parentElement.parentElement.parentElement.parentElement;
// Move the scrollbar down the amount that the height has changed
scrollParent.scrollTop = scrollParent.scrollTop + (currentHeight - previousHeight);
});
});
}
}
};
}
return MCTDataTable;
}
);

View File

@@ -0,0 +1,74 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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*/
/**
* Module defining MessagesViewPolicy. Created by shale on 06/24/2015.
*/
define(
[],
function () {
"use strict";
/**
* Policy controlling when the Messages view should be avaliable.
* @constructor
*/
function MessagesViewPolicy() {
function hasStringTelemetry(domainObject) {
var telemetry = domainObject &&
domainObject.getCapability('telemetry'),
metadata = telemetry ? telemetry.getMetadata() : {},
ranges = metadata.ranges || [];
return ranges.some(function (range) {
return range.format === 'string';
});
}
return {
/**
* Check whether or not a given action is allowed by this
* policy.
* @param {Action} action the action
* @param domainObject the domain object which will be viewed
* @returns {boolean} true if not disallowed
*/
allow: function (view, domainObject) {
// This policy only applies for the Messages view
if (view.key === 'messages') {
// The Messages view is allowed only if the domain
// object has string telemetry
if (!hasStringTelemetry(domainObject)) {
return false;
}
}
// Like all policies, allow by default.
return true;
}
};
}
return MessagesViewPolicy;
}
);

View File

@@ -0,0 +1,84 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* EventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/23/2015.
*/
define(
["../src/DomainColumn"],
function (DomainColumn) {
"use strict";
var TEST_DOMAIN_VALUE = "some formatted domain value";
describe("An event list domain column", function () {
var mockDataSet,
testMetadata,
mockFormatter,
column;
beforeEach(function () {
mockDataSet = jasmine.createSpyObj(
"data",
[ "getDomainValue" ]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
[ "formatDomainValue", "formatRangeValue" ]
);
testMetadata = {
key: "testKey",
name: "Test Name"
};
mockFormatter.formatDomainValue.andReturn(TEST_DOMAIN_VALUE);
column = new DomainColumn(testMetadata, mockFormatter);
});
it("reports a column header from domain metadata", function () {
expect(column.getTitle()).toEqual("Test Name");
});
it("looks up data from a data set", function () {
column.getValue(undefined, mockDataSet, 42);
expect(mockDataSet.getDomainValue)
.toHaveBeenCalledWith(42, "testKey");
});
it("formats domain values as time", function () {
mockDataSet.getDomainValue.andReturn(402513731000);
// Should have just given the value the formatter gave
expect(column.getValue(undefined, mockDataSet, 42))
.toEqual(TEST_DOMAIN_VALUE);
// Make sure that service interactions were as expected
expect(mockFormatter.formatDomainValue)
.toHaveBeenCalledWith(402513731000);
expect(mockFormatter.formatRangeValue)
.not.toHaveBeenCalled();
});
});
}
);

View File

@@ -0,0 +1,110 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* EventSpec. Created by shale on 06/24/2015.
*/
define(
["../src/EventListController"],
function (EventListController) {
"use strict";
describe("The event list controller", function () {
var mockScope,
mockTelemetry,
testMetadata,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
[ "$on", "$watch" ]
);
mockTelemetry = jasmine.createSpyObj(
"telemetryController",
[ "getResponse", "getMetadata", "getTelemetryObjects" ]
);
testMetadata = [
{
domains: [
{ key: "d0", name: "D0" },
{ key: "d1", name: "D1" }
],
ranges: [
{ key: "r0", name: "R0" },
{ key: "r1", name: "R1" }
]
},
{
domains: [
{ key: "d0", name: "D0" },
{ key: "d2", name: "D2" }
],
ranges: [
{ key: "r0", name: "R0" }
]
}
];
mockTelemetry.getMetadata.andReturn(testMetadata);
mockTelemetry.getResponse.andReturn([]);
mockTelemetry.getTelemetryObjects.andReturn([]);
mockScope.telemetry = mockTelemetry;
controller = new EventListController(mockScope);
});
it("listens for telemetry data updates", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
"telemetryUpdate",
jasmine.any(Function)
);
});
it("watches for telemetry controller changes", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"telemetry",
jasmine.any(Function)
);
});
it("provides a column for each unique domain and range", function () {
// Should have five columns based on metadata above,
// (d0, d1, d2, r0, r1)
mockScope.$watch.mostRecentCall.args[1](mockTelemetry);
expect(mockScope.headers).toEqual(["D0", "D1", "D2", "R0", "R1"]);
});
it("does not throw if telemetry controller is undefined", function () {
// Just a general robustness check
mockScope.telemetry = undefined;
expect(mockScope.$watch.mostRecentCall.args[1])
.not.toThrow();
});
it("provides default columns if domain/range metadata is unavailable", function () {
mockTelemetry.getMetadata.andReturn([]);
mockScope.$watch.mostRecentCall.args[1](mockTelemetry);
expect(mockScope.headers).toEqual(["Time", "Message"]);
});
});
}
);

View File

@@ -0,0 +1,103 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* EventSpec. Created by shale on 06/24/2015.
*/
define(
["../src/EventListPopulator"],
function (EventListPopulator) {
"use strict";
describe("The event list populator", function () {
var mockColumns,
mockDatas,
mockDomainObjects,
populator;
function makeMockColumn(name, index) {
var mockColumn = jasmine.createSpyObj(
"column" + index,
[ "getTitle", "getValue" ]
);
mockColumn.getTitle.andReturn(name);
mockColumn.getValue.andCallFake(function (obj, data, i) {
return data.getDomainValue(i);
});
return mockColumn;
}
function makeMockData(bias, index) {
var mockData = jasmine.createSpyObj(
"data" + index,
[ "getDomainValue", "getPointCount" ]
);
mockData.getPointCount.andReturn(1000);
mockData.getDomainValue.andCallFake(function (i) {
return i + bias;
});
return mockData;
}
function makeMockDomainObject(name, index) {
var mockDomainObject = jasmine.createSpyObj(
"domainObject" + index,
[ "getId", "getModel" ]
);
return mockDomainObject;
}
beforeEach(function () {
mockColumns = ["A", "B", "C", "D"].map(makeMockColumn);
mockDatas = [ 10, 0, 3 ].map(makeMockData);
mockDomainObjects = ["A", "B", "C"].map(makeMockDomainObject);
populator = new EventListPopulator(mockColumns);
});
it("returns column headers", function () {
expect(populator.getHeaders()).toEqual(["A", "B", "C", "D"]);
});
it("provides rows on request, with all columns in each row", function () {
var rows = populator.getRows(mockDatas, mockDomainObjects, 84);
expect(rows.length).toEqual(84);
rows.forEach(function (row) {
expect(row.length).toEqual(4); // number of columns
});
});
it("returns rows in most-recent-last order", function () {
var rows = populator.getRows(mockDatas, mockDomainObjects, 84),
previous = Number.NEGATIVE_INFINITY;
// Should always be most-recent-last
rows.forEach(function (row) {
expect(row[0]).not.toBeLessThan(previous);
previous = row[0];
});
});
});
}
);

View File

@@ -0,0 +1,81 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* EventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/23/2015.
*/
define(
["../src/RangeColumn"],
function (RangeColumn) {
"use strict";
var TEST_RANGE_VALUE = "some formatted range value";
describe("An event list range column", function () {
var mockDataSet,
testMetadata,
mockFormatter,
column;
beforeEach(function () {
mockDataSet = jasmine.createSpyObj(
"data",
[ "getRangeValue" ]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
[ "formatDomainValue", "formatRangeValue" ]
);
testMetadata = {
key: "testKey",
name: "Test Name"
};
mockFormatter.formatRangeValue.andReturn(TEST_RANGE_VALUE);
column = new RangeColumn(testMetadata, mockFormatter);
});
it("reports a column header from range metadata", function () {
expect(column.getTitle()).toEqual("Test Name");
});
it("looks up data from a data set", function () {
column.getValue(undefined, mockDataSet, 42);
expect(mockDataSet.getRangeValue)
.toHaveBeenCalledWith(42, "testKey");
});
it("formats range values as time", function () {
mockDataSet.getRangeValue.andReturn(123.45678);
expect(column.getValue(undefined, mockDataSet, 42))
.toEqual(TEST_RANGE_VALUE);
// Make sure that service interactions were as expected
expect(mockFormatter.formatRangeValue)
.toHaveBeenCalledWith(123.45678);
expect(mockFormatter.formatDomainValue)
.not.toHaveBeenCalled();
});
});
}
);

View File

@@ -0,0 +1,81 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,jasmine*/
/**
* EventSpec. Created by shale on 06/24/2015.
*/
define(
["../../src/policies/MessagesViewPolicy"],
function (MessagesViewPolicy) {
"use strict";
describe("The messages view policy", function () {
var mockDomainObject,
mockTelemetry,
telemetryType,
testType,
testView,
testMetadata,
policy;
beforeEach(function () {
testView = { key: "string" };
testMetadata = {};
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getModel', 'getCapability']
);
mockTelemetry = jasmine.createSpyObj(
'telemetry',
['getMetadata']
);
mockDomainObject.getModel.andCallFake(function (c) {
return {type: testType};
});
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'telemetry' ? mockTelemetry : undefined;
});
mockTelemetry.getMetadata.andReturn(testMetadata);
policy = new MessagesViewPolicy();
});
it("disallows the message view for objects without string telemetry", function () {
testMetadata.ranges = [ { format: 'notString' } ];
expect(policy.allow({ key: 'messages' }, mockDomainObject)).toBeFalsy();
});
it("allows the message view for objects with string telemetry", function () {
testMetadata.ranges = [ { format: 'string' } ];
expect(policy.allow({ key: 'messages' }, mockDomainObject)).toBeTruthy();
});
it("returns true when the current view is not the Messages view", function () {
expect(policy.allow({ key: 'notMessages' }, mockDomainObject)).toBeTruthy();
});
});
}
);

View File

@@ -0,0 +1,7 @@
[
"DomainColumn",
"EventListController",
"EventListPopulator",
"policies/MessagesViewPolicy",
"RangeColumn"
]

View File

@@ -194,6 +194,12 @@
"templateUrl": "templates/elements/image.html"
}
],
"policies": [
{
"category": "composition",
"implementation": "LayoutCompositionPolicy.js"
}
],
"types": [
{
"key": "layout",
@@ -267,4 +273,4 @@
}
]
}
}
}

View File

@@ -26,7 +26,7 @@
<span
class="l-elem l-value l-obj-val-format"
data-value="{{ngModel.value}}"
ng-class="ngModel.cssClass + ' ' + (ngModel.element.titled ? 'telem-only' : '')"
ng-class="ngModel.cssClass"
>
{{ngModel.value}}
</span>

View File

@@ -20,14 +20,13 @@
at runtime from the About dialog for additional information.
-->
<div class="t-fixed-position l-fixed-position"
ng-controller="FixedController as controller"
mct-resize="controller.setBounds(bounds)">
ng-controller="FixedController as controller">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
<div class="l-grid l-grid-x"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
<div class="l-grid l-grid-y"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
@@ -60,4 +59,4 @@
</div>
</span>
</div>
</div>

View File

@@ -27,8 +27,7 @@ define(
"use strict";
var DEFAULT_DIMENSIONS = [ 2, 1 ],
DEFAULT_GRID_SIZE = [64, 16],
DEFAULT_GRID_EXTENT = [4, 4];
DEFAULT_GRID_SIZE = [64, 16];
/**
* The FixedController is responsible for supporting the
@@ -40,10 +39,8 @@ define(
*/
function FixedController($scope, $q, dialogService, telemetrySubscriber, telemetryFormatter) {
var gridSize = DEFAULT_GRID_SIZE,
gridExtent = DEFAULT_GRID_EXTENT,
dragging,
subscription,
cellStyles = [],
elementProxies = [],
names = {}, // Cache names by ID
values = {}, // Cache values by ID
@@ -52,29 +49,6 @@ define(
moveHandle,
selection;
// Refresh cell styles (e.g. because grid extent changed)
function refreshCellStyles() {
var x, y;
// Clear previous styles
cellStyles = [];
// Update grid size from model
gridSize = ($scope.model || {}).layoutGrid || gridSize;
for (x = 0; x < gridExtent[0]; x += 1) {
for (y = 0; y < gridExtent[1]; y += 1) {
// Position blocks; subtract out border size from w/h
cellStyles.push({
left: x * gridSize[0] + 'px',
top: y * gridSize[1] + 'px',
width: gridSize[0] - 1 + 'px',
height: gridSize[1] - 1 + 'px'
});
}
}
}
// Convert from element x/y/width/height to an
// apropriate ng-style argument, to position elements.
function convertPosition(elementProxy) {
@@ -143,6 +117,16 @@ define(
}
}
// Update element positions when grid size changes
function updateElementPositions(layoutGrid) {
// Update grid size from model
gridSize = layoutGrid || DEFAULT_GRID_SIZE;
elementProxies.forEach(function (elementProxy) {
elementProxy.style = convertPosition(elementProxy);
});
}
// Update telemetry values based on new data available
function updateValues() {
if (subscription) {
@@ -256,6 +240,11 @@ define(
// Position a panel after a drop event
function handleDrop(e, id, position) {
// Don't handle this event if it has already been handled
if (e.defaultPrevented) {
return;
}
e.preventDefault();
// Store the position of this element.
addElement({
type: "fixed.telemetry",
@@ -284,6 +273,9 @@ define(
// Position panes when the model field changes
$scope.$watch("model.composition", updateComposition);
// Detect changes to grid size
$scope.$watch("model.layoutGrid", updateElementPositions);
// Subscribe to telemetry when an object is available
$scope.$watch("domainObject", subscribe);
@@ -293,19 +285,7 @@ define(
// Position panes where they are dropped
$scope.$on("mctDrop", handleDrop);
// Initialize styles (position etc.) for cells
refreshCellStyles();
return {
/**
* Get styles for all background cells, as will populate the
* ng-style tag.
* @memberof FixedController#
* @returns {Array} cell styles
*/
getCellStyles: function () {
return cellStyles;
},
/**
* Get the size of the grid, in pixels. The returned array
* is in the form `[x, y]`.
@@ -314,19 +294,6 @@ define(
getGridSize: function () {
return gridSize;
},
/**
* Set the size of the viewable fixed position area.
* @memberof FixedController#
* @param bounds the width/height, as reported by mct-resize
*/
setBounds: function (bounds) {
var w = Math.ceil(bounds.width / gridSize[0]),
h = Math.ceil(bounds.height / gridSize[1]);
if (w !== gridExtent[0] || h !== gridExtent[1]) {
gridExtent = [w, h];
refreshCellStyles();
}
},
/**
* Get an array of elements in this panel; these are
* decorated proxies for both selection and display.

View File

@@ -0,0 +1,52 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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(
[],
function () {
"use strict";
/**
* Defines composition policy for Display Layout objects.
* They cannot contain folders.
*/
function LayoutCompositionPolicy() {
return {
/**
* Is the type identified by the candidate allowed to
* contain the type described by the context?
*/
allow: function (candidate, context) {
var isFolderInLayout =
candidate &&
context &&
candidate.instanceOf('layout') &&
context.instanceOf('folder');
return !isFolderInLayout;
}
};
}
return LayoutCompositionPolicy;
}
);

View File

@@ -109,6 +109,9 @@ define(
// Position a panel after a drop event
function handleDrop(e, id, position) {
if (e.defaultPrevented) {
return;
}
// Ensure that configuration field is populated
$scope.configuration = $scope.configuration || {};
// Make sure there is a "panels" field in the
@@ -129,6 +132,10 @@ define(
}
// Populate template-facing position for this id
populatePosition(id);
// Layout may contain embedded views which will
// listen for drops, so call preventDefault() so
// that they can recognize that this event is handled.
e.preventDefault();
}
// Position panes when the model field changes
@@ -220,4 +227,4 @@ define(
return LayoutController;
}
);
);

View File

@@ -34,6 +34,7 @@ define(
mockFormatter,
mockDomainObject,
mockSubscription,
mockEvent,
testGrid,
testModel,
testValues,
@@ -98,6 +99,10 @@ define(
'subscription',
[ 'unsubscribe', 'getTelemetryObjects', 'getRangeValue', 'getDatum' ]
);
mockEvent = jasmine.createSpyObj(
'event',
[ 'preventDefault' ]
);
testGrid = [ 123, 456 ];
testModel = {
@@ -137,11 +142,6 @@ define(
);
});
it("provides styles for cells", function () {
expect(controller.getCellStyles())
.toEqual(jasmine.any(Array));
});
it("subscribes when a domain object is available", function () {
mockScope.domainObject = mockDomainObject;
findWatch("domainObject")(mockDomainObject);
@@ -266,25 +266,19 @@ define(
expect(elements[2].value).toEqual("Formatted 31.42");
});
it("adds grid cells to fill boundaries", function () {
var s1 = {
width: testGrid[0] * 8,
height: testGrid[1] * 4
},
s2 = {
width: testGrid[0] * 10,
height: testGrid[1] * 6
};
it("updates elements styles when grid size changes", function () {
var originalLeft;
mockScope.domainObject = mockDomainObject;
mockScope.model = testModel;
findWatch("domainObject")(mockDomainObject);
findWatch("model.modified")(1);
findWatch("model.composition")(mockScope.model.composition);
// Set first bounds
controller.setBounds(s1);
expect(controller.getCellStyles().length).toEqual(32); // 8 * 4
// Set new bounds
controller.setBounds(s2);
expect(controller.getCellStyles().length).toEqual(60); // 10 * 6
findWatch("model.layoutGrid")([10, 10]);
originalLeft = controller.getElements()[0].style.left;
findWatch("model.layoutGrid")([20, 20]);
expect(controller.getElements()[0].style.left)
.not.toEqual(originalLeft);
});
it("listens for drop events", function () {
@@ -302,7 +296,7 @@ define(
// Notify that a drop occurred
testModel.composition.push('d');
findOn('mctDrop')(
{},
mockEvent,
'd',
{ x: 300, y: 100 }
);
@@ -310,12 +304,31 @@ define(
// Should have added an element
expect(testConfiguration.elements.length).toEqual(4);
// ...and prevented default...
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Should have triggered commit (provided by
// EditRepresenter) with some message.
expect(mockScope.commit)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("ignores drops when default has been prevented", function () {
// Avoids redundant drop-handling, WTD-1233
mockEvent.defaultPrevented = true;
// Notify that a drop occurred
testModel.composition.push('d');
findOn('mctDrop')(
mockEvent,
'd',
{ x: 300, y: 100 }
);
// Should NOT have added an element
expect(testConfiguration.elements.length).toEqual(3);
});
it("unsubscribes when destroyed", function () {
// Make an object available
findWatch('domainObject')(mockDomainObject);
@@ -328,6 +341,7 @@ define(
});
it("exposes its grid size", function () {
findWatch('model.layoutGrid')(testGrid);
// Template needs to be able to pass this into line
// elements to size SVGs appropriately
expect(controller.getGridSize()).toEqual(testGrid);

View File

@@ -0,0 +1,71 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/LayoutCompositionPolicy"],
function (LayoutCompositionPolicy) {
"use strict";
describe("Layout's composition policy", function () {
var mockCandidate,
mockContext,
candidateType,
contextType,
policy;
beforeEach(function () {
mockCandidate =
jasmine.createSpyObj('candidateType', ['instanceOf']);
mockContext =
jasmine.createSpyObj('contextType', ['instanceOf']);
mockCandidate.instanceOf.andCallFake(function (t) {
return t === candidateType;
});
mockContext.instanceOf.andCallFake(function (t) {
return t === contextType;
});
policy = new LayoutCompositionPolicy();
});
it("disallows folders in layouts", function () {
candidateType = 'layout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(false);
});
it("does not disallow folders elsewhere", function () {
candidateType = 'nonlayout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
});
it("allows things other than folders in layouts", function () {
candidateType = 'layout';
contextType = 'nonfolder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
});
});
}
);

View File

@@ -28,6 +28,7 @@ define(
describe("The Layout controller", function () {
var mockScope,
mockEvent,
testModel,
testConfiguration,
controller;
@@ -37,6 +38,10 @@ define(
"$scope",
[ "$watch", "$on", "commit" ]
);
mockEvent = jasmine.createSpyObj(
'event',
[ 'preventDefault' ]
);
testModel = {
composition: [ "a", "b", "c" ]
@@ -144,17 +149,32 @@ define(
// Notify that a drop occurred
testModel.composition.push('d');
mockScope.$on.mostRecentCall.args[1](
{},
mockEvent,
'd',
{ x: 300, y: 100 }
);
expect(testConfiguration.panels.d).toBeDefined();
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Should have triggered commit (provided by
// EditRepresenter) with some message.
expect(mockScope.commit)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("ignores drops when default has been prevented", function () {
// Avoids redundant drop-handling, WTD-1233
mockEvent.defaultPrevented = true;
// Notify that a drop occurred
testModel.composition.push('d');
mockScope.$on.mostRecentCall.args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
expect(testConfiguration.panels.d).not.toBeDefined();
});
});
}
);
);

View File

@@ -2,6 +2,7 @@
"FixedController",
"FixedDragHandle",
"FixedProxy",
"LayoutCompositionPolicy",
"LayoutController",
"LayoutDrag",
"elements/AccessorMutator",
@@ -13,4 +14,4 @@
"elements/ResizeHandle",
"elements/TelemetryProxy",
"elements/TextProxy"
]
]

View File

@@ -23,7 +23,21 @@
{
"key": "PlotController",
"implementation": "PlotController.js",
"depends": [ "$scope", "telemetryFormatter", "telemetryHandler", "throttle" ]
"depends": [
"$scope",
"telemetryFormatter",
"telemetryHandler",
"throttle",
"PLOT_FIXED_DURATION"
]
}
],
"constants": [
{
"key": "PLOT_FIXED_DURATION",
"value": 900000,
"priority": "fallback",
"comment": "Fifteen minutes."
}
],
"policies": [

View File

@@ -93,8 +93,9 @@
<mct-chart draw="subplot.getDrawingObject()"
ng-mousemove="subplot.hover($event)"
ng-mousedown="subplot.startMarquee($event)"
ng-mouseup="subplot.endMarquee($event); plot.update()">
mct-drag="subplot.continueDrag($event)"
mct-drag-down="subplot.startDrag($event)"
mct-drag-up="subplot.endDrag($event); plot.update()">
</mct-chart>
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->

View File

@@ -52,7 +52,13 @@ define(
*
* @constructor
*/
function PlotController($scope, telemetryFormatter, telemetryHandler, throttle) {
function PlotController(
$scope,
telemetryFormatter,
telemetryHandler,
throttle,
PLOT_FIXED_DURATION
) {
var subPlotFactory = new SubPlotFactory(telemetryFormatter),
modeOptions = new PlotModeOptions([], subPlotFactory),
subplots = [],
@@ -101,7 +107,8 @@ define(
updater = new PlotUpdater(
handle,
($scope.axes[0].active || {}).key,
($scope.axes[1].active || {}).key
($scope.axes[1].active || {}).key,
PLOT_FIXED_DURATION
);
limitTracker = new PlotLimitTracker(
handle,
@@ -170,7 +177,7 @@ define(
// Unsubscribe when the plot is destroyed
$scope.$on("$destroy", releaseSubscription);
// Create a throttled update function
scheduleUpdate = throttle(function () {
modeOptions.getModeHandler().getSubPlots()
@@ -266,4 +273,4 @@ define(
return PlotController;
}
);
);

View File

@@ -56,6 +56,9 @@ define(
domainOffset,
mousePosition,
marqueeStart,
panStart,
panStartBounds,
subPlotBounds,
hoverCoordinates,
isHovering = false;
@@ -88,8 +91,7 @@ define(
// pixel coordinates in the canvas area) from a mouse
// event object.
function toMousePosition($event) {
var target = $event.target,
bounds = target.getBoundingClientRect();
var bounds = subPlotBounds;
return {
x: $event.clientX - bounds.left,
@@ -155,6 +157,25 @@ define(
tickGenerator.generateRangeTicks(RANGE_TICKS);
}
function updatePan() {
var start, current, delta, nextOrigin;
// Clear the previous panning pan-zoom state
panZoomStack.popPanZoom();
// Calculate what the new resulting pan-zoom should be
start = mousePositionToDomainRange(panStart);
current = mousePositionToDomainRange(mousePosition);
delta = [ current[0] - start[0], current[1] - start[1] ];
nextOrigin = [
panStartBounds.origin[0] - delta[0],
panStartBounds.origin[1] - delta[1]
];
// ...and push a new one at the current mouse position
panZoomStack.pushPanZoom(nextOrigin, panStartBounds.dimensions);
}
// Perform a marquee zoom.
function marqueeZoom(start, end) {
@@ -241,31 +262,77 @@ define(
*/
hover: function ($event) {
isHovering = true;
subPlotBounds = $event.target.getBoundingClientRect();
mousePosition = toMousePosition($event);
updateHoverCoordinates();
if (marqueeStart) {
updateMarqueeBox();
}
if (panStart) {
updatePan();
updateDrawingBounds();
updateTicks();
}
},
/**
* Continue a previously-start pan or zoom gesture.
* @param $event the mouse event
*/
continueDrag: function ($event) {
mousePosition = toMousePosition($event);
if (marqueeStart) {
updateMarqueeBox();
}
if (panStart) {
updatePan();
updateDrawingBounds();
updateTicks();
}
},
/**
* Initiate a marquee zoom action.
* @param $event the mouse event
*/
startMarquee: function ($event) {
mousePosition = marqueeStart = toMousePosition($event);
updateMarqueeBox();
startDrag: function ($event) {
subPlotBounds = $event.target.getBoundingClientRect();
mousePosition = toMousePosition($event);
// Treat any modifier key as a pan
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
// Start panning
panStart = mousePosition;
panStartBounds = panZoomStack.getPanZoom();
// We're starting a pan, so add this back as a
// state on the stack; it will get replaced
// during the pan.
panZoomStack.pushPanZoom(
panStartBounds.origin,
panStartBounds.dimensions
);
$event.preventDefault();
} else {
// Start marquee zooming
marqueeStart = mousePosition;
updateMarqueeBox();
}
},
/**
* Complete a marquee zoom action.
* @param $event the mouse event
*/
endMarquee: function ($event) {
endDrag: function ($event) {
mousePosition = toMousePosition($event);
subPlotBounds = undefined;
if (marqueeStart) {
marqueeZoom(marqueeStart, mousePosition);
marqueeStart = undefined;
updateMarqueeBox();
updateDrawingBounds();
updateTicks();
}
if (panStart) {
// End panning
panStart = undefined;
panStartBounds = undefined;
}
},
/**
@@ -311,4 +378,4 @@ define(
return SubPlot;
}
);
);

View File

@@ -43,9 +43,9 @@ define(
var mid = Math.floor((min + max) / 2),
found = buffer[mid * 2];
// Collisions are not wanted
// On collisions, insert at same index
if (found === value) {
return -1;
return mid;
}
// Otherwise, if we're down to a single index,
@@ -258,4 +258,4 @@ define(
return PlotLineBuffer;
}
);
);

View File

@@ -42,14 +42,17 @@ define(
* @param {TelemetryHandle} handle the handle to telemetry access
* @param {string} domain the key to use when looking up domain values
* @param {string} range the key to use when looking up range values
* @param {number} maxDuration maximum plot duration to display
* @param {number} maxPoints maximum number of points to display
*/
function PlotUpdater(handle, domain, range, maxPoints) {
function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
var ids = [],
lines = {},
dimensions = [0, 0],
origin = [0, 0],
domainExtrema,
rangeExtrema,
buffers = {},
bufferArray = [],
domainOffset;
@@ -61,11 +64,10 @@ define(
// Check if this set of ids matches the current set of ids
// (used to detect if line preparation can be skipped)
function idsMatch(nextIds) {
return nextIds.map(function (id, index) {
return ids[index] === id;
}).reduce(function (a, b) {
return a && b;
}, true);
return ids.length === nextIds.length &&
nextIds.every(function (id, index) {
return ids[index] === id;
});
}
// Prepare plot lines for this group of telemetry objects
@@ -74,7 +76,7 @@ define(
next = {};
// Detect if we already have everything we need prepared
if (ids.length === nextIds.length && idsMatch(nextIds)) {
if (idsMatch(nextIds)) {
// Nothing to prepare, move on
return;
}
@@ -88,13 +90,13 @@ define(
// Create buffers for these objects
bufferArray = ids.map(function (id) {
var buffer = new PlotLineBuffer(
domainOffset,
INITIAL_SIZE,
maxPoints
);
next[id] = lines[id] || new PlotLine(buffer);
return buffer;
buffers[id] = buffers[id] || new PlotLineBuffer(
domainOffset,
INITIAL_SIZE,
maxPoints
);
next[id] = lines[id] || new PlotLine(buffers[id]);
return buffers[id];
});
}
@@ -107,6 +109,7 @@ define(
lines = next;
}
// Initialize the domain offset, based on these observed values
function initializeDomainOffset(values) {
domainOffset =
@@ -133,7 +136,7 @@ define(
}
// Update dimensions and origin based on extrema of plots
function updateExtrema() {
function updateBounds() {
if (bufferArray.length > 0) {
domainExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getDomainExtrema();
@@ -143,10 +146,40 @@ define(
return lineBuffer.getRangeExtrema();
}).reduce(reduceExtrema);
// Calculate best-fit dimensions
dimensions = (rangeExtrema[0] === rangeExtrema[1]) ?
[dimensionsOf(domainExtrema), 2.0 ] :
[dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)];
origin = [originOf(domainExtrema), originOf(rangeExtrema)];
// ...then enforce a fixed duration if needed
if (fixedDuration !== undefined) {
origin[0] = origin[0] + dimensions[0] - fixedDuration;
dimensions[0] = fixedDuration;
}
}
}
// Enforce maximum duration on all plot lines; not that
// domain extrema must be up-to-date for this to behave correctly.
function enforceDuration() {
var cutoff;
function enforceDurationForBuffer(plotLineBuffer) {
var index = plotLineBuffer.findInsertionIndex(cutoff);
if (index > 0) {
// Leave one point untrimmed, such that line will
// continue off left edge of visible plot area.
plotLineBuffer.trim(index - 1);
}
}
if (fixedDuration !== undefined &&
domainExtrema !== undefined &&
(domainExtrema[1] - domainExtrema[0] > fixedDuration)) {
cutoff = domainExtrema[1] - fixedDuration;
bufferArray.forEach(enforceDurationForBuffer);
updateBounds(); // Extrema may have changed now
}
}
@@ -180,8 +213,8 @@ define(
// Add new data
objects.forEach(addPointFor);
// Finally, update extrema
updateExtrema();
// Then, update extrema
updateBounds();
}
// Add historical data for this domain object
@@ -213,12 +246,12 @@ define(
line.addSeries(series, domain, range);
}
// Finally, update extrema
updateExtrema();
// Update extrema
updateBounds();
}
// Use a default MAX_POINTS if none is provided
maxPoints = maxPoints || MAX_POINTS;
maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
// Initially prepare state for these objects.
// Note that this may be an empty array at this time,
@@ -290,4 +323,4 @@ define(
return PlotUpdater;
}
);
);

View File

@@ -127,7 +127,7 @@ define(
// Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20
subplot.startMarquee({
subplot.startDrag({
target: mockElement,
clientX: 60,
clientY: 45
@@ -137,7 +137,7 @@ define(
clientX: 75,
clientY: 85
});
subplot.endMarquee({
subplot.endDrag({
target: mockElement,
clientX: 80,
clientY: 95
@@ -162,7 +162,7 @@ define(
// Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20
subplot.startMarquee({
subplot.startDrag({
target: mockElement,
clientX: 60,
clientY: 45
@@ -172,7 +172,7 @@ define(
clientX: 75,
clientY: 85
});
subplot.endMarquee({
subplot.endDrag({
target: mockElement,
clientX: 60,
clientY: 45
@@ -198,4 +198,4 @@ define(
});
}
);
);

View File

@@ -83,9 +83,6 @@ define(
expect(buffer.findInsertionIndex(10)).toEqual(4);
expect(buffer.findInsertionIndex(14.5)).toEqual(5);
expect(buffer.findInsertionIndex(20)).toEqual(6);
// 9 is already in there, disallow insertion
expect(buffer.findInsertionIndex(9)).toEqual(-1);
});
it("allows insertion in the middle", function () {
@@ -169,4 +166,4 @@ define(
});
}
);
);

View File

@@ -0,0 +1,37 @@
{
"name": "Event Messages",
"description": "List of time-ordered event messages",
"extensions": {
"views": [
{
"key": "rtmessages",
"name": "RT Messages",
"glyph": "5",
"description": "Scrolling list of real time messages.",
"templateUrl": "templates/rtmessages.html",
"needs": [ "telemetry" ],
"delegation": true
}
],
"controllers": [
{
"key": "RTEventListController",
"implementation": "RTEventListController.js",
"depends": [ "$scope", "telemetryHandler", "telemetryFormatter" ]
}
],
"directives": [
{
"key": "mctRtDataTable",
"implementation": "directives/MCTRTDataTable.js",
"depends": [ "$window" ]
}
],
"policies": [
{
"category": "view",
"implementation": "policies/RTMessagesViewPolicy.js"
}
]
}
}

View File

@@ -0,0 +1,37 @@
<!--
Open MCT Web, Copyright (c) 2014-2015, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT Web 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 Web 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.
-->
<table class="tabular">
<thead>
<tr>
<th ng-repeat="header in headers">
{{header}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="cell in row">
{{cell}}
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,29 @@
<!--
Open MCT Web, Copyright (c) 2014-2015, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT Web 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 Web 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.
-->
<div class="w1">
<div class="w2"
ng-controller="RTEventListController as rtevent">
<mct-rt-data-table headers="rtevent.headers()" rows="rtevent.rows()" ascending-scroll="true"></mct-rt-data-table>
</div>
</div>

View File

@@ -0,0 +1,71 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,moment*/
/**
* Module defining DomainColumn. Created by vwoeltje on 11/18/14.
*/
define(
[],
function () {
"use strict";
/**
* A column which will report telemetry domain values
* (typically, timestamps.) Used by the ScrollingListController.
*
* @constructor
* @param domainMetadata an object with the machine- and human-
* readable names for this domain (in `key` and `name`
* fields, respectively.)
* @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable.
*/
function DomainColumn(telemetryFormatter) {
return {
/**
* Get the title to display in this column's header.
* @returns {string} the title to display
*/
getTitle: function () {
// At the moment there does not appear to be a way to get the
// column's title through metadata for real time telemetry
return "Time";
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
*/
getValue: function (domainObject, handle) {
return {
text: telemetryFormatter.formatDomainValue(
handle.getDomainValue(domainObject)
)
};
}
};
}
return DomainColumn;
}
);

View File

@@ -0,0 +1,138 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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*/
/**
* Module defining RTEventListController.
* Created by shale on 06/25/2014. Based on RT Scrolling lists.
*/
define(
["./DomainColumn", "./RangeColumn"],
function (DomainColumn, RangeColumn) {
"use strict";
var ROW_COUNT = 100;
/**
* The RTEventListController is responsible for populating
* the contents of the messages view.
* @constructor
*/
function RTEventListController($scope, telemetryHandler, telemetryFormatter) {
var handle,
lastUpdated = {},
lastIds = [],
columns = [],
headers = [],
rows = [];
function getTelemetryObjects() {
//console.log("handle.getTelemetryObjects() ", handle.getTelemetryObjects());
return handle ? handle.getTelemetryObjects() : [];
}
function idsChanged(telemetryObjects) {
function mismatch(id, index) {
return id !== telemetryObjects[index].getId();
}
return lastIds.length !== telemetryObjects.length ||
lastIds.some(mismatch);
}
function setupColumns(telemetryObjects) {
var id = $scope.domainObject && $scope.domainObject.getId(),
firstId =
telemetryObjects[0] && telemetryObjects[0].getId();
columns = [];
columns.push(new DomainColumn(telemetryFormatter));
columns.push(new RangeColumn());
headers = columns.map(function (column) {
return column.getTitle();
});
}
function updateObjects(telemetryObjects) {
if (idsChanged(telemetryObjects)) {
setupColumns(telemetryObjects);
lastIds = telemetryObjects.map(function (telemetryObject) {
return telemetryObject.getId();
});
}
}
function addRow(telemetryObject) {
var id = telemetryObject.getId(),
domainValue = handle.getDomainValue(telemetryObject);
if (lastUpdated[id] !== domainValue &&
domainValue !== undefined) {
// Instead of unshift (scrolling), use push (messages)
rows.push(columns.map(function (column) {
return column.getValue(telemetryObject, handle).text;
}));
// Remove first rows when adding past the max rows limit
rows.splice(0, rows.length - ROW_COUNT);
lastUpdated[id] = domainValue;
}
}
function updateValues() {
getTelemetryObjects().forEach(addRow);
}
function releaseSubscription() {
if (handle) {
handle.unsubscribe();
}
}
function makeSubscription(domainObject) {
releaseSubscription();
rows = [];
handle = telemetryHandler.handle(
domainObject,
updateValues,
true
);
}
$scope.$on("$destroy", releaseSubscription);
$scope.$watch("domainObject", makeSubscription);
$scope.$watch(getTelemetryObjects, updateObjects);
return {
rows: function () {
return rows;
},
headers: function () {
return headers;
}
};
}
return RTEventListController;
}
);

View File

@@ -0,0 +1,68 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,moment*/
/**
* Module defining DomainColumn.
* Created by vwoeltje on 11/18/14. Modified by shale on 06/25/2015.
*/
define(
[],
function () {
"use strict";
/**
* A column which will report telemetry range values
* (typically, measurements.) Used by the RTEventListController.
*
* @constructor
* @param rangeMetadata an object with the machine- and human-
* readable names for this range (in `key` and `name`
* fields, respectively.)
* @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable.
*/
function RangeColumn() {
return {
/**
* Get the title to display in this column's header.
* @returns {string} the title to display
*/
getTitle: function () {
return "Message";
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
*/
getValue: function (domainObject, handle) {
return {
text: handle.getRangeValue(domainObject)
};
}
};
}
return RangeColumn;
}
);

View File

@@ -0,0 +1,74 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,Promise*/
/**
* Module defining MCTRTDataTable. Created by shale on 06/25/2015.
*/
define(
[],
function () {
"use strict";
function MCTRTDataTable($window) {
return {
restrict: "E",
templateUrl: "platform/features/rtevents/res/templates/mct-rt-data-table.html",
scope: {
headers: "=",
rows: "=",
ascendingScroll: "="
},
link: function ($scope, $element) {
var currentHeight,
previousHeight,
scrollParent;
// If the scroll is set to ascending, we want to
// check when elements are added to the table, and move the scroll
// bar accordingly.
// (When viewing at the bottom of the page, the scroll bar will
// stay at the bottom despite additions to the table)
if ($scope.ascendingScroll) {
$scope.$watchCollection("rows", function () {
// Wait until the page as been repainted (otherwise the
// height will always be zero)
$window.requestAnimationFrame(function () {
previousHeight = currentHeight;
// The height of the table body
currentHeight = $element[0].firstElementChild.firstElementChild.nextElementSibling.clientHeight;
// One of the parents is a div that has vscroll
scrollParent = $element[0].parentElement.parentElement.parentElement.parentElement.parentElement;
// Move the scrollbar down the amount that the height has changed
scrollParent.scrollTop = scrollParent.scrollTop + (currentHeight - previousHeight);
});
});
}
}
};
}
return MCTRTDataTable;
}
);

View File

@@ -0,0 +1,74 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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*/
/**
* Module defining MessagesViewPolicy. Created by shale on 06/24/2015.
*/
define(
[],
function () {
"use strict";
/**
* Policy controlling when the real time Messages view should be avaliable.
* @constructor
*/
function RTMessagesViewPolicy() {
function hasStringTelemetry(domainObject) {
var telemetry = domainObject &&
domainObject.getCapability('telemetry'),
metadata = telemetry ? telemetry.getMetadata() : {},
ranges = metadata.ranges || [];
return ranges.some(function (range) {
return range.format === 'string';
});
}
return {
/**
* Check whether or not a given action is allowed by this
* policy.
* @param {Action} action the action
* @param domainObject the domain object which will be viewed
* @returns {boolean} true if not disallowed
*/
allow: function (view, domainObject) {
// This policy only applies for the RT Messages view
if (view.key === 'rtmessages') {
// The Messages view is allowed only if the domain
// object has string telemetry
if (!hasStringTelemetry(domainObject)) {
return false;
}
}
// Like all policies, allow by default.
return true;
}
};
}
return RTMessagesViewPolicy;
}
);

View File

@@ -0,0 +1,88 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* RTEventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/25/2015.
*/
define(
["../src/DomainColumn"],
function (DomainColumn) {
"use strict";
var TEST_DOMAIN_VALUE = "some formatted domain value";
describe("A real time event list domain column", function () {
var mockDomainObject,
mockTelemetryHandler,
mockHandle,
mockFormatter,
column;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getModel", "getCapability"]
);
mockTelemetryHandler = jasmine.createSpyObj(
"telemetryHandler",
["handle"]
);
mockHandle = jasmine.createSpyObj(
"handle",
["getDomainValue", "getRangeValue"]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockFormatter.formatDomainValue.andReturn(TEST_DOMAIN_VALUE);
column = new DomainColumn(mockFormatter);
});
it("reports the domain column header as 'Time'", function () {
expect(column.getTitle()).toEqual("Time");
});
it("retrives data from a telemetry provider", function () {
column.getValue(mockDomainObject, mockHandle);
expect(mockHandle.getDomainValue).toHaveBeenCalled();
});
it("formats domain values as time", function () {
mockHandle.getDomainValue.andReturn(402513731000);
// Should have just given the value the formatter gave
expect(column.getValue(mockDomainObject, mockHandle).text)
.toEqual(TEST_DOMAIN_VALUE);
// Make sure that service interactions were as expected
expect(mockFormatter.formatDomainValue)
.toHaveBeenCalledWith(402513731000);
expect(mockFormatter.formatRangeValue)
.not.toHaveBeenCalled();
});
});
}
);

View File

@@ -0,0 +1,130 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* RTEventSpec. Created by shale on 06/25/2015.
*/
define(
["../src/RTEventListController"],
function (RTEventListController) {
"use strict";
describe("The real time event list controller", function () {
var mockDomainObject,
mockScope,
mockTelemetryHandler,
mockHandle,
mockTelemetryFormatter,
controller;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
);
mockScope = jasmine.createSpyObj(
"$scope",
[ "$on", "$watch" ]
);
mockTelemetryHandler = jasmine.createSpyObj(
"telemetryHandler",
["handle"]
);
mockHandle = jasmine.createSpyObj(
"handle",
["getDomainValue", "getRangeValue", "getTelemetryObjects", "unsubscribe"]
);
mockTelemetryFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
controller = new RTEventListController(mockScope, mockTelemetryHandler, mockTelemetryFormatter);
mockHandle.getDomainValue.andReturn("domain value");
mockHandle.getRangeValue.andReturn("range value");
mockTelemetryHandler.handle.andReturn(mockHandle);
mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
// Subscribe to the RT telemetry
// second argument of: $scope.$watch("domainObject", makeSubscription);
mockScope.$watch.calls.forEach(function (c) {
// There are two possible calls of $watch, so we need to filter
// through the calls to get the correct kind
if (c.args[0] === 'domainObject') {
c.args[1]();
}
});
// callback, passed into telemetry handler
mockTelemetryHandler.handle.mostRecentCall.args[1]();
// Update the telemetry objects
// second argument of: $scope.$watch(getTelemetryObjects, updateObjects);
mockScope.$watch.calls.forEach(function (c) {
// There are two possible calls of $watch, so we need to filter
// through the calls to get the correct kind
if (c.args[0] !== 'domainObject') {
c.args[1]([mockDomainObject]);
}
});
});
it("provides a domain and a range column", function () {
// Should have two columns with these headers
expect(controller.headers()).toEqual(["Time", "Message"]);
});
it("listens for telemetry data updates", function () {
// Of the two possible $watch calls, this corresponds to
// $scope.$watch(getTelemetryObjects, updateObjects);
expect(mockScope.$watch).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
});
it("makes telemetry subscriptions", function () {
// Of the two possible $watch calls, this corresponds to
// $scope.$watch("domainObject", makeSubscription);
expect(mockScope.$watch).toHaveBeenCalledWith(
"domainObject",
jasmine.any(Function)
);
});
it("releases telemetry subscriptions on destruction", function () {
// Call the second argument of
// $scope.$on("$destroy", releaseSubscription);
mockScope.$on.mostRecentCall.args[1]();
expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy",
jasmine.any(Function)
);
});
});
}
);

View File

@@ -0,0 +1,84 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* RTEventSpec. Created by vwoeltje on 11/6/14. Modified by shale on 06/25/2015.
*/
define(
["../src/RangeColumn"],
function (RangeColumn) {
"use strict";
var TEST_RANGE_VALUE = "some formatted range value";
describe("A real time event list range column", function () {
var mockDomainObject,
mockTelemetryHandler,
mockHandle,
mockFormatter,
column;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getModel", "getCapability"]
);
mockTelemetryHandler = jasmine.createSpyObj(
"telemetryHandler",
["handle"]
);
mockHandle = jasmine.createSpyObj(
"handle",
["getDomainValue", "getRangeValue"]
);
mockFormatter = jasmine.createSpyObj(
"formatter",
["formatDomainValue", "formatRangeValue"]
);
mockFormatter.formatRangeValue.andReturn(TEST_RANGE_VALUE);
column = new RangeColumn();
});
it("reports a range column header as 'Message'", function () {
expect(column.getTitle()).toEqual("Message");
});
it("retrives data from a telemetry provider", function () {
column.getValue(mockDomainObject, mockHandle);
expect(mockHandle.getRangeValue).toHaveBeenCalled();
});
it("does not format range values", function () {
mockHandle.getRangeValue.andReturn(123.45678);
// Does not format range value as time
expect(column.getValue(mockDomainObject, mockHandle).text)
.not.toEqual(TEST_RANGE_VALUE);
// There should be no additional formatting
// i.e. the message string stays a string
expect(column.getValue(mockDomainObject, mockHandle).text)
.toEqual(123.45678);
});
});
}
);

View File

@@ -0,0 +1,82 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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,describe,it,expect,beforeEach,jasmine*/
/**
* RTEventSpec. Created by shale on 06/25/2015.
*/
define(
["../../src/policies/RTMessagesViewPolicy"],
function (RTMessagesViewPolicy) {
"use strict";
describe("The real time Messages view policy", function () {
var testView,
mockDomainObject,
mockTelemetry,
testMetadata,
policy;
beforeEach(function () {
testView = { key: "rtmessages" };
testMetadata = {};
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getModel', 'getCapability']
);
mockTelemetry = jasmine.createSpyObj(
'telemetry',
['getMetadata']
);
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'telemetry' ? mockTelemetry : undefined;
});
mockTelemetry.getMetadata.andReturn(testMetadata);
policy = new RTMessagesViewPolicy();
});
it("allows the real time messages view for domain objects with string telemetry", function () {
testMetadata.ranges = [ { key: "foo", format: "string" } ];
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
});
it("disallows the real time messages view for domain objects without string telemetry", function () {
testMetadata.ranges = [ { key: "foo", format: "somethingElse" } ];
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
});
it("disallows the real time messages view for domain objects without telemetry", function () {
testMetadata.ranges = [ { key: "foo", format: "string" } ];
mockDomainObject.getCapability.andReturn(undefined);
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
});
it("allows other views", function () {
testView.key = "somethingElse";
testMetadata.ranges = [ { key: "foo", format: "somethingElse" } ];
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
});
});
}
);

View File

@@ -0,0 +1,6 @@
[
"DomainColumn",
"policies/RTMessagesViewPolicy",
"RangeColumn",
"RTEventListController"
]

View File

@@ -23,7 +23,7 @@
<div class="w2"
ng-controller="ScrollingListController">
<!-- To add filtering, add class 'filterable' to <table> and uncomment 2nd <tr> in <thead> -->
<table class="tabular">
<table class="tabular fixed-header">
<thead>
<tr>
<th ng-repeat="header in headers">