CppGenerator.xtend 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. package be.uantwerpen.ansymo.semanticadaptation.cg.cpp
  2. import be.uantwerpen.ansymo.semanticadaptation.generator.SemanticAdaptationGenerator
  3. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Adaptation
  4. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.ControlRuleBlock
  5. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InOutRules
  6. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InRulesBlock
  7. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InnerFMU
  8. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.OutRulesBlock
  9. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SemanticAdaptation
  10. import java.io.File
  11. import java.util.ArrayList
  12. import java.util.LinkedHashMap
  13. import org.eclipse.emf.ecore.resource.Resource
  14. import org.eclipse.xtext.generator.IFileSystemAccess2
  15. import org.eclipse.xtext.generator.IGeneratorContext
  16. import java.util.Collection
  17. import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Port
  18. import org.eclipse.emf.common.util.EList
  19. class CppGenerator extends SemanticAdaptationGenerator {
  20. var ModelDescriptionCreator mdCreator = new ModelDescriptionCreator()
  21. var SwitchTest = new Visitor();
  22. private var IFileSystemAccess2 fsa;
  23. override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
  24. this.fsa = fsa;
  25. for (SemanticAdaptation type : resource.allContents.toIterable.filter(SemanticAdaptation)) {
  26. type.compile;
  27. }
  28. }
  29. // TODO: Verify adaptation.name is not a C++ keyword
  30. // TODO: Add initial value to inputs in the model description file
  31. def void compile(SemanticAdaptation adaptation) {
  32. for (Adaptation type : adaptation.elements.filter(Adaptation)) {
  33. val adapInteralRefName = type.name;
  34. val adapClassName = type.name.toFirstUpper;
  35. val adapExternalName = type.type.name;
  36. var LinkedHashMap<String, Pair<String, Integer>> svDefs = newLinkedHashMap();
  37. var ArrayList<Pair<String, String>> fmus = newArrayList();
  38. var LinkedHashMap<String, ScalarVariable> sVars = newLinkedHashMap();
  39. var String genSource = "";
  40. var ModelDescription md;
  41. var LinkedHashMap<String, Collection<ScalarVariable>> scalarVariables = newLinkedHashMap();
  42. var LinkedHashMap<String, LinkedHashMap<String, MappedScalarVariable>> mappedScalarVariables = newLinkedHashMap();
  43. // Load Model Description file
  44. val innerFmus = type.inner.eAllContents.toList.filter(InnerFMU);
  45. if (innerFmus.size > 1) {
  46. throw new IncorrectAmountOfElementsException("Only one InnerFmu is supported.")
  47. }
  48. if (innerFmus.isEmpty) {
  49. throw new IncorrectAmountOfElementsException("The adaptation does not contain any InnerFMUs.")
  50. }
  51. var ArrayList<SAScalarVariable> SASVs = calcSASVsFromInportsOutports(adapInteralRefName, type.inports, type.outports)
  52. for (fmu : type.inner.eAllContents.toList.filter(InnerFMU)) {
  53. // TODO: Merge this with ModelDescriptionCreator
  54. md = new ModelDescription(fmu.name, fmu.type.name, new File(fmu.path.replace('\"', '')));
  55. fmus.add(fmu.name -> fmu.type.name);
  56. svDefs.putAll(md.svDef);
  57. sVars.putAll(md.sv);
  58. scalarVariables.put(fmu.name, md.sv.values);
  59. mdCreator.name = adapExternalName;
  60. mdCreator.CalcContent(md);
  61. fsa.generateFile("modelDescription.xml", mdCreator.modelDescription);
  62. }
  63. /*
  64. * We might have conflicting scalar variables,
  65. * and therefore the scalar variables cannot be directly mapped to the mdCreator.
  66. * mappedScalarVariables is therefore: HashMap (fmuName -> HashMap(SVName->mappedSV))
  67. */
  68. for (mdSv : scalarVariables.entrySet) {
  69. val LinkedHashMap<String, MappedScalarVariable> msv = newLinkedHashMap();
  70. for (sv : mdSv.value) {
  71. var mappedSv = new MappedScalarVariable(sv);
  72. mappedSv.define = (mappedSv.mappedSv.owner + mappedSv.mappedSv.name).toUpperCase;
  73. msv.put(mappedSv.mappedSv.name, mappedSv);
  74. }
  75. mappedScalarVariables.put(mdSv.key, msv);
  76. }
  77. // Compile the in rules
  78. val inRuleResult = compileInOutRuleBlocks(InputOutputType.Input, adaptation.eAllContents.toIterable.filter(
  79. InRulesBlock).map[x|x as InOutRules], adapClassName, adapInteralRefName, mappedScalarVariables);
  80. genSource += inRuleResult.generatedCpp;
  81. // Compile the out rules
  82. val outRuleResult = compileInOutRuleBlocks(InputOutputType.Output, adaptation.eAllContents.toIterable.
  83. filter(OutRulesBlock).map[x|x as InOutRules], adapClassName, adapInteralRefName, mappedScalarVariables);
  84. genSource += outRuleResult.generatedCpp;
  85. /*
  86. * We now have the explicit input and output values determined in the SA.
  87. * This along with the model descriptions should be enough to create a model description for the SA FMU.
  88. */
  89. // Generate the Control Rules
  90. val crtlRuleResult = compileControlRuleBlock(adaptation.eAllContents.toIterable.filter(ControlRuleBlock),
  91. adapClassName, adapInteralRefName, svDefs);
  92. genSource += crtlRuleResult.generatedCpp;
  93. // Compile the source file includes, namespace and constructor
  94. val String include = '''#include "«adapClassName.toFirstLower».h"''';
  95. val String constructor = compileDeAndConstructor(adapClassName, outRuleResult, inRuleResult, sVars,
  96. fmus.head.key, fmus.head.value, md.guid);
  97. val String getRuleThis = compileGetRuleThis(adapClassName);
  98. // Compile the get functions
  99. val String getFuncs = compileGetFmuValue(adapClassName, sVars, svDefs);
  100. // Compile the set functions
  101. val String setFuncs = compileSetFmuValue(adapClassName, sVars, svDefs);
  102. // Generate the source file for the SA
  103. val source = compileSource(
  104. include,
  105. constructor,
  106. getRuleThis,
  107. getFuncs,
  108. setFuncs,
  109. inRuleResult.generatedCpp,
  110. crtlRuleResult.generatedCpp,
  111. outRuleResult.generatedCpp
  112. );
  113. fsa.generateFile(adapClassName.toFirstLower + ".cpp", source);
  114. // Compile defines for the scalar variables
  115. var genDef = calcDefines(svDefs).join("\n");
  116. // Compile the class definition file for the SA
  117. val String header = compileHeader(adapClassName, inRuleResult, outRuleResult, crtlRuleResult, fmus, sVars,
  118. genDef);
  119. // Generate the header file for the SA
  120. fsa.generateFile(adapClassName.toLowerCase + ".h", header);
  121. }
  122. }
  123. // Compiles the final source file
  124. def String compileSource(String include, String constructor, String getRuleThis, String getFunctions,
  125. String setFunctions, String inFunctions, String controlFunction, String outFunctions) {
  126. return '''
  127. «include»
  128. namespace adaptation
  129. {
  130. «constructor»
  131. «getRuleThis»
  132. «getFunctions»
  133. «setFunctions»
  134. «inFunctions»
  135. «controlFunction»
  136. «outFunctions»
  137. }
  138. '''
  139. }
  140. /*
  141. * Compiles the header file split into two: The first part contains the includes and using namespace definitions and start the ,
  142. * the second part contains the class
  143. */
  144. def String compileHeader(String adaptationName, InOutRulesBlockResult inRulesResult,
  145. InOutRulesBlockResult outRulesResult, RulesBlockResult crtlRulesResult, ArrayList<Pair<String, String>> fmus,
  146. LinkedHashMap<String, ScalarVariable> sVars, String defines) {
  147. return '''
  148. #include "SemanticAdaptation.h"
  149. #include <memory>
  150. #include "Fmu.h"
  151. using namespace std;
  152. using namespace fmi2;
  153. namespace adaptation
  154. {
  155. class «adaptationName» : public SemanticAdaptation<«adaptationName»>{
  156. public:
  157. «adaptationName»(shared_ptr<string> resourceLocation);
  158. virtual ~«adaptationName»();
  159. void setFmiValue(fmi2ValueReference id, int value);
  160. void setFmiValue(fmi2ValueReference id, bool value);
  161. void setFmiValue(fmi2ValueReference id, double value);
  162. int getFmiValueInteger(fmi2ValueReference id);
  163. bool getFmiValueBoolean(fmi2ValueReference id);
  164. double getFmiValueDouble(fmi2ValueReference id);
  165. private:
  166. «adaptationName»* getRuleThis();
  167. /*in rules*/
  168. «inRulesResult.functionSignatures.join("\n")»
  169. /*out rules*/
  170. «outRulesResult.functionSignatures.join("\n")»
  171. «crtlRulesResult.functionSignatures.join("\n")»
  172. «FOR fmu : fmus»
  173. shared_ptr<FmuComponent> «fmu.key»;
  174. «ENDFOR»
  175. «FOR sv : sVars.entrySet»
  176. «Conversions.fmiTypeToCppType(sv.value.type)» «sv.value.name»;
  177. «IF sv.value.causality == SVCausality.input»
  178. bool isSet«sv.value.name»;
  179. «ENDIF»
  180. «ENDFOR»
  181. «FOR v : outRulesResult.globalVars.entrySet»
  182. «Conversions.fmiTypeToCppType(v.value.key)» «v.key»;
  183. «ENDFOR»
  184. «FOR v : inRulesResult.globalVars.entrySet»
  185. «Conversions.fmiTypeToCppType(v.value.key)» «v.key»;
  186. «ENDFOR»
  187. }
  188. }
  189. ''';
  190. }
  191. /*
  192. * Compiles the source file constructor and destructor
  193. */
  194. def String compileDeAndConstructor(String adaptationName, InOutRulesBlockResult outRuleResult,
  195. InOutRulesBlockResult inRuleResult, LinkedHashMap<String, ScalarVariable> sVars, String fmuName,
  196. String fmuTypeName, String guid) {
  197. return '''
  198. «adaptationName»::«adaptationName»(shared_ptr<string> resourceLocation) : SemanticAdaptation(createInputRules(),createOutputRules())
  199. {
  200. «FOR v : outRuleResult.globalVars.entrySet»
  201. «(v.key)» = «v.value.value»;
  202. «ENDFOR»
  203. «FOR v : inRuleResult.globalVars.entrySet»
  204. this->«(v.key)» = «v.value.value»;
  205. «ENDFOR»
  206. «FOR v : sVars.entrySet»
  207. «IF v.value.start !== null»
  208. this->«(v.key)» = «v.value.start»;
  209. «ENDIF»
  210. «ENDFOR»
  211. const char* path = Fmu::combinePath(resourceLocation, make_shared<string>("«fmuTypeName».fmu"))->c_str();
  212. auto «fmuName»Fmu = make_shared<fmi2::Fmu>(path);
  213. «fmuName»Fmu->initialize();
  214. this->«fmuName» = «fmuName»Fmu->instantiate("«fmuTypeName»",fmi2CoSimulation, "«guid»", true, true, make_shared<Callback>());
  215. }
  216. «adaptationName»::~«adaptationName»()
  217. {
  218. }
  219. ''';
  220. }
  221. /*
  222. * Compiles the source file function getRuleThis
  223. */
  224. def String compileGetRuleThis(String adaptationName) {
  225. return '''
  226. «adaptationName»* «adaptationName»::getRuleThis()
  227. {
  228. return this;
  229. }
  230. '''
  231. }
  232. /*
  233. * Compiles the source file functions getFmiValue<double, int, string, bool>
  234. */
  235. def String compileGetFmuValue(String adaptationName, LinkedHashMap<String, ScalarVariable> sVars,
  236. LinkedHashMap<String, Pair<String, Integer>> sVarDefs) {
  237. var ArrayList<String> cpp = newArrayList();
  238. var sVarsOrdered = sVars.entrySet.filter[value.causality === SVCausality.output].groupBy[value.type];
  239. for (SVType type : SVType.values) {
  240. val functionSignature = '''«Conversions.fmiTypeToCppType(type)» «adaptationName»::getFmiValue«type.toString»(fmi2ValueReference id)''';
  241. val functionReturn = '''return «Conversions.fmiTypeToCppDefaultValue(type)»''';
  242. if (sVarsOrdered.containsKey(type)) {
  243. cpp.add(
  244. '''
  245. «functionSignature»
  246. {
  247. switch (id)
  248. {
  249. «FOR svInner : sVarsOrdered.get(type)»
  250. case «sVarDefs.get(svInner.value.owner + svInner.value.name).key»:
  251. {
  252. return this->«svInner.key»;
  253. }
  254. «ENDFOR»
  255. default:
  256. {
  257. «functionReturn»;
  258. }
  259. }
  260. }
  261. '''
  262. );
  263. } else {
  264. cpp.add(
  265. '''
  266. «functionSignature»
  267. {
  268. «functionReturn»;
  269. }
  270. '''
  271. );
  272. }
  273. }
  274. return cpp.join("\n");
  275. }
  276. /*
  277. * Compiles the source file functions setFmiValue<double, int, string, bool>*
  278. */
  279. def String compileSetFmuValue(String adaptationName, LinkedHashMap<String, ScalarVariable> sVars,
  280. LinkedHashMap<String, Pair<String, Integer>> sVarDefs) {
  281. var ArrayList<String> cpp = newArrayList();
  282. var sVarsOrdered = sVars.entrySet.filter[value.causality === SVCausality.input].groupBy[value.type];
  283. for (SVType type : SVType.values) {
  284. cpp.add(
  285. '''
  286. void «adaptationName»::setFmiValue(fmi2ValueReference id, «Conversions.fmiTypeToCppType(type)» value)
  287. {
  288. «IF sVarsOrdered.containsKey(type)»
  289. switch (id)
  290. {
  291. «FOR svInner : sVarsOrdered.get(type)»
  292. case «sVarDefs.get(svInner.value.owner + svInner.value.name).key»:
  293. {
  294. this->«svInner.key» = value;
  295. this->isSet«svInner.key» = true;
  296. break;
  297. }
  298. «ENDFOR»
  299. default:
  300. {
  301. }
  302. }
  303. «ENDIF»
  304. }
  305. '''
  306. );
  307. }
  308. return cpp.join("\n");
  309. }
  310. /*
  311. * Compiles the source file function executeInternalControlFlow.
  312. * Calculates necessary information on function signatures necessary for generation of the header file.
  313. */
  314. def RulesBlockResult compileControlRuleBlock(Iterable<ControlRuleBlock> crtlRuleBlocks, String adaptationClassName,
  315. String adaptationName, LinkedHashMap<String, Pair<String, Integer>> svDefs) {
  316. var cpp = "";
  317. val visitor = new ControlConditionSwitch(adaptationClassName, adaptationName, svDefs);
  318. for (crtlRule : crtlRuleBlocks) {
  319. cpp += visitor.doSwitch(crtlRule);
  320. }
  321. return new RulesBlockResult(cpp, visitor.functionSignatures);
  322. }
  323. def String SplitAtSpaceAndRemoveFirst(String content) {
  324. content.substring(content.indexOf(" ") + 1, content.length);
  325. }
  326. /*
  327. * Compiles the source file functions <in/out>_rule_<condition, body, flush>.
  328. * Calculates necessary information on global in/out variables necessary for generation of the header file.
  329. * Calculates necessary information on function signatures necessary for generation of the header file.
  330. */
  331. def InOutRulesBlockResult compileInOutRuleBlocks(InputOutputType ioType, Iterable<InOutRules> rulesBlocks,
  332. String adaptationClassName, String adaptationName,
  333. LinkedHashMap<String, LinkedHashMap<String, MappedScalarVariable>> mSVars) {
  334. val visitor = if (ioType == InputOutputType.Input)
  335. new InRulesConditionSwitch(adaptationClassName, adaptationName, mSVars)
  336. else
  337. new OutRulesConditionSwitch(adaptationClassName, adaptationName, mSVars);
  338. val functionName = "create" + ioType + "Rules()";
  339. var String cpp = "";
  340. val ruleBlock = rulesBlocks.head;
  341. if (ruleBlock !== null) {
  342. cpp += visitor.doSwitch(ruleBlock);
  343. if (!visitor.functionSignatures.empty) {
  344. var ArrayList<String> createRulesFunction = newArrayList();
  345. for (var int i = 0; i < (visitor.functionSignatures.length); i += 3) {
  346. createRulesFunction.add(
  347. '''
  348. list->push_back(
  349. (Rule<«adaptationClassName»>){
  350. &«adaptationClassName»::«visitor.functionSignatures.get(i).SplitAtSpaceAndRemoveFirst»,
  351. &«adaptationClassName»::«visitor.functionSignatures.get(i+1).SplitAtSpaceAndRemoveFirst»
  352. &«adaptationClassName»::«visitor.functionSignatures.get(i+2).SplitAtSpaceAndRemoveFirst»
  353. });
  354. ''');
  355. }
  356. val functionPrefix = '''shared_ptr<list<Rule<«adaptationClassName»>>>''';
  357. visitor.functionSignatures.add(functionPrefix + " " + functionName + ";")
  358. cpp += '''
  359. «functionPrefix» «adaptationClassName»::«functionName»
  360. {
  361. auto list = make_shared<list<Rule<«adaptationClassName»>>>()
  362. «createRulesFunction.join("\n")»
  363. return list;
  364. }
  365. '''
  366. }
  367. }
  368. return new InOutRulesBlockResult(cpp, visitor.functionSignatures, visitor.globalVars);
  369. }
  370. /*
  371. * Calculates defines for accessing scalar variables via value references.
  372. */
  373. def ArrayList<String> calcDefines(LinkedHashMap<String, Pair<String, Integer>> svDefs) {
  374. // Create Defines for the scalar values
  375. var defines = newArrayList();
  376. for (scalar : svDefs.entrySet) {
  377. val definition = scalar.value;
  378. defines.add("#define " + definition.key + " " + definition.value);
  379. }
  380. return defines;
  381. }
  382. def ArrayList<SAScalarVariable> calcSASVsFromInportsOutports(String definePrefix, EList<Port> inports, EList<Port> outports) {
  383. var saSVs = newArrayList();
  384. var int valueReference = 0;
  385. for (inport : inports) {
  386. var saSV = new SAScalarVariable();
  387. saSV.valueReference = valueReference++;
  388. saSV.name = inport.name;
  389. saSV.defineName = (definePrefix + inport.name).toUpperCase
  390. saSVs.add(saSV);
  391. }
  392. for (outport : outports) {
  393. var saSV = new SAScalarVariable();
  394. saSV.valueReference = valueReference++;
  395. saSV.defineName = (definePrefix + outport.name).toUpperCase
  396. saSV.name = outport.name;
  397. saSVs.add(saSV);
  398. }
  399. return saSVs;
  400. }
  401. }