Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #88 - Add support for integration with OpenMCT #89

Merged
merged 1 commit into from
Nov 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions ait/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,49 @@ def handle():
__setResponseToEventStream()
yield 'data: %s\n\n' % json.dumps(msg)

@App.route('/tlm/realtime/openmct')
def handle():
"""Return telemetry packets in realtime to client"""
session = Sessions.create()
pad = bytearray(1)
wsock = bottle.request.environ.get('wsgi.websocket')

if not wsock:
bottle.abort(400, 'Expected WebSocket request.')

try:
tlmdict = ait.core.tlm.getDefaultDict()
while not wsock.closed:
try:
uid, data = session.telemetry.popleft(timeout=30)
pkt_defn = None
for k, v in tlmdict.iteritems():
if v.uid == uid:
pkt_defn = v
break
else:
continue

wsock.send(json.dumps({
'packet': pkt_defn.name,
'data': ait.core.tlm.Packet(pkt_defn, data=data).toJSON()
}))

except IndexError:
# If no telemetry has been received by the GUI
# server after timeout seconds, "probe" the client
# websocket connection to make sure it's still
# active and if so, keep it alive. This is
# accomplished by sending a packet with an ID of
# zero and no packet data. Packet ID zero with no
# data is ignored by AIT GUI client-side
# Javascript code.

if not wsock.closed:
wsock.send(pad + struct.pack('>I', 0))
except geventwebsocket.WebSocketError:
pass


@App.route('/tlm/realtime')
def handle():
Expand Down
188 changes: 188 additions & 0 deletions ait/gui/openmct_adapter/ait_integration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT)
* Bespoke Link to Instruments and Small Satellites (BLISS)
*
* Copyright 2018, by the California Institute of Technology. ALL RIGHTS
* RESERVED. United States Government Sponsorship acknowledged. Any
* commercial use must be negotiated with the Office of Technology Transfer
* at the California Institute of Technology.
*
* This software may be subject to U.S. export control laws. By accepting
* this software, the user agrees to comply with all applicable U.S. export
* laws and regulations. User has the responsibility to obtain export licenses,
* or other export authority as may be required before exporting such
* information to foreign countries or providing access to foreign persons.
*/

/*
* Example functions for AIT integration with OpenMCT. The below code can be
* used in place of the examples provided in the OpenMCT Tutorial so that
* AIT telemetry can be viewed in real time.
*
* https://github.com/nasa/openmct-tutorial
*
* Important Notes and Setup:
* - The current AIT backend implementation does not support the OpenMCT
* historical data query implementation. To work around this, ensure
* that the historical data endpoint in OpenMCT returns an empty
* dataset for any queried point. The AITHistoricalTelemetryPlugin
* function calls the default OpenMCT Tutorial historical endpoint.
* - The below example code queries for the AIT telemetry dictionary
* in the getDictionary call so the example implementation follows along
* with the OpenMCT tutorial. The endpoint that it calls can be added
* to the example OpenMCT server file (server.js). The code for this
* can be found in example_ait_telem_fetch.js.
* - Include the below code into OpenMCT by including this file in the
* example index.html file and installing the various components.
*
* <script src="ait_integration.js"></script>
*
* openmct.install(AITIntegration());
* openmct.install(AITHistoricalTelemetryPlugin());
* openmct.install(AITRealtimeTelemetryPlugin());
*
* How to run:
* Assuming the above "Important Notes and Setup" have been handled and
* the installation instructions in the OpenMCT Tutorial have been run:
*
* 1) Run `ait-gui 8081` to start the AIT backend
* 2) Run `npm start` to run the OpenMCT server
* 3) Point your browser to localhost:8080
*/

let AIT_HOST = 'localhost'
let AIT_PORT = 8081
let tlmdict = getDictionary()

function getDictionary() {
return http.get('/telemdict')
.then(function (result) {
return result.data
});
};

