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

Enable generation of ROS2 nodes directly from C++ code #984

Merged
merged 51 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
1c8782c
add a ros2 target property
cmnrd Jan 28, 2022
1c914da
rename classes needed for standalone code generation
cmnrd Jan 28, 2022
fa5fbbb
modularize the C++ build process
cmnrd Jan 28, 2022
a7d886d
cleaner interface between CppGenerator and the platform specific classes
cmnrd Jan 28, 2022
5a3b1c8
add stub for ROS2 compilation
cmnrd Jan 28, 2022
1358dff
generate ros2 package description
cmnrd Jan 28, 2022
ce7911a
generate ros2 specific cmake file
cmnrd Jan 28, 2022
f97cd1c
fix typo; add email
cmnrd Jan 28, 2022
e84256a
use a more sophisticated build structure
cmnrd Jan 28, 2022
321f461
Merge branch 'master' into cpp-ros2-platform
cmnrd Feb 10, 2022
7679227
avoid using the fsa for writing files
cmnrd Feb 10, 2022
8431932
Merge branch 'master' into cpp-ros2-platform
cmnrd Feb 14, 2022
2253b3d
destination of copyFileFromClassPath should be given by a Path object
cmnrd Feb 14, 2022
e1d8329
remove unused method
cmnrd Feb 14, 2022
67cdfb6
delete another unused method
cmnrd Feb 14, 2022
37e20ec
remove more unused methods and fix a few warnings
cmnrd Feb 14, 2022
f7a3669
Merge branch 'file-config-cleanup' into cpp-ros2-platform
cmnrd Feb 14, 2022
aafc117
fix compile errors
cmnrd Feb 14, 2022
c1fdc89
fix more bugs after merge
cmnrd Feb 14, 2022
5eb36d7
generate src files in 'src' when compiling for ROS2
cmnrd Feb 14, 2022
1a580ee
generate a complete ros2 node
cmnrd Feb 14, 2022
4e73d4c
various fixes, Minimal test compiles now
cmnrd Feb 14, 2022
accbff4
Merge branch 'master' into cpp-ros2-platform
cmnrd Feb 15, 2022
cc60770
Merge branch 'fileconfig-cleanup' into cpp-ros2-platform
cmnrd Feb 28, 2022
5c2139d
fix ros2 build
cmnrd Feb 28, 2022
c2c3982
use a single package generator class
cmnrd Feb 28, 2022
f54f7be
properly shutdown ros2 nodes after LF execution terminates
cmnrd Mar 1, 2022
42b0fac
don't change the Minimal test
cmnrd Mar 1, 2022
416fb12
generate a script for executing a compiled node
cmnrd Mar 1, 2022
456bd27
invoke colcon build
cmnrd Mar 1, 2022
898e85b
Run all C++ Tests also with ROS2
cmnrd Mar 1, 2022
ca8cff8
add ci workflow for ros2 tests
cmnrd Mar 1, 2022
148fea2
fix branch name
cmnrd Mar 1, 2022
048f85c
fix xi config
cmnrd Mar 1, 2022
02bbc9c
fix test name
cmnrd Mar 1, 2022
d1370df
bugfix
cmnrd Mar 1, 2022
6208686
source the ROS2 setup script
cmnrd Mar 1, 2022
9a41527
try sourcing and running in the same run
cmnrd Mar 1, 2022
798bef5
abort if no ROS2 environment was found
cmnrd Mar 1, 2022
763c7a8
relatevize setup script path
cmnrd Mar 1, 2022
772d167
implement timeout for ros2 code generation
cmnrd Mar 1, 2022
3cd3b1c
ensure that there is a valid config object before adding a property
cmnrd Mar 2, 2022
f583e4d
Merge branch 'master' into cpp-ros2-platform
cmnrd Mar 2, 2022
5727fb0
fix include path for headers located in src
cmnrd Mar 2, 2022
54b3000
support cmake include
cmnrd Mar 2, 2022
ad261e7
add a global variable giving C++ reaction code access to the node
cmnrd Mar 2, 2022
4e42a1c
add a minimal ros example
cmnrd Mar 2, 2022
d959b80
don't test examples with ROS2 and fix warnings in CCppTest
cmnrd Mar 2, 2022
2cd8784
Merge branch 'master' into cpp-ros2-platform
cmnrd Mar 7, 2022
1d27e09
fix merge conflict
cmnrd Mar 7, 2022
a064d35
fix typos and add brief class documentation
cmnrd Mar 7, 2022
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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ jobs:
cpp-tests:
uses: lf-lang/lingua-franca/.github/workflows/cpp-tests.yml@master

# Run the C++ integration tests on ROS2.
cpp-ros2-tests:
uses: lf-lang/lingua-franca/.github/workflows/cpp-ros2-tests.yml@cpp-ros2-platform

# Run the Python integration tests.
py-tests:
uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/cpp-ros2-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: C++ tests

