[Events] Created RT Messages

Created a real-time version of the Messages view called
RT Messages.
Additionally, fixed the MessagesViewPolicy test. #18.
This commit is contained in:
Sarah Hale
2015-06-25 14:36:14 -07:00
parent c6405c50df
commit c98a381a42
15 changed files with 70 additions and 397 deletions

View File

@ -31,37 +31,50 @@ define(
describe("The messages view policy", function () { describe("The messages view policy", function () {
var mockDomainObject, var mockDomainObject,
testType, mockTelemetry,
telemetryType, telemetryType,
testType,
testView,
testMetadata,
policy; policy;
beforeEach(function () { beforeEach(function () {
testView = { key: "string" };
testMetadata = {};
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
'domainObject', 'domainObject',
['getModel'] ['getModel', 'getCapability']
); );
mockTelemetry = jasmine.createSpyObj(
'telemetry',
['getMetadata']
);
mockDomainObject.getModel.andCallFake(function (c) { mockDomainObject.getModel.andCallFake(function (c) {
return {type: testType}; return {type: testType};
}); });
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'telemetry' ? mockTelemetry : undefined;
});
mockTelemetry.getMetadata.andReturn(testMetadata);
policy = new MessagesViewPolicy(); policy = new MessagesViewPolicy();
}); });
it("disallows the message view for objects without string telemetry", function () { it("disallows the message view for objects without string telemetry", function () {
telemetryType = 'notString'; testMetadata.ranges = [ { format: 'notString' } ];
expect(policy.allow({ key: 'messages' }, mockDomainObject)) expect(policy.allow({ key: 'messages' }, mockDomainObject)).toBeFalsy();
.toBeFalsy();
}); });
it("allows the message view for objects with string telemetry", function () { it("allows the message view for objects with string telemetry", function () {
telemetryType = 'string'; testMetadata.ranges = [ { format: 'string' } ];
expect(policy.allow({ key: 'messages' }, mockDomainObject)) expect(policy.allow({ key: 'messages' }, mockDomainObject)).toBeTruthy();
.toBeTruthy();
}); });
it("returns true when the current view is not the Messages view", function () { it("returns true when the current view is not the Messages view", function () {
expect(policy.allow({ key: 'notMessages' }, mockDomainObject)) expect(policy.allow({ key: 'notMessages' }, mockDomainObject)).toBeTruthy();
.toBeTruthy();
}); });
}); });
} }

View File

@ -4,11 +4,11 @@
"extensions": { "extensions": {
"views": [ "views": [
{ {
"key": "messages", "key": "rtmessages",
"name": "RT Messages", "name": "RT Messages",
"glyph": "5", "glyph": "5",
"description": "Scrolling list of messages.", "description": "Scrolling list of real time messages.",
"templateUrl": "templates/messages.html", "templateUrl": "templates/rtmessages.html",
"needs": [ "telemetry" ], "needs": [ "telemetry" ],
"delegation": true "delegation": true
} }
@ -22,15 +22,15 @@
], ],
"directives": [ "directives": [
{ {
"key": "mctDataTable", "key": "mctRtDataTable",
"implementation": "directives/MCTDataTable.js", "implementation": "directives/MCTRTDataTable.js",
"depends": [ "$window" ] "depends": [ "$window" ]
} }
], ],
"policies": [ "policies": [
{ {
"category": "view", "category": "view",
"implementation": "policies/MessagesViewPolicy.js" "implementation": "policies/RTMessagesViewPolicy.js"
} }
] ]
} }

View File

@ -19,10 +19,10 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="w1" ng-controller="TelemetryController as telemetry"> <div class="w1">
<div class="w2" <div class="w2"
ng-controller="EventListController"> ng-controller="RTEventListController as rtevent">
<mct-data-table headers="headers" rows="rows" ascending-scroll="true"></mct-data-table> <mct-rt-data-table headers="rtevent.headers()" rows="rtevent.rows()" ascending-scroll="true"></mct-rt-data-table>
</div> </div>
</div> </div>

View File

