blob: 542f0112dd4f4e04c6bba091607466b104978ba6 [file] [log] [blame]
/**
********************************************************************************
* 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);
}
}
}