mirror of
https://github.com/nasa/openmct.git
synced 2025-06-14 05:08:15 +00:00
[Fixed Position] Updated fixed position view to use new Telemetry API
Added TelemetryCollection to handle bounds and buffering. Avoids loss of telemetry due to timing mismatch between telemetry providers and tick sources Fixed issues with removal and addition of objects
This commit is contained in:
@ -37,6 +37,15 @@ define(
|
||||
testModel,
|
||||
testValues,
|
||||
testConfiguration,
|
||||
mockOpenMCT,
|
||||
mockTelemetryAPI,
|
||||
mockCompositionAPI,
|
||||
mockCompositionCollection,
|
||||
mockChildren,
|
||||
mockConductor,
|
||||
mockMetadata,
|
||||
mockTimeSystem,
|
||||
mockLimitEvaluator,
|
||||
controller;
|
||||
|
||||
// Utility function; find a watch for a given expression
|
||||
@ -62,19 +71,18 @@ define(
|
||||
}
|
||||
|
||||
function makeMockDomainObject(id) {
|
||||
var mockObject = jasmine.createSpyObj(
|
||||
'domainObject-' + id,
|
||||
['getId', 'getModel', 'getCapability']
|
||||
);
|
||||
mockObject.getId.andReturn(id);
|
||||
mockObject.getModel.andReturn({ name: "Point " + id});
|
||||
return mockObject;
|
||||
return {
|
||||
identifier: {
|
||||
key: "domainObject-" + id
|
||||
},
|
||||
name: "Point " + id
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
'$scope',
|
||||
["$on", "$watch", "commit"]
|
||||
["$on", "$watch", "$digest", "commit"]
|
||||
);
|
||||
mockHandler = jasmine.createSpyObj(
|
||||
'telemetryHandler',
|
||||
@ -87,12 +95,17 @@ define(
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
'telemetryFormatter',
|
||||
['formatDomainValue', 'formatRangeValue']
|
||||
['format']
|
||||
);
|
||||
mockFormatter.format.andCallFake(function (value) {
|
||||
return "Formatted " + value;
|
||||
});
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['getId', 'getModel', 'getCapability']
|
||||
['getId', 'getModel', 'getCapability', 'useCapability']
|
||||
);
|
||||
|
||||
mockHandle = jasmine.createSpyObj(
|
||||
'subscription',
|
||||
[
|
||||
@ -104,11 +117,39 @@ define(
|
||||
'request'
|
||||
]
|
||||
);
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'on',
|
||||
'off',
|
||||
'bounds',
|
||||
'timeSystem',
|
||||
'follow'
|
||||
]);
|
||||
mockConductor.bounds.andReturn({});
|
||||
mockTimeSystem = {
|
||||
metadata: {
|
||||
key: 'key'
|
||||
}
|
||||
};
|
||||
mockConductor.timeSystem.andReturn(mockTimeSystem);
|
||||
|
||||
mockEvent = jasmine.createSpyObj(
|
||||
'event',
|
||||
['preventDefault']
|
||||
);
|
||||
|
||||
mockTelemetryAPI = jasmine.createSpyObj('telemetry',
|
||||
[
|
||||
'subscribe',
|
||||
'request',
|
||||
'canProvideTelemetry',
|
||||
'getMetadata',
|
||||
'limitEvaluator',
|
||||
'getValueFormatter'
|
||||
]
|
||||
);
|
||||
mockTelemetryAPI.canProvideTelemetry.andReturn(true);
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve([]));
|
||||
|
||||
testGrid = [123, 456];
|
||||
testModel = {
|
||||
composition: ['a', 'b', 'c'],
|
||||
@ -121,17 +162,23 @@ define(
|
||||
{ type: "fixed.telemetry", id: 'c', x: 1, y: 1 }
|
||||
]};
|
||||
|
||||
mockHandler.handle.andReturn(mockHandle);
|
||||
mockHandle.getTelemetryObjects.andReturn(
|
||||
testModel.composition.map(makeMockDomainObject)
|
||||
mockChildren = testModel.composition.map(makeMockDomainObject);
|
||||
mockCompositionCollection = jasmine.createSpyObj('compositionCollection',
|
||||
[
|
||||
'load'
|
||||
]
|
||||
);
|
||||
mockHandle.getRangeValue.andCallFake(function (o) {
|
||||
return testValues[o.getId()];
|
||||
});
|
||||
mockHandle.getDomainValue.andReturn(12321);
|
||||
mockFormatter.formatRangeValue.andCallFake(function (v) {
|
||||
return "Formatted " + v;
|
||||
});
|
||||
mockCompositionAPI = jasmine.createSpyObj('composition',
|
||||
[
|
||||
'get'
|
||||
]
|
||||
);
|
||||
mockCompositionAPI.get.andReturn(mockCompositionCollection);
|
||||
mockCompositionCollection.load.andReturn(
|
||||
Promise.resolve(mockChildren)
|
||||
);
|
||||
|
||||
|
||||
mockScope.model = testModel;
|
||||
mockScope.configuration = testConfiguration;
|
||||
mockScope.selection = jasmine.createSpyObj(
|
||||
@ -139,12 +186,47 @@ define(
|
||||
['select', 'get', 'selected', 'deselect', 'proxy']
|
||||
);
|
||||
|
||||
mockOpenMCT = {
|
||||
conductor: mockConductor,
|
||||
telemetry: mockTelemetryAPI,
|
||||
composition: mockCompositionAPI
|
||||
};
|
||||
|
||||
mockMetadata = jasmine.createSpyObj('mockMetadata', [
|
||||
'valuesForHints',
|
||||
'value'
|
||||
]);
|
||||
mockMetadata.value.andReturn({
|
||||
key: 'value'
|
||||
});
|
||||
|
||||
mockMetadata.valuesForHints.andCallFake(function (hints) {
|
||||
if (hints === ['domain']) {
|
||||
return [{
|
||||
key: 'time'
|
||||
}];
|
||||
} else {
|
||||
return [{
|
||||
key: 'value'
|
||||
}];
|
||||
}
|
||||
});
|
||||
|
||||
mockLimitEvaluator = jasmine.createSpyObj('limitEvaluator', [
|
||||
'evaluate'
|
||||
]);
|
||||
|
||||
mockLimitEvaluator.evaluate.andReturn({});
|
||||
|
||||
mockTelemetryAPI.getMetadata.andReturn(mockMetadata);
|
||||
mockTelemetryAPI.limitEvaluator.andReturn(mockLimitEvaluator);
|
||||
mockTelemetryAPI.getValueFormatter.andReturn(mockFormatter);
|
||||
|
||||
controller = new FixedController(
|
||||
mockScope,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockHandler,
|
||||
mockFormatter
|
||||
mockOpenMCT
|
||||
);
|
||||
|
||||
findWatch("model.layoutGrid")(testModel.layoutGrid);
|
||||
@ -152,26 +234,61 @@ define(
|
||||
});
|
||||
|
||||
it("subscribes when a domain object is available", function () {
|
||||
var dunzo = false;
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockHandler.handle).toHaveBeenCalledWith(
|
||||
mockDomainObject,
|
||||
jasmine.any(Function)
|
||||
);
|
||||
findWatch("domainObject")(mockDomainObject).then(function () {
|
||||
dunzo = true;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return dunzo;
|
||||
}, "Telemetry fetched", 200);
|
||||
|
||||
runs(function () {
|
||||
mockChildren.forEach(function (child) {
|
||||
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(
|
||||
child,
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Object)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("releases subscriptions when domain objects change", function () {
|
||||
var dunzo = false;
|
||||
var unsubscribe = jasmine.createSpy('unsubscribe');
|
||||
|
||||
mockTelemetryAPI.subscribe.andReturn(unsubscribe);
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
findWatch("domainObject")(mockDomainObject).then(function () {
|
||||
dunzo = true;
|
||||
});
|
||||
|
||||
// First pass - should simply should subscribe
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
expect(mockHandler.handle.calls.length).toEqual(1);
|
||||
waitsFor(function () {
|
||||
return dunzo;
|
||||
}, "Telemetry fetched", 200);
|
||||
|
||||
// Object changes - should unsubscribe then resubscribe
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
expect(mockHandler.handle.calls.length).toEqual(2);
|
||||
runs(function () {
|
||||
expect(unsubscribe).not.toHaveBeenCalled();
|
||||
|
||||
dunzo = false;
|
||||
|
||||
findWatch("domainObject")(mockDomainObject).then(function () {
|
||||
dunzo = true;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return dunzo;
|
||||
}, "Telemetry fetched", 200);
|
||||
|
||||
runs(function () {
|
||||
expect(unsubscribe.calls.length).toBe(mockChildren.length);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes visible elements based on configuration", function () {
|
||||
@ -254,25 +371,38 @@ define(
|
||||
expect(mockScope.selection.select.calls.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("provides values for telemetry elements", function () {
|
||||
it("Displays received values for telemetry elements", function () {
|
||||
var elements;
|
||||
// Initialize
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
mockScope.model = testModel;
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
findWatch("model.modified")(1);
|
||||
findWatch("model.composition")(mockScope.model.composition);
|
||||
var mockTelemetry = {
|
||||
time: 100,
|
||||
value: 200
|
||||
};
|
||||
var testElement = {};
|
||||
var telemetryObject = {
|
||||
identifier: {
|
||||
key: '12345'
|
||||
}
|
||||
};
|
||||
controller.elementProxiesById = {};
|
||||
controller.elementProxiesById['12345'] = [testElement];
|
||||
controller.elementProxies = [testElement];
|
||||
|
||||
// Invoke the subscription callback
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
controller.subscribeToObjects([telemetryObject]);
|
||||
mockConductor.follow.andReturn(true);
|
||||
mockTelemetryAPI.subscribe.mostRecentCall.args[1](mockTelemetry);
|
||||
|
||||
// Get elements that controller is now exposing
|
||||
elements = controller.getElements();
|
||||
waitsFor(function () {
|
||||
return controller.digesting === false;
|
||||
}, "digest to complete", 100);
|
||||
|
||||
runs(function () {
|
||||
// Get elements that controller is now exposing
|
||||
elements = controller.getElements();
|
||||
|
||||
// Formatted values should be available
|
||||
expect(elements[0].value).toEqual("Formatted 200");
|
||||
});
|
||||
|
||||
// Formatted values should be available
|
||||
expect(elements[0].value).toEqual("Formatted 10");
|
||||
expect(elements[1].value).toEqual("Formatted 42");
|
||||
expect(elements[2].value).toEqual("Formatted 31.42");
|
||||
});
|
||||
|
||||
it("updates elements styles when grid size changes", function () {
|
||||
@ -291,6 +421,9 @@ define(
|
||||
});
|
||||
|
||||
it("listens for drop events", function () {
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
mockScope.model = testModel;
|
||||
|
||||
// Layout should position panels according to
|
||||
// where the user dropped them, so it needs to
|
||||
// listen for drop events.
|
||||
@ -339,14 +472,29 @@ define(
|
||||
});
|
||||
|
||||
it("unsubscribes when destroyed", function () {
|
||||
// Make an object available
|
||||
findWatch('domainObject')(mockDomainObject);
|
||||
// Also verify precondition
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
// Destroy the scope
|
||||
findOn('$destroy')();
|
||||
// Should have unsubscribed
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
|
||||
var dunzo = false;
|
||||
var unsubscribe = jasmine.createSpy('unsubscribe');
|
||||
|
||||
mockTelemetryAPI.subscribe.andReturn(unsubscribe);
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
findWatch("domainObject")(mockDomainObject).then(function () {
|
||||
dunzo = true;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return dunzo;
|
||||
}, "Telemetry fetched", 200);
|
||||
|
||||
runs(function () {
|
||||
expect(unsubscribe).not.toHaveBeenCalled();
|
||||
// Destroy the scope
|
||||
findOn('$destroy')();
|
||||
|
||||
//Check that the same unsubscribe function returned by the
|
||||
expect(unsubscribe.calls.length).toBe(mockChildren.length);
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes its grid size", function () {
|
||||
@ -427,92 +575,102 @@ define(
|
||||
|
||||
describe("on display bounds changes", function () {
|
||||
var testBounds;
|
||||
var boundsChangeCallback;
|
||||
var objectOne;
|
||||
var objectTwo;
|
||||
|
||||
beforeEach(function () {
|
||||
testBounds = { start: 123, end: 321 };
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
mockScope.model = testModel;
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
findWatch("model.modified")(testModel.modified);
|
||||
findWatch("model.composition")(mockScope.model.composition);
|
||||
findOn('telemetry:display:bounds')({}, testBounds);
|
||||
boundsChangeCallback = mockConductor.on.mostRecentCall.args[1];
|
||||
objectOne = {};
|
||||
objectTwo = {};
|
||||
controller.telemetryObjects = [
|
||||
objectOne,
|
||||
objectTwo
|
||||
];
|
||||
spyOn(controller, "fetchHistoricalData");
|
||||
controller.fetchHistoricalData.andCallThrough();
|
||||
});
|
||||
|
||||
it("issues new requests", function () {
|
||||
expect(mockHandle.request).toHaveBeenCalled();
|
||||
it("registers a bounds change listener", function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("bounds", jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("requests only a single point", function () {
|
||||
expect(mockHandle.request.mostRecentCall.args[0].size)
|
||||
.toEqual(1);
|
||||
mockConductor.follow.andReturn(false);
|
||||
boundsChangeCallback(testBounds);
|
||||
expect(mockTelemetryAPI.request.calls.length).toBe(2);
|
||||
|
||||
mockTelemetryAPI.request.calls.forEach(function (call) {
|
||||
expect(call.args[1].size).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("and after data has been received", function () {
|
||||
var mockSeries,
|
||||
testValue;
|
||||
it("Does not fetch historical data on tick", function () {
|
||||
mockConductor.follow.andReturn(true);
|
||||
boundsChangeCallback(testBounds);
|
||||
expect(mockTelemetryAPI.request.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
testValue = 12321;
|
||||
describe("on receipt of telemetry", function () {
|
||||
var mockTelemetryObject;
|
||||
var testValue;
|
||||
var testElement;
|
||||
|
||||
mockSeries = jasmine.createSpyObj('series', [
|
||||
'getPointCount',
|
||||
'getDomainValue',
|
||||
'getRangeValue'
|
||||
]);
|
||||
mockSeries.getPointCount.andReturn(1);
|
||||
mockSeries.getRangeValue.andReturn(testValue);
|
||||
beforeEach(function () {
|
||||
mockTelemetryObject = {
|
||||
identifier: {
|
||||
key: '12345'
|
||||
}
|
||||
};
|
||||
testValue = 30;
|
||||
testElement = {};
|
||||
|
||||
// Fire the callback associated with the request
|
||||
mockHandle.request.mostRecentCall.args[1](
|
||||
mockHandle.getTelemetryObjects()[0],
|
||||
mockSeries
|
||||
);
|
||||
controller.elementProxiesById = {};
|
||||
controller.elementProxiesById['12345'] = [testElement];
|
||||
controller.elementProxies = [testElement];
|
||||
});
|
||||
|
||||
it("updates displayed values from historical telemetry", function () {
|
||||
spyOn(controller, "updateView");
|
||||
controller.updateView.andCallThrough();
|
||||
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve([{
|
||||
time: 100,
|
||||
value: testValue
|
||||
}]));
|
||||
|
||||
controller.fetchHistoricalData([mockTelemetryObject]);
|
||||
|
||||
waitsFor(function () {
|
||||
return controller.digesting === false;
|
||||
});
|
||||
|
||||
it("updates displayed values", function () {
|
||||
runs(function () {
|
||||
expect(controller.updateView).toHaveBeenCalled();
|
||||
expect(controller.getElements()[0].value)
|
||||
.toEqual("Formatted " + testValue);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
it("reflects limit status", function () {
|
||||
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
|
||||
controller.updateView(mockTelemetryObject, [{
|
||||
time: 100,
|
||||
value: testValue
|
||||
}]);
|
||||
|
||||
it("reflects limit status", function () {
|
||||
var elements;
|
||||
|
||||
mockHandle.getDatum.andReturn({});
|
||||
mockHandle.getTelemetryObjects().forEach(function (mockObject) {
|
||||
var id = mockObject.getId(),
|
||||
mockLimitCapability =
|
||||
jasmine.createSpyObj('limit-' + id, ['evaluate']);
|
||||
|
||||
mockObject.getCapability.andCallFake(function (key) {
|
||||
return (key === 'limit') && mockLimitCapability;
|
||||
waitsFor(function () {
|
||||
return controller.digesting === false;
|
||||
});
|
||||
|
||||
mockLimitCapability.evaluate
|
||||
.andReturn({ cssClass: 'alarm-' + id });
|
||||
runs(function () {
|
||||
// Limit-based CSS classes should be available
|
||||
expect(controller.getElements()[0].cssClass).toEqual("alarm-a");
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
mockScope.model = testModel;
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
findWatch("model.modified")(1);
|
||||
findWatch("model.composition")(mockScope.model.composition);
|
||||
|
||||
// Invoke the subscription callback
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
// Get elements that controller is now exposing
|
||||
elements = controller.getElements();
|
||||
|
||||
// Limit-based CSS classes should be available
|
||||
expect(elements[0].cssClass).toEqual("alarm-a");
|
||||
expect(elements[1].cssClass).toEqual("alarm-b");
|
||||
expect(elements[2].cssClass).toEqual("alarm-c");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
Reference in New Issue
Block a user