UNPKG

webgme-gridlabd

Version:

Metamodel, visualization, and model generators for gridlab-d in WebGME. Allows graphical model-driven development and simulation of power grids and power generation / transmission / distribution / storage systems.

420 lines (375 loc) 12.5 kB
/*globals define*/ /*jshint node:true, browser:true*/ /** * Generated by PluginGenerator 1.7.0 from webgme on Thu May 12 2016 19:55:04 GMT-0500 (CDT). * A plugin that inherits from the PluginBase. To see source code documentation about available * properties and methods visit %host%/docs/source/PluginBase.html. */ define([ 'plugin/PluginConfig', 'text!./metadata.json', 'plugin/PluginBase', 'common/util/ejs', // for ejs templates 'common/util/xmljsonconverter', // used to save model as json 'plugin/SimulateTES/SimulateTES/Templates/Templates', 'plugin/SimulateTES/SimulateTES/SimulateTES.Parser', 'plugin/SimulateTES/SimulateTES/SimulateTES.Plotter', 'gridlabd/meta', 'gridlabd/modelLoader', 'gridlabd/renderer', 'q' ], function ( PluginConfig, pluginMetadata, PluginBase, ejs, Converter, TEMPLATES, Parser, Plotter, MetaTypes, loader, renderer, Q) { 'use strict'; pluginMetadata = JSON.parse(pluginMetadata); /** * Initializes a new instance of SimulateTES. * @class * @augments {PluginBase} * @classdesc This class represents the plugin SimulateTES. * @constructor */ var SimulateTES = function () { // Call base class' constructor. PluginBase.call(this); this.pluginMetadata = pluginMetadata; this.metaTypes = MetaTypes; }; /** * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc. * This is also available at the instance at this.pluginMetadata. * @type {object} */ SimulateTES.metadata = pluginMetadata; // Prototypical inheritance from PluginBase. SimulateTES.prototype = Object.create(PluginBase.prototype); SimulateTES.prototype.constructor = SimulateTES; SimulateTES.prototype.notify = function(level, msg) { var self = this; var prefix = self.projectId + '::' + self.projectName + '::' + level + '::'; 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); self.sendNotification(prefix+msg); }; /** * 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 */ SimulateTES.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, modelNode; self.result.success = false; if (typeof WebGMEGlobal !== 'undefined') { var msg = 'You must run this plugin on the server!'; self.notify('error', msg); callback(new Error(msg), self.result); } self.updateMETA(self.metaTypes); // What did the user select for our configuration? var currentConfig = self.getCurrentConfig(); self.simulationTime = currentConfig.simulationTime; self.delayMean = currentConfig.delayMean; self.delayStdDev = currentConfig.delayStdDev; self.community1 = currentConfig.community1; self.community2 = currentConfig.community2; self.generator1 = currentConfig.generator1; self.generator2 = currentConfig.generator2; self.returnZip = currentConfig.returnZip; modelNode = self.activeNode; self.modelName = self.core.getAttribute(modelNode, 'name'); self.fileName = self.modelName + '.glm'; var path = require('path'); var filendir = require('filendir'); self.root_dir = path.join(process.cwd(), 'generated', self.project.projectId, self.branchName, 'models'); return loader.loadModel(self.core, modelNode, true, true) .then(function(powerModel) { self.powerModel = powerModel; }) .then(function() { return self.clean(); }) .then(function() { return self.renderModel(); }) .then(function() { return self.writeInputs(); }) .then(function() { return self.runSimulation(); }) .then(function() { return self.copyArtifacts(); }) .then(function() { return self.plotLogs(); }) .then(function() { self.result.success = true; self.notify('info', 'Simulation Complete.'); callback(null, self.result); }) .catch(function(err) { self.notify('error', err); self.result.success = false; callback(err, self.result); }); }; SimulateTES.prototype.clean = function() { var self = this; var basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/"; var cp = require('child_process'); var deferred = Q.defer(); var fedMgr = cp.spawn('bash', [], {cwd:basePath}); fedMgr.stdout.on('data', function (data) {}); fedMgr.stderr.on('data', function (error) {}); fedMgr.on('exit', function (code) { deferred.resolve(); }); setTimeout(function() { self.notify('info', 'Cleaning experiment artifacts and processes from system.'); fedMgr.stdin.write('rm -rf input/*\n'); fedMgr.stdin.write('rm -rf output/*\n'); fedMgr.stdin.write('./kill-all.sh\n'); fedMgr.stdin.end(); }, 1000); return deferred.promise; }; SimulateTES.prototype.renderModel = function() { var self = this; self.fileData = renderer.renderGLM(self.powerModel, self.core, self.META); }; SimulateTES.prototype.writeInputs = function() { var self = this, basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/input/", inputFiles = { "model.glm": self.fileData, "Community1DemandController.config": JSON.stringify({ "Threshold": +self.community1 }, null, 2), "Community2DemandController.config": JSON.stringify({ "Threshold": +self.community2 }, null, 2), "Generator1PriceController.config": JSON.stringify({ "Threshold": +self.generator1 }, null, 2), "Generator2PriceController.config": JSON.stringify({ "Threshold": +self.generator2 }, null, 2), "script.xml": ejs.render( TEMPLATES['script.xml.ejs'], { simEnd: self.simulationTime, delayMean: self.delayMean, delayStdDev: self.delayStdDev }) }, fs = require('fs'), path = require('path'), filendir = require('filendir'); var fileNames = Object.keys(inputFiles); var tasks = fileNames.map((fileName) => { var deferred = Q.defer(); var data = inputFiles[fileName]; filendir.writeFile(path.join(basePath, fileName), data, (err) => { if (err) { deferred.reject('Couldnt write ' + fileName + ': ' + err); } else { deferred.resolve(); } }); return deferred.promise; }); return Q.all(tasks) .then(function() { self.notify('info', 'Generated artifacts.'); }); }; SimulateTES.prototype.runSimulation = function() { var self = this; var path = require('path'); var cp = require('child_process'); self.notify('info', 'Starting Simulation'); var deferred = Q.defer(); var fname = path.join(self.root_dir, self.fileName); // start fed manager return self.startFederates() .then(function() { return self.monitorContainers(); }) .then(function() { return self.killFederates(); }); return deferred.promise; }; SimulateTES.prototype.startFederates = function() { // run-cpp-feds.sh var self = this; var basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/"; var cp = require('child_process'); var deferred = Q.defer(); var fedMgr = cp.spawn('bash', [], {cwd:basePath}); fedMgr.stdout.on('data', function (data) {}); fedMgr.stderr.on('data', function (error) { }); fedMgr.on('exit', function (code) { if (code == 0) { self.notify('info', 'Started Federates.'); deferred.resolve(code); } else { deferred.reject('federates:: child process exited with code ' + code); } }); setTimeout(function() { self.notify('info', 'Starting Federates.'); fedMgr.stdin.write('./run-cpp-feds.sh\n'); fedMgr.stdin.end(); }, 1000); return deferred.promise; }; SimulateTES.prototype.monitorContainers = function() { var self = this; var cp = require('child_process'); var deferred = Q.defer(); var stdout = cp.execSync('docker ps'); var regex = /(demo_c2wt_cpp_fedmgr_run_[\w+]*)/gi; var results = regex.exec(stdout); if (results) { var dockerName = results[1]; self.notify('info', 'Waiting for simulation to complete when ' + dockerName + ' exits.'); stdout = cp.execSync('docker wait ' + dockerName); self.notify('info', 'Docker federate exited with stdout: ' + stdout); } else { self.notify('error', 'Couldnt find the federate docker container in docker ps!'); } }; SimulateTES.prototype.killFederates = function() { // kill-all.sh var self = this; var basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/"; var cp = require('child_process'); var deferred = Q.defer(); var stopFeds = cp.spawn('bash', [], {cwd:basePath}); stopFeds.stdout.on('data', function (data) {}); stopFeds.stderr.on('data', function (error) { }); stopFeds.on('exit', function (code) { if (code == 0) { self.notify('info', 'Killed all experiment feds.'); deferred.resolve(code); } else { deferred.reject('stopFeds:: child process exited with code ' + code); } }); setTimeout(function() { self.notify('info', 'Killing experiment feds.'); stopFeds.stdin.write('docker stop $(docker ps -a -q)\n'); stopFeds.stdin.end(); }, 1000); return deferred.promise; }; SimulateTES.prototype.copyArtifacts = function() { var self = this; var basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/output"; self.notify('info', 'Copying output.'); return new Promise(function(resolve, reject) { var zlib = require('zlib'), tar = require('tar'), fstream = require('fstream'), input = basePath; 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); self.blobClient.putFile('output.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.'); }); }; SimulateTES.prototype.plotLogs = function() { var self = this; var path = require('path'); var fs = require('fs'); var basePath = "/home/jeb/tesDemo/repo/c2wtng-fedimgs/dockerfeds/examples/TES2016Demo/Demo/output"; var controllers = [ "Community1DemandController", "Community2DemandController", "Generator1PriceController", "Generator2PriceController" ]; self.notify('info', 'Plotting logs.'); var tasks = controllers.map((controller) => { var fileName = path.join(basePath, controller.toLowerCase(), controller + '.log'); var deferred = Q.defer(); // load the file fs.readFile(fileName, (err, data) => { if (err) { deferred.reject('Couldnt open ' + fileName + ': ' + err); return; } var logData = Parser.getDataFromLog(data); Plotter.logger = self.logger; Plotter.plotData(logData) .then((svgHtml) => { var resultFileName = controller + '.svg'; self.blobClient.putFile(resultFileName, svgHtml) .then((hash) => { self.result.addArtifact(hash); var resultUrl = '/rest/blob/download/' + hash + '/' + resultFileName; self.createMessage(self.activeNode, controller + ' log plot:' + svgHtml, 'info'); deferred.resolve(); }) .catch((err) => { deferred.reject('Couldnt add ' + resultFileName +' to blob'); }); }); }); return deferred.promise; }); return Q.all(tasks); }; return SimulateTES; });