MotionPlanningServer.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 */
25
27
28#include <algorithm>
29
30#include <Ice/ObjectAdapter.h>
31
32#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
33
38
40#include "util/PlanningUtil.h"
41#include <MemoryX/interface/components/PriorKnowledgeInterface.h>
42
43namespace armarx
44{
45
46 void
48 {
49 setTag("Server");
50
51 const std::string remNames = getProperty<std::string>("RemoteObjectNodes").getValue();
52 startLocalNode = getProperty<bool>("LocalRONStart").getValue();
53 localNodePercent = getProperty<float>("LocalRONCorePerc").getValue();
54 remoteObjectNodeNames = Split(remNames, ";,");
55 //del empty names
57 std::distance(remoteObjectNodeNames.begin(),
58 std::remove_if(remoteObjectNodeNames.begin(),
60 [](const std::string& s) { return s == ""; })));
61
62 for (const auto& name : remoteObjectNodeNames)
63 {
64 ARMARX_VERBOSE_S << "Using RemoteObjectNode with name: " << name;
65 usingProxy(name);
66 }
67
68
69 //start dispatcher
70 dispatcherThread = std::thread{[this] { this->dispatcherTask(); }};
71 }
72
73 void
75 {
76 remoteObjectNodes.clear();
77
78 for (const auto& name : remoteObjectNodeNames)
79 {
81 }
82
83 if (remoteObjectNodeNames.empty())
84 {
86 {
88 getIceProperties(), generateSubObjectName("RemoteObjectNode"));
89 localNode->setCoreCount(localNodePercent * localNode->getDefaultCoreCount());
90 //registration is required since a proxy is required
91 getArmarXManager()->addObject(localNode);
92 remoteObjectNodes.emplace_back(
93 RemoteObjectNodeInterfacePrx::uncheckedCast(localNode->getProxy(-1)));
94 }
95 else
96 {
97 ARMARX_WARNING_S << "Starting motion planning server without any remote object "
98 "nodes! (some algorithms may still work)";
99 }
100 }
101 auto wm = getProxy<memoryx::WorkingMemoryInterfacePrx>("WorkingMemory", false, "", false);
102 auto cs = getProxy<memoryx::CommonStorageInterfacePrx>("CommonStorage", false, "", false);
103 auto rsc =
104 getProxy<RobotStateComponentInterfacePrx>("RobotStateComponent", false, "", false);
105 if (wm && cs & rsc)
106 {
108 }
109 }
110
111 void
113 {
114 //request kill on dispatcher
116 {
117 //start aborting current task since this could take a while
118 std::lock_guard<std::recursive_mutex> lock{queueMutex};
119
120 //abort all queued tasks
121 for (auto taskId : taskQueue)
122 {
123 tasks.at(taskId).task->abortTask();
124 }
125 taskQueue.clear();
126
127 for (auto& task : tasks)
128 {
129 task.second.rh->forceDeletion();
130 }
131
132 tasks.clear();
133 }
134 //join dispatcher
135 waitForTaskOrDispatcherKill.notify_all();
137 dispatcherThread.join();
138 ARMARX_VERBOSE_S << "joined dispatcher";
139 //del local node
140 if (localNode)
141 {
142 getArmarXManager()->removeObjectNonBlocking(localNode);
143 }
144 }
145
146 void
148 {
149 decltype(remoteObjectNodes) ronCache;
150 while (true)
151 {
153 {
154 break;
155 }
156
157 //the task may be deleted concurrently from the map
158 //keep the smartpointer to ensure the task handle to stay alive until we are done with it
159 TaskAndRemoteHandle handle;
160 decltype(taskQueue)::value_type taskId; //simple to change taskids later
161 {
162 std::unique_lock<std::recursive_mutex> lock{queueMutex};
163
164 if (taskQueue.empty())
165 {
166 //no task pending. so wait for a signal
168 //could be woken up by kill request, rando wakeup or a new task=> redo checks
169 //since we perform a special action on dispatcherKillRequest we cant use a predicate
170 continue;
171 }
172
173 //there is a task to perform
174 //we have to save the id since deleteTaskById deletes the id from the queue
175 taskId = *taskQueue.begin();
176 handle = tasks.at(taskId);
177 }
178 //if disconnect+connect is called the vector may change. to remove the requirement of sync: copy
179 ronCache = remoteObjectNodes;
180
181 //what every task prints:
182#define task_info \
183 "TASK FAILED!" \
184 << "\n\tname: " << handle.task->getTaskName() << "\n\tID: " << taskId \
185 << "\n\tice id = " << handle.task->ice_id() << "\n\tstatus " \
186 << TaskStatus::toString(handle.task->getTaskStatus())
187
188 try
189 {
191 currentTask.rh = handle.rh;
192 currentTask.task = handle.task;
193 auto start = IceUtil::Time::now();
194 MotionPlanningTaskPtr t = handle.task;
195 while (t)
196 {
197 auto tp = t.get();
198 t->addTaskStatusCallback(
199 [tp, start, this](TaskStatus::Status s)
200 {
201 if (s == TaskStatus::eRefining)
202 {
203 auto duration = IceUtil::Time::now() - start;
204 tp->planningTime = duration.toMicroSeconds();
205 }
207 {
208 auto duration = IceUtil::Time::now() - start;
209 if (tp->planningTime > 0)
210 {
211 tp->refiningTime = duration.toMicroSeconds() - tp->planningTime;
212 }
213 else
214 {
215 tp->planningTime = duration.toMicroSeconds();
216 }
217 ARMARX_VERBOSE << tp->getTaskName()
218 << ": Planning time: " << tp->planningTime * 0.001f
219 << " ms, " << tp->refiningTime * 0.001f << " ms";
220 }
221 });
223 PostprocessingMotionPlanningTaskPtr::dynamicCast(t);
224 if (tmp)
225 {
226 t = MotionPlanningTaskPtr::dynamicCast(tmp->previousStep);
227 }
228 else
229 {
230 t = nullptr;
231 }
232 }
233 handle.task->run(ronCache);
234 }
235 catch (Ice::Exception& e)
236 {
237 ARMARX_ERROR_S << task_info << "\n\tWHAT:\n"
238 << e.what() << "\n\tSTACK:\n"
239 << e.ice_stackTrace();
240 handle.task->setTaskStatus(TaskStatus::eException);
241 }
242 catch (std::exception& e)
243 {
244 ARMARX_ERROR_S << task_info << "\n\tWHAT:\n" << e.what();
245 handle.task->setTaskStatus(TaskStatus::eException);
246 }
247 catch (...)
248 {
250 << "\n\tsomething not derived from std::exception was thrown";
251 handle.task->setTaskStatus(TaskStatus::eException);
252 }
253 currentTask.rh = nullptr;
254 currentTask.task = nullptr;
255#undef task_info
256 //task done dequeue it
257 {
258 std::lock_guard<std::recursive_mutex> lock{queueMutex};
259 taskQueue.erase(taskId);
260 }
261 //loop to wait for next task or kill
262 }
263 ARMARX_DEBUG << "dispatcher task exit";
264 }
265
266 bool
268 {
269 std::lock_guard<std::recursive_mutex> lock{queueMutex};
270 auto it = tasks.find(id);
271
272 if (it == tasks.end())
273 {
274 ARMARX_VERBOSE_S << "no task with id " << id << " to delete";
275 return false;
276 }
277
278 //request task kill
279 it->second.task->abortTask();
280
281 //don't remove it from the adapter yet. the proxy may be required to pass data to the task object.
282 //the task will remove it self from the adapter;
283
284 taskQueue.erase(id);
285 tasks.erase(it);
286 return true;
287 }
288
289 armarx::ClientSideRemoteHandleControlBlockBasePtr
290 MotionPlanningServer::enqueueTask(const MotionPlanningTaskBasePtr& task, const Ice::Current&)
291 {
293 {
294 throw armarx::ServerShuttingDown{"MotionPlanningServerComponent"};
295 }
296
297 MotionPlanningTaskPtr planningTask = MotionPlanningTaskPtr::dynamicCast(task);
298 ARMARX_CHECK_EXPRESSION(planningTask);
299 //register the task and add the data to unregister the task
300 auto adapter = getArmarXManager()->getAdapter();
301
302 auto id = getNewTaskId();
303
304 Ice::Identity ident;
305 ident.name = generateSubObjectName(task->getTaskName() + "_ID_" + to_string(id));
306 planningTask->registerAtIceAdapter(adapter, ident);
307
308 //add the task
309
310 auto remoteHandleHandles = armarx::RemoteHandleControlBlock::create(
312 MotionPlanningTaskControlInterfacePrx::uncheckedCast(planningTask->getProxy()),
313 [id, this, adapter, ident]
314 {
315 adapter->remove(ident);
316 //del from server
317 deleteTaskById(id);
318 });
319
320 {
321 std::lock_guard<std::recursive_mutex> lock{queueMutex};
322 ARMARX_CHECK_EXPRESSION(planningTask);
323 tasks[id] = TaskAndRemoteHandle{remoteHandleHandles.directHandle, planningTask};
324 taskQueue.insert(id);
325 planningTask->postEnqueueing();
326 }
327 //wake up dispatcher
328 //holding the mutex a cv is holding on is inefficient since it may cause additional thread switches
329 waitForTaskOrDispatcherKill.notify_all();
330 return remoteHandleHandles.clientSideRemoteHandleControlBlock;
331 }
332
333 Ice::Long
334 MotionPlanningServer::getNumberOfTasks(const Ice::Current&) const
335 {
336 std::lock_guard<std::recursive_mutex> lock{queueMutex};
337 return tasks.size();
338 }
339
340 Ice::Long
342 {
343 std::lock_guard<std::recursive_mutex> lock{queueMutex};
344 return taskQueue.size();
345 }
346
347 TaskInfoSeq
348 MotionPlanningServer::getAllTaskInfo(const Ice::Current&) const
349 {
350 std::lock_guard<std::recursive_mutex> lock{queueMutex};
351 TaskInfoSeq result;
352 result.reserve(tasks.size());
353 transform(tasks.begin(),
354 tasks.end(),
355 std::back_inserter(result),
356 [](const decltype(tasks)::value_type& v)
357 {
358 const TaskAndRemoteHandle& rhs = v.second;
359 //ident, status, iceType
360 return TaskInfo{v.first,
361 rhs.rh->getManagedObjectProxy()->ice_getIdentity(),
362 rhs.task->getTaskStatus(),
363 rhs.task->ice_id(),
364 rhs.task->getTaskName()};
365 });
366 return result;
367 }
368
369 ClientSideRemoteHandleControlBlockBasePtr
371 {
372 ClientSideRemoteHandleControlBlockBasePtr remoteHandle;
373 if (currentTask.rh)
374 {
375 remoteHandle = currentTask.rh->getClientSideRemoteHandleControlBlock();
376 }
377 return remoteHandle;
378 }
379
380 CSpaceAndPaths
381 MotionPlanningServer::getTaskCSpaceAndPathsById(Ice::Long id, const Ice::Current&)
382 {
383 CSpaceAndPaths result;
384 std::lock_guard<std::recursive_mutex> lock{queueMutex};
385 auto it = tasks.find(id);
386 if (it != tasks.end())
387 {
388 auto task = MotionPlanningTaskBasePtr::dynamicCast(it->second.task);
389
390 result.cspace = task ? task->getCSpace()->clone() : nullptr;
391 auto multPathTask = MotionPlanningMultiPathTaskControlInterfacePtr::dynamicCast(task);
392 if (multPathTask)
393 {
394 result.paths = multPathTask->getAllPaths();
395 }
396 else
397 {
398 result.paths.emplace_back(it->second.task->getPath());
399 }
400 }
401 else
402 {
403 ARMARX_WARNING_S << "no task with id " << id << " on the server " << getName();
404 }
405 return result;
406 }
407
408 bool
409 MotionPlanningServer::loadLibFromAbsolutePath(const std::string& path)
410 {
411 if (loadedLibs.count(path) > 0)
412 {
413 return true;
414 }
415 DynamicLibraryPtr lib(new DynamicLibrary());
416 try
417 {
418 lib->load(path);
419 }
420 catch (...)
421 {
423 return false;
424 }
425
426 if (lib->isLibraryLoaded())
427 {
428 loadedLibs[path] = lib;
429 }
430 else
431 {
432 ARMARX_ERROR << "Could not load lib " + path + ": " + lib->getErrorMessage();
433 return false;
434 }
435
436 ArmarXManager::RegisterKnownObjectFactoriesWithIce(
437 this->getIceManager()->getCommunicator());
438 return true;
439 }
440
441 bool
442 MotionPlanningServer::loadLibFromPath(const std::string& path, const Ice::Current&)
443 {
444 std::string absPath;
445 if (ArmarXDataPath::getAbsolutePath(path, absPath))
446 {
447 return loadLibFromAbsolutePath(absPath);
448 }
449 else
450 {
451 ARMARX_ERROR << "Could not find library " + path;
452 return false;
453 }
454 }
455
456 bool
458 const std::string& name,
459 const Ice::Current&)
460 {
461 CMakePackageFinder finder(package);
462 if (!finder.packageFound())
463 {
464 ARMARX_ERROR << "Could not find package '" << package << "'";
465 return false;
466 }
467
468 for (auto libDirPath : armarx::Split(finder.getLibraryPaths(), ";"))
469 {
470 std::filesystem::path fullPath = libDirPath;
471 fullPath /= "lib" + name + "." + DynamicLibrary::GetSharedLibraryFileExtension();
472 if (!std::filesystem::exists(fullPath))
473 {
474 fullPath = libDirPath;
475 fullPath /= name;
476 if (!std::filesystem::exists(fullPath))
477 {
478 continue;
479 }
480 }
481 if (loadLibFromAbsolutePath(fullPath.string()))
482 {
483 return true;
484 }
485 }
486 ARMARX_ERROR << "Could not find library " << name << " in package " << package;
487 return false;
488 }
489
491} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define task_info
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getLibraryPaths() const
Returns the library paths seperated by semi-colons.
bool packageFound() const
Returns whether or not this package was found with cmake.
static TPtr create(Ice::PropertiesPtr properties=Ice::createProperties(), const std::string &configName="", const std::string &configDomain="ArmarX")
Factory method for a component.
Definition Component.h:116
Property< PropertyType > getProperty(const std::string &name)
static std::string GetSharedLibraryFileExtension()
void setTag(const LogTag &tag)
Definition Logging.cpp:54
static std::string generateSubObjectName(const std::string &superObjectName, const std::string &subObjectName)
Generates a unique name for a sub object from a general name and unique name.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
TaskAndRemoteHandle currentTask
currentTask stores a handle to the currently dispatched planning task.
std::thread dispatcherThread
The dispatcher's thread.
ClientSideRemoteHandleControlBlockBasePtr getCurrentTaskHandle(const Ice::Current &) override
void onInitComponent() override
Initializes the server and starts the dispatcher thread.
std::vector< armarx::RemoteObjectNodeInterfacePrx > remoteObjectNodes
The used remote object nodes.
std::recursive_mutex queueMutex
mutex protecting the task queue
bool startLocalNode
Whether a local node should be started, if no rons were provided.
Ice::Long getNumberOfTasks(const Ice::Current &=Ice::emptyCurrent) const override
Returns the number of tasks.
float localNodePercent
Percentage of cores used by the local ron (if started).
bool loadLibFromPackage(const std::string &package, const std::string &lib, const Ice::Current &=Ice::emptyCurrent) override
armarx::ClientSideRemoteHandleControlBlockBasePtr enqueueTask(const MotionPlanningTaskBasePtr &task, const Ice::Current &=Ice::emptyCurrent) override
Enqueues a task.
std::vector< std::string > remoteObjectNodeNames
The used remote object nodes' names.
TaskMap tasks
The map of tasks.
TaskInfoSeq getAllTaskInfo(const Ice::Current &=Ice::emptyCurrent) const override
Returns information about all tasks.
std::condition_variable_any waitForTaskOrDispatcherKill
The dispatcher's cv.
std::set< Ice::Long > taskQueue
Contains the queue of task ids to execute.
std::atomic_bool dispatcherKillRequest
Whether the dispatcher should shut down.
void dispatcherTask()
the dispatcher task.
bool loadLibFromPath(const std::string &path, const Ice::Current &=Ice::emptyCurrent) override
void onConnectComponent() override
Connects to the used RemoteObjectNodes.
void onExitComponent() override
cleans up and joins the dispatcher
Ice::Long getNewTaskId()
Returns a new task id.
CSpaceAndPaths getTaskCSpaceAndPathsById(Ice::Long id, const Ice::Current &=Ice::emptyCurrent) override
Returns a task's cspace and all found paths.
Ice::Long getNumberOfQueuedTasks(const Ice::Current &) const override
Returns the number of queued tasks.
Ice::PropertiesPtr getIceProperties() const
Returns the set of Ice properties.
static ManagementData create(ArmarXManagerPtr manager, Ice::ObjectPrx managedObjectPrx, Deleter deleter)
Creates a new RemoteHandleControlBlock.
static SimoxCSpacePtr PrefetchWorkingMemoryObjects(memoryx::WorkingMemoryInterfacePrx workingMemoryPrx, memoryx::CommonStorageInterfacePrx commonStoragePrx, RobotStateComponentInterfacePrx rsc)
Load objects from WorkingMemory and puts them into the mesh cache.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_VERBOSE_S
Definition Logging.h:207
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
bool finishedRunning(Status status)
Returns whether the given task status describes a state where planning is done (may be failed).
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< PostprocessingMotionPlanningTask > PostprocessingMotionPlanningTaskPtr
IceInternal::Handle< MotionPlanningTask > MotionPlanningTaskPtr
void handleExceptions()
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition algorithm.h:351
const std::string & to_string(const std::string &s)