/** * 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. */ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.thingml.networkplugins.c.posix; import org.sintef.thingml.*; import org.sintef.thingml.helpers.AnnotatedElementHelper; import org.sintef.thingml.impl.ThingmlFactoryImpl; import org.thingml.compilers.Context; import org.thingml.compilers.c.CCompilerContext; import org.thingml.compilers.spi.NetworkPlugin; import org.thingml.compilers.spi.SerializationPlugin; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * * @author sintef */ public class PosixWebSocketPlugin extends NetworkPlugin { CCompilerContext ctx; Configuration cfg; public String getPluginID() { return "PosixWebSocketPlugin"; } public List<String> getSupportedProtocols() { List<String> res = new ArrayList<>(); res.add("WebSocket"); res.add("Websocket"); res.add("websocket"); res.add("WS"); return res; } public List<String> getTargetedLanguages() { List<String> res = new ArrayList<>(); res.add("posix"); res.add("posixmt"); return res; } private void addDependencies() { CCompilerContext ctx = (CCompilerContext) this.ctx; if (!ctx.hasAnnotationWithValue(cfg, "add_c_libraries", "websockets")) { ThingmlFactory factory; factory = ThingmlFactoryImpl.init(); PlatformAnnotation pan = factory.createPlatformAnnotation(); pan.setName("add_c_libraries"); pan.setValue("websockets"); AnnotatedElementHelper.allAnnotations(cfg).add(pan); } } public void generateNetworkLibrary(Configuration cfg, Context ctx, Set<Protocol> protocols) { this.ctx = (CCompilerContext) ctx; this.cfg = cfg; if (!protocols.isEmpty()) { addDependencies(); } for (Protocol prot : protocols) { WSPort port = new WSPort(); port.protocol = prot; try { port.sp = ctx.getSerializationPlugin(prot); } catch (UnsupportedEncodingException uee) { System.err.println("Could not get serialization plugin... Expect some errors in the generated code"); uee.printStackTrace(); return; } for (ExternalConnector eco : this.getExternalConnectors(cfg, prot)) { port.ecos.add(eco); eco.setName(eco.getProtocol().getName()); } port.generateNetworkLibrary(); } } private class WSPort { Set<ExternalConnector> ecos; Protocol protocol; Set<Message> messages; SerializationPlugin sp; WSPort() { ecos = new HashSet<>(); messages = new HashSet(); } public void generateNetworkLibrary() { if (!ecos.isEmpty()) { for (ThingPortMessage tpm : getMessagesReceived(cfg, protocol)) { Message m = tpm.m; messages.add(m); } String ctemplate; String htemplate; if (AnnotatedElementHelper.hasAnnotation(protocol, "websocket_client")) { if (AnnotatedElementHelper.annotation(protocol, "websocket_client").iterator().next().equalsIgnoreCase("true")) { ctemplate = ctx.getTemplateByID("templates/PosixWebsocketClientPlugin.c"); htemplate = ctx.getTemplateByID("templates/PosixWebsocketClientPlugin.h"); String serverAddress; if (AnnotatedElementHelper.hasAnnotation(protocol, "websocket_server_address")) { serverAddress = AnnotatedElementHelper.annotation(protocol, "websocket_server_address").iterator().next(); } else { serverAddress = "127.0.0.1"; } ctemplate = ctemplate.replace("/*ADDRESS*/", serverAddress); } else { ctemplate = ctx.getTemplateByID("templates/PosixWebsocketPlugin.c"); htemplate = ctx.getTemplateByID("templates/PosixWebsocketPlugin.h"); } } else { ctemplate = ctx.getTemplateByID("templates/PosixWebsocketPlugin.c"); htemplate = ctx.getTemplateByID("templates/PosixWebsocketPlugin.h"); } String portName = protocol.getName(); //Threaded listener --- BEGIN ctx.addToInitCode("\n" + portName + "_instance.listener_id = add_instance(&" + portName + "_instance);\n"); StringBuilder initThread = new StringBuilder(); initThread.append("//" + protocol.getName() + ":\n"); initThread.append(protocol.getName() + "_setup();\n"); initThread.append("pthread_t thread_"); initThread.append(protocol.getName()); initThread.append(";\n"); initThread.append("pthread_create( &thread_"); initThread.append(protocol.getName()); initThread.append(", NULL, "); initThread.append(protocol.getName() + "_start_receiver_process"); initThread.append(", NULL);\n"); ctx.addToInitCode(initThread.toString()); //Threaded listener --- END ctemplate = ctemplate.replace("/*PORT_NAME*/", portName); htemplate = htemplate.replace("/*PORT_NAME*/", portName); Integer portNumber; if (AnnotatedElementHelper.hasAnnotation(protocol, "websocket_port_number")) { portNumber = Integer.parseInt(AnnotatedElementHelper.annotation(protocol, "websocket_port_number").iterator().next()); } else { portNumber = 9000; } ctemplate = ctemplate.replace("/*PORT_NUMBER*/", portNumber.toString()); String wsProtocol = AnnotatedElementHelper.annotationOrElse(protocol, "ws_protocol", "ThingML-protocol"); ctemplate = ctemplate.replace("/*WS_PROTOCOL*/", wsProtocol); //Connector ready StringBuilder connectorReady = new StringBuilder(); for (Message m : messages) { if (AnnotatedElementHelper.hasAnnotation(m, "websocket_connector_ready")) { connectorReady.append("//Notify app with " + m.getName() + "\n"); connectorReady.append("byte forward_buf[2];\n"); connectorReady.append("forward_buf[0] = (" + ctx.getHandlerCode(cfg, m) + " >> 8) & 0xFF;\n"); connectorReady.append("forward_buf[1] = " + ctx.getHandlerCode(cfg, m) + " & 0xFF;\n\n"); connectorReady.append("externalMessageEnqueue(forward_buf, 2, " + portName + "_instance.listener_id);\n\n"); } } ctemplate = ctemplate.replace("/*CONNEXION_ESTABLISHED*/", connectorReady); //End connector Ready //Server ready StringBuilder listenerReady = new StringBuilder(); for (Message m : messages) { if (AnnotatedElementHelper.hasAnnotation(m, "websocket_server_ready")) { listenerReady.append("//Notify app with " + m.getName() + "\n"); listenerReady.append("byte forward_buf[2];\n"); listenerReady.append("forward_buf[0] = (" + ctx.getHandlerCode(cfg, m) + " >> 8) & 0xFF;\n"); listenerReady.append("forward_buf[1] = " + ctx.getHandlerCode(cfg, m) + " & 0xFF;\n\n"); listenerReady.append("externalMessageEnqueue(forward_buf, 2, " + portName + "_instance.listener_id);\n\n"); } } ctemplate = ctemplate.replace("/*LISTENER_READY*/", listenerReady); //end server ready Integer nbClientMax; if (AnnotatedElementHelper.hasAnnotation(protocol, "websocket_nb_client_max")) { nbClientMax = Integer.parseInt(AnnotatedElementHelper.annotation(protocol, "websocket_nb_client_max").iterator().next()); } else { nbClientMax = 16; } ctemplate = ctemplate.replace("/*NB_MAX_CLIENT*/", nbClientMax.toString()); //Connector Instanciation StringBuilder eco_instance = new StringBuilder(); eco_instance.append("//Connector"); /*Port p = eco.getPort(); if(!p.getReceives().isEmpty()) { //if(!p.getSends().isEmpty()) { eco_instance.append("// Pointer to receiver list\n"); eco_instance.append("struct Msg_Handler ** "); eco_instance.append(p.getName()); eco_instance.append("_receiver_list_head;\n"); eco_instance.append("struct Msg_Handler ** "); eco_instance.append(p.getName()); eco_instance.append("_receiver_list_tail;\n"); } //if(!p.getReceives().isEmpty()) { if(!p.getSends().isEmpty()) { eco_instance.append("// Handler Array\n"); eco_instance.append("struct Msg_Handler * "); eco_instance.append(p.getName()); eco_instance.append("_handlers;\n");//["); //builder.append(p.getReceives().size() + "];"); }*/ //ctemplate = ctemplate.replace("/*INSTANCE_INFORMATION*/", eco_instance); htemplate = htemplate.replace("/*PATH_TO_C*/", protocol.getName() + ".c"); //UNICAST vs BROADCAST String enableUnicast = null; boolean unicast = false; if (AnnotatedElementHelper.hasAnnotation(protocol, "websocket_enable_unicast")) { enableUnicast = AnnotatedElementHelper.annotation(protocol, "websocket_enable_unicast").iterator().next(); } if (enableUnicast != null) { if (enableUnicast.compareTo("true") == 0) { unicast = true; } } if (unicast) { /*PARAM_CLIENT_ID*/ ctemplate = ctemplate.replace("/*PARAM_CLIENT_ID*/", ", uint16_t clientID"); htemplate = htemplate.replace("/*PARAM_CLIENT_ID*/", ", uint16_t clientID"); /*NEW_CLIENT*/ StringBuilder newClient = new StringBuilder(); for (Message m : messages) { if (AnnotatedElementHelper.hasAnnotation(m, "websocket_new_client")) { newClient.append("//Notify app with " + m.getName() + "\n"); newClient.append("byte forward_buf[4];\n"); newClient.append("forward_buf[0] = (" + ctx.getHandlerCode(cfg, m) + " >> 8) & 0xFF;\n"); newClient.append("forward_buf[1] = " + ctx.getHandlerCode(cfg, m) + " & 0xFF;\n\n"); newClient.append("forward_buf[3] = (clientID >> 8) & 0xFF;\n"); newClient.append("forward_buf[2] = clientID & 0xFF;\n\n"); newClient.append("externalMessageEnqueue(forward_buf, 4, " + portName + "_instance.listener_id);\n\n"); } } ctemplate = ctemplate.replace("/*NEW_CLIENT*/", newClient); /*CLIENT_DECO*/ StringBuilder clientDC = new StringBuilder(); for (Message m : messages) { if (AnnotatedElementHelper.hasAnnotation(m, "websocket_client_disconnected")) { clientDC.append("//Notify app with " + m.getName() + "\n"); clientDC.append("byte forward_buf[4];\n"); clientDC.append("forward_buf[0] = (" + ctx.getHandlerCode(cfg, m) + " >> 8) & 0xFF;\n"); clientDC.append("forward_buf[1] = " + ctx.getHandlerCode(cfg, m) + " & 0xFF;\n\n"); clientDC.append("forward_buf[3] = (clientID >> 8) & 0xFF;\n"); clientDC.append("forward_buf[2] = clientID & 0xFF;\n\n"); clientDC.append("externalMessageEnqueue(forward_buf, 4, " + portName + "_instance.listener_id);\n\n"); } } ctemplate = ctemplate.replace("/*CLIENT_DECO*/", clientDC); /*SENDING_BROADCAST_OR_NOT*/ StringBuilder WSSending = new StringBuilder(); WSSending.append("if(clientID == 65535) {\n" + "for(i = 0; i < " + portName + "_nb_client; i++) {\n" + "if(" + portName + "_clients[i] != NULL) {\n" + "m = libwebsocket_write(" + portName + "_clients[i], p, length, LWS_WRITE_TEXT);\n" + //to check "}\n" + "}\n" + "} else {\n" + "if(clientID < " + nbClientMax + ") {\n" + "if(" + portName + "_clients[clientID] != NULL) {\n" + "m = libwebsocket_write(" + portName + "_clients[clientID], p, length, LWS_WRITE_TEXT);\n" + //to check "} else {\n" + "/*TRACE_LEVEL_1*/printf(\"[PosixWSForward] client %i not found\\n\", clientID);" + "}\n" + "} else {\n" + "/*TRACE_LEVEL_1*/printf(\"[PosixWSForward] client %i not found\\n\", clientID);" + "}\n" + "}\n" ); ctemplate = ctemplate.replace(" /*SENDING_BROADCAST_OR_NOT*/", WSSending); } else { /*PARAM_CLIENT_ID*/ ctemplate = ctemplate.replace("/*PARAM_CLIENT_ID*/", ""); htemplate = htemplate.replace("/*PARAM_CLIENT_ID*/", ""); /*NEW_CLIENT*/ ctemplate = ctemplate.replace("/*NEW_CLIENT*/", ""); /*CLIENT_DECO*/ ctemplate = ctemplate.replace("/*CLIENT_DECO*/", ""); /*SENDING_BROADCAST_OR_NOT*/ StringBuilder WSSending = new StringBuilder(); WSSending.append("for(i = 0; i < " + portName + "_nb_client; i++) {\n" + "m = libwebsocket_write(" + portName + "_clients[i], p, length, LWS_WRITE_TEXT);\n" + //to check "}\n"); ctemplate = ctemplate.replace(" /*SENDING_BROADCAST_OR_NOT*/", WSSending); } //De Serializer StringBuilder ParserImplementation = new StringBuilder(); ParserImplementation.append("void " + portName + "_parser(byte * msg, uint16_t size) {\n"); sp.generateParserBody(ParserImplementation, "msg", "size", messages, portName + "_instance.listener_id"); ParserImplementation.append("}\n"); ctemplate = ctemplate.replace("/*PARSER_IMPLEMENTATION*/", sp.generateSubFunctions() + ParserImplementation); Integer traceLevel; if (AnnotatedElementHelper.hasAnnotation(protocol, "trace_level")) { traceLevel = Integer.parseInt(AnnotatedElementHelper.annotation(protocol, "trace_level").iterator().next()); } else { traceLevel = 1; } if (traceLevel == null) { traceLevel = 1; } //System.out.println("TRACE_LEVEL:"+traceLevel); if (traceLevel.intValue() >= 3) { ctemplate = ctemplate.replace("/*TRACE_LEVEL_3*/", ""); //System.out.println("/*TRACE_LEVEL_3*/"); } else { ctemplate = ctemplate.replace("/*TRACE_LEVEL_3*/", "//"); } if (traceLevel.intValue() >= 2) { ctemplate = ctemplate.replace("/*TRACE_LEVEL_2*/", ""); //System.out.println("/*TRACE_LEVEL_2*/"); } else { ctemplate = ctemplate.replace("/*TRACE_LEVEL_2*/", "//"); } if (traceLevel.intValue() >= 1) { ctemplate = ctemplate.replace("/*TRACE_LEVEL_1*/", ""); //System.out.println("/*TRACE_LEVEL_1*/"); } else { ctemplate = ctemplate.replace("/*TRACE_LEVEL_1*/", "//"); } StringBuilder b = new StringBuilder(); StringBuilder h = new StringBuilder(); generateMessageForwarders(b, h, cfg, protocol); ctemplate += "\n" + b; htemplate += "\n" + h; ctx.getBuilder("lws_config.h").append(ctx.getTemplateByID("templates/lws_config.h")); ctx.getBuilder(protocol.getName() + ".c").append(ctemplate); ctx.getBuilder(protocol.getName() + ".h").append(htemplate); ctx.addToIncludes("#include \"" + protocol.getName() + ".h\""); } } public void generateMessageForwarders(StringBuilder builder, StringBuilder headerbuilder, Configuration cfg, Protocol prot) { for (ThingPortMessage tpm : getMessagesSent(cfg, prot)) { Thing t = tpm.t; Port p = tpm.p; Message m = tpm.m; SerializationPlugin sp = null; try { sp = ctx.getSerializationPlugin(prot); } catch (UnsupportedEncodingException uee) { System.err.println("Could not get serialization plugin... Expect some errors in the generated code"); uee.printStackTrace(); return; } builder.append("// Forwarding of messages " + prot.getName() + "::" + t.getName() + "::" + p.getName() + "::" + m.getName() + "\n"); builder.append("void forward_" + prot.getName() + "_" + ctx.getSenderName(t, p, m)); ctx.appendFormalParameters(t, builder, m); builder.append("{\n"); String i = sp.generateSerialization(builder, "forward_buf", m); builder.append("\n//Forwarding with specified function \n"); builder.append(prot.getName() + "_forwardMessage(forward_buf, " + i + ");\n"); builder.append("}\n\n"); headerbuilder.append("// Forwarding of messages " + prot.getName() + "::" + t.getName() + "::" + p.getName() + "::" + m.getName() + "\n"); headerbuilder.append("void forward_" + prot.getName() + "_" + ctx.getSenderName(t, p, m)); ctx.appendFormalParameters(t, headerbuilder, m); headerbuilder.append(";\n"); } } } }