webgme-dss
Version:
Design Studio for Dynamic Systems with Modelica as backend
261 lines (223 loc) • 10.1 kB
JavaScript
/*globals define*/
/*eslint-env node, browser*/
/**
* Generated by PluginGenerator 2.16.0 from webgme on Thu Feb 01 2018 16:33:12 GMT-0600 (Central Standard Time).
* A plugin that inherits from the PluginBase. To see source code documentation about available
* properties and methods visit %host%/docs/source/PluginBase.html.
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
define([
'plugin/PluginConfig',
'text!./metadata.json',
'plugin/PluginBase',
], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(
require('webgme-engine/src/plugin/PluginConfig'),
require('./metadata.json'),
require('webgme-engine/src/plugin/PluginBase'),
);
}
}(function (PluginConfig,
pluginMetadata,
PluginBase) {
pluginMetadata = typeof pluginMetadata === 'string' ? JSON.parse(pluginMetadata) : pluginMetadata;
/**
* Initializes a new instance of ModelicaCodeGenerator.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin ModelicaCodeGenerator.
* @constructor
*/
function ModelicaCodeGenerator() {
// Call base class' constructor.
PluginBase.call(this);
this.pluginMetadata = pluginMetadata;
}
/**
* 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}
*/
ModelicaCodeGenerator.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
ModelicaCodeGenerator.prototype = Object.create(PluginBase.prototype);
ModelicaCodeGenerator.prototype.constructor = ModelicaCodeGenerator;
/**
* 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
*/
ModelicaCodeGenerator.prototype.main = function main(callback) {
const {logger} = this;
function getMoFileContent(modelJson) {
// TODO: This string concatenation should be changed (it's from the MIC class demo)..
let moFile = 'model ' + modelJson.name;
// 11) Using the modelJson data that we built up we extract the data for the modelica
// code and build up the code.
modelJson.components
.sort((a, b) => {
if (a.URI > b.URI) {
return 1;
} else if (a.URI < b.URI) {
return -1;
} else if (a.name > b.name) {
return 1;
} else if (a.name < b.name) {
return -1;
}
return 0;
})
.forEach((data) => {
const params = Object.keys(data.parameters);
moFile += '\n ' + data.URI + ' ' + data.name;
if (params.length > 0) {
moFile += '(';
params.map((p, idx) => {
moFile += `${p} = ${data.parameters[p]}, `;
if (idx === params.length - 1) {
if (data.modifiers) {
moFile += data.modifiers;
} else {
moFile = moFile.slice(0, -2);
}
}
});
moFile += ')';
} else if (data.modifiers) {
moFile += `(${data.modifiers})`;
}
moFile += ';';
});
moFile += '\nequation';
modelJson.connections
.sort((a, b) => {
if (a.src > b.src) {
return 1;
} else if (a.src < b.src) {
return -1;
} else if (a.dst > b.dst) {
return 1;
} else if (a.dst < b.dst) {
return -1;
}
return 0;
})
.forEach((data) => {
moFile += '\n connect(' + data.src + ', ' + data.dst + ');';
});
moFile += '\nend ' + modelJson.name + ';';
logger.debug(moFile);
return moFile;
}
// 1) Retrieve an object will all nodes in the subtree of activeNode
this.loadNodeMap(this.activeNode)
.then((nodes) => {
const modelJson = this.extractModelData(nodes);
// 10) We turn the data-structure into a string (indentation 2) and log it
logger.debug('modelJson', JSON.stringify(modelJson, null, 2));
const moFile = getMoFileContent(modelJson);
this.moFile = moFile;
logger.debug('moFile', moFile);
// 12) Add the modelic file content as a file on the blobstorage.
return this.blobClient.putFile(`${modelJson.name}.mo`, moFile);
})
.then((metadataHash) => {
logger.debug(metadataHash);
// 13) Link the uploaded file (using the hash) from the plugin result.
this.result.addArtifact(metadataHash);
this.result.setSuccess(true);
callback(null, this.result);
})
.catch((err) => {
logger.error(err.stack);
callback(err, this.result);
});
};
ModelicaCodeGenerator.prototype.extractModelData = function extractModelData(nodes) {
const {
core,
META,
activeNode,
logger,
} = this;
const modelJson = {
name: '',
components: [],
connections: [],
};
function atComponent(node) {
const componentData = {
URI: '',
name: '',
parameters: {},
modifiers: '',
};
// 5) Extract the data we need from the components.
componentData.URI = core.getAttribute(node, 'ModelicaURI');
componentData.name = core.getAttribute(node, 'name');
core.getAttributeNames(node).forEach((attrName) => {
if (attrName !== 'name' && !core.getAttributeMeta(node, attrName).readonly) {
if (attrName === 'modifiers') {
componentData.modifiers = core.getAttribute(node, attrName) || '';
} else {
componentData.parameters[attrName] = core.getAttribute(node, attrName);
}
}
});
// 6) Push the data to the components array.
modelJson.components.push(componentData);
}
function atConnection(node) {
const connData = {
src: '',
dst: '',
};
// 7) Extract the data we need from connections.
const srcPath = core.getPointerPath(node, 'src');
const dstPath = core.getPointerPath(node, 'dst');
// 8) Only if both src and dst exist will the connection be accounted for
if (srcPath && dstPath) {
// (since connData is a referenced data-type we can push here and modify below)
modelJson.connections.push(connData);
const srcNode = nodes[srcPath];
const dstNode = nodes[dstPath];
// 9) Since the ports are contained in components we extract the parents in
// order to get the full modelica path to the port-instance.
const srcParent = core.getParent(srcNode);
const dstParent = core.getParent(dstNode);
connData.src = core.getAttribute(srcParent, 'name') + '.' + core.getAttribute(srcNode, 'name');
connData.dst = core.getAttribute(dstParent, 'name') + '.' + core.getAttribute(dstNode, 'name');
}
}
for (let path in nodes) {
logger.debug(core.getAttribute(nodes[path], 'name'));
}
modelJson.name = core.getAttribute(activeNode, 'name');
// 2) Get all the children paths of the active node
// (these are the immediated children)
const childrenPaths = core.getChildrenPaths(activeNode);
logger.debug('Paths', childrenPaths);
let childNode;
// 3) Iterate of the paths and retrieve the node using the node
// map from 1)
for (let i = 0; i < childrenPaths.length; i += 1) {
childNode = nodes[childrenPaths[i]];
// 4) Check the meta-type of the child-node and take action based on type.
if (core.isTypeOf(childNode, META.ComponentBase)) {
logger.debug('Component:', core.getAttribute(childNode, 'name'));
atComponent(childNode);
} else if (core.isTypeOf(childNode, META.ConnectionBase)) {
logger.debug('Connection:', core.getAttribute(childNode, 'name'));
atConnection(childNode);
}
}
return modelJson;
};
return ModelicaCodeGenerator;
}));