ros标准版Action通讯

Action通讯模型

Action通讯模型组成¶

ROS中,节点与节点间通讯,提供了新的方式,就是Action通讯。

Action通讯分为Client端Server端Client端负责给Server端发送指令,Server端接收到指令,根据自身的业务逻辑进行处理指令,处理过程中,可以反馈进度给为Client端,处理结束后将结构反馈给Client端

在Action通讯模型交互过程中,分为三个数据交互阶段:

  • client端请求阶段
  • server端进度反馈阶段
  • server端结果反馈阶段

对于Client端而言,可做的操作行为有两种:

  • 发送指令请求
  • 取消指令请求

对于Server而言,可做的操作行为有:

  • 响应进度信息
  • 响应结果信息

Action交互过程中的专有名称¶

Goal 请求指令¶

client端server端发送请求时所带的数据,我们称之为Goal

Feedback过程响应¶

server端进度反馈阶段,反馈给client端的数据,我们称之为Feedback

Result结果响应¶

server端结果反馈阶段,反馈给client端的数据,我们称之为Result

设计艺术¶

client端请求,server端响应,操作为异步的,通讯信息采用数据来规范,通过GoalFeedbackResult规定了统一操作数据的规范。

一.python

1.非ui版

1.1

client.py

#!/usr/bin/env python
# coding:utf-8
import rospy
from actionlib import ActionClient,ClientGoalHandle,CommState,TerminalState
from demo_actions.msg import CountNumberAction,CountNumberGoaldef transition_cb(goalHandle):commState = goalHandle.get_comm_state()if commState==CommState.PENDING:rospy.loginfo('PENDING')elif commState==CommState.ACTIVE:rospy.loginfo('ACTIVE')elif commState==CommState.DONE:rospy.loginfo('DONE')def feedback_cb(goalHandle,feedback):rospy.loginfo('feedback')if __name__ == '__main__':# 创建节点rospy.init_node("action_client")# action名字actionName = 'action_py'# 创建客户端client = ActionClient(actionName,CountNumberAction)# 等待client.wait_for_server()goal = CountNumberGoal()goal.max = 10goal.duration = 1# 发送指令goal = client.send_goal(goal,transition_cb,feedback_cb)# 阻塞rospy.spin()

1.2

server.py

#!/usr/bin/env python
# coding:utf-8
import rospy
from actionlib import ActionServer,ServerGoalHandle
from demo_actions.msg import CountNumberAction,CountNumberFeedbackdef goal_cb(goalHandle):rospy.loginfo('receive goal')# goal = ServerGoalHandle()# 接受goalHandle.set_accepted()# 获取目标max = goalHandle.get_goal().maxduration = goalHandle.get_goal().durationfor i in range(1,max):feed = CountNumberFeedback()feed.percent = float(i)/max# 进度goalHandle.publish_feedback(feed)def cancel_cb(goalHandle):rospy.loginfo('cancel goal')if __name__ == '__main__':# 创建节点rospy.init_node("action_server")# action名字actionName = 'action_py'# serverserver = ActionServer(actionName,CountNumberAction,goal_cb,cancel_cb,auto_start=False)# 开启服务server.start()# 阻塞rospy.spin()

ui版

ActionClientWindow.py

