ControllerBuilder.h
Go to the documentation of this file.
1/**
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @author Fabian Reister ( fabian dot reister at kit dot edu )
17 * @date 2022
18 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19 * GNU General Public License
20 */
21
22#pragma once
23
24#include <filesystem>
25#include <fstream>
26#include <optional>
27#include <type_traits>
28
29#include <SimoxUtility/json/json.hpp>
30
34
36#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h>
38
42#include <armarx/control/core/Controller.h>
43#include <armarx/control/interface/ConfigurableNJointControllerInterface.h>
44#include <armarx/control/memory/config/Reader.h>
45#include <armarx/control/memory/config/Writer.h>
47
49{
50
51 template <auto T>
52 class ControllerBuilder //: public AbstractControllerBuilder
53 {
54 public:
56
57 // Use ProxyType_t to conditionally use Proxy or the default fallback
59
60 // Use CtrlConfigClassName_v to conditionally use ctrlConfigClassName or fallback to 'name'
61 static constexpr const char* ctrlConfigClassName =
63
64 // static constexpr auto ControllerType = ct;
65 using AronDTO = typename ControllerDescriptionType::AronDTO;
66 // using AronType = AronDTO;
67
68 static_assert(::armarx::aron::cpp::isAronGeneratedClass<AronDTO>,
69 "The AronConfigT must be an AronGeneratedClass!");
70
72 const memory::config::Reader& configProvider,
73 const memory::config::Writer& configWriter,
74 const std::string& controllerNamePrefix) :
75 controllerCreator(controllerCreator),
76 configReader(configProvider),
77 configWriter(configWriter),
78 controllerNamePrefix(controllerNamePrefix)
79 {
80 ARMARX_CHECK_NOT_EMPTY(controllerNamePrefix)
81 << "By convention, the controller name prefix must exist. This ensures that "
82 "in between components, no naming clashes occur.";
83 }
84
86 withDefaultConfig(const std::string& name = "default")
87 {
88 if (auto cfg = configReader.getDefaultConfig(ControllerDescriptionType::name, name))
89 {
90 dto = AronDTO::FromAron(cfg);
91 }
92 else
93 {
94 ARMARX_WARNING << "Failed to obtain default config";
95 }
96
97 return *this;
98 }
99
101 withDefaultConfigFile(const std::string& name = "default",
102 const std::string& robotNameSuffix = "",
103 const std::string& appendix = "")
104 {
105 const std::string configClassName = ctrlConfigClassName;
106
107 const armarx::PackagePath configPath("armarx_control",
108 "controller_config/" + configClassName + "/" +
109 name + robotNameSuffix + appendix + ".json");
110 ARMARX_INFO << "using default config: " << configPath.toSystemPath();
111 return withConfig(configPath.toSystemPath());
112 }
113
116 {
117 auto cfg = configReader.getConfig(memoryId);
118 dto = AronDTO::FromAron(cfg);
119
120 return *this;
121 }
122
124 withConfig(const ::armarx::aron::data::dto::DictPtr& configDict)
125 {
126 dto = AronDTO::FromAron(configDict);
127 return *this;
128 }
129
131 withConfig(const std::filesystem::path& filename)
132 {
133 ARMARX_CHECK(std::filesystem::is_regular_file(filename)) << filename;
134
135 ARMARX_IMPORTANT << VAROUT(typeid(AronDTO).name());
136
137 ARMARX_INFO << "Loading config from file `" << filename << "`.";
138 std::ifstream ifs{filename};
139
140 nlohmann::json jsonConfig;
141 ifs >> jsonConfig;
142
143 ARMARX_VERBOSE << "Initializing config";
144 if (not this->dto.has_value())
145 {
146 this->dto = AronDTO();
147 }
148
150
151 ARMARX_CHECK(this->dto.has_value());
152
153 ARMARX_VERBOSE << "reading file.";
154 this->dto->read(reader, jsonConfig);
155
156 return *this;
157 }
158
160 withConfig(const AronDTO& dto)
161 {
162 this->dto = dto;
163 return *this;
164 }
165
166 AronDTO&
168 {
169 ensureInitialized();
170
171 return dto.value();
172 }
173
175 withNodeSet(const std::string& nodeSetName)
176 {
177 this->nodeSetName = nodeSetName;
178 return *this;
179 }
180
182 withNamePrefix(const std::string& namePrefix)
183 {
184 if (namePrefix.empty())
185 {
186 return *this;
187 }
188 this->controllerNamePrefix = this->controllerNamePrefix + "_" + namePrefix;
189 return *this;
190 }
191
194 {
195 this->daemonized = true;
196 return *this;
197 }
198
199 /**
200 * @brief Creates the controller with the default name
201 *
202 * @return std::optional<ControllerWrapper<AronDTO, ct>>
203 */
204 std::unique_ptr<ControllerWrapper<T>>
206 {
207 const std::string controllerClassName = ControllerDescriptionType::name;
208 return create(controllerClassName);
209 }
210
211 std::unique_ptr<ControllerWrapper<T>>
212 create(const std::string& name)
213 {
214 if (not ensureInitialized())
215 {
216 return nullptr;
217 }
218
219 ::armarx::control::ConfigurableNJointControllerConfigPtr config =
220 new ::armarx::control::ConfigurableNJointControllerConfig;
221
222 auto start = armarx::DateTime::now();
223 config->config = dto.value().toAronDTO();
224 ARMARX_DEBUG << "--> Aron conversion took "
225 << (armarx::DateTime::now() - start).toSecondsDouble() << " sec.";
226
227 // if (not nodeSetName.has_value())
228 // {
229 // ARMARX_WARNING << "You forgot to set the node set name!";
230 // return std::nullopt;
231 // }
232 // config->nodeSetName = nodeSetName.value();
233
234 // controllerName = controllerNamePrefix + "_" + nodeSetName.value() + "_" + name;
235 controllerName = controllerNamePrefix + "_" + name;
236 const std::string controllerClassName = ControllerDescriptionType::name;
237
238 // reuse or create controller
239
240 start = armarx::DateTime::now();
241 armarx::NJointControllerInterfacePrx controller =
242 controllerCreator->getNJointController(controllerName);
243 ARMARX_DEBUG << "--> get controller by name from robot unit took "
244 << (armarx::DateTime::now() - start).toSecondsDouble() << " sec.";
245
246 this->reusing_ = [&]() -> bool
247 {
248 if (not controller)
249 {
250 ARMARX_INFO << "Controller does not exist yet.";
251 return false;
252 }
253
254 if (not allowReuse_)
255 {
256 ARMARX_INFO << "Not allowed to reuse controller.";
257 return false;
258 }
259
260 // check if controller has correct type
261 if (controller->getClassName() != controllerClassName)
262 {
263 ARMARX_INFO << "Controller class does not match.";
264 return false;
265 }
266
267 // TODO: check if kinematic chains match
268 // if (controller->getNodeSetName() != nodeSetName)
269 // {
270 // return false;
271 // }
272
273 return true;
274 }();
275
276 // if((not allowReuse_) and controller)
277 // {
278 // ARMARX_TRACE;
279 //
280 // ARMARX_INFO << "Deleting existing controller `" << controllerName << "`";
281 // try
282 // {
283 // ARMARX_TRACE;
284 // controllerCreator->deactivateAndDeleteNJointController(controllerName);
285 // }
286 // catch(...)
287 // {
288 // ARMARX_WARNING << GetHandledExceptionString();
289 // }
290 //
291 // ARMARX_INFO << "Done deleting";
292 //
293 // Clock::WaitFor(Duration::MilliSeconds(150));
294 // }
295
296 if ((not allowReuse_) and controller)
297 {
299
300 ARMARX_INFO << "--- Deleting existing controller `" << controllerName << "`";
301 try
302 {
304 ARMARX_INFO << "--- Wait until controller is in-active";
305 // controllerCreator->deactivateAndDeleteNJointController(controllerName);
306 controller->deactivateController();
307
308 while (controller->isControllerActive())
309 {
311 }
312
313 ARMARX_INFO << "--- Controller is not active anymore, deleting controller";
314 controller->deleteController();
315 }
316 catch (...)
317 {
319 }
320
321 ARMARX_INFO << "--- Done deleting";
322
324 }
325 else
326 {
327 ARMARX_INFO << "Reuse of controller would be possible.";
328 }
329
330 // create controller if necessary
331 if (not this->reusing_)
332 {
334
335 ARMARX_INFO << "Creating controller `" << controllerName << "`";
336 start = armarx::DateTime::now();
337 controller = controllerCreator->createNJointController(
338 controllerClassName, controllerName, config);
339 ARMARX_DEBUG << "--> creating new controller took "
340 << (armarx::DateTime::now() - start).toSecondsDouble() << " sec.";
342 ARMARX_INFO << "Controller `" << controllerName << "` created.";
343 }
344 else
345 {
346 ARMARX_IMPORTANT << "Reusing the controller: " << controllerName;
347 }
348
349 if (not controller)
350 {
351 ARMARX_WARNING << "Failed to create controller of type `" << controllerClassName
352 << "`";
353 return nullptr;
354 }
355
356 auto ctrl =
357 armarx::control::ConfigurableNJointControllerInterfacePrx::checkedCast(controller);
358
359 std::unique_ptr<ControllerWrapper<T>> ctrlWrapper =
360 std::make_unique<ControllerWrapper<T>>(
361 ctrl, controllerName, configWriter, dto.value());
362 ctrlWrapper->daemonize(daemonized);
363
364 // Sends the data to the memory and updates the controller config.
365 // This is especially important, if the controller is reused.
366 start = armarx::DateTime::now();
367 ctrlWrapper->updateConfig();
368 ARMARX_DEBUG << "--> ctrlWrapper->updateConfig() took "
369 << (armarx::DateTime::now() - start).toSecondsDouble() << " sec.";
370 return ctrlWrapper;
371 }
372
373 std::unique_ptr<ControllerWrapper<T>>
374 createTSComplianceCtrl(const std::string& namePrefix = "",
375 const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
376 const std::string& configFilename = "",
377 bool flagActivate = true,
378 bool flagAllowReuse = true,
379 bool flagFromMemory = false)
380 {
381 ARMARX_CHECK(not(configDict.get() == nullptr and configFilename.empty()));
382 /// load predefined config from file or use the configFilename provided by the user
383 auto start = armarx::DateTime::now();
384 if (configDict)
385 {
386 withConfig(configDict);
387 }
388 else
389 {
390 if (configFilename.empty())
391 {
392 if (flagFromMemory)
394 else
396 }
397 else
398 {
399 ARMARX_CHECK(std::filesystem::exists(configFilename))
400 << "configuration file does not exist\n"
401 << configFilename
402 << "Other tips: \n"
403 "1) if you need to configure a controller by passing configuration "
404 "file "
405 "to "
406 "controllerBuilder (me), you need to make sure the "
407 "controllerCreator "
408 "component is running "
409 "on the same PC as your application, otherwise\n"
410 "2) use the createControllerFromConfig() interface, which does not "
411 "require "
412 "where controller"
413 "creator is running";
414 ARMARX_IMPORTANT << "Using user defined configuration file:\n"
415 << configFilename;
416 withConfig(configFilename);
417 }
418 }
419 ARMARX_DEBUG << "--> Loading config took "
420 << (armarx::DateTime::now() - start).toSecondsDouble() << " sec.";
421
422 allowReuse(flagAllowReuse);
423 daemonize();
424
425 std::ostringstream oss;
426 for (const auto& pair : config().limbs)
427 {
428 oss << pair.first;
429 if (&pair != &(*config().limbs.rbegin()))
430 {
431 oss << ",";
432 }
433 }
434 std::string robotNodeSets = oss.str();
435
436 if (namePrefix.empty())
437 {
438 withNamePrefix(robotNodeSets);
439 }
440 else
441 {
442 // allowReuse(true);
443 withNamePrefix(namePrefix + "_" + robotNodeSets);
444 }
445
447 ARMARX_INFO << "creating controller";
448
449 auto ctrlWrapper = create();
450
452
453 ARMARX_INFO << "using controller name " << controllerName;
455
456 if (getReusing())
457 {
459 << "setting nullspace target: if the default is null use the cfg from the "
460 "existing controller";
461 /// get up-to-date controller configs and update the copy of it in wrapper
462 /// with the null space target from the controller
463 auto ctrlConfig = ctrlWrapper->getConfig().limbs;
464 for (const auto& pair : config().limbs)
465 {
466 if (not pair.second.desiredNullspaceJointAngles.has_value())
467 {
468 ctrlWrapper->config.limbs.at(pair.first).desiredNullspaceJointAngles =
469 ctrlConfig.at(pair.first).desiredNullspaceJointAngles;
470 }
471 // ctrlWrapper->config.limbs.at(pair.first).desiredPose =
472 // ctrlConfig.at(pair.first).desiredPose;
473 // ARMARX_INFO << "Activating controller ..."
474 // << ctrlWrapper->config.limbs.at(pair.first)
475 // .desiredNullspaceJointAngles.has_value();
476 }
477 }
478
480 // ctrlWrapper->updateConfig();
481 if (flagActivate)
482 {
483 if (not ctrlWrapper->isActive())
484 {
485 ctrlWrapper->activate();
487 while (not ctrlWrapper->isActive())
488 {
490 }
491 }
492 ARMARX_INFO << "Controller " << controllerName << " is active";
493 }
494 else
495 {
496 ARMARX_INFO << "User have to activate " << controllerName
497 << " controller in your application";
498 }
499 return ctrlWrapper;
500 }
501
503 createGetTSComplianceCtrl(const std::string& namePrefix = "",
504 const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
505 const std::string& configFilename = "",
506 bool flagActivate = true,
507 bool flagAllowReuse = true,
508 bool flagFromMemory = false)
509 {
510 auto ctrlWrapper = createTSComplianceCtrl(namePrefix,
511 configDict,
512 configFilename,
513 flagActivate,
514 flagAllowReuse,
515 flagFromMemory);
516 auto ctrl = CtrlProxy::checkedCast(ctrlWrapper->ctrl());
517 return ctrl;
518 }
519
520 std::unique_ptr<ControllerWrapper<T>>
521 createTSComplianceMPCtrl(const std::string& namePrefix,
522 const ::armarx::aron::data::dto::DictPtr& mpConfigDict,
523 const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
524 const std::string& configFilename = "",
525 bool flagActivate = true,
526 bool flagAllowReuse = true,
527 bool flagFromMemory = false,
528 bool flagStart = true)
529 {
530 auto ctrlWrapper = createTSComplianceCtrl(namePrefix,
531 configDict,
532 configFilename,
533 flagActivate,
534 flagAllowReuse,
535 flagFromMemory);
536 auto ctrl = CtrlProxy::checkedCast(ctrlWrapper->ctrl());
537 ctrl->updateMPConfig(mpConfigDict);
538 ctrl->trainMP();
539
540 if (flagStart)
541 {
542 // start controller
544 ctrl->startAll();
545 }
546 return ctrlWrapper;
547 }
548
550 createGetTSComplianceMPCtrl(const std::string& namePrefix,
551 const ::armarx::aron::data::dto::DictPtr& mpConfigDict,
552 const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
553 const std::string& configFilename = "",
554 bool flagActivate = true,
555 bool flagAllowReuse = true,
556 bool flagFromMemory = false,
557 bool flagStart = true)
558 {
559 auto ctrlWrapper = createTSComplianceCtrl(namePrefix,
560 configDict,
561 configFilename,
562 flagActivate,
563 flagAllowReuse,
564 flagFromMemory);
565 auto ctrl = CtrlProxy::checkedCast(ctrlWrapper->ctrl());
566 ctrl->stopAll();
567 auto start = armarx::DateTime::Now();
568 ctrl->updateMPConfig(mpConfigDict);
569 ARMARX_DEBUG << "--> ctrl->updateMPConfig(mpConfigDict: DictPtr) took "
570 << (armarx::DateTime::Now() - start).toSecondsDouble() << " sec.";
571
572 ARMARX_DEBUG << "--> calling train mp";
573 start = armarx::DateTime::Now();
574 ctrl->trainMP();
575 ARMARX_DEBUG << "--> ctrl->trainMP() took "
576 << (armarx::DateTime::Now() - start).toSecondsDouble() << " sec.";
577
578 if (flagStart)
579 {
580 // start controller
582 ctrl->startAll();
583 }
584 return ctrl;
585 }
586
589 {
590 this->allowReuse_ = allowReuse;
591 return *this;
592 }
593
594 bool
596 {
597 return this->reusing_;
598 }
599
600 std::string
602 {
603 return controllerName;
604 }
605
606 private:
607 bool
608 initDefaultConfig()
609 {
610 if (auto cfg = configReader.getDefaultConfig(ControllerDescriptionType::name))
611 {
612 dto = AronDTO::FromAron(cfg);
613 return true;
614 }
615
616 ARMARX_WARNING << "Failed to obtain default config";
617 return false;
618 }
619
620 bool
621 ensureInitialized()
622 {
623 if (not dto.has_value())
624 {
625 return initDefaultConfig();
626 }
627
628 return true;
629 }
630
631 armarx::RobotUnitInterfacePrx controllerCreator;
632
633 const memory::config::Reader configReader;
634 const memory::config::Writer configWriter;
635
636 std::optional<AronDTO> dto;
637
638 std::optional<std::string> nodeSetName;
639
640 std::string controllerNamePrefix;
641 std::string controllerName;
642
643 bool daemonized = false;
644 bool allowReuse_ = false;
645 bool reusing_ = false;
646 };
647} // namespace armarx::control::client
#define ARMARX_CHECK_NOT_EMPTY(c)
#define VAROUT(x)
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
Definition Clock.cpp:99
static DateTime Now()
Definition DateTime.cpp:51
static DateTime now()
Definition DateTime.h:87
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
Definition Duration.cpp:78
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
static std::filesystem::path toSystemPath(const data::PackagePath &pp)
ControllerBuilder & withNodeSet(const std::string &nodeSetName)
ControllerBuilder & allowReuse(const bool allowReuse)
typename ControllerDescriptionType::AronDTO AronDTO
ControllerBuilder & withConfig(const ::armarx::aron::data::dto::DictPtr &configDict)
ControllerBuilder(const armarx::RobotUnitInterfacePrx &controllerCreator, const memory::config::Reader &configProvider, const memory::config::Writer &configWriter, const std::string &controllerNamePrefix)
ControllerBuilder & withDefaultConfig(const std::string &name="default")
ControllerBuilder & withConfig(const AronDTO &dto)
std::unique_ptr< ControllerWrapper< T > > create()
Creates the controller with the default name.
CtrlProxy createGetTSComplianceCtrl(const std::string &namePrefix="", const ::armarx::aron::data::dto::DictPtr &configDict=nullptr, const std::string &configFilename="", bool flagActivate=true, bool flagAllowReuse=true, bool flagFromMemory=false)
std::unique_ptr< ControllerWrapper< T > > createTSComplianceCtrl(const std::string &namePrefix="", const ::armarx::aron::data::dto::DictPtr &configDict=nullptr, const std::string &configFilename="", bool flagActivate=true, bool flagAllowReuse=true, bool flagFromMemory=false)
ControllerBuilder & withConfig(const std::filesystem::path &filename)
ControllerBuilder & withNamePrefix(const std::string &namePrefix)
detail::proxy::ProxyType_t< ControllerDescriptionType > CtrlProxy
ControllerBuilder & withConfig(const armarx::armem::MemoryID &memoryId)
CtrlProxy createGetTSComplianceMPCtrl(const std::string &namePrefix, const ::armarx::aron::data::dto::DictPtr &mpConfigDict, const ::armarx::aron::data::dto::DictPtr &configDict=nullptr, const std::string &configFilename="", bool flagActivate=true, bool flagAllowReuse=true, bool flagFromMemory=false, bool flagStart=true)
static constexpr const char * ctrlConfigClassName
std::unique_ptr< ControllerWrapper< T > > create(const std::string &name)
ControllerBuilder & withDefaultConfigFile(const std::string &name="default", const std::string &robotNameSuffix="", const std::string &appendix="")
ControllerDescription< T > ControllerDescriptionType
std::unique_ptr< ControllerWrapper< T > > createTSComplianceMPCtrl(const std::string &namePrefix, const ::armarx::aron::data::dto::DictPtr &mpConfigDict, const ::armarx::aron::data::dto::DictPtr &configDict=nullptr, const std::string &configFilename="", bool flagActivate=true, bool flagAllowReuse=true, bool flagFromMemory=false, bool flagStart=true)
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
typename ProxyType< T >::type ProxyType_t
This file is part of ArmarX.
::IceInternal::ProxyHandle<::IceProxy::armarx::RobotUnitInterface > RobotUnitInterfacePrx
std::string GetHandledExceptionString()
#define ARMARX_TRACE
Definition trace.h:77