Scheduler.cpp
Go to the documentation of this file.
1#include "Scheduler.h"
2
3#include <map>
4#include <mutex>
5#include <optional>
6#include <string>
7#include <vector>
8
9#include <SimoxUtility/color/Color.h>
10#include <VirtualRobot/XML/RobotIO.h>
11
19
22
26
28{
29
30 Scheduler::Scheduler(const InjectedServices& srv, const Params& params) :
31 srv(srv), params(params)
32 {
33 init();
34 }
35
36 void
37 Scheduler::init()
38 {
39 robot_ = srv.virtualRobotReader->getRobotWaiting(
40 params.robotName,
42 VirtualRobot::RobotIO::RobotDescription::eStructure);
43 ARMARX_CHECK_NOT_NULL(robot_) << params.robotName;
44
45 auto const infinite = armarx::Duration::MilliSeconds(-1);
46 bool constexpr keepInQueue = true;
47
48 _idleGazeTarget.name = "idle";
49 _idleGazeTarget.position = FramedPosition(
50 params.defaultTarget, robot_->getRootNode()->getName(), robot_->getName());
51 _idleGazeTarget.priority =
53 _idleGazeTarget.duration = infinite;
54 _idleGazeTarget.keepInQueue = keepInQueue;
55 _idleGazeTarget.creationTimestamp = DateTime::Now();
56 _idleGazeTarget.activationTimestamp = DateTime::Now();
57
58 srv.controllerHandler->createController();
59 resetRequestedTargets();
60
62 srv.controllerHandler->activateController();
63
64 if (task and task->isRunning())
65 {
66 task->stop();
67 }
68
69 task = new armarx::SimplePeriodicTask<>([&]() { scheduleNextTarget(); }, 100);
70 task->start();
71
72 if (visualizationTask and visualizationTask->isRunning())
73 {
74 visualizationTask->stop();
75 }
76
77 visualizationTask =
78 new armarx::SimplePeriodicTask<>([&]() { visualizeActiveTarget(); }, 100);
79 visualizationTask->start();
80 }
81
82 void
84 {
85 _clearGazeTargets.store(true);
86 }
87
88 void
89 Scheduler::scheduleNextTarget()
90 {
91 bool constexpr debugQueue = false;
92
93 auto debug = [&](std::string const& step)
94 {
95 if (not debugQueue)
96 {
97 return;
98 }
99
100 int i = 0;
101 ARMARX_INFO << "Gaze target queue contents (" << step << "): ";
102 for (auto const& targetInQueue : requestedTargets)
103 {
104 ARMARX_INFO << " #" << i++ << " " << targetInQueue;
105 }
106 };
107
108 if (requestedTargets.size() == 0)
109 {
110 ARMARX_WARNING << "No gaze targets in queue. At least the idle gaze target should be "
111 "in the queue.";
112 return;
113 }
114
115 // Get new gaze targets and copy them into this thread.
116 std::vector<gaze_targets::GazeTarget> newGazeTargets = [&]
117 {
118 std::scoped_lock<std::mutex> targetLock(_newGazeTargetsMutex);
119 std::vector<gaze_targets::GazeTarget> copy = _newGazeTargets;
120 _newGazeTargets.clear();
121 return copy;
122 }();
123
124 // Get the reference timestamp for this scheduling step.
125 DateTime const scheduleStep = Clock::Now();
126
127 debug("before filter");
128
129 // First, filter all requested targets that have expired. Consider all expired (except for
130 // idle gaze target) if clearing all gaze targets was requested.
131 if (_clearGazeTargets.load())
132 {
133 resetRequestedTargets();
134 _clearGazeTargets.store(false);
135 }
136 else
137 {
138 RequestedTargets newRequestedTargets;
139
140 for (auto const& targetInQueue : requestedTargets)
141 {
142 if (not targetInQueue.isExpired(scheduleStep))
143 {
144 newRequestedTargets.emplace(targetInQueue);
145 }
146 else
147 {
148 ARMARX_INFO << "Gaze target " << targetInQueue.name << " expired.";
149 }
150 }
151
152 requestedTargets = newRequestedTargets;
153 }
154
155 debug("after filter, before insertion");
156
157 // Second, add new gaze targets, replace task level targets or existing ones with the same
158 // name if applicable.
159 for (auto const& target : newGazeTargets)
160 {
161 RequestedTargets newRequestedTargets;
162 newRequestedTargets.emplace(target);
163
164 for (auto const& targetInQueue : requestedTargets)
165 {
166 if (targetInQueue.name == target.name)
167 {
168 // Do not keep targets with the same name in the queue, since they should be
169 // overwritten with the new one.
170 }
171 else if (target.priority.attentionType ==
173 targetInQueue.priority.attentionType ==
175 not targetInQueue.keepInQueue)
176 {
177 // New target is task driven, and this target is too and should not be kept in
178 // the queue.
179 }
180 else
181 {
182 // Keep in queue.
183 newRequestedTargets.emplace(targetInQueue);
184 }
185 }
186
187 requestedTargets = newRequestedTargets;
188 }
189
190 debug("after insertion, before selection");
191
192 // Third, use the first gaze target as next gaze target. It should be the first in the
193 // queue.
194 std::optional<gaze_targets::GazeTarget> const maybeNextTarget =
195 [&]() -> std::optional<gaze_targets::GazeTarget>
196 {
197 if (requestedTargets.empty())
198 {
199 return std::nullopt;
200 }
201
202 return *requestedTargets.begin();
203 }();
204
205 if (not maybeNextTarget.has_value())
206 {
207 ARMARX_WARNING << "Could not find any gaze targets after filtering. At least the idle "
208 "gaze target was expected. Trying to recover by resetting gaze "
209 "targets queue ...";
210 _clearGazeTargets.store(true); // Try to reset gaze targets list to recover.
211 return;
212 }
213
214 auto const currentTarget = currentTargetBuffer.getUpToDateReadBuffer();
215 gaze_targets::GazeTarget const nextTarget = maybeNextTarget.value();
216
217 if (not currentTarget.has_value() or currentTarget->isExpired() or
218 nextTarget != currentTarget)
219 {
220 submitControlTarget(nextTarget);
221 }
222 }
223
224 void
225 Scheduler::submitControlTarget(gaze_targets::GazeTarget const& target)
226 {
227 ARMARX_INFO << "Scheduling " << target << " now.";
228 currentTargetBuffer.getWriteBuffer() = target;
229 currentTargetBuffer.commitWrite();
230 srv.controllerHandler->updateControllerTarget(target);
231 }
232
233 void
234 Scheduler::resetRequestedTargets()
235 {
236 ARMARX_INFO << "Resetting gaze targets queue, old requests are discarded.";
237 requestedTargets.clear();
238
239 ARMARX_INFO << "Adding idle target `" << params.defaultTarget.transpose() << "`.";
240
241 currentTargetBuffer.getWriteBuffer() = std::nullopt;
242 currentTargetBuffer.commitWrite();
243
244 requestedTargets.emplace(_idleGazeTarget);
245 }
246
247 void
248 Scheduler::visualizeActiveTarget()
249 {
250 if (not(params.visualizeTaskDrivenGazeTarget or params.visualizeStimulusDrivenTarget or
251 params.visualizeRandomEventTarget))
252 {
253 return;
254 }
255
256 std::string const activeTargetLayerName = "active_target";
257
258 auto const currentTarget = currentTargetBuffer.getUpToDateReadBuffer();
259
260 ARMARX_CHECK_NOT_NULL(srv.arviz);
261
262 bool visualize = currentTarget.has_value();
263
264 if (currentTarget.has_value())
265 {
266 switch (currentTarget->priority.attentionType)
267 {
269 visualize = params.visualizeTaskDrivenGazeTarget;
270 break;
272 visualize = params.visualizeStimulusDrivenTarget;
273 break;
275 visualize = params.visualizeRandomEventTarget;
276 break;
277 }
278 }
279
280 // Do not visualize if current gaze target has no value or if current gaze target is an
281 // random event but only stimulus- and task-driven gaze targets should be visualized.
282 if (not visualize)
283 {
284 srv.arviz->commitDeleteLayer(activeTargetLayerName);
285 return;
286 }
287
288 if (not srv.virtualRobotReader->synchronizeRobot(*robot_, armarx::Clock::Now()))
289 {
290 ARMARX_VERBOSE << "Failed to synchronize robot. Cannot visualize targets.";
291 return;
292 }
293
294 Eigen::Vector3f const globalPosition = currentTarget->position.toGlobalEigen(robot_);
295
296 std::map<gaze_targets::AttentionType, simox::Color> attentionTypeColor{
298 simox::Color::gray(128, params.targetVizAlpha)},
300 simox::Color::orange(255, params.targetVizAlpha)},
302 simox::Color::red(255, params.targetVizAlpha)}};
303
304 auto const color = attentionTypeColor.at(currentTarget->priority.attentionType);
305 Eigen::Vector3f const from =
306 robot_->getRobotNode(params.gazeOriginFrameName)->getGlobalPosition();
307 Eigen::Vector3f const direction_vec = (globalPosition - from).normalized();
308 Eigen::Vector3f const to = (globalPosition - from).norm() > 750
309 ? Eigen::Vector3f(from + (direction_vec * 700))
310 : Eigen::Vector3f(globalPosition - (direction_vec * 50));
311
312 auto l = srv.arviz->layer(activeTargetLayerName);
313 l.add(armarx::viz::Sphere("target").position(globalPosition).radius(25).color(color));
314 l.add(armarx::viz::Arrow("gaze_direction")
315 .fromTo(from + (direction_vec * 50), to)
316 .width(10)
317 .color(color));
318 srv.arviz->commit(l);
319 }
320
321 void
323 {
324 std::scoped_lock<std::mutex> targetLock(_newGazeTargetsMutex);
325 target.activationTimestamp = armarx::Clock::Now(); // TODO: This is a hack for now.
326 _newGazeTargets.push_back(target);
327 }
328
330 {
331
332 if (task->isRunning())
333 {
334 ARMARX_INFO << "Stopping task";
335 task->stop();
336 }
337
338 if (visualizationTask->isRunning())
339 {
340 ARMARX_INFO << "Stopping visualization task";
341 visualizationTask->stop();
342 }
343 }
344} // namespace armarx::view_selection::gaze_scheduler
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
static DateTime Now()
Definition DateTime.cpp:51
static DateTime Invalid()
Definition DateTime.cpp:57
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
The FramedPosition class.
Definition FramedPose.h:158
void start()
Starts the thread.
void stop()
Stops the thread.
bool isRunning() const
Retrieve running state of the thread.
void submitToQueue(gaze_targets::GazeTarget target)
Scheduler(const InjectedServices &srv, const Params &params)
Definition Scheduler.cpp:30
Business Object (BO) class of GazeTarget.
Definition GazeTarget.h:23
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
void visualize(const algorithms::Costmap &costmap, viz::Layer &layer, const std::string &name, const float zOffset)
@ RandomEvent
Random Targets with lowest priority.
@ StimulusDriven
Stimulus-Driven attention is executed when there is no Task-Driven GazeTarget.
@ TaskDriven
Task-Driven attention has highest priority.
state::Type from(Eigen::Vector3f targetPosition)
SimplePeriodicTask(Ts...) -> SimplePeriodicTask< std::function< void(void)> >
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
double norm(const Point &a)
Definition point.hpp:102
std::experimental::observer_ptr< ControllerHandlerInterface > controllerHandler
Definition Scheduler.h:39
std::experimental::observer_ptr< armem::robot_state::VirtualRobotReader > virtualRobotReader
Definition Scheduler.h:36