Commit 156ed959 authored by Jean Meurice's avatar Jean Meurice
Browse files

Merge branch 'sim_dev' into 'master'

SimpleNetwork (MontiSim) support

See merge request !51
parents 84245561 59d7fca6
Pipeline #400609 failed with stage
in 12 minutes and 29 seconds
{
"configurations": [
{
"type": "java",
"name": "JAR EMAM2CPP",
"request": "launch",
"mainClass": "de.monticore.lang.monticar.generator.cpp.GeneratorCppCli",
"cwd": "${workspaceFolder}",
"classPaths": [
"${workspaceFolder}/target/embedded-montiarc-math-generator-0.4.5-jar-with-dependencies.jar"
],
"args": [
"--models-dir=temp/model",
"--root-model=de.rwth.armin.modeling.autopilot.autopilot",
"--output-dir=temp/target/cpp",
"--output-name=test1",
"--flag-use-armadillo-backend",
"--tcp-adapter",
"--flag-generate-cmake"
]
}
]
}
\ No newline at end of file
......@@ -10,7 +10,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-generator</artifactId>
<version>0.4.4-SNAPSHOT</version>
<version>0.4.5</version>
<!-- == PROJECT DEPENDENCIES ============================================= -->
......@@ -63,7 +63,7 @@
<dependency>
<groupId>montisim</groupId>
<artifactId>commons</artifactId>
<version>2.0.11</version>
<version>2.0.14</version>
</dependency>
<dependency>
......
......@@ -2,12 +2,14 @@ package de.monticore.lang.monticar.generator.cpp.dynamic_interface;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import de.rwth.montisim.commons.dynamicinterface.*;
import de.rwth.montisim.commons.dynamicinterface.PortInformation.PortDirection;
import de.rwth.montisim.commons.dynamicinterface.PortInformation.PortType;
import de.rwth.montisim.commons.utils.json.Json;
import de.rwth.montisim.commons.utils.json.SerializationException;
import de.monticore.ast.ASTNode;
......@@ -38,6 +40,10 @@ import de.monticore.types.types._ast.ASTType;
the EMA suite should verify how the different types and symbols are resolved.
*/
public class DynamicInterfaceGenerator {
public static String SOCK_IN_SUFFIX = "_socket_in";
public static String SOCK_OUT_SUFFIX = "_socket_out";
public static String SOCK_BC_IN_SUFFIX = "_socket_bc_in";
public static String SOCK_BC_OUT_SUFFIX = "_socket_bc_out";
HashSet<String> cppFileDependencies = new HashSet<>();
List<FileContent> files = new ArrayList<>();
......@@ -49,6 +55,8 @@ public class DynamicInterfaceGenerator {
TcpCommunication tcp;
DDCCommunication ddc;
public DynamicInterfaceGenerator(
EMAComponentInstanceSymbol componentSymbol,
CMakeConfig cmake,
......@@ -123,25 +131,204 @@ public class DynamicInterfaceGenerator {
cppFileDependencies.add(name);
}
void resolve(EMAComponentInstanceSymbol componentSymbol){
programInterface = new ProgramInterface();
programInterface.name = componentSymbol.getName();
programInterface.version = "0.0";
programInterface.version = ProgramInterface.CURRENT_VERSION;
// TODO ignore dynamic ports for now
this.componentName = GeneralHelperMethods.getTargetLanguageComponentName(componentSymbol.getFullName());
for ( EMAPortInstanceSymbol port : componentSymbol.getIncomingPortInstances()){
boolean multipleInputsAllowed = false; // TODO
boolean optional = false; // TODO
programInterface.ports.add(new PortInformation(port.getName(), getDataType(port), PortDirection.INPUT, multipleInputsAllowed, optional));
for ( EMAPortInstanceSymbol port : componentSymbol.getIncomingPortInstances()) {
String name = port.getName();
boolean isArray = isPortArray(port.getName());
if (isArray) name = getPortArrayName(port.getName());
if (isSocketPort(name)) {
if (!isSocketInput(name)) throw new IllegalArgumentException("Port '"+name+"' must be declared as output with this suffix.");
resolveSocket(port, name, isArray, true);
} else {
if (isArray) throw new IllegalArgumentException("ArrayPorts not implemented");
boolean multipleInputsAllowed = false; // TODO
boolean optional = false; // TODO
addPortInformation(PortInformation.newInputDataPort(name, getDataType(port), multipleInputsAllowed, optional));
}
}
for ( EMAPortInstanceSymbol port : componentSymbol.getOutgoingPortInstances()) {
String name = port.getName();
boolean isArray = isPortArray(port.getName());
if (isArray) name = getPortArrayName(port.getName());
if (isSocketPort(name)) {
if (isSocketInput(name)) throw new IllegalArgumentException("Port '"+name+"' must be declared as input with this suffix.");
resolveSocket(port, name, isArray, false);
} else {
if (isArray) throw new IllegalArgumentException("ArrayPorts not implemented");
boolean optional = false; // TODO
addPortInformation(PortInformation.newOutputDataPort(name, getDataType(port), optional));
}
}
// For sockets: Check array lengths and convert data type to simple packet
for (PortInformation port : programInterface.ports) {
if (port.port_type != PortType.SOCKET) continue;
port.data_type = new SimplePacketType(port.data_type);
SocketInfo sockInf = sockByName.get(port.name);
sockInf.array_length = -1;
if (sockInf.is_bc) {
if (port.isInput()) {
if (arraySizes.containsKey(port.name + SOCK_BC_IN_SUFFIX)){
sockInf.array_length = arraySizes.get(port.name + SOCK_BC_IN_SUFFIX);
} else throw new IllegalArgumentException("Socket-port '"+port.name + SOCK_BC_IN_SUFFIX+"' is not a port-array.");
}
if (port.isOutput()) {
if (arraySizes.containsKey(port.name + SOCK_BC_OUT_SUFFIX)) {
throw new IllegalArgumentException("Socket-port '"+port.name + SOCK_BC_OUT_SUFFIX+"' cannot be a port-array (must be size of one).");
}
}
} else {
if (port.isInput()) {
if (arraySizes.containsKey(port.name + SOCK_IN_SUFFIX)){
sockInf.array_length = arraySizes.get(port.name + SOCK_IN_SUFFIX);
} else throw new IllegalArgumentException("Socket-port '"+port.name + SOCK_IN_SUFFIX+"' is not a port-array.");
}
if (port.isOutput()) {
if (arraySizes.containsKey(port.name + SOCK_OUT_SUFFIX)){
int size = arraySizes.get(port.name + SOCK_OUT_SUFFIX);
if (sockInf.array_length == -1) sockInf.array_length = size;
else if (sockInf.array_length != size) throw new IllegalArgumentException("PortArray size mismatch between input and output for port '"+port.name+"'.");
} else throw new IllegalArgumentException("Socket-port '"+port.name + SOCK_OUT_SUFFIX+"' is not a port-array.");
}
}
}
}
void resolveSocket(EMAPortInstanceSymbol port, String name, boolean isArray, boolean isInput) {
String targetName = getSocketTargetName(name);
if (isArray) {
trackPortArraySize(name, getPortArrayIndex(port.getName()));
if (!isFirstEncounterAndSet(name, arrayPorts)) return;
}
for ( EMAPortInstanceSymbol port : componentSymbol.getOutgoingPortInstances()){
boolean multipleInputsAllowed = false; // TODO
boolean optional = false; // TODO
programInterface.ports.add(new PortInformation(port.getName(), getDataType(port), PortDirection.OUTPUT, multipleInputsAllowed, optional));
PortInformation portInf = portByName.get(targetName);
if (portInf == null) {
addPortInformation(PortInformation.newSocketPort(targetName, getDataType(port), isInput, !isInput));
sockByName.put(targetName, new SocketInfo(!isInput, isSocketBroadcast(name), name));
} else {
// Another socket already maps to the target name
// Cannot be a DATA port
if (portInf.port_type == PortType.DATA) throw new IllegalArgumentException("Port Name Clash: EMA Port '"+portInf.name+"' and EMA socket port '"+name+"' map to the same port name ('"+targetName+"').");
// DataType must match
DataType thisDataType = getDataType(port);
if (!portInf.data_type.equals(thisDataType)) throw new IllegalArgumentException("Socket port DataType mismatch between input and output for '"+portInf.name+"': "+portInf.data_type+" and "+thisDataType);
// 'Broadcast' must match
SocketInfo sockInf = sockByName.get(targetName);
if (sockInf.is_bc != isSocketBroadcast(name)) throw new IllegalArgumentException("Mixed BROADCAST usage of socket-port '"+targetName+"'.");
if (isInput) {
sockInf.input_name = name;
} else {
sockInf.output_name = name;
}
portInf.direction = PortDirection.IO;
portInf.addTag("network");
}
}
public static class SocketInfo {
boolean is_bc;
int array_length;
String input_name;
String output_name;
SocketInfo(boolean output, boolean bc, String originalName) {
this.is_bc = bc;
if (output) {
output_name = originalName;
} else {
input_name = originalName;
}
}
}
HashMap<String, Integer> arraySizes = new HashMap<>();
HashMap<String, PortInformation> portByName = new HashMap<>();
HashMap<String, SocketInfo> sockByName = new HashMap<>();
HashSet<String> arrayPorts = new HashSet<>();
SocketInfo getSocketInfo(PortInformation portInfo) {
return sockByName.get(portInfo.name);
}
static boolean isPortArray(String portName) {
return portName.charAt(portName.length()-1) == ']';
}
static int getPortArrayIndex(String portName) {
int p = portName.length()-2;
while (p >= 0 && portName.charAt(p) != '[') --p;
return Integer.parseInt(portName.substring(p+1, portName.length()-1));
}
static String getPortArrayName(String portName) {
int p = portName.length()-2;
while (p >= 0 && portName.charAt(p) != '[') --p;
return portName.substring(0, p);
}
void trackPortArraySize(String name, int index) {
Integer size = arraySizes.get(name);
if (size == null || size < index) arraySizes.put(name, index);
}
static boolean isFirstEncounterAndSet(String name, HashSet<String> encountered) {
if (encountered.contains(name)) return false;
encountered.add(name);
return true;
}
static boolean isSocketPort(String name) {
return name.endsWith(SOCK_BC_IN_SUFFIX) || name.endsWith(SOCK_IN_SUFFIX) || name.endsWith(SOCK_BC_OUT_SUFFIX) || name.endsWith(SOCK_OUT_SUFFIX);
}
static boolean isSocketInput(String name) {
return name.endsWith(SOCK_BC_IN_SUFFIX) || name.endsWith(SOCK_IN_SUFFIX);
}
static boolean isSocketBroadcast(String name) {
return name.endsWith(SOCK_BC_IN_SUFFIX) || name.endsWith(SOCK_BC_OUT_SUFFIX);
}
void addPortInformation(PortInformation portInf) {
// Add portInf and check there is no name clash
if (portByName.containsKey(portInf.name)) {
throw new IllegalArgumentException("Port Name Clash: Multiple EMA ports map to the same name: '"+portInf.name+"' (is one a socket port with suffix?)");
}
portByName.put(portInf.name, portInf);
programInterface.ports.add(portInf);
}
static String removeSuffix(String str, String suffix) {
return str.substring(0, str.length()-suffix.length());
}
static String getSocketTargetName(String name) {
if (name.endsWith(SOCK_BC_IN_SUFFIX)) return removeSuffix(name, SOCK_BC_IN_SUFFIX);
if (name.endsWith(SOCK_IN_SUFFIX)) return removeSuffix(name, SOCK_IN_SUFFIX);
if (name.endsWith(SOCK_BC_OUT_SUFFIX)) return removeSuffix(name, SOCK_BC_OUT_SUFFIX);
if (name.endsWith(SOCK_OUT_SUFFIX)) return removeSuffix(name, SOCK_OUT_SUFFIX);
return name;
}
int evalExpr(ASTExpression expr){
......
......@@ -78,7 +78,7 @@ public class JsonCommunication {
b.init();
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
generateSetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
generateSetter(portInfo.data_type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
b.a(BASE_INDENT, "} break;");
return b.getContent();
......@@ -88,7 +88,7 @@ public class JsonCommunication {
b.init();
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
generateGetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
generateGetter(portInfo.data_type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
b.a(BASE_INDENT, "} break;");
return b.getContent();
......
......@@ -6,9 +6,10 @@ import java.util.HashSet;
import java.util.List;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.cpp.dynamic_interface.DynamicInterfaceGenerator.SocketInfo;
import de.monticore.lang.monticar.generator.cpp.template.AllTemplates;
import de.rwth.montisim.commons.dynamicinterface.*;
import de.rwth.montisim.commons.dynamicinterface.PortInformation.PortDirection;
import de.rwth.montisim.commons.dynamicinterface.PortInformation.PortType;
import de.rwth.montisim.commons.utils.json.SerializationException;
public class TcpCommunication {
......@@ -29,16 +30,23 @@ public class TcpCommunication {
List<String> setInputCases = new ArrayList<>();
List<String> sendOutputCalls = new ArrayList<>();
List<String> sendOutputCases = new ArrayList<>();
List<String> sendSocketOutputsCases = new ArrayList<>();
List<String> initDataCalls = new ArrayList<>();
int i = 0;
for (PortInformation portInfo : gen.programInterface.ports){
initDataCalls.add(generateInitData(portInfo));
if (portInfo.direction == PortDirection.INPUT){
if (portInfo.isInput()){
setInputCases.add(generateSetInputCase(i, portInfo));
} else {
sendOutputCalls.add(String.format("send_output(%d);", i));
sendOutputCases.add(generateSendOutputCase(i, portInfo));
}
if (portInfo.isOutput()) {
if (portInfo.port_type == PortType.DATA) {
sendOutputCalls.add(String.format("send_output(%d);", i));
sendOutputCases.add(generateSendOutputCase(i, portInfo));
} else {
sendOutputCalls.add(String.format("send_socket_outputs(%d);", i));
sendSocketOutputsCases.add(generateSendSocketOutputsCase(i, portInfo));
}
}
++i;
}
......@@ -49,6 +57,7 @@ public class TcpCommunication {
templateData.put("setInputCases", setInputCases);
templateData.put("sendOutputCalls", sendOutputCalls);
templateData.put("sendOutputCases", sendOutputCases);
templateData.put("sendSocketOutputsCases", sendSocketOutputsCases);
templateData.put("initDataCalls", initDataCalls);
templateData.put("useDDC", hasDDC);
......@@ -79,13 +88,28 @@ public class TcpCommunication {
}
public String generateSetInputCase(int id, PortInformation portInfo){
b.init();
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
generateSetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
b.a(BASE_INDENT, "} break;");
if (portInfo.port_type == PortType.DATA) {
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
generateSetter(portInfo.data_type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
b.a(BASE_INDENT, "} break;");
} else {
SocketInfo sockInf = gen.getSocketInfo(portInfo);
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
int indent = BASE_INDENT+1;
b.a(indent, "auto id = get_socket_id(input_packet, %d);", sockInf.array_length);
b.a(indent, "if (id < 0) return;");
b.a(indent, "auto &target = program_instance.%s[id];", sockInf.input_name);
generateSetter(((SimplePacketType) portInfo.data_type).getPayloadType(), BASE_INDENT+1, 1, "target");
b.a(BASE_INDENT, "} break;");
}
return b.getContent();
}
......@@ -94,16 +118,70 @@ public class TcpCommunication {
b.init();
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
generateGetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
generateGetter(portInfo.data_type, BASE_INDENT+1, 1, "program_instance."+portInfo.name);
b.a(BASE_INDENT, "} break;");
return b.getContent();
}
private String generateSendSocketOutputsCase(int id, PortInformation portInfo) {
b.init();
b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name);
SocketInfo sockInf = gen.getSocketInfo(portInfo);
if (sockInf.is_bc) {
b.a(BASE_INDENT+1, "PacketWriter packet(buffer, BUFFER_SIZE, PACKET_OUTPUT);");
b.a(BASE_INDENT+1, "packet.write_u16(port_id);");
b.a(BASE_INDENT+1, "// Payload");
b.a(BASE_INDENT+1, "packet.write_str(N_TO_N_BROADCAST_ADDR); // Write address");
generateGetter(((SimplePacketType) portInfo.data_type).getPayloadType(), BASE_INDENT+1, 1, "program_instance."+sockInf.output_name);
b.a(BASE_INDENT+1, "packet.send(socket);");
} else {
b.a(BASE_INDENT+1, "for (int i = 0; i < %d; ++i) {", sockInf.array_length);
b.a(BASE_INDENT+1, " PacketWriter packet(buffer, BUFFER_SIZE, PACKET_OUTPUT);");
b.a(BASE_INDENT+1, " packet.write_u16(port_id);");
b.a(BASE_INDENT+1, " // Payload");
b.a(BASE_INDENT+1, " packet.write_str(N_TO_N_PREFIX + to_string(i+1)); // Write address");
b.a(BASE_INDENT+1, " auto &target = program_instance.%s[i];", sockInf.output_name);
generateGetter(((SimplePacketType) portInfo.data_type).getPayloadType(), BASE_INDENT+2, 1, "target");
b.a(BASE_INDENT+1, " packet.send(socket);");
b.a(BASE_INDENT+1, "}");
}
b.a(BASE_INDENT, "} break;");
return b.getContent();
}
private String generateInitData(PortInformation portInfo) {
b.init();
generateInitData(portInfo.type, 1, 1, "program_instance."+portInfo.name);
if (portInfo.port_type == PortType.DATA) {
generateInitData(portInfo.data_type, 1, 1, "program_instance."+portInfo.name);
} else {
DataType dt = ((SimplePacketType) portInfo.data_type).getPayloadType();
SocketInfo sockInf = gen.getSocketInfo(portInfo);
if (portInfo.isInput()) {
for (int i =0; i < sockInf.array_length; ++i) {
String varName = sockInf.input_name + Integer.toString(i);
b.a(1, "auto &%s = program_instance.%s[%d];", varName, sockInf.input_name, i);
generateInitData(dt, 1, 1, varName);
}
}
if (portInfo.isOutput()) {
if (sockInf.is_bc) {
generateInitData(dt, 1, 1, "program_instance."+sockInf.output_name);
} else {
for (int i =0; i < sockInf.array_length; ++i) {
String varName = sockInf.output_name + Integer.toString(i);
b.a(1, "auto &%s = program_instance.%s[%d];", varName, sockInf.output_name, i);
generateInitData(dt, 1, 1, varName);
}
}
}
}
return b.getContent();
}
......
......@@ -21,7 +21,7 @@ constexpr int BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
void signal_handler(int signal);
int get_socket_id(PacketReader &input_packet, int max_count);
......@@ -170,6 +170,20 @@ void SimulationSession::init(PacketReader &init_packet) {
cout << "Adapter initialization complete. Ready to run the program." << endl;
}
int get_socket_id(PacketReader &input_packet, int max_count) {
std::string ip = input_packet.read_str();
if (ip.find("2::") != 0) {
cerr << "Unsupported IP: '"<< ip << "' (only supports 'N-to-N' IPs with '2::' prefix)" << endl;
}
int id = strtol(ip.c_str()+3, nullptr, 10)-1;
if (id >= max_count) {
cerr << "'N-to-N' IP '" << ip << "' outside of range [1:"<< (max_count) << "]. Ignoring packet." << endl;
return -1;
}
return id;
}
void SimulationSession::set_input(PacketReader &input_packet) {
auto port_id = input_packet.read_u16();
......@@ -219,6 +233,19 @@ void SimulationSession::send_output(int port_id){
// Send packet
packet.send(socket);
}
static constexpr auto N_TO_N_BROADCAST_ADDR = "ff02::2";
static constexpr auto N_TO_N_PREFIX = "2::";
void SimulationSession::send_socket_outputs(int port_id){
switch (port_id) {
<#list sendSocketOutputsCases as case>${case}</#list>
default:
cerr << "Invalid output port ID: '"<< port_id << "'" << endl;
throw exception();
}
}
void SimulationSession::send_time(double time) {
PacketWriter writer(buffer, BUFFER_SIZE, PACKET_TIME);
writer.write_f64(time);
......
......@@ -28,6 +28,7 @@ struct SimulationSession {
void set_input(PacketReader &input_packet);
void run_cycle(PacketReader &run_packet);
void send_output(int port_id);
void send_socket_outputs(int port_id);
void send_time(double time);
void init_program_data();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment