8#include <IceUtil/Optional.h>
10#include <nlohmann/json.hpp>
25#include <QtConcurrent/QtConcurrent>
27#include <QtWidgets/QSlider>
28#include <QtWidgets/QTableWidgetItem>
42#include <RobotAPI/interface/components/SkillDashboardInterface.h>
43#include <RobotAPI/interface/skills/SkillManagerInterface.h>
55 DEFAULT_SETTINGS_PLUGIN_NAME(
"SkillDashboardGuiPlugin"),
56 DEFAULT_SETTINGS_CUSTOM_TEXT(
"custom text")
61 this->shortcutLayout =
new QVBoxLayout();
63 this->editModeAction =
new QAction(
"Edit Mode",
this);
64 this->editModeAction->setCheckable(
true);
65 this->editModeAction->setToolTip(
"If toggled the shortcut config buttons and the reload, "
66 "add and export button will be shown.");
68 this->recoverButtons =
new QToolButton();
69 QIcon iconRecover =
getWidget()->style()->standardIcon(QStyle::SP_BrowserReload);
70 this->recoverButtons->setIcon(iconRecover);
71 this->recoverButtons->setToolTip(
"Recover all buttons");
74 ui.messageArea->layout()->addWidget(this->errorMessageArea);
76 ui.shortcutListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
77 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
78 ui.shortcutListWidget->setDefaultDropAction(Qt::MoveAction);
79 ui.shortcutListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
80 ui.shortcutListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
81 ui.shortcutListWidget->setStyleSheet(R
"(
83 background: transparent;
92 qRegisterMetaType<skills::core::dto::Execution::Status>(
93 "skills::core::dto::Execution::Status");
95 connect(this->editModeAction, SIGNAL(toggled(
bool)),
this, SLOT(editMode(
bool)));
96 connect(this->recoverButtons,
97 &QToolButton::clicked,
99 &SkillDashboardWidget::enableBlockedButtons);
100 connect(
ui.addFromClipboardButton,
101 &QPushButton::clicked,
103 &SkillDashboardWidget::addFromClipboard);
104 connect(
ui.addButton,
105 &QPushButton::clicked,
107 [
this]() { openConfigWindow(
"",
"",
"",
""); });
108 connect(
ui.reloadButton, &QPushButton::clicked,
this, &SkillDashboardWidget::loadButtons);
111 connect(
ui.exportButton, &QPushButton::clicked,
this, &SkillDashboardWidget::exportButtons);
112 connect(
ui.importButton, &QPushButton::clicked,
this, &SkillDashboardWidget::importButtons);
117 &SkillDashboardWidget::activateButton);
120 connect(this->dialog,
123 &SkillDashboardWidget::onShortcutNameChanged);
124 connect(
ui.stopAllButton, &QPushButton::clicked,
this, &SkillDashboardWidget::stopAll);
127 this->editModeAction->toggle();
139 if (not this->skillManagerOberserverName.empty())
153 this->connected.store(
true);
154 getProxy(this->dashboardPrx, skillDashboardProxyName);
155 getProxy(this->managerPrx, this->skillManagerOberserverName);
156 ARMARX_INFO <<
"Starting thread that queries the SkillsMemory if skills are running in the "
158 this->exampleTask = std::thread([&] { exampleThreadMethod(); });
164 SkillDashboardWidget::updateShortcutListHeight()
166 int rows =
ui.shortcutListWidget->count();
169 ui.shortcutListWidget->setMinimumHeight(0);
173 int rowHeight =
ui.shortcutListWidget->sizeHintForRow(0);
174 int frame =
ui.shortcutListWidget->frameWidth() * 2;
175 ui.shortcutListWidget->setMinimumHeight(rows * rowHeight + frame);
183 case skills::core::dto::Execution::Status::Failed:
185 case skills::core::dto::Execution::Status::Succeeded:
187 case skills::core::dto::Execution::Status::Aborted:
196 SkillDashboardWidget::saveShortcutOrder()
198 std::vector<std::string> order;
200 for (
int i = 0; i <
ui.shortcutListWidget->count(); ++i)
202 QListWidgetItem* item =
ui.shortcutListWidget->item(i);
203 QString name = item->data(Qt::UserRole).toString();
204 order.push_back(name.toStdString());
209 dashboardPrx->saveShortcutOrder(order);
211 catch (Ice::Exception
const&)
218 SkillDashboardWidget::loadPath()
220 std::vector<std::string> pathSegments;
223 pathSegments = dashboardPrx->getPathStructure();
225 catch (Ice::Exception
const&)
229 if (pathSegments.size() == 3)
231 ui.packageEdit->setText(QString::fromStdString(pathSegments[0]));
232 ui.folderEdit->setText(QString::fromStdString(pathSegments[1]));
233 ui.fileNameEdit->setText(QString::fromStdString(pathSegments[2]));
238 SkillDashboardWidget::activateButton(
const std::string& name,
239 skills::core::dto::Execution::Status
status)
241 auto* btn = this->shortcutButtons.at(name);
242 btn->setDisabled(
false);
243 if (
status != skills::core::dto::Execution::Status::Succeeded)
245 btn->finishProgress(
true);
246 std::string text =
"Shortcut '" + name +
"' terminated with status '" +
248 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
249 QString messageWithTimestamp =
250 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
251 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
253 btn->finishProgress(
false);
257 SkillDashboardWidget::addFromClipboard()
259 QClipboard* clipboard = QApplication::clipboard();
260 auto clipboardText = clipboard->text().toStdString();
264 j = nlohmann::json::parse(clipboardText);
266 catch (
const nlohmann::json::parse_error& e)
268 ARMARX_ERROR <<
"JSON Parse Error: " << e.what() <<
"\n";
271 auto shortcutImport = j[
"shortcuts"][0];
272 auto skillArgs = shortcutImport[
"skill_args"].dump(2);
273 auto skillId = shortcutImport[
"skill_id"];
274 this->openConfigWindow(
"", skillId, skillArgs,
"");
278 SkillDashboardWidget::stopAll()
285 auto results = managerPrx->abortAllSkills();
286 for (
auto& r : results)
289 catch (Ice::Exception
const&)
294 this->enableBlockedButtons();
298 SkillDashboardWidget::exampleThreadMethod()
300 while (this->connected.load())
302 if (not this->runningSkills.empty())
304 for (
auto it = this->runningSkills.cbegin(); it != this->runningSkills.cend();)
306 skills::core::dto::Execution::Status
status =
307 skills::core::dto::Execution::Status::Succeeded;
311 IceUtil::Optional<skills::manager::dto::SkillStatusUpdate>
update =
312 this->managerPrx->getSkillExecutionStatus(it->second);
318 catch (Ice::Exception
const&)
323 if (
status == skills::core::dto::Execution::Status::Succeeded ||
324 status == skills::core::dto::Execution::Status::Failed ||
325 status == skills::core::dto::Execution::Status::Aborted)
329 it = this->runningSkills.erase(it);
343 SkillDashboardWidget::onShortcutNameChanged()
346 if ((this->shortcutButtons.find(this->dialog->getShortcutName().toStdString()) !=
347 this->shortcutButtons.end()) and
348 (this->dialog->getShortcutName().toStdString() != this->currentShortcutName))
350 this->dialog->setInfoText(
351 "A shortcut with this name already exists! The old one will be overwritten.");
353 <<
"A shortcut with this name already exists! The old one will be overwritten!";
357 this->dialog->setInfoText(
"");
362 SkillDashboardWidget::openConfigWindow(
const std::string& name,
363 const std::string&
id,
364 const std::string& args,
365 const std::string& iconName)
368 this->dialog->setShortcutName(name);
369 this->dialog->setSkillId(
id);
370 this->dialog->setIconName(iconName);
371 this->dialog->setSkillConfig(args);
374 if (this->dialog->exec() == QDialog::Accepted)
378 SkillShortcut newShortcut;
379 newShortcut.shortcutName = this->dialog->getShortcutName().toStdString();
380 newShortcut.skillId = this->dialog->getSkillId().toStdString();
381 newShortcut.skillArgs = this->dialog->getSkillConfig().toStdString();
382 newShortcut.iconName = this->dialog->getIconName().toStdString();
383 this->dashboardPrx->addNewShortcut(newShortcut);
385 catch (Ice::Exception
const&)
394 SkillDashboardWidget::exportButtons()
397 std::string packageName =
ui.packageEdit->text().toStdString();
398 std::string folderName =
ui.folderEdit->text().toStdString();
399 if (
ui.fileNameEdit->text().count(
' ') ==
ui.fileNameEdit->text().length())
404 std::string fileName =
ui.fileNameEdit->text().toStdString();
407 this->dashboardPrx->exportShortcuts(packageName, folderName, fileName);
409 catch (Ice::Exception
const&)
414 std::string coloredPath =
"<span style='color:red;'>" + packageName +
"/" + folderName +
415 "/" + fileName +
"</span>";
416 std::string propertyPath =
"Insert " + coloredPath +
417 " into the property 'ArmarX.SkillDashboard.ShortcutPath' of the "
418 "SkillDashboard component.";
421 ui.exportPath->setFullText(QString::fromStdString(propertyPath));
422 ui.exportPath->setTextInteractionFlags(Qt::TextSelectableByMouse);
426 SkillDashboardWidget::importButtons()
429 std::string packageName =
ui.packageEdit->text().toStdString();
430 std::string folderName =
ui.folderEdit->text().toStdString();
431 if (
ui.fileNameEdit->text().count(
' ') ==
ui.fileNameEdit->text().length())
436 std::string fileName =
ui.fileNameEdit->text().toStdString();
439 success = this->dashboardPrx->importShortcuts(packageName, folderName, fileName);
441 catch (Ice::Exception
const&)
452 SkillDashboardWidget::loadButtons()
454 this->shortcutButtons.clear();
455 this->configButtons.clear();
456 this->deleteButtons.clear();
457 ui.shortcutListWidget->clear();
459 std::vector<SkillShortcut> shortcuts;
462 shortcuts = this->dashboardPrx->getShortcuts();
464 catch (Ice::Exception
const& e)
470 for (
const auto& shortcut : shortcuts)
473 new EllipsisPushButton(QString::fromStdString(shortcut.shortcutName));
474 mainButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
475 mainButton->setToolTip(QString(
"Execute"));
477 QToolButton* editButton =
new QToolButton();
479 getWidget()->style()->standardIcon(QStyle::SP_FileDialogContentsView));
480 editButton->setToolTip(QString(
"Edit"));
482 QToolButton* deleteButton =
new QToolButton();
483 deleteButton->setIcon(
getWidget()->style()->standardIcon(QStyle::SP_TrashIcon));
484 deleteButton->setToolTip(QString(
"Delete"));
486 QWidget* rowWidget =
new QWidget();
487 QHBoxLayout* layout =
new QHBoxLayout(rowWidget);
488 layout->setContentsMargins(0, 0, 0, 0);
489 layout->setSpacing(2);
490 layout->addWidget(mainButton);
491 layout->addWidget(editButton);
492 layout->addWidget(deleteButton);
494 QListWidgetItem* item =
new QListWidgetItem(
ui.shortcutListWidget);
495 item->setSizeHint(rowWidget->sizeHint());
496 item->setData(Qt::UserRole, QString::fromStdString(shortcut.shortcutName));
497 ui.shortcutListWidget->addItem(item);
498 ui.shortcutListWidget->setItemWidget(item, rowWidget);
500 this->shortcutButtons[shortcut.shortcutName] = mainButton;
501 this->configButtons[shortcut.shortcutName] = editButton;
502 this->deleteButtons[shortcut.shortcutName] = deleteButton;
505 &QPushButton::clicked,
507 [
this, name = shortcut.shortcutName] { executeSkill(name); });
509 &QToolButton::clicked,
511 [
this, name = shortcut.shortcutName] { editShortcut(name); });
512 connect(deleteButton,
513 &QToolButton::clicked,
515 [
this, name = shortcut.shortcutName] { deleteShortcut(name); });
517 updateShortcutListHeight();
521 SkillDashboardWidget::executeSkill(
const std::string& name)
523 ARMARX_INFO <<
"About to execute skill with shortcut name `" << name
524 <<
"` from skill dashboard.";
526 SkillShortcut shortcut;
529 shortcut = this->dashboardPrx->getShortcut(name);
531 catch (Ice::Exception
const&)
534 std::string text =
"Shortcut '" + name +
"': Could not fetch shortcut.";
535 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
536 QString messageWithTimestamp =
537 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
538 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
541 size_t pos = shortcut.skillId.find(
'/');
542 std::string provider =
"";
543 std::string nameSkill =
"";
544 nlohmann::json json = nlohmann::json::parse(shortcut.skillArgs);
549 if (pos != std::string::npos)
551 provider = shortcut.skillId.substr(0, pos);
552 nameSkill = shortcut.skillId.substr(pos + 1);
553 skills::manager::dto::ProviderID providerId{.providerName = provider};
555 skills::manager::dto::SkillID skillId{.providerId = providerId, .skillName = nameSkill};
557 char hostname[HOST_NAME_MAX];
558 gethostname(hostname, HOST_NAME_MAX);
560 skills::manager::dto::SkillExecutionRequest request{
562 .executorName =
"Skills.Dashboard GUI (hostname: " + std::string(hostname) +
")",
563 .parameters = paramterDto,
569 ARMARX_IMPORTANT <<
"Executing skill with shortcut name `" << shortcut.shortcutName
570 <<
"` from skill dashboard.";
571 armarx::core::time::Duration skillTimeout =
574 IceUtil::Optional<armarx::skills::manager::dto::SkillDescription> optDto =
575 managerPrx->getSkillDescription(skillId);
579 const auto& dto = *optDto;
587 skills::manager::dto::SkillExecutionID executionId =
588 this->managerPrx->executeSkillAsync(request);
589 this->runningSkills[shortcut.shortcutName] = executionId;
590 auto* btn = this->shortcutButtons.at(shortcut.shortcutName);
591 btn->setDisabled(
true);
592 btn->startTimeout(skillTimeout.
toSeconds());
594 catch (Ice::Exception
const&)
597 std::string text =
"Shortcut '" + name +
"': Could not send execute request.";
598 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
599 QString messageWithTimestamp =
600 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
601 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
611 SkillDashboardWidget::editShortcut(
const std::string& name)
613 this->currentShortcutName = name;
615 SkillShortcut shortcut;
618 shortcut = this->dashboardPrx->getShortcut(name);
620 catch (Ice::Exception
const&)
625 shortcut.shortcutName, shortcut.skillId, shortcut.skillArgs, shortcut.iconName);
626 this->currentShortcutName =
"not set";
630 SkillDashboardWidget::deleteShortcut(
const std::string& name)
635 this->dashboardPrx->deleteShortcut(name);
638 catch (Ice::Exception
const&)
646 SkillDashboardWidget::clearLayout(QLayout* layout)
651 while (QLayoutItem* item = layout->takeAt(0))
653 if (QWidget* widget = item->widget())
655 widget->deleteLater();
657 else if (QLayout* subLayout = item->layout())
659 clearLayout(subLayout);
670 if (parent != customToolbar->parent())
672 customToolbar->setParent(parent);
675 return customToolbar.data();
678 customToolbar =
new QToolBar(parent);
679 customToolbar->setIconSize(QSize(16, 16));
680 customToolbar->addAction(editModeAction);
681 customToolbar->addWidget(this->recoverButtons);
683 return customToolbar.data();
687 SkillDashboardWidget::editMode(
bool edit)
691 for (
const auto& button : this->deleteButtons)
693 button.second->setVisible(
true);
695 for (
const auto& button : this->configButtons)
697 button.second->setVisible(
true);
699 ui.addButton->setVisible(
true);
700 ui.reloadButton->setVisible(
true);
701 ui.exportButton->setVisible(
true);
702 ui.importButton->setVisible(
true);
703 ui.exportConfiguration->setVisible(
true);
704 ui.addFromClipboardButton->setVisible(
true);
705 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
709 for (
const auto& button : this->deleteButtons)
711 button.second->setVisible(
false);
713 for (
const auto& button : this->configButtons)
715 button.second->setVisible(
false);
717 ui.addButton->setVisible(
false);
718 ui.reloadButton->setVisible(
false);
719 ui.exportButton->setVisible(
false);
720 ui.importButton->setVisible(
false);
721 ui.exportConfiguration->setVisible(
false);
722 ui.addFromClipboardButton->setVisible(
false);
723 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::NoDragDrop);
728 SkillDashboardWidget::enableBlockedButtons()
730 for (
auto& shortCutB : this->shortcutButtons)
732 shortCutB.second->setDisabled(
false);
739 this->connected.store(
false);
740 ARMARX_INFO <<
"Stopping thread which queries skill memory ...";
741 this->exampleTask.join();
755 if (not m_config_dialog)
759 {
"SkillDashboard",
"Skill Dashboard",
"*SkillDashboard"});
760 m_config_dialog->addProxyFinder<skills::manager::dti::SkillManagerInterfacePrx>(
761 "SkillMemory",
"",
"*SkillMemory");
763 return qobject_cast<QDialog*>(m_config_dialog);
770 this->skillDashboardProxyName =
772 ->value(
"skillDashboardProxyName", QString::fromStdString(skillDashboardProxyName))
775 this->skillManagerOberserverName =
776 settings->value(
"SkillMemory",
"SkillMemory").toString().toStdString();
783 settings->setValue(
"skillDashboardProxyName",
784 QString::fromStdString(skillDashboardProxyName));
785 settings->setValue(
"SkillMemory", QString::fromStdString(this->skillManagerOberserverName));
794 this->skillDashboardProxyName = m_config_dialog->getProxyName(
"SkillDashboard");
795 this->skillManagerOberserverName = m_config_dialog->getProxyName(
"SkillMemory");
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
std::enable_if<!HasGetWidgetName< ArmarXWidgetType >::value >::type addWidget()
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
A config-dialog containing one (or multiple) proxy finders.
void addProxyFinder(const std::vector< EntryData > &entryData)
void shortcutNameHasChanged()
SkillDashboardGuiPlugin()
static data::DictPtr ConvertFromNlohmannJSONObject(const nlohmann::json &, const armarx::aron::Path &p={})
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
static Duration Seconds(std::int64_t seconds)
Constructs a duration in seconds.
std::int64_t toSeconds() const
Returns the amount of seconds.
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
::IceInternal::Handle< Dict > DictPtr
std::shared_ptr< Dict > DictPtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::string errorStatustoString(skills::core::dto::Execution::Status s)