UNPKG

webgme-rosmod

Version:

This repository contains ROSMOD developed for WebGME. ROSMOD is a web-based, collaborative, modeling and execution environment for distributed embedded applications built using ROS http://rosmod.rcps.isis.vanderbilt.edu

651 lines (565 loc) 19.6 kB
/*globals define, WebGMEGlobal*/ /*jshint browser: true*/ /** * Generated by VisualizerGenerator 1.7.0 from webgme on Tue Sep 27 2016 23:15:32 GMT-0500 (Central Daylight Time). */ define([ 'cytoscape/cytoscape', 'cytoscape-cose-bilkent/cytoscape-cose-bilkent', 'text!./style2.css', 'handlebars/dist/handlebars', 'blob-util/dist/blob-util.min', 'q', 'css!./styles/CommVizWidget.css'], function ( cytoscape, regCose, styleText, Handlebars, blobUtil, Q) { 'use strict'; regCose( cytoscape ); var CommVizWidget, WIDGET_CLASS = 'comm-viz'; CommVizWidget = function (logger, container, client) { this._logger = logger.fork('Widget'); this._el = container; this._client = client; // set widget class this._el.addClass(WIDGET_CLASS); this._el.append('<div id="cy"></div>'); this._cy_container = this._el.find('#cy'); this._initialize(); this._logger.debug('ctor finished'); }; CommVizWidget.prototype._initialize = function () { var width = this._el.width(), height = this._el.height(), self = this; this.nodes = {}; this.dependencies = { 'nodes': {}, 'edges': {} }; this.waitingNodes = {}; this._cytoscape_options = { container: this._cy_container, style: styleText, // interaction options: minZoom: 1e-50, maxZoom: 1e50, zoomingEnabled: true, userZoomingEnabled: true, panningEnabled: true, userPanningEnabled: true, boxSelectionEnabled: false, selectionType: 'single', touchTapThreshold: 8, desktopTapThreshold: 4, autolock: false, autoungrabify: false, autounselectify: false, // rendering options: headless: false, styleEnabled: true, hideEdgesOnViewport: false, hideLabelsOnViewport: false, textureOnViewport: false, motionBlur: false, motionBlurOpacity: 0.2, wheelSensitivity: 1, pixelRatio: 'auto' }; var self = this; this._layout_options = { 'name': 'cose-bilkent', // Called on `layoutready` ready: function () { self._cy.nodes().forEach(function(node) { var p = node.position(); node.data('orgPos',{ x: p.x, y: p.y }); }); }, // Called on `layoutstop` stop: function () { self._cy.nodes().forEach(function(node) { var p = node.position(); node.data('orgPos',{ x: p.x, y: p.y }); }); }, // Whether to fit the network view after when done fit: true, // Padding on fit padding: 10, // Whether to enable incremental mode randomize: true, // Node repulsion (non overlapping) multiplier nodeRepulsion: 5500, // 4500 // Ideal edge (non nested) length idealEdgeLength: 100, // 50 // Divisor to compute edge forces edgeElasticity: 0.45, // Nesting factor (multiplier) to compute ideal edge length for nested edges nestingFactor: 0.1, // Gravity force (constant) gravity: 0.1, // 0.25 // Maximum number of iterations to perform numIter: 2500, // For enabling tiling tile: false, // true // Type of layout animation. The option set is {'during', 'end', false} animate: 'end', // Represents the amount of the vertical space to put between the zero degree members during the tiling operation(can also be a function) tilingPaddingVertical: 10, // Represents the amount of the horizontal space to put between the zero degree members during the tiling operation(can also be a function) tilingPaddingHorizontal: 10, // Gravity range (constant) for compounds gravityRangeCompound: 1.5, // Gravity force (constant) for compounds gravityCompound: 1.0, // Gravity range (constant) gravityRange: 3.8 }; this._cytoscape_options.layout = self._layout_options; this._cy = cytoscape(self._cytoscape_options); // copied from the wine and cheese demo var infoTemplate = Handlebars.compile([ '<p class="ac-name">{{name}}</p>', '<p class="ac-node-type"><i class="fa fa-info-circle"></i> {{NodeTypeFormatted}} {{#if Type}}({{Type}}){{/if}}</p>', '{{#if Milk}}<p class="ac-milk"><i class="fa fa-angle-double-right"></i> {{Milk}}</p>{{/if}}', '{{#if Country}}<p class="ac-country"><i class="fa fa-map-marker"></i> {{Country}}</p>{{/if}}', '<p class="ac-more"><i class="fa fa-external-link"></i> <a target="_blank" href="https://duckduckgo.com/?q={{name}}">More information</a></p>' ].join('')); var layoutPadding = 50; var layoutDuration = 500; function highlight( node ){ var nhood = node.closedNeighborhood(); self._cy.batch(function(){ self._cy.elements("edge").not( nhood ).removeClass('highlighted').addClass('faded'); self._cy.elements('[NodeType="Component"]').not( nhood ).removeClass('highlighted').addClass('faded'); self._cy.elements('[NodeType="Message"]').not( nhood ).removeClass('highlighted').addClass('faded'); self._cy.elements('[NodeType="Service"]').not( nhood ).removeClass('highlighted').addClass('faded'); nhood.removeClass('faded').addClass('highlighted'); var npos = node.position(); var w = window.innerWidth; var h = window.innerHeight; self._cy.stop().animate({ fit: { eles: self._cy.elements(), padding: layoutPadding } }, { duration: layoutDuration }).delay( layoutDuration, function(){ nhood.layout({ name: 'concentric', padding: layoutPadding, animate: true, animationDuration: layoutDuration, boundingBox: { x1: npos.x - w/2, x2: npos.x + w/2, y1: npos.y - w/2, y2: npos.y + w/2 }, fit: true, concentric: function( n ){ if( node.id() === n.id() ){ return 2; } else { return 1; } }, levelWidth: function(){ return 1; } }); } ); }); } function clear(){ self._cy.batch(function(){ self._cy.$('.highlighted').forEach(function(n){ n.animate({ position: n.data('orgPos') }); }); self._cy.elements().removeClass('highlighted').removeClass('faded'); }); } function showNodeInfo( node ){ $('#info').html( infoTemplate( node.data() ) ).show(); } function hideNodeInfo(){ $('#info').hide(); } self._cy.on('free', 'node', function( e ){ var n = e.cyTarget; var p = n.position(); n.data('orgPos', { x: p.x, y: p.y }); }); self._cy.on('add', _.debounce(self.reLayout.bind(self), 250)); self._cy.on('tap', function(){ $('#search').blur(); }); self._cy.on('select', 'node', function(e){ var node = this; if (node) { var id = node.id(); WebGMEGlobal.State.registerActiveSelection([id]); highlight( node ); showNodeInfo( node ); } }); self._cy.on('unselect', 'node', function(e){ var node = this; clear(); hideNodeInfo(); self.onZoomClicked(); }); WebGMEGlobal.State.registerActiveSelection([]); }; /* * * * * * * * Display Functions * * * * * * * */ function download(filename, text) { var element = document.createElement('a'); var imgData = text.split(',')[1]; // after the comma is the actual image data blobUtil.base64StringToBlob( imgData.toString() ).then(function(blob) { var blobURL = blobUtil.createObjectURL(blob); element.setAttribute('href', blobURL); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); }).catch(function(err) { console.log('Couldnt make blob from image!'); console.log(err); }); } CommVizWidget.prototype.onZoomClicked = function() { var self = this; var layoutPadding = 50; self._cy.fit( self._cy.elements(), layoutPadding); }; CommVizWidget.prototype._addSplitPanelToolbarBtns = function(toolbarEl) { var self = this; // BUTTON EVENT HANDLERS var printEl = [ '<span id="print" class="split-panel-toolbar-btn fa fa-print">', '</span>', ].join('\n'); var zoomEl = [ '<span id="zoom" class="split-panel-toolbar-btn fa fa-home">', '</span>', ].join('\n'); var layoutEl = [ '<span id="layout" class="split-panel-toolbar-btn fa fa-random">', '</span>', ].join('\n'); var enableLoggingEl = [ '<span id="enableLogging" class="split-panel-toolbar-btn fa fa-toggle-on">', '</span>', ].join('\n'); var disableLoggingEl = [ '<span id="disableLogging" class="split-panel-toolbar-btn fa fa-toggle-off">', '</span>', ].join('\n'); toolbarEl.append(printEl); toolbarEl.append(zoomEl); toolbarEl.append(layoutEl); toolbarEl.append(enableLoggingEl); toolbarEl.append(disableLoggingEl); toolbarEl.find('#print').on('click', function(){ var png = self._cy.png({ full: true, scale: 6, bg: 'white' }); download( 'CommViz.png', png ); }); toolbarEl.find('#zoom').on('click', function(){ self.onZoomClicked(); }); toolbarEl.find('#layout').on('click', function(){ self.reLayout(); }); toolbarEl.find('#enableLogging').on('click', function(){ self.enableLogging(); }); toolbarEl.find('#disableLogging').on('click', function(){ self.disableLogging(); }); }; CommVizWidget.prototype.getAllComponents = function() { var self = this; return Object.keys(self.nodes).filter(function(path) { var n = self.nodes[path]; return n.type == 'Component'; }); }; CommVizWidget.prototype.enableLogging = function() { var self = this; var compPaths = self.getAllComponents(); self._client.startTransaction('Enabling logging for all components.'); compPaths.map(function(k) { var id = k; if (self.nodes[id]) { self._client.setAttribute(id, 'Logging_TraceEnable', true); self._client.setAttribute(id, 'Logging_UserEnable', true); } }); self._client.completeTransaction(); }; CommVizWidget.prototype.disableLogging = function() { var self = this; var compPaths = self.getAllComponents(); self._client.startTransaction('Disabling logging for all components.'); compPaths.map(function(k) { var id = k; if (self.nodes[id]) { self._client.setAttribute(id, 'Logging_TraceEnable', false); self._client.setAttribute(id, 'Logging_UserEnable', false); } }); self._client.completeTransaction(); }; CommVizWidget.prototype.onWidgetContainerResize = function (width, height) { this._cy.resize(); }; var connectionTypes = [ 'Publisher', 'Subscriber', 'Client', 'Server', 'Action Client', 'Action Server' ]; CommVizWidget.prototype.checkDependencies = function(desc) { var self = this; var depsMet = false; if (desc) { // dependencies will always be either parentId (nodes & edges) or connection (edges) var deps = []; if (desc.parentId && !self.nodes[desc.parentId]) { deps.push(desc.parentId); } if (desc.connection && !self.nodes[desc.connection]) { deps.push(desc.connection); } depsMet = (deps.length == 0); if (!depsMet) { if (connectionTypes.indexOf(desc.type) > -1) self.dependencies.edges[desc.id] = deps; else self.dependencies.nodes[desc.id] = deps; } } return depsMet; }; CommVizWidget.prototype.updateDependencies = function() { var self = this; var nodePaths = Object.keys(self.dependencies.nodes); var edgePaths = Object.keys(self.dependencies.edges); // create any nodes whose depenencies are fulfilled now nodePaths.map(function(nodePath) { var depPaths = self.dependencies.nodes[nodePath]; if (depPaths && depPaths.length > 0) { depPaths = depPaths.filter(function(objPath) { return self.nodes[objPath] == undefined; }); if (!depPaths.length) { var desc = self.waitingNodes[nodePath]; self.waitingNodes[nodePath] = undefined; self.dependencies.nodes[nodePath] = undefined; self.createNode(desc); } else { self.dependencies.nodes[nodePath] = depPaths; } } else { self.dependencies.nodes[nodePath] = undefined; } }); // Create any edges whose dependencies are fulfilled now edgePaths.map(function(edgePath) { var depPaths = self.dependencies.edges[edgePath]; if (depPaths && depPaths.length > 0) { depPaths = depPaths.filter(function(objPath) { return self.nodes[objPath] == undefined; }); if (!depPaths.length) { var connDesc = self.waitingNodes[edgePath]; self.waitingNodes[edgePath] = undefined; self.dependencies.edges[edgePath] = undefined; self.createEdge(connDesc); } else { self.dependencies.edges[edgePath] = depPaths; } } else { self.dependencies.edges[edgePath] = undefined; } }); }; CommVizWidget.prototype.reLayout = function() { var self = this; self._cy.layout(self._layout_options); }; CommVizWidget.prototype.getDescData = function(desc) { var self = this; var data = null; if (desc.isConnection) { var from = self.nodes[desc.from]; var to = self.nodes[desc.to]; if (from && to) { data = { id: from.id + to.id, type: desc.type, interaction: desc.type, source: from.id, target: to.id }; } } else { data = { id: desc.id, parent: desc.parentId, type: desc.type, NodeType: desc.type, userLogging: (desc.Logging_UserEnable && !desc.Logging_TraceEnable) ? "True" : "False", traceLogging: (desc.Logging_TraceEnable && !desc.Logging_UserEnable) ? "True" : "False", allLogging: (desc.Loging_UserEnable && desc.Logging_TraceEnable) ? "True" : "False", noLogging: (desc.Loging_UserEnable || desc.Logging_TraceEnable) ? "False" : "True", name: desc.name, label: desc.name, orgPos: null }; } return data; }; // pub, sub, client, server are all edges CommVizWidget.prototype.createEdge = function(desc) { var self = this; var data = self.getDescData( desc ); if (data) { self._cy.add({ group: 'edges', data: data }); self.nodes[desc.id] = desc; self.updateDependencies(); } }; // nodes are components, nodes, containers, messages, services CommVizWidget.prototype.createNode = function(desc) { var self = this; var data = self.getDescData( desc ); if (data) { self._cy.add({ group: 'nodes', data: data }); self.nodes[desc.id] = desc; self.updateDependencies(); } }; // Adding/Removing/Updating items CommVizWidget.prototype.addNode = function (desc) { var self = this; if (desc && desc.type != 'Deployment') { var depsMet = self.checkDependencies(desc); // Add node to a table of nodes if (connectionTypes.indexOf(desc.type) > -1) { // if this is actually an edge if (depsMet) { // ready to make edge self.createEdge(desc); } else { // missing some dependencies (either parentId or connection) self.waitingNodes[desc.id] = desc; } } else { if (depsMet) { // ready to make node self.createNode(desc); } else { self.waitingNodes[desc.id] = desc; } } } }; CommVizWidget.prototype.removeNode = function (gmeId) { var self = this; if (self._el && self.nodes) { var idTag = gmeId.replace(/\//gm, "\\/"); var desc = self.nodes[gmeId]; if (desc) { if (!desc.isConnection) { delete self.dependencies.nodes[gmeId]; self._cy.$('#'+idTag).neighborhood().forEach(function(ele) { if (ele && ele.isEdge()) { var edgeId = ele.data( 'id' ); var edgeDesc = self.nodes[edgeId]; self.checkDependencies(edgeDesc); } }); } else { delete self.dependencies.edges[gmeId]; } delete self.nodes[gmeId]; delete self.waitingNodes[gmeId]; self._cy.remove("#" + idTag); self.updateDependencies(); } } }; CommVizWidget.prototype.updateNode = function (desc) { var self = this; if (self._el && self.nodes && desc) { var oldDesc = self.nodes[desc.id]; if (oldDesc) { var idTag = desc.id.replace(/\//gm, "\\/"); var cyNode = self._cy.$('#'+idTag); if (desc.isConnection) { if (desc.src != oldDesc.src || desc.dst != oldDesc.dst) { self._cy.remove('#' + idTag); self.checkDependencies( desc ); self.updateDependencies(); } else { cyNode.data( self.getDescData(desc) ); } } else { cyNode.data( self.getDescData(desc) ); } } self.nodes[desc.id] = desc; } }; /* * * * * * * * Visualizer event handlers * * * * * * * */ CommVizWidget.prototype.onBackgroundDblClick = function () { }; /* * * * * * * * Visualizer life cycle callbacks * * * * * * * */ CommVizWidget.prototype.destroy = function () { this._el.remove(); delete this._el; delete this.nodes; this._cy.destroy(); }; CommVizWidget.prototype.onActivate = function () { //console.log('CommVizWidget has been activated'); }; CommVizWidget.prototype.onDeactivate = function () { //console.log('CommVizWidget has been deactivated'); }; return CommVizWidget; });