21#include <QtConcurrent/QtConcurrent>
23#include <QtWidgets/QSlider>
24#include <QtWidgets/QTableWidgetItem>
38#include <nlohmann/json.hpp>
48 DEFAULT_SETTINGS_PLUGIN_NAME(
"SkillDashboardGuiPlugin"),
49 DEFAULT_SETTINGS_CUSTOM_TEXT(
"custom text")
54 this->shortcutLayout =
new QVBoxLayout();
56 this->editModeAction =
new QAction(
"Edit Mode",
this);
57 this->editModeAction->setCheckable(
true);
58 this->editModeAction->setToolTip(
"If toggled the shortcut config buttons and the reload, "
59 "add and export button will be shown.");
61 this->recoverButtons =
new QToolButton();
62 QIcon iconRecover =
getWidget()->style()->standardIcon(QStyle::SP_BrowserReload);
63 this->recoverButtons->setIcon(iconRecover);
64 this->recoverButtons->setToolTip(
"Recover all buttons");
67 ui.messageArea->layout()->addWidget(this->errorMessageArea);
69 ui.shortcutListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
70 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
71 ui.shortcutListWidget->setDefaultDropAction(Qt::MoveAction);
72 ui.shortcutListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
73 ui.shortcutListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
74 ui.shortcutListWidget->setStyleSheet(R
"(
76 background: transparent;
85 qRegisterMetaType<skills::core::dto::Execution::Status>(
86 "skills::core::dto::Execution::Status");
88 connect(this->editModeAction, SIGNAL(toggled(
bool)),
this, SLOT(editMode(
bool)));
89 connect(this->recoverButtons,
90 &QToolButton::clicked,
92 &SkillDashboardWidget::enableBlockedButtons);
93 connect(
ui.addFromClipboardButton,
94 &QPushButton::clicked,
96 &SkillDashboardWidget::addFromClipboard);
98 ui.addButton, &QPushButton::clicked,
this, [
this]() { openConfigWindow(
"",
"",
""); });
99 connect(
ui.reloadButton, &QPushButton::clicked,
this, &SkillDashboardWidget::loadButtons);
102 connect(
ui.exportButton, &QPushButton::clicked,
this, &SkillDashboardWidget::exportButtons);
103 connect(
ui.importButton, &QPushButton::clicked,
this, &SkillDashboardWidget::importButtons);
108 &SkillDashboardWidget::activateButton);
111 connect(this->dialog,
114 &SkillDashboardWidget::onShortcutNameChanged);
115 connect(
ui.stopAllButton, &QPushButton::clicked,
this, &SkillDashboardWidget::stopAll);
118 this->editModeAction->toggle();
130 if (not this->skillManagerOberserverName.empty())
144 this->connected.store(
true);
145 getProxy(this->dashboardPrx, skillDashboardProxyName);
146 getProxy(this->managerPrx, this->skillManagerOberserverName);
147 ARMARX_INFO <<
"Starting thread that queries the SkillsMemory if skills are running in the "
149 this->exampleTask = std::thread([&] { exampleThreadMethod(); });
155 SkillDashboardWidget::updateShortcutListHeight()
157 int rows =
ui.shortcutListWidget->count();
160 ui.shortcutListWidget->setMinimumHeight(0);
164 int rowHeight =
ui.shortcutListWidget->sizeHintForRow(0);
165 int frame =
ui.shortcutListWidget->frameWidth() * 2;
166 ui.shortcutListWidget->setMinimumHeight(rows * rowHeight + frame);
174 case skills::core::dto::Execution::Status::Failed:
176 case skills::core::dto::Execution::Status::Succeeded:
178 case skills::core::dto::Execution::Status::Aborted:
187 SkillDashboardWidget::saveShortcutOrder()
189 std::vector<std::string> order;
191 for (
int i = 0; i <
ui.shortcutListWidget->count(); ++i)
193 QListWidgetItem* item =
ui.shortcutListWidget->item(i);
194 QString name = item->data(Qt::UserRole).toString();
195 order.push_back(name.toStdString());
200 dashboardPrx->saveShortcutOrder(order);
202 catch (Ice::Exception
const&)
209 SkillDashboardWidget::loadPath()
211 std::vector<std::string> pathSegments;
214 pathSegments = dashboardPrx->getPathStructure();
216 catch (Ice::Exception
const&)
220 if (pathSegments.size() == 3)
222 ui.packageEdit->setText(QString::fromStdString(pathSegments[0]));
223 ui.folderEdit->setText(QString::fromStdString(pathSegments[1]));
224 ui.fileNameEdit->setText(QString::fromStdString(pathSegments[2]));
229 SkillDashboardWidget::activateButton(
const std::string& name,
230 skills::core::dto::Execution::Status
status)
232 auto* btn = this->shortcutButtons.at(name);
233 btn->setDisabled(
false);
234 if (
status != skills::core::dto::Execution::Status::Succeeded)
236 btn->finishProgress(
true);
237 std::string text =
"Shortcut '" + name +
"' terminated with status '" +
239 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
240 QString messageWithTimestamp =
241 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
242 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
244 btn->finishProgress(
false);
248 SkillDashboardWidget::addFromClipboard()
250 QClipboard* clipboard = QApplication::clipboard();
251 auto clipboardText = clipboard->text().toStdString();
255 j = nlohmann::json::parse(clipboardText);
257 catch (
const nlohmann::json::parse_error& e)
259 ARMARX_ERROR <<
"JSON Parse Error: " << e.what() <<
"\n";
262 auto shortcutImport = j[
"shortcuts"][0];
263 auto skillArgs = shortcutImport[
"skill_args"].dump(2);
264 auto skillId = shortcutImport[
"skill_id"];
265 this->openConfigWindow(
"", skillId, skillArgs);
269 SkillDashboardWidget::stopAll()
276 auto results = managerPrx->abortAllSkills();
277 for (
auto& r : results)
280 catch (Ice::Exception
const&)
285 this->enableBlockedButtons();
289 SkillDashboardWidget::exampleThreadMethod()
291 while (this->connected.load())
293 if (not this->runningSkills.empty())
295 for (
auto it = this->runningSkills.cbegin(); it != this->runningSkills.cend();)
297 skills::core::dto::Execution::Status
status =
298 skills::core::dto::Execution::Status::Succeeded;
302 IceUtil::Optional<skills::manager::dto::SkillStatusUpdate>
update =
303 this->managerPrx->getSkillExecutionStatus(it->second);
309 catch (Ice::Exception
const&)
314 if (
status == skills::core::dto::Execution::Status::Succeeded ||
315 status == skills::core::dto::Execution::Status::Failed ||
316 status == skills::core::dto::Execution::Status::Aborted)
320 it = this->runningSkills.erase(it);
334 SkillDashboardWidget::onShortcutNameChanged()
337 if ((this->shortcutButtons.find(this->dialog->getShortcutName().toStdString()) !=
338 this->shortcutButtons.end()) and
339 (this->dialog->getShortcutName().toStdString() != this->currentShortcutName))
341 this->dialog->setInfoText(
342 "A shortcut with this name already exists! The old one will be overwritten.");
344 <<
"A shortcut with this name already exists! The old one will be overwritten!";
348 this->dialog->setInfoText(
"");
353 SkillDashboardWidget::openConfigWindow(
const std::string& name,
354 const std::string&
id,
355 const std::string& args)
358 this->dialog->setShortcutName(name);
359 this->dialog->setSkillId(
id);
360 this->dialog->setSkillConfig(args);
363 if (this->dialog->exec() == QDialog::Accepted)
367 SkillShortcut newShortcut{
368 .shortcutName = this->dialog->getShortcutName().toStdString(),
369 .skillId = this->dialog->getSkillId().toStdString(),
370 .skillArgs = this->dialog->getSkillConfig().toStdString()};
371 this->dashboardPrx->addNewShortcut(newShortcut);
373 catch (Ice::Exception
const&)
382 SkillDashboardWidget::exportButtons()
385 std::string packageName =
ui.packageEdit->text().toStdString();
386 std::string folderName =
ui.folderEdit->text().toStdString();
387 if (
ui.fileNameEdit->text().count(
' ') ==
ui.fileNameEdit->text().length())
392 std::string fileName =
ui.fileNameEdit->text().toStdString();
395 this->dashboardPrx->exportShortcuts(packageName, folderName, fileName);
397 catch (Ice::Exception
const&)
402 std::string coloredPath =
"<span style='color:red;'>" + packageName +
"/" + folderName +
403 "/" + fileName +
"</span>";
404 std::string propertyPath =
"Insert " + coloredPath +
405 " into the property 'ArmarX.SkillDashboard.ShortcutPath' of the "
406 "SkillDashboard component.";
409 ui.exportPath->setFullText(QString::fromStdString(propertyPath));
410 ui.exportPath->setTextInteractionFlags(Qt::TextSelectableByMouse);
414 SkillDashboardWidget::importButtons()
417 std::string packageName =
ui.packageEdit->text().toStdString();
418 std::string folderName =
ui.folderEdit->text().toStdString();
419 if (
ui.fileNameEdit->text().count(
' ') ==
ui.fileNameEdit->text().length())
424 std::string fileName =
ui.fileNameEdit->text().toStdString();
427 success = this->dashboardPrx->importShortcuts(packageName, folderName, fileName);
429 catch (Ice::Exception
const&)
440 SkillDashboardWidget::loadButtons()
442 this->shortcutButtons.clear();
443 this->configButtons.clear();
444 this->deleteButtons.clear();
445 ui.shortcutListWidget->clear();
447 std::vector<SkillShortcut> shortcuts;
450 shortcuts = this->dashboardPrx->getShortcuts();
452 catch (Ice::Exception
const&)
458 for (
const auto& shortcut : shortcuts)
461 new EllipsisPushButton(QString::fromStdString(shortcut.shortcutName));
462 mainButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
463 mainButton->setToolTip(QString(
"Execute"));
465 QToolButton* editButton =
new QToolButton();
467 getWidget()->style()->standardIcon(QStyle::SP_FileDialogContentsView));
468 editButton->setToolTip(QString(
"Edit"));
470 QToolButton* deleteButton =
new QToolButton();
471 deleteButton->setIcon(
getWidget()->style()->standardIcon(QStyle::SP_TrashIcon));
472 deleteButton->setToolTip(QString(
"Delete"));
474 QWidget* rowWidget =
new QWidget();
475 QHBoxLayout* layout =
new QHBoxLayout(rowWidget);
476 layout->setContentsMargins(0, 0, 0, 0);
477 layout->setSpacing(2);
478 layout->addWidget(mainButton);
479 layout->addWidget(editButton);
480 layout->addWidget(deleteButton);
482 QListWidgetItem* item =
new QListWidgetItem(
ui.shortcutListWidget);
483 item->setSizeHint(rowWidget->sizeHint());
484 item->setData(Qt::UserRole, QString::fromStdString(shortcut.shortcutName));
485 ui.shortcutListWidget->addItem(item);
486 ui.shortcutListWidget->setItemWidget(item, rowWidget);
488 this->shortcutButtons[shortcut.shortcutName] = mainButton;
489 this->configButtons[shortcut.shortcutName] = editButton;
490 this->deleteButtons[shortcut.shortcutName] = deleteButton;
493 &QPushButton::clicked,
495 [
this, name = shortcut.shortcutName] { executeSkill(name); });
497 &QToolButton::clicked,
499 [
this, name = shortcut.shortcutName] { editShortcut(name); });
500 connect(deleteButton,
501 &QToolButton::clicked,
503 [
this, name = shortcut.shortcutName] { deleteShortcut(name); });
505 updateShortcutListHeight();
509 SkillDashboardWidget::executeSkill(
const std::string& name)
511 ARMARX_INFO <<
"About to execute skill with shortcut name `" << name
512 <<
"` from skill dashboard.";
514 SkillShortcut shortcut;
517 shortcut = this->dashboardPrx->getShortcut(name);
519 catch (Ice::Exception
const&)
522 std::string text =
"Shortcut '" + name +
"': Could not fetch shortcut.";
523 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
524 QString messageWithTimestamp =
525 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
526 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
529 size_t pos = shortcut.skillId.find(
'/');
530 std::string provider =
"";
531 std::string nameSkill =
"";
532 nlohmann::json json = nlohmann::json::parse(shortcut.skillArgs);
537 if (pos != std::string::npos)
539 provider = shortcut.skillId.substr(0, pos);
540 nameSkill = shortcut.skillId.substr(pos + 1);
541 skills::manager::dto::ProviderID providerId{.providerName = provider};
543 skills::manager::dto::SkillID skillId{.providerId = providerId, .skillName = nameSkill};
545 char hostname[HOST_NAME_MAX];
546 gethostname(hostname, HOST_NAME_MAX);
548 skills::manager::dto::SkillExecutionRequest request{
550 .executorName =
"Skills.Dashboard GUI (hostname: " + std::string(hostname) +
")",
551 .parameters = paramterDto,
557 ARMARX_IMPORTANT <<
"Executing skill with shortcut name `" << shortcut.shortcutName
558 <<
"` from skill dashboard.";
559 armarx::core::time::Duration skillTimeout =
562 IceUtil::Optional<armarx::skills::manager::dto::SkillDescription> optDto =
563 managerPrx->getSkillDescription(skillId);
567 const auto& dto = *optDto;
575 skills::manager::dto::SkillExecutionID executionId =
576 this->managerPrx->executeSkillAsync(request);
577 this->runningSkills[shortcut.shortcutName] = executionId;
578 auto* btn = this->shortcutButtons.at(shortcut.shortcutName);
579 btn->setDisabled(
true);
580 btn->startTimeout(skillTimeout.
toSeconds());
582 catch (Ice::Exception
const&)
585 std::string text =
"Shortcut '" + name +
"': Could not send execute request.";
586 QString
timestamp = QDateTime::currentDateTime().toString(
"HH:mm:ss");
587 QString messageWithTimestamp =
588 QString(
"[%1] %2").arg(
timestamp, QString::fromStdString(text));
589 this->errorMessageArea->newErrorMessage(messageWithTimestamp);
599 SkillDashboardWidget::editShortcut(
const std::string& name)
601 this->currentShortcutName = name;
603 SkillShortcut shortcut;
606 shortcut = this->dashboardPrx->getShortcut(name);
608 catch (Ice::Exception
const&)
612 openConfigWindow(shortcut.shortcutName, shortcut.skillId, shortcut.skillArgs);
613 this->currentShortcutName =
"not set";
617 SkillDashboardWidget::deleteShortcut(
const std::string& name)
622 this->dashboardPrx->deleteShortcut(name);
625 catch (Ice::Exception
const&)
633 SkillDashboardWidget::clearLayout(QLayout* layout)
638 while (QLayoutItem* item = layout->takeAt(0))
640 if (QWidget* widget = item->widget())
642 widget->deleteLater();
644 else if (QLayout* subLayout = item->layout())
646 clearLayout(subLayout);
657 if (parent != customToolbar->parent())
659 customToolbar->setParent(parent);
662 return customToolbar.data();
665 customToolbar =
new QToolBar(parent);
666 customToolbar->setIconSize(QSize(16, 16));
667 customToolbar->addAction(editModeAction);
668 customToolbar->addWidget(this->recoverButtons);
670 return customToolbar.data();
674 SkillDashboardWidget::editMode(
bool edit)
678 for (
const auto& button : this->deleteButtons)
680 button.second->setVisible(
true);
682 for (
const auto& button : this->configButtons)
684 button.second->setVisible(
true);
686 ui.addButton->setVisible(
true);
687 ui.reloadButton->setVisible(
true);
688 ui.exportButton->setVisible(
true);
689 ui.importButton->setVisible(
true);
690 ui.exportConfiguration->setVisible(
true);
691 ui.addFromClipboardButton->setVisible(
true);
692 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::InternalMove);
696 for (
const auto& button : this->deleteButtons)
698 button.second->setVisible(
false);
700 for (
const auto& button : this->configButtons)
702 button.second->setVisible(
false);
704 ui.addButton->setVisible(
false);
705 ui.reloadButton->setVisible(
false);
706 ui.exportButton->setVisible(
false);
707 ui.importButton->setVisible(
false);
708 ui.exportConfiguration->setVisible(
false);
709 ui.addFromClipboardButton->setVisible(
false);
710 ui.shortcutListWidget->setDragDropMode(QAbstractItemView::NoDragDrop);
715 SkillDashboardWidget::enableBlockedButtons()
717 for (
auto& shortCutB : this->shortcutButtons)
719 shortCutB.second->setDisabled(
false);
726 this->connected.store(
false);
727 ARMARX_INFO <<
"Stopping thread which queries skill memory ...";
728 this->exampleTask.join();
742 if (not m_config_dialog)
746 {
"SkillDashboard",
"Skill Dashboard",
"*SkillDashboard"});
747 m_config_dialog->addProxyFinder<skills::manager::dti::SkillManagerInterfacePrx>(
748 "SkillMemory",
"",
"*SkillMemory");
750 return qobject_cast<QDialog*>(m_config_dialog);
757 this->skillDashboardProxyName =
759 ->value(
"skillDashboardProxyName", QString::fromStdString(skillDashboardProxyName))
762 this->skillManagerOberserverName =
763 settings->value(
"SkillMemory",
"SkillMemory").toString().toStdString();
770 settings->setValue(
"skillDashboardProxyName",
771 QString::fromStdString(skillDashboardProxyName));
772 settings->setValue(
"SkillMemory", QString::fromStdString(this->skillManagerOberserverName));
781 this->skillDashboardProxyName = m_config_dialog->getProxyName(
"SkillDashboard");
782 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)