queued_action_server.h
Go to the documentation of this file.
1 #pragma once
2 
6 
9 #include <ros/ros.h>
10 
11 #include <chrono>
12 #include <condition_variable>
13 #include <memory>
14 #include <mutex>
15 #include <queue>
16 #include <string>
17 #include <thread>
18 
25 template <class ActionSpec>
27 {
28 public:
29  ACTION_DEFINITION(ActionSpec)
30  using GoalHandle = typename actionlib::ActionServer<ActionSpec>::GoalHandle;
31 
32  using ExecuteCallback = ensenso::std::function<void(GoalConstPtr const&)>;
33 
34 public:
36  bool autoStart = false)
37  : nh(nh), name(name), callback(callback)
38  {
39  actionServer = ensenso::std::make_unique<actionlib::ActionServer<ActionSpec>>(nh, name, false);
40  actionServer->registerGoalCallback(ensenso::std::bind(&QueuedActionServer::onGoalReceived, this, _1));
41  actionServer->registerCancelCallback(ensenso::std::bind(&QueuedActionServer::onCancelReceived, this, _1));
42 
43  if (autoStart)
44  {
45  start();
46  }
47  }
48 
50  {
51  shutdown();
52  }
53 
54  void start()
55  {
56  actionServer->start();
57  loopThread = std::thread([this] { loop(); }); // NOLINT
58  }
59 
61  {
62  return preemptRequested;
63  }
64 
65  void setSucceeded(Result result = Result())
66  {
67  std::lock_guard<std::mutex> lock(mutex);
68 
69  ENSENSO_DEBUG_NAMED(getLoggerName(), "Setting the current goal as succeeded.");
70  currentGoal.setSucceeded(result);
71  }
72 
73  void setAborted(Result result = Result())
74  {
75  std::lock_guard<std::mutex> lock(mutex);
76 
77  ENSENSO_DEBUG_NAMED(getLoggerName(), "Setting the current goal as aborted.");
78  currentGoal.setAborted(result);
79  }
80 
81  void setPreempted(Result result = Result())
82  {
83  std::lock_guard<std::mutex> lock(mutex);
84 
85  ENSENSO_DEBUG_NAMED(getLoggerName(), "Setting the current goal as canceled.");
86  currentGoal.setCanceled(result);
87  }
88 
89  void publishFeedback(FeedbackConstPtr const& feedback)
90  {
91  currentGoal.publishFeedback(*feedback);
92  }
93 
94  void publishFeedback(Feedback const& feedback)
95  {
96  currentGoal.publishFeedback(feedback);
97  }
98 
99 private:
100  void shutdown()
101  {
102  if (loopThread.joinable())
103  {
104  shutdownRequested = true;
105  loopThread.join();
106  }
107  }
108 
109  std::string getLoggerName()
110  {
111  return ros::this_node::getName() + "/QueuedActionServer/" + name;
112  }
113 
115  {
116  ENSENSO_DEBUG_NAMED(getLoggerName(), "Received a new goal.");
117  std::lock_guard<std::mutex> lock(mutex);
118 
119  goalQueue.push(goal);
120  loopCondition.notify_one();
121  }
122 
124  {
125  ENSENSO_DEBUG_NAMED(getLoggerName(), "Received a cancel request.");
126  std::lock_guard<std::mutex> lock(mutex);
127 
128  if (goal == currentGoal)
129  {
130  // The goal is already being executed. We set the preemption flag and the user is responsible for polling it with
131  // isPreemptRequested() and canceling his action handler with setPreempted().
132  preemptRequested = true;
133  }
134  else
135  {
136  // The goal is in the queue. We cancel it and ignore it later, when we extract new goals from the queue.
137  goal.setCanceled(Result(), "Goal was canceled by the user.");
138  }
139  }
140 
141  void loop()
142  {
143  std::unique_lock<std::mutex> lock(mutex);
144 
145  while (nh.ok() && !shutdownRequested)
146  {
147  if (!goalQueue.empty())
148  {
149  currentGoal = goalQueue.front();
150  goalQueue.pop();
151 
152  if (!currentGoal.getGoal())
153  {
154  continue;
155  }
156  else if (currentGoal.getGoalStatus().status == actionlib_msgs::GoalStatus::RECALLING)
157  {
158  currentGoal.setCanceled(Result(), "Goal was canceled by the user.");
159  }
160  else if (currentGoal.getGoalStatus().status != actionlib_msgs::GoalStatus::PENDING)
161  {
162  // The goal might have already been canceled by the cancel callback above.
163  continue;
164  }
165  else
166  {
167  ENSENSO_DEBUG_NAMED(getLoggerName(), "Accepting a new goal.");
168  currentGoal.setAccepted();
169 
170  preemptRequested = false;
171 
172  lock.unlock();
173  callback(currentGoal.getGoal());
174  lock.lock();
175 
176  if (currentGoal.getGoalStatus().status == actionlib_msgs::GoalStatus::ACTIVE ||
177  currentGoal.getGoalStatus().status == actionlib_msgs::GoalStatus::PREEMPTING)
178  {
180  "Your action handler did not set the goal to a terminal state. Aborting it for now.");
181  setAborted(Result());
182  }
183  }
184  }
185 
186  loopCondition.wait_for(lock, std::chrono::milliseconds(100));
187  }
188  }
189 
190 private:
192  std::string name;
193  std::unique_ptr<actionlib::ActionServer<ActionSpec>> actionServer;
194 
195  std::thread loopThread;
196 
197  std::mutex mutex;
198  std::condition_variable loopCondition;
199 
201 
203  std::queue<GoalHandle> goalQueue;
204 
205  // Whether the user canceled the goal that is currently being executed.
206  bool preemptRequested = false;
207 
208  volatile bool shutdownRequested = false;
209 };
typename actionlib::ActionServer< ActionSpec >::GoalHandle GoalHandle
volatile bool shutdownRequested
void onCancelReceived(GoalHandle goal)
ensenso::ros::NodeHandle nh
ROSCPP_DECL const std::string & getName()
ExecuteCallback callback
void publishFeedback(Feedback const &feedback)
std::queue< GoalHandle > goalQueue
std::unique_ptr< actionlib::ActionServer< ActionSpec > > actionServer
void setPreempted(Result result=Result())
void ENSENSO_DEBUG_NAMED(std::string name, T... args)
Definition: logging.h:87
void setAborted(Result result=Result())
void ENSENSO_WARN_NAMED(std::string name, T... args)
Definition: logging.h:223
::ros::NodeHandle NodeHandle
Definition: node.h:214
std::condition_variable loopCondition
#define ACTION_DEFINITION(ActionSpec)
void onGoalReceived(GoalHandle goal)
ensenso::std::function< void(GoalConstPtr const &)> ExecuteCallback
void publishFeedback(FeedbackConstPtr const &feedback)
void setSucceeded(Result result=Result())


ensenso_camera
Author(s): Ensenso
autogenerated on Sat Jun 3 2023 02:17:04