Services#
Agnocast supports ROS 2-style services with zero-copy request and response handling via shared memory.
Warning
Agnocast service/client is not officially supported yet. The API may introduce breaking changes without a major version increment. Use at your own risk.
Migrating a Service Server#
Before (rclcpp)#
#include "rclcpp/rclcpp.hpp"
#include "my_package/srv/my_service.hpp"
using ServiceT = my_package::srv::MyService;
using RequestT = ServiceT::Request;
using ResponseT = ServiceT::Response;
class MyServer : public rclcpp::Node
{
rclcpp::Service<ServiceT>::SharedPtr service_;
public:
MyServer() : Node("my_server")
{
service_ = create_service<ServiceT>(
"my_service",
[this](const std::shared_ptr<RequestT> request,
std::shared_ptr<ResponseT> response) {
response->result = process(request->data);
RCLCPP_INFO(get_logger(), "Processed request");
});
}
};
After (Agnocast Stage 1)#
#include "agnocast/agnocast.hpp" // (1)
#include "my_package/srv/my_service.hpp"
using ServiceT = my_package::srv::MyService;
using RequestT = agnocast::Service<ServiceT>::RequestT; // (2)
using ResponseT = agnocast::Service<ServiceT>::ResponseT; // (2)
class MyServer : public rclcpp::Node
{
agnocast::Service<ServiceT>::SharedPtr service_; // (3)
public:
MyServer() : Node("my_server")
{
service_ = agnocast::create_service<ServiceT>( // (4)
this, "my_service",
[this](const agnocast::ipc_shared_ptr<RequestT> & request, // (5)
agnocast::ipc_shared_ptr<ResponseT> & response) {
response->result = process(request->data);
RCLCPP_INFO(get_logger(), "Processed request");
});
}
};
Key changes:
- Include changes to
agnocast/agnocast.hpp - Type aliases change to
agnocast::Service<ServiceT>::RequestT/ResponseT rclcpp::Service→agnocast::Service- Use free function
agnocast::create_service(this, ...)instead ofthis->create_service(...) - Callback takes
agnocast::ipc_shared_ptrreferences instead ofstd::shared_ptr
After (Agnocast Stage 2)#
#include "agnocast/agnocast.hpp"
#include "my_package/srv/my_service.hpp"
using ServiceT = my_package::srv::MyService;
using RequestT = agnocast::Service<ServiceT>::RequestT;
using ResponseT = agnocast::Service<ServiceT>::ResponseT;
class MyServer : public agnocast::Node // (1)
{
agnocast::Service<ServiceT>::SharedPtr service_;
public:
MyServer() : Node("my_server")
{
service_ = this->create_service<ServiceT>( // (2)
"my_service",
[this](const agnocast::ipc_shared_ptr<RequestT> & request,
agnocast::ipc_shared_ptr<ResponseT> & response) {
response->result = process(request->data);
RCLCPP_INFO(get_logger(), "Processed request");
});
}
};
Additional changes from Stage 1:
- Base class changes to
agnocast::Node - Service creation uses member function instead of free function
Migrating a Service Client#
Before (rclcpp)#
auto client = node->create_client<ServiceT>("my_service");
while (!client->wait_for_service(1s)) {
RCLCPP_INFO(node->get_logger(), "Waiting for service...");
}
auto request = std::make_shared<ServiceT::Request>();
request->data = 42;
client->async_send_request(
request,
[](rclcpp::Client<ServiceT>::SharedFuture future) {
auto response = future.get();
// process response
});
After (Agnocast Stage 1)#
auto client = agnocast::create_client<ServiceT>( // (1)
node.get(), "my_service");
while (!client->wait_for_service(1s)) {
RCLCPP_INFO(node->get_logger(), "Waiting for service...");
}
auto request = client->borrow_loaned_request(); // (2)
request->data = 42;
client->async_send_request(
std::move(request), // (3)
[](agnocast::Client<ServiceT>::SharedFuture future) { // (4)
auto response = future.get();
// process response
});
Key changes:
- Use free function
agnocast::create_client(node.get(), ...) - Allocate request via
borrow_loaned_request()(shared memory) instead ofstd::make_shared - Pass request with
std::move(zero-copy handoff) - Future type changes to
agnocast::Client<ServiceT>::SharedFuture
After (Agnocast Stage 2)#
auto client = node->create_client<ServiceT>("my_service"); // (1)
// Same usage pattern as Stage 1
Additional change from Stage 1:
- Client creation uses member function instead of free function