#! /usr/bin/env python
# coding: utf-8
from PyQt5.QtWidgets import QWidget,QFormLayout,QLineEdit,QLabel,QPushButton
from PyQt5.QtCore import QTimer
from actionlib import ActionClient,ClientGoalHandle,CommState,TerminalState
from demo_actions.msg import CountNumberAction,CountNumberGoal,CountNumberResult,CountNumberFeedback
import rospyclass ActionClientWindow(QWidget):def __init__(self):super(ActionClientWindow, self).__init__()# 自定义 timer, 负责页面更新# update_timer = QTimer(self)# update_timer.setInterval(16)# update_timer.start()# # timer事件回调# update_timer.timeout.connect(self.on_update)# 设置titleself.setWindowTitle("")layout = QFormLayout()self.setLayout(layout)self.le_max = QLineEdit("100")layout.addRow("数到多少", self.le_max)self.le_duration = QLineEdit("0.1")layout.addRow("间隔时间", self.le_duration)self.lb_percent = QLabel()layout.addRow("进度显示", self.lb_percent)self.lb_result = QLabel()layout.addRow("结果显示", self.lb_result)self.lb_result_state = QLabel()layout.addRow("结果状态", self.lb_result_state)btn_send = QPushButton("发送")layout.addRow(btn_send)btn_cancel = QPushButton("取消执行")layout.addRow(btn_cancel)btn_send.clicked.connect(self.click_send)btn_cancel.clicked.connect(self.click_cancel)# 创建Action clientaction_name = "/hello/action"self.client = ActionClient(action_name, CountNumberAction)self.handle = Nonedef click_send(self):goal = CountNumberGoal()goal.max = long(self.le_max.text())goal.duration = float(self.le_duration.text())self.handle = self.client.send_goal(goal, self.transition_cb, self.feedback_cb)def transition_cb(self, handle):if not isinstance(handle, ClientGoalHandle):returncomm_state = handle.get_comm_state()if comm_state == CommState.ACTIVE:print "ACTIVE"elif comm_state == CommState.DONE:# 干完活后的回调state = handle.get_terminal_state()if state == TerminalState.ABORTED:# sever中断self.lb_result_state.setText("sever中断")elif state == TerminalState.PREEMPTED:# client取消self.lb_result_state.setText("client取消")elif state == TerminalState.REJECTED:# client数据校验未通过self.lb_result_state.setText("client数据校验未通过")elif state == TerminalState.SUCCEEDED:# SUCCEEDEDself.lb_result_state.setText("SUCCEEDED")result = handle.get_result()if not isinstance(result, CountNumberResult):returnself.lb_result.setText(str(result.count))def feedback_cb(self, handle, feedback):if not isinstance(handle, ClientGoalHandle):returnif not isinstance(feedback, CountNumberFeedback):returnself.lb_percent.setText(str(feedback.percent))def click_cancel(self):print "cancel"if not isinstance(self.handle, ClientGoalHandle):returnself.handle.cancel()# def on_update(self):#     # qt刷新#     self.update()##     # 判断用户终止操作#     if rospy.is_shutdown():#         self.close()

ActionServerWindow.py

