| /** |
| ******************************************************************************** |
| * Copyright (c) 2020-2021 Robert Bosch GmbH. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| * ******************************************************************************* |
| */ |
| |
| package org.eclipse.app4mc.slg.ros2.transformers.sw; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.eclipse.app4mc.amalthea.model.ActivityGraph; |
| import org.eclipse.app4mc.amalthea.model.ActivityGraphItem; |
| import org.eclipse.app4mc.amalthea.model.Amalthea; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaFactory; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaIndex; |
| import org.eclipse.app4mc.amalthea.model.BooleanObject; |
| import org.eclipse.app4mc.amalthea.model.Channel; |
| import org.eclipse.app4mc.amalthea.model.ChannelReceive; |
| import org.eclipse.app4mc.amalthea.model.ChannelSend; |
| import org.eclipse.app4mc.amalthea.model.ConditionDisjunction; |
| import org.eclipse.app4mc.amalthea.model.ConditionDisjunctionEntry; |
| import org.eclipse.app4mc.amalthea.model.DataSize; |
| import org.eclipse.app4mc.amalthea.model.ILocalModeValueSource; |
| import org.eclipse.app4mc.amalthea.model.InterProcessStimulus; |
| import org.eclipse.app4mc.amalthea.model.InterProcessTrigger; |
| import org.eclipse.app4mc.amalthea.model.Label; |
| import org.eclipse.app4mc.amalthea.model.LocalModeCondition; |
| import org.eclipse.app4mc.amalthea.model.LocalModeLabel; |
| import org.eclipse.app4mc.amalthea.model.ModeLiteral; |
| import org.eclipse.app4mc.amalthea.model.ModeLiteralConst; |
| import org.eclipse.app4mc.amalthea.model.Runnable; |
| import org.eclipse.app4mc.amalthea.model.Switch; |
| import org.eclipse.app4mc.amalthea.model.SwitchEntry; |
| import org.eclipse.app4mc.amalthea.model.Value; |
| import org.eclipse.app4mc.amalthea.model.emf.AmaltheaEObjectImpl; |
| import org.eclipse.app4mc.slg.commons.m2t.CustomObjectsStore; |
| import org.eclipse.app4mc.slg.commons.m2t.transformers.SLGTranslationUnit; |
| import org.eclipse.app4mc.slg.commons.m2t.transformers.sw.LabelTransformer; |
| import org.eclipse.app4mc.slg.commons.m2t.transformers.sw.RunnableTransformer; |
| import org.eclipse.app4mc.slg.config.CodehookType; |
| import org.eclipse.app4mc.slg.config.ConfigModel; |
| import org.eclipse.app4mc.slg.config.util.ConfigModelUtils; |
| import org.eclipse.app4mc.slg.ros2.generators.RosLabelGenerator; |
| import org.eclipse.app4mc.slg.ros2.generators.RosRunnableGenerator; |
| import org.eclipse.app4mc.slg.ros2.transformers.utils.Utils; |
| import org.eclipse.app4mc.transformation.TransformationConstants; |
| import org.eclipse.app4mc.transformation.util.OutputBuffer; |
| import org.eclipse.app4mc.util.sessionlog.SessionLogger; |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| |
| import com.google.inject.Inject; |
| |
| public class RosRunnableTransformer extends RunnableTransformer { |
| |
| public static final String LIB_NAME = "RUNNABLES_LIB"; |
| public static final String BASE_PATH = "ROS2_SLG/synthetic_gen"; |
| public static final String MODULE_NAME = "runnables"; |
| public static final String MODULE_PATH = BASE_PATH + "/" + MODULE_NAME; |
| public static final String MAKEFILE_PATH = MODULE_PATH + "/CMakeLists.txt"; |
| |
| @Inject private OutputBuffer outputBuffer; |
| @Inject private RosActivityGraphItemTransformer activityGraphItemTransformer; |
| @Inject private RosRunnableCache rosRunnableCache; |
| @Inject private RosLabelTransformer rosLabelTransformer; |
| @Inject private CustomObjectsStore customObjsStore; // model |
| @Inject private Properties properties; |
| @Inject private SessionLogger logger; |
| |
| |
| @Override |
| protected void genFiles(SLGTranslationUnit tu, Runnable runnable) { |
| |
| |
| if (isSrcFileEmpty(tu)) { // all stuff only required once regardless of runnable instance |
| srcAppend(tu, "#include \"" + getIncFile(tu) + "\"\n"); |
| } |
| |
| customObjsStore.indexData(tu.getCall(), runnable); |
| |
| |
| boolean extOverwrite = false; // enabling codehook overwriting |
| final List<String> callsOverwrite = new ArrayList<>(); //overwrite codehook fct |
| final HashSet<String> includes = new LinkedHashSet<>(); |
| final List<String> calls = new ArrayList<>(); |
| |
| final Map<SwitchEntry,List<String>> switchBasedCalls = new LinkedHashMap<SwitchEntry, List<String>>(); |
| |
| final Map<SwitchEntry,List<String>> switchBasedCallsOverwrite = new LinkedHashMap<SwitchEntry, List<String>>(); //overwrite codehook fct |
| |
| final List<SwitchEntry> switchEntries=new ArrayList<SwitchEntry>(); |
| |
| final Map<SwitchEntry, Boolean> switchEntryOverwriteActivityGraph=new LinkedHashMap<SwitchEntry, Boolean>(); |
| |
| final EList<ActivityGraphItem> defaultActivityGraphItems=new BasicEList<ActivityGraphItem>(); |
| |
| |
| boolean measurePerformance = false; |
| if (runnable != null && runnable.getCustomProperties().get("measure_performance") instanceof BooleanObject) { |
| measurePerformance = ((BooleanObject) runnable.getCustomProperties().get("measure_performance")).isValue(); |
| } |
| |
| if (measurePerformance) { |
| includes.add("aml.h"); |
| } |
| //----------------------------------/* fetch headers names*/-------------------------------------- |
| // to fetch the headers names we use the getHeaderFilesDirectories function and we go through every directory with the for loop |
| if (isIncFileEmpty(tu)) { |
| incAppend(tu, "#pragma once \n\n"); |
| final ConfigModel configModel = customObjsStore.<ConfigModel>getInstance(ConfigModel.class); |
| boolean enableExtCode = Boolean.parseBoolean(properties.getProperty("enableExternalCode")); |
| if(enableExtCode) { |
| // input property to test if we need to include the external code headerfiles |
| String workingDirectory=customObjsStore.getData(TransformationConstants.WORKING_DIRECTORY); |
| for (String hDir : ConfigModelUtils.getHeaderFilesDirectories(configModel,CodehookType.RUNNABLE,workingDirectory,logger)) |
| { |
| final File folder = new File(hDir.trim()); // fetching all the names in the headerfile directory. |
| String names = ConfigModelUtils.getHeaderFilesIncludeMultiString(folder,logger); |
| incAppend(tu,names); |
| } |
| } |
| } |
| //------------------------------------------------------------------------+ |
| |
| // Compute characteristic values of runnable |
| |
| final List<String> params = new ArrayList<>(); |
| final List<String> nodeParams = new ArrayList<>(); |
| final List<String> publishers = new ArrayList<>(); |
| final List<String> clientDeclarations = new ArrayList<>(); |
| final List<String> clientInits = new ArrayList<>(); |
| |
| |
| if (runnable != null && runnable.getActivityGraph() != null) { |
| |
| EList<ActivityGraphItem> activityGraphItems = runnable.getActivityGraph().getItems(); |
| |
| for (ActivityGraphItem activityGraphItem : activityGraphItems) { |
| |
| if(activityGraphItem instanceof Switch) { |
| |
| for(SwitchEntry entry: ((Switch)activityGraphItem).getEntries()) { |
| switchBasedCalls.put(entry, new ArrayList<String>()); |
| switchBasedCallsOverwrite.put(entry, new ArrayList<String>()); |
| switchEntries.add(entry); |
| } |
| }else { |
| defaultActivityGraphItems.add(activityGraphItem); |
| } |
| |
| } |
| |
| extOverwrite = extracted(tu, extOverwrite, callsOverwrite, includes, calls, params, nodeParams, |
| publishers, clientDeclarations, clientInits, runnable.getActivityGraph(), defaultActivityGraphItems); |
| |
| |
| for(SwitchEntry switchEntry: switchEntries) { |
| |
| Boolean extOverwriteActivityGraph_switch = extracted(tu, extOverwrite, switchBasedCallsOverwrite.get(switchEntry), includes, switchBasedCalls.get(switchEntry), params, nodeParams, |
| publishers, clientDeclarations, clientInits, switchEntry, switchEntry.getItems()); |
| |
| switchEntryOverwriteActivityGraph.put(switchEntry, extOverwriteActivityGraph_switch); |
| |
| } |
| |
| |
| } |
| |
| |
| |
| // |
| |
| |
| |
| |
| String nodeParam = String.join(",", nodeParams); |
| String param = String.join(",", params); |
| |
| // store characteristic values in runnable cache |
| rosRunnableCache.storeValues(tu, runnable, param, nodeParam, publishers, clientDeclarations, clientInits); |
| |
| String fullCall = "run_" + runnable.getName() + "(" + param + ")"; |
| |
| String fullCall_with_context = "run_" + runnable.getName()+"_Context" + "(" + param + ","+getParamNames(runnable.getLocalLabels())+" )"; |
| |
| // write header |
| |
| incAppend(tu, "\n//Runnable " + runnable.getName() + "----\n"); |
| toH(tu, includes, fullCall,param); |
| |
| |
| |
| //------------------------ write body with overwrite codehook function |
| |
| if (extOverwrite) { |
| String call_overwrite = "run_" + runnable.getName(); |
| |
| srcAppend(tu, "void " + call_overwrite + "("+ param + ")" + "{\n" + "\n"); |
| |
| for (String call : callsOverwrite) { |
| srcAppend(tu, call + ";" + "\n"); |
| } |
| srcAppend(tu, "\n" + "}" + "\n"); |
| |
| if(runnable.getLocalLabels().size()>0) { |
| |
| //additional method for switch |
| srcAppend(tu, "void " + call_overwrite+"_Context"+ "("+ param+ ","+getParamNames(runnable.getLocalLabels())+" ){\n" + "\n"); |
| |
| for(SwitchEntry switchEntry: switchEntries) { |
| |
| if(switchEntryOverwriteActivityGraph.get(switchEntry)) { |
| |
| srcAppend(tu, getConditionString(switchEntry) + "\n"); |
| |
| for (String call : switchBasedCallsOverwrite.get(switchEntry)) { |
| srcAppend(tu, call + ";" + "\n"); |
| } |
| |
| }else { |
| for (String call : switchBasedCalls.get(switchEntry)) { |
| srcAppend(tu, call + ";" + "\n"); |
| } |
| } |
| |
| } |
| srcAppend(tu, "\n" + "}" + "\n"); |
| } |
| |
| } |
| // ------------------------ |
| else { |
| // write body |
| srcAppend(tu, "\n//Runnable " + runnable.getName() + "----\n"); |
| toCustomCpp(tu, runnable.getName(), fullCall, fullCall_with_context,calls, measurePerformance,switchEntries,switchBasedCalls,runnable); |
| } |
| |
| |
| } |
| |
| private String getConditionString(SwitchEntry switchEntry) { |
| |
| ConditionDisjunction condition = switchEntry.getCondition(); |
| |
| if(condition !=null) { |
| |
| EList<ConditionDisjunctionEntry> entries = condition.getEntries(); |
| |
| for (ConditionDisjunctionEntry conditionDisjunctionEntry : entries) { |
| |
| if(conditionDisjunctionEntry instanceof LocalModeCondition) { |
| LocalModeLabel localLabel = ((LocalModeCondition) conditionDisjunctionEntry).getLabel(); |
| |
| if(localLabel !=null) { |
| |
| String localVariableName = localLabel.getName(); |
| |
| |
| ILocalModeValueSource valueSource = ((LocalModeCondition) conditionDisjunctionEntry).getValueSource(); |
| |
| if(valueSource !=null && valueSource instanceof ModeLiteralConst) { |
| ModeLiteral value = ((ModeLiteralConst)valueSource).getValue(); |
| |
| String caseName= value.getName(); |
| |
| return "strcmp("+localVariableName+",\""+caseName+"\")==0"; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| |
| return "false"; |
| } |
| |
| private boolean extracted(SLGTranslationUnit tu, boolean extOverwrite, |
| final List<String> callsOverwrite, final HashSet<String> includes, final List<String> calls, |
| final List<String> params, final List<String> nodeParams, final List<String> publishers, |
| final List<String> clientDeclarations, final List<String> clientInits, final EObject elementContainingCustomProps, final EList<ActivityGraphItem> activityGraphItems ) { |
| |
| extOverwrite = processCustomProperties(extOverwrite, calls, callsOverwrite,elementContainingCustomProps); // for codehooks implementation |
| |
| for (ActivityGraphItem item : activityGraphItems) { |
| |
| final SLGTranslationUnit graphItemTU = activityGraphItemTransformer.transform(item); |
| String graphItemIncFile = getIncFile(graphItemTU); |
| if (graphItemIncFile != null && !graphItemIncFile.isEmpty() && !getIncFile(tu).equals(graphItemIncFile)) { //includes problem is in here. It should be if .. is empty. |
| |
| |
| if (!(item instanceof ChannelReceive)) // temporary |
| { |
| includes.add(graphItemIncFile); |
| System.out.println(graphItemIncFile); |
| } |
| } |
| |
| // check if item is publisher |
| |
| if (item instanceof ChannelSend) { |
| ChannelSend cs = (ChannelSend) item; |
| Channel data = cs.getData(); |
| |
| publishers.add(data.getName() + "_publisher"); |
| nodeParams.add(data.getName() + "_publisher"); |
| params.add("rclcpp::Publisher<std_msgs::msg::String>::SharedPtr& " + data.getName() + "_publisher"); |
| |
| } |
| |
| if (item instanceof InterProcessTrigger) { |
| InterProcessTrigger trigger = (InterProcessTrigger) item; |
| InterProcessStimulus stimulus = trigger.getStimulus(); |
| |
| String stimName = stimulus.getName(); |
| String idlName = Utils.toIdlCompliantName(stimName + "_service"); |
| |
| includes.add(stimName + "_service/srv/" + stimName + "_service" + ".hpp"); |
| clientDeclarations.add("rclcpp::Client<" + stimName + "_service::srv::" + idlName + ">::SharedPtr " + stimName + "_client"); |
| clientInits.add(stimName + "_client = this->create_client<" + stimName + "_service::srv::" + idlName + ">" + "(\"" + stimName + "_service\")"); |
| nodeParams.add(stimName + "_client"); |
| params.add("rclcpp::Client<" + stimName + "_service::srv::" + idlName + ">::SharedPtr& " + stimName + "_client"); |
| } |
| |
| // check if item is subscriber. |
| if (item instanceof ChannelReceive) { |
| |
| |
| ChannelReceive cr = (ChannelReceive) item; |
| Channel data = cr.getData(); |
| String subLabelName = data.getName()+ "_sub_label"; |
| |
| // check for the second channel receive |
| //AmaltheaIndex.getElements(context, name, targetClass) |
| // AmaltheaIndex.getElements((@NonNull Notifier) customObjsStore, subLabelName, customObjsStore.getInstance(RosLabelTransformer.class) ); |
| |
| |
| Label temp = AmaltheaFactory.eINSTANCE.createLabel(); |
| temp.setName(subLabelName); |
| |
| DataSize value = data.getSize(); |
| System.out.print(temp.getName()+": "); |
| System.out.print(value +"\n"); |
| //temp.setSize(value); // transformation error |
| //Without injection: |
| //LabelTransformer labelTransformer = new LabelTransformer(); |
| |
| // with injection |
| rosLabelTransformer.transform(temp); |
| |
| calls.add("//ChannelReceiveTry"); // temporary |
| calls.add("read_"+temp.getName()+"("+value.getNumberBytes()+")"); |
| |
| |
| } |
| |
| |
| final String call = graphItemTU.getCall(); |
| if (call != null && !call.isEmpty()) { |
| calls.add(call); |
| } |
| } |
| |
| return extOverwrite; |
| } |
| |
| protected void toH(SLGTranslationUnit tu, final HashSet<String> includes, final String fullCall, String param) { |
| for (String include : includes) { |
| incAppend(tu, "#include \"" + include + "\"\n"); |
| } |
| |
| if(!includes.contains("labels.h")) { |
| incAppend(tu, "#include \"labels.h\"\n"); |
| } |
| incAppend(tu, "void " + fullCall + ";\n"); |
| |
| Runnable data = customObjsStore.getData(tu.getCall()); |
| EList<LocalModeLabel> localLabels = ((Runnable)data).getLocalLabels(); |
| |
| |
| if(localLabels.size()>0) |
| incAppend(tu, "void " + "run_" + data.getName() +"_Context" + "("+param+","+getParamNames(localLabels)+" );\n"); |
| } |
| |
| private String getParamNames(EList<LocalModeLabel> localLabels) { |
| |
| List<String> ls=new ArrayList<String>(); |
| |
| for (LocalModeLabel localModeLabel : localLabels) { |
| ls.add("char* "+localModeLabel.getName()); |
| } |
| |
| return String.join(",", ls); |
| } |
| |
| protected void toCpp(SLGTranslationUnit tu, final String runnableName, final String fullCall, final List<String> calls, boolean measurePerformance) { |
| srcAppend(tu, "void " + fullCall + "{\n"); |
| if (measurePerformance) { |
| srcAppend(tu, |
| "uint64_t event_list[] = {0x11, 0x13, 0x17}; //CPU CYCLES, MEM ACCESS, L2 Cache Refill\n" |
| + "int total_events = sizeof(event_list)/sizeof(event_list[0]);\n" |
| + "int fd = instrument_start(0,event_list, total_events);\n"); |
| } |
| |
| for (String call : calls) { |
| srcAppend(tu, "\t" + call + ";\n"); |
| } |
| |
| if (measurePerformance) { |
| srcAppend(tu, "instrument_stop(fd, \"" + runnableName + ".log\");\n"); |
| } |
| srcAppend(tu, "}\n\n"); |
| } |
| |
| |
| protected void toCustomCpp(SLGTranslationUnit tu, final String runnableName, final String fullCall, final String fullCall_with_context, final List<String> calls, boolean measurePerformance, List<SwitchEntry> switchEntries, Map<SwitchEntry, List<String>> switchBasedCalls, Runnable runnable) { |
| srcAppend(tu, "void " + fullCall + "{\n"); |
| if (measurePerformance) { |
| srcAppend(tu, |
| "uint64_t event_list[] = {0x11, 0x13, 0x17}; //CPU CYCLES, MEM ACCESS, L2 Cache Refill\n" |
| + "int total_events = sizeof(event_list)/sizeof(event_list[0]);\n" |
| + "int fd = instrument_start(0,event_list, total_events);\n"); |
| } |
| |
| for (String call : calls) { |
| srcAppend(tu, "\t" + call + ";\n"); |
| } |
| |
| if (measurePerformance) { |
| srcAppend(tu, "instrument_stop(fd, \"" + runnableName + ".log\");\n"); |
| } |
| srcAppend(tu, "}\n\n"); |
| |
| |
| //Adding additional method |
| |
| if(runnable.getLocalLabels().size()>0) { |
| |
| srcAppend(tu, "void " + fullCall_with_context + "{\n"+ "\n"); |
| if (measurePerformance) { |
| srcAppend(tu, |
| "uint64_t event_list[] = {0x11, 0x13, 0x17}; //CPU CYCLES, MEM ACCESS, L2 Cache Refill\n" |
| + "int total_events = sizeof(event_list)/sizeof(event_list[0]);\n" |
| + "int fd = instrument_start(0,event_list, total_events);\n"); |
| } |
| |
| for(SwitchEntry switchEntry: switchEntries) { |
| |
| { |
| srcAppend(tu, "if("+getConditionString(switchEntry)+"){" + "\n"); |
| |
| for (String call : switchBasedCalls.get(switchEntry)) { |
| srcAppend(tu, call + ";" + "\n"); |
| } |
| srcAppend(tu, "\n" + "}" + "\n"); |
| } |
| |
| } |
| |
| if (measurePerformance) { |
| srcAppend(tu, "instrument_stop(fd, \"" + runnableName + "_context.log\");\n"); |
| } |
| srcAppend(tu, "}\n\n"); |
| } |
| |
| |
| |
| |
| |
| } |
| |
| |
| |
| @Override |
| public boolean createCMake() { |
| return outputBuffer.appendTo( |
| "OTHER", MAKEFILE_PATH, RosRunnableGenerator.toCMake(LIB_NAME, getSrcFiles())); |
| } |
| |
| @Override |
| protected SLGTranslationUnit createTranslationUnit(Runnable runnable) { |
| if ((runnable == null)) { |
| return new SLGTranslationUnit("UNSPECIFIED RUNNABLE"); |
| } else { |
| String basePath = BASE_PATH; |
| String moduleName = MODULE_NAME; |
| String call = "run_" + runnable.getName() + "()"; |
| return new SLGTranslationUnit(basePath, moduleName, call); |
| } |
| } |
| |
| |
| |
| } |