Simon Hunt

GUI -- Added websock open listeners mechanism.

- made event handler structure parsing a little cleverer.

Change-Id: I15120dabd0aade28e6ee3680faf96f408aed1450
......@@ -30,10 +30,13 @@
sid = 0, // event sequence identifier
handlers = {}, // event handler bindings
pendingEvents = [], // events TX'd while socket not up
host, // web socket host
url, // web socket URL
clusterNodes = [], // ONOS instances data for failover
clusterIndex = -1, // the instance to which we are connected
connectRetries = 0;
connectRetries = 0, // limit our attempts at reconnecting
openListeners = {}, // registered listeners for websocket open()
nextListenerId = 1; // internal ID for open listeners
// =======================
// === Bootstrap Handler
......@@ -55,7 +58,7 @@
// === Web socket callbacks
function handleOpen() {
$log.info('Web socket open');
$log.info('Web socket open - ', url);
vs.hide();
$log.debug('Sending ' + pendingEvents.length + ' pending event(s)...');
......@@ -66,6 +69,7 @@
connectRetries = 0;
wsUp = true;
informListeners(host, url);
}
// Handles the specified (incoming) message using handler bindings.
......@@ -78,7 +82,7 @@
$log.error('Message.data is not valid JSON', msgEvent.data, e);
return;
}
$log.debug(' *Rx* >> ', ev.event, ev.payload);
$log.debug(' << *Rx* ', ev.event, ev.payload);
if (h = handlers[ev.event]) {
try {
......@@ -129,12 +133,17 @@
return ip;
}
function informListeners(host, url) {
angular.forEach(openListeners, function(lsnr) {
lsnr.cb(host, url);
});
}
function _send(ev) {
$log.debug(' *Tx* >> ', ev.event, ev.payload);
ws.send(JSON.stringify(ev));
}
// ===================
// === API Functions
......@@ -145,12 +154,14 @@
// Currently supported opts:
// wsport: web socket port (other than default 8181)
// server: if defined, is the server address to use
function createWebSocket(opts, server) {
// host: if defined, is the host address to use
function createWebSocket(opts, _host_) {
var wsport = (opts && opts.wsport) || null;
webSockOpts = opts; // preserved for future calls
url = ufs.wsUrl('core', wsport, server);
host = _host_ || $loc.host();
url = ufs.wsUrl('core', wsport, _host_);
$log.debug('Attempting to open websocket to: ' + url);
ws = wsock.newWebSocket(url);
......@@ -163,15 +174,18 @@
return url;
}
// Binds the specified message handlers.
// keys are the event IDs
// values are the API on which the handler function is a property
// Binds the message handlers to their message type (event type) as
// specified in the given map. Note that keys are the event IDs; values
// are either:
// * the event handler function, or
// * an API object which has an event handler for the key
//
function bindHandlers(handlerMap) {
var m = d3.map(handlerMap),
dups = [];
m.forEach(function (eventId, api) {
var fn = fs.isF(api[eventId]);
var fn = fs.isF(api) || fs.isF(api[eventId]);
if (!fn) {
$log.warn(eventId + ' handler not a function');
return;
......@@ -198,6 +212,28 @@
});
}
function addOpenListener(callback) {
var id = nextListenerId++,
cb = fs.isF(callback),
o = { id: id, cb: cb };
if (cb) {
openListeners[id] = o;
} else {
$log.error('WSS.addOpenListener(): callback not a function');
o.error = 'No callback defined';
}
return o;
}
function removeOpenListener(lsnr) {
var id = lsnr && lsnr.id,
o = openListeners[id];
if (o) {
delete openListeners[id];
}
}
// Formulates an event message and sends it via the web-socket.
// If the websocket is not up yet, we store it in a pending list.
function sendEvent(evType, payload) {
......@@ -230,17 +266,15 @@
wsock = _wsock_;
vs = _vs_;
// TODO: Consider how to simplify handler structure
// Now it is an object of key -> object that has a method named 'key'.
bindHandlers({
bootstrap: builtinHandlers
});
bindHandlers(builtinHandlers);
return {
resetSid: resetSid,
createWebSocket: createWebSocket,
bindHandlers: bindHandlers,
unbindHandlers: unbindHandlers,
addOpenListener: addOpenListener,
removeOpenListener: removeOpenListener,
sendEvent: sendEvent
};
}
......
......@@ -30,7 +30,8 @@
var $log, wss, tps, tis, tfs, tss, tts;
// internal state
var handlerMap;
var handlerMap,
openListener;
// ==========================
......@@ -58,6 +59,14 @@
};
}
function wsOpen(host, url) {
$log.debug('TOPO: web socket open - cluster node:', host, 'URL:', url);
// TODO: request "instanceUpdate" events for all instances
// this should give us the updated uiAttached icon placement
}
angular.module('ovTopo')
.factory('TopoEventService',
['$log', '$location', 'WebSocketService',
......@@ -76,6 +85,7 @@
createHandlerMap();
function start() {
openListener = wss.addOpenListener(wsOpen);
wss.bindHandlers(handlerMap);
wss.sendEvent('topoStart');
wss.sendEvent('requestSummary');
......@@ -85,6 +95,8 @@
function stop() {
wss.sendEvent('topoStop');
wss.unbindHandlers(handlerMap);
wss.removeOpenListener(openListener);
openListener = null;
$log.debug('topo comms stopped');
}
......
......@@ -46,7 +46,7 @@ describe('factory: fw/remote/websocket.js', function () {
it('should define api functions', function () {
expect(fs.areFunctions(wss, [
'resetSid', 'createWebSocket', 'bindHandlers', 'unbindHandlers',
'sendEvent'
'addOpenListener', 'removeOpenListener', 'sendEvent'
])).toBeTruthy();
});
......