Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cpp ROS2 federate impl #1736

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions org.lflang/src/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,20 @@ public static boolean isEnclave(Instantiation node) {
return getEnclaveAttribute(node) != null;
}

/**
* Return the {@code @federate} attribute annotated on the given node.
*
* <p>Returns null if there is no such attribute.
*/
public static Attribute getFederateAttribute(Instantiation node) {
return findAttributeByName(node, "federate");
}

/**
* Return true if the specified instance has an {@code @federate} attribute. TODO: this needs some
* other name bec of c target federate
*/
public static boolean isFederate(Instantiation node) {
return getFederateAttribute(node) != null;
}
}
37 changes: 24 additions & 13 deletions org.lflang/src/org/lflang/generator/cpp/CppRos2Generator.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.lflang.generator.cpp

import org.lflang.AttributeUtils
import org.lflang.generator.LFGeneratorContext
import org.lflang.reactor
import org.lflang.util.FileUtil
import java.nio.file.Path

Expand All @@ -9,24 +11,33 @@ class CppRos2Generator(generator: CppGenerator) : CppPlatformGenerator(generator

override val srcGenPath: Path = generator.fileConfig.srcGenPath.resolve("src")
private val packagePath: Path = generator.fileConfig.srcGenPath
private val nodeGenerator = CppRos2NodeGenerator(mainReactor, targetConfig, fileConfig);
private val packageGenerator = CppRos2PackageGenerator(generator, nodeGenerator.nodeName)
private val nodeGenerators : MutableList<CppRos2NodeGenerator> = mutableListOf(CppRos2NodeGenerator(mainReactor, targetConfig, fileConfig))
private val packageGenerator = CppRos2PackageGenerator(generator)

override fun generatePlatformFiles() {
FileUtil.writeToFile(
nodeGenerator.generateHeader(),
packagePath.resolve("include").resolve("${nodeGenerator.nodeName}.hh"),
true
)
FileUtil.writeToFile(
nodeGenerator.generateSource(),
packagePath.resolve("src").resolve("${nodeGenerator.nodeName}.cc"),
true
)
mainReactor.instantiations.forEach{
if (AttributeUtils.isFederate(it)) {
nodeGenerators.add(
CppRos2NodeGenerator(it.reactor, targetConfig, fileConfig))
}
}

for (nodeGen in nodeGenerators) {
FileUtil.writeToFile(
nodeGen.generateHeader(),
packagePath.resolve("include").resolve("${nodeGen.nodeName}.hh"),
true
)
FileUtil.writeToFile(
nodeGen.generateSource(),
packagePath.resolve("src").resolve("${nodeGen.nodeName}.cc"),
true
)
}

FileUtil.writeToFile(packageGenerator.generatePackageXml(), packagePath.resolve("package.xml"), true)
FileUtil.writeToFile(
packageGenerator.generatePackageCmake(generator.cppSources),
packageGenerator.generatePackageCmake(generator.cppSources, nodeGenerators.map { it.nodeName }),
packagePath.resolve("CMakeLists.txt"),
true
)
Expand Down
20 changes: 10 additions & 10 deletions org.lflang/src/org/lflang/generator/cpp/CppRos2NodeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import org.lflang.TargetConfig
import org.lflang.lf.Reactor
import org.lflang.toUnixString

/** A C++ code generator for creating a ROS2 node from a main reactor definition */
/** A C++ code generator for creating a ROS2 node from reactor definition */
class CppRos2NodeGenerator(
private val main: Reactor,
private val reactor: Reactor,
private val targetConfig: TargetConfig,
private val fileConfig: CppFileConfig
) {

val nodeName = "${fileConfig.name}Node"
val nodeName = "${reactor.name}Node"

fun generateHeader(): String {
return """
Expand All @@ -20,17 +20,17 @@ class CppRos2NodeGenerator(
|#include <rclcpp/rclcpp.hpp>
|#include "reactor-cpp/reactor-cpp.hh"
|
|#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}"
|#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}"
|
|rclcpp::Node* lf_node{nullptr};
|
|class $nodeName : public rclcpp::Node {
|private:
| std::unique_ptr<reactor::Environment> lf_env;
| std::unique_ptr<${main.name}> lf_main_reactor;
| std::unique_ptr<${reactor.name}> lf_reactor;
|
| // main thread of the LF execution
| std::thread lf_main_thread;
| // thread of the LF execution
| std::thread lf_thread;
| // an additional thread that we use for waiting for LF termination
| // and then shutting down the LF node
| std::thread lf_shutdown_thread;
Expand All @@ -51,7 +51,7 @@ class CppRos2NodeGenerator(
|#include <thread>
|
|void $nodeName::wait_for_lf_shutdown() {
| lf_main_thread.join();
| lf_thread.join();
| this->get_node_options().context()->shutdown("LF execution terminated");
|}
|
Expand All @@ -68,13 +68,13 @@ class CppRos2NodeGenerator(
| lf_env = std::make_unique<reactor::Environment>(workers, fast, lf_timeout);
|
| // instantiate the main reactor
| lf_main_reactor = std::make_unique<${main.name}> ("${main.name}", lf_env.get(), ${main.name}::Parameters{});
| lf_reactor = std::make_unique<${reactor.name}> ("${reactor.name}", lf_env.get(), ${reactor.name}::Parameters{});
|
| // assemble reactor program
| lf_env->assemble();
|
| // start execution
| lf_main_thread = lf_env->startup();
| lf_thread = lf_env->startup();
| lf_shutdown_thread = std::thread([this] { wait_for_lf_shutdown(); });
|}
|
Expand Down
53 changes: 28 additions & 25 deletions org.lflang/src/org/lflang/generator/cpp/CppRos2PackageGenerator.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.lflang.generator.cpp

import org.lflang.generator.PrependOperator
import org.lflang.joinLn
import org.lflang.joinWithLn
import org.lflang.toUnixString
import java.nio.file.Path

/** A C++ code generator for creating the required files for defining a ROS2 package. */
class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: String) {
class CppRos2PackageGenerator(generator: CppGenerator) {
private val fileConfig = generator.fileConfig
private val targetConfig = generator.targetConfig
val reactorCppSuffix = targetConfig.runtimeVersion ?: "default"
Expand Down Expand Up @@ -45,7 +46,7 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
""".trimMargin()
}

fun generatePackageCmake(sources: List<Path>): String {
fun generatePackageCmake(sources: List<Path>, nodeNames: List<String>): String {
// Resolve path to the cmake include files if any was provided
val includeFiles = targetConfig.cmakeIncludes?.map { fileConfig.srcPath.resolve(it).toUnixString() }

Expand All @@ -68,31 +69,33 @@ class CppRos2PackageGenerator(generator: CppGenerator, private val nodeName: Str
|# Invoke find_package() for all build and buildtool dependencies.
|find_package(ament_cmake_auto REQUIRED)
|ament_auto_find_build_dependencies()
${ nodeNames.map {
"""
|ament_auto_add_library($it SHARED
| src/$it.cc
${" | "..sources.joinWithLn { "src/$it" }}
|)
|ament_target_dependencies($it ${dependencies.joinToString(" ")})
|target_include_directories($it PUBLIC
| "$S{LF_SRC_PKG_PATH}/src"
| "$S{PROJECT_SOURCE_DIR}/src/"
| "$S{PROJECT_SOURCE_DIR}/src/__include__"
|)
|target_link_libraries($it $reactorCppName)
|
|rclcpp_components_register_node($it
| PLUGIN $it
| EXECUTABLE ${it}_exe
|)
|if(MSVC)
| target_compile_options($it PRIVATE /W4)
|else()
| target_compile_options($it PRIVATE -Wall -Wextra -pedantic)
|endif()
"""
}.joinLn()}
|
|set(LF_MAIN_TARGET ${fileConfig.name})
|
|ament_auto_add_library($S{LF_MAIN_TARGET} SHARED
| src/$nodeName.cc
${" | "..sources.joinWithLn { "src/$it" }}
|)
|ament_target_dependencies($S{LF_MAIN_TARGET} ${dependencies.joinToString(" ")})
|target_include_directories($S{LF_MAIN_TARGET} PUBLIC
| "$S{LF_SRC_PKG_PATH}/src"
| "$S{PROJECT_SOURCE_DIR}/src/"
| "$S{PROJECT_SOURCE_DIR}/src/__include__"
|)
|target_link_libraries($S{LF_MAIN_TARGET} $reactorCppName)
|
|rclcpp_components_register_node($S{LF_MAIN_TARGET}
| PLUGIN "$nodeName"
| EXECUTABLE $S{LF_MAIN_TARGET}_exe
|)
|
|if(MSVC)
| target_compile_options($S{LF_MAIN_TARGET} PRIVATE /W4)
|else()
| target_compile_options($S{LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
|endif()
|
|ament_auto_package()
|
Expand Down
3 changes: 3 additions & 0 deletions org.lflang/src/org/lflang/validation/AttributeSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ enum AttrParamType {
List.of(new AttrParamSpec(EACH_ATTR, AttrParamType.BOOLEAN, true))
));

ATTRIBUTE_SPECS_BY_NAME.put("federate",
new AttributeSpec(List.of(new AttrParamSpec(EACH_ATTR, AttrParamType.BOOLEAN, true))));

// attributes that are used internally only by the federated code generation
ATTRIBUTE_SPECS_BY_NAME.put("_unordered", new AttributeSpec(null));
ATTRIBUTE_SPECS_BY_NAME.put("_fed_config", new AttributeSpec(
Expand Down