국비과정을 진행하며 물류창고 출고 자동화시스템이라는 주제로 프로젝트를 진행하게 되었다.
통신을 위한 서버가 필요했고, 필자는 Crow
를 사용해 보기로 하였다.
서버를 구축하면서 Crow
, MySQL
, ROS2
를 사용했다.
Crow의 기본 구조는 다음과 같다.
#include "crow.h"
int main()
{
crow::SimpleApp app;
CROW_ROUTE(app, "/")([](){
return "Hello world";
});
app.port(18080).multithreaded().run();
}
하나씩 확인해보자면
#include <crow.h>
Crow를 사용하기 위해서 추가하는 헤더파일이다.
crow::SimpleApp app;
App을 선언한다.
CROW_ROUTE(app, "/")([](){
return "Hello world";
});
경로(엔드포인트)를 추가한다.
app.port(18080).multithreaded().run();
App을 실행한다. (port()
와 multithreaded()
는 필수가 아니다)
Crow는 Flask-style blueprint를 지원한다.
/* 예시 */
CROW_BP_ROUTE(bp, "/route_name").methods(crow::HTTPMethod::POST)([this](const crow::request& req)
{
// 구현할 기능
}
구현한 서버 코드 중 일부를 발췌한 것이다.
// destination.hpp
#pragma once
#include <crow.h>
#include <memory.h>
#include <string>
#include <cppconn/driver.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/resultset.h>
#include "rclcpp/rclcpp.hpp"
#include "database_connection.hpp"
#include "outbound_delivery_robot_interfaces/msg/location.hpp"
class Destination : public DatabaseConnection, rclcpp::Node
{
public:
Destination(const std::string& configFile): DatabaseConnection(configFile), Node("send_destination"), bp("destination")
{
publisher_ = this->create_publisher<outbound_delivery_robot_interfaces::msg::Location>("location", 10);
CROW_BP_ROUTE(bp, "/send").methods(crow::HTTPMethod::POST)([this](const crow::request& req)
{
auto data = crow::json::load(req.body);
if (!data) {return crow::response(400, "Invalid JSON"); }
auto robot_id = data["robot_id"].s();
auto product = data["task"].s();
auto conn = Connection();
std::unique_ptr<sql::PreparedStatement> pstmt(conn->prepareStatement("SELECT * FROM location WHERE product=?"));
pstmt->setString(1, std::string(product));
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
if (res->next())
{
location_msg.robot_id = std::stoi(robot_id);
location_msg.section = res->getString("section");
location_msg.x = res->getDouble("x");
location_msg.y = res->getDouble("y");
location_msg.z = res->getDouble("z");
location_msg.w = res->getDouble("w");
publisher_->publish(location_msg);
}
return crow::response(200, "Location published successfully\n");
});
}
crow::Blueprint& getBlueprint()
{
return bp;
}
private:
crow::Blueprint bp;
rclcpp::Publisher<outbound_delivery_robot_interfaces::msg::Location>::SharedPtr publisher_;
outbound_delivery_robot_interfaces::msg::Location location_msg;
};
// main.cpp
#include "rclcpp/rclcpp.hpp"
#include <crow.h>
#include <filesystem>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include "server/destination.hpp"
int main(int argc, char* argv[])
{
rclcpp::init(argc, argv);
std::string package_share_directory = ament_index_cpp::get_package_share_directory("server");
std::string configFile = (std::filesystem::path(package_share_directory) / "config" / "database.yaml").string();
Destination destination(configFile);
crow::SimpleApp app;
app.loglevel(crow::LogLevel::Info);
app.register_blueprint(destination.getBlueprint());
app.port(5000).multithreaded().run();
rclcpp::shutdown();
return 0;
}