11#include <SimoxUtility/color/Color.h>
12#include <VirtualRobot/XML/RobotIO.h>
36 srv(srv), params(params), reachedMonitor(params.reached)
47 VirtualRobot::RobotIO::RobotDescription::eStructure);
51 bool constexpr keepInQueue =
true;
53 _idleGazeTarget.
name =
"idle";
55 params.
defaultTarget, robot_->getRootNode()->getName(), robot_->getName());
64 resetRequestedTargets();
77 if (visualizationTask and visualizationTask->
isRunning())
79 visualizationTask->
stop();
84 visualizationTask->
start();
90 _clearGazeTargets.store(
true);
94 Scheduler::scheduleNextTarget()
96 bool constexpr debugQueue =
false;
98 auto debug = [&](std::string
const& step)
106 ARMARX_INFO <<
"Gaze target queue contents (" << step <<
"): ";
107 for (
auto const& targetInQueue : requestedTargets)
109 ARMARX_INFO <<
" #" << i++ <<
" " << targetInQueue;
113 if (requestedTargets.size() == 0)
115 ARMARX_WARNING <<
"No gaze targets in queue. At least the idle gaze target should be "
121 std::vector<gaze_targets::GazeTarget> newGazeTargets = [&]
123 std::scoped_lock<std::mutex> targetLock(_newGazeTargetsMutex);
124 std::vector<gaze_targets::GazeTarget> copy = _newGazeTargets;
125 _newGazeTargets.clear();
132 debug(
"before filter");
136 if (_clearGazeTargets.load())
138 resetRequestedTargets();
139 _clearGazeTargets.store(
false);
143 RequestedTargets newRequestedTargets;
145 for (
auto const& targetInQueue : requestedTargets)
147 if (not targetInQueue.isExpired(scheduleStep))
149 newRequestedTargets.emplace(targetInQueue);
153 ARMARX_INFO <<
"Gaze target " << targetInQueue.name <<
" expired.";
154 leaveActiveSlot(targetInQueue,
"duration elapsed",
false);
158 requestedTargets = newRequestedTargets;
161 debug(
"after filter, before insertion");
165 for (
auto const& target : newGazeTargets)
167 RequestedTargets newRequestedTargets;
168 newRequestedTargets.emplace(target);
170 for (
auto const& targetInQueue : requestedTargets)
172 if (targetInQueue.name ==
target.name)
177 else if (
target.priority.attentionType ==
179 targetInQueue.priority.attentionType ==
181 not targetInQueue.keepInQueue)
189 newRequestedTargets.emplace(targetInQueue);
193 requestedTargets = newRequestedTargets;
196 debug(
"after insertion, before selection");
200 std::optional<gaze_targets::GazeTarget>
const maybeNextTarget =
201 [&]() -> std::optional<gaze_targets::GazeTarget>
203 if (requestedTargets.empty())
208 return *requestedTargets.begin();
211 if (not maybeNextTarget.has_value())
213 ARMARX_WARNING <<
"Could not find any gaze targets after filtering. At least the idle "
214 "gaze target was expected. Trying to recover by resetting gaze "
216 _clearGazeTargets.store(
true);
220 auto const currentTarget = currentTargetBuffer.getUpToDateReadBuffer();
221 gaze_targets::GazeTarget
const nextTarget = maybeNextTarget.value();
223 if (not currentTarget.has_value() or currentTarget->isExpired() or
224 nextTarget != currentTarget)
226 if (currentTarget.has_value() and currentTarget->name != nextTarget.name)
228 const bool staysQueued =
229 currentTarget->keepInQueue and
230 std::any_of(requestedTargets.begin(),
231 requestedTargets.end(),
232 [&](
const gaze_targets::GazeTarget& queued)
233 { return queued.name == currentTarget->name; });
235 leaveActiveSlot(*currentTarget,
"preempted by " + nextTarget.name, staysQueued);
238 submitControlTarget(nextTarget);
243 Scheduler::submitControlTarget(gaze_targets::GazeTarget
const& target)
248 reachedMonitor.reset(currentTargetId);
249 reachedEmitted =
false;
250 unreachableEmitted =
false;
252 currentTargetBuffer.getWriteBuffer() =
target;
253 currentTargetBuffer.commitWrite();
254 srv.controllerHandler->updateControllerTarget(target, currentTargetId);
256 if (
const std::optional<armem::MemoryID> gazeTargetID = gazeTargetIDOf(
target.name))
258 srv.statusPublisher->scheduled(*gazeTargetID);
262 std::optional<armem::MemoryID>
263 Scheduler::gazeTargetIDOf(
const std::string& targetName)
const
265 const auto it = gazeTargetIDs.find(targetName);
267 if (it == gazeTargetIDs.end())
276 Scheduler::leaveActiveSlot(
const gaze_targets::GazeTarget& target,
277 const std::string& reason,
278 const bool staysQueued)
280 const std::optional<armem::MemoryID> gazeTargetID = gazeTargetIDOf(
target.name);
282 if (not gazeTargetID.has_value())
289 srv.statusPublisher->preempted(*gazeTargetID, reason);
295 if (srv.statusPublisher->wasReached(*gazeTargetID))
297 srv.statusPublisher->released(*gazeTargetID, reason);
301 srv.statusPublisher->aborted(*gazeTargetID, reason);
304 gazeTargetIDs.erase(
target.name);
311 .lateralError =
status.lateralError};
313 reachedMonitor.update(
status.targetId, residual,
status.targetReachable);
315 const auto currentTarget = currentTargetBuffer.getUpToDateReadBuffer();
317 if (not currentTarget.has_value())
322 const std::optional<armem::MemoryID> gazeTargetID = gazeTargetIDOf(currentTarget->name);
324 if (not gazeTargetID.has_value())
329 if (not reachedEmitted and reachedMonitor.reached())
331 reachedEmitted =
true;
336 <<
" reached (angular error " << reachedMonitor.residual().angularError
337 <<
" rad, lateral error " << reachedMonitor.residual().lateralError
338 <<
" mm); controller reported at " <<
status.timestamp.timeSinceEpoch
341 srv.statusPublisher->reached(*gazeTargetID, reachedMonitor.residual());
344 if (not unreachableEmitted and not reachedEmitted and reachedMonitor.unreachable())
346 unreachableEmitted =
true;
349 <<
" cannot be reached by the controller; aborting it.";
351 srv.statusPublisher->aborted(*gazeTargetID,
352 "unreachable: the controller cannot look there");
353 gazeTargetIDs.erase(currentTarget->name);
358 Scheduler::resetRequestedTargets()
360 ARMARX_INFO <<
"Resetting gaze targets queue, old requests are discarded.";
362 for (
const auto& targetInQueue : requestedTargets)
364 leaveActiveSlot(targetInQueue,
"queue reset",
false);
367 requestedTargets.clear();
371 currentTargetBuffer.getWriteBuffer() = std::nullopt;
372 currentTargetBuffer.commitWrite();
374 requestedTargets.emplace(_idleGazeTarget);
378 Scheduler::visualizeActiveTarget()
386 std::string
const activeTargetLayerName =
"active_target";
388 auto const currentTarget = currentTargetBuffer.getUpToDateReadBuffer();
392 bool visualize = currentTarget.has_value();
394 if (currentTarget.has_value())
396 switch (currentTarget->priority.attentionType)
414 srv.arviz->commitDeleteLayer(activeTargetLayerName);
420 ARMARX_VERBOSE <<
"Failed to synchronize robot. Cannot visualize targets.";
424 Eigen::Vector3f
const globalPosition = currentTarget->position.toGlobalEigen(robot_);
426 std::map<gaze_targets::AttentionType, simox::Color> attentionTypeColor{
428 simox::Color::gray(128, params.targetVizAlpha)},
430 simox::Color::orange(255, params.targetVizAlpha)},
432 simox::Color::red(255, params.targetVizAlpha)}};
434 auto const color = attentionTypeColor.at(currentTarget->priority.attentionType);
435 Eigen::Vector3f
const from =
436 robot_->getRobotNode(params.gazeOriginFrameName)->getGlobalPosition();
437 Eigen::Vector3f
const direction_vec = (globalPosition -
from).normalized();
438 Eigen::Vector3f
const to = (globalPosition -
from).
norm() > 750
439 ? Eigen::Vector3f(from + (direction_vec * 700))
440 : Eigen::Vector3f(globalPosition - (direction_vec * 50));
442 auto l = srv.arviz->layer(activeTargetLayerName);
443 l.add(armarx::viz::Sphere(
"target").position(globalPosition).radius(25).color(color));
444 l.add(armarx::viz::Arrow(
"gaze_direction")
445 .fromTo(from + (direction_vec * 50), to)
448 srv.arviz->commit(l);
455 std::scoped_lock<std::mutex> targetLock(_newGazeTargetsMutex);
457 _newGazeTargets.push_back(target);
466 gazeTargetIDs[target.name] = gazeTargetID;
470 srv.statusPublisher->requested(gazeTargetID, target.creationTimestamp);
476 if (task->isRunning())
482 if (visualizationTask->isRunning())
485 visualizationTask->stop();
static DateTime Now()
Current time on the virtual clock.
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
static DateTime Invalid()
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
The FramedPosition class.
void start()
Starts the thread.
void stop()
Stops the thread.
bool isRunning() const
Retrieve running state of the thread.
bool hasEntityName() const
void handleControllerStatus(const gaze_controller::GazeControllerStatus &status)
Feed one report from the low-level controller.
void submitToQueue(gaze_targets::GazeTarget target, armem::MemoryID gazeTargetID)
Queue a request.
Scheduler(const InjectedServices &srv, const Params ¶ms)
void resetPriorityQueue()
Business Object (BO) class of GazeTarget.
armarx::core::time::Duration duration
armarx::DateTime activationTimestamp
armarx::DateTime creationTimestamp
The Priority of a GazeTarget.
#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.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
@ 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)
Periodic report of the low-level gaze controller's residual.
How far the gaze currently is from a target.
std::experimental::observer_ptr< viz::Client > arviz
std::experimental::observer_ptr< ControllerHandlerInterface > controllerHandler
std::experimental::observer_ptr< armem::robot_state::VirtualRobotReader > virtualRobotReader
bool visualizeRandomEventTarget
Eigen::Vector3f defaultTarget
bool visualizeTaskDrivenGazeTarget
bool visualizeStimulusDrivenTarget