on:
workflow_call:
inputs:
compiler-ref:
required: false
type: string
runtime-ref:
required: false
type: string
jobs:
run:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v1.4.3
with:
java-version: 11
- name: Check out lingual-franca repository
uses: actions/checkout@v2
with:
repository: lf-lang/lingua-franca
submodules: true
ref: ${{ inputs.compiler-ref }}
- name: Check out specific ref of reactor-CPO
uses: actions/checkout@v2
with:
repository: lf-lang/reactor-cpp
path: org.lflang/src/lib/cpp/reactor-cpp
ref: ${{ inputs.runtime-ref }}
if: ${{ inputs.runtime-ref }}
- name: Setup ROS2
uses: ros-tooling/setup-ros@0.2.2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be very useful to test the C target's ROS 2 support as well.
Is it okay to call this just ros2-tests, or should we extract the setup here and share it among two separate workflows?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do something similar as in the benchmark tests, by adding a target parameter and making the jobs conditional. Feel free to add the C tests (but probably in another PR).

with:
required-ros-distributions: galactic
- name: Run C++ tests;
run: |
source /opt/ros/galactic/setup.bash
./gradlew test --tests org.lflang.tests.runtime.CppRos2Test.*
- name: Report to CodeCov
uses: codecov/codecov-action@v2.1.0
with:
file: org.lflang.tests/build/reports/xml/jacoco
fail_ci_if_error: false
verbose: true
if: ${{ !inputs.runtime-ref }} # i.e., if this is part of the main repo's CI
146 changes: 0 additions & 146 deletions example/Cpp/src/ROS/BasicROS.lf

This file was deleted.

96 changes: 0 additions & 96 deletions example/Cpp/src/ROS/CMakeLists.txt

This file was deleted.

31 changes: 31 additions & 0 deletions example/Cpp/src/ROS/MinimalPublisher.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
target Cpp {
ros2: true
}

public preamble {=
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It occurs to me that these two can be automatically included in the generated code since the CppRos2PackageGenerator is already adding them as package dependencies anyway.

=}

main reactor {
private preamble {=
// FIXME: forward declaration to make the node visible
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary? Can't you generate this declaration?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, lf_ros_node can be a reserved state variable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a temporary workaround until I found a better solution. I am trying to keep the code for generating reactor classes agnostic of the target platform, as it keeps the code generator clean. However, there needs to be some way of extending the generated code. I will fix this mechanism as soon as I have found a satisfactory solution.

extern rclcpp::Node* lf_node;
=}

state publisher: {= rclcpp::Publisher<std_msgs::msg::String>::SharedPtr =}
state count: unsigned(0)

timer t(0, 500 ms)

reaction(startup) {=
publisher = lf_node->create_publisher<std_msgs::msg::String>("topic", 10);
=}

reaction(t) {=
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count++);
reactor::log::Info() << "Publishing: " << message.data;
publisher->publish(message);
Copy link
Contributor

@Soroosh129 Soroosh129 Mar 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the default rclcpp executor is thread-safe, so calling publish for the same topic from multiple LF worker threads will be problematic without acquiring a mutex. This is of course fine for this example, but I think we should be mindful of this limitation, especially if reactors other than the main reactor will be publishing messages.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, thread-safety is not discussed in the documentation. According to this post, however, these operations should be thread-safe.

=}
}
32 changes: 32 additions & 0 deletions example/Cpp/src/ROS/MinimalSubscriber.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
target Cpp {
ros2: true,
keepalive: true,
}

public preamble {=
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
=}

main reactor {
private preamble {=
// FIXME: forward declaration to make the node visible
extern rclcpp::Node* lf_node;
=}

state subscription: {= rclcpp::Subscription<std_msgs::msg::String>::SharedPtr =}
state count: unsigned(0)

physical action message: std::string;

reaction(startup) -> message {=
subscription = lf_node->create_subscription<std_msgs::msg::String>(
"topic", 10, [&message](const std_msgs::msg::String::SharedPtr msg) { message.schedule(msg->data); } );
// FIXME: Why can't we use a reference type in the lambda argument?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean something like this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so, but I am not sure. I tried to use const std_msgs::msg::String::SharedPtr& msg in the signature, but that fails with a lot of weird C++ errors. I just realized after checking the documentation again that galactic uses const std_msgs::msg::String& msg in the signature and foxy uses const std_msgs::msg::String::SharedPtr msg. So I guess const std_msgs::msg::String::SharedPtr& msg is not supported, but const std_msgs::msg::String& msg is.

// const std_msgs::msg::String::SharedPtr& msg
=}

reaction(message) {=
reactor::log::Info() << "I heard: " << *message.get();
=}
}
5 changes: 5 additions & 0 deletions example/Cpp/src/ROS/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This is an LF reimplementation of the ROS 2 minimal publisher and sunscriber
[example](https://docs.ros.org/en/galactic/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html).

It consists of two LF files, MinimalPublisher and MinimalSubscriber, each
implementing the corresponding nodes from the original example.
1 change: 1 addition & 0 deletions org.lflang.tests/src/org/lflang/tests/TestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public static class Message {
public static final String DESC_AS_CCPP = "Running C tests as CCpp.";
public static final String DESC_FOUR_THREADS = "Run non-concurrent and non-federated tests (threads = 4).";
public static final String DESC_SCHED_SWAPPING = "Running with non-default runtime scheduler ";
public static final String DESC_ROS2 = "Running tests using ROS2.";

/* Missing dependency messages */
public static final String MISSING_DOCKER = "Executable 'docker' not found or 'docker' daemon thread not running";
Expand Down
Loading