#! /usr/bin/env python
# coding: utf-8
from PyQt5.QtWidgets import QWidget, QTableView, QFormLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtGui import QStandardItemModel,QStandardItem
from PyQt5.QtCore import QTimer,pyqtSignal,Qt
from actionlib import ActionServer,ServerGoalHandle
from demo_actions.msg import CountNumberAction,CountNumberGoal,CountNumberFeedback,CountNumberResult
import rospy
import threadingclass ActionServerWindow(QWidget):# 创建信号_update_signal = pyqtSignal()def __init__(self):super(ActionServerWindow, self).__init__()print threading.current_thread().name# 自定义 timer, 负责页面更新update_timer = QTimer(self)update_timer.setInterval(16)update_timer.start()# timer事件回调update_timer.timeout.connect(self.on_update)# 设置titleself.setWindowTitle("")layout = QFormLayout()self.setLayout(layout)self.table = QTableView()layout.addRow(self.table)self.model = QStandardItemModel()self.table.setModel(self.model)# 为表格设计两个列self.model.setColumnCount(2)self.model.setHeaderData(0, Qt.Horizontal, "任务ID")self.model.setHeaderData(1, Qt.Horizontal, "终止操作")# 存放用户操作的数据self.map = {}# 绑定事件信号# btn = QPushButton("")# btn.clicked.connect(self.click)self._update_signal.connect(self.update_table)# 新建Action Serveraction_name = "/hello/action"self.server = ActionServer(action_name, CountNumberAction, self.goal_cb, self.cancel_cb, False)self.server.start()def goal_cb(self, handle):print threading.current_thread().nameif not isinstance(handle, ServerGoalHandle):return# UI上需要显示 任务id = handle.get_goal_id().idself.map[id] = {"id": id,"isCanceled": False,"isAborted": False}# 在子线程中发送信号,调用ui更新self._update_signal.emit()# self.update_table()# 开启子线程,去执行任务thread = threading.Thread(target=self.do_goal, args=(handle,))thread.start()def update_table(self):# model清理self.model.clear()row = 0# 循环便利数据,将数据显示到table上for key in self.map.keys():value = self.map[key]self.model.setItem(row, 0, QStandardItem(value["id"]))self.model.setItem(row, 1, QStandardItem("xxxxxxxxxxxxxx"))btn = QPushButton("终止操作")btn.clicked.connect(lambda: self.do_aborted(value["id"]))self.table.setIndexWidget(self.model.index(row, 1), btn)row += 1def do_aborted(self, id):print "do_aborted: {}".format(id)# 修改记录的数据self.map[id]["isAborted"] = Truedef do_goal(self, handle):if not isinstance(handle, ServerGoalHandle):return# server接收到client goal指令id = handle.get_goal_id().idgoal = handle.get_goal()if not isinstance(goal, CountNumberGoal):# 数据不符合要求handle.set_rejected()return# 1. 数据校验# 2. 完成业务# 3. 发布进度# 4. 发布结果# 5. 取消操作# 6. 中断操作# 1. 数据校验max = goal.maxduration = goal.durationif max < 0 or duration < 0:handle.set_rejected()return# 数据校验成功后,手动更改状态为 Activehandle.set_accepted()count = 0while not rospy.is_shutdown() and count < max:# 5. 取消操作:if self.map[id]["isCanceled"]:break# 6. 中断操作if self.map[id]["isAborted"]:break# 3. 发布进度feedback = CountNumberFeedback()feedback.percent = count * 100.0 / maxhandle.publish_feedback(feedback)# print feedback.percent# 2. 完成业务count += 1rospy.sleep(duration)# 4. 发布结果result = CountNumberResult()result.count = countif self.map[id]["isAborted"]:handle.set_aborted(result)elif self.map[id]["isCanceled"]:handle.set_canceled(result)else:handle.set_succeeded(result)# 清理数据del self.map[id]# 更新UIself._update_signal.emit()def cancel_cb(self, handle):if not isinstance(handle, ServerGoalHandle):return# 接收到取消的逻辑id = handle.get_goal_id().id# 设置数据self.map[id]["isCanceled"] = Truedef on_update(self):# qt刷新self.update()# 判断用户终止操作if rospy.is_shutdown():self.close()

gui_action_client.py

#! /usr/bin/env python
# coding: utf-8import rospy
from PyQt5.QtWidgets import *
import sys
from ActionClientWindow import ActionClientWindowif __name__ == '__main__':# 创建nodenode_name = "gui_action_client_node"rospy.init_node(node_name, anonymous=True)app = QApplication(sys.argv)window = ActionClientWindow()window.show()sys.exit(app.exec_())

gui_action_server.py

#! /usr/bin/env python
# coding: utf-8import rospy
from PyQt5.QtWidgets import *
import sys
from ActionServerWindow import ActionServerWindowif __name__ == '__main__':# 创建nodenode_name = "gui_action_server_node"rospy.init_node(node_name)app = QApplication(sys.argv)window = ActionServerWindow()window.show()sys.exit(app.exec_())

二.c++版

1.非ui版

1.1client.cpp

