mirror of
https://github.com/nasa/openmct.git
synced 2024-12-20 21:53:08 +00:00
Merge branch 'api-tutorials' into api-type-proto
This commit is contained in:
commit
09a833f524
127
tutorial-server/app.js
Normal file
127
tutorial-server/app.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*global require,process,console*/
|
||||||
|
|
||||||
|
var CONFIG = {
|
||||||
|
port: 8081,
|
||||||
|
dictionary: "dictionary.json",
|
||||||
|
interval: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var WebSocketServer = require('ws').Server,
|
||||||
|
fs = require('fs'),
|
||||||
|
wss = new WebSocketServer({ port: CONFIG.port }),
|
||||||
|
dictionary = JSON.parse(fs.readFileSync(CONFIG.dictionary, "utf8")),
|
||||||
|
spacecraft = {
|
||||||
|
"prop.fuel": 77,
|
||||||
|
"prop.thrusters": "OFF",
|
||||||
|
"comms.recd": 0,
|
||||||
|
"comms.sent": 0,
|
||||||
|
"pwr.temp": 245,
|
||||||
|
"pwr.c": 8.15,
|
||||||
|
"pwr.v": 30
|
||||||
|
},
|
||||||
|
histories = {},
|
||||||
|
listeners = [];
|
||||||
|
|
||||||
|
function updateSpacecraft() {
|
||||||
|
spacecraft["prop.fuel"] = Math.max(
|
||||||
|
0,
|
||||||
|
spacecraft["prop.fuel"] -
|
||||||
|
(spacecraft["prop.thrusters"] === "ON" ? 0.5 : 0)
|
||||||
|
);
|
||||||
|
spacecraft["pwr.temp"] = spacecraft["pwr.temp"] * 0.985
|
||||||
|
+ Math.random() * 0.25 + Math.sin(Date.now());
|
||||||
|
spacecraft["pwr.c"] = spacecraft["pwr.c"] * 0.985;
|
||||||
|
spacecraft["pwr.v"] = 30 + Math.pow(Math.random(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateTelemetry() {
|
||||||
|
var timestamp = Date.now(), sent = 0;
|
||||||
|
Object.keys(spacecraft).forEach(function (id) {
|
||||||
|
var state = { timestamp: timestamp, value: spacecraft[id] };
|
||||||
|
histories[id] = histories[id] || []; // Initialize
|
||||||
|
histories[id].push(state);
|
||||||
|
spacecraft["comms.sent"] += JSON.stringify(state).length;
|
||||||
|
});
|
||||||
|
listeners.forEach(function (listener) {
|
||||||
|
listener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
updateSpacecraft();
|
||||||
|
generateTelemetry();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleConnection(ws) {
|
||||||
|
var subscriptions = {}, // Active subscriptions for this connection
|
||||||
|
handlers = { // Handlers for specific requests
|
||||||
|
dictionary: function () {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "dictionary",
|
||||||
|
value: dictionary
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
subscribe: function (id) {
|
||||||
|
subscriptions[id] = true;
|
||||||
|
},
|
||||||
|
unsubscribe: function (id) {
|
||||||
|
delete subscriptions[id];
|
||||||
|
},
|
||||||
|
history: function (id) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "history",
|
||||||
|
id: id,
|
||||||
|
value: histories[id]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function notifySubscribers() {
|
||||||
|
Object.keys(subscriptions).forEach(function (id) {
|
||||||
|
var history = histories[id];
|
||||||
|
if (history) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "data",
|
||||||
|
id: id,
|
||||||
|
value: history[history.length - 1]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for requests
|
||||||
|
ws.on('message', function (message) {
|
||||||
|
var parts = message.split(' '),
|
||||||
|
handler = handlers[parts[0]];
|
||||||
|
if (handler) {
|
||||||
|
handler.apply(handlers, parts.slice(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop sending telemetry updates for this connection when closed
|
||||||
|
ws.on('close', function () {
|
||||||
|
listeners = listeners.filter(function (listener) {
|
||||||
|
return listener !== notifySubscribers;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Notify subscribers when telemetry is updated
|
||||||
|
listeners.push(notifySubscribers);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
setInterval(update, CONFIG.interval);
|
||||||
|
|
||||||
|
wss.on('connection', handleConnection);
|
||||||
|
|
||||||
|
console.log("Example spacecraft running on port ");
|
||||||
|
console.log("Press Enter to toggle thruster state.");
|
||||||
|
process.stdin.on('data', function (data) {
|
||||||
|
spacecraft['prop.thrusters'] =
|
||||||
|
(spacecraft['prop.thrusters'] === "OFF") ? "ON" : "OFF";
|
||||||
|
console.log("Thrusters " + spacecraft["prop.thrusters"]);
|
||||||
|
});
|
||||||
|
}());
|
66
tutorial-server/dictionary.json
Normal file
66
tutorial-server/dictionary.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"name": "Example Spacecraft",
|
||||||
|
"identifier": "sc",
|
||||||
|
"subsystems": [
|
||||||
|
{
|
||||||
|
"name": "Propulsion",
|
||||||
|
"identifier": "prop",
|
||||||
|
"measurements": [
|
||||||
|
{
|
||||||
|
"name": "Fuel",
|
||||||
|
"identifier": "prop.fuel",
|
||||||
|
"units": "kilograms",
|
||||||
|
"type": "float"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Thrusters",
|
||||||
|
"identifier": "prop.thrusters",
|
||||||
|
"units": "None",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Communications",
|
||||||
|
"identifier": "comms",
|
||||||
|
"measurements": [
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"identifier": "comms.recd",
|
||||||
|
"units": "bytes",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sent",
|
||||||
|
"identifier": "comms.sent",
|
||||||
|
"units": "bytes",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Power",
|
||||||
|
"identifier": "pwr",
|
||||||
|
"measurements": [
|
||||||
|
{
|
||||||
|
"name": "Generator Temperature",
|
||||||
|
"identifier": "pwr.temp",
|
||||||
|
"units": "\u0080C",
|
||||||
|
"type": "float"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Generator Current",
|
||||||
|
"identifier": "pwr.c",
|
||||||
|
"units": "A",
|
||||||
|
"type": "float"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Generator Voltage",
|
||||||
|
"identifier": "pwr.v",
|
||||||
|
"units": "V",
|
||||||
|
"type": "float"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
66
tutorials/bargraph/bundle.js
Normal file
66
tutorials/bargraph/bundle.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
define([
|
||||||
|
'legacyRegistry',
|
||||||
|
'./src/controllers/BarGraphController'
|
||||||
|
], function (
|
||||||
|
legacyRegistry,
|
||||||
|
BarGraphController
|
||||||
|
) {
|
||||||
|
legacyRegistry.register("tutorials/bargraph", {
|
||||||
|
"name": "Bar Graph",
|
||||||
|
"description": "Provides the Bar Graph view of telemetry elements.",
|
||||||
|
"extensions": {
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"name": "Bar Graph",
|
||||||
|
"key": "example.bargraph",
|
||||||
|
"glyph": "H",
|
||||||
|
"templateUrl": "templates/bargraph.html",
|
||||||
|
"needs": [ "telemetry" ],
|
||||||
|
"delegation": true,
|
||||||
|
"editable": true,
|
||||||
|
"toolbar": {
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Low",
|
||||||
|
"property": "low",
|
||||||
|
"required": true,
|
||||||
|
"control": "textfield",
|
||||||
|
"size": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Middle",
|
||||||
|
"property": "middle",
|
||||||
|
"required": true,
|
||||||
|
"control": "textfield",
|
||||||
|
"size": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High",
|
||||||
|
"property": "high",
|
||||||
|
"required": true,
|
||||||
|
"control": "textfield",
|
||||||
|
"size": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stylesheets": [
|
||||||
|
{
|
||||||
|
"stylesheetUrl": "css/bargraph.css"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"controllers": [
|
||||||
|
{
|
||||||
|
"key": "BarGraphController",
|
||||||
|
"implementation": BarGraphController,
|
||||||
|
"depends": [ "$scope", "telemetryHandler" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
35
tutorials/bargraph/res/templates/bargraph.html
Normal file
35
tutorials/bargraph/res/templates/bargraph.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<div class="example-bargraph" ng-controller="BarGraphController">
|
||||||
|
<div class="example-tick-labels">
|
||||||
|
<div ng-repeat="value in [low, middle, high] track by $index"
|
||||||
|
class="example-tick-label"
|
||||||
|
style="bottom: {{ toPercent(value) }}%">
|
||||||
|
{{value}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example-graph-area">
|
||||||
|
<div ng-repeat="telemetryObject in telemetryObjects"
|
||||||
|
style="left: {{barWidth * $index}}%; width: {{barWidth}}%"
|
||||||
|
class="example-bar-holder">
|
||||||
|
<div class="example-bar"
|
||||||
|
ng-style="{
|
||||||
|
bottom: getBottom(telemetryObject) + '%',
|
||||||
|
top: getTop(telemetryObject) + '%'
|
||||||
|
}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="bottom: {{ toPercent(middle) }}%"
|
||||||
|
class="example-graph-tick">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example-bar-labels">
|
||||||
|
<div ng-repeat="telemetryObject in telemetryObjects"
|
||||||
|
style="left: {{barWidth * $index}}%; width: {{barWidth}}%"
|
||||||
|
class="example-bar-holder example-label">
|
||||||
|
<mct-representation key="'label'"
|
||||||
|
mct-object="telemetryObject">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
75
tutorials/bargraph/src/controllers/BarGraphController.js
Normal file
75
tutorials/bargraph/src/controllers/BarGraphController.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
define(function () {
|
||||||
|
function BarGraphController($scope, telemetryHandler) {
|
||||||
|
var handle;
|
||||||
|
|
||||||
|
// Expose configuration constants directly in scope
|
||||||
|
function exposeConfiguration() {
|
||||||
|
$scope.low = $scope.configuration.low;
|
||||||
|
$scope.middle = $scope.configuration.middle;
|
||||||
|
$scope.high = $scope.configuration.high;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate a default value in the configuration
|
||||||
|
function setDefault(key, value) {
|
||||||
|
if ($scope.configuration[key] === undefined) {
|
||||||
|
$scope.configuration[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter-setter for configuration properties (for view proxy)
|
||||||
|
function getterSetter(property) {
|
||||||
|
return function (value) {
|
||||||
|
value = parseFloat(value);
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
$scope.configuration[property] = value;
|
||||||
|
exposeConfiguration();
|
||||||
|
}
|
||||||
|
return $scope.configuration[property];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add min/max defaults
|
||||||
|
setDefault('low', -1);
|
||||||
|
setDefault('middle', 0);
|
||||||
|
setDefault('high', 1);
|
||||||
|
exposeConfiguration($scope.configuration);
|
||||||
|
|
||||||
|
// Expose view configuration options
|
||||||
|
if ($scope.selection) {
|
||||||
|
$scope.selection.proxy({
|
||||||
|
low: getterSetter('low'),
|
||||||
|
middle: getterSetter('middle'),
|
||||||
|
high: getterSetter('high')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert value to a percent between 0-100
|
||||||
|
$scope.toPercent = function (value) {
|
||||||
|
var pct = 100 * (value - $scope.low) /
|
||||||
|
($scope.high - $scope.low);
|
||||||
|
return Math.min(100, Math.max(0, pct));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get bottom and top (as percentages) for current value
|
||||||
|
$scope.getBottom = function (telemetryObject) {
|
||||||
|
var value = handle.getRangeValue(telemetryObject);
|
||||||
|
return $scope.toPercent(Math.min($scope.middle, value));
|
||||||
|
};
|
||||||
|
$scope.getTop = function (telemetryObject) {
|
||||||
|
var value = handle.getRangeValue(telemetryObject);
|
||||||
|
return 100 - $scope.toPercent(Math.max($scope.middle, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use the telemetryHandler to get telemetry objects here
|
||||||
|
handle = telemetryHandler.handle($scope.domainObject, function () {
|
||||||
|
$scope.telemetryObjects = handle.getTelemetryObjects();
|
||||||
|
$scope.barWidth =
|
||||||
|
100 / Math.max(($scope.telemetryObjects).length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Release subscriptions when scope is destroyed
|
||||||
|
$scope.$on('$destroy', handle.unsubscribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BarGraphController;
|
||||||
|
});
|
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;
|
||||||
|
}
|
||||||
|
);
|
@ -4,7 +4,7 @@ define([
|
|||||||
], function (
|
], function (
|
||||||
legacyRegistry,
|
legacyRegistry,
|
||||||
TodoController
|
TodoController
|
||||||
) {
|
) {
|
||||||
legacyRegistry.register("tutorials/todo", {
|
legacyRegistry.register("tutorials/todo", {
|
||||||
"name": "To-do Plugin",
|
"name": "To-do Plugin",
|
||||||
"description": "Allows creating and editing to-do lists.",
|
"description": "Allows creating and editing to-do lists.",
|
||||||
|
Loading…
Reference in New Issue
Block a user