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
499 lines (447 loc) • 17.8 kB
JavaScript
/*globals define*/
/*jshint node:true, browser:true*/
/**
* Generated by PluginGenerator 0.14.0 from webgme on Tue Apr 12 2016 13:46:59 GMT-0500 (CDT).
*/
define([
'plugin/PluginConfig',
'plugin/PluginBase',
'text!./metadata.json',
'ejs', // for ejs templates
'common/util/xmljsonconverter', // used to save model as json
'plugin/GenerateDocumentation/GenerateDocumentation/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 GenerateDocumentation.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin GenerateDocumentation.
* @constructor
*/
var GenerateDocumentation = function () {
// Call base class' constructor.
PluginBase.call(this);
this.pluginMetadata = pluginMetadata;
this.FILES = {
'conf': 'conf.py.ejs'
};
};
GenerateDocumentation.metadata = pluginMetadata;
// Prototypal inheritance from PluginBase.
GenerateDocumentation.prototype = Object.create(PluginBase.prototype);
GenerateDocumentation.prototype.constructor = GenerateDocumentation;
GenerateDocumentation.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
*/
GenerateDocumentation.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.returnZip = currentConfig.returnZip;
self.runningOnClient = false;
if (typeof WebGMEGlobal !== 'undefined') {
self.runningOnClient = true;
callback(new Error('Cannot run ' + self.getName() + ' in the browser!'), self.result);
return;
}
var path = require('path');
// the active node for this plugin is project
var projectNode = self.activeNode;
self.projectName = self.core.getAttribute(projectNode, 'name');
// 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,
'manual');
self.projectModel = {}; // will be filled out by loadProjectModel (and associated functions)
self.nodesWithDocs = {}; // keep track of where the docs are
self.srcData = {};
self.rstData = {};
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.buildDocs();
})
.then(function () {
return self.createZip();
})
.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();
};
GenerateDocumentation.prototype.generateArtifacts = function() {
var self = this;
var child_process = require('child_process');
var path = require('path');
self.notify('info', 'Generating Artifacts.');
var prefix = 'src';
var dir = path.join(
self.gen_dir,
prefix
);
// clear out any previous project files
child_process.execSync('rm -rf ' + self.gen_dir);
var paths = Object.keys(self.objectDict);
var tasks = paths.map(function(path) {
var obj = self.objectDict[path];
if (obj.type != 'Documentation')
return self.generateObjectDocumentation(obj);
});
return Q.all(tasks)
.then(function() {
// figure out which trees contain docs
self.buildDocTree(self.projectModel);
// now add relevant ToC to each generated rst
var rstPaths = Object.keys(self.rstData);
rstPaths.map((rstPath) => {
self.rstData[rstPath] = self.addToC(
self.objectDict[rstPath],
self.rstData[rstPath]
);
});
})
.then(function() {
// write src files out (.md)
var srcFileData = {};
var srcPaths = Object.keys(self.srcData);
srcPaths.map((srcPath) => {
var fname = self.pathToFileName(srcPath) + '.md';
srcFileData[fname] = self.srcData[srcPath];
});
return self.writeFiles(dir, srcFileData);
})
.then(function() {
// write converted files out (.rst)
var rstFileData = {};
var rstPaths = Object.keys(self.rstData);
rstPaths.map((rstPath) => {
var fname = self.pathToFileName(rstPath) + '.rst';
rstFileData[fname] = self.rstData[rstPath];
});
return self.writeFiles(dir, rstFileData);
})
.then(function() {
self.rootRST = self.pathToFileName(self.projectModel.path);
if (!self.objectHasDoc(self.projectModel)) {
// write a root ToC file if docs aren't a part of the root object
var fileData = {
'rootFile.rst': self.addToC(self.projectModel, '')
};
self.rootRST = 'rootFile';
return self.writeFiles(dir, fileData);
}
})
.then(function() {
return self.writeTemplate();
})
.then(function() {
return self.copyStatic();
})
.then(function() {
self.notify('info', 'Generated Artifacts');
});
};
GenerateDocumentation.prototype.objectHasDoc = function(object) {
var self = this;
if (object.Documentation_list) {
var docObj = object.Documentation_list[0];
var documentation = docObj.documentation;
return documentation.length > 0;
}
return false;
};
GenerateDocumentation.prototype.buildDocTree = function(object) {
var self = this;
var closestChildDocs = [];
object.childPaths.map((childPath) => {
if (self.nodesWithDocs[childPath] !== undefined) {
closestChildDocs.push(childPath);
self.buildDocTree(self.objectDict[childPath]);
}
else {
closestChildDocs = closestChildDocs.concat(
self.buildDocTree(self.objectDict[childPath])
);
}
});
object.closestDocSubTrees = closestChildDocs;
return closestChildDocs;
};
GenerateDocumentation.prototype.writeTemplate = function() {
var self = this;
var path = require('path');
var filendir = require('filendir');
var filesToAdd = {};
var prefix = 'src';
var configTemplate = TEMPLATES[self.FILES['conf']];
var configName = prefix + '/conf.py';
filesToAdd[configName] = ejs.render(configTemplate, {
'projectName': self.projectName,
'masterDoc' : self.rootRST,
'authors' : self.projectModel.Authors
});
var fileNames = Object.keys(filesToAdd);
var tasks = fileNames.map(function(fileName) {
var deferred = Q.defer();
var data = filesToAdd[fileName];
filendir.writeFile(path.join(self.gen_dir, fileName), data, function(err) {
if (err) {
deferred.reject(err);
}
else {
deferred.resolve();
}
});
return deferred.promise;
});
return Q.all(tasks)
.then(function() {
var msg = 'Wrote config.';
self.notify('info', msg);
});
};
GenerateDocumentation.prototype.copyStatic = function() {
var self = this;
var fs = require('fs');
var path = require('path');
var unzip = require('unzip');
var fstream = require('fstream');
var deferred = Q.defer();
var staticFile = path.join(process.cwd(), 'src/plugins/GenerateDocumentation/static.zip');
var readStream = fs.createReadStream(staticFile);
var writeStream = fstream.Writer(self.gen_dir);
if (writeStream == undefined) {
throw new String('Couldn\'t open '+self.gen_dir);
}
writeStream.on('unpipe', () => { deferred.resolve(); } );
readStream
.pipe(unzip.Parse())
.pipe(writeStream);
return deferred.promise;
};
GenerateDocumentation.prototype.pathToFileName = function(path) {
var self = this;
return self.projectName.replace(/ /g,'_').toLowerCase() + path.replace(/\//g, '_');
};
GenerateDocumentation.prototype.addToC = function(obj, str) {
var self = this;
if (obj.closestDocSubTrees !== undefined && obj.closestDocSubTrees.length > 0) {
str += '\n.. toctree::\n :includehidden:\n :maxdepth: 2\n\n';
obj.closestDocSubTrees.map(function(childPath) {
str += ' '+self.pathToFileName(childPath) + '\n';
});
}
return str;
};
GenerateDocumentation.prototype.generateObjectDocumentation = function(obj) {
var self = this;
var path = require('path');
var fs = require('fs');
var child_process= require('child_process');
var filendir = require('filendir');
var deferred = Q.defer();
if (obj.Documentation_list) {
obj.Documentation_list.map(function(documentation) {
// do something with docs here
if (documentation.documentation) {
self.nodesWithDocs[obj.path] = obj;
self.srcData[obj.path] = documentation.documentation;
var result = '';
var pandoc = child_process.spawn('pandoc', ['-f','markdown','-t','rst']);
pandoc.stdout.on('data', function(data) {
result += data + '';
});
pandoc.stdout.on('end', function() {
self.rstData[obj.path] = result;
deferred.resolve();
});
pandoc.stderr.on('data', function(err) {
deferred.reject('Conversion with pandoc failed: ' + err);
});
pandoc.on('error', function(err) {
deferred.reject('Failed to start pandoc process (pandoc might not be installed): ' + err);
});
pandoc.stdin.end(documentation.documentation, 'utf-8');
}
else {
deferred.resolve();
}
});
}
else {
deferred.resolve();
}
return deferred.promise;
};
GenerateDocumentation.prototype.writeFiles = function(dir, fileDict) {
var self = this;
var path = require('path');
var fs = require('fs');
var filendir = require('filendir');
var fileNames = Object.keys(fileDict);
var tasks = fileNames.map((fileName) => {
var deferred = Q.defer();
var file = path.join(dir, fileName);
filendir.writeFile(file, fileDict[fileName], function (err) {
if (err) {
deferred.reject('Writing file failed: ' + err);
}
else {
deferred.resolve();
}
});
});
return Q.all(tasks);
};
GenerateDocumentation.prototype.buildDocs = function() {
var self = this;
self.notify('info', 'Building docs into HTML and PDF');
var deferred = Q.defer();
var terminal= require('child_process').spawn('bash', [], {cwd:self.gen_dir});
var stdout = '',
stderr = '';
terminal.stdout.on('data', function (data) { stdout += data; });
terminal.stderr.on('data', function (data) { stderr += data; });
terminal.on('exit', function(code) {
var files = {
'docs.stdout.txt': stdout,
'docs.stderr.txt': stderr
};
var fnames = Object.keys(files);
var tasks = fnames.map((fname) => {
return self.blobClient.putFile(fname, files[fname])
.then((hash) => {
self.result.addArtifact(hash);
});
});
return Q.all(tasks)
.then(() => {
if (code == 0) {
deferred.resolve(code);
}
else {
deferred.reject('buildDocs:: child process exited with code ' + code);
}
});
});
var pdfName = self.projectName.replace(/ /g, '') + '.pdf';
setTimeout(function() {
terminal.stdin.write('make\n');
terminal.stdin.write('make pdf\n');
terminal.stdin.write('mv ./build/html .\n');
terminal.stdin.write('mv ./build/pdf/'+pdfName+' .\n');
terminal.stdin.write('rm -rf ./build/pdf/\n');
terminal.stdin.write('rm -rf ./build\n');
terminal.stdin.end();
}, 1000);
return deferred.promise;
};
GenerateDocumentation.prototype.createZip = function() {
var self = this;
if (!self.returnZip || self.runningOnClient) {
self.notify('info', 'Skipping compression.');
return;
}
self.notify('info', 'Starting compression.');
return new Promise(function(resolve, reject) {
var zlib = require('zlib'),
tar = require('tar'),
fstream = require('fstream'),
input = self.gen_dir;
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 + '+Documentation';
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 GenerateDocumentation;
});