//
// Created by wt on 2020/7/6.
//
#include <iostream>
#include <ros/ros.h>
#include <actionlib/client/action_client.h>
#include <demo_actions/CountNumberAction.h>
#include <thread>using namespace std;//状态改变
void transition_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle) {//    ROS_INFO_STREAM("transition cb");const actionlib::CommState &commState = goalHandle.getCommState();//等待if (commState == actionlib::CommState::PENDING) {ROS_INFO_STREAM("pending");} else if (commState == actionlib::CommState::ACTIVE) {//激活ROS_INFO_STREAM("active");} else if (commState == actionlib::CommState::DONE) {//结束const actionlib::TerminalState &state = goalHandle.getTerminalState();if (state == actionlib::TerminalState::REJECTED) {ROS_INFO_STREAM("reject");} else if (state == actionlib::TerminalState::PREEMPTED) {ROS_INFO_STREAM("preempt");} else if (state == actionlib::TerminalState::ABORTED) {ROS_INFO_STREAM("aborted "<<goalHandle.getResult()->count);} else if (state == actionlib::TerminalState::SUCCEEDED) {ROS_INFO_STREAM("succeed "<<goalHandle.getResult()->count);} else if (state == actionlib::TerminalState::LOST) {//服务端没有做任何处理ROS_INFO_STREAM("lost");}}}//进度回调
void feedback_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle,const demo_actions::CountNumberFeedback::ConstPtr &feedback) {ROS_INFO_STREAM("feedback "<<feedback->percent);ROS_INFO_STREAM("feedback "<<this_thread::get_id());
}int main(int argc, char *argv[]) {ROS_INFO_STREAM("main "<<this_thread::get_id());//节点名string nodeName = "action_client";//初始化节点ros::init(argc, argv, nodeName,ros::init_options::AnonymousName);//创建节点ros::NodeHandle node;/*-------------------------- asy --------------------------*/ros::AsyncSpinner spinner(1);spinner.start();/*-------------------------- action --------------------------*///action名字string actionName = "action_gui";actionlib::ActionClient<demo_actions::CountNumberAction> client(node, actionName);//等到服务开启(阻塞线程 一直查看服务是否已经开启)client.waitForActionServerToStart();//目标demo_actions::CountNumberGoal goal;goal.max = 10;goal.duration = 1;//发送目标actionlib::ActionClient<demo_actions::CountNumberAction>::GoalHandle handle =client.sendGoal(goal,transition_cb, feedback_cb);/*-------------------------- 取消 --------------------------*/ros::Rate rate(0.3);rate.sleep();
//    handle.cancel();
//    client.cancelAllGoals();//客户端不要停下来ros::waitForShutdown();return 0;
}

1.2 server.cpp

//
// Created by wt on 2020/7/6.
//
#include <iostream>
#include <ros/ros.h>
#include <actionlib/server/action_server.h>
#include <demo_actions/CountNumberAction.h>
#include <thread>using namespace std;
//被取消
bool isCancel = false;void done_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle);void done_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle) {//2.处理long max = goalHandle.getGoal()->max;double duration = goalHandle.getGoal()->duration;ros::Rate rate(duration);//结果demo_actions::CountNumberResult result;for (int i = 1; i < max; ++i) {/*-------------------------- 中断状态 --------------------------*/
//        if(i==7){
//            //中断
//            result.count = i;
//            goalHandle.setAborted(result);
//            return;
//        }/*-------------------------- 取消 --------------------------*/if(isCancel){result.count = i;goalHandle.setCanceled(result);return;}//进度demo_actions::CountNumberFeedback feedback;feedback.percent = (double)i/max;//回调进度goalHandle.publishFeedback(feedback);//睡眠rate.sleep();}/*-------------------------- 成功状态 --------------------------*///成功result.count = max;goalHandle.setSucceeded(result);
}//收到目标回调
void goal_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle){ROS_INFO_STREAM("goal cb "<<this_thread::get_id());ROS_INFO_STREAM("receive goal");//拒绝
//    goalHandle.setRejected();//1.决定是否要接受任务goalHandle.setAccepted();//开启新线程new std::thread(done_cb,goalHandle);
}//收到取消指令
void cancel_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle){ROS_INFO_STREAM("cancel goal");goalHandle.getGoalID().id;isCancel = true;
}
int main(int argc,char *argv[]){ROS_INFO_STREAM("thread "<<this_thread::get_id());//节点名string nodeName = "action_server";//初始化节点ros::init(argc,argv,nodeName);//创建节点ros::NodeHandle node;/*-------------------------- action --------------------------*///action名字string actionName = "action_gui";actionlib::ActionServer<demo_actions::CountNumberAction> server(node,actionName,goal_cb,cancel_cb, false);//开启serverserver.start();//事件轮询ros::spin();return 0;
}