@ -40,28 +40,30 @@ define(
* @param {TelemetryFormatter} telemetryFormatter the telemetry * @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable. * formatting service, for making values human-readable.
*/ */
function DomainColumn(domainMetadata, telemetryFormatter) { function DomainColumn(telemetryFormatter) {
return { return {
/** /**
* Get the title to display in this column's header. * Get the title to display in this column's header.
* @returns {string} the title to display * @returns {string} the title to display
*/ */
getTitle: function () { getTitle: function () {
return domainMetadata.name; return "Time";
}, },
/** /**
* Get the text to display inside a row under this * Get the text to display inside a row under this
* column. * column.
* @returns {string} the text to display * @returns {string} the text to display
*/ */
getValue: function (domainObject, data, index) { getValue: function (domainObject, handle) {
return telemetryFormatter.formatDomainValue( return {
data.getDomainValue(index, domainMetadata.key) text: telemetryFormatter.formatDomainValue(
); handle.getDomainValue(domainObject)
)
};
} }
}; };
} }
return DomainColumn; return DomainColumn;
} }
); );

View File

@ -64,10 +64,7 @@ define(
telemetryObjects[0] && telemetryObjects[0].getId(); telemetryObjects[0] && telemetryObjects[0].getId();
columns = []; columns = [];
if (telemetryObjects > 1 || id !== firstId) {
columns.push(new NameColumn());
}
columns.push(new DomainColumn(telemetryFormatter)); columns.push(new DomainColumn(telemetryFormatter));
columns.push(new RangeColumn()); columns.push(new RangeColumn());
@ -92,9 +89,11 @@ define(
domainValue !== undefined) { domainValue !== undefined) {
// Instead of unshift (scrolling), use push (messages) // Instead of unshift (scrolling), use push (messages)
rows.push(columns.map(function (column) { rows.push(columns.map(function (column) {
return column.getValue(telemetryObject, handle); return column.getValue(telemetryObject, handle).text;
})); }));
rows.splice(ROW_COUNT, Number.MAX_VALUE); // Remove first rows when adding past the max rows limit
//rows.splice(ROW_COUNT, Number.MAX_VALUE);
rows.splice(0, rows.length - ROW_COUNT);
lastUpdated[id] = domainValue; lastUpdated[id] = domainValue;
} }
} }

View File

@ -19,10 +19,11 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*global define,Promise*/ /*global define,moment*/
/** /**
* Module defining DomainColumn. Created by vwoeltje on 11/18/14. * Module defining DomainColumn.
* Created by vwoeltje on 11/18/14. Modified by shale on 06/25/2015.
*/ */
define( define(
[], [],
@ -31,7 +32,7 @@ define(
/** /**
* A column which will report telemetry range values * A column which will report telemetry range values
* (typically, measurements.) Used by the ScrollingListController. * (typically, measurements.) Used by the RTScrollingListController.
* *
* @constructor * @constructor
* @param rangeMetadata an object with the machine- and human- * @param rangeMetadata an object with the machine- and human-
@ -40,28 +41,28 @@ define(
* @param {TelemetryFormatter} telemetryFormatter the telemetry * @param {TelemetryFormatter} telemetryFormatter the telemetry
* formatting service, for making values human-readable. * formatting service, for making values human-readable.
*/ */
function RangeColumn(rangeMetadata, telemetryFormatter) { function RangeColumn() {
return { return {
/** /**
* Get the title to display in this column's header. * Get the title to display in this column's header.
* @returns {string} the title to display * @returns {string} the title to display
*/ */
getTitle: function () { getTitle: function () {
return rangeMetadata.name; return "Message";
}, },
/** /**
* Get the text to display inside a row under this * Get the text to display inside a row under this
* column. * column.
* @returns {string} the text to display * @returns {string} the text to display
*/ */
getValue: function (domainObject, data, index) { getValue: function (domainObject, handle) {
return telemetryFormatter.formatRangeValue( return {
data.getRangeValue(index, rangeMetadata.key) text: handle.getRangeValue(domainObject)
); };
} }
}; };
} }
return RangeColumn; return RangeColumn;
} }
); );

View File

