/**
* 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.cpp.rcd;
import java.io.UnsupportedEncodingException;
import org.sintef.thingml.*;
import org.thingml.compilers.Context;
import org.thingml.compilers.c.CCompilerContext;
import org.thingml.compilers.spi.NetworkPlugin;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.sintef.thingml.helpers.AnnotatedElementHelper;
import org.thingml.compilers.spi.SerializationPlugin;
/**
*
* @author steffend
*/
public class RcdPortPlugin extends NetworkPlugin {
CCompilerContext ctx;
Set<RcdPort> rcdPorts;
public String getPluginID() {
return "RcdPortPlugin";
}
public String getCppNameScope() {
return "/*CFG_CPPNAME_SCOPE*/";
}
public List<String> getSupportedProtocols() {
List<String> res = new ArrayList<>();
res.add("RcdPort_0");
res.add("RcdPort_1");
res.add("RcdPort_2");
res.add("RcdPort_3");
res.add("RcdPort_4");
res.add("RcdPort_5");
res.add("RcdPort_6");
res.add("RcdPort_7");
res.add("RcdPort_8");
res.add("RcdPort_9");
res.add("RcdPortTunnel_0");
res.add("RcdPortTunnel_1");
res.add("RcdPortTunnel_2");
res.add("RcdPortTunnel_3");
res.add("RcdPortTunnel_4");
res.add("RcdPortTunnel_5");
res.add("RcdPortTunnel_6");
res.add("RcdPortTunnel_7");
res.add("RcdPortTunnel_8");
res.add("RcdPortTunnel_9");
return res;
}
public String getTargetedLanguage() {
return "sintefboard";
}
public void generateNetworkLibrary(Configuration cfg, Context ctx, Set<Protocol> protocols) {
this.ctx = (CCompilerContext) ctx;
rcdPorts = new HashSet<RcdPort>();
//System.out.println("RcdPortPlugin.generateNetworkLibrary() " + protocols);
if (!protocols.isEmpty()) {
StringBuilder builder = new StringBuilder();
ctx.getBuilder("hashdefines").append("#define RCDPORT_IN_USE\n");
//************ Generate method for receiving messages from rcdports
//This header is part of the "sintefboard_main_header.h" template file
//headerbuilder.append("// Receive forwarding of messages from ports\n");
//headerbuilder.append("void " + "rcdport_receive_forward(msgc_t *msg_in_ptr, int16_t from_port)");
//headerbuilder.append(";\n");
builder.append("// Receive forwarding of messages from ports\n");
builder.append("void " + getCppNameScope() + "rcd_port_receive_forward(msgc_t *msg_in_ptr, int16_t from_port)");
builder.append("{\n");
builder.append("switch (from_port) {\n");
for (Protocol prot : protocols) {
RcdPort port = new RcdPort();
rcdPorts.add(port);
port.protocol = prot;
port.cfg = cfg;
//System.out.println("Protocol " + prot.getName() + " => "+ prot.getAnnotations());
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(this.ctx);
String portnum = AnnotatedElementHelper.annotation(prot, "rcdport_number").iterator().next();
String portName = prot.getName();
builder.append("// "+portName+" portnum is() " + portnum + "\n");
builder.append("case " + portnum + ":\n");
builder.append(portName + "_parser(msg_in_ptr);\n");
builder.append("break;\n");
}
builder.append("} // switch from port\n");
builder.append("}\n");
ctx.getBuilder("RcdPortPlugin" + ".c").append(builder.toString());
generatePortInfo();
}
}
@Override
public List<String> getTargetedLanguages() {
List<String> res = new ArrayList<>();
res.add("sintefboard");
return res;
}
private void generatePortInfo() {
StringBuilder rcdportinfoBbuilder = new StringBuilder();
rcdportinfoBbuilder.append("#define NUM_OF_PORTS_IN_TASK "+rcdPorts.size()+"\n\n");
rcdportinfoBbuilder.append("const static port_info_entry_t _class_port_info[] = {\n");
for (RcdPort port : rcdPorts) {
rcdportinfoBbuilder.append("{"+port.getPortNumber()+", "+port.getPortRole()+", \""+port.getPortName()+"\"},\n");
}
rcdportinfoBbuilder.append("{-1, PORT_END, \"\"},\n");
rcdportinfoBbuilder.append("};\n");
ctx.getBuilder("rcdportinfo").append(rcdportinfoBbuilder.toString());
}
private class RcdPort {
Configuration cfg;
Set<ExternalConnector> ecos;
Protocol protocol;
SerializationPlugin sp;
RcdPort() {
ecos = new HashSet<>();
}
public String getPortNumber() {
return AnnotatedElementHelper.annotation(protocol, "rcdport_number").iterator().next();
}
public String getPortRole() {
String ret = "";
Port p = getPorts(cfg, protocol).iterator().next();
if (p instanceof RequiredPort) {
ret = "PORT_REQUIRED";
}
if (p instanceof ProvidedPort) {
ret = "PORT_PROVIDED";
}
return ret;
}
public String getPortName() {
String ret = "";
Port p = getPorts(cfg, protocol).iterator().next();
Thing t = getThings(cfg, protocol).iterator().next();
ret = t.getName()+":"+p.getName();
if (AnnotatedElementHelper.hasAnnotation(protocol, "rcdport_name")) {
ret = AnnotatedElementHelper.annotation(protocol, "rcdport_name").iterator().next();
}
return ret;
}
public void printPortInfo(int i, Port p) {
System.out.print("RcdPort("+i+") "+ protocol.getName() +" is connected to port "+ p.getName()+ " ");
if (p instanceof RequiredPort) {
System.out.print("ProvidedPort ");
}
if (p instanceof ProvidedPort) {
System.out.print("RequiredPort ");
}
System.out.println();
}
public void generateMessageForwarders(StringBuilder builder, StringBuilder headerbuilder, Protocol prot) {
for (ThingPortMessage tpm : getMessagesSent(cfg, prot)) {
Thing t = tpm.t;
Port p = tpm.p;
Message m = tpm.m;
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");
builder.append("// Forwarding of messages " + prot.getName() + "::" + t.getName() + "::" + p.getName() + "::" + m.getName() + "\n");
builder.append("void " + getCppNameScope() + "forward_" + prot.getName() + "_" + ctx.getSenderName(t, p, m));
ctx.appendFormalParameters(t, builder, m);
builder.append("{\n");
String portnum = AnnotatedElementHelper.annotation(prot, "rcdport_number").iterator().next();
List<String> formats = sp.getSupportedFormat();
if (formats.get(0).contentEquals("None")) {
// No serializing, just map ThingML message to Rcd message
String msgid = AnnotatedElementHelper.annotation(m, "rcdport_msgid").iterator().next();
builder.append("msgc_t msg_out; // Outgoing message\n");
builder.append("if( Ports_ptr->IsConnected(" + portnum + ") ) {\n");
builder.append("APP_MSGC_comp_" + msgid + "(&msg_out" + ctx.getActualParametersSection(m) + ");\n");
builder.append("Ports_ptr->SendMsgc(" + portnum + ", &msg_out);\n");
builder.append("}\n");
} else {
// Serializing specified, create a rcd tunnel
String i = sp.generateSerialization(builder, "forward_buf", m);
String tunnel_msgid = AnnotatedElementHelper.annotation(prot, "rcdporttunnel_msgid").iterator().next();
builder.append("msgc_t msg_out; // Outgoing message\n");
builder.append("if( Ports_ptr->IsConnected(" + portnum + ") ) {\n");
builder.append("APP_MSGC_comp_" + tunnel_msgid + "(&msg_out, forward_buf, " + i + ");\n");
builder.append("Ports_ptr->SendMsgc(" + portnum + ", &msg_out);\n");
builder.append("}\n");
}
builder.append("}\n\n");
}
}
public void generatePortDeserializer(StringBuilder builder, StringBuilder headerbuilder, Protocol prot) {
String portName = protocol.getName();
headerbuilder.append("void " + portName + "_parser(msgc_t *msg_in_ptr);\n");
builder.append("void " + getCppNameScope() + " " + portName + "_parser(msgc_t *msg_in_ptr) {\n");
builder.append("switch (msg_in_ptr->MsgId) {\n");
List<String> formats = sp.getSupportedFormat();
if (formats.get(0).contentEquals("None")) {
// No serializing, just map ThingML message to Rcd message
for (ThingPortMessage tpm : getMessagesReceived(cfg, protocol)) {
Thing t = tpm.t;
Port p = tpm.p;
Message m = tpm.m;
Set<String> ignoreList = new HashSet<String>();
String msgid = AnnotatedElementHelper.annotation(m, "rcdport_msgid").iterator().next();
builder.append("//m.annotation(rcdport_msgid) is " + msgid + "\n");
//String decompproto = m.annotation("rcdport_decompproto").iterator().next();
//parserBuilder.append("//m.annotation(rcdport_decompproto) is " + decompproto + "\n");
builder.append("case " + msgid + ":\n");
builder.append("{\n");
ctx.appendFormalParameterDeclarations(builder, m);
builder.append("APP_MSGC_decomp_" + msgid + "(msg_in_ptr" + ctx.getActualPtrParametersSection(m) + ");\n");
builder.append("{\n");
ctx.generateSerializationForForwarder(m, builder, ctx.getHandlerCode(cfg, m), ignoreList);
builder.append("externalMessageEnqueue(forward_buf, " + (ctx.getMessageSerializationSize(m) - 2) + ", " + portName + "_instance.listener_id);\n");
builder.append("}\n");
builder.append("}\n");
builder.append("break;\n");
}
} else {
// Serializing specified, create a rcd tunnel
Set<Message> rcvMessages = new HashSet();
for (ThingPortMessage tpm : getMessagesReceived(cfg, protocol)) {
Message m = tpm.m;
rcvMessages.add(m);
}
String tunnel_msgid = AnnotatedElementHelper.annotation(prot, "rcdporttunnel_msgid").iterator().next();
builder.append("case " + tunnel_msgid + ":\n");
builder.append("{\n");
builder.append("uint8_t *rcv_buf_ptr;\n");
builder.append("uint8_t rcv_len;\n");
builder.append("APP_MSGC_decomp_" + tunnel_msgid + "(msg_in_ptr, &rcv_buf_ptr, &rcv_len);\n");
sp.generateParserBody(builder, "rcv_buf_ptr", "rcv_len", rcvMessages, portName + "_instance.listener_id");
builder.append("}\n");
builder.append("break;\n");
}
builder.append("} // switch MsgId \n");
builder.append("}\n\n");
}
void generateNetworkLibrary(CCompilerContext ctx) {
if (!ecos.isEmpty()) {
String ctemplate = ctx.getTemplateByID("templates/RcdPortForward.c");
String htemplate = ctx.getTemplateByID("templates/RcdPortForward.h");
String portName = protocol.getName();
ctemplate = ctemplate.replace("/*PORT_NAME*/", portName);
htemplate = htemplate.replace("/*PORT_NAME*/", portName);
StringBuilder b = new StringBuilder();
StringBuilder h = new StringBuilder();
generatePortDeserializer(b, h, protocol);
generateMessageForwarders(b, h, protocol);
ctemplate += "\n" + b;
htemplate += "\n" + h;
ctx.getBuilder(protocol.getName() + ".c").append(ctemplate);
ctx.getBuilder(protocol.getName() + ".h").append(htemplate);
ctx.addToInitCode("\n" + portName + "_instance.listener_id = add_instance(&" + portName + "_instance);\n");
int i = 0;
for (Port p : getPorts(cfg, protocol)) {
printPortInfo(++i, p);
}
}
}
}
}