ui版

MainWindowClient.h

//
// Created by wt on 2020/7/6.
//#ifndef DEMO_USE_ACTION_NEW_MAINWINDOWCLIENT_H
#define DEMO_USE_ACTION_NEW_MAINWINDOWCLIENT_H#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QFormLayout>
#include <QLabel>
#include <actionlib/client/action_client.h>
#include <demo_actions/CountNumberAction.h>class MainWindowClient: public QWidget {
private:QFormLayout layout;QLineEdit maxEdit;QLineEdit durationEdit;QLabel feedLabel;QLabel activeLabel;QLabel doneLabel;QPushButton sendBtn;QPushButton preemptBtn;//clientactionlib::ActionClient<demo_actions::CountNumberAction> *client;//发送之后的handleactionlib::ActionClient<demo_actions::CountNumberAction>::GoalHandle handle;
public:MainWindowClient(ros::NodeHandle node,QWidget* parent = Q_NULLPTR);~MainWindowClient();//发送void send();//取消任务void cancel();//结果回调void transition_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle);//进度回调void feedback_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle, const demo_actions::CountNumberFeedback::ConstPtr &feedback);};#endif //DEMO_USE_ACTION_NEW_MAINWINDOWCLIENT_H

MainWindowClient.cpp

//
// Created by wt on 2020/7/6.
//#include "MainWindowClient.h"MainWindowClient::MainWindowClient(ros::NodeHandle node, QWidget *parent) : QWidget(parent), sendBtn("发送"),preemptBtn("取消") {//设置布局setLayout(&layout);//默认值maxEdit.setText("10");durationEdit.setText("1");//添加控件layout.addRow("max:", &maxEdit);layout.addRow("duration:", &durationEdit);layout.addRow("进度:", &feedLabel);layout.addRow("激活状态:", &activeLabel);layout.addRow("完成状态:", &doneLabel);layout.addRow("", &sendBtn);layout.addRow("", &preemptBtn);//创建clientclient = new actionlib::ActionClient<demo_actions::CountNumberAction>(node, "action_gui");//阻塞当前线程  服务是否连接上
//    client->waitForActionServerToStart();//信号和槽connect(&sendBtn, &QPushButton::clicked, this, &MainWindowClient::send);connect(&preemptBtn, &QPushButton::clicked, this, &MainWindowClient::cancel);
}MainWindowClient::~MainWindowClient() {}void MainWindowClient::send() {//发送目标demo_actions::CountNumberGoal goal;goal.max = maxEdit.text().toInt();goal.duration = durationEdit.text().toDouble();//一定要保存成员变量handle = client->sendGoal(goal, boost::bind(&MainWindowClient::transition_cb, this, _1),boost::bind(&MainWindowClient::feedback_cb, this, _1, _2));
}void MainWindowClient::transition_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle) {const actionlib::CommState &comState = goalHandle.getCommState();if (comState == actionlib::CommState::ACTIVE) {activeLabel.setText("激活");} else if (comState == actionlib::CommState::DONE) {const actionlib::TerminalState &terState = goalHandle.getTerminalState();if (terState == actionlib::TerminalState::REJECTED) {doneLabel.setText("拒绝");} else if (terState == actionlib::TerminalState::ABORTED) {doneLabel.setText("中止");} else if (terState == actionlib::TerminalState::PREEMPTED) {doneLabel.setText("取消");} else if (terState == actionlib::TerminalState::SUCCEEDED) {doneLabel.setText("成功");}} else if (comState == actionlib::CommState::PENDING) {activeLabel.setText("等待");}
}void MainWindowClient::feedback_cb(actionlib::ClientGoalHandle<demo_actions::CountNumberAction> goalHandle,const demo_actions::CountNumberFeedback::ConstPtr &feedback) {feedLabel.setText(QString::number(feedback->percent));
}void MainWindowClient::cancel() {handle.cancel();
}

