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
26#include <algorithm>
27
28#include <Ice/ObjectAdapter.h>
29
30#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
31
36
38#include "util/PlanningUtil.h"
39#include <MemoryX/interface/components/PriorKnowledgeInterface.h>
40
41namespace armarx
42{
43
44 void
46 {
47 setTag("Server");
48
49 const std::string remNames = getProperty<std::string>("RemoteObjectNodes").getValue();
50 startLocalNode = getProperty<bool>("LocalRONStart").getValue();
51 localNodePercent = getProperty<float>("LocalRONCorePerc").getValue();
52 remoteObjectNodeNames = Split(remNames, ";,");
53 //del empty names
55 std::distance(remoteObjectNodeNames.begin(),
56 std::remove_if(remoteObjectNodeNames.begin(),
58 [](const std::string& s) { return s == ""; })));
59
60 for (const auto& name : remoteObjectNodeNames)
61 {
62 ARMARX_VERBOSE_S << "Using RemoteObjectNode with name: " << name;
63 usingProxy(name);
64 }
65
66
67 //start dispatcher
68 dispatcherThread = std::thread{[this] { this->dispatcherTask(); }};
69 }
70
71 void
73 {
74 remoteObjectNodes.clear();
75
76 for (const auto& name : remoteObjectNodeNames)
77 {
79 }
80
81 if (remoteObjectNodeNames.empty())
82 {
84 {
86 getIceProperties(), generateSubObjectName("RemoteObjectNode"));
87 localNode->setCoreCount(localNodePercent * localNode->getDefaultCoreCount());
88 //registration is required since a proxy is required
89 getArmarXManager()->addObject(localNode);
90 remoteObjectNodes.emplace_back(
91 RemoteObjectNodeInterfacePrx::uncheckedCast(localNode->getProxy(-1)));
92 }
93 else
94 {
95 ARMARX_WARNING_S << "Starting motion planning server without any remote object "
96 "nodes! (some algorithms may still work)";
97 }
98 }
99 auto wm = getProxy<memoryx::WorkingMemoryInterfacePrx>("WorkingMemory", false, "", false);
100 auto cs = getProxy<memoryx::CommonStorageInterfacePrx>("CommonStorage", false, "", false);
101 auto rsc =
102 getProxy<RobotStateComponentInterfacePrx>("RobotStateComponent", false, "", false);
103 if (wm && cs & rsc)
104 {
106 }
107 }
108
109 void
111 {
112 //request kill on dispatcher
114 {
115 //start aborting current task since this could take a while
116 std::lock_guard<std::recursive_mutex> lock{queueMutex};
117
118 //abort all queued tasks
119 for (auto taskId : taskQueue)
120 {
121 tasks.at(taskId).task->abortTask();
122 }
123 taskQueue.clear();
124
125 for (auto& task : tasks)
126 {
127 task.second.rh->forceDeletion();
128 }
129
130 tasks.clear();
131 }
132 //join dispatcher
133 waitForTaskOrDispatcherKill.notify_all();
135 dispatcherThread.join();
136 ARMARX_VERBOSE_S << "joined dispatcher";
137 //del local node
138 if (localNode)
139 {
140 getArmarXManager()->removeObjectNonBlocking(localNode);
141 }
142 }
143
144 void
146 {
147 decltype(remoteObjectNodes) ronCache;
148 while (true)
149 {
151 {
152 break;
153 }
154
155 //the task may be deleted concurrently from the map
156 //keep the smartpointer to ensure the task handle to stay alive until we are done with it
157 TaskAndRemoteHandle handle;
158 decltype(taskQueue)::value_type taskId; //simple to change taskids later
159 {
160 std::unique_lock<std::recursive_mutex> lock{queueMutex};
161
162 if (taskQueue.empty())
163 {
164 //no task pending. so wait for a signal
166 //could be woken up by kill request, rando wakeup or a new task=> redo checks
167 //since we perform a special action on dispatcherKillRequest we cant use a predicate
168 continue;
169 }
170
171 //there is a task to perform
172 //we have to save the id since deleteTaskById deletes the id from the queue
173 taskId = *taskQueue.begin();
174 handle = tasks.at(taskId);
175 }
176 //if disconnect+connect is called the vector may change. to remove the requirement of sync: copy
177 ronCache = remoteObjectNodes;
178
179 //what every task prints:
180#define task_info \
181 "TASK FAILED!" \
182 << "\n\tname: " << handle.task->getTaskName() << "\n\tID: " << taskId \
183 << "\n\tice id = " << handle.task->ice_id() << "\n\tstatus " \
184 << TaskStatus::toString(handle.task->getTaskStatus())
185
186 try
187 {
189 currentTask.rh = handle.rh;
190 currentTask.task = handle.task;
191 auto start = IceUtil::Time::now();
192 MotionPlanningTaskPtr t = handle.task;
193 while (t)
194 {
195 auto tp = t.get();
196 t->addTaskStatusCallback(
197 [tp, start, this](TaskStatus::Status s)
198 {
199 if (s == TaskStatus::eRefining)
200 {
201 auto duration = IceUtil::Time::now() - start;
202 tp->planningTime = duration.toMicroSeconds();
203 }
205 {
206 auto duration = IceUtil::Time::now() - start;
207 if (tp->planningTime > 0)
208 {
209 tp->refiningTime = duration.toMicroSeconds() - tp->planningTime;
210 }
211 else
212 {
213 tp->planningTime = duration.toMicroSeconds();
214 }
215 ARMARX_VERBOSE << tp->getTaskName()
216 << ": Planning time: " << tp->planningTime * 0.001f
217 << " ms, " << tp->refiningTime * 0.001f << " ms";
218 }
219 });
221 PostprocessingMotionPlanningTaskPtr::dynamicCast(t);
222 if (tmp)
223 {
224 t = MotionPlanningTaskPtr::dynamicCast(tmp->previousStep);
225 }
226 else
227 {
228 t = nullptr;
229 }
230 }
231 handle.task->run(ronCache);
232 }
233 catch (Ice::Exception& e)
234 {
235 ARMARX_ERROR_S << task_info << "\n\tWHAT:\n"
236 << e.what() << "\n\tSTACK:\n"
237 << e.ice_stackTrace();
238 handle.task->setTaskStatus(TaskStatus::eException);
239 }
240 catch (std::exception& e)
241 {
242 ARMARX_ERROR_S << task_info << "\n\tWHAT:\n" << e.what();
243 handle.task->setTaskStatus(TaskStatus::eException);
244 }
245 catch (...)
246 {
248 << "\n\tsomething not derived from std::exception was thrown";
249 handle.task->setTaskStatus(TaskStatus::eException);
250 }
251 currentTask.rh = nullptr;
252 currentTask.task = nullptr;
253#undef task_info
254 //task done dequeue it
255 {
256 std::lock_guard<std::recursive_mutex> lock{queueMutex};
257 taskQueue.erase(taskId);
258 }
259 //loop to wait for next task or kill
260 }
261 ARMARX_DEBUG << "dispatcher task exit";
262 }
263
264 bool
266 {
267 std::lock_guard<std::recursive_mutex> lock{queueMutex};
268 auto it = tasks.find(id);
269
270 if (it == tasks.end())
271 {
272 ARMARX_VERBOSE_S << "no task with id " << id << " to delete";
273 return false;
274 }
275
276 //request task kill
277 it->second.task->abortTask();
278
279 //don't remove it from the adapter yet. the proxy may be required to pass data to the task object.
280 //the task will remove it self from the adapter;
281
282 taskQueue.erase(id);
283 tasks.erase(it);
284 return true;
285 }
286
287 armarx::ClientSideRemoteHandleControlBlockBasePtr
288 MotionPlanningServer::enqueueTask(const MotionPlanningTaskBasePtr& task, const Ice::Current&)
289 {
291 {
292 throw armarx::ServerShuttingDown{"MotionPlanningServerComponent"};
293 }
294
295 MotionPlanningTaskPtr planningTask = MotionPlanningTaskPtr::dynamicCast(task);
296 ARMARX_CHECK_EXPRESSION(planningTask);
297 //register the task and add the data to unregister the task
298 auto adapter = getArmarXManager()->getAdapter();
299
300 auto id = getNewTaskId();
301
302 Ice::Identity ident;
303 ident.name = generateSubObjectName(task->getTaskName() + "_ID_" + to_string(id));
304 planningTask->registerAtIceAdapter(adapter, ident);
305
306 //add the task
307
308 auto remoteHandleHandles = armarx::RemoteHandleControlBlock::create(
310 MotionPlanningTaskControlInterfacePrx::uncheckedCast(planningTask->getProxy()),
311 [id, this, adapter, ident]
312 {
313 adapter->remove(ident);
314 //del from server
315 deleteTaskById(id);
316 });
317
318 {
319 std::lock_guard<std::recursive_mutex> lock{queueMutex};
320 ARMARX_CHECK_EXPRESSION(planningTask);
321 tasks[id] = TaskAndRemoteHandle{remoteHandleHandles.directHandle, planningTask};
322 taskQueue.insert(id);
323 planningTask->postEnqueueing();
324 }
325 //wake up dispatcher
326 //holding the mutex a cv is holding on is inefficient since it may cause additional thread switches
327 waitForTaskOrDispatcherKill.notify_all();
328 return remoteHandleHandles.clientSideRemoteHandleControlBlock;
329 }
330
331 Ice::Long
332 MotionPlanningServer::getNumberOfTasks(const Ice::Current&) const
333 {
334 std::lock_guard<std::recursive_mutex> lock{queueMutex};
335 return tasks.size();
336 }
337
338 Ice::Long
340 {
341 std::lock_guard<std::recursive_mutex> lock{queueMutex};
342 return taskQueue.size();
343 }
344
345 TaskInfoSeq
346 MotionPlanningServer::getAllTaskInfo(const Ice::Current&) const
347 {
348 std::lock_guard<std::recursive_mutex> lock{queueMutex};
349 TaskInfoSeq result;
350 result.reserve(tasks.size());
351 transform(tasks.begin(),
352 tasks.end(),
353 std::back_inserter(result),
354 [](const decltype(tasks)::value_type& v)
355 {
356 const TaskAndRemoteHandle& rhs = v.second;
357 //ident, status, iceType
358 return TaskInfo{v.first,
359 rhs.rh->getManagedObjectProxy()->ice_getIdentity(),
360 rhs.task->getTaskStatus(),
361 rhs.task->ice_id(),
362 rhs.task->getTaskName()};
363 });
364 return result;
365 }
366
367 ClientSideRemoteHandleControlBlockBasePtr
369 {
370 ClientSideRemoteHandleControlBlockBasePtr remoteHandle;
371 if (currentTask.rh)
372 {
373 remoteHandle = currentTask.rh->getClientSideRemoteHandleControlBlock();
374 }
375 return remoteHandle;
376 }
377
378 CSpaceAndPaths
379 MotionPlanningServer::getTaskCSpaceAndPathsById(Ice::Long id, const Ice::Current&)
380 {
381 CSpaceAndPaths result;
382 std::lock_guard<std::recursive_mutex> lock{queueMutex};
383 auto it = tasks.find(id);
384 if (it != tasks.end())
385 {
386 auto task = MotionPlanningTaskBasePtr::dynamicCast(it->second.task);
387
388 result.cspace = task ? task->getCSpace()->clone() : nullptr;
389 auto multPathTask = MotionPlanningMultiPathTaskControlInterfacePtr::dynamicCast(task);
390 if (multPathTask)
391 {
392 result.paths = multPathTask->getAllPaths();
393 }
394 else
395 {
396 result.paths.emplace_back(it->second.task->getPath());
397 }
398 }
399 else
400 {
401 ARMARX_WARNING_S << "no task with id " << id << " on the server " << getName();
402 }
403 return result;
404 }
405
406 bool
407 MotionPlanningServer::loadLibFromAbsolutePath(const std::string& path)
408 {
409 if (loadedLibs.count(path) > 0)
410 {
411 return true;
412 }
413 DynamicLibraryPtr lib(new DynamicLibrary());
414 try
415 {
416 lib->load(path);
417 }
418 catch (...)
419 {
421 return false;
422 }
423
424 if (lib->isLibraryLoaded())
425 {
426 loadedLibs[path] = lib;
427 }
428 else
429 {
430 ARMARX_ERROR << "Could not load lib " + path + ": " + lib->getErrorMessage();
431 return false;
432 }
433
434 ArmarXManager::RegisterKnownObjectFactoriesWithIce(
435 this->getIceManager()->getCommunicator());
436 return true;
437 }
438
439 bool
440 MotionPlanningServer::loadLibFromPath(const std::string& path, const Ice::Current&)
441 {
442 std::string absPath;
443 if (ArmarXDataPath::getAbsolutePath(path, absPath))
444 {
445 return loadLibFromAbsolutePath(absPath);
446 }
447 else
448 {
449 ARMARX_ERROR << "Could not find library " + path;
450 return false;
451 }
452 }
453
454 bool
456 const std::string& name,
457 const Ice::Current&)
458 {
459 CMakePackageFinder finder(package);
460 if (!finder.packageFound())
461 {
462 ARMARX_ERROR << "Could not find package '" << package << "'";
463 return false;
464 }
465
466 for (auto libDirPath : armarx::Split(finder.getLibraryPaths(), ";"))
467 {
468 std::filesystem::path fullPath = libDirPath;
469 fullPath /= "lib" + name + "." + DynamicLibrary::GetSharedLibraryFileExtension();
470 if (!std::filesystem::exists(fullPath))
471 {
472 fullPath = libDirPath;
473 fullPath /= name;
474 if (!std::filesystem::exists(fullPath))
475 {
476 continue;
477 }
478 }
479 if (loadLibFromAbsolutePath(fullPath.string()))
480 {
481 return true;
482 }
483 }
484 ARMARX_ERROR << "Could not find library " << name << " in package " << package;
485 return false;
486 }
487} // namespace armarx
#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)