yarp-devices
Loading...
Searching...
No Matches
DeviceMapper.hpp
1// -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-
2
3#ifndef __DEVICE_MAPPER_HPP__
4#define __DEVICE_MAPPER_HPP__
5
6#include <cstdlib> // std::size_t
7
8#include <algorithm> // std::copy
9#include <functional> // std::invoke
10#include <memory>
11#include <tuple>
12#include <typeindex>
13#include <unordered_map>
14#include <vector>
15
16#include <yarp/conf/version.h>
17
18#include <yarp/dev/PolyDriver.h>
19#include <yarp/dev/ReturnValue.h>
20
21#include "FutureTask.hpp"
22#include "RawDevice.hpp"
23
24namespace roboticslab
25{
26
68class DeviceMapper final
69{
70public:
73
76
78 void enableParallelization(unsigned int concurrentTasks);
79
81 bool registerDevice(yarp::dev::PolyDriver * driver);
82
84 void clear();
85
87 std::unique_ptr<FutureTask> createTask() const
88 { return taskFactory->createTask(); }
89
91 using dev_index_t = std::tuple<const RawDevice *, int>;
92
94 using dev_group_t = std::tuple<const RawDevice *, std::vector<int>, int>;
95
100 const std::vector<std::unique_ptr<const RawDevice>> & getDevices() const
101 { return devices; }
102
112 dev_index_t getMotorDevice(int globalAxis) const;
113
121 std::vector<dev_index_t> getMotorDevicesWithOffsets() const;
122
131 std::vector<dev_group_t> getMotorDevicesWithIndices(int globalAxesCount, const int * globalAxes) const;
132
135 { return totalAxes; }
136
138#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
139 template<typename T, typename... T_ref>
140 using motor_single_joint_fn = yarp::dev::ReturnValue (T::*)(int, T_ref...);
141
142 template<typename T, typename... T_ref>
143 using motor_single_joint_const_fn = yarp::dev::ReturnValue (T::*)(int, T_ref...) const;
144#else
145 template<typename T, typename... T_ref>
146 using motor_single_joint_fn = bool (T::*)(int, T_ref...);
147#endif
148
150 template<typename T, typename... T_ref, typename... Args>
151#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
152 yarp::dev::ReturnValue mapSingleJoint(motor_single_joint_fn<T, T_ref...> fn, int j, Args &&... args)
153#else
154 bool mapSingleJoint(motor_single_joint_fn<T, T_ref...> fn, int j, Args &&... args)
155#endif
156 {
157 auto [device, offset] = getMotorDevice(j);
158 auto * p = device ? device->getHandle<T>() : nullptr;
159#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
160 return p ? std::invoke(fn, p, offset, std::forward<Args>(args)...) : yarp::dev::ReturnValue_error_method_failed;
161#else
162 return p && std::invoke(fn, p, offset, std::forward<Args>(args)...);
163#endif
164 }
165
166#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
168 template<typename T, typename... T_ref, typename... Args>
169 yarp::dev::ReturnValue mapSingleJoint(motor_single_joint_const_fn<T, T_ref...> fn, int j, Args &&... args) const
170 {
171 auto [device, offset] = getMotorDevice(j);
172 auto * p = device ? device->getHandle<T>() : nullptr;
173 return p ? std::invoke(fn, p, offset, std::forward<Args>(args)...) : yarp::dev::ReturnValue_error_method_failed;
174 }
175#endif
176
178 template<typename T, typename... T_refs>
179#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
180 using motor_all_joints_fn = yarp::dev::ReturnValue (T::*)(T_refs *...);
181#else
182 using motor_all_joints_fn = bool (T::*)(T_refs *...);
183#endif
184
186 template<typename T, typename... T_refs>
187#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
188 yarp::dev::ReturnValue mapAllJoints(motor_all_joints_fn<T, T_refs...> fn, T_refs *... refs)
189#else
191#endif
192 {
193 auto task = createTask();
194 bool ok = false;
195
196 for (const auto & [device, offset] : getMotorDevicesWithOffsets())
197 {
198 auto * p = device ? device->template getHandle<T>() : nullptr;
199 ok |= p && (task->add(p, fn, refs + offset...), true);
200 }
201
202 // at least one targeted device must implement the 'T' iface
203#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
204 return ok && task->dispatch() ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
205#else
206 return ok && task->dispatch();
207#endif
208 }
209
210#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
211 template<typename T, typename Elem>
212 using motor_all_joints_vec_fn = yarp::dev::ReturnValue (T::*)(std::vector<Elem> &);
213
214 // Overload for non-const std::vector<Elem> &.
215 template<typename T, typename Elem>
216 yarp::dev::ReturnValue mapAllJoints(motor_all_joints_vec_fn<T, Elem> fn, std::vector<Elem> & vec)
217 {
218 auto task = createTask();
219 bool ok = false;
220 const auto & devices = getMotorDevicesWithOffsets();
221
222 // store shared/ref sub-vectors so they remain alive and can be copied back
223 struct DeviceSubSlice
224 {
225 int offset;
226 std::vector<Elem> sub_vec;
227 };
228
229 auto slices = std::make_shared<std::vector<DeviceSubSlice>>();
230
231 for (auto i = 0; i < devices.size(); i++)
232 {
233 auto [device, offset] = devices[i];
234 auto * p = device ? device->template getHandle<T>() : nullptr;
235
236 if (p)
237 {
238 auto next_offset = (i + 1 < devices.size()) ? std::get<1>(devices[i + 1]) : vec.size();
239 slices->push_back({offset, std::vector(vec.begin() + offset, vec.begin() + next_offset)});
240 ok |= (task->add(p, fn, std::ref(slices->back().sub_vec)), true);
241 }
242 }
243
244 ok &= task->dispatch();
245
246 if (ok)
247 {
248 for (const auto & slice : *slices)
249 {
250 std::copy(slice.sub_vec.begin(), slice.sub_vec.end(), vec.begin() + slice.offset);
251 }
252 }
253
254 return ok ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
255 }
256
257 template<typename T, typename Elem>
258 using motor_all_joints_const_vec_fn = yarp::dev::ReturnValue (T::*)(const std::vector<Elem> &);
259
260 // Overload for const std::vector<Elem>&.
261 template<typename T, typename Elem>
262 yarp::dev::ReturnValue mapAllJoints(motor_all_joints_const_vec_fn<T, Elem> fn, const std::vector<Elem> & vec)
263 {
264 auto task = createTask();
265 bool ok = false;
266 const auto & devices = getMotorDevicesWithOffsets();
267
268 for (auto i = 0; i < devices.size(); i++)
269 {
270 auto [device, offset] = devices[i];
271 auto * p = device ? device->template getHandle<T>() : nullptr;
272
273 if (p)
274 {
275 auto next_offset = (i + 1 < devices.size()) ? std::get<1>(devices[i + 1]) : vec.size();
276 ok |= (task->add(p, fn, std::vector(vec.begin() + offset, vec.begin() + next_offset)), true);
277 }
278 }
279
280 return ok && task->dispatch() ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
281 }
282#endif
283
285 template<typename T, typename... T_refs>
286#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
287 using motor_multi_joints_fn = yarp::dev::ReturnValue (T::*)(int, const int *, T_refs *...);
288#else
289 using motor_multi_joints_fn = bool (T::*)(int, const int *, T_refs *...);
290#endif
291
293 template<typename T, typename... T_refs>
294#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
295 yarp::dev::ReturnValue mapJointGroup(motor_multi_joints_fn<T, T_refs...> fn, int n_joint, const int * joints, T_refs *... refs)
296#else
297 bool mapJointGroup(motor_multi_joints_fn<T, T_refs...> fn, int n_joint, const int * joints, T_refs *... refs)
298#endif
299 {
300 auto task = createTask();
301 auto devices = getMotorDevicesWithIndices(n_joint, joints); // extend lifetime of vector of local indices
302 bool ok = true;
303
304 for (const auto & [device, localIndices, globalIndex] : devices)
305 {
306 auto * p = device ? device->template getHandle<T>() : nullptr;
307 ok &= p && (task->add(p, fn, localIndices.size(), localIndices.data(), refs + globalIndex...), true);
308 }
309
310 // all targeted devices must implement the 'T' iface
311#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
312 return ok && task->dispatch() ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
313#else
314 return ok && task->dispatch();
315#endif
316 }
317
318#if YARP_VERSION_COMPARE(>=, 4, 0, 0)
319 template<typename T, typename Elem>
320 using motor_multi_joints_vec_fn = yarp::dev::ReturnValue (T::*)(const std::vector<int> &, std::vector<Elem> &);
321
322 // Overload for non-const std::vector output parameters (getters)
323 template<typename T, typename Elem>
324 yarp::dev::ReturnValue mapJointGroup(motor_multi_joints_vec_fn<T, Elem> fn, const std::vector<int> & joints, std::vector<Elem> & vec)
325 {
326 auto task = createTask();
327 auto devices = getMotorDevicesWithIndices(joints.size(), joints.data());
328 bool ok = true;
329
330 // data structure to hold local joint indices and sub-vector slice per device
331 struct SliceInfo
332 {
333 std::vector<size_t> global_indices;
334 std::vector<int> local_indices;
335 std::vector<Elem> sub_vec;
336 };
337
338 auto slices = std::make_shared<std::vector<SliceInfo>>();
339 slices->reserve(devices.size());
340
341 for (const auto & [device, localIndices, globalIndex] : devices)
342 {
343 auto * p = device ? device->template getHandle<T>() : nullptr;
344
345 if (p)
346 {
347 SliceInfo info;
348 info.local_indices = localIndices;
349 info.global_indices.reserve(localIndices.size());
350 info.sub_vec.reserve(localIndices.size());
351
352 // extract current elements corresponding to the targeted global joints
353 for (auto i = 0; i < localIndices.size(); ++i)
354 {
355 auto g_idx = globalIndex + i; // or the global joint index for this element
356 info.global_indices.push_back(g_idx);
357 info.sub_vec.push_back(vec[g_idx]);
358 }
359
360 slices->push_back(std::move(info));
361 auto & current = slices->back();
362 ok &= (task->add(p, fn, std::cref(current.local_indices), std::ref(current.sub_vec)), true);
363 }
364 }
365
366 ok &= task->dispatch();
367
368 if (ok)
369 {
370 for (const auto & slice : *slices)
371 {
372 for (auto i = 0; i < slice.sub_vec.size(); ++i)
373 {
374 vec[slice.global_indices[i]] = slice.sub_vec[i];
375 }
376 }
377 }
378
379 return ok ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
380 }
381
382 template<typename T, typename Elem>
383 using motor_multi_joints_const_vec_fn = yarp::dev::ReturnValue (T::*)(const std::vector<int> &, const std::vector<Elem> &);
384
385 // Overload for const std::vector input parameters (setters).
386 template<typename T, typename Elem>
387 yarp::dev::ReturnValue mapJointGroup(motor_multi_joints_const_vec_fn<T, Elem> fn, const std::vector<int> & joints, const std::vector<Elem> & vec)
388 {
389 auto task = createTask();
390 auto devices = getMotorDevicesWithIndices(joints.size(), joints.data());
391 bool ok = true;
392
393 struct SliceInfo
394 {
395 std::vector<int> local_indices;
396 std::vector<Elem> sub_vec;
397 };
398
399 auto slices = std::make_shared<std::vector<SliceInfo>>();
400 slices->reserve(devices.size());
401
402 for (const auto & [device, localIndices, globalIndex] : devices)
403 {
404 auto * p = device->template getHandle<T>();
405
406 if (p)
407 {
408 SliceInfo info;
409 info.local_indices = localIndices;
410 info.sub_vec.reserve(localIndices.size());
411
412 for (auto i = 0; i < localIndices.size(); ++i)
413 {
414 info.sub_vec.push_back(vec[globalIndex + i]);
415 }
416
417 slices->push_back(std::move(info));
418 auto & current = slices->back();
419 ok &= (task->add(p, fn, std::cref(current.local_indices), std::cref(current.sub_vec)), true);
420 }
421 }
422
423 return ok && task->dispatch() ? yarp::dev::ReturnValue_ok : yarp::dev::ReturnValue_error_method_failed;
424 }
425#endif
426
428 template<typename T>
429#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
430 yarp::dev::ReturnValue getConnectedSensors(std::size_t & num) const
431#else
432 std::size_t getConnectedSensors() const
433#endif
434 {
435 // operator[] will insert a default-constructed value if not found
436 if (auto it = connectedSensors.find(typeid(T)); it != connectedSensors.cend())
437 {
438#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
439 num = it->second;
440 return yarp::dev::ReturnValue_ok;
441#else
442 return it->second;
443#endif
444 }
445
446#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
447 return yarp::dev::ReturnValue_error_method_failed;
448#else
449 return 0;
450#endif
451 }
452
459 template<typename T>
460 dev_index_t getSensorDevice(int globalIndex) const
461 {
462 if (auto it = sensorOffsets.find(typeid(T)); it != sensorOffsets.cend())
463 {
464 const auto & [deviceIndex, offset, count] = it->second[globalIndex];
465 return {devices[deviceIndex].get(), globalIndex - offset};
466 }
467
468 return {&invalidDevice, 0};
469 }
470
471 template<typename T, typename T_out>
472 using sensor_status_fn = T_out (T::*)(std::size_t) const;
473
478 template<typename T, typename T_out>
479 T_out getSensorStatus(sensor_status_fn<T, T_out> fn, std::size_t index) const
480 {
481 auto [device, offset] = getSensorDevice<T>(index);
482 auto * p = device->template getHandle<T>();
483 return p ? std::invoke(fn, p, offset) : static_cast<T_out>(DeviceMapper::getSensorFailureStatus());
484 }
485
486 template<typename T>
487 using sensor_size_fn = std::size_t (T::*)(std::size_t) const;
488
493 template<typename T>
494 std::size_t getSensorArraySize(sensor_size_fn<T> fn, std::size_t index) const
495 {
496 auto [device, offset] = getSensorDevice<T>(index);
497 auto * p = device->template getHandle<T>();
498 return p ? std::invoke(fn, p, offset) : 0;
499 }
500
501 template<typename T, typename... T_out_params>
502#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
503 using sensor_output_fn = yarp::dev::ReturnValue (T::*)(std::size_t, T_out_params &...) const;
504#else
505 using sensor_output_fn = bool (T::*)(std::size_t, T_out_params &...) const;
506#endif
507
512 template<typename T, typename... T_out_params>
513#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
514 yarp::dev::ReturnValue getSensorOutput(sensor_output_fn<T, T_out_params...> fn, std::size_t index, T_out_params &... params) const
515#else
516 bool getSensorOutput(sensor_output_fn<T, T_out_params...> fn, std::size_t index, T_out_params &... params) const
517#endif
518 {
519 auto [device, offset] = getSensorDevice<T>(index);
520 auto * p = device->template getHandle<T>();
521#if YARP_VERSION_COMPARE(>=, 4, 1, 0) || defined(YARP_NEXT)
522 return p ? std::invoke(fn, p, offset, params...) : yarp::dev::ReturnValue_error_method_failed;
523#else
524 return p ? std::invoke(fn, p, offset, params...) : false;
525#endif
526 }
527
528private:
529 static const int getSensorFailureStatus();
530
531 using dev_index_offset_t = std::tuple<int, int, int>;
532
533 std::vector<std::unique_ptr<const RawDevice>> devices;
534 std::vector<dev_index_offset_t> motorOffsets;
535 std::unordered_map<std::type_index, std::vector<dev_index_offset_t>> sensorOffsets;
536 std::unordered_map<std::type_index, std::size_t> connectedSensors;
537
538 int totalAxes {0};
539
540 std::unique_ptr<FutureTaskFactory> taskFactory;
541};
542
543} // namespace roboticslab
544
545#endif // __DEVICE_MAPPER_HPP__
Exposes raw subdevice interface handles on a per-axis manner.
Definition DeviceMapper.hpp:69
std::vector< dev_index_t > getMotorDevicesWithOffsets() const
Retrieve all registered motor subdevices and their associated offsets.
Definition DeviceMapper.cpp:206
std::tuple< const RawDevice *, std::vector< int >, int > dev_group_t
Tuple of a raw device pointer, its local indices and the global index.
Definition DeviceMapper.hpp:94
std::tuple< const RawDevice *, int > dev_index_t
Tuple of a raw device pointer and either an offset or a local index.
Definition DeviceMapper.hpp:91
std::unique_ptr< FutureTask > createTask() const
Create an instance of a deferred task.
Definition DeviceMapper.hpp:87
bool(T::*)(T_refs *...) motor_all_joints_fn
Alias for a full-joint command. See class description.
Definition DeviceMapper.hpp:182
const std::vector< std::unique_ptr< const RawDevice > > & getDevices() const
Retrieve all registered raw devices, regardless of type.
Definition DeviceMapper.hpp:100
std::size_t getSensorArraySize(sensor_size_fn< T > fn, std::size_t index) const
Retrieve the size of the sensor array at the specified global index.
Definition DeviceMapper.hpp:494
DeviceMapper()
Constructor.
Definition DeviceMapper.cpp:150
void enableParallelization(unsigned int concurrentTasks)
Whether to enable parallel mappings and on how many concurrent threads.
Definition DeviceMapper.cpp:159
bool(T::*)(int, T_ref...) motor_single_joint_fn
Alias for a single-joint command. See class description.
Definition DeviceMapper.hpp:146
dev_index_t getSensorDevice(int globalIndex) const
Retrieve a sensor device handle and its local index given a global index.
Definition DeviceMapper.hpp:460
void clear()
Delete all internal handles.
Definition DeviceMapper.cpp:245
bool registerDevice(yarp::dev::PolyDriver *driver)
Extract interface handles and perform sanity checks.
Definition DeviceMapper.cpp:164
~DeviceMapper()
Destructor.
Definition DeviceMapper.cpp:154
T_out getSensorStatus(sensor_status_fn< T, T_out > fn, std::size_t index) const
Retrieve the status of the sensor device at the specified global index.
Definition DeviceMapper.hpp:479
std::vector< dev_group_t > getMotorDevicesWithIndices(int globalAxesCount, const int *globalAxes) const
Retrieve motor subdevices that map to the specified global axes.
Definition DeviceMapper.cpp:220
int getControlledAxes() const
Retrieve the number of controlled axes across all subdevices.
Definition DeviceMapper.hpp:134
bool mapSingleJoint(motor_single_joint_fn< T, T_ref... > fn, int j, Args &&... args)
Single-joint non-const command mapping. See class description.
Definition DeviceMapper.hpp:154
bool(T::*)(int, const int *, T_refs *...) motor_multi_joints_fn
Alias for a joint-group command. See class description.
Definition DeviceMapper.hpp:289
std::size_t getConnectedSensors() const
Retrieve the number of connected sensors of the specified type across all subdevices.
Definition DeviceMapper.hpp:432
bool mapJointGroup(motor_multi_joints_fn< T, T_refs... > fn, int n_joint, const int *joints, T_refs *... refs)
Joint-group command mapping. See class description.
Definition DeviceMapper.hpp:297
bool mapAllJoints(motor_all_joints_fn< T, T_refs... > fn, T_refs *... refs)
Full-joint command mapping. See class description.
Definition DeviceMapper.hpp:190
dev_index_t getMotorDevice(int globalAxis) const
Retrieve a motor device handle and its local index given a global index.
Definition DeviceMapper.cpp:200
bool getSensorOutput(sensor_output_fn< T, T_out_params... > fn, std::size_t index, T_out_params &... params) const
Retrieve information from the sensor device at the specified global index.
Definition DeviceMapper.hpp:516
The main, catch-all namespace for Robotics Lab UC3M.
Definition groups.dox:6
const RawDevice invalidDevice(nullptr)
Singleton instance for an invalid (empty) raw device.