WorkerNode.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package RobotComponents
19  * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
20  * @date 2015
21  * @copyright http://www.gnu.org/licenses/gpl.txt
22  * GNU General Public License
23  */
24 
25 #include "WorkerNode.h"
26 
27 #include <algorithm>
28 #include <chrono>
29 
31 
32 #include "../../util/CollisionCheckUtil.h"
33 #include "util.h"
34 
35 namespace armarx::addirrtstar
36 {
37  void
39  {
40  ARMARX_CHECK_EXPRESSION(manager);
41  //init values
42  killRequest = false;
43  workerPaused = false;
44  updateTopicName = topicPrefix + ADDIRRTSTAR_TREE_UPDATE_TOPIC_NAME;
45  //init collision test
46  cspace->initCollisionTest();
47  //register for armarx message passing
50 
51  //start worker
52  workerThread = std::thread{[this] { workerTask(); }};
53  }
54 
55  void
57  {
58  globalWorkers = getTopic<TreeUpdateInterfacePrx>(updateTopicName);
59  }
60 
61  void
63  {
64  //kill worker and join it
65  killWorker();
66  workerThread.join();
67  }
68 
69  void
70  WorkerNode::updateTree(const Update& u, const Ice::Current&)
71  {
72  std::lock_guard<std::mutex> lock{updateMutex};
74  }
75 
76  void
78  {
79  //init sampler
80  std::vector<std::pair<float, float>> dimensionBounds{};
81  dimensionBounds.reserve(getDimensionality());
82 
83  for (const auto& dim : cspace->getDimensionsBounds())
84  {
85  dimensionBounds.emplace_back(dim.min, dim.max);
86  }
87 
88  ARMARX_CHECK_EXPRESSION(getDimensionality() == goalCfg.size());
89  ARMARX_CHECK_EXPRESSION(getDimensionality() == startCfg.size());
90 
91  sampler.reset(
92  new InformedSampler{typename InformedSampler::DistributionType{dimensionBounds.begin(),
93  dimensionBounds.end(),
94  startCfg.begin(),
95  startCfg.end(),
96  goalCfg.begin(),
97  rotationMatrix},
98  std::mt19937{std::random_device{}()}});
99  rotationMatrix.clear();
100 
101  //after starting the requred proxies are set.
102  getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted);
103 
104  //some assertions
106  //init tree
107  //it is initialized after we subscribed to the topic
108  initTree();
109 
110  while (true)
111  {
112  //check for kill
113  if (killRequest)
114  {
115  break;
116  }
117 
118  if (workerPaused)
119  {
120  std::this_thread::sleep_for(std::chrono::seconds(2));
121  continue;
122  }
123 
124  doBatch();
125  }
126 
127  auto finalUpdateId = tree.getPreviousUpdateId();
128  ARMARX_CHECK_EXPRESSION(tree.getAppliedUpdateIds().at(workerId) == finalUpdateId);
129  //send the the final update id. Important: DON'T send size-1 since the last element is an empty update
130  manager->setWorkersFinalUpdateId(workerId, finalUpdateId);
131  }
132 
133  void
135  {
136  //update max cost for sampler
137  sampler->getDistribution().setMaximalCost(tree.getBestCost());
138 
139  //iterate batch
140  for (Ice::Long currentBatchIteration = 0; currentBatchIteration < batchSize;
141  ++currentBatchIteration)
142  {
143  // //break on kill => sends last update and ends calculation
144  if (killRequest)
145  {
146  break;
147  }
148 
150  } //for batch
151 
152  //was the goal reached?
153  //try connect the nearest node of all newly (since the last check) added nodes to the goal
154  const auto& workersNodes = tree.getNodes().at(workerId);
155 
156  if (onePastLastGoalConnect + nodeCountDeltaForGoalConnectionTries <= workersNodes.size())
157  {
158  NodeId goalConnectNodeNearestId;
159  goalConnectNodeNearestId.workerId = workerId;
160  float goalConnectNodeNearestDistance = std::numeric_limits<float>::infinity();
161 
162  // const Update& u = tree.getCurrentUpdate();
163  for (std::size_t numberOfNode = onePastLastGoalConnect;
164  numberOfNode < workersNodes.size();
165  ++numberOfNode)
166  {
167  const Tree::NodeType& currNode = workersNodes.at(numberOfNode);
168  const float currNodeDistance = distance(goalCfg, currNode.config);
169 
170  if (currNodeDistance < goalConnectNodeNearestDistance)
171  {
172  goalConnectNodeNearestDistance = currNodeDistance;
173  goalConnectNodeNearestId.numberOfNode = numberOfNode;
174  }
175  }
176 
177  if (goalConnectNodeNearestDistance < std::numeric_limits<float>::infinity())
178  {
179  const auto cfgReached =
180  steer(tree.getNode(goalConnectNodeNearestId).config, goalCfg);
181 
182  if (cfgReached == tree.getNode(goalConnectNodeNearestId).config)
183  {
184  //this node is a boundary node!
185  tree.decreaseRadius(goalConnectNodeNearestId);
186  }
187  else if (cfgReached == goalCfg)
188  {
189  //node can reach goal node
190  //since this node was added to the tree it is rewired
191  //new rewiring could improve other paths but no path leading to the goal
192  //wiht a lower cost can include this node => rewiring would be a waste of time
193  tree.addGoalReached(goalConnectNodeNearestId, goalConnectNodeNearestDistance);
194  }
195  else
196  {
197  //reached some config => add it to the tree and rewire it
198  addAndRewireConfig(cfgReached, goalConnectNodeNearestId);
199  }
200  }
201  onePastLastGoalConnect = workersNodes.size();
202  }
203 
204  //send update
205  {
206  std::lock_guard<std::mutex> lock{updateMutex};
208  }
209  }
210 
211  void
213  {
214  {
215  //add updates
216  tree.applyPendingUpdates(std::unique_lock<std::mutex>{updateMutex},
217  [this](Ice::Long workerNodeId, Ice::Long updateSubId)
218  {
219  return getRemoteUpdate(workerNodeId, updateSubId);
220  }); //this methode is thread save if the given mutex is correct
221  }
222  }
223 
224  void
226  {
228 
229  // //sample
230  ConfigType cfgRnd(getDimensionality());
231 
232  NodeId nodeNearestId;
233  float nodeNearestDistance;
234  //sample until the sample is in the dynamic domain of nearest
235 
236  {
237  u_int64_t samplingTries{0}; //should never overflow
238  do
239  {
240  //dont get stuck in this loop when killed
241  if (!(samplingTries % 1000))
242  {
243  //check every 1000th loop
244  if (killRequest)
245  {
246  return;
247  }
248 
249  //prevent getting stuck with a few nodes with min radius
250  //+1000 => worker will not try to apply updates at sampling try 0 (it just applied them!)
251  if (!((samplingTries + 1000) % 10000))
252  {
254  }
255  }
256  (*sampler)(cfgRnd.data());
257  std::tie(nodeNearestId, nodeNearestDistance) =
259  ++samplingTries;
260  } while (nodeNearestDistance > tree.getNode(nodeNearestId).radius);
261  }
262 
263  const auto cfgReached = steer(tree.getNode(nodeNearestId).config, cfgRnd);
264 
265  //if the dcd parameter is correct the path xNear -> xNew
266  //is collision free (per invariant), but may be the same as xNearest
267  //we do no continuous CD
268  if (tree.getNode(nodeNearestId).config == cfgReached)
269  {
270  //this node is a boundary node!
271  tree.decreaseRadius(nodeNearestId);
272  return;
273  }
274 
275  //no boundary node! find parent, add it to the tree, and rewire it
276  addAndRewireConfig(cfgReached, nodeNearestId);
277  }
278 
279  void
280  WorkerNode::addAndRewireConfig(const ConfigType& cfgReached, const NodeId& nodeNearestId)
281  {
282  NodeId nodeBestId;
283  float costReachedToNodeBest;
284  std::vector<std::pair<NodeId, float>> kNearestIdsAndDistances;
285  std::map<NodeId, bool> isCollisionFreeCache;
286 
287 
288  findBestParent(nodeNearestId,
289  cfgReached,
290  //out
291  nodeBestId,
292  costReachedToNodeBest,
293  kNearestIdsAndDistances,
294  isCollisionFreeCache);
295 
296  //add node to the tree
297  const auto nodeReachedId = tree.addNode(cfgReached, //reached point
298  nodeBestId, //parent id
299  costReachedToNodeBest //cost parent->node
300  );
301 
302  rewire(nodeReachedId, kNearestIdsAndDistances, isCollisionFreeCache);
303  }
304 
305  void
306  WorkerNode::rewire(const NodeId& nodeId,
307  const std::vector<std::pair<NodeId, float>>& kNearestIdsAndDistances,
308  const std::map<NodeId, bool>& isCollisionFreeCache)
309  {
310  //find root node
311  const auto& node = tree.getNode(nodeId);
312  const ConfigType& cfgNode = node.config;
313  const float nodeFromStartCost = node.fromStartCost;
314 
315  for (const auto& nearIdAndDist : kNearestIdsAndDistances)
316  {
317  const auto& nodeNearId = nearIdAndDist.first;
318  auto& nodeNear = tree.getNode(nodeNearId);
319  const auto& cfgNodeNear = nodeNear.config;
320 
321  const auto costPathToNearOld = nodeNear.fromStartCost;
322  const auto costNodeToNear = nearIdAndDist.second;
323  const auto costPathToNearOverNode = nodeFromStartCost + costNodeToNear;
324 
325  //is this a better path
326  if (costPathToNearOverNode < costPathToNearOld)
327  {
328  //check collision
329  const auto it = isCollisionFreeCache.find(nodeNearId);
330 
331  if ((it != isCollisionFreeCache.end() && (*it).second) ||
332  isPathCollisionFree(cfgNode, cfgNodeNear))
333  {
334  //rewire node
335  tree.setNodeParent(nodeNearId, nodeId, costNodeToNear);
336  }
337  }
338  }
339  }
340 
341  void
342  WorkerNode::findBestParent(const NodeId& nodeNearestId,
343  const ConfigType& cfgReached,
344  NodeId& outNodeBestId,
345  float& outCostReachedToNodeBest,
346  std::vector<std::pair<NodeId, float>>& outKNearestIdsAndDistances,
347  std::map<NodeId, bool>& outIsCollisionFreeCache)
348  {
349  // //find parent
350  outNodeBestId = nodeNearestId;
351  outCostReachedToNodeBest = distance(tree.getNode(outNodeBestId).config, cfgReached);
352  auto costPathToReachedOverNodeBest =
353  tree.getNode(nodeNearestId).fromStartCost + outCostReachedToNodeBest;
354  outKNearestIdsAndDistances = tree.getKNearestNeighboursAndDistances(cfgReached, getK());
355 
356  for (const auto& nearIdAndDist : outKNearestIdsAndDistances)
357  {
358 
359  auto& nodeNearId = nearIdAndDist.first;
360  auto& nodeNear = tree.getNode(nodeNearId);
361  const auto& cfgNodeNear = nodeNear.config;
362 
363  const auto costReachedToNodeNear = nearIdAndDist.second;
364  const auto costPathToReachedOverNodeNear =
365  nodeNear.fromStartCost + costReachedToNodeNear;
366 
367  if (costPathToReachedOverNodeNear < costPathToReachedOverNodeBest)
368  {
369  //check collision
370  if (isPathCollisionFree(cfgNodeNear, cfgReached))
371  {
372  outNodeBestId = nodeNearId;
373  costPathToReachedOverNodeBest = costPathToReachedOverNodeNear;
374  outCostReachedToNodeBest = costReachedToNodeNear;
375  outIsCollisionFreeCache[nodeNearId] = true;
376  }
377  else
378  {
379  outIsCollisionFreeCache[nodeNearId] = false;
380  }
381  }
382  }
383  }
384 
386  WorkerNode::steer(const ConfigType& from, const ConfigType& to)
387  {
388  ARMARX_CHECK_EXPRESSION(getDimensionality() == from.size());
390 
391  return dcdSteer(
392  from, to, DCDStepSize, [this](const ConfigType& cfg) { return isCollisionFree(cfg); });
393  }
394 
395  bool
397  {
398  bool tmp = dcdIsPathCollisionFree(from,
399  to,
400  DCDStepSize,
401  [this](const ConfigType& cfg) {
402  return isCollisionFree(cfg);
403  }); //steer(from, to) == to;
404  return tmp;
405  }
406 
407  bool
409  {
410  bufferCDRange.first = cfg.data();
411  bufferCDRange.second = bufferCDRange.first + cfg.size();
412  return cspace->isCollisionFree(bufferCDRange);
413  }
414 
415  std::size_t
417  {
418  const auto dim = static_cast<float>(getDimensionality());
419  const auto kRRT = std::pow(2.f, dim + 1.f) * M_E * (1.f + 1.f / dim);
420  const auto k = kRRT * std::log(static_cast<float>(tree.size()));
421  return static_cast<std::size_t>(std::ceil(k));
422  }
423 
424  Update
425  WorkerNode::getUpdate(Ice::Long updateId, const Ice::Current&) const
426  {
427  std::lock_guard<std::mutex> lock{updateMutex};
428  ARMARX_CHECK_EXPRESSION(updateId >= 0);
430  return tree.getLocalUpdates().at(static_cast<std::size_t>(updateId));
431  }
432 
433  Update
435  {
436  return manager->getUpdate(workerNodeId, updateSubId);
437  }
438 } // namespace armarx::addirrtstar
armarx::addirrtstar::WorkerNode::onInitComponent
void onInitComponent() override
Initializes the worker and starts the worker thread.
Definition: WorkerNode.cpp:38
armarx::addirrtstar::WorkerNode::killRequest
std::atomic_bool killRequest
Wheether the node should shut down.
Definition: WorkerNode.h:298
armarx::addirrtstar::WorkerNode::getUpdate
Update getUpdate(Ice::Long updateId, const Ice::Current &=Ice::emptyCurrent) const override
Definition: WorkerNode.cpp:425
armarx::addirrtstar::WorkerNode::applyPendingUpdates
void applyPendingUpdates()
Applies all pending updates to the tree.
Definition: WorkerNode.cpp:212
armarx::addirrtstar::WorkerNode::rewire
void rewire(const NodeId &nodeId, const std::vector< std::pair< NodeId, float >> &kNearestIdsAndDistances, const std::map< NodeId, bool > &isCollisionFreeCache)
The rewire operation of the rrt* algorithm.
Definition: WorkerNode.cpp:306
armarx::addirrtstar::WorkerNode::updateTopicName
std::string updateTopicName
The update topic's name.
Definition: WorkerNode.h:318
armarx::addirrtstar::WorkerNode::initTree
void initTree()
initializes the tree by getting the current tree from the manager
Definition: WorkerNode.h:249
WorkerNode.h
armarx::addirrtstar::Tree::size
std::size_t size() const
Definition: Tree.h:694
armarx::addirrtstar::Tree::setNodeParent
void setNodeParent(const NodeId &child, const NodeId &newParent, float fromParentCost)
Sets the parent node of child to parent.
Definition: Tree.h:548
armarx::dcdIsPathCollisionFree
bool dcdIsPathCollisionFree(const ConfigType &from, const ConfigType &to, RealType dcdStepSize, CollisionChecker isCollisionFree, bool toIsCollisionFree=true, const std::optional< Distance > &distanceLambda=std::optional< Distance >(), const std::optional< Interpolate > &interpolationLambda=std::optional< Interpolate >(), std::vector< ConfigType > *resultPath=NULL)
Returns whether the line startCfg to to is collision free.
Definition: CollisionCheckUtil.h:122
armarx::addirrtstar::WorkerNode::getRemoteUpdate
Update getRemoteUpdate(Ice::Long workerNodeId, Ice::Long updateSubId)
Definition: WorkerNode.cpp:434
armarx::addirrtstar::WorkerNode::ConfigType
Tree::ConfigType ConfigType
Type of a configuration.
Definition: WorkerNode.h:81
util.h
armarx::addirrtstar::Tree::decreaseRadius
void decreaseRadius(const NodeId &id)
Decreases a node's radius.
Definition: Tree.h:559
armarx::addirrtstar::Tree::applyPendingUpdates
void applyPendingUpdates()
Applies all pending updates.
Definition: Tree.h:206
armarx::addirrtstar::Tree::NodeType::config
ConfigType config
The node's configuration.
Definition: Tree.h:66
armarx::Sampler
Stores a distribution and a generator.
Definition: Samplers.h:47
armarx::addirrtstar::WorkerNode::tree
Tree tree
The RRT tree.
Definition: WorkerNode.h:293
armarx::addirrtstar::WorkerNode::updateMutex
std::mutex updateMutex
Mutex protecting the update structures.
Definition: WorkerNode.h:308
armarx::addirrtstar::Tree::NodeType
Represents a node of thr rrt.
Definition: Tree.h:61
armarx::addirrtstar::Tree::addNode
NodeId addNode(ConfigType cfg, const NodeId &parent, float fromParentCost)
Adds a node to the tree.
Definition: Tree.cpp:235
armarx::addirrtstar::WorkerNode::workerPaused
std::atomic_bool workerPaused
workerPaused is true if the worker is currently on hold and does not perform any calculations.
Definition: WorkerNode.h:303
armarx::addirrtstar::Tree::getNearestNeighbourAndDistance
std::pair< NodeId, float > getNearestNeighbourAndDistance(const ConfigType &cfg)
Definition: Tree.h:659
armarx::addirrtstar::Tree::NodeType::radius
float radius
The node's radius.
Definition: Tree.h:74
armarx::addirrtstar::WorkerNode::distance
static float distance(const ConfigType &from, const ConfigType &to)
Calculates the euclidian distance between from and to.
Definition: WorkerNode.h:218
armarx::addirrtstar::WorkerNode::doBatchIteration
void doBatchIteration()
Executes one iteration of a batch.
Definition: WorkerNode.cpp:225
armarx::addirrtstar::WorkerNode::onExitComponent
void onExitComponent() override
Kills the worker thread (if it is still running) and joins it.
Definition: WorkerNode.cpp:62
armarx::addirrtstar::WorkerNode::onConnectComponent
void onConnectComponent() override
Gets a proxy to the topic.
Definition: WorkerNode.cpp:56
armarx::addirrtstar::Tree::sendCurrentUpdate
void sendCurrentUpdate(TreeUpdateInterfacePrx &prx)
Sends the current update to the given proxy.
Definition: Tree.cpp:178
armarx::addirrtstar::WorkerNode::workerTask
void workerTask()
The worker task.
Definition: WorkerNode.cpp:77
armarx::Sampler::DistributionType
Distribution DistributionType
Type of the stored distribution.
Definition: Samplers.h:53
armarx::addirrtstar::WorkerNode::workerThread
std::thread workerThread
Thread executing the worker task.
Definition: WorkerNode.h:313
armarx::dcdSteer
ConfigType dcdSteer(const ConfigType &from, const ConfigType &to, RealType dcdStepSize, CollisionChecker isCollisionFree)
Tries to reach to from from using the given stepsize.
Definition: CollisionCheckUtil.h:47
armarx::addirrtstar::WorkerNode::isCollisionFree
bool isCollisionFree(const ConfigType &cfg)
Definition: WorkerNode.cpp:408
armarx::addirrtstar::WorkerNode::steer
ConfigType steer(const ConfigType &from, const ConfigType &to)
Does a linear interpolation from from to to and checks the path per DCD.
Definition: WorkerNode.cpp:386
armarx::addirrtstar::Tree::getLocalUpdates
const std::deque< Update > & getLocalUpdates() const
Definition: Tree.h:160
armarx::addirrtstar::Tree::getAppliedUpdateIds
const Ice::LongSeq & getAppliedUpdateIds() const
Definition: Tree.h:845
armarx::VariantType::Long
const VariantTypeId Long
Definition: Variant.h:918
armarx::addirrtstar::WorkerNode::isPathCollisionFree
bool isPathCollisionFree(const ConfigType &from, const ConfigType &to)
Definition: WorkerNode.cpp:396
ArmarXObjectScheduler.h
armarx::addirrtstar::WorkerNode::getDimensionality
std::size_t getDimensionality()
Definition: WorkerNode.h:280
armarx::addirrtstar::Tree::getNode
NodeType & getNode(const NodeId &id)
Definition: Tree.cpp:358
armarx::addirrtstar::WorkerNode::getK
std::size_t getK()
Definition: WorkerNode.cpp:416
armarx::addirrtstar::WorkerNode::globalWorkers
TreeUpdateInterfacePrx globalWorkers
Proxy used to write to the update topic.
Definition: WorkerNode.h:322
armarx::addirrtstar::WorkerNode::onePastLastGoalConnect
std::size_t onePastLastGoalConnect
The index one past the last node that was considered to be connected to the goal configuration.
Definition: WorkerNode.h:332
armarx::addirrtstar::WorkerNode::doBatch
void doBatch()
Executes a batch.
Definition: WorkerNode.cpp:134
armarx::addirrtstar::Tree::getPreviousUpdateId
Ice::Long getPreviousUpdateId() const
Definition: Tree.h:285
armarx::addirrtstar::Tree::NodeType::fromStartCost
float fromStartCost
Cost of the path (sttart node, this node)
Definition: Tree.h:82
armarx::addirrtstar::WorkerNode::sampler
std::unique_ptr< InformedSampler > sampler
Stores the sampler.
Definition: WorkerNode.h:341
armarx::addirrtstar::Tree::getKNearestNeighboursAndDistances
std::vector< std::pair< NodeId, float > > getKNearestNeighboursAndDistances(const ConfigType &cfg, std::size_t k)
Definition: Tree.cpp:317
armarx::ManagedIceObject::usingTopic
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Definition: ManagedIceObject.cpp:254
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::addirrtstar::WorkerNode::findBestParent
void findBestParent(const NodeId &nodeNearestId, const ConfigType &cfgReached, NodeId &outNodeBestId, float &outCostReachedToNodeBest, std::vector< std::pair< NodeId, float >> &outKNearestIdsAndDistances, std::map< NodeId, bool > &outIsCollisionFreeCache)
Searches the optimal parent for a configuration.
Definition: WorkerNode.cpp:342
armarx::ManagedIceObject::offeringTopic
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
Definition: ManagedIceObject.cpp:300
armarx::addirrtstar::Tree::addPendingUpdate
void addPendingUpdate(const Update &u)
Adds a new update to the list of pending updates.
Definition: Tree.cpp:502
armarx::addirrtstar::WorkerNode::updateTree
void updateTree(const Update &u, const Ice::Current &=Ice::emptyCurrent) override
Receives an update from other workers and adds it to the queue of pending updates.
Definition: WorkerNode.cpp:70
armarx::addirrtstar::Tree::getBestCost
float getBestCost() const
Definition: Tree.cpp:406
armarx::addirrtstar::Tree::addGoalReached
void addGoalReached(const NodeId &goal, float costToGoToGoal)
Adds a node to the list of nodes that can reach the goal configuration.
Definition: Tree.h:582
armarx::addirrtstar::Tree::getNodes
const std::deque< std::deque< NodeType > > & getNodes() const
Definition: Tree.h:679
armarx::ManagedIceObject::getObjectScheduler
ArmarXObjectSchedulerPtr getObjectScheduler() const
Definition: ManagedIceObject.cpp:776
armarx::addirrtstar::WorkerNode::addAndRewireConfig
void addAndRewireConfig(const ConfigType &cfgReached, const NodeId &nodeNearestId)
Adds a configuration to the tree.
Definition: WorkerNode.cpp:280
armarx::addirrtstar::WorkerNode::killWorker
void killWorker(const Ice::Current &=Ice::emptyCurrent) override
Signals the worker thread to exit.
Definition: WorkerNode.h:176
armarx::addirrtstar
Definition: ManagerNode.cpp:36