client_gui.cpp

//
// Created by wt on 2020/7/6.
//
#include <iostream>
#include <ros/ros.h>
#include <QApplication>
#include "MainWindowClient.h"
using namespace std;int main(int argc,char *argv[]){//节点名string nodeName = "action_client";//初始化节点ros::init(argc,argv,nodeName,ros::init_options::AnonymousName);//创建节点ros::NodeHandle node;/*-------------------------- asy --------------------------*/ros::AsyncSpinner spinner(1);spinner.start();/*-------------------------- qt --------------------------*/QApplication app(argc,argv);MainWindowClient w(node);w.show();return QApplication::exec();
}

MainWindowServer.h

//
// Created by wt on 2020/7/6.
//#ifndef DEMO_USE_ACTION_NEW_MAINWINDOWSERVER_H
#define DEMO_USE_ACTION_NEW_MAINWINDOWSERVER_H#include <QWidget>
#include <actionlib/server/action_server.h>
#include <demo_actions/CountNumberAction.h>
#include <thread>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTableView>
#include <iostream>
#include <QStandardItemModel>using namespace std;struct Goal {string id;//是否被中止bool isAborted = false;//是否被取消bool isCanceled = false;
};class MainWindowServer : public QWidget {
Q_OBJECT
private://serveractionlib::ActionServer<demo_actions::CountNumberAction> *server;//布局QVBoxLayout layout;QPushButton aboredAllBtn;QTableView tableView;//modelQStandardItemModel model;//所有的任务 map<id,task>map<string, Goal> goals;//task id 是否被取消  是否被中止public:MainWindowServer(ros::NodeHandle node, QWidget *parent = Q_NULLPTR);~MainWindowServer();//收到目标回调void goal_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle);//取消void cancel_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle);//处理目标的方法void done_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle);//更新列表void updateTableView();
signals://更新ui信号void updateUI();
};#endif //DEMO_USE_ACTION_NEW_MAINWINDOWSERVER_H

MainWindowServer.cpp