@ -22,17 +22,17 @@
/*global define,Promise*/ /*global define,Promise*/
/** /**
* Module defining MCTDataTable. Created by shale on 06/22/2015. * Module defining MCTRTDataTable. Created by shale on 06/25/2015.
*/ */
define( define(
[], [],
function () { function () {
"use strict"; "use strict";
function MCTDataTable($window) { function MCTRTDataTable($window) {
return { return {
restrict: "E", restrict: "E",
templateUrl: "platform/features/events/res/templates/mct-data-table.html", templateUrl: "platform/features/rtevents/res/templates/mct-rt-data-table.html",
scope: { scope: {
headers: "=", headers: "=",
rows: "=", rows: "=",
@ -49,7 +49,7 @@ define(
// (When viewing at the bottom of the page, the scroll bar will // (When viewing at the bottom of the page, the scroll bar will
// stay at the bottom despite additions to the table) // stay at the bottom despite additions to the table)
if ($scope.ascendingScroll) { if ($scope.ascendingScroll) {
$scope.$watch("rows", function () { $scope.$watchCollection("rows", function () {
// Wait until the page as been repainted (otherwise the // Wait until the page as been repainted (otherwise the
// height will always be zero) // height will always be zero)
$window.requestAnimationFrame(function () { $window.requestAnimationFrame(function () {
@ -59,7 +59,7 @@ define(
// One of the parents is a div that has vscroll // One of the parents is a div that has vscroll
scrollParent = $element[0].parentElement.parentElement.parentElement.parentElement.parentElement; scrollParent = $element[0].parentElement.parentElement.parentElement.parentElement.parentElement;
// Move the scrollbar down the amount that the height has changed // Move the scrollbar down the amount that the height has changed
scrollParent.scrollTop = scrollParent.scrollTop + (currentHeight - previousHeight); scrollParent.scrollTop = scrollParent.scrollTop + (currentHeight - previousHeight);
}); });
@ -69,6 +69,6 @@ define(
}; };
} }
return MCTDataTable; return MCTRTDataTable;
} }
); );

View File

@ -33,14 +33,15 @@ define(
* Policy controlling when the Messages view should be avaliable. * Policy controlling when the Messages view should be avaliable.
* @constructor * @constructor
*/ */
function MessagesViewPolicy() { function RTMessagesViewPolicy() {
function hasStringTelemetry(domainObject) { function hasStringTelemetry(domainObject) {
var telemetry = domainObject && var telemetry = domainObject &&
domainObject.getCapability('telemetry'), domainObject.getCapability('telemetry'),
metadata = telemetry ? telemetry.getMetadata() : {}, metadata = telemetry ? telemetry.getMetadata() : {},
data = telemetry ? telemetry.requestData() : {},
ranges = metadata.ranges || []; ranges = metadata.ranges || [];
return ranges.some(function (range) { return ranges.some(function (range) {
return range.format === 'string'; return range.format === 'string';
}); });
@ -54,8 +55,8 @@ define(
* @returns {boolean} true if not disallowed * @returns {boolean} true if not disallowed
*/ */
allow: function (view, domainObject) { allow: function (view, domainObject) {
// This policy only applies for the Messages view // This policy only applies for the RT Messages view
if (view.key === 'messages') { if (view.key === 'rtmessages') {
// The Messages view is allowed only if the domain // The Messages view is allowed only if the domain
// object has string telemetry // object has string telemetry
if (!hasStringTelemetry(domainObject)) { if (!hasStringTelemetry(domainObject)) {
@ -69,6 +70,6 @@ define(
}; };
} }
return MessagesViewPolicy; return RTMessagesViewPolicy;
} }
); );

View File

@ -1,84 +0,0 @@
/*****************************************************************************
* 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

@ -1,110 +0,0 @@
/*****************************************************************************
* 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/25/2015.
*/
define(
["../src/RTEventListController"],
function (RTEventListController) {
"use strict";
describe("The real time 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

@ -1,81 +0,0 @@
/*****************************************************************************
* 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

@ -1,68 +0,0 @@
/*****************************************************************************
* 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,
testType,
telemetryType,
policy;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getModel']
);
mockDomainObject.getModel.andCallFake(function (c) {
return {type: testType};
});
policy = new MessagesViewPolicy();
});
it("disallows the message view for objects without string telemetry", function () {
telemetryType = 'notString';
expect(policy.allow({ key: 'messages' }, mockDomainObject))
.toBeFalsy();
});
it("allows the message view for objects with string telemetry", function () {
telemetryType = '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