function AITIntegration() {
let objectProvider = {
get: function (identifier) {
return tlmdict.then(function (dictionary) {
if (identifier.key === 'spacecraft') {
return {
identifier: identifier,
name: dictionary.name,
type: 'folder',
location: 'ROOT'
};
} else {
let measurement = dictionary.measurements.filter(function (m) {
return m.key === identifier.key;
})[0];
return {
identifier: identifier,
name: measurement.name,
type: 'telemetry',
telemetry: {
values: measurement.values
},
location: 'taxonomy:spacecraft'
};
}
});
}
};

let compositionProvider = {
appliesTo: function (domainObject) {
return domainObject.identifier.namespace === 'taxonomy' &&
domainObject.type === 'folder';
},
load: function (domainObject) {
return tlmdict
.then(function (dictionary) {
return dictionary.measurements.map(function (m) {
return {
namespace: 'taxonomy',
key: m.key
};
});
});
}
};

return function install(openmct) {
openmct.objects.addRoot({
namespace: 'taxonomy',
key: 'spacecraft'
});

openmct.objects.addProvider('taxonomy', objectProvider);

openmct.composition.addProvider(compositionProvider);

openmct.types.addType('telemetry', {
name: 'Telemetry Point',
description: 'Spacecraft Telemetry point',
cssClass: 'icon-telemetry'
});
};
};

function AITHistoricalTelemetryPlugin() {
return function install (openmct) {
let provider = {
supportsRequest: function (domainObject) {
return domainObject.type === 'telemetry';
},
request: function (domainObject, options) {
let url = '/history/' + domainObject.identifier.key +
'?start=' + options.start + '&end=' + options.end;

return http.get(url)
.then(function (resp) {
return resp.data
});
}
};

openmct.telemetry.addProvider(provider);
}
};

function AITRealtimeTelemetryPlugin() {
return function install(openmct) {
let socket = new WebSocket(
'ws://' + AIT_HOST + ':' + AIT_PORT + '/tlm/realtime/openmct');
let listener = {};

socket.onmessage = function (event) {
let now = Date.now()

let data = JSON.parse(event.data)
let packet = data['packet']
for (let p in data['data']) {
let point = {
'id': packet + '.' + p,
'timestamp': Date.now(),
'value': data['data'][p]
}

if (listener[point.id]) {
listener[point.id](point);
}
}
};

let provider = {
supportsSubscribe: function (domainObject) {
return domainObject.type === 'telemetry';
},
subscribe: function (domainObject, callback) {
listener[domainObject.identifier.key] = callback;
return function unsubscribe() {
delete listener[domainObject.identifier.key];
};
}
};

openmct.telemetry.addProvider(provider);
}
};
76 changes: 76 additions & 0 deletions ait/gui/openmct_adapter/example_ait_telem_fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Advanced Multi-Mission Operations System (AMMOS) Instrument Toolkit (AIT)
* Bespoke Link to Instruments and Small Satellites (BLISS)
*
* Copyright 2018, by the California Institute of Technology. ALL RIGHTS
* RESERVED. United States Government Sponsorship acknowledged. Any
* commercial use must be negotiated with the Office of Technology Transfer
* at the California Institute of Technology.
*
* This software may be subject to U.S. export control laws. By accepting
* this software, the user agrees to comply with all applicable U.S. export
* laws and regulations. User has the responsibility to obtain export licenses,
* or other export authority as may be required before exporting such
* information to foreign countries or providing access to foreign persons.
*/

let AIT_HOST = 'localhost'
let AIT_PORT = 8081

function AITTlmDict(body) {
let retJSON = JSON.parse(body);
let dict = {}
dict['name'] = 'AIT Telemetry'
dict['key'] = 'ait_telemetry_dictionary'
dict['measurements'] = []

Object.keys(retJSON).forEach((packet) => {
let fieldDefns = retJSON[packet]["fields"]
Object.keys(fieldDefns).forEach((key) => {
let fDict = {
'key': packet + '.' + key,
'name': packet + ' ' + fieldDefns[key]['name']
}

let vals = [
{
"key": "value",
"name": "Value",
"hints": {
"range": 1
}
},
{
"key": "utc",
"source": "timestamp",
"name": "Timestamp",
"format": "utc",
"hints": {
"domain": 1
}
}
]
fDict['values'] = vals
dict['measurements'].push(fDict)
})
})
return dict
};

app.get('/telemdict', function (req, res) {
let url = 'http://' + AIT_HOST + ':' + AIT_PORT + '/tlm/dict'
http.get(url, function(response){
var body = '';

response.on('data', function(chunk){
body += chunk;
});

response.on('end', function() {
let dict = AITTlmDict(body)
res.json(dict)
});
}).on('error', function(e){
console.log("Got an error: ", e);
});
});