mirror of
https://github.com/nasa/openmct.git
synced 2024-12-20 05:37:53 +00:00
[Tutorials] Add telemetry tutorial
This commit is contained in:
parent
d3e5d95d6b
commit
9c4e17bfab
90
tutorials/telemetry/bundle.js
Normal file
90
tutorials/telemetry/bundle.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
define([
|
||||||
|
'legacyRegistry',
|
||||||
|
'./src/ExampleTelemetryServerAdapter',
|
||||||
|
'./src/ExampleTelemetryInitializer',
|
||||||
|
'./src/ExampleTelemetryModelProvider'
|
||||||
|
], function (
|
||||||
|
legacyRegistry,
|
||||||
|
ExampleTelemetryServerAdapter,
|
||||||
|
ExampleTelemetryInitializer,
|
||||||
|
ExampleTelemetryModelProvider
|
||||||
|
) {
|
||||||
|
legacyRegistry.register("tutorials/telemetry", {
|
||||||
|
"name": "Example Telemetry Adapter",
|
||||||
|
"extensions": {
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"name": "Spacecraft",
|
||||||
|
"key": "example.spacecraft",
|
||||||
|
"glyph": "o"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Subsystem",
|
||||||
|
"key": "example.subsystem",
|
||||||
|
"glyph": "o",
|
||||||
|
"model": { "composition": [] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Measurement",
|
||||||
|
"key": "example.measurement",
|
||||||
|
"glyph": "T",
|
||||||
|
"model": { "telemetry": {} },
|
||||||
|
"telemetry": {
|
||||||
|
"source": "example.source",
|
||||||
|
"domains": [
|
||||||
|
{
|
||||||
|
"name": "Time",
|
||||||
|
"key": "timestamp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"roots": [
|
||||||
|
{
|
||||||
|
"id": "example:sc",
|
||||||
|
"priority": "preferred",
|
||||||
|
"model": {
|
||||||
|
"type": "example.spacecraft",
|
||||||
|
"name": "My Spacecraft",
|
||||||
|
"composition": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"key": "example.adapter",
|
||||||
|
"implementation": "ExampleTelemetryServerAdapter.js",
|
||||||
|
"depends": [ "$q", "EXAMPLE_WS_URL" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constants": [
|
||||||
|
{
|
||||||
|
"key": "EXAMPLE_WS_URL",
|
||||||
|
"priority": "fallback",
|
||||||
|
"value": "ws://localhost:8081"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"runs": [
|
||||||
|
{
|
||||||
|
"implementation": "ExampleTelemetryInitializer.js",
|
||||||
|
"depends": [ "example.adapter", "objectService" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"provides": "modelService",
|
||||||
|
"type": "provider",
|
||||||
|
"implementation": "ExampleTelemetryModelProvider.js",
|
||||||
|
"depends": [ "example.adapter", "$q" ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provides": "telemetryService",
|
||||||
|
"type": "provider",
|
||||||
|
"implementation": "ExampleTelemetryProvider.js",
|
||||||
|
"depends": [ "example.adapter", "$q" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
47
tutorials/telemetry/src/ExampleTelemetryInitializer.js
Normal file
47
tutorials/telemetry/src/ExampleTelemetryInitializer.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
define(
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var TAXONOMY_ID = "example:sc",
|
||||||
|
PREFIX = "example_tlm:";
|
||||||
|
|
||||||
|
function ExampleTelemetryInitializer(adapter, objectService) {
|
||||||
|
// Generate a domain object identifier for a dictionary element
|
||||||
|
function makeId(element) {
|
||||||
|
return PREFIX + element.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the dictionary is available, add all subsystems
|
||||||
|
// to the composition of My Spacecraft
|
||||||
|
function initializeTaxonomy(dictionary) {
|
||||||
|
// Get the top-level container for dictionary objects
|
||||||
|
// from a group of domain objects.
|
||||||
|
function getTaxonomyObject(domainObjects) {
|
||||||
|
return domainObjects[TAXONOMY_ID];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate
|
||||||
|
function populateModel(taxonomyObject) {
|
||||||
|
return taxonomyObject.useCapability(
|
||||||
|
"mutation",
|
||||||
|
function (model) {
|
||||||
|
model.name =
|
||||||
|
dictionary.name;
|
||||||
|
model.composition =
|
||||||
|
dictionary.subsystems.map(makeId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up My Spacecraft, and populate it accordingly.
|
||||||
|
objectService.getObjects([TAXONOMY_ID])
|
||||||
|
.then(getTaxonomyObject)
|
||||||
|
.then(populateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.dictionary().then(initializeTaxonomy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExampleTelemetryInitializer;
|
||||||
|
}
|
||||||
|
);
|
78
tutorials/telemetry/src/ExampleTelemetryModelProvider.js
Normal file
78
tutorials/telemetry/src/ExampleTelemetryModelProvider.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
define(
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var PREFIX = "example_tlm:",
|
||||||
|
FORMAT_MAPPINGS = {
|
||||||
|
float: "number",
|
||||||
|
integer: "number",
|
||||||
|
string: "string"
|
||||||
|
};
|
||||||
|
|
||||||
|
function ExampleTelemetryModelProvider(adapter, $q) {
|
||||||
|
var modelPromise, empty = $q.when({});
|
||||||
|
|
||||||
|
// Check if this model is in our dictionary (by prefix)
|
||||||
|
function isRelevant(id) {
|
||||||
|
return id.indexOf(PREFIX) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a domain object identifier by adding a prefix
|
||||||
|
function makeId(element) {
|
||||||
|
return PREFIX + element.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create domain object models from this dictionary
|
||||||
|
function buildTaxonomy(dictionary) {
|
||||||
|
var models = {};
|
||||||
|
|
||||||
|
// Create & store a domain object model for a measurement
|
||||||
|
function addMeasurement(measurement) {
|
||||||
|
var format = FORMAT_MAPPINGS[measurement.type];
|
||||||
|
models[makeId(measurement)] = {
|
||||||
|
type: "example.measurement",
|
||||||
|
name: measurement.name,
|
||||||
|
telemetry: {
|
||||||
|
key: measurement.identifier,
|
||||||
|
ranges: [{
|
||||||
|
key: "value",
|
||||||
|
name: "Value",
|
||||||
|
units: measurement.units,
|
||||||
|
format: format
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create & store a domain object model for a subsystem
|
||||||
|
function addSubsystem(subsystem) {
|
||||||
|
var measurements =
|
||||||
|
(subsystem.measurements || []);
|
||||||
|
models[makeId(subsystem)] = {
|
||||||
|
type: "example.subsystem",
|
||||||
|
name: subsystem.name,
|
||||||
|
composition: measurements.map(makeId)
|
||||||
|
};
|
||||||
|
measurements.forEach(addMeasurement);
|
||||||
|
}
|
||||||
|
|
||||||
|
(dictionary.subsystems || []).forEach(addSubsystem);
|
||||||
|
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin generating models once the dictionary is available
|
||||||
|
modelPromise = adapter.dictionary().then(buildTaxonomy);
|
||||||
|
|
||||||
|
return {
|
||||||
|
getModels: function (ids) {
|
||||||
|
// Return models for the dictionary only when they
|
||||||
|
// are relevant to the request.
|
||||||
|
return ids.some(isRelevant) ? modelPromise : empty;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExampleTelemetryModelProvider;
|
||||||
|
}
|
||||||
|
);
|
80
tutorials/telemetry/src/ExampleTelemetryProvider.js
Normal file
80
tutorials/telemetry/src/ExampleTelemetryProvider.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
define(
|
||||||
|
['./ExampleTelemetrySeries'],
|
||||||
|
function (ExampleTelemetrySeries) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var SOURCE = "example.source";
|
||||||
|
|
||||||
|
function ExampleTelemetryProvider(adapter, $q) {
|
||||||
|
var subscribers = {};
|
||||||
|
|
||||||
|
// Used to filter out requests for telemetry
|
||||||
|
// from some other source
|
||||||
|
function matchesSource(request) {
|
||||||
|
return (request.source === SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for data, notify subscribers
|
||||||
|
adapter.listen(function (message) {
|
||||||
|
var packaged = {};
|
||||||
|
packaged[SOURCE] = {};
|
||||||
|
packaged[SOURCE][message.id] =
|
||||||
|
new ExampleTelemetrySeries([message.value]);
|
||||||
|
(subscribers[message.id] || []).forEach(function (cb) {
|
||||||
|
cb(packaged);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
requestTelemetry: function (requests) {
|
||||||
|
var packaged = {},
|
||||||
|
relevantReqs = requests.filter(matchesSource);
|
||||||
|
|
||||||
|
// Package historical telemetry that has been received
|
||||||
|
function addToPackage(history) {
|
||||||
|
packaged[SOURCE][history.id] =
|
||||||
|
new ExampleTelemetrySeries(history.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve telemetry for a specific measurement
|
||||||
|
function handleRequest(request) {
|
||||||
|
var key = request.key;
|
||||||
|
return adapter.history(key).then(addToPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
packaged[SOURCE] = {};
|
||||||
|
return $q.all(relevantReqs.map(handleRequest))
|
||||||
|
.then(function () { return packaged; });
|
||||||
|
},
|
||||||
|
subscribe: function (callback, requests) {
|
||||||
|
var keys = requests.filter(matchesSource)
|
||||||
|
.map(function (req) { return req.key; });
|
||||||
|
|
||||||
|
function notCallback(cb) {
|
||||||
|
return cb !== callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsubscribe(key) {
|
||||||
|
subscribers[key] =
|
||||||
|
(subscribers[key] || []).filter(notCallback);
|
||||||
|
if (subscribers[key].length < 1) {
|
||||||
|
adapter.unsubscribe(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.forEach(function (key) {
|
||||||
|
subscribers[key] = subscribers[key] || [];
|
||||||
|
adapter.subscribe(key);
|
||||||
|
subscribers[key].push(callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
keys.forEach(unsubscribe);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExampleTelemetryProvider;
|
||||||
|
}
|
||||||
|
);
|
23
tutorials/telemetry/src/ExampleTelemetrySeries.js
Normal file
23
tutorials/telemetry/src/ExampleTelemetrySeries.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function ExampleTelemetrySeries(data) {
|
||||||
|
return {
|
||||||
|
getPointCount: function () {
|
||||||
|
return data.length;
|
||||||
|
},
|
||||||
|
getDomainValue: function (index) {
|
||||||
|
return (data[index] || {}).timestamp;
|
||||||
|
},
|
||||||
|
getRangeValue: function (index) {
|
||||||
|
return (data[index] || {}).value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExampleTelemetrySeries;
|
||||||
|
}
|
||||||
|
);
|
60
tutorials/telemetry/src/ExampleTelemetryServerAdapter.js
Normal file
60
tutorials/telemetry/src/ExampleTelemetryServerAdapter.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function ExampleTelemetryServerAdapter($q, wsUrl) {
|
||||||
|
var ws = new WebSocket(wsUrl),
|
||||||
|
histories = {},
|
||||||
|
listeners = [],
|
||||||
|
dictionary = $q.defer();
|
||||||
|
|
||||||
|
// Handle an incoming message from the server
|
||||||
|
ws.onmessage = function (event) {
|
||||||
|
var message = JSON.parse(event.data);
|
||||||
|
|
||||||
|
switch (message.type) {
|
||||||
|
case "dictionary":
|
||||||
|
dictionary.resolve(message.value);
|
||||||
|
break;
|
||||||
|
case "history":
|
||||||
|
histories[message.id].resolve(message);
|
||||||
|
delete histories[message.id];
|
||||||
|
break;
|
||||||
|
case "data":
|
||||||
|
listeners.forEach(function (listener) {
|
||||||
|
listener(message);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Request dictionary once connection is established
|
||||||
|
ws.onopen = function () {
|
||||||
|
ws.send("dictionary");
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
dictionary: function () {
|
||||||
|
return dictionary.promise;
|
||||||
|
},
|
||||||
|
history: function (id) {
|
||||||
|
histories[id] = histories[id] || $q.defer();
|
||||||
|
ws.send("history " + id);
|
||||||
|
return histories[id].promise;
|
||||||
|
},
|
||||||
|
subscribe: function (id) {
|
||||||
|
ws.send("subscribe " + id);
|
||||||
|
},
|
||||||
|
unsubscribe: function (id) {
|
||||||
|
ws.send("unsubscribe " + id);
|
||||||
|
},
|
||||||
|
listen: function (callback) {
|
||||||
|
listeners.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExampleTelemetryServerAdapter;
|
||||||
|
}
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user