//
// Created by wt on 2020/7/6.
//#include "MainWindowServer.h"MainWindowServer::MainWindowServer(ros::NodeHandle node, QWidget *parent) : QWidget(parent), aboredAllBtn("中止") {//创建serverserver = new actionlib::ActionServer<demo_actions::CountNumberAction>(node, "action_gui",boost::bind(&MainWindowServer::goal_cb, this,_1),boost::bind(&MainWindowServer::cancel_cb,this, _1), false);server->start();/*-------------------------- 初始化ui --------------------------*/setLayout(&layout);layout.addWidget(&aboredAllBtn);layout.addWidget(&tableView);//设置modeltableView.setModel(&model);//表头model.setHorizontalHeaderItem(0, new QStandardItem("ID"));model.setHorizontalHeaderItem(1, new QStandardItem("操作"));/*-------------------------- 绑定信号和槽 --------------------------*/connect(this, &MainWindowServer::updateUI, this, &MainWindowServer::updateTableView);connect(&aboredAllBtn,&QPushButton::clicked,[this]{//把所有的任务的isAborted变成truefor (auto i = goals.begin(); i != goals.end(); ++i) {i->second.isAborted = true;}});
}void MainWindowServer::done_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle) {//获取当前任务状态,是否被取消string id = goalHandle.getGoalID().id;long max = goalHandle.getGoal()->max;double duration = goalHandle.getGoal()->duration;ros::Rate rate(duration);//结果demo_actions::CountNumberResult result;for (int i = 1; i < max; ++i) {/*-------------------------- 中断状态 --------------------------*/if(goals[id].isAborted){//中断result.count = i;goalHandle.setAborted(result);//删除当前任务goals.erase(id);// 再更新uiemit updateUI();return;}/*-------------------------- 取消 --------------------------*/if(goals[id].isCanceled){result.count = i;goalHandle.setCanceled(result);//删除当前任务goals.erase(id);// 再更新uiemit updateUI();return;}//进度demo_actions::CountNumberFeedback feedback;feedback.percent = (double) i / max;//回调进度goalHandle.publishFeedback(feedback);//睡眠rate.sleep();}/*-------------------------- 成功状态 --------------------------*///成功result.count = max;goalHandle.setSucceeded(result);//删除当前任务goals.erase(id);// 再更新uiemit updateUI();
}void MainWindowServer::goal_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle) {ROS_INFO_STREAM("goal cb  " << this_thread::get_id());//1.决定是否要接受任务goalHandle.setAccepted();/*-------------------------- 添加到任务map中,更新界面 --------------------------*/Goal goal;string id = goalHandle.getGoalID().id;goal.id = id;//添加到mapgoals.insert({id, goal});//更新界面(主线程更新) 在主线程中更新//发送更新信号emit updateUI();
//    updateTableView();//开启线程处理new std::thread(&MainWindowServer::done_cb, this, goalHandle);
}//取消
void MainWindowServer::cancel_cb(actionlib::ServerGoalHandle<demo_actions::CountNumberAction> goalHandle) {//获取任务idstring id = goalHandle.getGoalID().id;//停下里任务,修改当前任务的isCanceled状态goals[id].isCanceled = true;
}MainWindowServer::~MainWindowServer() {}//子线程更新界面就会出现问题
void MainWindowServer::updateTableView() {//清理列表model.clear();//表头model.setHorizontalHeaderItem(0, new QStandardItem("ID"));model.setHorizontalHeaderItem(1, new QStandardItem("操作"));int row = 0;//把所有的任务展示出来for (auto i = goals.begin(); i != goals.end(); ++i) {string id = i->second.id;//添加一条数据model.setItem(row, 0, new QStandardItem(QString(id.c_str())));model.setItem(row, 1, new QStandardItem(""));//按钮QPushButton *btn = new QPushButton("中止");//中止点击事件connect(btn,&QPushButton::clicked,[i]{//修改中止状态i->second.isAborted = true;});//把后面的控件替换成按钮tableView.setIndexWidget(model.index(row, 1), btn);++row;}
}

server_gui.cpp

//
// Created by wt on 2020/7/6.
//
#include <iostream>
#include <ros/ros.h>
#include <QApplication>
#include "MainWindowServer.h"
#include <thread>
using namespace std;int main(int argc,char *argv[]){ROS_INFO_STREAM("main "<<this_thread::get_id());//节点名string nodeName = "action_server";//初始化节点ros::init(argc,argv,nodeName);//创建节点ros::NodeHandle node;/*-------------------------- 异步 --------------------------*/ros::AsyncSpinner spinner(1);spinner.start();/*-------------------------- qt --------------------------*/QApplication app(argc,argv);MainWindowServer w(node);w.show();return QApplication::exec();
}

