Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
01908aa
update dockerfile
aquintan4 Apr 1, 2026
42825a2
install json lib
aquintan4 Apr 6, 2026
626092c
Merge remote-tracking branch 'upstream/cpp_dockerfile' into cpp_support
aquintan4 Apr 7, 2026
dc9ae56
dockerfile changes
aquintan4 Apr 7, 2026
39f9936
cpp support lib for loc vaccum cleaner
aquintan4 Apr 8, 2026
7fdeab2
modify the dockerfile to install the cpp libraries
aquintan4 Apr 8, 2026
3e3ed2a
change dockerfile cpp support path
aquintan4 Apr 8, 2026
5088b0f
add find package for ros2 msg interfaces in CMakeLists
aquintan4 Apr 8, 2026
dbe90ca
use ament_target_dependencies to link dependencies easily
aquintan4 Apr 8, 2026
3a6c3ef
remove PUBLIC from link libraries
aquintan4 Apr 8, 2026
9b40f0e
organice cmake and add gazebo dependencie
aquintan4 Apr 8, 2026
3d624fa
fix path in the cp command for cpp support
aquintan4 Apr 8, 2026
1549324
Merge branch 'humble-devel' of https://github.com/JdeRobot/RoboticsAc…
aquintan4 Apr 8, 2026
731b717
remove libs compilation from docker file
aquintan4 Apr 8, 2026
a92647e
cpp support libs and api
aquintan4 Apr 8, 2026
e628c1b
Merge pull request #3736 from aquintan4/probando
javizqh Apr 8, 2026
f5faf89
move lib creation for vacuum cleaner
aquintan4 Apr 8, 2026
321b64a
Merge pull request #3738 from aquintan4/probando
aquintan4 Apr 8, 2026
aec2448
remove ra_cpp_common from mv command in dockerfile
aquintan4 Apr 8, 2026
429f241
src files
aquintan4 Apr 8, 2026
686f845
remove headers in the cpp template
aquintan4 Apr 8, 2026
4b7912e
change name in macro definition
aquintan4 Apr 8, 2026
12695ae
compile new libs with the proper macro
aquintan4 Apr 9, 2026
4c35271
make a whitelist in file_access read function for our simple API libs
aquintan4 Apr 9, 2026
0721201
restore file_access to it's original version
aquintan4 Apr 9, 2026
6f565c9
Fix read binary helper
javizqh Apr 9, 2026
4fef1b0
Fix binary files in helpers
javizqh Apr 9, 2026
a1fd71d
fix CMakeLists
aquintan4 Apr 9, 2026
154ec0d
add headers inside a subdirectory
aquintan4 Apr 9, 2026
2752d00
Rename the cpp library
javizqh Apr 10, 2026
f9ec76c
Rename cpp_libs
javizqh Apr 10, 2026
f8ec713
Update docs
javizqh Apr 10, 2026
ebc9bbc
Update gitignore
javizqh Apr 10, 2026
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: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __pycache__/
*$py.class

