mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57: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