SkillManagerMonitorWidgetController.cpp
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  * \package RobotAPI::gui-plugins::SkillManagerMonitorWidgetController
17  * \author Raphael Grimm ( raphael dot grimm at kit dot edu )
18  * \date 2020
19  * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #include <string>
24 
26 
30 
32 
33 // modals
35 
36 // debug
38 
39 #include <QDoubleSpinBox>
40 #include <QClipboard>
41 
42 #include "aronTreeWidget/Data.h"
43 
44 //config
45 namespace armarx
46 {
47  QPointer<QDialog> SkillManagerMonitorWidgetController::getConfigDialog(QWidget* parent)
48  {
49  if (!dialog)
50  {
51  dialog = new SimpleConfigDialog(parent);
52  dialog->addProxyFinder<skills::manager::dti::SkillManagerInterfacePrx>("SkillManager", "", "Skill*");
53  }
54  return qobject_cast<SimpleConfigDialog*>(dialog);
55  }
57  {
58  observerName = dialog->getProxyName("SkillManager");
59  }
61  {
62  observerName = settings->value("SkillManager", "SkillManager").toString().toStdString();
63  }
65  {
66  settings->setValue("SkillManager", QString::fromStdString(observerName));
67  }
68 }
69 
70 // Others
71 namespace armarx
72 {
74  {
75  widget.setupUi(getWidget());
76 
77  widget.doubleSpinBoxUpdateFreq->setValue(5.0);
78  widget.doubleSpinBoxUpdateFreq->setMinimum(0);
79  widget.doubleSpinBoxUpdateFreq->setMaximum(100);
80  widget.doubleSpinBoxUpdateFreq->setSingleStep(0.5);
81  widget.doubleSpinBoxUpdateFreq->setSuffix(" Hz");
82 
83  refreshSkillsResultTimer = new QTimer(this);
84  refreshSkillsResultTimer->setInterval(1000 / 5); // Keep this stable.
85  refreshSkillsResultTimer->start();
86 
87  connect(widget.doubleSpinBoxUpdateFreq, &QDoubleSpinBox::editingFinished,
88  this, &SkillManagerMonitorWidgetController::updateTimerFrequency);
89  connect(refreshSkillsResultTimer, &QTimer::timeout,
90  this, &SkillManagerMonitorWidgetController::refreshSkills);
91 
92  connect(widget.pushButtonCopy, &QPushButton::clicked,
93  this, &SkillManagerMonitorWidgetController::copyCurrentConfig);
94  connect(widget.pushButtonPaste, &QPushButton::clicked,
95  this, &SkillManagerMonitorWidgetController::pasteCurrentConfig);
96 
97  connect(widget.pushButtonExecuteSkill, &QPushButton::clicked,
98  this, &SkillManagerMonitorWidgetController::executeSkill);
99  connect(widget.pushButtonStopSkill, &QPushButton::clicked,
100  this, &SkillManagerMonitorWidgetController::stopSkill);
101 
102  connect(widget.treeWidgetSkills, &QTreeWidget::currentItemChanged,
103  this, &SkillManagerMonitorWidgetController::skillSelectionChanged);
104 
105  connect(widget.treeWidgetSkillDetails, &QTreeWidget::itemDoubleClicked,
106  this, &SkillManagerMonitorWidgetController::onTreeWidgetItemDoubleClicked);
107  }
108 
110  {
111 
112  }
113 
115  {
116  usingProxy(observerName);
117  }
118 
119 
121  {
122  getProxy(manager, observerName);
123  widget.groupBoxSkills->setTitle(QString::fromStdString(observerName));
124  widget.treeWidgetSkillDetails->setEditTriggers(QAbstractItemView::EditTrigger::NoEditTriggers);
125  widget.treeWidgetSkillDetails->setColumnHidden(3, true);
126 
127  connected = true;
128  }
129 
131  {
132  connected = false;
133 
134  // reset all
135  skills.clear();
136  widget.treeWidgetSkills->clear();
137  widget.treeWidgetSkillDetails->clear();
138  skillsArgumentsTreeWidgetItem = nullptr;
139  selectedSkill.providerName = "";
140  selectedSkill.skillName = "";
141  }
142 
143  void SkillManagerMonitorWidgetController::updateTimerFrequency()
144  {
145  int f = static_cast<int>(std::round(1000 / widget.doubleSpinBoxUpdateFreq->value()));
146  refreshSkillsResultTimer->setInterval(f);
147  }
148 
149  void SkillManagerMonitorWidgetController::refreshSkills()
150  {
151  static std::map<skills::provider::dto::Execution::Status, std::string> ExecutionStatus2String = {
152  {skills::provider::dto::Execution::Status::Aborted, "Aborted"},
153  {skills::provider::dto::Execution::Status::Failed, "Failed"},
154  {skills::provider::dto::Execution::Status::Idle, "Not yet started"},
156  {skills::provider::dto::Execution::Status::Scheduled, "Scheduled"},
157  {skills::provider::dto::Execution::Status::Succeeded, "Succeeded"}
158  };
159 
160  if (!connected)
161  return;
162 
163  /* CHECK OWN SKILLS LIST */
164  // remove non-existing ones
165  auto managerSkills = manager->getSkillDescriptions();
166  std::vector<std::string> removedProviders;
167  for (auto it = skills.begin(); it != skills.end();)
168  {
169  // TODO: iterate over skills, not just over providers!
170  std::string providerName = it->first;
171  if (managerSkills.find(providerName) == managerSkills.end())
172  {
173  removedProviders.push_back(providerName);
174  it = skills.erase(it);
175  }
176  else
177  {
178  it++;
179  }
180  }
181 
182  // add new ones
183  std::vector<std::string> newProviders;
184  for (const auto& [providerName, providerSkills] : managerSkills)
185  {
186  if (skills.find(providerName) == skills.end())
187  {
188  skills.insert(std::make_pair(providerName, providerSkills));
189  newProviders.push_back(providerName);
190  }
191  }
192 
193  /* CHECK TREE VIEW */
194  // remove providers from tree
195  int i = 0;
196  while (i < widget.treeWidgetSkills->topLevelItemCount())
197  {
198  QTreeWidgetItem* item = widget.treeWidgetSkills->topLevelItem(i);
199  if (std::find(removedProviders.begin(), removedProviders.end(), item->text(0).toStdString()) != removedProviders.end())
200  {
201  delete widget.treeWidgetSkills->takeTopLevelItem(i);
202  }
203  else
204  {
205  ++i;
206  }
207  }
208 
209  // add new providers
210  for (const auto& [providerName, providerSkills] : skills)
211  {
212  if (auto it = std::find(newProviders.begin(), newProviders.end(), providerName); it != newProviders.end())
213  {
214  auto item = new QTreeWidgetItem(widget.treeWidgetSkills);
215  item->setText(0, QString::fromStdString(providerName));
216  for (const auto& [name, sk] : providerSkills)
217  {
218  auto itsk = new QTreeWidgetItem(item);
219  item->addChild(itsk);
220  itsk->setText(0, QString::fromStdString(name));
221  }
222  }
223  }
224 
225  // update status and active skills window
226  std::map<skills::SkillID, std::string> activeSkillsAndPrefixes;
227  auto managerStatuses = manager->getSkillExecutionStatuses();
228  for (int i = 0; i < widget.treeWidgetSkills->topLevelItemCount(); ++i)
229  {
230  try
231  {
232  QTreeWidgetItem* item = widget.treeWidgetSkills->topLevelItem(i);
233  auto providerName = item->text(0).toStdString();
234 
235  auto allStatusesForProvider = managerStatuses.at(providerName);
236 
237  for (int j = 0; j < item->childCount(); ++j)
238  {
239  QTreeWidgetItem* skillItem = item->child(j);
240  skills::SkillID currentSkillId(providerName, skillItem->text(0).toStdString());
241 
242  auto statusForSkill = allStatusesForProvider.at(currentSkillId.skillName);
243  skillItem->setText(2, QString::fromStdString(ExecutionStatus2String.at(statusForSkill.header.status)));
244 
245  if (not statusForSkill.header.executorName.empty()) // it means that the skill was called by someone
246  {
247  activeSkillsAndPrefixes.insert({currentSkillId, statusForSkill.header.executorName});
248  }
249  }
250  }
251  catch (const std::exception& e)
252  {
253  // Perhaps the skill provider died after the check at the beginning of this method
254  continue;
255  }
256  }
257 
258  // finally update the view of active skills
259  widget.listWidgetActiveSkills->clear();
260  for (const auto& [id, prefix] : activeSkillsAndPrefixes)
261  {
262  auto prefixedStr = id.toString(prefix);
263  bool longest = true;
264  for (const auto& [id2, prefix2] : activeSkillsAndPrefixes) // check if there is a deeper skill currently executing
265  {
266  auto prefixedStr2 = id.toString(prefix2);
267  if (prefixedStr == prefixedStr2)
268  {
269  continue;
270  }
271 
272  if (simox::alg::starts_with(prefixedStr2, prefixedStr))
273  {
274  longest = false;
275  break;
276  }
277  }
278 
279  if (longest)
280  {
281  widget.listWidgetActiveSkills->addItem(QString::fromStdString(id.toString() + ": " + id.toString(prefix)));
282  }
283  }
284  }
285 
286  void SkillManagerMonitorWidgetController::executeSkill()
287  {
288  if (selectedSkill.providerName.empty() or selectedSkill.skillName.empty())
289  {
290  return;
291  }
292 
293  const auto& skillDescriptions = skills.at(selectedSkill.providerName);
294  if (!skillDescriptions.count(selectedSkill.skillName))
295  {
296  return;
297  }
298 
299  auto data = getConfigAsAron();
300 
301  char hostname[HOST_NAME_MAX];
302 
303  gethostname(hostname, HOST_NAME_MAX);
304 
305  skills::manager::dto::SkillExecutionRequest exInfo;
306  exInfo.executorName = "Skills.Manager GUI (hostname: " + std::string(hostname) + ")";
307  exInfo.skillId = {selectedSkill.providerName, selectedSkill.skillName};
308  exInfo.params = aron::data::Dict::ToAronDictDTO(data);
309 
310  ARMARX_IMPORTANT << "Executing skill from GUI: " << selectedSkill.providerName << "/" << selectedSkill.skillName << ". The data was: " << data;
311  // Note that we execute the skill in a seperate thread so that the GUI thread does not freeze.
312  manager->begin_executeSkill(exInfo);
313  }
314 
315  void SkillManagerMonitorWidgetController::stopSkill()
316  {
317  if (selectedSkill.providerName.empty() or selectedSkill.skillName.empty())
318  {
319  return;
320  }
321 
322  const auto& skillDescriptions = skills.at(selectedSkill.providerName);
323  if (!skillDescriptions.count(selectedSkill.skillName))
324  {
325  return;
326  }
327 
328  ARMARX_INFO << "Stopping skill from GUI: " << selectedSkill.providerName << "/" << selectedSkill.skillName;
329  manager->abortSkill(selectedSkill.providerName, selectedSkill.skillName);
330  }
331 
332  void SkillManagerMonitorWidgetController::skillSelectionChanged(QTreeWidgetItem* current, QTreeWidgetItem*)
333  {
334  widget.groupBoxSkillDetails->setEnabled(false);
335 
336  if (!current)
337  {
338  // gui has died?
339  return;
340  }
341 
342  if (!current->parent())
343  {
344  // no parent available. Should not happen
345  return;
346  }
347 
348  SelectedSkill newSelectedSkill;
349 
350  // setup selected skill
351  newSelectedSkill.providerName = current->parent()->text(0).toStdString();
352  newSelectedSkill.skillName = current->text(0).toStdString();
353 
354  // setup groupBox
355  widget.groupBoxSkillDetails->setTitle(QString::fromStdString(newSelectedSkill.providerName + "/" + newSelectedSkill.skillName));
356  widget.groupBoxSkillDetails->setEnabled(true);
357 
358  if (newSelectedSkill.providerName == selectedSkill.providerName and newSelectedSkill.skillName == selectedSkill.skillName)
359  {
360  return;
361  }
362 
363  selectedSkill = newSelectedSkill;
364 
365  // setup table view
366  widget.treeWidgetSkillDetails->clear();
367  aronTreeWidgetController = nullptr;
368  skillsArgumentsTreeWidgetItem = nullptr;
369 
370  auto skillDesc = skills.at(selectedSkill.providerName).at(selectedSkill.skillName);
371 
372  {
373  auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails,
374  {QString::fromStdString("Name"), QString::fromStdString(skillDesc.skillName)});
375  widget.treeWidgetSkillDetails->addTopLevelItem(it);
376  }
377 
378  {
379  auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails,
380  {QString::fromStdString("Robot"), QString::fromStdString(simox::alg::join(skillDesc.robots, ", "))});
381  widget.treeWidgetSkillDetails->addTopLevelItem(it);
382  }
383 
384  {
385  auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails,
386  {QString::fromStdString("Description"), QString::fromStdString(skillDesc.description)});
387  widget.treeWidgetSkillDetails->addTopLevelItem(it);
388  }
389 
390  {
391  auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails,
392  {QString::fromStdString("Timeout"), QString::fromStdString(std::to_string(skillDesc.timeoutMs)) + " ms"});
393  widget.treeWidgetSkillDetails->addTopLevelItem(it);
394  }
395 
396  skillsArgumentsTreeWidgetItem = new QTreeWidgetItem(widget.treeWidgetSkillDetails, {QString::fromStdString("Arguments")});
397  auto aron_args = aron::type::Object::FromAronObjectDTO(skillDesc.acceptedType);
398  auto default_args = aron::data::Dict::FromAronDictDTO(skillDesc.defaultParams);
399 
400  aronTreeWidgetController = std::make_shared<AronTreeWidgetController>(widget.treeWidgetSkillDetails, skillsArgumentsTreeWidgetItem, aron_args, default_args);
401  }
402 
403  aron::data::DictPtr SkillManagerMonitorWidgetController::getConfigAsAron() const
404  {
405  // create argument aron (if there is an accepted type set)
406  if (aronTreeWidgetController)
407  {
408  return aronTreeWidgetController->convertToAron();
409  }
410  return nullptr;
411  }
412 
413  void SkillManagerMonitorWidgetController::copyCurrentConfig()
414  {
415  auto data = getConfigAsAron();
416  if (!data)
417  {
418  return;
419  }
420 
422  QClipboard* clipboard = QApplication::clipboard();
423  clipboard->setText(QString::fromStdString(json.dump(2)));
424  }
425 
426  void SkillManagerMonitorWidgetController::pasteCurrentConfig()
427  {
428  QClipboard* clipboard = QApplication::clipboard();
429  std::string s = clipboard->text().toStdString();
430  nlohmann::json json = nlohmann::json::parse(s);
432 
433  if (!aronTreeWidgetController)
434  {
435  return;
436  }
437 
438  aronTreeWidgetController->setFromAron(data);
439  }
440 
441  void SkillManagerMonitorWidgetController::resetCurrentConfig()
442  {
443  // TODO
444  }
445 
446  void SkillManagerMonitorWidgetController::onTreeWidgetItemDoubleClicked(QTreeWidgetItem* item, int column)
447  {
448  if (!item)
449  {
450  return;
451  }
452 
453  if (column == 1)
454  {
455  if (item->flags() & Qt::ItemIsEditable) // we use the flag to indicate whether the item is editable or not
456  {
457  // we assume its aron item
458  AronTreeWidgetItem* aItem = AronTreeWidgetItem::DynamicCastAndCheck(item);
459  std::string name = aItem->text(aron_tree_widget::constantes::TREE_WIDGET_ITEM_NAME).toStdString();
460  std::string type = aItem->text(aron_tree_widget::constantes::TREE_WIDGET_ITEM_TYPE).toStdString();
461 
462  // why visitor?!?!?
463  AronTreeWidgetModalCreatorVisitor v(name, aItem, widget.treeWidgetSkillDetails);
464  aron::type::visit(v, aItem->aronType);
465  auto modal = v.createdModal;
466  modal->exec();
467  }
468  }
469  }
470 }
471 
armarx::aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSONObject
static data::DictPtr ConvertFromNlohmannJSONObject(const nlohmann::json &)
Definition: NLohmannJSONConverter.cpp:29
skills
This file is part of ArmarX.
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
armarx::aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON
static nlohmann::json ConvertToNlohmannJSON(const data::VariantPtr &)
Definition: NLohmannJSONConverter.cpp:5
Data.h
KITProsthesis::ProsthesisState::Running
@ Running
Definition: KITProstheticHandInterface.ice:43
armarx::SkillManagerMonitorWidgetController::SkillManagerMonitorWidgetController
SkillManagerMonitorWidgetController()
Controller Constructor.
Definition: SkillManagerMonitorWidgetController.cpp:73
AronTreeWidgetConverter.h
armarx::starts_with
bool starts_with(const std::string &haystack, const std::string &needle)
Definition: StringHelpers.cpp:43
armarx::aron::data::Dict::FromAronDictDTO
static PointerType FromAronDictDTO(const data::dto::DictPtr &aron)
Definition: Dict.cpp:99
NLohmannJSONConverter.h
SkillManagerMonitorWidgetController.h
Skill.h
armarx::aron::type::Object::FromAronObjectDTO
static ObjectPtr FromAronObjectDTO(const type::dto::AronObjectPtr &)
Definition: Object.cpp:59
armarx::AronTreeWidgetItem::DynamicCastAndCheck
static AronTreeWidgetItem * DynamicCastAndCheck(QTreeWidgetItem *)
Definition: AronTreeWidgetItem.cpp:18
armarx::SkillManagerMonitorWidgetController::loadSettings
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
Definition: SkillManagerMonitorWidgetController.cpp:60
AronTreeWidgetModalCreator.h
armarx::aron::data::Dict::ToAronDictDTO
static data::dto::DictPtr ToAronDictDTO(const PointerType &navigator)
Definition: Dict.cpp:108
armarx::SkillManagerMonitorWidgetController::onConnectComponent
void onConnectComponent() override
Pure virtual hook for the subclass.
Definition: SkillManagerMonitorWidgetController.cpp:120
AronTreeWidgetCreator.h
armarx::SkillManagerMonitorWidgetController::getConfigDialog
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
Definition: SkillManagerMonitorWidgetController.cpp:47
armarx::aron::data::DictPtr
std::shared_ptr< Dict > DictPtr
Definition: Dict.h:41
armarx::aron_tree_widget::constantes::TREE_WIDGET_ITEM_TYPE
const int TREE_WIDGET_ITEM_TYPE
Definition: Data.h:9
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:35
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
AronTreeWidgetTextInputModalController.h
armarx::viz::toString
const char * toString(InteractionFeedbackType type)
Definition: Interaction.h:27
armarx::SkillManagerMonitorWidgetController::onDisconnectComponent
void onDisconnectComponent() override
Hook for subclass.
Definition: SkillManagerMonitorWidgetController.cpp:130
armarx::ArmarXWidgetController::getWidget
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
Definition: ArmarXWidgetController.cpp:54
armarx::aron::type::visit
void visit(VisitorImplementation &v, typename VisitorImplementation::Input &t)
The visit function.
Definition: Visitor.h:42
armarx::aron_tree_widget::constantes::TREE_WIDGET_ITEM_NAME
const int TREE_WIDGET_ITEM_NAME
Definition: Data.h:7
armarx::ManagedIceObject::getProxy
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
Definition: ManagedIceObject.cpp:393
armarx::SkillManagerMonitorWidgetController::~SkillManagerMonitorWidgetController
virtual ~SkillManagerMonitorWidgetController()
Controller destructor.
Definition: SkillManagerMonitorWidgetController.cpp:109
armarx::ManagedIceObject::usingProxy
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Definition: ManagedIceObject.cpp:151
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:31
armarx::SkillManagerMonitorWidgetController::configured
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
Definition: SkillManagerMonitorWidgetController.cpp:56
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::SkillManagerMonitorWidgetController::saveSettings
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
Definition: SkillManagerMonitorWidgetController.cpp:64
armarx::SkillManagerMonitorWidgetController::onInitComponent
void onInitComponent() override
Pure virtual hook for the subclass.
Definition: SkillManagerMonitorWidgetController.cpp:114
armarx::SimpleConfigDialog
A config-dialog containing one (or multiple) proxy finders.
Definition: SimpleConfigDialog.h:84