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

557 lines (518 loc) 19.1 kB
/*globals define*/ /*jshint node:true, browser:true*/ /** * Generated by PluginGenerator 0.14.0 from webgme on Sun Apr 10 2016 20:47:31 GMT-0700 (PDT). */ define([ 'plugin/PluginConfig', 'plugin/PluginBase', 'text!./metadata.json', 'ejs', // for ejs templates 'common/util/xmljsonconverter', // used to save model as json 'plugin/TimingAnalysis/TimingAnalysis/Templates/Templates', // 'remote-utils/remote-utils', 'webgme-to-json/webgme-to-json', 'rosmod/processor', 'q' ], function ( PluginConfig, PluginBase, pluginMetadata, ejs, Converter, TEMPLATES, utils, webgmeToJson, processor, Q) { 'use strict'; pluginMetadata = JSON.parse(pluginMetadata); /** * Initializes a new instance of TimingAnalysis. * @class * @augments {PluginBase} * @classdesc This class represents the plugin TimingAnalysis. * @constructor */ var TimingAnalysis = function () { // Call base class' constructor. PluginBase.call(this); this.pluginMetadata = pluginMetadata; this.FILES = { 'cpn': 'cpn.ejs' }; }; TimingAnalysis.metadata = pluginMetadata; // Prototypal inheritance from PluginBase. TimingAnalysis.prototype = Object.create(PluginBase.prototype); TimingAnalysis.prototype.constructor = TimingAnalysis; TimingAnalysis.prototype.notify = function(level, msg) { var self = this; var prefix = self.projectId + '::' + self.projectName + '::' + level + '::'; var max_msg_len = 100; if (level=='error') self.logger.error(msg); else if (level=='debug') self.logger.debug(msg); else if (level=='info') self.logger.info(msg); else if (level=='warning') self.logger.warn(msg); self.createMessage(self.activeNode, msg, level); if (msg.length < max_msg_len) self.sendNotification(prefix+msg); else { var splitMsgs = utils.chunkString(msg, max_msg_len); splitMsgs.map(function(splitMsg) { self.sendNotification(prefix+splitMsg); }); } }; /** * Main function for the plugin to execute. This will perform the execution. * Notes: * - Always log with the provided logger.[error,warning,info,debug]. * - Do NOT put any user interaction logic UI, etc. inside this method. * - callback always has to be called even if error happened. * * @param {function(string, plugin.PluginResult)} callback - the result callback */ TimingAnalysis.prototype.main = function (callback) { // Use self to access core, project, result, logger etc from PluginBase. // These are all instantiated at this point. var self = this; // Default fails self.result.success = false; // What did the user select for our configuration? var currentConfig = self.getCurrentConfig(); self.generateCPNAnalysis = currentConfig.generateCPN; self.returnZip = currentConfig.returnZip; self.runningOnClient = false; self.artifacts = {}; if (typeof WebGMEGlobal !== 'undefined') { self.runningOnClient = true; } // the active node for this plugin is software -> project var projectNode = self.activeNode; self.projectName = self.core.getAttribute(projectNode, 'name'); if (!self.runningOnClient) { var path = require('path'); // Setting up variables that will be used by various functions of this plugin self.gen_dir = path.join(process.cwd(), 'generated', self.project.projectId, self.branchName, self.projectName); } self.projectModel = {}; // will be filled out by loadProjectModel (and associated functions) webgmeToJson.notify = function(level, msg) {self.notify(level, msg);} utils.notify = function(level, msg) {self.notify(level, msg);} webgmeToJson.loadModel(self.core, self.rootNode, projectNode, true) .then(function (projectModel) { processor.processModel(projectModel); self.projectModel = projectModel.root; self.objectDict = projectModel.objects; }) .then(function () { return self.generateArtifacts(); }) .then(function () { return self.saveArtifactsOnServer(); //downloads template and renders files to server }) .then(function () { return self.returnArtifactsToUser(); }) .then(function () { self.result.setSuccess(true); callback(null, self.result); }) .catch(function (err) { self.notify('error', err); self.result.setSuccess(false); callback(err, self.result); }) .done(); }; TimingAnalysis.prototype.generateArtifacts = function() { var self = this; if ( !self.generateCPNAnalysis ) { return; } self.notify('info','Generating CPN Model.'); var deployments_folder = self.projectModel.Deployments_list; if (deployments_folder && deployments_folder[0] && deployments_folder[0].Deployment_list) { deployments_folder[0].Deployment_list.map(function(deployment) { self.notify('info', 'Parsing Deployment: ' + deployment.name); var timer_tokens = '1`[\n'; var clock_tokens = '1`[\n'; var interaction_tokens = '1`[\n'; var component_thread_tokens = '1`[\n'; var message_queue_tokens = '1`[\n'; var hardware_num = 1 var msg_map = {}; var srv_map = {}; var component_hardware_map = {}; if (deployment.Container_list) { // build up the maps deployment.Container_list.map(function(container) { if (container.Node_list) { container.Node_list.map(function(node) { if (node.Component_list) { node.Component_list.map(function(component) { component_hardware_map[component.name] = "CPU_" + hardware_num; // possibly refactor the code below to use Array.prototype.filter() if (component.Publisher_list) { component.Publisher_list.map(function(publisher) { var msg = publisher.Message.AdvertisedName; if (!msg_map[msg]) { msg_map[msg] = { publishers: [], subscribers: [] }; } msg_map[msg].publishers.push(publisher); }); } if (component.Subscriber_list) { component.Subscriber_list.map(function(subscriber) { var msg = subscriber.Message.AdvertisedName; if (!msg_map[msg]) { msg_map[msg] = { publishers: [], subscribers: [] }; } msg_map[msg].subscribers.push(subscriber); }); } if (component.Client_list) { component.Client_list.map(function(client) { var srv = client.Service.AdvertisedName; if (!srv_map[srv]) { srv_map[srv] = { clients: [], servers: [] }; } srv_map[srv].clients.push(client); }); } if (component.Server_list) { component.Server_list.map(function(server) { var srv = server.Service.AdvertisedName; if (!srv_map[srv]) { srv_map[srv] = { clients: [], servers: [] }; } srv_map[srv].servers.push(server); }); } }); // end Component_list.map(component) } hardware_num += 1; // done with the nodes on this container }); // end Node_list.map(node) } }); // end Container_list.map(container) hardware_num = 1; // Now generate the tokens based on the maps deployment.Container_list.map(function(container) { // Clock Tokens check if (clock_tokens != '1`[\n') { clock_tokens += ',\n'; } clock_tokens += '{node="CPU_' + hardware_num.toString() + '", value=0, next_tick=4000}'; // Component Thread Tokens check if (component_thread_tokens != '1`[\n') { component_thread_tokens += ',\n'; } component_thread_tokens += '{node="CPU_' + hardware_num.toString() + '", threads=['; // Component Message Queue Tokens check if (message_queue_tokens != '1`[\n') { message_queue_tokens += ',\n'; } // 1`[{node=&quot;BBB_111&quot;, cmql=[{component=&quot;Component_1&quot;, scheme=PFIFO, queue=[]}]}] message_queue_tokens += '{node="CPU_' + hardware_num.toString() + '", cmql=['; if (container.Node_list) { container.Node_list.map(function(node) { var node_priority = node.Priority; if (node.Component_list) { node.Component_list.map(function(component) { if (component_thread_tokens.slice(-1) != '[') component_thread_tokens += ', '; component_thread_tokens += '{node="CPU_' + hardware_num.toString() + '", component="' + component.name + '", priority=' + node_priority + ', operation=[]}'; if (message_queue_tokens.slice(-1) != '[') message_queue_tokens += ', '; message_queue_tokens += '{component="' + component.name + '", scheme=' + component.SchedulingScheme + ', queue=[]}'; if (component.Timer_list) { component.Timer_list.map(function(timer) { // Timer Tokens check if (timer_tokens != '1`[\n') { timer_tokens += ',\n'; } timer_tokens += '{node="CPU_' + hardware_num.toString() + '", period=' + timer.Period * 1000000 + ', offset=0, operation=' + '{node="CPU_' + hardware_num.toString() + '", component="' + component.name + '", operation="' + timer.name + '_operation"' + ', priority=' + timer.Priority + ', deadline=' + timer.Deadline * 1000000 + ', enqueue_time=0, steps=['; var re = /([A-Z]*)\s([\w\_\.\(\)]+);/g; var result = re.exec(timer.AbstractBusinessLogic); while(result != null) { var port_type = result[1]; var wcet = 0; if (port_type == "LOCAL") { var wcet = result[2]; if (timer_tokens.slice(-1) != '[') timer_tokens += ', '; timer_tokens += '{kind="LOCAL", port="LOCAL", unblk=[], '+ 'exec_time=0, duration=' + wcet * 1000000 + '}'; } else if (port_type == "RMI") { if (timer_tokens.slice(-1) != '[') timer_tokens += ', '; timer_tokens += '{kind="CLIENT", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } else if (port_type == "PUBLISH") { if (timer_tokens.slice(-1) != '[') timer_tokens += ', '; timer_tokens += '{kind="PUBLISHER", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } result = re.exec(timer.AbstractBusinessLogic); } timer_tokens += ']}}'; }); // end Timer_list.map(timer) } if (component.Publisher_list) { component.Publisher_list.map(function(publisher) { // publisher tokens var topic = publisher.Message.AdvertisedName; if (msg_map[topic].subscribers) { msg_map[topic].subscribers.map(function(subscriber) { var subCompName= self.objectDict[subscriber.parentPath].name; if (interaction_tokens != '1`[\n') { interaction_tokens += ',\n'; } interaction_tokens += '{node="CPU_' + hardware_num.toString() + '", port="' + publisher.name + '", operation={node="' + component_hardware_map[subCompName] + '", component="' + subCompName + '", operation="' + subscriber.name + '_operation", priority=' + subscriber.Priority + ', deadline=' + subscriber.Deadline * 1000000 + ', enqueue_time=0, steps=['; var re = /([A-Z]*)\s([\w\_\.\(\)]+);/g; var result = re.exec(subscriber.AbstractBusinessLogic); while(result != null) { var port_type = result[1]; var wcet = 0; if (port_type == "LOCAL") { var wcet = result[2]; if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="LOCAL", port="LOCAL", unblk=[], '+ 'exec_time=0, duration=' + wcet * 1000000 + '}'; } else if (port_type == "RMI") { if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="CLIENT", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } else if (port_type == "PUBLISH") { if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="PUBLISHER", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } result = re.exec(subscriber.AbstractBusinessLogic); } interaction_tokens += ']}}'; }); // end msg_map[topic].subscribers.map(subscriber) } }); // end Publisher_list.map(publisher) } if (component.Client_list) { component.Client_list.map(function(client) { // client tokens var service = client.Service.AdvertisedName; if (srv_map[service].servers) { srv_map[service].servers.map(function(server) { var serverCompName = self.objectDict[server.parentPath].name; if (interaction_tokens != '1`[\n') { interaction_tokens += ',\n'; } interaction_tokens += '{node="CPU_' + hardware_num.toString() + '", port="' + client.name + '", operation={node="' + component_hardware_map[serverCompName] + '", component="' + serverCompName + '", operation="' + server.name + '_operation", ' + 'priority=' + server.Priority + ', deadline=' + server.Deadline * 1000000 + ', enqueue_time=0, steps=['; var re = /([A-Z]*)\s([\w\_\.\(\)]+);/g; var result = re.exec(server.AbstractBusinessLogic); while(result != null) { var port_type = result[1]; var wcet = 0; if (port_type == "LOCAL") { var wcet = result[2]; if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="LOCAL", port="LOCAL", unblk=[], '+ 'exec_time=0, duration=' + wcet * 1000000 + '}'; } else if (port_type == "RMI") { if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="CLIENT", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } else if (port_type == "PUBLISH") { if (interaction_tokens.slice(-1) != '[') interaction_tokens += ', '; interaction_tokens += '{kind="PUBLISHER", port="' + result[2] + '", unblk=[], exec_time=0, duration=0}'; } result = re.exec(server.AbstractBusinessLogic); } var n = interaction_tokens.lastIndexOf("unblk=[]"); interaction_tokens = interaction_tokens.slice(0, n) + interaction_tokens.slice(n).replace( "unblk=[]", 'unblk=[{node="CPU_' + hardware_num.toString() + '", component="' + component.name + '", port="' + client.name + '"}]' ); interaction_tokens += ']}}'; }); // end srv_map[service].servers.map(server) } }); // end component.Client_list.map(client) } }); // end Component_list.map(component) } }); // end Node_list.map(node) component_thread_tokens += ']}'; message_queue_tokens += ']}'; hardware_num += 1 } }); // end Container_list.map(container) } // finish the token sequences for this deployment clock_tokens += '\n]'; timer_tokens += '\n]'; interaction_tokens += '\n]'; component_thread_tokens += '\n]'; message_queue_tokens += '\n]'; var cpn = deployment.name + '_Analysis_Model.cpn', cpnTemplate = TEMPLATES[self.FILES['cpn']]; self.artifacts[cpn] = ejs.render(cpnTemplate, {'clock_tokens' : clock_tokens, 'timer_tokens' : timer_tokens, 'interaction_tokens' : interaction_tokens, 'component_thread_tokens' : component_thread_tokens, 'message_queue_tokens' : message_queue_tokens}); }); // end Deployment_list.map(deployment) } }; TimingAnalysis.prototype.saveArtifactsOnServer = function() { var self = this; if ( self.runningOnClient ) { return; } var path = require('path'); var dir = path.join(self.gen_dir,'cpn'); var mkdirp = require('mkdirp'); var child_process = require('child_process'); // clear out any previous project files child_process.execSync('rm -rf ' + utils.sanitizePath(dir)); mkdirp.sync(dir); // Get the dummy cpn template var file_url = 'https://github.com/rosmod/rosmod-cpn/releases/download/v1.0.0/cpn.zip'; return utils.wgetAndUnzipLibrary(file_url, self.gen_dir) .then(function() { self.notify('info', 'Downloaded CPN template'); var filendir = require('filendir'); var fileKeys = Object.keys(self.artifacts); var tasks = fileKeys.map(function(key) { var fname = path.join(dir,key), data = self.artifacts[key]; return new Promise(function(resolve, reject) { filendir.writeFile(fname, data, function(err) { if (err) { self.logger.error(err); reject(err); } else { resolve(); } }); }); }); return Q.all(tasks); }) .then(function() { self.notify('info', 'Generated CPN'); }); }; TimingAnalysis.prototype.returnArtifactsToUser = function() { var self = this; if (!self.returnZip) { self.notify('info', 'User did not request the artifacts to be returned.'); return; } self.notify('info', 'Returning artifacts to user.'); if (self.runningOnClient) { // we are running on client; couldn't download template and can't zip var fileNames = Object.keys(self.artifacts); var tasks = fileNames.map(function(fileName) { return self.blobClient.putFile(fileName, self.artifacts[fileName]) .then(function (hash) { self.result.addArtifact(hash); }); }); return Q.all(tasks); } else { // we are running on server, downloaded template and need to zip; return new Promise(function(resolve, reject) { var zlib = require('zlib'), tar = require('tar'), fstream = require('fstream'), path = require('path'), input = path.join(self.gen_dir,'cpn'); var bufs = []; var packer = tar.Pack() .on('error', function(e) { reject(e); }); var gzipper = zlib.Gzip() .on('error', function(e) { reject(e); }) .on('data', function(d) { bufs.push(d); }) .on('end', function() { var buf = Buffer.concat(bufs); var name = self.projectName + '+CPN'; self.blobClient.putFile(name+'.tar.gz',buf) .then(function (hash) { self.result.addArtifact(hash); resolve(); }) .catch(function(err) { reject(err); }) .done(); }); var reader = fstream.Reader({ 'path': input, 'type': 'Directory' }) .on('error', function(e) { reject(e); }); reader .pipe(packer) .pipe(gzipper); }) .then(function() { self.notify('info', 'Created archive.'); }); } }; return TimingAnalysis; });