Skip to content

Commit 06e67a6

Browse files
authored
Merge pull request #3737 from JdeRobot/cpp_vacuum_cleaner
Cpp vacuum cleaner update
2 parents 64864d4 + ebc9bbc commit 06e67a6

File tree

27 files changed

+639
-26058
lines changed

27 files changed

+639
-26058
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __pycache__/
44
*$py.class
55

66
# C extensions
7-
*.so
7+
cpp_lib/build/*
88

99
# Distribution / packaging
1010
.Python
@@ -118,7 +118,7 @@ webpack-stats.json
118118
*.DS_Store
119119
react_frontend/static
120120
developer_scripts
121-
src
121+
# src
122122
!/react_frontend/src
123123

124124
# Intermediate file created in root during development

academy/views.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from .error_handler import error_wrapper
2121
from .templates import select_template
2222
from .models import Exercise, Universe, ExerciseUniverses
23+
from .project_view import is_binary_mimetype
2324
from rest_framework.response import Response
2425

2526

@@ -178,10 +179,18 @@ def get_helper_file(fal, request):
178179
project = request.GET.get("project")
179180
language = request.GET.get("language")
180181
filename = request.GET.get("filename", None)
182+
binary = request.GET.get("binary", None)
181183

182184
path = fal.exercise_helper_path(project, language)
185+
file_path = fal.path_join(path, filename)
186+
187+
if binary is None or binary is False:
188+
content = fal.read(file_path)
189+
else:
190+
content = fal.read_binary(file_path)
191+
b64 = base64.b64encode(content)
192+
content = b64.decode("utf-8")
183193

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

docs/InstructionsForDevelopers.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ Create a folder with the folder name as "exercise_id" at the location from repos
377377
Inside that folder create 2 or more new ones with the following names:
378378

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

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

404405
#### Source code: inside `cpp_template`
405406

407+
An exercise must contain this 3 files and a folder:
408+
409+
- **main.cpp**: used as the entrypoint for launching the WebGUI, HAL, console control and the user code. **Must** contain the next code:
410+
411+
```cpp
412+
#include "HAL.hpp"
413+
#include "WebGUI.hpp"
414+
#include "academy.cpp"
415+
#include "rclcpp/rclcpp.hpp"
416+
#include <bits/stdc++.h>
417+
#include <filesystem>
418+
#include <string>
419+
#include <thread>
420+
421+
void start_console()
422+
{
423+
int virtual_terminal = 0;
424+
for (const auto &entry : std::filesystem::directory_iterator("/dev/pts/"))
425+
{
426+
std::filesystem::path outfilename = entry.path();
427+
std::string filename = outfilename.filename().string();
428+
if (filename != "ptmx" && std::stoi(filename) > virtual_terminal)
429+
{
430+
virtual_terminal = std::stoi(filename);
431+
}
432+
}
433+
434+
const std::string v_terminal_str = "/dev/pts/" + std::to_string(virtual_terminal);
435+
436+
if (freopen(v_terminal_str.c_str(), "w", stdout) == NULL)
437+
{
438+
std::cerr << "Error redirecting stdout!" << std::endl;
439+
}
440+
441+
if (freopen(v_terminal_str.c_str(), "w", stderr) == NULL)
442+
{
443+
std::cerr << "Error redirecting stderr!" << std::endl;
444+
}
445+
446+
if (freopen(v_terminal_str.c_str(), "w", stdin) == NULL)
447+
{
448+
std::cerr << "Error redirecting stdin!" << std::endl;
449+
}
450+
};
451+
452+
int main(int argc, char *argv[])
453+
{
454+
rclcpp::init(argc, argv);
455+
start_console();
456+
457+
rclcpp::executors::MultiThreadedExecutor executor(rclcpp::ExecutorOptions(), 2);
458+
459+
auto HAL_node = std::make_shared<HAL>();
460+
executor.add_node(HAL_node);
461+
462+
auto WebGUI_node = std::make_shared<WebGUINode>();
463+
executor.add_node(WebGUI_node);
464+
465+
#ifdef USER_NODE
466+
auto user_node = std::make_shared<UserNode>();
467+
executor.add_node(user_node);
468+
#else
469+
std::thread user(exercise);
470+
#endif
471+
std::thread ros([&executor]{executor.spin();});
472+
WebGUI();
473+
474+
#ifndef USER_NODE
475+
user.join();
476+
#endif
477+
ros.join();
478+
479+
rclcpp::shutdown();
480+
return 0;
481+
}
482+
```
483+
484+
- **package.xml**: Package description of the ROS package.
485+
- **CMakeLists.txt**: needed for compilation of the ROS package.
486+
- **libs/**: needed for storing the libraries for user access.
487+
488+
#### Source code: inside `cpp_lib`
489+
406490
**WORK IN PROGRESS**
407491
492+
This directory contains the source code for the C++ libraries WebGUI, HAL, Frequency and others.
493+
494+
It must contain:
495+
496+
- **src/**: contains the source code for at least HAL, WebGUI and Frequency.
497+
- **include/**: contains the headers for the source code.
498+
- **CMakeLists.txt**: needed for compilation of the libraries.
499+
500+
This directory will not be accesible to the user and will only be used to compile the libraries.
501+
502+
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):
503+
504+
```bash
505+
docker exec -it cf17d87822efd8f7596d8c5dd274ef84789e7be57eaa6a9a78ad7d1e16dd0807 bash
506+
```
507+
508+
Then inside the **RADI** navigate to the desired exercise like:
509+
510+
```bash
511+
cd RoboticsAcademy/exercises/vacuum_cleaner
512+
```
513+
514+
After being inside the exercise directory you must compile the libary inside the **RADI** using;
515+
516+
```bash
517+
cd cpp_lib
518+
mkdir build
519+
cd build/
520+
cmake ..
521+
make
522+
chmod 777 *.so
523+
```
524+
525+
After compiling the libraries you **must** move them to the libs folder created in the **cpp_template** section.
526+
527+
```bash
528+
mv *.so ../../cpp_template/libs/
529+
cd ..
530+
rm -r build/
531+
# Copy the headers to the cpp_template directory
532+
cp -r include/ ../cpp_template/libs/
533+
```
534+
408535
#### Frontend: inside `frontend`
409536

410537
An exercise must contain the following files:
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(VacuumCleanerLibs CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
7+
8+
find_package(ament_cmake REQUIRED)
9+
10+
set(ROS2_PACKAGES
11+
rclcpp
12+
nav_msgs
13+
gazebo_msgs
14+
geometry_msgs
15+
sensor_msgs
16+
std_msgs
17+
tf2
18+
tf2_geometry_msgs
19+
ros_gz_interfaces
20+
)
21+
22+
foreach(pkg IN LISTS ROS2_PACKAGES)
23+
find_package(${pkg} REQUIRED)
24+
endforeach()
25+
26+
find_package(Boost REQUIRED COMPONENTS system)
27+
find_package(nlohmann_json REQUIRED)
28+
29+
set(COMMON_INTERFACES_INCLUDE "/home/ws/install/common_interfaces_cpp/include")
30+
set(COMMON_INTERFACES_LIB "/home/ws/install/common_interfaces_cpp/lib/libcommon_interfaces_cpp.so")
31+
32+
set(WEBGUI_ROS_DEPS rclcpp nav_msgs gazebo_msgs tf2 tf2_geometry_msgs)
33+
set(WEBGUI_SYS_DEPS Frequency Boost::system nlohmann_json::nlohmann_json)
34+
set(HAL_ROS_DEPS rclcpp geometry_msgs sensor_msgs std_msgs ros_gz_interfaces)
35+
36+
37+
# --- libFrequency.so ---
38+
add_library(Frequency SHARED src/Frequency.cpp)
39+
target_include_directories(Frequency PUBLIC include)
40+
41+
# --- libWebGUI.so ---
42+
add_library(WebGUI SHARED src/WebGUI.cpp)
43+
target_include_directories(WebGUI PUBLIC include)
44+
45+
ament_target_dependencies(WebGUI ${WEBGUI_ROS_DEPS})
46+
target_link_libraries(WebGUI ${WEBGUI_SYS_DEPS})
47+
48+
# --- libHAL.so ---
49+
add_library(HAL SHARED src/HAL.cpp)
50+
target_include_directories(HAL PUBLIC include ${COMMON_INTERFACES_INCLUDE})
51+
52+
ament_target_dependencies(HAL ${HAL_ROS_DEPS})
53+
target_link_libraries(HAL ${COMMON_INTERFACES_LIB})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef INCLUDE_FREQUENCY_HPP_
2+
#define INCLUDE_FREQUENCY_HPP_
3+
4+
#include <chrono>
5+
#include <cmath>
6+
#include <thread>
7+
8+
class Frequency
9+
{
10+
private:
11+
std::chrono::high_resolution_clock::time_point last_time;
12+
int is_first_iteration;
13+
14+
public:
15+
Frequency();
16+
~Frequency();
17+
18+
static int rate;
19+
void tick(int ideal_cycle = 50);
20+
};
21+
22+
#endif
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef INCLUDE_HAL_HPP_
2+
#define INCLUDE_HAL_HPP_
3+
4+
#include <vector>
5+
#include <memory>
6+
#include <thread>
7+
#include <chrono>
8+
#include "rclcpp/rclcpp.hpp"
9+
#include "common_interfaces_cpp/hal/motors.hpp"
10+
#include "common_interfaces_cpp/hal/laser.hpp"
11+
#include "common_interfaces_cpp/hal/bumper.hpp"
12+
13+
class HAL : public rclcpp::Node
14+
{
15+
public:
16+
HAL();
17+
static void set_v(const float speed);
18+
static void set_w(const float speed);
19+
static const LaserData *get_laser_data();
20+
static std::vector<bool> get_bumper_data();
21+
22+
private:
23+
static std::shared_ptr<MotorsNode> motors_node_;
24+
static std::shared_ptr<LaserNode> laser_node_;
25+
static std::shared_ptr<BumperNode> bumper_node_;
26+
std::thread spin_thread_;
27+
};
28+
29+
#endif
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef INCLUDE_WEBGUI_HPP_
2+
#define INCLUDE_WEBGUI_HPP_
3+
4+
#include <boost/beast/core.hpp>
5+
#include <boost/beast/websocket.hpp>
6+
#include <boost/asio/connect.hpp>
7+
#include <boost/asio/ip/tcp.hpp>
8+
#include <boost/asio/strand.hpp>
9+
#include <nlohmann/json.hpp>
10+
#include "rclcpp/rclcpp.hpp"
11+
#include "nav_msgs/msg/odometry.hpp"
12+
#include "gazebo_msgs/msg/performance_metrics.hpp"
13+
#include "Frequency.hpp"
14+
15+
namespace beast = boost::beast;
16+
namespace websocket = beast::websocket;
17+
namespace net = boost::asio;
18+
using tcp = net::ip::tcp;
19+
using json = nlohmann::json;
20+
21+
class WebGUI
22+
{
23+
public:
24+
WebGUI();
25+
static std::string img_payload;
26+
};
27+
28+
class WebGUINode : public rclcpp::Node
29+
{
30+
public:
31+
WebGUINode();
32+
static std::vector<double> get_pose();
33+
static double get_performance();
34+
35+
private:
36+
void pose_callback(nav_msgs::msg::Odometry::UniquePtr msg);
37+
void performance_callback(gazebo_msgs::msg::PerformanceMetrics::UniquePtr msg);
38+
39+
rclcpp::Subscription<nav_msgs::msg::Odometry>::SharedPtr odom_sub_;
40+
rclcpp::Subscription<gazebo_msgs::msg::PerformanceMetrics>::SharedPtr perf_sub_;
41+
42+
static nav_msgs::msg::Odometry last_odom;
43+
static gazebo_msgs::msg::PerformanceMetrics last_perf;
44+
};
45+
46+
#endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "Frequency.hpp"
2+
3+
int Frequency::rate = 0;
4+
5+
Frequency::Frequency() : is_first_iteration(1) {}
6+
7+
Frequency::~Frequency() {}
8+
9+
void Frequency::tick(int ideal_cycle)
10+
{
11+
const auto ideal_ms = std::chrono::milliseconds(1000 / ideal_cycle);
12+
const auto now = std::chrono::high_resolution_clock::now();
13+
14+
if (is_first_iteration == 1)
15+
{
16+
last_time = now;
17+
is_first_iteration = 0;
18+
return;
19+
}
20+
21+
const auto iter_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
22+
23+
if (iter_ms < ideal_ms)
24+
{
25+
rate = std::round(1000.0 / ideal_ms.count());
26+
std::this_thread::sleep_for(ideal_ms - iter_ms);
27+
}
28+
else
29+
{
30+
rate = std::round(1000.0 / iter_ms.count());
31+
}
32+
33+
last_time = now;
34+
}

0 commit comments

Comments
 (0)