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 =
62  detail::ctrl_class_name::CtrlConfigClassName_v<ControllerDescriptionType>;
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 
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_INFO << "Loading config from file `" << filename << "`.";
136  std::ifstream ifs{filename};
137 
138  nlohmann::json jsonConfig;
139  ifs >> jsonConfig;
140 
141  ARMARX_INFO << "Initializing config";
142  if (not this->dto.has_value())
143  {
144  this->dto = AronDTO();
145  }
146 
148 
149  ARMARX_CHECK(this->dto.has_value());
150 
151  ARMARX_INFO << "reading file.";
152  this->dto->read(reader, jsonConfig);
153 
154  return *this;
155  }
156 
158  withConfig(const AronDTO& dto)
159  {
160  this->dto = dto;
161  return *this;
162  }
163 
164  AronDTO&
166  {
167  ensureInitialized();
168 
169  return dto.value();
170  }
171 
173  withNodeSet(const std::string& nodeSetName)
174  {
175  this->nodeSetName = nodeSetName;
176  return *this;
177  }
178 
180  withNamePrefix(const std::string& namePrefix)
181  {
182  if (namePrefix.empty())
183  {
184  return *this;
185  }
186  this->controllerNamePrefix = this->controllerNamePrefix + "_" + namePrefix;
187  return *this;
188  }
189 
192  {
193  this->daemonized = true;
194  return *this;
195  }
196 
197  /**
198  * @brief Creates the controller with the default name
199  *
200  * @return std::optional<ControllerWrapper<AronDTO, ct>>
201  */
202  std::unique_ptr<ControllerWrapper<T>>
204  {
205  const std::string controllerClassName = ControllerDescriptionType::name;
206  return create(controllerClassName);
207  }
208 
209  std::unique_ptr<ControllerWrapper<T>>
210  create(const std::string& name)
211  {
212  if (not ensureInitialized())
213  {
214  return nullptr;
215  }
216 
217  ::armarx::control::ConfigurableNJointControllerConfigPtr config =
218  new ::armarx::control::ConfigurableNJointControllerConfig;
219  config->config = dto.value().toAronDTO();
220 
221  // if (not nodeSetName.has_value())
222  // {
223  // ARMARX_WARNING << "You forgot to set the node set name!";
224  // return std::nullopt;
225  // }
226  // config->nodeSetName = nodeSetName.value();
227 
228  // controllerName = controllerNamePrefix + "_" + nodeSetName.value() + "_" + name;
229  controllerName = controllerNamePrefix + "_" + name;
230  const std::string controllerClassName = ControllerDescriptionType::name;
231 
232  // reuse or create controller
233 
234  armarx::NJointControllerInterfacePrx controller =
235  controllerCreator->getNJointController(controllerName);
236 
237 
238  this->reusing_ = [&]() -> bool
239  {
240  if (not controller)
241  {
242  ARMARX_INFO << "Controller does not exist yet.";
243  return false;
244  }
245 
246  if (not allowReuse_)
247  {
248  ARMARX_INFO << "Not allowed to reuse controller.";
249  return false;
250  }
251 
252  // check if controller has correct type
253  if (controller->getClassName() != controllerClassName)
254  {
255  ARMARX_INFO << "Controller class does not match.";
256  return false;
257  }
258 
259  // TODO: check if kinematic chains match
260  // if (controller->getNodeSetName() != nodeSetName)
261  // {
262  // return false;
263  // }
264 
265  return true;
266  }();
267 
268  // if((not allowReuse_) and controller)
269  // {
270  // ARMARX_TRACE;
271  //
272  // ARMARX_INFO << "Deleting existing controller `" << controllerName << "`";
273  // try
274  // {
275  // ARMARX_TRACE;
276  // controllerCreator->deactivateAndDeleteNJointController(controllerName);
277  // }
278  // catch(...)
279  // {
280  // ARMARX_WARNING << GetHandledExceptionString();
281  // }
282  //
283  // ARMARX_INFO << "Done deleting";
284  //
285  // Clock::WaitFor(Duration::MilliSeconds(150));
286  // }
287 
288  if ((not allowReuse_) and controller)
289  {
290  ARMARX_TRACE;
291 
292  ARMARX_INFO << "--- Deleting existing controller `" << controllerName << "`";
293  try
294  {
295  ARMARX_TRACE;
296  ARMARX_INFO << "--- Wait until controller is in-active";
297  // controllerCreator->deactivateAndDeleteNJointController(controllerName);
298  controller->deactivateController();
299 
300  while (controller->isControllerActive())
301  {
303  }
304 
305  ARMARX_INFO << "--- Controller is not active anymore, deleting controller";
306  controller->deleteController();
307  }
308  catch (...)
309  {
311  }
312 
313  ARMARX_INFO << "--- Done deleting";
314 
316  }
317  else
318  {
319  ARMARX_INFO << "resue controller is possible";
320  }
321 
322  // create controller if necessary
323  if (not this->reusing_)
324  {
325  ARMARX_TRACE;
326 
327  ARMARX_INFO << "Creating controller `" << controllerName << "`";
328 
329  controller = controllerCreator->createNJointController(
330  controllerClassName, controllerName, config);
332  ARMARX_INFO << controllerName << " is now created.";
333  }
334  else
335  {
336  ARMARX_IMPORTANT << "Reusing the controller: " << controllerName;
337  }
338 
339  if (not controller)
340  {
341  ARMARX_WARNING << "Failed to create controller of type `" << controllerClassName
342  << "`";
343  return nullptr;
344  }
345 
346  auto ctrl =
347  armarx::control::ConfigurableNJointControllerInterfacePrx::checkedCast(controller);
348 
349  std::unique_ptr<ControllerWrapper<T>> ctrlWrapper =
350  std::make_unique<ControllerWrapper<T>>(
351  ctrl, controllerName, configWriter, dto.value());
352  ctrlWrapper->daemonize(daemonized);
353 
354  // Sends the data to the memory and updates the controller config.
355  // This is especially important, if the controller is reused.
356  ctrlWrapper->updateConfig();
357  return ctrlWrapper;
358  }
359 
360  std::unique_ptr<ControllerWrapper<T>>
361  createTSComplianceCtrl(const std::string& namePrefix = "",
362  const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
363  const std::string& configFilename = "",
364  bool flagActivate = true,
365  bool flagAllowReuse = true,
366  bool flagFromMemory = false)
367  {
368  ARMARX_CHECK(not(configDict.get() == nullptr and configFilename.empty()));
369  /// load predefined config from file or use the configFilename provided by the user
370  if (configDict)
371  {
372  withConfig(configDict);
373  }
374  else
375  {
376  if (configFilename.empty())
377  {
378  if (flagFromMemory)
380  else
382  }
383  else
384  {
385  ARMARX_CHECK(std::filesystem::exists(configFilename))
386  << "configuration file does not exist\n"
387  << configFilename
388  << "Other tips: \n"
389  "1) if you need to configure a controller by passing configuration "
390  "file "
391  "to "
392  "controllerBuilder (me), you need to make sure the "
393  "controllerCreator "
394  "component is running "
395  "on the same PC as your application, otherwise\n"
396  "2) use the createControllerFromConfig() interface, which does not "
397  "require "
398  "where controller"
399  "creator is running";
400  ARMARX_IMPORTANT << "Using user defined configuration file:\n"
401  << configFilename;
402  withConfig(configFilename);
403  }
404  }
405 
406  allowReuse(flagAllowReuse);
407  daemonize();
408 
409  std::ostringstream oss;
410  for (const auto& pair : config().limbs)
411  {
412  oss << pair.first;
413  if (&pair != &(*config().limbs.rbegin()))
414  {
415  oss << ",";
416  }
417  }
418  std::string robotNodeSets = oss.str();
419 
420  if (namePrefix.empty())
421  {
422  withNamePrefix(robotNodeSets);
423  }
424  else
425  {
426  allowReuse(true);
427  withNamePrefix(namePrefix + "_" + robotNodeSets);
428  }
429 
430  ARMARX_TRACE;
431  ARMARX_INFO << "creating controller";
432 
433  auto ctrlWrapper = create();
434 
435  ARMARX_TRACE;
436 
437  ARMARX_INFO << "using controller name " << controllerName;
438  ARMARX_TRACE;
439 
440  if (getReusing())
441  {
443  << "setting nullspace target: if the default is null use the cfg from the "
444  "existing controller";
445  /// get up-to-date controller configs and update the copy of it in wrapper
446  /// with the null space target from the controller
447  auto ctrlConfig = ctrlWrapper->getConfig().limbs;
448  for (const auto& pair : config().limbs)
449  {
450  if (not pair.second.desiredNullspaceJointAngles.has_value())
451  {
452  ctrlWrapper->config.limbs.at(pair.first).desiredNullspaceJointAngles =
453  ctrlConfig.at(pair.first).desiredNullspaceJointAngles;
454  }
455  // ctrlWrapper->config.limbs.at(pair.first).desiredPose =
456  // ctrlConfig.at(pair.first).desiredPose;
457  // ARMARX_INFO << "Activating controller ..."
458  // << ctrlWrapper->config.limbs.at(pair.first)
459  // .desiredNullspaceJointAngles.has_value();
460  }
461  }
462 
463  ARMARX_TRACE;
464  // ctrlWrapper->updateConfig();
465  if (flagActivate)
466  {
467  ctrlWrapper->activate();
468  ARMARX_TRACE;
469  while (not ctrlWrapper->isActive())
470  {
472  }
473  ARMARX_INFO << "Controller " << controllerName << " is active";
474  }
475  else
476  {
477  ARMARX_INFO << "User have to activate " << controllerName
478  << " controller in your application";
479  }
480  return ctrlWrapper;
481  }
482 
483  std::unique_ptr<ControllerWrapper<T>>
484  createTSComplianceMPCtrl(const std::string& namePrefix,
486  const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
487  const std::string& configFilename = "",
488  bool flagActivate = true,
489  bool flagAllowReuse = true,
490  bool flagFromMemory = false,
491  bool flagStart = true)
492  {
493  auto ctrlWrapper = createTSComplianceCtrl(namePrefix,
494  configDict,
495  configFilename,
496  flagActivate,
497  flagAllowReuse,
498  flagFromMemory);
499  auto ctrl = CtrlProxy::checkedCast(ctrlWrapper->ctrl());
500  ctrl->updateMPConfig(mpConfigDict);
501  ctrl->trainMP();
502 
503  if (flagStart)
504  {
505  // start controller
506  ARMARX_CHECK_NOT_NULL(ctrl);
507  ctrl->startAll();
508  }
509  return ctrlWrapper;
510  }
511 
512  CtrlProxy
513  createGetTSComplianceMPCtrl(const std::string& namePrefix,
515  const ::armarx::aron::data::dto::DictPtr& configDict = nullptr,
516  const std::string& configFilename = "",
517  bool flagActivate = true,
518  bool flagAllowReuse = true,
519  bool flagFromMemory = false,
520  bool flagStart = true)
521  {
522  auto ctrlWrapper = createTSComplianceCtrl(namePrefix,
523  configDict,
524  configFilename,
525  flagActivate,
526  flagAllowReuse,
527  flagFromMemory);
528  auto ctrl = CtrlProxy::checkedCast(ctrlWrapper->ctrl());
529  ctrl->stopAll();
530  ctrl->updateMPConfig(mpConfigDict);
531  ctrl->trainMP();
532 
533  if (flagStart)
534  {
535  // start controller
536  ARMARX_CHECK_NOT_NULL(ctrl);
537  ctrl->startAll();
538  }
539  return ctrl;
540  }
541 
543  allowReuse(const bool allowReuse)
544  {
545  this->allowReuse_ = allowReuse;
546  return *this;
547  }
548 
549  bool
551  {
552  return this->reusing_;
553  }
554 
555  std::string
557  {
558  return controllerName;
559  }
560 
561  private:
562  bool
563  initDefaultConfig()
564  {
565  if (auto cfg = configReader.getDefaultConfig(ControllerDescriptionType::name))
566  {
567  dto = AronDTO::FromAron(cfg);
568  return true;
569  }
570 
571  ARMARX_WARNING << "Failed to obtain default config";
572  return false;
573  }
574 
575  bool
576  ensureInitialized()
577  {
578  if (not dto.has_value())
579  {
580  return initDefaultConfig();
581  }
582 
583  return true;
584  }
585 
586  armarx::RobotUnitInterfacePrx controllerCreator;
587 
588  const memory::config::Reader configReader;
589  const memory::config::Writer configWriter;
590 
591  std::optional<AronDTO> dto;
592 
593  std::optional<std::string> nodeSetName;
594 
595  std::string controllerNamePrefix;
596  std::string controllerName;
597 
598  bool daemonized = false;
599  bool allowReuse_ = false;
600  bool reusing_ = false;
601  };
602 } // namespace armarx::control::client
armarx::control::client::ControllerBuilder::create
std::unique_ptr< ControllerWrapper< T > > create()
Creates the controller with the default name.
Definition: ControllerBuilder.h:203
armarx::control::client::ControllerBuilder::CtrlProxy
detail::proxy::ProxyType_t< ControllerDescriptionType > CtrlProxy
Definition: ControllerBuilder.h:58
armarx::control::client::ControllerBuilder::config
AronDTO & config()
Definition: ControllerBuilder.h:165
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
LocalException.h
armarx::control::client::ControllerBuilder::createTSComplianceCtrl
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)
Definition: ControllerBuilder.h:361
armarx::core::time::Clock::WaitFor
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
Definition: Clock.cpp:104
NlohmannJSONReaderWithoutTypeCheck.h
ARMARX_CHECK_NOT_NULL
#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...
Definition: ExpressionException.h:206
armarx::control::client::ControllerBuilder::ctrlConfigClassName
static constexpr const char * ctrlConfigClassName
Definition: ControllerBuilder.h:61
armarx::control::client::ControllerBuilder::ControllerBuilder
ControllerBuilder(const armarx::RobotUnitInterfacePrx &controllerCreator, const memory::config::Reader &configProvider, const memory::config::Writer &configWriter, const std::string &controllerNamePrefix)
Definition: ControllerBuilder.h:71
armarx::control::client::ControllerBuilder::withDefaultConfig
ControllerBuilder & withDefaultConfig(const std::string &name="default")
Definition: ControllerBuilder.h:86
armarx::control::client::ControllerBuilder::AronDTO
typename ControllerDescriptionType::AronDTO AronDTO
Definition: ControllerBuilder.h:65
armarx::control::client
This file is part of ArmarX.
Definition: ComponentPlugin.cpp:20
armarx::control::client::ControllerBuilder::withConfig
ControllerBuilder & withConfig(const ::armarx::aron::data::dto::DictPtr &configDict)
Definition: ControllerBuilder.h:124
ARMARX_CHECK_NOT_EMPTY
#define ARMARX_CHECK_NOT_EMPTY(c)
Definition: ExpressionException.h:224
armarx::control::client::ControllerBuilder::withNodeSet
ControllerBuilder & withNodeSet(const std::string &nodeSetName)
Definition: ControllerBuilder.h:173
armarx::control::client::detail::proxy::ProxyType_t
typename ProxyType< T >::type ProxyType_t
Definition: ControllerDescription.h:62
armarx::control::client::ControllerBuilder
Definition: ControllerBuilder.h:52
armarx::control::client::ControllerDescription
Definition: ControllerDescription.h:103
armarx::control::client::ControllerBuilder::allowReuse
ControllerBuilder & allowReuse(const bool allowReuse)
Definition: ControllerBuilder.h:543
type.h
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
Clock.h
armarx::GetHandledExceptionString
std::string GetHandledExceptionString()
Definition: Exception.cpp:147
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
armarx::control::client::ControllerBuilder::create
std::unique_ptr< ControllerWrapper< T > > create(const std::string &name)
Definition: ControllerBuilder.h:210
controller
Definition: AddOperation.h:39
armarx::armem::MemoryID
A memory ID.
Definition: MemoryID.h:47
armarx::PackagePath::toSystemPath
static std::filesystem::path toSystemPath(const data::PackagePath &pp)
Definition: PackagePath.cpp:54
armarx::aron::data::reader::NlohmannJSONReaderWithoutTypeCheck
Definition: NlohmannJSONReaderWithoutTypeCheck.h:35
armarx::control::client::ControllerBuilder::createGetTSComplianceMPCtrl
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)
Definition: ControllerBuilder.h:513
ControllerDescription.h
armarx::core::time::Duration::SecondsDouble
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
Definition: Duration.cpp:90
filename
std::string filename
Definition: VisualizationRobot.cpp:83
armarx::control::client::ControllerBuilder::createTSComplianceMPCtrl
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)
Definition: ControllerBuilder.h:484
armarx::control::client::ControllerBuilder::withNamePrefix
ControllerBuilder & withNamePrefix(const std::string &namePrefix)
Definition: ControllerBuilder.h:180
armarx::control::client::ControllerBuilder::getControllerName
std::string getControllerName()
Definition: ControllerBuilder.h:556
armarx::aron::data::DictPtr
std::shared_ptr< Dict > DictPtr
Definition: Dict.h:41
armarx::control::client::ControllerBuilder::daemonize
ControllerBuilder & daemonize()
Definition: ControllerBuilder.h:191
constants.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::control::client::ControllerBuilder::withConfig
ControllerBuilder & withConfig(const AronDTO &dto)
Definition: ControllerBuilder.h:158
armarx::control::client::ControllerBuilder::getReusing
bool getReusing()
Definition: ControllerBuilder.h:550
IceInternal::ProxyHandle<::IceProxy::armarx::RobotUnitInterface >
armarx::control::client::ControllerBuilder::withDefaultConfigFile
ControllerBuilder & withDefaultConfigFile(const std::string &name="default", const std::string &robotNameSuffix="", const std::string &appendix="")
Definition: ControllerBuilder.h:101
armarx::control::client::ControllerBuilder::withConfig
ControllerBuilder & withConfig(const std::filesystem::path &filename)
Definition: ControllerBuilder.h:131
armarx::control::client::ControllerBuilder::withConfig
ControllerBuilder & withConfig(const armarx::armem::MemoryID &memoryId)
Definition: ControllerBuilder.h:115
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::PackagePath
Definition: PackagePath.h:55
AronGeneratedClass.h
armarx::core::time::Duration::MilliSeconds
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition: Duration.cpp:55
ControllerWrapper.h
PackagePath.h