NavigatingSkillHelper.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @author Fabian Reister ( fabian dot reister at kit dot edu )
17 * @date 2023
18 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19 * GNU General Public License
20 */
21
22#pragma once
23
24#include <chrono>
25#include <experimental/memory>
26#include <future>
27#include <mutex>
28#include <optional>
29
33
37
44#include <armarx/navigation/skills/aron/NavigatingSkillParams.aron.generated.h>
45
47{
48 namespace detail
49 {
50 /// Time to wait between two attempts to plan a path to the goal.
53
54 /// Granularity at which `shouldTerminate` is evaluated while waiting.
57 } // namespace detail
58
59 /// True if `f` holds a result (or has none to wait for). A deferred task is started here.
60 template <typename R>
61 bool
62 is_ready(std::future<R> const& f)
63 {
64 if (!f.valid())
65 {
66 return true;
67 }
68 std::future_status status = f.wait_for(std::chrono::seconds(0));
69 if (status == std::future_status::ready)
70 {
71 return true;
72 }
73 else if (status == std::future_status::deferred)
74 {
75 // if the task has not been started --> start the task
76 f.wait();
77 return true;
78 }
79 else
80 {
81 return false;
82 }
83 }
84
86 {
87 public:
94
102
103 NavigatingSkillHelper(const Properties& properties, const Services& srv);
104
105 void init(const arondto::NavigatingSkillParams& params, const std::string& id);
106
107 std::optional<client::Navigator>& getNavigator();
108
109 /// Issues the navigation command and waits for the navigator to stop, polling
110 /// `shouldTerminate` meanwhile. If the global planner fails and the `retryTimeout`
111 /// budget (measured from the first attempt) is not exhausted yet, the command is
112 /// re-issued after a short pause. Returns the stop event of the last attempt.
113 ///
114 /// Templated rather than taking `std::function`s: the navigation commands can throw
115 /// (e.g. Ice exceptions), which `std::function` cannot express and `-Wnoexcept` rejects.
116 template <typename IssueNavigationCommandT, typename ShouldTerminateT>
118 moveAndWaitForStop(IssueNavigationCommandT&& issueNavigationCommand,
119 ShouldTerminateT&& shouldTerminate)
120 {
121 ARMARX_CHECK(navigator.has_value());
122
123 const auto deadline = armarx::Clock::Now() + retryTimeout_;
124
125 while (true)
126 {
127 // Re-issuing is safe: every `client::Navigator` move command resets the stop
128 // event before forwarding the request, and a planning failure leaves the robot
129 // in place.
130 issueNavigationCommand();
131
132 auto future =
133 std::async(std::launch::async, [this]() { return navigator->waitForStop(); });
134 while (not is_ready(future))
135 {
136 if (shouldTerminate())
137 {
138 ARMARX_INFO << "Skill should terminate. Stopping the navigator.";
139 navigator->stop();
140 break;
141 }
143 }
144
145 client::StopEvent se = future.get();
146
147 if (not se.isGlobalPlanningFailedEvent() or shouldTerminate())
148 {
149 return se;
150 }
151
152 const auto now = armarx::Clock::Now();
153 if (now >= deadline)
154 {
155 ARMARX_WARNING << "Global planning failed and the retry budget of "
156 << retryTimeout_.toSecondsDouble()
157 << "s is exhausted. Giving up.";
158 return se;
159 }
160
161 ARMARX_INFO << "Global planning failed ("
162 << se.toGlobalPlanningFailedEvent().message << "). Retrying, "
163 << (deadline - now).toSecondsDouble() << "s left.";
164
165 // Wait for the retry interval in small slices so a stop request does not have
166 // to wait out the full interval.
167 const auto remaining = deadline - now;
168 const auto waitBudget =
169 remaining < detail::RetryInterval ? remaining : detail::RetryInterval;
170 const auto waitStart = armarx::Clock::Now();
171 while (armarx::Clock::Now() - waitStart < waitBudget)
172 {
173 if (shouldTerminate())
174 {
175 return se;
176 }
177 const auto remainingWait = waitBudget - (armarx::Clock::Now() - waitStart);
179 ? remainingWait
181 }
182 }
183 }
184
185 static arondto::NavigatingSkillParams DefaultSkillDescription();
186
187 private:
188 Properties properties_;
189 std::optional<Services> srv_;
190
191 /// Time budget for re-planning after a failed global plan. Set in `init()`.
192 armarx::core::time::Duration retryTimeout_{};
193
194 // will be initialized in "init()"
195 std::optional<client::MemorySubscriber> memorySubscriber;
196 client::NavigatorHandlePtr iceNavigator;
197 std::optional<client::Navigator> navigator;
198 };
199
200} // namespace armarx::navigation::skills
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
Definition Clock.cpp:99
The memory name system (MNS) client.
Represents a duration.
Definition Duration.h:17
static Duration Seconds(std::int64_t seconds)
Constructs a duration in seconds.
Definition Duration.cpp:72
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
core::GlobalPlanningFailedEvent & toGlobalPlanningFailedEvent()
Definition Navigator.h:107
std::optional< client::Navigator > & getNavigator()
NavigatingSkillHelper(const Properties &properties, const Services &srv)
void init(const arondto::NavigatingSkillParams &params, const std::string &id)
static arondto::NavigatingSkillParams DefaultSkillDescription()
client::StopEvent moveAndWaitForStop(IssueNavigationCommandT &&issueNavigationCommand, ShouldTerminateT &&shouldTerminate)
Issues the navigation command and waits for the navigator to stop, polling shouldTerminate meanwhile.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
std::unique_ptr< core::NavigatorInterface > NavigatorHandlePtr
const armarx::core::time::Duration PollInterval
Granularity at which shouldTerminate is evaluated while waiting.
const armarx::core::time::Duration RetryInterval
Time to wait between two attempts to plan a path to the goal.
bool is_ready(std::future< R > const &f)
True if f holds a result (or has none to wait for). A deferred task is started here.
std::experimental::observer_ptr< std::mutex > safetyGuardParamsMutex
std::experimental::observer_ptr< safety_guard::LaserBasedProximityParams > safetyGuardParams