/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. */ package org.thingml.compilers.javascript; import org.eclipse.emf.ecore.util.EcoreUtil; import org.sintef.thingml.*; import org.sintef.thingml.constraints.ThingMLHelpers; import org.sintef.thingml.helpers.AnnotatedElementHelper; import org.sintef.thingml.helpers.ConfigurationHelper; import org.sintef.thingml.helpers.ThingHelper; import org.thingml.compilers.Context; import org.thingml.compilers.DebugProfile; import org.thingml.compilers.configuration.CfgMainGenerator; import java.util.AbstractMap; import java.util.List; import java.util.Map; /** * Created by bmori on 10.12.2014. */ public class JSCfgMainGenerator extends CfgMainGenerator { public static String getDefaultValue(Type type) { if (AnnotatedElementHelper.isDefined(type, "js_type", "boolean")) return "false"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "int")) return "0"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "long")) return "0"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "float")) return "0.0f"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "double")) return "0.0d"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "byte")) return "0"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "short")) return "0"; else if (AnnotatedElementHelper.isDefined(type, "js_type", "char")) return "'\u0000'"; else return "null"; } public static void generateInstance(Instance i, Configuration cfg, StringBuilder builder, Context ctx, boolean useThis, boolean debug) { for (Property a : ConfigurationHelper.allArrays(cfg, i)) { builder.append("const " + i.getName() + "_" + a.getName() + "_array = [];\n"); } for (Map.Entry<Property, List<AbstractMap.SimpleImmutableEntry<Expression, Expression>>> entry : ConfigurationHelper.initExpressionsForInstanceArrays(cfg, i).entrySet()) { for (AbstractMap.SimpleImmutableEntry<Expression, Expression> e : entry.getValue()) { String result = ""; StringBuilder tempBuilder = new StringBuilder(); result += i.getName() + "_" + entry.getKey().getName() + "_array ["; ctx.getCompiler().getThingActionCompiler().generate(e.getKey(), tempBuilder, ctx); result += tempBuilder.toString(); result += "] = "; tempBuilder = new StringBuilder(); ctx.getCompiler().getThingActionCompiler().generate(e.getValue(), tempBuilder, ctx); result += tempBuilder.toString() + ";\n"; builder.append(result); } } //MT if(((JSCompiler)ctx.getCompiler()).multiThreaded) { if (useThis) { builder.append("this."); } else { builder.append("const "); } builder.append(i.getName() + " = fork(require('" + ctx.firstToUpper(i.getType().getName()) + "').resolve(), ['" + i.getName() + "', null");//FIXME: For Kevoree lib/xxx.js } else { if (useThis) { builder.append("this." + i.getName() + " = new " + ctx.firstToUpper(i.getType().getName()) + "('" + i.getName() + "', null"); } else { builder.append("const " + i.getName() + " = new " + ctx.firstToUpper(i.getType().getName()) + "('" + i.getName() + "', null"); } } StringBuilder mt = new StringBuilder(); for (Property prop : ThingHelper.allUsedProperties(i.getType())) {//TODO: not optimal, to be improved for (AbstractMap.SimpleImmutableEntry<Property, Expression> p : ConfigurationHelper.initExpressionsForInstance(cfg, i)) { if (p.getKey().equals(prop) && prop.getCardinality() == null && !AnnotatedElementHelper.isDefined(prop, "private", "true") && prop.eContainer() instanceof Thing) { String result = ""; if (prop.getType() instanceof Enumeration) { Enumeration enum_ = (Enumeration) prop.getType(); EnumLiteralRef enumL = (EnumLiteralRef) p.getValue(); StringBuilder tempbuilder = new StringBuilder(); if (enumL == null) { tempbuilder.append("Enum." + ctx.firstToUpper(enum_.getName()) + "_ENUM." + enum_.getLiterals().get(0).getName().toUpperCase()); } else { tempbuilder.append("Enum" + ctx.firstToUpper(enum_.getName()) + "_ENUM." + enumL.getLiteral().getName().toUpperCase()); } result += tempbuilder.toString(); } else { if (p.getValue() != null) { StringBuilder tempbuilder = new StringBuilder(); ctx.currentInstance = i; ctx.generateFixedAtInitValue(cfg, i, p.getValue(), tempbuilder); ctx.currentInstance = null; result += tempbuilder.toString(); } else { result += getDefaultValue(p.getKey().getType()); } } builder.append(", "); builder.append(result); mt.append(", " + prop.getName() + ": "); mt.append(result); } } for (Property a : ConfigurationHelper.allArrays(cfg, i)) { if (prop.equals(a) && !(AnnotatedElementHelper.isDefined(prop, "private", "true")) && prop.eContainer() instanceof Thing) { builder.append(", "); builder.append(i.getName() + "_" + a.getName() + "_array"); mt.append(", " + prop.getName() + ": "); mt.append(i.getName() + "_" + a.getName() + "_array"); } } } DebugProfile debugProfile = ctx.getCompiler().getDebugProfiles().get(i.getType()); boolean debugInst = false; for (Instance inst : debugProfile.getDebugInstances()) { if (i.getName().equals(inst.getName())) { debugInst = true; break; } } if (debugInst) { builder.append(", true"); } else { builder.append(", false"); } //MT if(((JSCompiler)ctx.getCompiler()).multiThreaded) { builder.append("]"); } builder.append(");\n"); if(((JSCompiler)ctx.getCompiler()).multiThreaded) { if (useThis) { builder.append("this."); } builder.append(i.getName() + ".send({lc: 'new'" + mt.toString() + "});\n"); } if (useThis) { if (debug || debugProfile.getDebugInstances().contains(i)) { builder.append("this." + i.getName() + "." + i.getType().getName() + "_print_debug(this." + i.getName() + ", '" + ctx.traceInit(i.getType()) + "');\n"); } } else { if (debug || debugProfile.getDebugInstances().contains(i)) { builder.append(i.getName() + "." + i.getType().getName() + "_print_debug(" + i.getName() + ", '" + ctx.traceInit(i.getType()) + "');\n"); } } } public static void generateInstances(Configuration cfg, StringBuilder builder, Context ctx, boolean useThis) { final boolean debug = AnnotatedElementHelper.isDefined(cfg, "debug", "true"); for (Instance i : ConfigurationHelper.allInstances(cfg)) { generateInstance(i, cfg, builder, ctx, useThis, debug); } builder.append("/*$PLUGINS$*/\n"); } private static void generateOnEvent(StringBuilder builder, String prefix, Message msg, String client, String clientPort, String server, String serverPort) { builder.append(prefix + server + ".bus.on('" + serverPort + "?" + msg.getName() + "', ("); int id = 0; for(Parameter pa : msg.getParameters()) { if(id>0) builder.append(", "); builder.append(pa.getName()); id++; } builder.append(") => "); builder.append(prefix + client + ".receive" + msg.getName() + "On" + clientPort + "("); id = 0; for(Parameter pa : msg.getParameters()) { if(id>0) builder.append(", "); builder.append(pa.getName()); id++; } builder.append("));\n"); } public static void generateConnectors(Configuration cfg, StringBuilder builder, Context ctx, boolean useThis) { String prefix = ""; if (useThis) { prefix = "this."; } if(((JSCompiler)ctx.getCompiler()).multiThreaded) {//FIXME: Harmonize event management between MT and non-MT builder.append("//Connecting ports...\n"); for (Instance i : ConfigurationHelper.allInstances(cfg)) { builder.append(i.getName() + ".on('message', (m) => {\n"); builder.append("switch(m._port) {\n"); for(Port p : ThingMLHelpers.allPorts(i.getType())) { builder.append("case '" + p.getName() + "':\n"); if(p instanceof InternalPort) { builder.append(i.getName() + ".send(m);\n"); } else { for (Connector c : ConfigurationHelper.allConnectors(cfg)) { if (EcoreUtil.equals(i, c.getCli().getInstance()) && EcoreUtil.equals(p, c.getRequired())) { builder.append("m._port = '" + c.getProvided().getName() + "';\n"); builder.append(c.getSrv().getInstance().getName() + ".send(m);\n"); } else if (EcoreUtil.equals(i, c.getSrv().getInstance()) && EcoreUtil.equals(p, c.getProvided())) { builder.append("m._port = '" + c.getRequired().getName() + "';\n"); builder.append(c.getCli().getInstance().getName() + ".send(m);\n"); } } } builder.append("break;\n"); } builder.append("default:\nbreak;\n"); builder.append("}\n"); builder.append("});\n\n"); } } else { builder.append("//Connecting internal ports...\n"); for (Map.Entry<Instance, List<InternalPort>> entries : ConfigurationHelper.allInternalPorts(cfg).entrySet()) { Instance i = entries.getKey(); for (InternalPort p : entries.getValue()) { for (Message rec : p.getReceives()) { for (Message send : p.getSends()) { if (EcoreUtil.equals(rec, send)) { generateOnEvent(builder, prefix, send, i.getName(), p.getName(), i.getName(), p.getName()); break; } } } } } builder.append("//Connecting ports...\n"); for (Connector c : ConfigurationHelper.allConnectors(cfg)) { for (Message req : c.getRequired().getReceives()) { for (Message prov : c.getProvided().getSends()) { if (req.getName().equals(prov.getName())) { generateOnEvent(builder, prefix, req, c.getCli().getInstance().getName(), c.getRequired().getName(), c.getSrv().getInstance().getName(), c.getProvided().getName()); break; } } } for (Message req : c.getProvided().getReceives()) { for (Message prov : c.getRequired().getSends()) { if (req.getName().equals(prov.getName())) { generateOnEvent(builder, prefix, req, c.getSrv().getInstance().getName(), c.getProvided().getName(), c.getCli().getInstance().getName(), c.getRequired().getName()); break; } } } } } builder.append("/*$PLUGINS_CONNECTORS$*/\n"); } @Override public void generateMainAndInit(Configuration cfg, ThingMLModel model, Context ctx) { final StringBuilder builder = ctx.getBuilder("main.js"); builder.append("'use strict';\n\n"); boolean debug = false; if (AnnotatedElementHelper.isDefined(cfg, "debug", "true")) ; debug = true; if (!debug) { for (Instance i : ConfigurationHelper.allInstances(cfg)) { if (AnnotatedElementHelper.isDefined(i, "debug", "true")) { debug = true; break; } } } if(((JSCompiler)ctx.getCompiler()).multiThreaded) { builder.append("const fork = require('child_process').fork;\n"); } for (Type ty : ThingMLHelpers.allUsedSimpleTypes(model)) { if (ty instanceof Enumeration) { builder.append("const Enum = require('./enums');\n"); break; } } if(!((JSCompiler)ctx.getCompiler()).multiThreaded) { for (Thing t : ConfigurationHelper.allThings(cfg)) { builder.append("const " + ctx.firstToUpper(t.getName()) + " = require('./" + ctx.firstToUpper(t.getName()) + "');\n"); } } builder.append("/*$REQUIRE_PLUGINS$*/\n"); generateInstances(cfg, builder, ctx, false); generateConnectors(cfg, builder, ctx, false); List<Instance> instances = ConfigurationHelper.orderInstanceInit(cfg); Instance inst; while (!instances.isEmpty()) { inst = instances.get(instances.size() - 1); instances.remove(inst); if(((JSCompiler)ctx.getCompiler()).multiThreaded) { builder.append(inst.getName() + ".send({lc: 'init'});\n"); } else { builder.append(inst.getName() + "._init();\n"); } } builder.append("/*$PLUGINS_END$*/\n"); builder.append("//terminate all things on SIGINT (e.g. CTRL+C)\n"); builder.append("process.on('SIGINT', function() {\n"); instances = ConfigurationHelper.orderInstanceInit(cfg); while (!instances.isEmpty()) { inst = instances.get(0); instances.remove(inst); if(((JSCompiler)ctx.getCompiler()).multiThreaded) { builder.append(inst.getName() + ".kill();\n"); } else { builder.append(inst.getName() + "._stop();\n"); builder.append(inst.getName() + "._delete();\n"); } } builder.append("/*$STOP_PLUGINS$*/\n"); builder.append("setTimeout(() => {\n"); builder.append("process.exit();\n"); builder.append("}, 1000);\n"); builder.append("});\n\n"); } }