# C extensions
*.so
cpp_lib/build/*

# Distribution / packaging
.Python
Expand Down Expand Up @@ -118,7 +118,7 @@ webpack-stats.json
*.DS_Store
react_frontend/static
developer_scripts
src
# src
!/react_frontend/src

# Intermediate file created in root during development
Expand Down
11 changes: 10 additions & 1 deletion academy/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .error_handler import error_wrapper
from .templates import select_template
from .models import Exercise, Universe, ExerciseUniverses
from .project_view import is_binary_mimetype
from rest_framework.response import Response


Expand Down Expand Up @@ -178,10 +179,18 @@ def get_helper_file(fal, request):
project = request.GET.get("project")
language = request.GET.get("language")
filename = request.GET.get("filename", None)
binary = request.GET.get("binary", None)

path = fal.exercise_helper_path(project, language)
file_path = fal.path_join(path, filename)

if binary is None or binary is False:
content = fal.read(file_path)
else:
content = fal.read_binary(file_path)
b64 = base64.b64encode(content)
content = b64.decode("utf-8")

content = fal.read(fal.path_join(path, filename))
serializer = FileContentSerializer({"content": content})
return Response(serializer.data)

Expand Down
127 changes: 127 additions & 0 deletions docs/InstructionsForDevelopers.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ Create a folder with the folder name as "exercise_id" at the location from repos
Inside that folder create 2 or more new ones with the following names:

- `<language>_template`: the available languages are `python` and `cpp`. One for each supported language.
- `cpp_lib`: used for precompilation of additional C++ libaries like WebGUI or HAL.
- `frontend`

You may add a teaser to the exercise by creating a file called `teaser.png` using a 9/10 aspect ratio.
Expand All @@ -403,8 +404,134 @@ For knowing how to use each package, please follow the links in the list above.

#### Source code: inside `cpp_template`

An exercise must contain this 3 files and a folder:

- **main.cpp**: used as the entrypoint for launching the WebGUI, HAL, console control and the user code. **Must** contain the next code:

```cpp
#include "HAL.hpp"
#include "WebGUI.hpp"
#include "academy.cpp"
#include "rclcpp/rclcpp.hpp"
#include <bits/stdc++.h>
#include <filesystem>
#include <string>
#include <thread>

void start_console()
{
int virtual_terminal = 0;
for (const auto &entry : std::filesystem::directory_iterator("/dev/pts/"))
{
std::filesystem::path outfilename = entry.path();
std::string filename = outfilename.filename().string();
if (filename != "ptmx" && std::stoi(filename) > virtual_terminal)
{
virtual_terminal = std::stoi(filename);
}
}

const std::string v_terminal_str = "/dev/pts/" + std::to_string(virtual_terminal);

if (freopen(v_terminal_str.c_str(), "w", stdout) == NULL)
{
std::cerr << "Error redirecting stdout!" << std::endl;
}

if (freopen(v_terminal_str.c_str(), "w", stderr) == NULL)
{
std::cerr << "Error redirecting stderr!" << std::endl;
}

if (freopen(v_terminal_str.c_str(), "w", stdin) == NULL)
{
std::cerr << "Error redirecting stdin!" << std::endl;
}
};

int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
start_console();

rclcpp::executors::MultiThreadedExecutor executor(rclcpp::ExecutorOptions(), 2);

auto HAL_node = std::make_shared<HAL>();
executor.add_node(HAL_node);

auto WebGUI_node = std::make_shared<WebGUINode>();
executor.add_node(WebGUI_node);

#ifdef USER_NODE
auto user_node = std::make_shared<UserNode>();
executor.add_node(user_node);
#else
std::thread user(exercise);
#endif
std::thread ros([&executor]{executor.spin();});
WebGUI();

#ifndef USER_NODE
user.join();
#endif
ros.join();

rclcpp::shutdown();
return 0;
}
```

- **package.xml**: Package description of the ROS package.
- **CMakeLists.txt**: needed for compilation of the ROS package.
- **libs/**: needed for storing the libraries for user access.

#### Source code: inside `cpp_lib`

**WORK IN PROGRESS**

This directory contains the source code for the C++ libraries WebGUI, HAL, Frequency and others.

It must contain:

- **src/**: contains the source code for at least HAL, WebGUI and Frequency.
- **include/**: contains the headers for the source code.
- **CMakeLists.txt**: needed for compilation of the libraries.

This directory will not be accesible to the user and will only be used to compile the libraries.

To do that compilation you must launch the **RADI** using Robotics Academy as a **volume** (it is recommende to use the developer script) using the next command (in Linux):

```bash
docker exec -it cf17d87822efd8f7596d8c5dd274ef84789e7be57eaa6a9a78ad7d1e16dd0807 bash
```

Then inside the **RADI** navigate to the desired exercise like:

```bash
cd RoboticsAcademy/exercises/vacuum_cleaner
```

After being inside the exercise directory you must compile the libary inside the **RADI** using;

```bash
cd cpp_lib
mkdir build
cd build/
cmake ..
make
chmod 777 *.so
```

After compiling the libraries you **must** move them to the libs folder created in the **cpp_template** section.

```bash
mv *.so ../../cpp_template/libs/
cd ..
rm -r build/
# Copy the headers to the cpp_template directory
cp -r include/ ../cpp_template/libs/
```

#### Frontend: inside `frontend`

An exercise must contain the following files:
Expand Down
53 changes: 53 additions & 0 deletions exercises/vacuum_cleaner/cpp_lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.10)
project(VacuumCleanerLibs CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

find_package(ament_cmake REQUIRED)

set(ROS2_PACKAGES
rclcpp
nav_msgs
gazebo_msgs
geometry_msgs
sensor_msgs
std_msgs
tf2
tf2_geometry_msgs
ros_gz_interfaces
)

foreach(pkg IN LISTS ROS2_PACKAGES)
find_package(${pkg} REQUIRED)
endforeach()

find_package(Boost REQUIRED COMPONENTS system)
find_package(nlohmann_json REQUIRED)

set(COMMON_INTERFACES_INCLUDE "/home/ws/install/common_interfaces_cpp/include")
set(COMMON_INTERFACES_LIB "/home/ws/install/common_interfaces_cpp/lib/libcommon_interfaces_cpp.so")

set(WEBGUI_ROS_DEPS rclcpp nav_msgs gazebo_msgs tf2 tf2_geometry_msgs)
set(WEBGUI_SYS_DEPS Frequency Boost::system nlohmann_json::nlohmann_json)
set(HAL_ROS_DEPS rclcpp geometry_msgs sensor_msgs std_msgs ros_gz_interfaces)


# --- libFrequency.so ---
add_library(Frequency SHARED src/Frequency.cpp)
target_include_directories(Frequency PUBLIC include)

# --- libWebGUI.so ---
add_library(WebGUI SHARED src/WebGUI.cpp)
target_include_directories(WebGUI PUBLIC include)

ament_target_dependencies(WebGUI ${WEBGUI_ROS_DEPS})
target_link_libraries(WebGUI ${WEBGUI_SYS_DEPS})

# --- libHAL.so ---
add_library(HAL SHARED src/HAL.cpp)
target_include_directories(HAL PUBLIC include ${COMMON_INTERFACES_INCLUDE})

ament_target_dependencies(HAL ${HAL_ROS_DEPS})
target_link_libraries(HAL ${COMMON_INTERFACES_LIB})
22 changes: 22 additions & 0 deletions exercises/vacuum_cleaner/cpp_lib/include/Frequency.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef INCLUDE_FREQUENCY_HPP_
#define INCLUDE_FREQUENCY_HPP_

#include <chrono>
#include <cmath>
#include <thread>

class Frequency
{
private:
std::chrono::high_resolution_clock::time_point last_time;
int is_first_iteration;

public:
Frequency();
~Frequency();

static int rate;
void tick(int ideal_cycle = 50);
};

#endif
29 changes: 29 additions & 0 deletions exercises/vacuum_cleaner/cpp_lib/include/HAL.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef INCLUDE_HAL_HPP_
#define INCLUDE_HAL_HPP_

#include <vector>
#include <memory>
#include <thread>
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "common_interfaces_cpp/hal/motors.hpp"
#include "common_interfaces_cpp/hal/laser.hpp"
#include "common_interfaces_cpp/hal/bumper.hpp"

class HAL : public rclcpp::Node
{
public:
HAL();
static void set_v(const float speed);
static void set_w(const float speed);
static const LaserData *get_laser_data();
static std::vector<bool> get_bumper_data();

private:
static std::shared_ptr<MotorsNode> motors_node_;
static std::shared_ptr<LaserNode> laser_node_;
static std::shared_ptr<BumperNode> bumper_node_;
std::thread spin_thread_;
};

#endif
46 changes: 46 additions & 0 deletions exercises/vacuum_cleaner/cpp_lib/include/WebGUI.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef INCLUDE_WEBGUI_HPP_
#define INCLUDE_WEBGUI_HPP_

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <nlohmann/json.hpp>
#include "rclcpp/rclcpp.hpp"
#include "nav_msgs/msg/odometry.hpp"
#include "gazebo_msgs/msg/performance_metrics.hpp"
#include "Frequency.hpp"

namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
using json = nlohmann::json;

class WebGUI
{
public:
WebGUI();
static std::string img_payload;
};

class WebGUINode : public rclcpp::Node
{
public:
WebGUINode();
static std::vector<double> get_pose();
static double get_performance();

private:
void pose_callback(nav_msgs::msg::Odometry::UniquePtr msg);
void performance_callback(gazebo_msgs::msg::PerformanceMetrics::UniquePtr msg);

rclcpp::Subscription<nav_msgs::msg::Odometry>::SharedPtr odom_sub_;
rclcpp::Subscription<gazebo_msgs::msg::PerformanceMetrics>::SharedPtr perf_sub_;

static nav_msgs::msg::Odometry last_odom;
static gazebo_msgs::msg::PerformanceMetrics last_perf;
};

#endif
34 changes: 34 additions & 0 deletions exercises/vacuum_cleaner/cpp_lib/src/Frequency.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "Frequency.hpp"

int Frequency::rate = 0;

Frequency::Frequency() : is_first_iteration(1) {}

Frequency::~Frequency() {}

void Frequency::tick(int ideal_cycle)
{
const auto ideal_ms = std::chrono::milliseconds(1000 / ideal_cycle);
const auto now = std::chrono::high_resolution_clock::now();

if (is_first_iteration == 1)
{
last_time = now;
is_first_iteration = 0;
return;
}

const auto iter_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);

if (iter_ms < ideal_ms)
{
rate = std::round(1000.0 / ideal_ms.count());
std::this_thread::sleep_for(ideal_ms - iter_ms);
}
else
{
rate = std::round(1000.0 / iter_ms.count());
}

last_time = now;
}
Loading
Loading