服务的接口类型由两部分组成,请求和相应
在包的src的同级目录下创建sev文件夹,在sev文件夹里添加Xxx.srv文件(文件名首字母必须大写,不能有下划线)
# 文件名Borrow.srv
#request
string name
uint32 money
---
# response
bool success
uint32 money
# 文件名SellNovel.srv
uint32 money
---
string[] novel
需要在CMakeLists.txt文件中添加rosidl_generate_interfaces
表明srv文件所在的目录,如果有添加的数据类型需要添加find_package
# 如果有添加的数据类型需要添加find_package
find_package(std_msgs REQUIRED)
# rosidl_generate_interfaces表明srv文件所在的目录
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/Borrow.srv"
"srv/SellNovel.srv"
DEPENDENCIES
)
需要添加的
<exec_depend>rosidl_default_runtimeexec_depend>
<member_of_group>rosidl_interface_packagesmember_of_group>
使用命令来查看ros2 interface list
,命令查看看到三个数据类型,表示在service_interface包里面srv目录下的Borrow类型
<depend>service_interfacedepend>
//添加service_interface
find_package(service_interface REQUIRED)
// 添加service_interface
ament_target_dependencies(
my_services
"std_msgs"
"rclcpp"
service_interface
)
// 添加数据类型的头文件
// service_interface包的srv目录下有两个数据类型文件Borrow.srv和Buy.srv
// 导入的头文件都必须是小写
// srv文件名:Borrow.srv SellNovel.srv
#include "service_interface/srv/borrow.hpp"
#include "service_interface/srv/sell_novel.hpp"
对调函数是当服务端接受到数据做出反应的函数,参数是两个之只能指针
#include "service_interface/srv/sell_novel.hpp"
// 声明一个回调函数,用于接受导数据并做出相应
// 参数有两个一个是请求,一个是回应
//类型是共享指针类型,书写格式如下
void callback(const service_interface::srv::SellNovel::Request::SharedPtr request,
const service_interface::srv::SellNovel::Response::SharedPtr response
)
{
unsigned int num = (int)request->money/(1.0);
if(num>novel_queue.size())
{
// 等待
RCLCPP_INFO(this->get_logger(),"书不够,书库有:%d,要卖出:%d",novel_queue.size(),num);
// 设置睡眠的时间为一秒
rclcpp::Rate rate(1);
while (novel_queue.size()<num)
{
RCLCPP_INFO(this->get_logger(),"等待中,还差:%d本书",novel_queue.size());
// 循环中睡眠一秒
rate.sleep();
}
}
RCLCPP_INFO(this->get_logger(),"当前图书的数量有:%d,大于要卖出去的数量:%d",novel_queue.size(),num);
for(int i=0;i<(int)num;i++)
{
response->novel.push_back(novel_queue.front());
novel_queue.pop();
}
}
在定义回调函数时,我们为了不影响主线程的顺序执行,才用多线程的方式来处理一些回调函数中的流程
// 类中声明回调函数组-------------------
// ros2中使用多线程执行器和回调组来实现多线程,我们先在类中声明一个回调组成员变量
rclcpp::CallbackGroup::SharedPtr callback_group;
// 构造函数中初始化回调组----------------------------
// 自定义函数回调组
callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
// 单线程的主函数
int main(int argc, char ** argv)
{
//2.初始化客户端库
rclcpp::init(argc,argv);
//3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
auto node = std::make_shared<my_server>("ros2");
//4.使用spin循环节点
rclcpp::spin(node);
//5. 关闭客户端库
rclcpp::shutdown();
}
// 多线程主函数
int main(int argc, char ** argv)
{
//2.初始化客户端库
rclcpp::init(argc,argv);
//3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
auto node = std::make_shared<my_server>("ros2");
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(node); // 第一个线程节点
executor.add_node(node); // 第二个线程节点
executor.spin();
rclcpp::shutdown();
}
// 类中定义服务-------------
// 自定义函数回调组
callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
// 服务端的声明,没有初始化,在构造函数中初始化
rclcpp::Service<service_interface::srv::SellNovel>::SharedPtr services_pub;
// 构造函数中声明服务-------------
services_pub = this->create_service<service_interface::srv::SellNovel>("service_name",
std::bind(&my_server::callback,this,_1,_2),
rmw_qos_profile_services_default,
callback_group);
有了第四个参数,函数回调组之后,当接收到客户端的函数请求之后,就会在callback_group函数回调组里面执行callback函数。
#include
#include
#include
#include "rclcpp/rclcpp.hpp"
#include "service_interface/srv/borrow.hpp"
#include "service_interface/srv/sell_novel.hpp"
using std::placeholders::_1;
using std::placeholders::_2;
class my_server :public rclcpp::Node
{
private:
// 存放二手书的一个队列容器
std::queue<std::string> novel_queue;
// ros2中使用多线程执行器和回调组来实现多线程,我们先在类中声明一个回调组成员变量
rclcpp::CallbackGroup::SharedPtr callback_group;
// 服务端的声明,没有初始化,在构造函数中初始化
rclcpp::Service<service_interface::srv::SellNovel>::SharedPtr services_pub;
// 声明一个回调函数,用于接受导数据并做出相应
// 参数有两个一个是请求,一个是回应
//类型是共享指针类型,书写格式如下
void callback(const service_interface::srv::SellNovel::Request::SharedPtr request,
const service_interface::srv::SellNovel::Response::SharedPtr response
)
{
unsigned int num = (int)request->money/(1.0);
if(num>novel_queue.size())
{
// 等待
RCLCPP_INFO(this->get_logger(),"书不够,书库有:%d,要卖出:%d",novel_queue.size(),num);
// 设置睡眠的时间为一秒
rclcpp::Rate rate(1);
while (novel_queue.size()<num)
{
RCLCPP_INFO(this->get_logger(),"等待中,还差:%d本书",novel_queue.size());
// 循环中睡眠一秒
rate.sleep();
}
}
RCLCPP_INFO(this->get_logger(),"当前图书的数量有:%d,大于要卖出去的数量:%d",novel_queue.size(),num);
for(int i=0;i<(int)num;i++)
{
response->novel.push_back(novel_queue.front());
novel_queue.pop();
}
}
public:
my_server(std::string name);
~my_server();
};
my_server :: my_server(std::string name):Node(name)
{
RCLCPP_INFO(this->get_logger(),"构造 service");
// 自定义函数回调组
callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
// 对服务对象进行实例化
services_pub = this->create_service<service_interface::srv::SellNovel>("service_name",
std::bind(&my_server::callback,this,_1,_2),
rmw_qos_profile_services_default,
callback_group);
}
my_server:: ~my_server()
{
RCLCPP_INFO(this->get_logger(),"结束service");
}
int main(int argc, char ** argv)
{
//2.初始化客户端库
rclcpp::init(argc,argv);
//3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
auto node = std::make_shared<my_server>("ros2");
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(node); // 第一个线程节点
// executor.add_node(node); // 第二个线程节点
executor.spin();
rclcpp::shutdown();
}