ros标准版Action通讯相关推荐

  1. ros简版Action通讯SimpleAction

    ros简版Action通讯SimpleAction 一.python 1.非ui界面版 1.1client.py #!/usr/bin/env python # coding:utf-8 import ...

  2. ISA2006标准版,本地主机不能上网问题的解决一例

    今天,帮一位朋友解决ISA SERVER2006标准版本地主机不能上网的问题,中间经历了一些困难,有点意思,故写了下来,供各位参考分享. 一.安装环境: windows server 2003 sp2 ...

  3. 1月25日再次开抢!三星Galaxy S21系列标准版已多次开售即罄

    1月24日消息,在智能手机行业中,三星Galaxy S系列手机一直都是当之无愧的标杆,引领着行业的发展.只是,略显尴尬的是,集品质与科技感于一身的Galaxy S系列手机,价格相对较高,与消费者有着不 ...

  4. Rational AppScan 标准版可扩展性和二次开发能力简介

    下载:IBM® Rational® AppScan 标准版  |   Web 应用安全与 IBM Rational AppScan 工具包 获取免费的 Rational 软件工具包系列,下载更多的 R ...

  5. PayPal网站付款标准版(for PHP)

    原文:PayPal网站付款标准版(for PHP) 简单整理一下PHP项目整合PayPal支付功能. 一.表单的构建: <form method="post" name=&q ...

  6. OCS2007标准版服务器部署

    Office communications Server 2003是LCS2005的新版本,OCS优化了性能增加了新功能.OCS有两个版本,Standard Edition 和 Enterprise ...

  7. PayPal集成标准版案例(asp.net)关键源码

    Paypal国际版网站集成简易教程(一):序言 本文介绍如何在自己网站上集成paypal标准版支付,是从百度文库中找到的,要感谢下这位作者把自己的经验心得分享出来,毕竟paypal集成中文的教程非常少 ...

  8. Adobe Photoshop CS5 标准版新增功能

    转载请说明来源于"厦门SEO" 本文地址:http://www.96096.cc/Article/161770.html 国外ps教程 2010年4月12日北京时间23时,Adob ...

  9. 最新v4.2版本CRMEB商城API接口文档标准版后台(一)

    CRMEB_标准版后台 基础接口 登录页面图片数据 基本信息 Path: /adminapi/login/info Method: GET 接口描述: 请求参数 返回数据 名称 类型 是否必须 默认值 ...

最新文章

  1. Codeforces Gym 101630J Journey from Petersburg to Moscow (最短路)
  2. ansible基础-Jinja2模版 | 过滤器
  3. 贝叶斯定理、显著性检验、p值关系、分类
  4. css3 media query orientation,CSS3之media query
  5. mysql存储过程_Mysql存储过程
  6. linux sftp ssh端口分开,sftp ssh服务分离
  7. php 使用支付宝SDK报错解决
  8. mysql 登录默认实例_【MySQL案例】mysql本机登录-S失灵_mysql
  9. 通过谷歌浏览器,找到页面某个事件属于哪个js文件
  10. 如何备考系统集成项目管理工程师?
  11. linux应用程序使用aplay播放,Linux中如何解决Aplay不能播放问题
  12. linux内核学习(5)山重水复疑无路*
  13. linux版高德导航软件下载,高德导航下载2021年最新版本_高德导航2021手机版下载-太平洋下载中心...
  14. 从两幅图像的匹配点计算焦距f
  15. [Springboot]发送邮件、重置密码业务实战
  16. 【ArangoDB 介绍】
  17. Django知识点:认识Django
  18. linux限制ssh 无公网ip白名单限制 基于网段
  19. 【互联网的恩怨情仇】盘点2015年互联网十大撕逼事件
  20. GOM和GEE引擎黑屏不显示界面,装备地图怪物的解决方法

热门文章

  1. Linux下的 API Hook
  2. zabbix 搭建 mysql 连接报错
  3. Generator的异步编程
  4. 如何让IOS应用从容地崩溃
  5. C#数据库类(zz)
  6. “服务器发送了一个意外的数据包。received:3,expected:20“问题的解决方法
  7. sonar+Jenkins 构建代码质量自动化分析平台
  8. Python获取照片信息
  9. jsp mysql事务锁,JSP操作数据库的事务回滚
  10. mysql 日志大小_查看mysql日志文件大小和数据库大小