3#include <RobotAPI/gui-plugins/ui_SkillDashboardWidget_small.h>
10#include <IceUtil/Optional.h>
12#include <nlohmann/json.hpp>
27#include <QtConcurrent/QtConcurrent>
29#include <QtWidgets/QSlider>
30#include <QtWidgets/QTableWidgetItem>
44#include <RobotAPI/interface/components/SkillDashboardInterface.h>
45#include <RobotAPI/interface/skills/SkillManagerInterface.h>
59 DEFAULT_SETTINGS_PLUGIN_NAME(
"SkillDashboardGuiPlugin"),
60 DEFAULT_SETTINGS_CUSTOM_TEXT(
"custom text")
64 ui = std::make_unique<Ui::SkillDashboardWidget>();
66 this->shortcutLayout =
new QVBoxLayout();
68 this->editModeAction =
new QAction(
"Edit Mode",
this);
69 this->editModeAction->setCheckable(
true);
70 this->editModeAction->setToolTip(
"If toggled the shortcut config buttons and the reload, "
71 "add and export button will be shown.");
73 this->recoverButtons =
new QToolButton();
74 QIcon iconRecover =
getWidget()->style()->standardIcon(QStyle::SP_BrowserReload);
75 this->recoverButtons->setIcon(iconRecover);
76 this->recoverButtons->setToolTip(
"Recover all buttons");
79 ui->messageArea->layout()->addWidget(this->errorMessageArea);
81 ui->shortcutListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
82 ui->shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
83 ui->shortcutListWidget->setDefaultDropAction(Qt::MoveAction);
84 ui->shortcutListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
85 ui->shortcutListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
86 ui->shortcutListWidget->setStyleSheet(R
"(
88 background: transparent;
97 qRegisterMetaType<skills::core::dto::Execution::Status>(
98 "skills::core::dto::Execution::Status");
100 connect(this->editModeAction, SIGNAL(toggled(
bool)),
this, SLOT(editMode(
bool)));
101 connect(this->recoverButtons,
102 &QToolButton::clicked,
104 &SkillDashboardWidget::enableBlockedButtons);
105 connect(
ui->addFromClipboardButton,
106 &QPushButton::clicked,
108 &SkillDashboardWidget::addFromClipboard);
109 connect(
ui->addButton,
110 &QPushButton::clicked,
112 [
this]() { openConfigWindow(
"",
"",
"",
"",
""); });
113 connect(
ui->reloadButton, &QPushButton::clicked,
this, &SkillDashboardWidget::loadButtons);
116 connect(
ui->exportButton, &QPushButton::clicked,
this, &SkillDashboardWidget::exportButtons);
117 connect(
ui->importButton, &QPushButton::clicked,
this, &SkillDashboardWidget::importButtons);
122 &SkillDashboardWidget::activateButton);
125 connect(this->dialog,
128 &SkillDashboardWidget::onShortcutNameChanged);
129 connect(
ui->stopAllButton, &QPushButton::clicked,
this, &SkillDashboardWidget::stopAll);
132 this->editModeAction->toggle();
144 if (not this->skillManagerOberserverName.empty())
158 this->connected.store(
true);
159 getProxy(this->dashboardPrx, skillDashboardProxyName);
160 getProxy(this->managerPrx, this->skillManagerOberserverName);
161 ARMARX_INFO <<
"Starting thread that queries the SkillsMemory if skills are running in the "
163 this->exampleTask = std::thread([&] { exampleThreadMethod(); });
169 SkillDashboardWidget::updateShortcutListHeight()
171 int rows =
ui->shortcutListWidget->count();
174 ui->shortcutListWidget->setMinimumHeight(0);
178 int rowHeight =
ui->shortcutListWidget->sizeHintForRow(0);
179 int frame =
ui->shortcutListWidget->frameWidth() * 2;
180 ui->shortcutListWidget->setMinimumHeight(rows * rowHeight + frame);
188 case skills::core::dto::Execution::Status::Failed:
190 case skills::core::dto::Execution::Status::Succeeded:
192 case skills::core::dto::Execution::Status::Aborted:
201 SkillDashboardWidget::saveShortcutOrder()
203 std::vector<std::string> order;
205 for (
int i = 0; i <
ui->shortcutListWidget->count(); ++i)
207 QListWidgetItem* item =
ui->shortcutListWidget->item(i);
208 QString name = item->data(Qt::UserRole).toString();
209 order.push_back(name.toStdString());
214 dashboardPrx->saveShortcutOrder(order);
216 catch (Ice::Exception
const&)
223 SkillDashboardWidget::loadPath()
225 std::vector<std::string> pathSegments;
228 pathSegments = dashboardPrx->getPathStructure();
230 catch (Ice::Exception
const&)
234 if (pathSegments.size() == 3)
236 ui->packageEdit->setText(QString::fromStdString(pathSegments[0]));
237 ui->folderEdit->setText(QString::fromStdString(pathSegments[1]));
238 ui->fileNameEdit->setText(QString::fromStdString(pathSegments[2]));
243 SkillDashboardWidget::activateButton(
const std::string& name,
244 skills::core::dto::Execution::Status
status)
246 auto* btn = this->shortcutButtons.at(name);
247 btn->setDisabled(
false);
248 if (
status != skills::core::dto::Execution::Status::Succeeded)
250 btn->finishProgress(
true);
251 std::string text =
"Shortcut '" + name +
"' terminated with status '" +
253 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
254 QString messageWithTimestamp =
255 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
256 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
258 btn->finishProgress(
false);
262 SkillDashboardWidget::addFromClipboard()
264 QClipboard* clipboard = QApplication::clipboard();
265 auto clipboardText = clipboard->text().toStdString();
269 j = nlohmann::json::parse(clipboardText);
271 catch (
const nlohmann::json::parse_error& e)
273 ARMARX_ERROR <<
"JSON Parse Error: " << e.what() <<
"\n";
276 auto shortcutImport = j[
"shortcuts"][0];
277 auto skillArgs = shortcutImport[
"skill_args"].dump(2);
278 auto skillId = shortcutImport[
"skill_id"];
279 this->openConfigWindow(
"", skillId, skillArgs,
"",
"");
283 SkillDashboardWidget::stopAll()
290 auto results = managerPrx->abortAllSkills();
291 for (
auto& r : results)
294 catch (Ice::Exception
const&)
299 this->enableBlockedButtons();
303 SkillDashboardWidget::exampleThreadMethod()
305 while (this->connected.load())
307 if (not this->runningSkills.empty())
309 for (
auto it = this->runningSkills.cbegin(); it != this->runningSkills.cend();)
311 skills::core::dto::Execution::Status
status =
312 skills::core::dto::Execution::Status::Succeeded;
316 IceUtil::Optional<skills::manager::dto::SkillStatusUpdate>
update =
317 this->managerPrx->getSkillExecutionStatus(it->second);
323 catch (Ice::Exception
const&)
328 if (
status == skills::core::dto::Execution::Status::Succeeded ||
329 status == skills::core::dto::Execution::Status::Failed ||
330 status == skills::core::dto::Execution::Status::Aborted)
334 it = this->runningSkills.erase(it);
348 SkillDashboardWidget::onShortcutNameChanged()
351 if ((this->shortcutButtons.find(this->dialog->getShortcutName().toStdString()) !=
352 this->shortcutButtons.end()) and
353 (this->dialog->getShortcutName().toStdString() != this->currentShortcutName))
355 this->dialog->setInfoText(
356 "A shortcut with this name already exists! The old one will be overwritten.");
358 <<
"A shortcut with this name already exists! The old one will be overwritten!";
362 this->dialog->setInfoText(
"");
367 SkillDashboardWidget::openConfigWindow(
const std::string& name,
368 const std::string&
id,
369 const std::string& args,
370 const std::string& iconName,
371 const std::string& shortcutId)
374 this->dialog->setShortcutName(name);
375 this->dialog->setSkillId(
id);
376 this->dialog->setIconName(iconName);
377 this->dialog->setSkillConfig(args);
380 if (this->dialog->exec() == QDialog::Accepted)
384 SkillShortcut newShortcut;
385 newShortcut.shortcutName = this->dialog->getShortcutName().toStdString();
386 newShortcut.skillId = this->dialog->getSkillId().toStdString();
387 newShortcut.skillArgs = this->dialog->getSkillConfig().toStdString();
388 newShortcut.iconName = this->dialog->getIconName().toStdString();
389 newShortcut.id = shortcutId;
390 this->dashboardPrx->addNewShortcut(newShortcut);
392 catch (Ice::Exception
const&)
401 SkillDashboardWidget::exportButtons()
404 std::string packageName =
ui->packageEdit->text().toStdString();
405 std::string folderName =
ui->folderEdit->text().toStdString();
406 if (
ui->fileNameEdit->text().count(
' ') ==
ui->fileNameEdit->text().length())
411 std::string fileName =
ui->fileNameEdit->text().toStdString();
414 this->dashboardPrx->exportShortcuts(packageName, folderName, fileName);
416 catch (Ice::Exception
const&)
421 std::string coloredPath =
"<span style='color:red;'>" + packageName +
"/" + folderName +
422 "/" + fileName +
"</span>";
423 std::string propertyPath =
"Insert " + coloredPath +
424 " into the property 'ArmarX.SkillDashboard.ShortcutPath' of the "
425 "SkillDashboard component.";
428 ui->exportPath->setFullText(QString::fromStdString(propertyPath));
429 ui->exportPath->setTextInteractionFlags(Qt::TextSelectableByMouse);
433 SkillDashboardWidget::importButtons()
436 std::string packageName =
ui->packageEdit->text().toStdString();
437 std::string folderName =
ui->folderEdit->text().toStdString();
438 if (
ui->fileNameEdit->text().count(
' ') ==
ui->fileNameEdit->text().length())
443 std::string fileName =
ui->fileNameEdit->text().toStdString();
446 success = this->dashboardPrx->importShortcuts(packageName, folderName, fileName);
448 catch (Ice::Exception
const&)
459 SkillDashboardWidget::loadButtons()
462 this->saveShortcutOrder();
463 this->dashboardPrx->syncOrderedWithDefault();
464 this->shortcutButtons.clear();
465 this->configButtons.clear();
466 this->deleteButtons.clear();
467 ui->shortcutListWidget->clear();
469 std::vector<SkillShortcut> shortcuts;
472 shortcuts = this->dashboardPrx->getShortcuts();
474 catch (Ice::Exception
const& e)
480 for (
const auto& shortcut : shortcuts)
483 new EllipsisPushButton(QString::fromStdString(shortcut.shortcutName));
484 mainButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
485 mainButton->setToolTip(QString(
"Execute"));
487 QToolButton* editButton =
new QToolButton();
489 getWidget()->style()->standardIcon(QStyle::SP_FileDialogContentsView));
490 editButton->setToolTip(QString(
"Edit"));
492 QToolButton* deleteButton =
new QToolButton();
493 deleteButton->setIcon(
getWidget()->style()->standardIcon(QStyle::SP_TrashIcon));
494 deleteButton->setToolTip(QString(
"Delete"));
496 QWidget* rowWidget =
new QWidget();
497 QHBoxLayout* layout =
new QHBoxLayout(rowWidget);
498 layout->setContentsMargins(0, 0, 0, 0);
499 layout->setSpacing(2);
500 layout->addWidget(mainButton);
501 layout->addWidget(editButton);
502 layout->addWidget(deleteButton);
504 QListWidgetItem* item =
new QListWidgetItem(
ui->shortcutListWidget);
505 item->setSizeHint(rowWidget->sizeHint());
506 item->setData(Qt::UserRole, QString::fromStdString(shortcut.shortcutName));
507 ui->shortcutListWidget->addItem(item);
508 ui->shortcutListWidget->setItemWidget(item, rowWidget);
510 this->shortcutButtons[shortcut.shortcutName] = mainButton;
511 this->configButtons[shortcut.shortcutName] = editButton;
512 this->deleteButtons[shortcut.shortcutName] = deleteButton;
515 &QPushButton::clicked,
517 [
this, name = shortcut.shortcutName] { executeSkill(name); });
519 &QToolButton::clicked,
521 [
this, name = shortcut.shortcutName] { editShortcut(name); });
522 connect(deleteButton,
523 &QToolButton::clicked,
525 [
this, name = shortcut.shortcutName] { deleteShortcut(name); });
527 updateShortcutListHeight();
531 SkillDashboardWidget::executeSkill(
const std::string& name)
533 ARMARX_INFO <<
"About to execute skill with shortcut name `" << name
534 <<
"` from skill dashboard.";
536 SkillShortcut shortcut;
539 shortcut = this->dashboardPrx->getShortcut(name);
541 catch (Ice::Exception
const&)
544 std::string text =
"Shortcut '" + name +
"': Could not fetch shortcut.";
545 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
546 QString messageWithTimestamp =
547 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
548 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
551 size_t pos = shortcut.skillId.find(
'/');
552 std::string provider =
"";
553 std::string nameSkill =
"";
554 nlohmann::json json = nlohmann::json::parse(shortcut.skillArgs);
559 if (pos != std::string::npos)
561 provider = shortcut.skillId.substr(0, pos);
562 nameSkill = shortcut.skillId.substr(pos + 1);
563 skills::manager::dto::ProviderID providerId{.providerName = provider};
565 skills::manager::dto::SkillID skillId{.providerId = providerId, .skillName = nameSkill};
567 char hostname[HOST_NAME_MAX];
568 gethostname(hostname, HOST_NAME_MAX);
570 skills::manager::dto::SkillExecutionRequest request{
572 .executorName =
"Skills.Dashboard GUI (hostname: " + std::string(hostname) +
")",
573 .parameters = paramterDto,
579 ARMARX_IMPORTANT <<
"Executing skill with shortcut name `" << shortcut.shortcutName
580 <<
"` from skill dashboard.";
581 armarx::core::time::Duration skillTimeout =
584 IceUtil::Optional<armarx::skills::manager::dto::SkillDescription> optDto =
585 managerPrx->getSkillDescription(skillId);
589 const auto& dto = *optDto;
597 skills::manager::dto::SkillExecutionID executionId =
598 this->managerPrx->executeSkillAsync(request);
599 this->runningSkills[shortcut.shortcutName] = executionId;
600 auto* btn = this->shortcutButtons.at(shortcut.shortcutName);
601 btn->setDisabled(
true);
602 btn->startTimeout(skillTimeout.
toSeconds());
604 catch (Ice::Exception
const&)
607 std::string text =
"Shortcut '" + name +
"': Could not send execute request.";
608 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
609 QString messageWithTimestamp =
610 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
611 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
621 SkillDashboardWidget::editShortcut(
const std::string& name)
623 this->currentShortcutName = name;
625 SkillShortcut shortcut;
628 shortcut = this->dashboardPrx->getShortcut(name);
630 catch (Ice::Exception
const&)
634 openConfigWindow(shortcut.shortcutName,
639 this->currentShortcutName =
"not set";
643 SkillDashboardWidget::deleteShortcut(
const std::string& name)
648 this->dashboardPrx->deleteShortcut(name);
651 catch (Ice::Exception
const&)
659 SkillDashboardWidget::clearLayout(QLayout* layout)
664 while (QLayoutItem* item = layout->takeAt(0))
666 if (QWidget* widget = item->widget())
668 widget->deleteLater();
670 else if (QLayout* subLayout = item->layout())
672 clearLayout(subLayout);
683 if (parent != customToolbar->parent())
685 customToolbar->setParent(parent);
688 return customToolbar.data();
691 customToolbar =
new QToolBar(parent);
692 customToolbar->setIconSize(QSize(16, 16));
693 customToolbar->addAction(editModeAction);
694 customToolbar->addWidget(this->recoverButtons);
696 return customToolbar.data();
700 SkillDashboardWidget::editMode(
bool edit)
704 for (
const auto& button : this->deleteButtons)
706 button.second->setVisible(
true);
708 for (
const auto& button : this->configButtons)
710 button.second->setVisible(
true);
712 ui->addButton->setVisible(
true);
713 ui->reloadButton->setVisible(
true);
714 ui->exportButton->setVisible(
true);
715 ui->importButton->setVisible(
true);
716 ui->exportConfiguration->setVisible(
true);
717 ui->addFromClipboardButton->setVisible(
true);
718 ui->shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
722 for (
const auto& button : this->deleteButtons)
724 button.second->setVisible(
false);
726 for (
const auto& button : this->configButtons)
728 button.second->setVisible(
false);
730 ui->addButton->setVisible(
false);
731 ui->reloadButton->setVisible(
false);
732 ui->exportButton->setVisible(
false);
733 ui->importButton->setVisible(
false);
734 ui->exportConfiguration->setVisible(
false);
735 ui->addFromClipboardButton->setVisible(
false);
736 ui->shortcutListWidget->setDragDropMode(QAbstractItemView::NoDragDrop);
741 SkillDashboardWidget::enableBlockedButtons()
743 for (
auto& shortCutB : this->shortcutButtons)
745 shortCutB.second->setDisabled(
false);
752 this->connected.store(
false);
753 ARMARX_INFO <<
"Stopping thread which queries skill memory ...";
754 this->exampleTask.join();
768 if (not m_config_dialog)
772 {
"SkillDashboard",
"Skill Dashboard",
"*SkillDashboard"});
773 m_config_dialog->addProxyFinder<skills::manager::dti::SkillManagerInterfacePrx>(
774 "SkillMemory",
"",
"*SkillMemory");
776 return qobject_cast<QDialog*>(m_config_dialog);
783 this->skillDashboardProxyName =
785 ->value(
"skillDashboardProxyName", QString::fromStdString(skillDashboardProxyName))
788 this->skillManagerOberserverName =
789 settings->value(
"SkillMemory",
"SkillMemory").toString().toStdString();
796 settings->setValue(
"skillDashboardProxyName",
797 QString::fromStdString(skillDashboardProxyName));
798 settings->setValue(
"SkillMemory", QString::fromStdString(this->skillManagerOberserverName));
807 this->skillDashboardProxyName = m_config_dialog->getProxyName(
"SkillDashboard");
808 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)