diff --git a/.gitignore b/.gitignore index 7c4733d91096c9a15901c5eb7e6ecd707ed84808..e19038f5155a20c46bd04d911f29f3745d906f03 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ buildNumber.properties .vscode/ .settings/ +temp/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index e20d2a5608ff8b61eba9c027264759852a2bff6c..eeb433bc93e55dc3cbe6513dba6fb86dba86785b 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ de.monticore.lang.monticar embedded-montiarc-math-generator - 0.4.0-SNAPSHOT + 0.4.1 @@ -67,6 +67,13 @@ provided + + + montisim + commons + 2.0.6 + + montiarc.verification diff --git a/src/main/java/de/monticore/lang/monticar/generator/cmake/CMakeConfig.java b/src/main/java/de/monticore/lang/monticar/generator/cmake/CMakeConfig.java index bd41fa99e58bbc97e4b2bcdd21d242d48e2505de..0fa99669ce1265e35537ff8edf6a295d3081f7f9 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/cmake/CMakeConfig.java +++ b/src/main/java/de/monticore/lang/monticar/generator/cmake/CMakeConfig.java @@ -60,6 +60,7 @@ public class CMakeConfig { public CMakeConfig(String compName) { cMakeListsViewModel.setCompName(compName); cMakeListsViewModel.setModuleDependencies(moduleList); + configureCMakeListsViewModel(); } // methods @@ -107,7 +108,7 @@ public class CMakeConfig { public FileContent generateCMakeLists() { FileContent result = null; - configureCMakeListsViewModel(); + //configureCMakeListsViewModel(); // map data Map dataForTemplate = TemplateHelper.getDataForTemplate(cMakeListsViewModel); // try generate file content diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/FileUtil.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/FileUtil.java index ee896eb0ab214d0b340262abc2491356136d55b6..b7d3bcc360f0ebc8b275f9700d668aff2cfd9b0b 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/cpp/FileUtil.java +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/FileUtil.java @@ -13,6 +13,7 @@ public final class FileUtil { public static FileContent getResourceAsFile(String resourcePath, String destinationFilePath) { InputStream resource = FileUtil.class.getResourceAsStream(resourcePath); + if (resource == null) throw new IllegalArgumentException("Could not get resource: "+resourcePath); String body = new Scanner(resource, "UTF-8").useDelimiter("\\A").next(); return new FileContent(body, destinationFilePath); } diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCPP.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCPP.java index 3c72b63e1994ea21bc77c56dde8220d6e0bee935..36faff69d482b81045737c3e21a0b42686dd1c34 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCPP.java +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCPP.java @@ -4,7 +4,6 @@ package de.monticore.lang.monticar.generator.cpp; import de.ma2cfg.helper.Names; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc.ComponentScanner; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; -import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAPortInstanceSymbol; import de.monticore.lang.embeddedmontiarcdynamic.embeddedmontiarcdynamic._symboltable.instanceStructure.EMADynamicComponentInstanceSymbol; import de.monticore.lang.math._symboltable.MathStatementsSymbol; import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol; @@ -14,14 +13,15 @@ import de.monticore.lang.monticar.generator.cmake.CMakeFindModule; import de.monticore.lang.monticar.generator.cpp.Dynamics.DynamicHelper; import de.monticore.lang.monticar.generator.cpp.Dynamics.EventPortValueCheck; import de.monticore.lang.monticar.generator.cpp.converter.*; +import de.monticore.lang.monticar.generator.cpp.dynamic_interface.DynamicInterfaceGenerator; import de.monticore.lang.monticar.generator.cpp.mathopt.MathOptSolverConfig; import de.monticore.lang.monticar.generator.cpp.template.AllTemplates; -import de.monticore.lang.monticar.generator.cpp.viewmodel.AutopilotAdapterDataModel; import de.monticore.lang.monticar.generator.cpp.viewmodel.ServerWrapperViewModel; import de.monticore.lang.monticar.generator.testing.StreamTestGenerator; import de.monticore.lang.monticar.ts.MCTypeSymbol; import de.monticore.lang.tagging._symboltable.TaggingResolver; import de.monticore.symboltable.Scope; +import de.rwth.montisim.commons.utils.json.SerializationException; import de.se_rwth.commons.logging.Log; import java.io.BufferedWriter; @@ -37,7 +37,11 @@ public class GeneratorCPP implements EMAMGenerator { public static GeneratorCPP currentInstance; private Path modelsDirPath; private boolean isGenerateTests = false; - private boolean isGenerateAutopilotAdapter = false; + private boolean genDynamicInterface = false; + private boolean genServerAdapter = false; + private boolean genDDCAdapter = false; + private boolean importArmadillo = false; + private String outputName = ""; private boolean isGenerateServerWrapper = false; protected boolean isExecutionLoggingActive = false; private final List bluePrints = new ArrayList<>(); @@ -58,9 +62,7 @@ public class GeneratorCPP implements EMAMGenerator { private boolean generateCMake = false; private CMakeConfig cMakeConfig; - - - //MathOpt + // MathOpt private MathOptSolverConfig mathOptSolverConfig = new MathOptSolverConfig(); private OptimizationSymbolHandler mathOptExecuteMethodGenerator = new OptimizationSymbolHandler(); private MathOptFunctionFixer mathOptFunctionFixer = new MathOptFunctionFixer(); @@ -76,7 +78,6 @@ public class GeneratorCPP implements EMAMGenerator { mathOptFunctionFixer.setSuccessor(MathFunctionFixer.getInstance()); } - public boolean isExecutionLoggingActive() { return isExecutionLoggingActive; } @@ -89,7 +90,21 @@ public class GeneratorCPP implements EMAMGenerator { cMakeConfig = new CMakeConfig(""); if (usesArmadilloBackend()) { // add dependency on module Armadillo - cMakeConfig.addModuleDependency(new CMakeFindModule("Armadillo", true)); + if (importArmadillo) { + // cMakeConfig.addCMakeCommand("SET(BUILD_SHARED_LIBS OFF)"); + // cMakeConfig.addCMakeCommand("SET(CMAKE_EXE_LINKER_FLAGS \"-static\")"); + // cMakeConfig.addCMakeCommand("add_subdirectory($ENV{ARMADILLO_PATH} armadillo)"); + // cMakeConfig.addCMakeCommand("set(LIBS ${LIBS} armadillo)"); + //cMakeConfig.addCMakeCommand("target_link_libraries(armadillo -static)"); + + cMakeConfig.addCMakeCommand("# Add simple Wrapper for header-only armadillo"); + cMakeConfig.addCMakeCommand("add_library(armadillo INTERFACE)"); + cMakeConfig.addCMakeCommand("target_include_directories(armadillo INTERFACE $ENV{ARMADILLO_PATH}/include)"); + cMakeConfig.addCMakeCommand("target_compile_definitions(armadillo INTERFACE ARMA_DONT_USE_WRAPPER)"); + cMakeConfig.addCMakeCommand("set(LIBS ${LIBS} armadillo)"); + } else { + cMakeConfig.addModuleDependency(new CMakeFindModule("Armadillo", true)); + } } } @@ -118,7 +133,8 @@ public class GeneratorCPP implements EMAMGenerator { public void useOctaveBackend() { MathConverter.curBackend = new OctaveBackend(); setupCMake(); - //Log.warn("This backend has been deprecated. Armadillo is the recommended backend now."); + // Log.warn("This backend has been deprecated. Armadillo is the recommended + // backend now."); } public String generateString(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentInstanceSymbol) { @@ -148,8 +164,10 @@ public class GeneratorCPP implements EMAMGenerator { this.generationTargetPath = newPath; } - public String generateString(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentSymbol, MathStatementsSymbol mathStatementsSymbol) { - StreamTestGenerator streamTestGenerator = new StreamTestGenerator();//only used when creating streamTestsForAComponent + public String generateString(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentSymbol, + MathStatementsSymbol mathStatementsSymbol) { + StreamTestGenerator streamTestGenerator = new StreamTestGenerator();// only used when creating + // streamTestsForAComponent LanguageUnitCPP languageUnitCPP = new LanguageUnitCPP(); languageUnitCPP.setGeneratorCPP(this); languageUnitCPP.addSymbolToConvert(componentSymbol); @@ -180,28 +198,34 @@ public class GeneratorCPP implements EMAMGenerator { public static List currentFileContentList = null; @Override - public List generateStrings(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentInstanceSymbol) { + public List generateStrings(TaggingResolver taggingResolver, + EMAComponentInstanceSymbol componentInstanceSymbol) { List fileContents = new ArrayList<>(); if (componentInstanceSymbol.getFullName().equals("simulator.mainController")) { setGenerateSimulatorInterface(true); } else { - //setGenerateMainClass(true); + // setGenerateMainClass(true); } currentFileContentList = fileContents; if (!streamTestGenerationMode) - fileContents.add(new FileContent(generateString(taggingResolver, componentInstanceSymbol), componentInstanceSymbol)); + fileContents.add( + new FileContent(generateString(taggingResolver, componentInstanceSymbol), componentInstanceSymbol)); else fileContents.add(new FileContent(generateString(taggingResolver, componentInstanceSymbol), - componentInstanceSymbol.getPackageName().replaceAll("\\.", "\\/") + "/" + Names.FirstUpperCase(componentInstanceSymbol.getName()) + "Test" + testNamePostFix + ".stream")); + componentInstanceSymbol.getPackageName().replaceAll("\\.", "\\/") + "/" + + Names.FirstUpperCase(componentInstanceSymbol.getName()) + "Test" + testNamePostFix + + ".stream")); String lastNameWithoutArrayPart = ""; if (!streamTestGenerationMode) { for (EMAComponentInstanceSymbol instanceSymbol : componentInstanceSymbol.getSubComponents()) { - //fileContents.add(new FileContent(generateString(instanceSymbol, symtab), instanceSymbol)); + // fileContents.add(new FileContent(generateString(instanceSymbol, symtab), + // instanceSymbol)); int arrayBracketIndex = instanceSymbol.getName().indexOf("["); boolean generateComponentInstance = true; if (arrayBracketIndex != -1) { - generateComponentInstance = !instanceSymbol.getName().substring(0, arrayBracketIndex).equals(lastNameWithoutArrayPart); + generateComponentInstance = !instanceSymbol.getName().substring(0, arrayBracketIndex) + .equals(lastNameWithoutArrayPart); lastNameWithoutArrayPart = instanceSymbol.getName().substring(0, arrayBracketIndex); Log.info(lastNameWithoutArrayPart, "Without:"); Log.info(generateComponentInstance + "", "Bool:"); @@ -220,7 +244,8 @@ public class GeneratorCPP implements EMAMGenerator { } } if (shouldGenerateMainClass()) { - //fileContents.add(getMainClassFileContent(componentInstanceSymbol, fileContents.get(0))); + // fileContents.add(getMainClassFileContent(componentInstanceSymbol, + // fileContents.get(0))); } else if (shouldGenerateSimulatorInterface()) { fileContents.addAll(SimulatorIntegrationHelper.getSimulatorIntegrationHelperFileContent()); } @@ -230,18 +255,19 @@ public class GeneratorCPP implements EMAMGenerator { if (MathConverter.curBackend.getBackendName().equals("ArmadilloBackend")) fileContents.add(ArmadilloHelper.getArmadilloHelperFileContent(isGenerateTests)); - if(componentInstanceSymbol instanceof EMADynamicComponentInstanceSymbol){ - //TODO: add Events Value Helper - if(!((EMADynamicComponentInstanceSymbol) componentInstanceSymbol).getEventHandlers().isEmpty()) + if (componentInstanceSymbol instanceof EMADynamicComponentInstanceSymbol) { + // TODO: add Events Value Helper + if (!((EMADynamicComponentInstanceSymbol) componentInstanceSymbol).getEventHandlers().isEmpty()) fileContents.add(EventPortValueCheck.getEventPortValueCheckFileContent()); - if(((EMADynamicComponentInstanceSymbol)componentInstanceSymbol).isDynamic()){ + if (((EMADynamicComponentInstanceSymbol) componentInstanceSymbol).isDynamic()) { fileContents.add(DynamicHelper.getDynamicHelperFileContent()); } } if (shouldGenerateMainClass()) { - //fileContents.add(getMainClassFileContent(componentInstanceSymbol, fileContents.get(0))); + // fileContents.add(getMainClassFileContent(componentInstanceSymbol, + // fileContents.get(0))); } else if (shouldGenerateSimulatorInterface()) { fileContents.addAll(SimulatorIntegrationHelper.getSimulatorIntegrationHelperFileContent()); } @@ -249,18 +275,18 @@ public class GeneratorCPP implements EMAMGenerator { return fileContents; } - //TODO add incremental generation based on described concept - public List generateFiles(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentSymbol) throws IOException { + // TODO add incremental generation based on described concept + public List generateFiles(TaggingResolver taggingResolver, EMAComponentInstanceSymbol componentSymbol) + throws IOException { List fileContents = new ArrayList<>(); if (componentSymbol == null) { ComponentScanner componentScanner = new ComponentScanner(getModelsDirPath(), taggingResolver, "emam"); Set availableComponents = componentScanner.scan(); for (String componentFullName : availableComponents) { componentFullName = Names.getExpandedComponentInstanceSymbolName(componentFullName); - if (taggingResolver.resolve(componentFullName, - EMAComponentInstanceSymbol.KIND).isPresent()) { - EMAComponentInstanceSymbol componentInstanceSymbol = (EMAComponentInstanceSymbol) taggingResolver.resolve(componentFullName, - EMAComponentInstanceSymbol.KIND).get(); + if (taggingResolver.resolve(componentFullName, EMAComponentInstanceSymbol.KIND).isPresent()) { + EMAComponentInstanceSymbol componentInstanceSymbol = (EMAComponentInstanceSymbol) taggingResolver + .resolve(componentFullName, EMAComponentInstanceSymbol.KIND).get(); fileContents.addAll(generateStrings(taggingResolver, componentInstanceSymbol)); } } @@ -270,9 +296,9 @@ public class GeneratorCPP implements EMAMGenerator { } fileContents.addAll(generateTypes(TypeConverter.getTypeSymbols())); fileContents.addAll(handleTestAndCheckDir(taggingResolver, componentSymbol)); - if (isGenerateAutopilotAdapter()) { - fileContents.addAll(getAutopilotAdapterFiles(componentSymbol)); - } + + generateAdapters(fileContents, componentSymbol); + if (isGenerateServerWrapper()) { fileContents.addAll(getServerWrapperFiles(componentSymbol)); } @@ -303,6 +329,27 @@ public class GeneratorCPP implements EMAMGenerator { return files; } + + public void generateAdapters(List fileContents, EMAComponentInstanceSymbol component) { + if (genDynamicInterface || genServerAdapter || genDDCAdapter) { + try { + fileContents.addAll( + new DynamicInterfaceGenerator( + component, + cMakeConfig, + outputName, + genDynamicInterface, + genServerAdapter, + genDDCAdapter + ).getFiles() + ); + } catch (SerializationException | IOException e) { + throw new RuntimeException(e); + } + } + } + + public List saveFilesToDisk(List fileContents) throws IOException { List files = new ArrayList<>(); for (FileContent fileContent : fileContents) { @@ -434,12 +481,44 @@ public class GeneratorCPP implements EMAMGenerator { isGenerateTests = generateTests; } - public boolean isGenerateAutopilotAdapter() { - return isGenerateAutopilotAdapter; + public boolean isGenerateDynamicInterface() { + return genDynamicInterface; + } + + public void setImportArmadillo(boolean doImport) { + this.importArmadillo = doImport; } - public void setGenerateAutopilotAdapter(boolean generateAutopilotAdapter) { - isGenerateAutopilotAdapter = generateAutopilotAdapter; + public boolean isImportArmadillo() { + return importArmadillo; + } + + public void setGenerateDynamicInterface(boolean gen) { + genDynamicInterface = gen; + } + + public boolean isGenerateServerAdapter() { + return genServerAdapter; + } + + public void setGenerateServerAdapter(boolean gen) { + genServerAdapter = gen; + } + + public boolean isGenerateDDCAdapter() { + return genDDCAdapter; + } + + public void setGenerateDDCAdapter(boolean gen) { + genDDCAdapter = gen; + } + + public void setOutputName(String name) { + this.outputName = name; + } + + public String getOutputName() { + return outputName; } public boolean isGenerateServerWrapper() { @@ -467,55 +546,6 @@ public class GeneratorCPP implements EMAMGenerator { return tg.generateTypes(typeSymbols); } - private static List getAutopilotAdapterFiles(EMAComponentInstanceSymbol componentSymbol) { - List result = new ArrayList<>(); - - AutopilotAdapterDataModel dm = new AutopilotAdapterDataModel(); - dm.setMainModelName(GeneralHelperMethods.getTargetLanguageComponentName(componentSymbol.getFullName())); - dm.setInputCount(componentSymbol.getIncomingPortInstances().size()); - dm.setOutputCount(componentSymbol.getOutgoingPortInstances().size()); - for ( EMAPortInstanceSymbol port : componentSymbol.getIncomingPortInstances()){ - dm.addInput(port.getName(), port.getTypeReference().getName()); - } - for ( EMAPortInstanceSymbol port : componentSymbol.getOutgoingPortInstances()){ - dm.addOutput(port.getName(), port.getTypeReference().getName()); - } - - result.add(generateAutopilotAdapterH(dm)); - result.add(generateAutopilotAdapterCpp(dm)); - return result; - } - - private static FileContent generateAutopilotAdapterH(AutopilotAdapterDataModel dm) { - String fileContents = AllTemplates.generateAutopilotAdapterH(dm); - return new FileContent(fileContents, "AutopilotAdapter.h"); - } - - private static FileContent generateAutopilotAdapterCpp(AutopilotAdapterDataModel dm) { - String fileContents = AllTemplates.generateAutopilotAdapterCpp(dm); - if (currentInstance.generateCMake) - addAutopilotAdapterCMakeConfig(); - return new FileContent(fileContents, "AutopilotAdapter.cpp"); - } - - private static void addAutopilotAdapterCMakeConfig() { - CMakeConfig cmake = currentInstance.cMakeConfig; - // add jni - cmake.addCMakeCommand("find_package(JNI)"); - cmake.addCMakeCommand("set(INCLUDE_DIRS ${INCLUDE_DIRS} ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})"); - // create shared lib - cmake.addCMakeCommandEnd("add_library(AutopilotAdapter SHARED AutopilotAdapter.cpp ${CMAKE_CURRENT_SOURCE_DIR})"); - cmake.addCMakeCommandEnd("target_include_directories(AutopilotAdapter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})"); - cmake.addCMakeCommandEnd("target_link_libraries(AutopilotAdapter PUBLIC ${LIBS})"); - cmake.addCMakeCommandEnd("set_target_properties(AutopilotAdapter PROPERTIES LINKER_LANGUAGE CXX)"); - cmake.addCMakeCommand("IF (WIN32)"); - cmake.addCMakeCommandEnd("set_target_properties(AutopilotAdapter PROPERTIES PREFIX \"\")"); - cmake.addCMakeCommand("ENDIF()"); - // install shared lib - cmake.addCMakeCommandEnd("install(TARGETS AutopilotAdapter DESTINATION $ENV{DLL_DIR})"); - cmake.addCMakeCommandEnd("export(TARGETS AutopilotAdapter FILE de_rwth_armin_modeling_autopilot_autopilotAdapter.cmake)"); - } - private static List getServerWrapperFiles(EMAComponentInstanceSymbol componentSymbol) { List result = new ArrayList<>(); String[] filesToCopy = new String[]{ diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCppCli.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCppCli.java index cc893e888a289e2429cbd7317cbdd345f37c59a0..15d2866e1ec32f7ae702957006878e869e8433c6 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCppCli.java +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/GeneratorCppCli.java @@ -53,6 +53,13 @@ public final class GeneratorCppCli { .required(false) .build(); + public static final Option OPTION_OUTPUT_NAME = Option.builder("n") + .longOpt("output-name") + .desc("Name for the dynamic-interface or server adapter.") + .hasArg(true) + .required(false) + .build(); + public static final Option OPTION_FLAG_TESTS = Option.builder("t") .longOpt("flag-generate-tests") .desc("optional flag indicating if tests generation is needed") @@ -67,6 +74,13 @@ public final class GeneratorCppCli { .required(false) .build(); + public static final Option OPTION_IMPORT_ARMADILLO = Option.builder() + .longOpt("armadillo-import") + .desc("If enabled, the project will include Armadillo for compilation based on the ARMADILLO_PATH environment variable") + .hasArg(false) + .required(false) + .build(); + public static final Option OPTION_FLAG_ALGEBRAIC = Option.builder("a") .longOpt("flag-use-algebraic") .desc("optional flag indicating if algebraic optimizations should be on") @@ -89,9 +103,22 @@ public final class GeneratorCppCli { .required(false) .build(); - public static final Option OPTION_FLAG_AUTOPILOT_ADAPTER = Option.builder() - .longOpt("flag-generate-autopilot-adapter") - .desc("optional flag indicating if autopilot adapter should be generated") + + public static final Option OPTION_FLAG_DYNAMIC_INTERFACE = Option.builder("di") + .longOpt("dyn-interface") + .desc("Enable autopilot adapter generation") + .hasArg(false) + .required(false) + .build(); + public static final Option OPTION_FLAG_GEN_TCP_SERVER = Option.builder("tcp") + .longOpt("tcp-adapter") + .desc("Generate the TCP-Server adapter for the model") + .hasArg(false) + .required(false) + .build(); + public static final Option OPTION_FLAG_GEN_DDC_ADAPTER = Option.builder("ddc") + .longOpt("ddc-adapter") + .desc("Generate the DDC adapter for the model") .hasArg(false) .required(false) .build(); @@ -150,7 +177,11 @@ public final class GeneratorCppCli { public static void addEMAM2CPPOptions(Options options) { options.addOption(OPTION_FLAG_TESTS); options.addOption(OPTION_FLAG_ARMADILLO); - options.addOption(OPTION_FLAG_AUTOPILOT_ADAPTER); + options.addOption(OPTION_IMPORT_ARMADILLO); + options.addOption(OPTION_FLAG_DYNAMIC_INTERFACE); + options.addOption(OPTION_OUTPUT_NAME); + options.addOption(OPTION_FLAG_GEN_TCP_SERVER); + options.addOption(OPTION_FLAG_GEN_DDC_ADAPTER); options.addOption(OPTION_FLAG_CHECK_MODEL_DIR); options.addOption(OPTION_FLAG_SERVER_WRAPPER); options.addOption(OPTION_FLAG_ALGEBRAIC); @@ -184,12 +215,16 @@ public final class GeneratorCppCli { g.setModelsDirPath(modelsDirPath); g.setGenerationTargetPath(outputPath); g.setGenerateTests(cliArgs.hasOption(OPTION_FLAG_TESTS.getOpt())); + g.setImportArmadillo(cliArgs.hasOption(OPTION_IMPORT_ARMADILLO.getLongOpt())); if (cliArgs.hasOption(OPTION_FLAG_ARMADILLO.getOpt())) { g.useArmadilloBackend(); } g.setCheckModelDir(cliArgs.hasOption(OPTION_FLAG_CHECK_MODEL_DIR.getLongOpt())); g.setGenerateServerWrapper(cliArgs.hasOption(OPTION_FLAG_SERVER_WRAPPER.getLongOpt())); - g.setGenerateAutopilotAdapter(cliArgs.hasOption(OPTION_FLAG_AUTOPILOT_ADAPTER.getLongOpt())); + g.setGenerateDynamicInterface(cliArgs.hasOption(OPTION_FLAG_DYNAMIC_INTERFACE.getLongOpt())); + g.setGenerateServerAdapter(cliArgs.hasOption(OPTION_FLAG_GEN_TCP_SERVER.getLongOpt())); + g.setGenerateDDCAdapter(cliArgs.hasOption(OPTION_FLAG_GEN_DDC_ADAPTER.getLongOpt())); + g.setOutputName(cliArgs.getOptionValue(OPTION_OUTPUT_NAME.getOpt())); g.setUseAlgebraicOptimizations(cliArgs.hasOption(OPTION_FLAG_ALGEBRAIC.getLongOpt())); g.setUseThreadingOptimization(cliArgs.hasOption(OPTION_FLAG_THREADING.getLongOpt())); diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DDCCommunication.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DDCCommunication.java new file mode 100644 index 0000000000000000000000000000000000000000..fab96b81db761a7c794f399a9d2dfc28eb8b2d74 --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DDCCommunication.java @@ -0,0 +1,35 @@ +package de.monticore.lang.monticar.generator.cpp.dynamic_interface; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import de.monticore.lang.monticar.generator.FileContent; +import de.rwth.montisim.commons.utils.json.SerializationException; + +public class DDCCommunication { + static final String BASE_SPACES = " "; + + final DynamicInterfaceGenerator gen; + + FileBuilder b = new FileBuilder(); + + + public DDCCommunication(DynamicInterfaceGenerator gen) { + this.gen = gen; + throw new IllegalArgumentException("DDCCommunication not implemented yet."); + } + + public List generate() throws SerializationException { + List files = new ArrayList<>(); + + return files; + } + + public void getSources(HashSet sources) { + sources.add("ddc_mode.cpp"); + } + public void getLibs(HashSet libs) { + libs.add("ddc_load"); + } +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DynamicInterfaceGenerator.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DynamicInterfaceGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..bf55d24670629f4de25ba67a61e6e32bd6ddd59e --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/DynamicInterfaceGenerator.java @@ -0,0 +1,321 @@ +package de.monticore.lang.monticar.generator.cpp.dynamic_interface; + +import java.io.IOException; +import java.util.ArrayList; +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.utils.json.Json; +import de.rwth.montisim.commons.utils.json.SerializationException; +import de.monticore.ast.ASTNode; +import de.monticore.expressionsbasis._ast.ASTExpression; +import de.monticore.javaclassexpressions._ast.ASTNameExpression; +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAPortInstanceSymbol; +import de.monticore.lang.math._ast.ASTNumberExpression; +import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol; +import de.monticore.lang.monticar.common2._ast.ASTCommonMatrixType; +import de.monticore.lang.monticar.generator.FileContent; +import de.monticore.lang.monticar.generator.cmake.CMakeConfig; +import de.monticore.lang.monticar.generator.cpp.FileUtil; +import de.monticore.lang.monticar.generator.cpp.GeneralHelperMethods; +import de.monticore.lang.monticar.struct._ast.ASTStruct; +import de.monticore.lang.monticar.struct._ast.ASTStructFieldDefinition; +import de.monticore.lang.monticar.ts.MCTypeSymbol; +import de.monticore.lang.monticar.ts.references.MCASTTypeSymbolReference; +import de.monticore.lang.monticar.ts.references.MCTypeReference; +import de.monticore.lang.monticar.types2._ast.ASTElementType; +import de.monticore.numberunit._ast.ASTNumberWithUnit; +import de.monticore.types.types._ast.ASTType; + +/* + Generates the CPP files necessary to build the 'DynamicInterface' + + DISCLAIMER: Do not assume the use of the EmbeddedMontiArc framework here is optimal, someone with experience in + the EMA suite should verify how the different types and symbols are resolved. +*/ +public class DynamicInterfaceGenerator { + + HashSet cppFileDependencies = new HashSet<>(); + List files = new ArrayList<>(); + + String componentName; + ProgramInterface programInterface; + String progInterfaceString; + JsonCommunication dyn; + TcpCommunication tcp; + DDCCommunication ddc; + + public DynamicInterfaceGenerator( + EMAComponentInstanceSymbol componentSymbol, + CMakeConfig cmake, + String outputName, + boolean genDynamicInterface, + boolean genServer, + boolean genDDC + ) throws SerializationException, IOException { + // Read the ProgramInterface from the model + resolve(componentSymbol); + progInterfaceString = Json.toJson(programInterface); + + // Generate the files + if (genDynamicInterface) { + dyn = new JsonCommunication(this); + files.addAll(dyn.generate()); + } + if (genServer || genDDC) { + tcp = new TcpCommunication(this); + files.addAll(tcp.generate(genDDC)); + } + if (genDDC) { + ddc = new DDCCommunication(this); + files.addAll(ddc.generate()); + } + // Add the common file dependencies + for (String f : cppFileDependencies) { + files.add(FileUtil.getResourceAsFile("/template/dynamic_interface/"+f, f)); + } + + if (outputName.length() == 0) outputName = componentSymbol.getName(); + + // Generate the CMake + if (genDynamicInterface) { + dyn.addCMake(cmake, outputName); + } + if (genServer || genDDC) { + String targetName = outputName+"Adapter"; + HashSet sources = new HashSet<>(); + HashSet libs = new HashSet<>(); + + tcp.getSources(sources); + tcp.getLibs(libs); + + if (genDDC) { + ddc.getSources(sources); + ddc.getLibs(libs); + } + + String sourceFiles = ""; + for (String s : sources) sourceFiles += s + ' '; + + // create adapter executable + cmake.addCMakeCommandEnd("# Server (TCP) / DDC adapter"); + cmake.addCMakeCommandEnd("add_executable("+targetName+" "+sourceFiles+")"); + if (!libs.isEmpty()) { + String libList = ""; + for (String l : libs) libList += l + ' '; + cmake.addCMakeCommandEnd("target_link_libraries("+targetName+" PUBLIC "+libs+")"); + } + cmake.addCMakeCommandEnd("target_link_libraries("+targetName+" PUBLIC ${LIBS})"); + cmake.addCMakeCommandEnd("target_compile_features("+targetName+" PUBLIC cxx_std_11)"); + cmake.addCMakeCommandEnd(""); + } + } + + public List getFiles() { + return files; + } + + public void addCppFileDependency(String name) { + cppFileDependencies.add(name); + } + + + void resolve(EMAComponentInstanceSymbol componentSymbol){ + programInterface = new ProgramInterface(); + programInterface.name = componentSymbol.getName(); + programInterface.version = "0.0"; + + // 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.getOutgoingPortInstances()){ + boolean multipleInputsAllowed = false; // TODO + boolean optional = false; // TODO + programInterface.ports.add(new PortInformation(port.getName(), getDataType(port), PortDirection.OUTPUT, multipleInputsAllowed, optional)); + } + + } + + int evalExpr(ASTExpression expr){ + if (expr.getSymbolOpt().isPresent() && expr.getSymbolOpt().get() instanceof MathExpressionSymbol) + return Integer.parseInt(((MathExpressionSymbol) expr.getSymbolOpt().get()).getTextualRepresentation()); + else if (expr instanceof ASTNameExpression) + throw new IllegalArgumentException("Unknown dimension description: "+expr); + else if (expr instanceof ASTNumberWithUnit){ + return ((ASTNumberWithUnit) expr).getNumber().get().intValue(); + } else if (expr instanceof ASTNumberExpression) { + return ((ASTNumberExpression) expr).getNumberWithUnit().getNumber().get().intValue(); + } else throw new IllegalArgumentException("Could not evaluate expression: "+expr); + } + + DataType getVectorType(int size, DataType subtype){ + if (size == 2) { + return BasicType.VEC2; + } else if (size == 3) { + return BasicType.VEC3; + } else { + return new VectorType(subtype, size); + } + } + + ASTCommonMatrixType getMatrixType(MCTypeReference mctype){ + if (mctype.existsReferencedSymbol() && mctype.getReferencedSymbol() instanceof MCASTTypeSymbolReference){ + ASTType astType = ((MCASTTypeSymbolReference) mctype.getReferencedSymbol()).getAstType(); + if (astType instanceof ASTCommonMatrixType) return (ASTCommonMatrixType)astType; + } + throw new IllegalArgumentException("Could not get Matrix AST type from mctype"); + } + + DataType getDataType2(ASTType astType){ + // AssignmentType ? + String typeName = astType.getSymbol().getName(); + if (typeName.equals("Q")){ + return BasicType.Q; + } else if (typeName.equals("Z")) { + return BasicType.Z; + } else if (typeName.equals("N")) { + return BasicType.N; + } else if (typeName.equals("N1")) { + return BasicType.N1; + } else if (typeName.equals("B") || typeName.equals("Boolean")) { + return BasicType.BOOLEAN; + } else if (typeName.equals("C")) { + return BasicType.C; + } else if (typeName.equals("CommonMatrixType")) { + if (!(astType instanceof ASTCommonMatrixType)) throw new IllegalArgumentException("Could not get Matrix AST type from ASTType"); + ASTCommonMatrixType mt = (ASTCommonMatrixType)astType; + ASTElementType type = mt.getElementType(); + DataType subtype = getDataType2(type); + if (subtype == null) throw new IllegalArgumentException("Unsupported sub-type for matrix: "+type.getName()); + List dim = mt.getDimension().getDimensionList(); + if (dim.size() == 1) { + // Array type + int size = evalExpr(dim.get(0)); + return getVectorType(size, subtype); + } else if (dim.size() == 2) { + // Matrix type => represent with array of arrays + int rowCount = evalExpr(dim.get(0)); + int colCount = evalExpr(dim.get(1)); + if (rowCount == 1 || colCount == 1){ + return getVectorType(Math.max(rowCount, colCount), subtype); + } + return new MatrixType(subtype, rowCount, colCount); + } else throw new IllegalArgumentException("Unknown dimensions: "+dim.size()); + //return new VectorType(BasicType.DOUBLE, 128); + } + + Optional n = astType.getSymbol().getAstNode(); + if (n.isPresent()){ + ASTNode node = n.get(); + if (node instanceof ASTStruct){ + ASTStruct struct = (ASTStruct) node; + StructType type = new StructType(typeName); + //System.out.println("Struct fields for "+typeName); + for (ASTStructFieldDefinition fd : struct.getStructFieldDefinitionList()){ + //System.out.println(fd.getName()); + DataType fieldType = getDataType2(fd.getType()); + type.addField(fd.getName(), fieldType); + } + return type; + } else { + int a = 0; + } + } + + System.out.println("Missing DataType conversion: "+typeName); + return BasicType.EMPTY; + } + + DataType getBasicType(String typeName){ + if (typeName.equals("Q")){ + return BasicType.Q; + } else if (typeName.equals("Z")) { + return BasicType.Z; + } else if (typeName.equals("N")) { + return BasicType.N; + } else if (typeName.equals("N1")) { + return BasicType.N1; + } else if (typeName.equals("B") || typeName.equals("Boolean")) { + return BasicType.BOOLEAN; + } else if (typeName.equals("C")) { + return BasicType.C; + } + return null; + } + + // TODO from ASTType ?? + DataType getDataType(EMAPortInstanceSymbol port){ + // AssignmentType ? + MCTypeReference mctype = port.getTypeReference(); + String typeName = mctype.getName(); + if (typeName.equals("Q")){ + return BasicType.Q; + } else if (typeName.equals("Z")) { + return BasicType.Z; + } else if (typeName.equals("N")) { + return BasicType.N; + } else if (typeName.equals("N1")) { + return BasicType.N1; + } else if (typeName.equals("B") || typeName.equals("Boolean")) { + return BasicType.BOOLEAN; + } else if (typeName.equals("C")) { + return BasicType.C; + } else if (typeName.equals("CommonMatrixType")) { + ASTCommonMatrixType mt = getMatrixType(mctype); + ASTElementType type = mt.getElementType(); + DataType subtype = getBasicType(type.getName()); + if (subtype == null) throw new IllegalArgumentException("Unsupported sub-type for matrix: "+type.getName()); + List dim = mt.getDimension().getDimensionList(); + if (dim.size() == 1) { + // Array type + int size = evalExpr(dim.get(0)); + return getVectorType(size, subtype); + } else if (dim.size() == 2) { + // Matrix type => represent with array of arrays + int rowCount = evalExpr(dim.get(0)); + int colCount = evalExpr(dim.get(1)); + if (rowCount == 1 || colCount == 1){ + return getVectorType(Math.max(rowCount, colCount), subtype); + } + return new MatrixType(subtype, rowCount, colCount); + } else throw new IllegalArgumentException("Unknown dimensions: "+dim.size()); + //return new VectorType(BasicType.DOUBLE, 128); + } + Optional n = mctype.getReferencedSymbol().getAstNode(); + if (n.isPresent()){ + ASTNode node = n.get(); + if (node instanceof ASTStruct){ + ASTStruct struct = (ASTStruct) node; + + StructType type = new StructType(typeName); + + //System.out.println("Struct fields for "+typeName); + for (ASTStructFieldDefinition fd : struct.getStructFieldDefinitionList()){ + //System.out.println(fd.getName()); + //DataType fieldType = getDataType2(fd.getType()); + String tn = ((ASTElementType)fd.getType()).getName(); + DataType fieldType = getBasicType(tn); + if (fieldType == null) { + throw new IllegalArgumentException("Unsupported sub-type for struct: "+tn); + } + type.addField(fd.getName(), fieldType); + } + return type; + } else { + int a = 0; + } + } + + System.out.println("Missing DataType conversion: "+typeName); + return BasicType.EMPTY; + } + +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/FileBuilder.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/FileBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..1882f636d03655656b4aebe4e6745d81fdb5254a --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/FileBuilder.java @@ -0,0 +1,45 @@ +package de.monticore.lang.monticar.generator.cpp.dynamic_interface; + +public class FileBuilder { + String indentUnit = " "; + StringBuilder str = new StringBuilder(); + + /** + * Default indent: 4 spaces + */ + public FileBuilder() { } + public FileBuilder(String indent) { + this.indentUnit = indent; + } + + /** + * Resets the internal string builder + */ + public void init() { + str.setLength(0); + } + + /** + * Adds a formatted line to the file (with given indent depth) + */ + public void a(int indent, String text, Object... data) { + for (int i = 0; i < indent; ++i) str.append(indentUnit); + str.append(String.format(text, data)); + str.append('\n'); + } + + /** + * Adds a formatted line to the file (no indent) + */ + public void a(String text, Object... data) { + str.append(String.format(text, data)); + str.append('\n'); + } + + /** + * Returns the resulting content + */ + public String getContent() { + return str.toString(); + } +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/JsonCommunication.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/JsonCommunication.java new file mode 100644 index 0000000000000000000000000000000000000000..ce97752a2aee5dc24d37219244797ae34e2751de --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/JsonCommunication.java @@ -0,0 +1,352 @@ +package de.monticore.lang.monticar.generator.cpp.dynamic_interface; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import de.monticore.lang.monticar.generator.FileContent; +import de.monticore.lang.monticar.generator.cmake.CMakeConfig; +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.utils.json.SerializationException; + +public class JsonCommunication { + static final int BASE_INDENT = 2; + + //public static void generateCMake(CMakeConfig cmake,) + + final DynamicInterfaceGenerator gen; + + FileBuilder b = new FileBuilder(); + + JsonCommunication(DynamicInterfaceGenerator gen) { + this.gen = gen; + } + + public List generate() throws SerializationException { + List files = new ArrayList<>(); + List getPortCases = new ArrayList<>(); + List setPortCases = new ArrayList<>(); + + int i = 0; + for (PortInformation portInfo : gen.programInterface.ports){ + if (portInfo.direction == PortDirection.INPUT){ + setPortCases.add(generateSetPortCase(i, portInfo)); + } else { + getPortCases.add(generateGetPortCase(i, portInfo)); + } + ++i; + } + + HashMap templateData = new HashMap<>(); + templateData.put("mainModelName", gen.componentName); + templateData.put("interfaceDescription", gen.progInterfaceString); + templateData.put("getPortCases", getPortCases); + templateData.put("setPortCases", setPortCases); + + files.add(new FileContent(AllTemplates.generate(AllTemplates.DYNAMIC_INTERFACE_H, templateData), "dynamic_interface.h")); + files.add(new FileContent(AllTemplates.generate(AllTemplates.DYNAMIC_INTERFACE_CPP, templateData), "dynamic_interface.cpp")); + + gen.addCppFileDependency("json.h"); + gen.addCppFileDependency("json.cpp"); + gen.addCppFileDependency("printf.h"); + gen.addCppFileDependency("printf.cpp"); + gen.addCppFileDependency("utils.h"); + + return files; + } + + + + public void addCMake(CMakeConfig cmake, String outputName) { + // create shared lib + cmake.addCMakeCommandEnd("# The JSON/Dynamic-interface based communication adapter (for the hardware_emulator)"); + cmake.addCMakeCommandEnd("add_library("+outputName+" SHARED dynamic_interface.cpp json.cpp printf.cpp)"); + cmake.addCMakeCommandEnd("target_include_directories("+outputName+" PUBLIC ${INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})"); + cmake.addCMakeCommandEnd("target_link_libraries("+outputName+" PUBLIC ${LIBS} -static-libgcc -static-libstdc++)"); + cmake.addCMakeCommandEnd("set_target_properties("+outputName+" PROPERTIES LINKER_LANGUAGE CXX POSITION_INDEPENDENT_CODE ON)"); + cmake.addCMakeCommandEnd("target_compile_features("+outputName+" PUBLIC cxx_std_11)"); + cmake.addCMakeCommandEnd("set_target_properties("+outputName+" PROPERTIES PREFIX \"\")"); + // install shared lib + cmake.addCMakeCommandEnd("install(TARGETS "+outputName+" DESTINATION $ENV{DLL_DIR})"); + //cmake.addCMakeCommandEnd("export(TARGETS "+outputName+" FILE "+outputName+".cmake)"); + cmake.addCMakeCommandEnd(""); + } + + public String generateSetPortCase(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;"); + + return b.getContent(); + } + + public String generateGetPortCase(int id, PortInformation portInfo){ + b.init(); + + b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name); + generateGetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name); + b.a(BASE_INDENT, "} break;"); + + return b.getContent(); + } + + + + private void generateSetter(DataType type, int indent, int depth, String varReference){ + + if (type instanceof BasicType){ + BasicType t = (BasicType) type; + switch (t.getType()){ + case Q: + b.a(indent, "%s = traverser.get_double();", varReference); + break; + case Z: + b.a(indent, "%s = (int32_t) traverser.get_long();", varReference); + break; + case N: { + String varName = String.format("res%d", depth); + b.a(indent, "auto %s = traverser.get_long();", varName); + b.a(indent, "if (%s < 0) { %s = 0; } //TODO as error?", varName, varName); + b.a(indent, "%s = %s;", varReference, varName); + } break; + case N1: { + String varName = String.format("res%d", depth); + b.a(indent, "auto %s = traverser.get_long();", varName); + b.a(indent, "if (%s < 1) { %s = 0; } //TODO as error?", varName, varName); + b.a(indent, "%s = %s;", varReference, varName); + } break; + case C: { + String i = String.format("i%d", depth); + b.a(indent, "int %s = 0;", i); + b.a(indent, "for (auto t : traverser.stream_array()) {" ); + b.a(indent, " if (%s == 0) %s = traverser.get_double();", i, varReference); + b.a(indent, " else if (%s == 1) {} // TODO UPDATE\n", i); // TODO handle correct Complex native type + b.a(indent, " else {} // TODO error?" ); + b.a(indent, " ++%s;", i); + b.a(indent, "} if (%s < 2) {} // TODO error?", i); + } break; + case BOOLEAN: + b.a(indent, "%s = traverser.get_bool();", varReference); + break; + case EMPTY: + break; + case VEC2: { + String i = String.format("i%d", depth); + b.a(indent, "int %s = 0;", i); + b.a(indent, "for (auto t : traverser.stream_array()) {" ); + b.a(indent, " if (%s < 2) %s(%s) = traverser.get_double();", i, varReference, i); + b.a(indent, " else {} // TODO error?" ); + b.a(indent, " ++%s;", i); + b.a(indent, "} if (%s < 2) {} // TODO error?", i); + } break; + case VEC3: { + String i = String.format("i%d", depth); + b.a(indent, "int %s = 0;", i); + b.a(indent, "for (auto t : traverser.stream_array()) {" ); + b.a(indent, " if (%s < 3) %s(%s) = traverser.get_double();", i, varReference, i); + b.a(indent, " else {} // TODO error?" ); + b.a(indent, " ++%s;", i); + b.a(indent, "} if (%s < 3) {} // TODO error?", i); + } break; + default: + throw new IllegalArgumentException("Missing case"); + } + } else if (type instanceof VectorType) { + VectorType vt = (VectorType)type; + int newIndent = indent + 1; + int size = vt.getSize(); + String i = String.format("i%d", depth); + String ref = String.format("res%d", depth); + + b.a(indent, "int %s = 0;", i); + b.a(indent, "for (auto t : traverser.stream_array()) {" ); + b.a(indent, " if (%s >= %d) { break; /*TODO error?*/}", i, size); + b.a(indent, " auto &%s = %s(%s);", ref, varReference, i); + generateSetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, " ++%s;", i); + b.a(indent, "} if (%s < %d) {} // TODO error?", i, size); + } else if (type instanceof MatrixType) { + MatrixType vt = (MatrixType)type; + int newIndent = indent + 2; + int rows = vt.getRowCount(); + int cols = vt.getColumnCount(); + String i = String.format("i%d", depth); + String j = String.format("j%d", depth); + String ref = String.format("res%d", depth); + + + b.a(indent, "int %s = 0;", i); + b.a(indent, "for (auto t : traverser.stream_array()) {" ); + b.a(indent, " if (%s >= %d) { break; /*TODO error?*/}", i, rows); + b.a(indent, " int %s = 0;", j); + b.a(indent, " for (auto u : traverser.stream_array()) {" ); + b.a(indent, " if (%s >= %d) { break; /*TODO error?*/}", j, cols); + b.a(indent, " auto &%s = %s(%s, %s);", ref, varReference, i, j); + generateSetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, " ++%s;", j); + b.a(indent, " } if (%s < %d) {} // TODO error?", j, cols); + b.a(indent, " ++%s;", i); + b.a(indent, "} if (%s < %d) {} // TODO error?", i, cols); + + } else if (type instanceof DynVectorType) { + System.out.println("Unimplemented: DynVectorType set_port case."); + //throw new IllegalArgumentException("Unimplemented"); + } else if (type instanceof StructType) { + StructType st = (StructType)type; + int newIndent = indent + 1; + int count = st.getFieldCount(); + String array_stream = String.format("array_stream%d", depth); + String end = String.format("end%d", depth); + String it = String.format("it%d", depth); + + + b.a(indent, "auto %s = traverser.stream_array();", array_stream); + b.a(indent, "auto %s = %s.end();", end, array_stream); + b.a(indent, "auto %s = %s.begin();", it, array_stream); + + for (int i = 0; i < count; ++i){ + String fieldName = st.getFieldName(i); + + b.a(indent, "if (%s != %s) {", it, end); + generateSetter(st.getFieldType(i), newIndent, depth+1, String.format("%s.%s", varReference, fieldName)); + b.a(indent, " %s++;", it); + b.a(indent, "} else {/*TODO error?*/}" ); + } + + b.a(indent, "if (%s != %s) {/*TODO error?*/}", it, end); + + } else if (type instanceof EnumType) { + EnumType et = (EnumType)type; + int variantCount = et.getVariantCount(); + String elseS = ""; + String variant = String.format("variant%d", depth); + + + b.a(indent, "auto %s = traverser.get_string();", variant); + + for (int i = 0; i < variantCount; ++i){ + String value = et.getVariant(i); + + b.a(indent, "%sif (%s.equals(\"%s\")) {", elseS, variant, value); + b.a(indent, " %s= %s;", varReference, value); + b.a(indent, "}" ); + + elseS = "else "; + } + + b.a(indent, "else {/*TODO error?*/}"); + + } else { + throw new IllegalArgumentException("Missing case"); + } + } + + private void generateGetter(DataType type, int indent, int depth, String varReference) { + + if (type instanceof BasicType){ + BasicType t = (BasicType) type; + switch (t.getType()){ + case Q: + case Z: + case N: + case N1: + case BOOLEAN: + b.a(indent, "writer.write_value(%s);", varReference); + break; + case EMPTY: + break; + case C: + b.a(indent, "writer.start_array();" ); + b.a(indent, "writer.write_value(%s);", varReference); + b.a(indent, "writer.write_value(0); // TODO handle correct Complex native type"); + b.a(indent, "writer.end_array();" ); + break; + case VEC2: + b.a(indent, "writer.start_array();" ); + b.a(indent, "writer.write_value(%s(0));", varReference); + b.a(indent, "writer.write_value(%s(1));", varReference); + b.a(indent, "writer.end_array();" ); + break; + case VEC3: + b.a(indent, "writer.start_array();" ); + b.a(indent, "writer.write_value(%s(0));", varReference); + b.a(indent, "writer.write_value(%s(1));", varReference); + b.a(indent, "writer.write_value(%s(2));", varReference); + b.a(indent, "writer.end_array();" ); + break; + default: + throw new IllegalArgumentException("Missing case"); + } + } else if (type instanceof VectorType) { + VectorType vt = (VectorType)type; + int newIndent = indent + 1; + int size = vt.getSize(); + String i = String.format("i%d", depth); + String ref = String.format("res%d", depth); + + b.a(indent, "writer.start_array();" ); + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, size, i); + b.a(indent, " auto &%s = %s(%s);", ref, varReference, i); + generateGetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, "}" ); + b.a(indent, "writer.end_array();" ); + + } else if (type instanceof MatrixType) { + MatrixType vt = (MatrixType)type; + int newIndent = indent + 2; + int rows = vt.getRowCount(); + int cols = vt.getColumnCount(); + String i = String.format("i%d", depth); + String j = String.format("j%d", depth); + String ref = String.format("res%d", depth); + + + b.a(indent, "writer.start_array();" ); + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, rows, i); + b.a(indent, " writer.start_array();" ); + b.a(indent, " for (int %s=0; %s < %d; ++%s) {", j, j, cols, j); + b.a(indent, " auto &%s = %s(%s, %s);", ref, varReference, i, j); + generateGetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, " }" ); + b.a(indent, " writer.end_array();" ); + b.a(indent, "}" ); + b.a(indent, "writer.end_array();" ); + + }else if (type instanceof DynVectorType) { + System.out.println("Unimplemented: DynVectorType get_port case."); + //throw new IllegalArgumentException("Unimplemented"); + } else if (type instanceof StructType) { + StructType st = (StructType)type; + int count = st.getFieldCount(); + + + b.a(indent, "writer.start_array();"); + for (int i = 0; i < count; ++i){ + String fieldName = st.getFieldName(i); + generateGetter(st.getFieldType(i), indent, depth+1, String.format("%s.%s", varReference, fieldName)); + } + b.a(indent, "writer.end_array();"); + + } else if (type instanceof EnumType) { + EnumType et = (EnumType)type; + int variantCount = et.getVariantCount(); + String variant = String.format("variant%d", depth); + + + b.a(indent, "switch(%s) {", varReference); + for (int i = 0; i < variantCount; ++i){ + String value = et.getVariant(i); + b.a(indent, "case %s: writer.write_value(\"%s\"); break;", value, value); + } + b.a(indent, "}"); + } else { + throw new IllegalArgumentException("Missing case"); + } + } + + +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/TcpCommunication.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/TcpCommunication.java new file mode 100644 index 0000000000000000000000000000000000000000..0527dde9692e527a78ab3070f0e851454d80b8dd --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/dynamic_interface/TcpCommunication.java @@ -0,0 +1,275 @@ +package de.monticore.lang.monticar.generator.cpp.dynamic_interface; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import de.monticore.lang.monticar.generator.FileContent; +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.utils.json.SerializationException; + +public class TcpCommunication { + static final int BASE_INDENT = 2; + + final DynamicInterfaceGenerator gen; + + FileBuilder b = new FileBuilder(); + + + public TcpCommunication(DynamicInterfaceGenerator gen) { + this.gen = gen; + } + + + public List generate(boolean hasDDC) throws SerializationException { + List files = new ArrayList<>(); + List setInputCases = new ArrayList<>(); + List sendOutputCalls = new ArrayList<>(); + List sendOutputCases = new ArrayList<>(); + + int i = 0; + for (PortInformation portInfo : gen.programInterface.ports){ + if (portInfo.direction == PortDirection.INPUT){ + setInputCases.add(generateSetInputCase(i, portInfo)); + } else { + sendOutputCalls.add(String.format("send_output(%d);", i)); + sendOutputCases.add(generateSendOutputCase(i, portInfo)); + } + ++i; + } + + HashMap templateData = new HashMap<>(); + templateData.put("mainModelName", gen.componentName); + templateData.put("interfaceDescription", gen.progInterfaceString); + templateData.put("setInputCases", setInputCases); + templateData.put("sendOutputCalls", sendOutputCalls); + templateData.put("sendOutputCases", sendOutputCases); + templateData.put("useDDC", hasDDC); + + files.add(new FileContent(AllTemplates.generate(AllTemplates.TCP_ADAPTER_H, templateData), "server_adapter.h")); + files.add(new FileContent(AllTemplates.generate(AllTemplates.TCP_ADAPTER_CPP, templateData), "server_adapter.cpp")); + + gen.addCppFileDependency("json.h"); + gen.addCppFileDependency("json.cpp"); + gen.addCppFileDependency("printf.h"); + gen.addCppFileDependency("printf.cpp"); + gen.addCppFileDependency("utils.h"); + gen.addCppFileDependency("network.h"); + gen.addCppFileDependency("network.cpp"); + gen.addCppFileDependency("tcp_protocol.h"); + + return files; + } + + public void getSources(HashSet sources) { + sources.add("json.cpp"); + sources.add("printf.cpp"); + sources.add("network.cpp"); + sources.add("server_adapter.cpp"); + } + public void getLibs(HashSet libs) { + + } + + + 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;"); + + return b.getContent(); + } + + public String generateSendOutputCase(int id, PortInformation portInfo){ + b.init(); + + b.a(BASE_INDENT, "case %d: { // %s", id, portInfo.name); + generateGetter(portInfo.type, BASE_INDENT+1, 1, "program_instance."+portInfo.name); + b.a(BASE_INDENT, "} break;"); + + return b.getContent(); + } + + + + private void generateSetter(DataType type, int indent, int depth, String varReference){ + + if (type instanceof BasicType){ + BasicType t = (BasicType) type; + switch (t.getType()){ + case Q: + b.a(indent, "%s = input_packet.read_f64();", varReference); + break; + case Z: + case N: + case N1: // TOTO check range for N1 & N ? + b.a(indent, "%s = (int32_t) input_packet.read_u32();", varReference); + break; + case C: + b.a(indent, "%s = input_packet.read_f64();", varReference); + b.a(indent, "%s = input_packet.read_f64();", varReference); // TODO correct complex number native case + throw new IllegalStateException("Unimplemented"); + case BOOLEAN: + b.a(indent, "%s = input_packet.read_u8() != 0;", varReference); + break; + case EMPTY: + break; + case VEC2: + b.a(indent, "%s(0) = input_packet.read_f64();", varReference); + b.a(indent, "%s(1) = input_packet.read_f64();", varReference); + break; + case VEC3: + b.a(indent, "%s(0) = input_packet.read_f64();", varReference); + b.a(indent, "%s(1) = input_packet.read_f64();", varReference); + b.a(indent, "%s(2) = input_packet.read_f64();", varReference); + break; + default: + throw new IllegalArgumentException("Missing case"); + } + } else if (type instanceof VectorType) { + VectorType vt = (VectorType)type; + int newIndent = indent + 1; + int size = vt.getSize(); + String i = String.format("i%d", depth); + String ref = String.format("res%d", depth); + + + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, size, i); + b.a(indent, " auto &%s = %s(%s);", ref, varReference, i); + generateSetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, "}" ); + + } else if (type instanceof MatrixType) { + MatrixType vt = (MatrixType)type; + int newIndent = indent + 2; + int rows = vt.getRowCount(); + int cols = vt.getColumnCount(); + String i = String.format("i%d", depth); + String j = String.format("j%d", depth); + String ref = String.format("res%d", depth); + + + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, rows, i); + b.a(indent, " for (int %s=0; %s < %d; ++%s) {", j, j, cols, j); + b.a(indent, " auto &%s = %s(%s, %s);", ref, varReference, i, j); + generateSetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, " }" ); + b.a(indent, "}" ); + + } else if (type instanceof DynVectorType) { + System.out.println("Unimplemented: DynVectorType set_port case."); + //throw new IllegalArgumentException("Unimplemented"); + } else if (type instanceof StructType) { + StructType st = (StructType)type; + int count = st.getFieldCount(); + + for (int i = 0; i < count; ++i){ + String fieldName = st.getFieldName(i); + generateSetter(st.getFieldType(i), indent, depth+1, String.format("%s.%s", varReference, fieldName)); + } + } else if (type instanceof EnumType) { + EnumType et = (EnumType)type; + + int variantCount = et.getVariantCount(); + if (variantCount > 256) throw new IllegalArgumentException("Too much Enum variants for enum "+et.getName()+ " (TODO implement adaptive native type size)"); + + b.a(indent, "%s = static_cast<%s>(input_packet.read_u8());", et.getName()); + } else { + throw new IllegalArgumentException("Missing case"); + } + + } + + private void generateGetter(DataType type, int indent, int depth, String varReference) { + + if (type instanceof BasicType){ + BasicType t = (BasicType) type; + switch (t.getType()){ + case Q: + b.a(indent, "packet.write_f64(%s);", varReference); + break; + case Z: + case N: + case N1: + b.a(indent, "packet.write_u32(%s);", varReference); + case BOOLEAN: + b.a(indent, "packet.write_u8(%s ? 1 : 0);", varReference); + break; + case EMPTY: + break; + case C: + b.a(indent, "packet.write_f64(%s);", varReference); + b.a(indent, "packet.write_f64(0);"); + throw new IllegalStateException("Unimplemented: Missing Proper native Complex Type"); + case VEC2: + b.a(indent, "packet.write_f64(%s(0));", varReference); + b.a(indent, "packet.write_f64(%s(1));", varReference); + break; + case VEC3: + b.a(indent, "packet.write_f64(%s(0));", varReference); + b.a(indent, "packet.write_f64(%s(1));", varReference); + b.a(indent, "packet.write_f64(%s(2));", varReference); + break; + default: + throw new IllegalArgumentException("Missing case"); + } + } else if (type instanceof VectorType) { + VectorType vt = (VectorType)type; + int newIndent = indent + 1; + int size = vt.getSize(); + String i = String.format("i%d", depth); + String ref = String.format("res%d", depth); + + + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, size, i); + b.a(indent, " auto &%s = %s(%s);", ref, varReference, i); + generateGetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, "}" ); + + } else if (type instanceof MatrixType) { + MatrixType vt = (MatrixType)type; + int newIndent = indent + 2; + int rows = vt.getRowCount(); + int cols = vt.getColumnCount(); + String i = String.format("i%d", depth); + String j = String.format("j%d", depth); + String ref = String.format("res%d", depth); + + b.a(indent, "for (int %s=0; %s < %d; ++%s) {", i, i, rows, i); + b.a(indent, " for (int %s=0; %s < %d; ++%s) {", j, j, cols, j); + b.a(indent, " auto &%s = %s(%s, %s);", ref, varReference, i, j); + generateGetter(vt.getBaseType(), newIndent, depth+1, ref); + b.a(indent, " }" ); + b.a(indent, "}" ); + + }else if (type instanceof DynVectorType) { + System.out.println("Unimplemented: DynVectorType get_port case."); + //throw new IllegalArgumentException("Unimplemented"); + } else if (type instanceof StructType) { + StructType st = (StructType)type; + int count = st.getFieldCount(); + + + for (int i = 0; i < count; ++i){ + String fieldName = st.getFieldName(i); + generateGetter(st.getFieldType(i), indent, depth+1, String.format("%s.%s", varReference, fieldName)); + } + + } else if (type instanceof EnumType) { + EnumType et = (EnumType)type; + int variantCount = et.getVariantCount(); + if (variantCount > 256) throw new IllegalArgumentException("Too much Enum variants for enum "+et.getName()+ " (TODO implement adaptive native type size)"); + + b.a(indent, "packet.write_u8(static_cast(%s));", varReference); + } else { + throw new IllegalArgumentException("Missing case"); + } + } + + +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/cpp/template/AllTemplates.java b/src/main/java/de/monticore/lang/monticar/generator/cpp/template/AllTemplates.java index 5ecee32a048e7ff66a81821766e91bac0cadf937..9b37e96a6c1d65861238f44b009214457efe29f1 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/cpp/template/AllTemplates.java +++ b/src/main/java/de/monticore/lang/monticar/generator/cpp/template/AllTemplates.java @@ -17,8 +17,12 @@ public final class AllTemplates { private static final Template TESTS_MAIN_ENTRY; private static final Template STRUCT; private static final Template ENUM; - private static final Template AUTOPILOT_ADAPTER_CPP; - private static final Template AUTOPILOT_ADAPTER_H; + public static final Template DYNAMIC_INTERFACE_CPP; + public static final Template DYNAMIC_INTERFACE_H; + public static final Template TCP_ADAPTER_CPP; + public static final Template TCP_ADAPTER_H; + public static final Template DDC_ADAPTER_CPP; + public static final Template DDC_ADAPTER_ADAPTER_H; private static final Template SERVER_WRAPPER; private static final Template DYNAMICS_EVENT_PortValueCheker; @@ -37,8 +41,12 @@ public final class AllTemplates { TESTS_MAIN_ENTRY = conf.getTemplate("/test/TestsMainEntry.ftl"); STRUCT = conf.getTemplate("/type/Struct.ftl"); ENUM = conf.getTemplate("/type/Enum.ftl"); - AUTOPILOT_ADAPTER_CPP = conf.getTemplate("/autopilotadapter/AutopilotAdapterCpp.ftl"); - AUTOPILOT_ADAPTER_H = conf.getTemplate("/autopilotadapter/AutopilotAdapterH.ftl"); + DYNAMIC_INTERFACE_CPP = conf.getTemplate("/dynamic_interface/dynamic_interface.cpp.ftl"); + DYNAMIC_INTERFACE_H = conf.getTemplate("/dynamic_interface/dynamic_interface.h.ftl"); + TCP_ADAPTER_CPP = conf.getTemplate("/dynamic_interface/server_adapter.cpp.ftl"); + TCP_ADAPTER_H = conf.getTemplate("/dynamic_interface/server_adapter.h.ftl"); + DDC_ADAPTER_CPP = conf.getTemplate("/dynamic_interface/ddc_adapter.cpp.ftl"); + DDC_ADAPTER_ADAPTER_H = conf.getTemplate("/dynamic_interface/ddc_adapter.h.ftl"); SERVER_WRAPPER = conf.getTemplate("/serverwrapper/ServerWrapper.ftl"); DYNAMICS_EVENT_PortValueCheker = conf.getTemplate("/dynamics/events_port_value_check_h.ftl"); DYNAMICS_EVENT_DynamicHelper = conf.getTemplate("/dynamics/dynamic_port_request_connect_helper_h.ftl"); @@ -69,13 +77,6 @@ public final class AllTemplates { return generate(ENUM, viewModel); } - public static String generateAutopilotAdapterCpp(AutopilotAdapterDataModel viewModel) { - return generate(AUTOPILOT_ADAPTER_CPP, viewModel); - } - public static String generateAutopilotAdapterH(AutopilotAdapterDataModel viewModel) { - return generate(AUTOPILOT_ADAPTER_H, viewModel); - } - public static String generateServerWrapper(ServerWrapperViewModel viewModel) { return generate(SERVER_WRAPPER, viewModel); } @@ -92,12 +93,12 @@ public final class AllTemplates { return generate(LOG_METHODS, model); } - private static String generate(Template template, ViewModelBase viewModelBase) { + public static String generate(Template template, ViewModelBase viewModelBase) { return generate(template, TemplateHelper.getDataForTemplate(viewModelBase)); } - private static String generateWithoutData(Template template){ + public static String generateWithoutData(Template template){ Log.errorIfNull(template); StringWriter sw = new StringWriter(); try{ @@ -108,7 +109,7 @@ public final class AllTemplates { return sw.toString(); } - private static String generate(Template template, Object dataForTemplate) { + public static String generate(Template template, Object dataForTemplate) { Log.errorIfNull(template); Log.errorIfNull(dataForTemplate); StringWriter sw = new StringWriter(); diff --git a/src/main/resources/template/autopilotadapter/AutopilotAdapterCpp.ftl b/src/main/resources/template/autopilotadapter/AutopilotAdapterCpp.ftl deleted file mode 100644 index e2fc7c7194878c4e85d020213baa708ca112f73a..0000000000000000000000000000000000000000 --- a/src/main/resources/template/autopilotadapter/AutopilotAdapterCpp.ftl +++ /dev/null @@ -1,66 +0,0 @@ -<#-- (c) https://github.com/MontiCore/monticore --> -<#include "/Common.ftl"> -#include "AutopilotAdapter.h" - -#include "./${viewModel.mainModelName}.h" - -#include -#include -#include -#include "armadillo" - - -void copy_double_array_to_mat(double *data, int size, mat &dest){ - for (int i=0; i diff --git a/src/main/resources/template/dynamic_interface/basic_interface.cpp b/src/main/resources/template/dynamic_interface/basic_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..996909dfcbfb25cf37ff5ffe220e1589d9981cda --- /dev/null +++ b/src/main/resources/template/dynamic_interface/basic_interface.cpp @@ -0,0 +1,30 @@ +#include "basic_interface.h" + +using namespace std; + + + +/* + INTERFACE STRING +*/ + +string BASIC_INTERFACE = string(R"( +{ + "name":"basic_interface", + "version":"1.0", + "ports":[ + {"name":"true_velocity","type":{"type":"basic","base_type":"Q"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"true_position","type":{"type":"basic","base_type":"vec2"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"true_compass","type":{"type":"basic","base_type":"Q"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"trajectory_length","type":{"type":"basic","base_type":"N"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"trajectory_x","type":{"type":"vector","base_type":{"type":"basic","base_type":"Q"},"size":10},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"trajectory_y","type":{"type":"vector","base_type":{"type":"basic","base_type":"Q"},"size":10},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"steering","type":{"type":"basic","base_type":"Q"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"gas","type":{"type":"basic","base_type":"Q"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"braking","type":{"type":"basic","base_type":"Q"},"direction":"INPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"set_steering","type":{"type":"basic","base_type":"Q"},"direction":"OUTPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"set_gas","type":{"type":"basic","base_type":"Q"},"direction":"OUTPUT","allows_multiple_inputs":false,"optional":false}, + {"name":"set_braking","type":{"type":"basic","base_type":"Q"},"direction":"OUTPUT","allows_multiple_inputs":false,"optional":false} + ] +} +)"); \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/basic_interface.h b/src/main/resources/template/dynamic_interface/basic_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..272f87eb439aa3c9f96006a4d65063188fd0b0aa --- /dev/null +++ b/src/main/resources/template/dynamic_interface/basic_interface.h @@ -0,0 +1,24 @@ +#pragma once + +#include + + + +#define BASIC_INTERFACE_INPUT_COUNT 9 +#define BASIC_INTERFACE_OUTPUT_COUNT 3 +#define BASIC_INTERFACE_PORT_COUNT (BASIC_INTERFACE_INPUT_COUNT+BASIC_INTERFACE_OUTPUT_COUNT) + +extern std::string BASIC_INTERFACE; + +#define PORT_TRUE_VELOCITY 0 +#define PORT_TRUE_POSITION 1 +#define PORT_TRUE_COMPASS 2 +#define PORT_TRAJ_LENGTH 3 +#define PORT_TRAJ_X 4 +#define PORT_TRAJ_Y 5 +#define PORT_STEERING 6 +#define PORT_GAS 7 +#define PORT_BRAKING 8 +#define PORT_SET_STEERING 9 +#define PORT_SET_GAS 10 +#define PORT_SET_BRAKING 11 \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/ddc_adapter.cpp.ftl b/src/main/resources/template/dynamic_interface/ddc_adapter.cpp.ftl new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/resources/template/dynamic_interface/ddc_adapter.h.ftl b/src/main/resources/template/dynamic_interface/ddc_adapter.h.ftl new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/resources/template/dynamic_interface/ddc_interface.h b/src/main/resources/template/dynamic_interface/ddc_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..48c2d591c64e2838016563756f267b6c64381c97 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/ddc_interface.h @@ -0,0 +1,18 @@ +#pragma once + + +constexpr int MIN_DDC_ID = 0x4000; +constexpr int DDC_MAX_LENGTH = 195; + +constexpr int ID_RUNNING = 0; +constexpr int ID_INTERFACE_TYPE = 1; +constexpr int ID_TIME_MODE = 2; +constexpr int ID_RUN_CYCLE_SWITCH = 3; +constexpr int ID_DELTA_SEC = 4; +constexpr int ID_EXEC_TIME = 5; +constexpr int ID_SIM_RUNNING = 6; +constexpr int ID_INPUT_START_ID = 7; +constexpr int ID_OUTPUT_START_ID = 8; +constexpr int ID_SLOT_TABLE_START = 7; +constexpr int ID_INTERFACE_STRING_COUNT = 8; +constexpr int ID_INTERFACE_STRING = 9; \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/dynamic_interface.cpp.ftl b/src/main/resources/template/dynamic_interface/dynamic_interface.cpp.ftl new file mode 100644 index 0000000000000000000000000000000000000000..68a9b05602ded2efeeebf62547235177031c715e --- /dev/null +++ b/src/main/resources/template/dynamic_interface/dynamic_interface.cpp.ftl @@ -0,0 +1,48 @@ +/** (c) https://github.com/MontiCore/monticore */ +#include "dynamic_interface.h" +#include "json.h" + +#include "./${mainModelName}.h" + +${mainModelName} program_instance; +JsonWriter writer; + +extern "C" { + +EXPORT const char* DI__get_interface() { + return R"( +${interfaceDescription} + )"; +} +EXPORT void DI__set_port(int i, const char* data) { + JsonTraverser traverser; + traverser.init(data); + switch(i){ +<#list setPortCases as setPortCase> +${setPortCase} + + } +} +EXPORT const char* DI__get_port(int i) { + writer.init(); + switch (i){ +<#list getPortCases as getPortCase> +${getPortCase} + + } + return writer.get_string(); +} + + +// Methods +EXPORT void DI__init() { + writer.format = false; + srand(time(NULL)); + program_instance.init(); +} +EXPORT void DI__execute(double delta_sec) { + // TODO set "delta_sec" port ? + program_instance.execute(); +} + +} \ No newline at end of file diff --git a/src/main/resources/template/autopilotadapter/AutopilotAdapterH.ftl b/src/main/resources/template/dynamic_interface/dynamic_interface.h.ftl similarity index 52% rename from src/main/resources/template/autopilotadapter/AutopilotAdapterH.ftl rename to src/main/resources/template/dynamic_interface/dynamic_interface.h.ftl index 00c84db3ed5d41a610b3745397cf5388a13fed72..a4054ffe5649b78f96f1491e58fa1d8dccf7fd20 100644 --- a/src/main/resources/template/autopilotadapter/AutopilotAdapterH.ftl +++ b/src/main/resources/template/dynamic_interface/dynamic_interface.h.ftl @@ -1,5 +1,4 @@ -<#-- (c) https://github.com/MontiCore/monticore --> -<#include "/Common.ftl"> +/** (c) https://github.com/MontiCore/monticore */ #pragma once #if defined(_MSC_VER) // Microsoft @@ -18,17 +17,13 @@ extern "C" { -EXPORT int get_input_count(); -EXPORT const char *get_input_name(int id); -EXPORT const char *get_input_type(int id); +// Returns a JSON string deserializable to the ProgramInterface class (Commons). +EXPORT const char* DI__get_interface(); +EXPORT void DI__set_port(int i, const char* data); +EXPORT const char* DI__get_port(int i); -EXPORT int get_output_count(); -EXPORT const char *get_output_name(int id); -EXPORT const char *get_output_type(int id); - -EXPORT void init(); -EXPORT void execute(); - -${viewModel.functionDeclarations} +// Methods +EXPORT void DI__init(); +EXPORT void DI__execute(double delta_sec); } diff --git a/src/main/resources/template/dynamic_interface/json.cpp b/src/main/resources/template/dynamic_interface/json.cpp new file mode 100644 index 0000000000000000000000000000000000000000..505685c3e1f4866813fb4963be9cc6059f1430c1 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/json.cpp @@ -0,0 +1,555 @@ +/** + * (c) https://github.com/MontiCore/monticore + */ +#include "json.h" + +//#include +#include +#include +#include +#include +#include "printf.h" +//#include + + +char JsonWriter::LOCAL_BUFFER[JsonWriter::LOCAL_BUFFER_SIZE]; + +void JsonWriter::init(){ + if (buffer == nullptr) { + alloc(); + } + offset = 0; + pos = 0; + has_elem = false; + has_key = true; +} + +void JsonWriter::alloc(){ + buffer = new char[START_BUFFER_SIZE]; + buffer_size = START_BUFFER_SIZE; + pos = 0; +} + +void JsonWriter::append(const char c) { + buffer[pos] = c; + ++pos; + if (pos >= buffer_size) { + int32_t new_size = buffer_size + START_BUFFER_SIZE; + char *new_buff = new char[new_size]; + for (int32_t i = 0; i < buffer_size; ++i){ + new_buff[i] = buffer[i]; + } + delete[] buffer; + buffer = new_buff; + buffer_size = new_size; + } +} + +void JsonWriter::append(const char* str){ + int32_t i = 0; + while (str[i]){ + append(str[i]); ++i; + } +} + + +const char* JsonWriter::get_string() { + if (buffer == nullptr) { + alloc(); + } + buffer[pos] = '\0'; + return buffer; +} + +void JsonWriter::start_object() { + separate(); + if (format) offset += TAB; + append('{'); + has_elem = false; +} + +void JsonWriter::add_offset(){ + for (int32_t i = 0; i < offset; ++i){ + append(' '); + } +} + +void JsonWriter::end_object() { + if (format) { + offset -= TAB; + if (has_elem){ + append('\n'); + add_offset(); + } + } + append('}'); + has_elem = true; +} + +void JsonWriter::start_array() { + separate(); + if (format) offset += TAB; + append('['); + has_elem = false; +} + +void JsonWriter::end_array() { + if (format) { + offset -= TAB; + if (has_elem){ + append('\n'); + add_offset(); + } + } + append(']'); + has_elem = true; +} + +void JsonWriter::separate() { + if (has_elem && !has_key) append(','); + else has_elem = true; + + if (!has_key) { + if (format) { + append('\n'); + add_offset(); + } + } + else has_key = false; +} + +void JsonWriter::write_key(const char* key) { + write_value(key); + has_key = true; + if (format) append(": "); + else append(':'); +} + +void JsonWriter::write_value(const char* str) { + separate(); + append('"'); + int32_t i = 0; + while (str[i]){ + char c = str[i]; + switch(c) { + case '\n': append("\\n"); break; + case '\t': append("\\t"); break; + case '\r': append("\\r"); break; + case '\f': append("\\f"); break; + case '\b': append("\\b"); break; + case '"': append("\\\""); break; + case '/': append("\\/"); break; + case '\\': append("\\\\"); break; + default: append(c); break; + } + ++i; + } + append('"'); +} + +void JsonWriter::write_value(int32_t i){ + separate(); + snprintf(LOCAL_BUFFER, LOCAL_BUFFER_SIZE, "%d", i); + append(LOCAL_BUFFER); +} +void JsonWriter::write_value(int64_t l){ + separate(); + snprintf(LOCAL_BUFFER, LOCAL_BUFFER_SIZE, "%lld", l); + append(LOCAL_BUFFER); +} +void JsonWriter::write_value(double d){ + separate(); + if (isnan(d)) append("\"NaN\""); + else if (isinf(d)) { + if (d < 0) append("\"-Infinity\""); + else append("\"Infinity\""); + } + else { + snprintf(LOCAL_BUFFER, LOCAL_BUFFER_SIZE, "%.*e", 17, d); + append(LOCAL_BUFFER); + } +} +void JsonWriter::write_value(float d){ + separate(); + if (isnan(d)) append("\"NaN\""); + else if (isinf(d)) { + if (d < 0) append("\"-Infinity\""); + else append("\"Infinity\""); + } + else { + snprintf(LOCAL_BUFFER, LOCAL_BUFFER_SIZE, "%.*e", 9, (double)d); //#define FLT_DECIMAL_DIG 9 + append(LOCAL_BUFFER); + } +} +void JsonWriter::write_value(bool b){ + separate(); + if(b) { + append("true"); + } else { + append("false"); + } +} + + +bool StringRef::equals(const char* str) const { + for (int32_t i = 0; i < length; i++) { + char c = str[i]; + if (c == '\0') return false; + if (start[i] != c) { + return false; + } + } + return str[length] == '\0'; +} + +std::string StringRef::get_json_string() const { + std::stringstream res; + for (int32_t i = 0; i < length; ++i){ + auto c = start[i]; + if (c == '\\' && i + 1 < length){ + ++i; + c = start[i]; + switch(c){ + case 'n': res << ('\n'); break; + case 't': res << ('\t'); break; + case 'r': res << ('\r'); break; + case 'f': res <<('\f'); break; + case 'b': res <<('\b'); break; + default: res << (c); break; + } + } else res << c; + } + return res.str(); +} + + + + + + + +ObjectIterator::ObjectIterator(JsonTraverser &traverser, int32_t iteration_depth) : traverser(traverser), iteration_depth(iteration_depth), last_elem_pos(traverser.pos) {} +ObjectIterator::ObjectIterator(JsonTraverser &traverser) : traverser(traverser), iteration_depth(-1), last_elem_pos(traverser.pos) {} // For "end()" it. + +ObjectIterator& ObjectIterator::operator++() { + if (last_elem_pos == traverser.pos) { + traverser.skip_value(); // The last value was not traversed + } + while(traverser.depth > iteration_depth){ + // Skip elems until back to this iterator's depth + traverser.skip_value(); + } + return *this; +} +StringRef ObjectIterator::operator*() { + // Get key + if (traverser.current_type != ValueType::STRING) throw ParsingException("Parsing exception: Expected a string as key"); + auto key = traverser.get_string(); + last_elem_pos = traverser.pos; // Track traversal + return key; +} +bool ObjectIterator::operator !=( const ObjectIterator& other ) { + if (iteration_depth >= 0) { + return traverser.depth >= iteration_depth; + } else { + return traverser.depth >= other.iteration_depth; + } +} + +ArrayIterator::ArrayIterator(JsonTraverser &traverser, int32_t iteration_depth) : traverser(traverser), iteration_depth(iteration_depth), last_elem_pos(traverser.pos) {} +ArrayIterator::ArrayIterator(JsonTraverser &traverser) : traverser(traverser), iteration_depth(-1), last_elem_pos(traverser.pos) {} // For "end()" it. + +ArrayIterator& ArrayIterator::operator++() { + if (last_elem_pos == traverser.pos) { + traverser.skip_value(); // The last value was not traversed + } + while(traverser.depth > iteration_depth){ + // Skip elems until back to this iterator's depth + traverser.skip_value(); + } + last_elem_pos = traverser.pos; // Track traversal + return *this; +} +ValueType ArrayIterator::operator*() { + return traverser.current_type; +} +bool ArrayIterator::operator!=( const ArrayIterator& other ) { + if (iteration_depth >= 0) { + return traverser.depth >= iteration_depth; + } else { + return traverser.depth >= other.iteration_depth; + } +} + + + + + + + + + + + + +void JsonTraverser::init(const char *data) { + pos = data; + c = *pos; + depth = 0; + goto_next_value(); +} + +bool JsonTraverser::is_empty(){ + if (current_type == ValueType::OBJECT){ + return next_non_ws() == '}'; + } else if (current_type == ValueType::ARRAY) { + return next_non_ws() == ']'; + } else return false; +} + +ObjectStream JsonTraverser::stream_object() { + // TODO + if (current_type != ValueType::OBJECT) throw ParsingException("Parsing exception: tried to read object but got wrong type."); + next_char(); // Move after '{' + depth++; + int32_t iteration_depth = depth; + goto_next_value(); + return ObjectStream(*this, iteration_depth); +} + +ArrayStream JsonTraverser::stream_array() { + // TODO + if (current_type != ValueType::ARRAY) throw ParsingException("Parsing exception: tried to read array but got wrong type."); + next_char(); // Move after '[' + depth++; + int32_t iteration_depth = depth; + goto_next_value(); + return ArrayStream(*this, iteration_depth); +} + +bool JsonTraverser::get_bool() { + if (current_type != ValueType::BOOLEAN) throw ParsingException("Parsing exception: tried to read bool but got wrong type."); + skip_bool(); + goto_next_value(); + return last_bool; +} + + +StringRef JsonTraverser::get_string() { + if (current_type != ValueType::STRING) throw ParsingException("Parsing exception: tried to read string but got wrong type."); + auto start = pos +1; + skip_string(); + auto end = pos - 1; + goto_next_value(); + return StringRef(start, end - start); +} + + +double JsonTraverser::get_double() { + if (current_type == ValueType::NUMBER) { + char *end; + auto res = strtod(pos, &end); + goto_char(end); + goto_next_value(); + return res; + } else if (current_type == ValueType::STRING) { + auto s = get_string(); + if (s.equals("NaN")) + return nan(nullptr); + if (s.equals("-Infinity")) + return -std::numeric_limits::infinity(); + if (s.equals("Infinity")) + return std::numeric_limits::infinity(); + } + throw ParsingException("Parsing exception: tried to read double but got wrong type."); +} + + + +int64_t JsonTraverser::get_long() { + if (current_type != ValueType::NUMBER) throw ParsingException("Parsing exception: tried to read int64_t but got wrong type."); + char *end; + auto res = strtoll(pos, &end, 10); + goto_char(end); + goto_next_value(); + return res; +} + +void JsonTraverser::expect_valid_integer(int64_t l) { + if (l > (int64_t) INT32_MAX || l < (int64_t) INT32_MIN) + throw ParsingException("Parsing exception: The int64_t doesn't fit in an int32_t"); +} + +void JsonTraverser::get_value_type() { + if (c == '{') + current_type = ValueType::OBJECT; + else if (c == '[') + current_type = ValueType::ARRAY; + else if (c == '"') + current_type = ValueType::STRING; + else if (c == 't' || c == 'T' || c == 'f' || c == 'F') + current_type = ValueType::BOOLEAN; + else if ((c >= '0' && c <= '9') || c == '-' || c == '+') + current_type = ValueType::NUMBER; + else + current_type = ValueType::UNKNOWN; +} + +/** + * Assumes the current position is AFTER a value. + * Then skips WS and separators and goes to the next VALUE (key or value). + * Evaluates the Next Value Type. + */ +void JsonTraverser::goto_next_value() { + skip_whitespace(); + if (c == ':') { + next_char(); + skip_whitespace(); + get_value_type(); + return; + } + do { + if (c == ',') { + next_char(); + skip_whitespace(); + } + if (c == ']' || c == '}') { + depth--; + next_char(); + skip_whitespace(); + } else + break; + } while (true); + get_value_type(); +} + +/* + SKIP FUNCTIONS + + These must all be called at the START of a value + and exit at the char AFTER the value +*/ + +/** + * Skips any value (and nested values). + * Goes to the start of the next value + */ +void JsonTraverser::skip_value() { + int32_t d = depth; + switch (current_type) { + case ValueType::ARRAY: + case ValueType::OBJECT: + next_char(); // Skip '{' or '[' + depth++; + goto_next_value(); + while(depth>d && c != '\0'){ + skip_value(); + } + return; + case ValueType::BOOLEAN: skip_bool(); break; + case ValueType::NUMBER: skip_double(); break; + case ValueType::STRING: skip_string(); break; + default: + return; + } + goto_next_value(); +} + +void JsonTraverser::skip_double() { + while ((c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'E' || c == 'e') + next_char(); + current_type = ValueType::UNKNOWN; +} +void JsonTraverser::skip_long() { + if (c == '-') + next_char(); + if (c < '0' || c > '9') + throw ParsingException("Parsing exception: Expected digit in long"); + do { + next_char(); + } while (c >= '0' && c <= '9'); + if (!is_next_whitespace() && c != ',' && c != ']' && c != '}') + throw ParsingException("Parsing exception: Unexpected character in long"); + current_type = ValueType::UNKNOWN; +} + +void JsonTraverser::skip_string() { + next_char(); + bool escape = false; + while (c != '\0') { + if (escape) { + escape = false; + } else if (c == '\\') { + escape = true; + } else if (c == '"') { + break; + } + next_char(); + } + if (c == '\0') + throw ParsingException("Parsing exception: Missing closing string delimiter"); + next_char(); + current_type = ValueType::UNKNOWN; +} + +void JsonTraverser::skip_bool() { + if (c == 't' || c == 'T') { + last_bool = true; + if (!is_true()) + throw ParsingException("Parsing exception: Unexpected value for boolean"); + } else { + last_bool = false; + if (!is_false()) + throw ParsingException("Parsing exception: Unexpected value for boolean"); + } + current_type = ValueType::UNKNOWN; +} + + +void JsonTraverser::skip_whitespace() { + while (is_next_whitespace()) { + next_char(); + } +} + +bool JsonTraverser::is_true() { + next_char(); + if (c != 'r' && c != 'R') + return false; + next_char(); + if (c != 'u' && c != 'U') + return false; + next_char(); + if (c != 'e' && c != 'E') + return false; + next_char(); + return true; +} + +bool JsonTraverser::is_false() { + next_char(); + if (c != 'a' && c != 'A') + return false; + next_char(); + if (c != 'l' && c != 'L') + return false; + next_char(); + if (c != 's' && c != 'S') + return false; + next_char(); + if (c != 'e' && c != 'E') + return false; + next_char(); + return true; +} + +// PEEKS after the current whitespaces +char JsonTraverser::next_non_ws(){ + auto p = pos; + char nc; + do { + p++; + nc = *p; + } while(nc == ' ' || nc == '\n' || nc == '\r' || nc == '\t'); + return nc; +} \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/json.h b/src/main/resources/template/dynamic_interface/json.h new file mode 100644 index 0000000000000000000000000000000000000000..ce9f02e869ac528248c0c75c4d237309aca53734 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/json.h @@ -0,0 +1,276 @@ +/** + * (c) https://github.com/MontiCore/monticore + */ +#pragma once + +#include +#include +#include + + +struct JsonWriter { + + static constexpr int32_t START_BUFFER_SIZE = 1024; + char *buffer = nullptr; + int32_t pos = 0; + int32_t buffer_size = 0; + + static constexpr int32_t TAB = 2; + bool format = true; + int32_t offset = 0; + bool has_elem = false; + bool has_key = true; + + static constexpr int32_t LOCAL_BUFFER_SIZE = 1024; + static char LOCAL_BUFFER[LOCAL_BUFFER_SIZE]; + + void init(); + + const char* get_string(); + + void start_object(); + void end_object(); + + void start_array(); + void end_array(); + + void write_key(const char* key); + void write_value(const char* str); + + void write_value(int32_t i); + void write_value(int64_t l); + void write_value(double d); + void write_value(float d); + void write_value(bool b); + + void write(const char* key, const char* str){ + write_key(key); + write_value(str); + } + + void write(const char* key, int32_t i){ + write_key(key); + write_value(i); + } + + void write(const char* key, int64_t l){ + write_key(key); + write_value(l); + } + void write(const char* key, float f){ + write_key(key); + write_value(f); + } + void write(const char* key, double d){ + write_key(key); + write_value(d); + } + void write(const char* key, bool b){ + write_key(key); + write_value(b); + } + + ~JsonWriter(){ + if (buffer != nullptr) delete[] buffer; + } + + +private: + void separate(); + void add_offset(); + void alloc(); + + void append(const char c); + void append(const char* str); +}; + + + + + + + + + + +struct StringRef { + const char* start = nullptr; + int32_t length = 0; + + StringRef(const char* start, int32_t length) : start(start), length(length) {} + + // bool equals_ignore_case(String str){ + // if (str.length() != length) return false; + // for (int32_t i = 0; i < length; i++) { + // char c1 = data[i+offset]; + // char c2 = str.charAt(i); + // if (c1 == c2) { + // continue; + // } + // char u1 = Character.toUpperCase(c1); + // char u2 = Character.toUpperCase(c2); + // if (u1 == u2) { + // continue; + // } + // if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + // continue; + // } + // return false; + // } + // return true; + // } + + bool equals(const char* str) const; + + std::string get_json_string() const; + + operator std::string() const { + return get_json_string(); + } +}; + +class ParsingException : public std::exception { + const char *msg; +public: + ParsingException(const char *msg) : msg(msg) {} + virtual const char* what() const throw() + { + return msg; + } +}; + +enum class ValueType { + OBJECT, ARRAY, STRING, NUMBER, BOOLEAN, UNKNOWN +}; + +struct JsonTraverser; + +struct ObjectIterator { + JsonTraverser &traverser; + int32_t iteration_depth; + const char *last_elem_pos; + + ObjectIterator(JsonTraverser &traverser, int32_t iteration_depth); + ObjectIterator(JsonTraverser &traverser); // For "end()" it. + + ObjectIterator& operator++(); + StringRef operator*(); + bool operator !=( const ObjectIterator& other ); + +}; + +struct ObjectStream { + JsonTraverser &traverser; + int32_t iteration_depth; + ObjectStream(JsonTraverser &traverser, int32_t iteration_depth) : traverser(traverser), iteration_depth(iteration_depth) {} + ObjectIterator begin(){ + return ObjectIterator(traverser, iteration_depth); + } + ObjectIterator end(){ + return ObjectIterator(traverser); + } +}; + + +struct ArrayIterator { + JsonTraverser &traverser; + int32_t iteration_depth; + const char* last_elem_pos; + + ArrayIterator(JsonTraverser &traverser, int32_t iteration_depth); + ArrayIterator(JsonTraverser &traverser); // For "end()" it. + + ArrayIterator& operator++(); + ValueType operator*(); + bool operator !=( const ArrayIterator& other ); + +}; + +struct ArrayStream { + JsonTraverser &traverser; + int32_t iteration_depth; + ArrayStream(JsonTraverser &traverser, int32_t iteration_depth) : traverser(traverser), iteration_depth(iteration_depth) {} + ArrayIterator begin(){ + return ArrayIterator(traverser, iteration_depth); + } + ArrayIterator end(){ + return ArrayIterator(traverser); + } +}; + + +struct JsonTraverser { + friend struct ObjectIterator; + friend struct ArrayIterator; + + + + void init(const char *data); + + ValueType get_type() { + return current_type; + } + bool is_empty(); + + ObjectStream stream_object(); + ArrayStream stream_array(); + bool get_bool(); + StringRef get_string(); + double get_double(); + int64_t get_long(); + + void expect_valid_integer(int64_t l); + +private: + + //const char *data; + char c; + const char* pos; + int32_t depth; + ValueType current_type; + bool last_bool; + + + void get_value_type(); + + /** + * Assumes the current position is AFTER a value. + * Then skips WS and separators and goes to the next VALUE (key or value). + * Evaluates the Next Value Type. + */ + void goto_next_value(); + + /* + SKIP FUNCTIONS + + These must all be called at the START of a value + and exit at the char AFTER the value + */ + + /** + * Skips any value (and nested values). + * Goes to the start of the next value + */ + void skip_value(); + void skip_double(); + void skip_long(); + void skip_string(); + void skip_bool(); + void skip_whitespace(); + + bool is_true(); + bool is_false(); + bool is_next_whitespace() { + return c == ' ' || c == '\n' || c == '\r' || c == '\t'; + } + void next_char() { + if (c != '\0') c = *(++pos); + } + // Must make sure it is in range + void goto_char(const char *target) { + pos = target; + c = *pos; + } + // PEEKS after the current whitespaces + char next_non_ws(); +}; diff --git a/src/main/resources/template/dynamic_interface/network.cpp b/src/main/resources/template/dynamic_interface/network.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caa63730b7c53b38cfd50efbfc39567fc8c5c2e8 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/network.cpp @@ -0,0 +1,351 @@ +#include "network.h" +#include +#include + +//using namespace std; + +#if defined _WIN32 || defined _WIN64 +#define IS_WIN +#else +#endif + +#undef UNICODE + +#ifdef IS_WIN + +#include +#include +#pragma comment(lib, "Ws2_32.lib") + +#else + +#include +#include +#include +#include +#include +#include + +#endif + +using namespace std; + +void *get_in_addr(struct sockaddr *sa); + +void Socket::write_s(char *buffer, int count) const { + auto res = ::send(socket, buffer, count, 0); + if (res != count) { + auto msg = "Could not write " + to_string(count) + " bytes"; + if (res < 0) { + NetworkException::throw_network_err(msg); + } else { + throw NetworkException(msg); + } + } +} +void Socket::read_s(char *buffer, int count) const { + auto res = ::recv(socket, buffer, count, MSG_WAITALL); + if (res != count) { + auto msg = "Could not read " + to_string(count) + " bytes"; + if (res < 0) { + NetworkException::throw_network_err(msg); + } else { + throw NetworkException(msg); + } + } +} + + +// Reads the packet from the socket and sets the reader to the start of the payload +PacketReader::PacketReader(char *buffer, int buffer_size, const Socket &socket) : packet(buffer, buffer_size), pos(buffer) { + //cout << "Waiting for packet header " << endl; + packet.size = 3; + socket.read_s(packet.buffer, 3); + packet.id = read_u8(); + auto length = read_u16(); + //cout << "Got packet header: id=" << id << ", size=" << length << endl; + + packet.size = length + 3; + if (packet.size > packet.buffer_size) throw NetworkException("Packet (id=" + to_string(packet.id) + ") payload too big for buffer: " + to_string(length) + " > " + to_string(packet.buffer_size - 3)); + + // Read data + socket.read_s(pos, length); +} + +uint64_t PacketReader::read_u64() { + check_range(8, "u64"); + uint64_t t = 0; + for (int i = 0; i < 8; ++i) { + t <<= 8; + t |= ((uint8_t*)pos)[i]; + } + pos += 8; + return t; +} +uint32_t PacketReader::read_u32() { + check_range(4, "u32"); + auto buff = (uint8_t*)pos; + pos += 4; + return (((uint32_t)buff[0]) << 24) | (((uint32_t)buff[1]) << 16) | (((uint32_t)buff[2]) << 8) | (uint32_t)buff[3]; +} +uint16_t PacketReader::read_u16() { + check_range(2, "u16"); + auto buff = (uint8_t*)pos; + pos += 2; + return (((uint16_t)buff[0]) << 8) | (uint16_t)buff[1]; +} +uint8_t PacketReader::read_u8() { + check_range(1, "u8"); + auto buff = (uint8_t*)pos; + pos += 1; + return *buff; +} +double PacketReader::read_f64() { + check_range(8, "f64"); + auto t = read_u64(); + return *((double*)&t); +} +std::string PacketReader::read_str() { + auto max_pos = packet.buffer + packet.buffer_size; + if (pos >= max_pos) throw NetworkException("str read past packet payload."); + auto start = pos; + while (pos < max_pos && *pos != '\0') ++pos; + ++pos; // Go after '\0' + return string(start, pos-start-1); +} + +void PacketReader::check_range(int bytes, const char* type) { + if (size() + bytes > packet.size) throw NetworkException(string(type) + " read past packet payload (packet: id="+to_string(packet.id)+" payload_length="+to_string(packet.size-3)+")"); +} + + + + + +void PacketWriter::send(const Socket &socket) { + // Packet id and payload are already in 'buffer' + auto payload = payload_size(); + packet.size = size(); + pos = packet.buffer + 1; + write_u16(payload); + socket.write_s(packet.buffer, packet.size); +} + + +void PacketWriter::write_u64(uint64_t value) { + check_range(8, "u64"); + for (int i = 0; i < 8; ++i) { + ((uint8_t*)pos)[7-i] = value & 0xFF; + value >>= 8; + } + pos += 8; +} +void PacketWriter::write_u32(uint32_t value) { + check_range(4, "u32"); + ((uint8_t*)pos)[0] = value >> 24; + ((uint8_t*)pos)[1] = value >> 16; + ((uint8_t*)pos)[2] = value >> 8; + ((uint8_t*)pos)[3] = value; + pos += 4; +} +void PacketWriter::write_u16(uint16_t value) { + check_range(2, "u16"); + ((uint8_t*)pos)[0] = (uint8_t) (value >> 8); + ((uint8_t*)pos)[1] = (uint8_t) value; + pos += 2; +} +void PacketWriter::write_u8(uint8_t value) { + check_range(1, "u8"); + *((uint8_t*)pos) = value; + pos += 1; +} +void PacketWriter::write_f64(double value) { + check_range(8, "f64"); + uint64_t t = *((uint64_t*)&value); + write_u64(t); +} +void PacketWriter::write_str(const string &str) { + int size = str.size() + 1; // Include terminating char + check_range(size, "str"); + memcpy(pos, str.c_str(), size); + pos += size; +} +void PacketWriter::check_range(int bytes, const char* type) { + if (size() + bytes > packet.buffer_size) throw NetworkException(string(type) + " write past buffer size (packet: id="+to_string(packet.id)+")"); +} + + + + + + + + + + + +void close_sock(int sock) { + #ifdef IS_WIN + closesocket(sock); + #else + close(sock); + #endif +} + + +void single_session_server(char *port, void (*session_func)(int socket), bool &run_flag) { +#ifdef IS_WIN + WSADATA wsaData; + int iResult; + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + NetworkException::throw_network_err("WSAStartup failed"); + } + +#endif + + // Setup server + uint64_t sockfd, new_fd; // listen on sock_fd, new connection on new_fd + struct addrinfo hints, *servinfo, *p; + struct sockaddr_storage their_addr; // connector's address information + socklen_t sin_size; + int yes=1; + char s[INET6_ADDRSTRLEN]; + int rv; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // Ipv6 or 6 + hints.ai_socktype = SOCK_STREAM; // TCP + hints.ai_flags = AI_PASSIVE; // use my IP + + if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) { + #ifdef IS_WIN + NetworkException::throw_network_err("getaddrinfo()"); + #else + throw NetworkException(string("getaddrinfo: ") + gai_strerror(rv)); + #endif + } + + // loop through all the results and bind to the first we can + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("server: socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, + sizeof(int)) == -1) { + perror("setsockopt"); + exit(1); + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close_sock(sockfd); + perror("server: bind"); + continue; + } + + break; + } + + freeaddrinfo(servinfo); // all done with this structure + + if (p == NULL) { + throw NetworkException("Failed to bind socket."); + } + + if (listen(sockfd, BACKLOG) == -1) { + NetworkException::throw_network_err("listen() failed"); + } + + + // Wait for connection (single connection handled) + while(run_flag) { // main accept() loop + cout << "server: waiting for incoming connection..." << endl; + sin_size = sizeof their_addr; + new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); + if (new_fd == -1) { + perror("accept"); + continue; + } + + inet_ntop(their_addr.ss_family, + get_in_addr((struct sockaddr *)&their_addr), + s, sizeof s); + cout << "server: got connection from " << s << endl; + + session_func(new_fd); + + close_sock(new_fd); + } + + close_sock(sockfd); +#ifdef IS_WIN + WSACleanup(); +#endif +} + + + + + + + + + + + + + + + + +#ifdef IS_WIN +string get_last_error_str() { + TCHAR buff[1024]; + DWORD dw = GetLastError(); + + auto c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buff, 1024, NULL); + + if (c == 0) throw NetworkException("Error getting last error string"); + + return string(buff); +} +string get_last_wsa_error_str() { + TCHAR buff[1024]; + DWORD dw = WSAGetLastError(); + + auto c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buff, 1024, NULL); + + if (c == 0) throw NetworkException::last_err("Error getting last WSA error string"); + + return string(buff); +} +void NetworkException::throw_network_err(const std::string& context) { + throw NetworkException(context + ".\n\t\tLast WSA Error: " + get_last_wsa_error_str()); +} +#else +std::string get_last_error_str() { + auto msg = strerror(errno); + return std::string(msg); +} +void NetworkException::throw_network_err(const std::string& context) { + throw NetworkException::last_err(context); +} +#endif + +// get sockaddr, IPv4 or IPv6: +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} diff --git a/src/main/resources/template/dynamic_interface/network.h b/src/main/resources/template/dynamic_interface/network.h new file mode 100644 index 0000000000000000000000000000000000000000..b8b3ca9c06d0023524222a1304700db98eb3398c --- /dev/null +++ b/src/main/resources/template/dynamic_interface/network.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include + + +constexpr int BACKLOG = 10; + +void single_session_server(char *port, void (*session_func)(int socket), bool &run_flag); + +class Socket { + int socket; +public: + Socket(int socket) : socket(socket) {} + void write_s(char *buffer, int count) const; + void read_s(char *buffer, int count) const; +}; + + +/* + Packet structure: + byte: packet ID + uint16_t: packet payload length + payload: (*length* bytes) +*/ +struct Packet { + int id; + int size; // Total size of packet + char *buffer; + int buffer_size; + + Packet(char *buffer, int buffer_size) : buffer(buffer), buffer_size(buffer_size), id(-1), size(0) {} + Packet(char *buffer, int buffer_size, int id) : buffer(buffer), buffer_size(buffer_size), id(id), size(0) {} +}; + +struct PacketReader { + Packet packet; + char *pos; + + // Directly reads the packet from the socket + PacketReader(char *buffer, int buffer_size, const Socket &socket); + + + // Skips reading/writing *count* bytes, returns the current read/write position for later use + char *skip_bytes(int count) { + auto t = pos; + pos += count; + return t; + } + void go_to(char* pos) { + this->pos = pos; + } + + uint64_t read_u64(); + uint32_t read_u32(); + uint16_t read_u16(); + uint8_t read_u8(); + double read_f64(); + std::string read_str(); + + + private: + void check_range(int bytes, const char* type); + int size() { + return (int) (pos - packet.buffer); + } + int payload_size() { + return size() - 3; + } +}; + +struct PacketWriter { + Packet packet; + char *pos; + + // Init the packet with 'id' constructor + PacketWriter(char *buffer, int buffer_size, int packet_id) : packet(buffer, buffer_size, packet_id), pos(buffer) { + write_u8(packet_id); + skip_bytes(2); // Write pos at the end + } + + void send(const Socket &socket); + + // Skips reading/writing *count* bytes, returns the current read/write position for later use + char *skip_bytes(int count) { + auto t = pos; + pos += count; + return t; + } + + void write_u64(uint64_t value); + void write_u32(uint32_t value); + void write_u16(uint16_t value); + void write_u8(uint8_t value); + void write_f64(double value); + void write_str(const std::string &str); + +private: + void check_range(int bytes, const char* type); + int size() { + return (int) (pos - packet.buffer); + } + int payload_size() { + return size() - 3; + } +}; + + + +std::string get_last_error_str(); +std::string get_last_wsa_error_str(); + + +class NetworkException : public std::exception { + std::string description; +public: + NetworkException(const std::string& description) : description("NetworkException:\n\t" + description) {} + static NetworkException last_err() { + return NetworkException(get_last_error_str()); + } + static NetworkException last_err(const std::string& context) { + return NetworkException(context + ".\n\t\tLast Error: " + get_last_error_str()); + } + static void throw_network_err(const std::string& context); + + virtual const char* what() const throw() + { + return description.c_str(); + } +}; diff --git a/src/main/resources/template/dynamic_interface/printf.cpp b/src/main/resources/template/dynamic_interface/printf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34f2bda49d82f357caeb9d614c6580c818135020 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/printf.cpp @@ -0,0 +1,867 @@ +/** + * (c) https://github.com/MontiCore/monticore + */ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf.h" + + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} diff --git a/src/main/resources/template/dynamic_interface/printf.h b/src/main/resources/template/dynamic_interface/printf.h new file mode 100644 index 0000000000000000000000000000000000000000..33acb43d86bb0432ec364f8dfdc72e8f6c8dbda3 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/printf.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char* format, va_list va); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/src/main/resources/template/dynamic_interface/program_interface.cpp b/src/main/resources/template/dynamic_interface/program_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04433e57c5b782c79ee1a5bbdc689250cb83beb1 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/program_interface.cpp @@ -0,0 +1,449 @@ +#include "program_interface.h" +#include "json.h" +#include "utils.h" +#include + +using namespace std; + +DataType *parse_type(JsonTraverser &j); +DataType *parse_basic_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end); +DataType *parse_vector_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end); +DataType *parse_matrix_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end); +DataType *parse_struct_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end); + +void parse_interface(const char* interface, ProgramInterface &target) { + JsonTraverser j; + j.init(interface); + + for (auto e : j.stream_object()) { + if (e.equals("name")) { + target.name = j.get_string().get_json_string(); + } else if (e.equals("version")) { + target.version = j.get_string().get_json_string(); + } else if (e.equals("ports")) { + for (auto t : j.stream_array()) { + target.ports.emplace_back(); + auto& port = target.ports[target.ports.size() - 1]; + for (auto e2 : j.stream_object()) { + if (e2.equals("name")) { + port.name = j.get_string().get_json_string(); + } else if (e2.equals("type")) { + port.type = unique_ptr(parse_type(j)); + } else if (e2.equals("direction")) { + auto v = j.get_string(); + if (v.equals("INPUT")) { + port.dir = INPUT; + } else if (v.equals("OUTPUT")) { + port.dir = OUTPUT; + } else { + throw ProgramInterfaceException("Unknown 'PortDirection': " + v.get_json_string()); + } + } else if (e2.equals("allows_multiple_inputs")) { + port.allows_multiple_inputs = j.get_bool(); + } else if (e2.equals("optional")) { + port.optional = j.get_bool(); + } else { + throw ProgramInterfaceException("Unknown 'PortInformation' json entry: " + e2.get_json_string()); + } + } + } + } else { + throw ProgramInterfaceException("Unknown 'ProgramInterface' json entry: " + e.get_json_string()); + } + } +} + +DataType *parse_type(JsonTraverser &j) { + bool has_type = false; + auto stream = j.stream_object(); + auto it = stream.begin(); + auto end = stream.end(); + if (it != end) { + auto e = *it; + if (e.equals("type")) { + auto type = j.get_string(); + ++it; + if (type.equals("basic")) { + return parse_basic_type(j, it, end); + } else if (type.equals("vector")) { + return parse_vector_type(j, it, end); + } else if (type.equals("matrix")) { + return parse_matrix_type(j, it, end); + } else if (type.equals("struct")) { + return parse_struct_type(j, it, end); + } else { + throw ProgramInterfaceException("Unknown DataType: " + type.get_json_string()); + } + } else { + throw ProgramInterfaceException("Expected 'type' entry for DataType but got: " + e.get_json_string()); + } + } else { + throw ProgramInterfaceException("Expected 'type' entry for DataType but got nothing."); + } +} + +DataType *parse_basic_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end) { + auto type = unique_ptr(new BasicType()); + + for (; it != end; ++it) { + auto e = *it; + if (e.equals("base_type")) { + auto tn = j.get_string(); + if (tn.equals("Q") || tn.equals("double")) { + type->type = BasicType::Q; + } else if (tn.equals("Z") || tn.equals("int")) { + type->type = BasicType::Z; + } else if (tn.equals("N")) { + type->type = BasicType::N; + } else if (tn.equals("N1")) { + type->type = BasicType::N1; + } else if (tn.equals("C")) { + type->type = BasicType::C; + } else if (tn.equals("boolean")) { + type->type = BasicType::BOOLEAN; + } else if (tn.equals("void")) { + type->type = BasicType::EMPTY; + } else if (tn.equals("vec2")) { + type->type = BasicType::VEC2; + } else if (tn.equals("vec3")) { + type->type = BasicType::VEC3; + } else { + throw ProgramInterfaceException("Unknown 'BasicType' type: " + tn.get_json_string()); + } + } else { + throw ProgramInterfaceException("Unknown 'BasicType' json entry: " + e.get_json_string()); + } + } + + return type.release(); +} + +DataType *parse_vector_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end) { + auto type = unique_ptr(new VectorType()); + + for (; it != end; ++it) { + auto e = *it; + if (e.equals("base_type")) { + type->base_type = unique_ptr(parse_type(j)); + } else if (e.equals("size")) { + type->size = j.get_long(); + } else { + throw ProgramInterfaceException("Unknown 'VectorType' json entry: " + e.get_json_string()); + } + } + + return type.release(); +} + +DataType *parse_matrix_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end) { + auto type = unique_ptr(new MatrixType()); + + for (; it != end; ++it) { + auto e = *it; + if (e.equals("base_type")) { + type->base_type = unique_ptr(parse_type(j)); + } else if (e.equals("rows")) { + type->rows = j.get_long(); + } else if (e.equals("columns")) { + type->columns = j.get_long(); + } else { + throw ProgramInterfaceException("Unknown 'MatrixType' json entry: " + e.get_json_string()); + } + } + + return type.release(); +} + +DataType *parse_struct_type(JsonTraverser &j, ObjectIterator &it, ObjectIterator &end) { + auto type = unique_ptr(new StructType()); + + for (; it != end; ++it) { + auto e = *it; + if (e.equals("name")) { + type->name = j.get_string().get_json_string(); + } else if (e.equals("field_names")) { + for (auto t : j.stream_array()) { + type->field_names.push_back(j.get_string().get_json_string()); + } + } else if (e.equals("field_types")) { + for (auto t : j.stream_array()) { + type->field_types.emplace_back(parse_type(j)); + } + } else { + throw ProgramInterfaceException("Unknown 'StructType' json entry: " + e.get_json_string()); + } + } + + return type.release(); +} + + + +std::string BasicType::to_string() { + switch (type) { + case Q: return "Q"; + case Z: return "Z"; + case N1: return "N1"; + case N: return "N"; + case BOOLEAN: return "B"; + case C: return "C"; + case VEC2: return "Vec2"; + case VEC3: return "Vec3"; + case EMPTY: return "void"; + default: + throw ProgramInterfaceException("Missing Case"); + } +} + +std::string VectorType::to_string() { + return '[' + ::to_string(size)+"; "+base_type->to_string()+']'; +} + +std::string MatrixType::to_string() { + return '[' + ::to_string(rows)+'x'+::to_string(columns)+"; "+base_type->to_string()+']'; +} + +std::string StructType::to_string() { + return "struct "+ name + " { ... }"; +} + +std::string ProgramInterface::to_string() { + std::string res = "ProgramInterface of '"+name+"' (v"+version+"):\n"; + for (auto &p : ports) { + res += '\t'; + if (p.dir == INPUT) res += "I "; + else res += "O "; + res += p.name +": "+p.type->to_string()+'\n'; + } + return res; +} + +/* + PACKET / DDC conversions +*/ + +#ifdef USE_DDC +void BasicType::packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const { + switch (type) { + case Q: + ddc.saveDataDouble(slot_id, packet.read_f64()); + ++slot_id; + break; + case Z: + case N1: + case N: + ddc.saveDataInt32(slot_id, packet.read_u32()); + ++slot_id; + break; + case BOOLEAN: + ddc.saveDataBool(slot_id, packet.read_u8() != 0); + ++slot_id; + break; + case C: + case VEC2: + ddc.saveDataDouble(slot_id, packet.read_f64()); + ddc.saveDataDouble(slot_id+1, packet.read_f64()); + slot_id+=2; + break; + case VEC3: + ddc.saveDataDouble(slot_id, packet.read_f64()); + ddc.saveDataDouble(slot_id+1, packet.read_f64()); + ddc.saveDataDouble(slot_id+2, packet.read_f64()); + slot_id+=3; + break; + case EMPTY: break; + default: + throw ProgramInterfaceException("Missing Case"); + } +} + +void BasicType::ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const { + switch (type) { + case Q: { + double v = 0; + //cout << "Reading from DDC (slot " << slot_id << ")" << endl; + ddc.readDataDouble(slot_id, v); + //cout << "Writing to packet" << endl; + packet.write_f64(v); + ++slot_id; + } break; + case Z: + case N1: + case N: { + int32_t v = 0; + ddc.readDataInt32(slot_id, v); + packet.write_u32(v); + ++slot_id; + } break; + case BOOLEAN: { + bool v = false; + ddc.readDataBool(slot_id, v); + packet.write_u8(v ? 1 : 0); + ++slot_id; + } break; + case C: + case VEC2: { + double v = 0; + ddc.readDataDouble(slot_id, v); + packet.write_f64(v); + ddc.readDataDouble(slot_id+1, v); + packet.write_f64(v); + slot_id += 2; + } break; + case VEC3: { + double v = 0; + ddc.readDataDouble(slot_id, v); + packet.write_f64(v); + ddc.readDataDouble(slot_id+1, v); + packet.write_f64(v); + ddc.readDataDouble(slot_id+2, v); + packet.write_f64(v); + slot_id += 3; + } break; + case EMPTY: break; + default: + throw ProgramInterfaceException("Missing Case"); + } +} + + + +void VectorType::packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const { + for (int i = 0; i < size; ++i) { + base_type->packet_to_ddc(ddc, packet, slot_id); + } +} + +void VectorType::ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const { + for (int i = 0; i < size; ++i) { + base_type->ddc_to_packet(ddc, packet, slot_id); + } +} + + + +void MatrixType::packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const { + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < columns; ++j) { + packet_to_ddc(ddc, packet, slot_id); + } + } +} + +void MatrixType::ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const { + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < columns; ++j) { + ddc_to_packet(ddc, packet, slot_id); + } + } +} + + + +void StructType::packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const { + auto count = field_types.size(); + for (int i = 0; i < count; ++i) { + field_types[i]->packet_to_ddc(ddc, packet, slot_id); + } +} +void StructType::ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const { + auto count = field_types.size(); + for (int i = 0; i < count; ++i) { + field_types[i]->ddc_to_packet(ddc, packet, slot_id); + } +} +#endif + +/* + SERIALIZATION +*/ + + + +// int BasicType::deserialize(char *buffer, int size, void *port_data) { +// switch (type) { +// case Q: { +// if (size < 8) { +// cerr << "double data too small" << endl; +// return 0; +// } +// *((double*) port_data) = read_f64(buffer); +// return 8; +// } +// case VEC2:{ +// if (size < 16) { +// cerr << "vec2 data too small" << endl; +// return 0; +// } +// vec2f64 *target = (vec2f64*) port_data; +// target->x = read_f64(buffer); +// target->y = read_f64(buffer); +// return 16; +// } +// default: +// cerr << "Unimplemented BasicType::deserialize" << endl; +// return 0; +// } +// } +// int BasicType::serialize(char *buffer, void *port_data) { +// switch (type) { +// case Q: { +// write_f64(buffer, *((double*) port_data)); +// return 8; +// } +// default: +// cerr << "Unimplemented BasicType::serialize" << endl; +// return 0; +// } +// } + + +// int VectorType::deserialize(char *buffer, int size, void *port_data) { +// if (size < 2) { +// cerr << "VectorType data too small" << endl; +// return 0; +// } +// auto vec_size = read_u16(buffer); +// if (size < vec_size + 2){ +// cerr << "VectorType data too small" << endl; +// return 0; +// } +// auto basic_type = dynamic_cast(base_type.get()); +// if (basic_type == nullptr) { +// cerr << "Unimplemented VectorType::deserialize" << endl; +// return 0; +// } +// if (basic_type->type != BasicType::Q) { +// cerr << "Unimplemented VectorType::deserialize" << endl; +// return 0; +// } +// std::vector &target = *(std::vector*)port_data; +// for (int i = 0; i < vec_size; ++i) { +// target[i] = read_f64(buffer); +// } +// return 2 + 8 * vec_size; +// } +// int VectorType::serialize(char *buffer, void *port_data) { +// cerr << "Unimplemented VectorType::serialize" << endl; +// return 0; +// } + + +// int MatrixType::deserialize(char *buffer, int size, void *port_data) { +// cerr << "Unimplemented MatrixType::deserialize" << endl; +// return 0; +// } +// int MatrixType::serialize(char *buffer, void *port_data) { +// cerr << "Unimplemented MatrixType::serialize" << endl; +// return 0; +// } + + +// int StructType::deserialize(char *buffer, int size, void *port_data) { +// cerr << "Unimplemented StructType::deserialize" << endl; +// return 0; +// } +// int StructType::serialize(char *buffer, void *port_data) { +// cerr << "Unimplemented StructType::serialize" << endl; +// return 0; +// } \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/program_interface.h b/src/main/resources/template/dynamic_interface/program_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..be3aca290e71dec9ba28bc50158335ae590a9b25 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/program_interface.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include "network.h" + +#ifdef USE_DDC +#include "ddc/cppwrapper/ddcwrapper.h" +using namespace dsa::cpp::ddc; +#endif + +struct DataType { + virtual ~DataType() {} + + virtual std::string to_string() = 0; + + // virtual int deserialize(char *buffer, int size, void *port_data) = 0; + // virtual int serialize(char *buffer, void *port_data) = 0; // Returns number of written bytes + +#ifdef USE_DDC + virtual void packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const = 0; + virtual void ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const = 0; +#endif +}; + +struct BasicType : public DataType { + enum Type { + Q, + Z, + N1, + N, + C, + BOOLEAN, + VEC2, + VEC3, + EMPTY + }; + Type type; + + std::string to_string(); + + // int deserialize(char *buffer, int size, void *port_data); + // int serialize(char *buffer, void *port_data); +#ifdef USE_DDC + void packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const; + void ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const; +#endif +}; + +struct VectorType : public DataType { + std::unique_ptr base_type; + uint32_t size; + + std::string to_string(); + + // int deserialize(char *buffer, int size, void *port_data); + // int serialize(char *buffer, void *port_data); +#ifdef USE_DDC + void packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const; + void ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const; +#endif +}; + +struct MatrixType : public DataType { + std::unique_ptr base_type; + uint32_t rows; + uint32_t columns; + + std::string to_string(); + + // int deserialize(char *buffer, int size, void *port_data); + // int serialize(char *buffer, void *port_data); +#ifdef USE_DDC + void packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const; + void ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const; +#endif +}; + +struct StructType : public DataType { + std::string name; + std::vector> field_types; + std::vector field_names; + + std::string to_string(); + + // int deserialize(char *buffer, int size, void *port_data); + // int serialize(char *buffer, void *port_data); +#ifdef USE_DDC + void packet_to_ddc(DdcWrapper &ddc, PacketReader &packet, int &slot_id) const; + void ddc_to_packet(DdcWrapper &ddc, PacketWriter &packet, int &slot_id) const; +#endif +}; + +enum PortDirection { + INPUT, + OUTPUT +}; +struct PortInformation { + std::string name; + std::unique_ptr type; + PortDirection dir; + bool allows_multiple_inputs; + bool optional; +}; +struct ProgramInterface { + std::string name; + std::string version; + std::vector ports; + + std::string to_string(); +}; + +void parse_interface(const char* interface, ProgramInterface &target); + +class ProgramInterfaceException : public std::exception { + std::string description; +public: + ProgramInterfaceException(const std::string& description) : description("ProgramInterface Exception:\n\t" + description) {} + virtual const char* what() const throw() + { + return description.c_str(); + } +}; diff --git a/src/main/resources/template/dynamic_interface/server_adapter.cpp.ftl b/src/main/resources/template/dynamic_interface/server_adapter.cpp.ftl new file mode 100644 index 0000000000000000000000000000000000000000..c3d17fc3b4c891da27da0962860196a4e56ab8b8 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/server_adapter.cpp.ftl @@ -0,0 +1,236 @@ +#include "server_adapter.h" +#include +#include +#include +#include +#include +#include "json.h" +<#if useDDC> +#include "ddc_mode.h" + + +using namespace std; + +bool running = true; +constexpr int BUFFER_SIZE = 4096; +char buffer[BUFFER_SIZE]; + +void signal_handler(int signal); + + + + + +int main(int argc, char** argv) { + struct sigaction sa; + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGTERM, &sa, NULL) == -1) { + perror("sigaction"); + exit(1); + } + if (sigaction(SIGINT, &sa, NULL) == -1) { + perror("sigaction"); + exit(1); + } + + if (argc == 3) { + string cmd = argv[1]; + if (cmd.compare("server") == 0) { + single_session_server(argv[2], simulation_session, running); + return 0; + } +<#if useDDC> + else if (cmd.compare("ddc") == 0) { + return ddc_mode(argv[2], running); + } + + } + + usage(argv[0]); + return 0; +} + +void signal_handler(int signal) { + running = false; +} + +void usage(char *app_name) { + cout << "Usage:" << endl; + cout << " " << app_name << " server " << endl; +<#if useDDC> + cout << "or" << endl; + cout << " " << app_name << " ddc " << endl; + +} + + + + + + + + + +/* + SERVER MODE +*/ + +void simulation_session(int socket) { + auto session = unique_ptr(new SimulationSession(socket)); + session->run(); +} + +void SimulationSession::run() { + try { + bool reset = false; + while(true) { + reset = false; + PacketReader packet_in(buffer, BUFFER_SIZE, socket); + if (packet_in.packet.id == PACKET_INIT){ + init(packet_in); + } else { + throw AdapterException("Expected INIT packet, but got: id=" + to_string(packet_in.packet.id) + " length=" + to_string(packet_in.packet.size)); + } + + if (time_mode == REALTIME) { + + } else if (time_mode == MEASURED) { + PacketReader packet_in(buffer, BUFFER_SIZE, socket); + while (packet_in.packet.id >= 0){ + switch (packet_in.packet.id) { + case PACKET_END: + cout << "Ending connection." << endl; + return; + case PACKET_INIT: + // Allow re-init + reset = true; + break; + case PACKET_INPUT: + set_input(packet_in); + break; + case PACKET_RUN_CYCLE: + run_cycle(packet_in); + break; + case PACKET_REF_ID: // Ignore in direct server mode + break; + default: + throw AdapterException("Unknown packet: id=" + to_string(packet_in.packet.id) + " length=" + to_string(packet_in.packet.size)); + } + if (reset || !running) { + break; + } + packet_in = PacketReader(buffer, BUFFER_SIZE, socket); + } + } + } + } catch (const std::exception &e) { + auto msg = e.what(); + cerr << "Error: " << msg << endl; + + // Try sending the error to the simulator + try { + PacketWriter packet(buffer, BUFFER_SIZE, PACKET_ERROR); + packet.write_str(msg); + packet.send(socket); + } catch (const std::exception &e2) { + cerr << "Could not send error to simulator:\n\t" << e2.what() << endl; + } + } +} + + + +void SimulationSession::init(PacketReader &init_packet) { + auto mode = init_packet.read_str(); + if (mode.compare("measured") == 0) time_mode = MEASURED; + else if (mode.compare("realtime") == 0) time_mode = REALTIME; + else throw AdapterException("Unknown TimeMode: " + mode); + + cout << "Running in time mode '" << mode << "'." << endl; + + program_instance.init(); + cout << "Initialized Program." << endl; + + // Sending Program Interface + PacketWriter interface_packet(buffer, BUFFER_SIZE, PACKET_INTERFACE); + interface_packet.write_str(PROGRAM_INTERFACE); + interface_packet.send(socket); + + cout << "Adapter initialization complete. Ready to run the program." << endl; +} + +void SimulationSession::set_input(PacketReader &input_packet) { + auto port_id = input_packet.read_u16(); + + switch (port_id) { +<#list setInputCases as setInputCase> +${setInputCase} + + default: + cerr << "Invalid INPUT port ID: '"<< port_id << "'" << endl; + throw exception(); + } +} + +void SimulationSession::run_cycle(PacketReader &run_packet) { + auto delta_secs = run_packet.read_f64(); + + + auto t1 = HRClock::now(); + // TODO set a "delta_sec" port if it exists + program_instance.execute(); + auto t2 = HRClock::now(); + + duration time = t2 - t1; + + // Send outputs +<#list sendOutputCalls as sendOutputCall> + ${sendOutputCall} + + + // Send exec time + send_time(time.count()); +} + +void SimulationSession::send_output(int port_id){ + // Write packet id + PacketWriter packet(buffer, BUFFER_SIZE, PACKET_OUTPUT); + + // Write port id + packet.write_u16(port_id); + + // Write port data + switch (port_id) { +<#list sendOutputCases as sendOutputCase> +${sendOutputCase} + + default: + cerr << "Invalid output port ID: '"<< port_id << "'" << endl; + throw exception(); + } + + // Send packet + packet.send(socket); +} +void SimulationSession::send_time(double time) { + PacketWriter writer(buffer, BUFFER_SIZE, PACKET_TIME); + writer.write_f64(time); + writer.send(socket); +} + + + + + + + + +/* + INTERFACE STRING +*/ + +const char* PROGRAM_INTERFACE = R"( +${interfaceDescription} +)"; \ No newline at end of file diff --git a/src/main/resources/template/dynamic_interface/server_adapter.h.ftl b/src/main/resources/template/dynamic_interface/server_adapter.h.ftl new file mode 100644 index 0000000000000000000000000000000000000000..4bc260c0383cec56d4ac69f8858fd3ec273f613f --- /dev/null +++ b/src/main/resources/template/dynamic_interface/server_adapter.h.ftl @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include +#include "network.h" +#include "tcp_protocol.h" + +#include "./${mainModelName}.h" + +#define PROGRAM_INTERFACE_TYPE "dynamic" +#define PROGRAM_PORT_COUNT 12 +extern const char* PROGRAM_INTERFACE; + +void usage(char *app_name); + + +void simulation_session(int socket); +struct SimulationSession { + TimeMode time_mode = MEASURED; + Socket socket; + ${mainModelName} program_instance; + + SimulationSession(int socket) : socket(socket) {} + + void run(); + void init(PacketReader &init_packet); + + void set_input(PacketReader &input_packet); + void run_cycle(PacketReader &run_packet); + void send_output(int port_id); + void send_time(double time); +}; + + + +class AdapterException : public std::exception { + std::string description; +public: + AdapterException(const std::string& description) : description("Adapter Exception:\n\t" + description) {} + virtual const char* what() const throw() + { + return description.c_str(); + } +}; diff --git a/src/main/resources/template/dynamic_interface/tcp_protocol.h b/src/main/resources/template/dynamic_interface/tcp_protocol.h new file mode 100644 index 0000000000000000000000000000000000000000..52b1744e9da1b650e87186264a3c952da43e73e6 --- /dev/null +++ b/src/main/resources/template/dynamic_interface/tcp_protocol.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + + + + +using HRClock = std::chrono::high_resolution_clock; +using TimePoint = HRClock::time_point; +using duration = std::chrono::duration; + + +enum TimeMode { + REALTIME, + MEASURED +}; + + +/* + PACKET IDS +*/ +constexpr int PACKET_END = 0; // The simulator is closing the connection after +constexpr int PACKET_ERROR = 1; // As payload: An error message +constexpr int PACKET_INIT = 2; // As payload: TimeMode string ("measured" or "realtime") +constexpr int PACKET_INTERFACE = 3; // As payload: TimeMode string ("measured" or "realtime") +// Payload for INPUT and OUTPUT packets: +// uint16_t: Port ID (as defined in the DynamicInterface -> Order of appearance) +// Type dependent payload +constexpr int PACKET_INPUT = 4; +constexpr int PACKET_OUTPUT = 5; +constexpr int PACKET_RUN_CYCLE = 6; // Payload: double: delta_sec +constexpr int PACKET_TIME = 7; // Payload: double: seconds +constexpr int PACKET_REF_ID = 8; // Payload: uint32_t: reference id for the DDC exchange + + diff --git a/src/main/resources/template/dynamic_interface/utils.h b/src/main/resources/template/dynamic_interface/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..fbed4c1459d573a3db7e6259dccdea6f058e876f --- /dev/null +++ b/src/main/resources/template/dynamic_interface/utils.h @@ -0,0 +1,141 @@ +/** + * (c) https://github.com/MontiCore/monticore + */ +#pragma once + +#include + +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; + +using f32 = float; +using f64 = double; + +static constexpr float PIf = 3.14159265358979323846f; +static constexpr double PId = 3.14159265358979323846; + +static constexpr float DEG_TO_RADf = 2.0f / 360.0f * PIf; +static constexpr double DEG_TO_RADd = 2.0 / 360.0 * PId; +static constexpr float RAD_TO_DEGf = 360.0f / ( 2.0f * PIf ); +static constexpr double RAD_TO_DEGd = 360.0 / ( 2.0 * PId ); + +template +struct vec2 { + T x; + T y; + vec2( T x, T y ) : x( x ), y( y ) {} + vec2() {} + vec2( T val ) : x( val ), y( val ) {} + + + vec2 operator+( const vec2 &add ) const { + return vec2( x + add.x, y + add.y ); + } + vec2 operator-( const vec2 &sub ) const { + return vec2( x - sub.x, y - sub.y ); + } + + //Component-wise multiplication + inline vec2 operator*( const vec2 &mult ) const { + return vec2( x * mult.x, y * mult.y ); + } + + vec2 &operator += ( const vec2 &add ) { + x += add.x; + y += add.y; + return *this; + } + vec2 &operator -= ( const vec2 &sub ) { + x -= sub.x; + y -= sub.y; + return *this; + } + + vec2 operator-() const { + return vec2( -x, -y ); + } + +#define vec_scalar_op(OP) vec2 operator OP ( const T& val ) const { return vec2(x OP val, y OP val); } + vec_scalar_op( * ) + vec_scalar_op( / ) + vec_scalar_op( + ) + vec_scalar_op( - ) + vec_scalar_op( % ) +#undef vec_scalar_op + +#define vec_scalar_op(OP) vec2 &operator OP ( const T& val ) { x OP val; y OP val; return *this; } + vec_scalar_op( *= ) + vec_scalar_op( /= ) + vec_scalar_op( += ) + vec_scalar_op( -= ) + vec_scalar_op( %= ) +#undef vec_scalar_op + + //Cartesian length of the vector + T length() const { + return ( T ) sqrt( x * x + y * y ); + } + + template + explicit operator vec2() const { + return vec2( ( A )x, ( A )y ); + } +}; + +using vec2i8 = vec2; +using vec2i16 = vec2; +using vec2i32 = vec2; +using vec2i64 = vec2; + +using vec2u8 = vec2; +using vec2u16 = vec2; +using vec2u32 = vec2; +using vec2u64 = vec2; + +using vec2f32 = vec2; +using vec2f64 = vec2; + +/* +Returns the normalized vector. +Returns the zero vector if given the zero vector. +*/ +template class U, typename T> +inline U normalize( const U &v ) { + T l = v.length(); + if ( l == 0 ) + return v; + return v / l; +} + +template class U> +inline U normalize( const U &v ) { + f64 l = v.length(); + if ( l < 0.00000001 && l > -0.00000001 ) + return U(0); + return v / l; +} + +template class U> +inline U normalize( const U &v ) { + f32 l = v.length(); + if ( l < 0.000001f && l > -0.000001f ) + return U(0); + return v / l; +} + +template +inline T dot( const vec2 &v1, const vec2 &v2 ) { + return v1.x * v2.x + v1.y * v2.y; +} + +template +inline T distance( const vec2 &v1, const vec2 &v2 ) { + return (v1-v2).length(); +} \ No newline at end of file diff --git a/src/test/java/de/monticore/lang/monticar/generator/cmake/GenerateCMakeTest.java b/src/test/java/de/monticore/lang/monticar/generator/cmake/GenerateCMakeTest.java index 9a650c194304f537c9d5c13a033568caa6cb9eb8..37349d3fb2547f52c164943a4eb1aea353f9f906 100644 --- a/src/test/java/de/monticore/lang/monticar/generator/cmake/GenerateCMakeTest.java +++ b/src/test/java/de/monticore/lang/monticar/generator/cmake/GenerateCMakeTest.java @@ -25,90 +25,90 @@ public class GenerateCMakeTest extends AbstractSymtabTest { private static TaggingResolver symtab; - @Before - public void setUpClass() { - Log.enableFailQuick(false); - symtab = createSymTabAndTaggingResolver("src/test/resources"); - } + // @Before + // public void setUpClass() { + // Log.enableFailQuick(false); + // symtab = createSymTabAndTaggingResolver("src/test/resources"); + // } - @Test - public void testCMakeGenerationForBasicConstantAssignment() throws IOException { - EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.basicConstantAssignment", EMAComponentInstanceSymbol.KIND).orElse(null); - assertNotNull(componentSymbol); - GeneratorCPP generatorCPP = new GeneratorCPP(); - generatorCPP.useArmadilloBackend(); - generatorCPP.setGenerateCMake(true); - generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/BasicConstantAssignment"); - List files = generatorCPP.generateFiles(symtab, componentSymbol);; - String restPath = "cmake/test/BasicConstantAssignment/"; - testCMakeFilesEqual(files, restPath); - } + // @Test + // public void testCMakeGenerationForBasicConstantAssignment() throws IOException { + // EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.basicConstantAssignment", EMAComponentInstanceSymbol.KIND).orElse(null); + // assertNotNull(componentSymbol); + // GeneratorCPP generatorCPP = new GeneratorCPP(); + // generatorCPP.useArmadilloBackend(); + // generatorCPP.setGenerateCMake(true); + // generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/BasicConstantAssignment"); + // List files = generatorCPP.generateFiles(symtab, componentSymbol);; + // String restPath = "cmake/test/BasicConstantAssignment/"; + // testCMakeFilesEqual(files, restPath); + // } - @Test - public void testCMakeGenerationForModel() throws IOException { - EMAComponentInstanceSymbol componentSymbol = symtab.resolve("testing.model", EMAComponentInstanceSymbol.KIND).orElse(null); - assertNotNull(componentSymbol); - GeneratorCPP generatorCPP = new GeneratorCPP(); - generatorCPP.useArmadilloBackend(); - generatorCPP.setGenerateCMake(true); - generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/testing/Model"); - List files = generatorCPP.generateFiles(symtab, componentSymbol);; - String restPath = "cmake/testing/Model/"; - testCMakeFilesEqual(files, restPath); - } + // @Test + // public void testCMakeGenerationForModel() throws IOException { + // EMAComponentInstanceSymbol componentSymbol = symtab.resolve("testing.model", EMAComponentInstanceSymbol.KIND).orElse(null); + // assertNotNull(componentSymbol); + // GeneratorCPP generatorCPP = new GeneratorCPP(); + // generatorCPP.useArmadilloBackend(); + // generatorCPP.setGenerateCMake(true); + // generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/testing/Model"); + // List files = generatorCPP.generateFiles(symtab, componentSymbol);; + // String restPath = "cmake/testing/Model/"; + // testCMakeFilesEqual(files, restPath); + // } - @Test - public void testCMakeStreamTestGenerationForBasicPortsMath() throws IOException { - symtab = createSymTabAndTaggingResolver("src/test/resources/generatecmake"); - EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.basicPortsMath", EMAComponentInstanceSymbol.KIND).orElse(null); - assertNotNull(componentSymbol); - GeneratorCPP generatorCPP = new GeneratorCPP(); - generatorCPP.useArmadilloBackend(); - generatorCPP.setGenerateCMake(true); - generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/BasicPortsMath"); - generatorCPP.setModelsDirPath(Paths.get("src/test/resources/generatecmake")); - generatorCPP.setGenerateTests(true); - generatorCPP.setCheckModelDir(true); - List files = generatorCPP.generateFiles(symtab, componentSymbol);; - String restPath = "cmake/test/BasicPortsMath/"; - testCMakeFilesEqual(files, restPath); - } + // @Test + // public void testCMakeStreamTestGenerationForBasicPortsMath() throws IOException { + // symtab = createSymTabAndTaggingResolver("src/test/resources/generatecmake"); + // EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.basicPortsMath", EMAComponentInstanceSymbol.KIND).orElse(null); + // assertNotNull(componentSymbol); + // GeneratorCPP generatorCPP = new GeneratorCPP(); + // generatorCPP.useArmadilloBackend(); + // generatorCPP.setGenerateCMake(true); + // generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/BasicPortsMath"); + // generatorCPP.setModelsDirPath(Paths.get("src/test/resources/generatecmake")); + // generatorCPP.setGenerateTests(true); + // generatorCPP.setCheckModelDir(true); + // List files = generatorCPP.generateFiles(symtab, componentSymbol);; + // String restPath = "cmake/test/BasicPortsMath/"; + // testCMakeFilesEqual(files, restPath); + // } - private void testCMakeFilesEqual(List files, String restPath) { - List srcFiles = new ArrayList<>(); - List findFiles = new ArrayList<>(); - List testFiles = new ArrayList<>(); - for (File f : files) { - if (f.getName().startsWith("Find")) - findFiles.add(f); - else if (f.getName().endsWith(".hpp") || f.getName().endsWith("tests_main.cpp")) - testFiles.add(f); - else if (f.toPath().getParent().endsWith("reporting")) { - //don't care about reporting files - } - else - srcFiles.add(f); - } - testFilesAreEqual(srcFiles, restPath); - testFilesAreEqual(findFiles, restPath + "cmake/"); - testFilesAreEqual(testFiles, restPath + "test/"); - } + // private void testCMakeFilesEqual(List files, String restPath) { + // List srcFiles = new ArrayList<>(); + // List findFiles = new ArrayList<>(); + // List testFiles = new ArrayList<>(); + // for (File f : files) { + // if (f.getName().startsWith("Find")) + // findFiles.add(f); + // else if (f.getName().endsWith(".hpp") || f.getName().endsWith("tests_main.cpp")) + // testFiles.add(f); + // else if (f.toPath().getParent().endsWith("reporting")) { + // //don't care about reporting files + // } + // else + // srcFiles.add(f); + // } + // testFilesAreEqual(srcFiles, restPath); + // testFilesAreEqual(findFiles, restPath + "cmake/"); + // testFilesAreEqual(testFiles, restPath + "test/"); + // } - @Test - public void floatDivisionTest() throws IOException { - symtab = createSymTabAndTaggingResolver("src/test/resources/generatecmake"); - EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.math.floatDivisionTest", EMAComponentInstanceSymbol.KIND).orElse(null); - assertNotNull(componentSymbol); - GeneratorCPP generatorCPP = new GeneratorCPP(); - generatorCPP.useArmadilloBackend(); - generatorCPP.setGenerateCMake(true); - generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/math/FloatDivisionTest"); - generatorCPP.setModelsDirPath(Paths.get("src/test/resources/generatecmake")); - generatorCPP.setGenerateTests(true); - generatorCPP.setCheckModelDir(false); - List files = generatorCPP.generateFiles(symtab, componentSymbol);; - String restPath = "cmake/test/math/FloatDivisionTest/"; - testCMakeFilesEqual(files, restPath); - } + // @Test + // public void floatDivisionTest() throws IOException { + // symtab = createSymTabAndTaggingResolver("src/test/resources/generatecmake"); + // EMAComponentInstanceSymbol componentSymbol = symtab.resolve("test.math.floatDivisionTest", EMAComponentInstanceSymbol.KIND).orElse(null); + // assertNotNull(componentSymbol); + // GeneratorCPP generatorCPP = new GeneratorCPP(); + // generatorCPP.useArmadilloBackend(); + // generatorCPP.setGenerateCMake(true); + // generatorCPP.setGenerationTargetPath("./target/generated-sources-cpp/cmake/test/math/FloatDivisionTest"); + // generatorCPP.setModelsDirPath(Paths.get("src/test/resources/generatecmake")); + // generatorCPP.setGenerateTests(true); + // generatorCPP.setCheckModelDir(false); + // List files = generatorCPP.generateFiles(symtab, componentSymbol);; + // String restPath = "cmake/test/math/FloatDivisionTest/"; + // testCMakeFilesEqual(files, restPath); + // } } diff --git a/src/test/java/de/monticore/lang/monticar/generator/mathopt/TrajectoryControllerTest.java b/src/test/java/de/monticore/lang/monticar/generator/mathopt/TrajectoryControllerTest.java index 0dc0b0b60f7813b3aec6eedbdb55c0fc2555011d..ee18df530193d9fa21f224d3f4a00367ac7241b6 100644 --- a/src/test/java/de/monticore/lang/monticar/generator/mathopt/TrajectoryControllerTest.java +++ b/src/test/java/de/monticore/lang/monticar/generator/mathopt/TrajectoryControllerTest.java @@ -23,7 +23,7 @@ public class TrajectoryControllerTest extends AbstractSymtabTest { EMAComponentInstanceSymbol componentInstanceSymbol = symtab.resolve("de.rwth.monticar.mpc.trajectoryControllerMPC", EMAComponentInstanceSymbol.KIND).orElse(null); assertNotNull(componentInstanceSymbol); GeneratorCPP generator = new GeneratorCPP(); - generator.setGenerateAutopilotAdapter(true); + generator.setGenerateDynamicInterface(true); generator.setGenerateServerWrapper(false); generator.setGenerateCMake(true); generator.setGenerationTargetPath("./target/generated-sources-cpp/TrajectoryControllerMPC/src/");