YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
clusterWidget.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-License-Identifier: LGPL-2.1-or-later
4 */
5
6#include "clusterWidget.h"
7#include "ui_clusterWidget.h"
8#include "iostream"
9#include <QLineEdit>
10#include <QCheckBox>
11#include <QLabel>
12#include <QRadioButton>
13#include <QStringList>
14
15#include <yarp/os/LogStream.h>
16#include <yarp/os/Time.h>
17
20
21#include <algorithm>
22
23#include <mainwindow.h>
24
25using namespace yarp::os;
26using namespace yarp::manager;
27
29 QWidget(parent),
30 ui(new Ui::ClusterWidget), confFile(""), clusLoader(nullptr), checkNs(false)
31{
32
33#ifdef WIN32
34 this->setDisabled(true);
35 return;
36#endif
37 ui->setupUi(this);
38 ui->executeBtn->setDisabled(true);
39 ui->labelNs->setPixmap(QPixmap(":/close.svg").scaledToHeight(ui->checkRos->height()));
40
41 //Connections to slots
42
43 //nameserver
44 connect(ui->checkServerBtn, SIGNAL(clicked(bool)), this, SLOT(onCheckServer()));
45 connect(ui->runServerBtn, SIGNAL(clicked(bool)), this, SLOT(onRunServer()));
46 connect(ui->stopServerBtn, SIGNAL(clicked(bool)), this, SLOT(onStopServer()));
47 //yarprun
48 connect(ui->checkAllBtn, SIGNAL(clicked(bool)), this, SLOT(onCheckAll()));
49 connect(ui->runSelBtn, SIGNAL(clicked(bool)), this, SLOT(onRunSelected()));
50 connect(ui->stopSelBtn, SIGNAL(clicked(bool)), this, SLOT(onStopSelected()));
51 connect(ui->killSelBtn, SIGNAL(clicked(bool)), this, SLOT(onKillSelected()));
52 //execute
53 connect(ui->executeBtn, SIGNAL(clicked(bool)), this, SLOT(onExecute()));
54
55 connect(ui->nodestreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onNodeSelectionChanged()));
56 connect(ui->lineEditExecute, SIGNAL(textChanged(QString)), SLOT(onExecuteTextChanged()));
57
58}
59
61{
62 confFile = _confFile;
63}
64
66{
67 clusLoader = new XmlClusterLoader(confFile);
68 if (clusLoader)
69 {
70 if (!clusLoader->parseXmlFile(cluster))
71 {
72 yError()<<"ClusterWidget:Unable parse cluster-config.xml in context iCubCluster";
73 this->setDisabled(true);
74 return;
75 }
76 }
77
78 ui->lineEditUser->setText(cluster.user.c_str());
79 ui->lineEditNs->setText(cluster.nameSpace.c_str());
80
81 //check if yarpserver is running
82
83 onCheckServer();
84
86 //Adding nodes
87
88 l.push_back(cluster.nsNode.c_str());
89 int i{0};
90 for (auto& node:cluster.nodes)
91 {
92 addRow(node.name, node.displayValue, node.user, node.address, node.onOff, node.log, i);
93 i++;
94 if (cluster.nsNode == node.name) {
95 continue;
96 }
97 l.push_back(node.name.c_str());
98 }
99
100 // populate the execute combo box
101 ui->executeComboBox->addItems(l);
102 ui->executeComboBox->setEditable(true);
103
104 ui->nsNodeComboBox->addItems(l);
105 ui->nsNodeComboBox->setEditable(true);
106
107 //check if all the nodes are up
108 if (checkNs)
109 {
110 onCheckAll();
111 }
112
113 ui->nodestreeWidget->header()->resizeSection(0, 50);
114
115 onNodeSelectionChanged();
116
117}
118
119void ClusterWidget::onCheckAll()
120{
121 for (int i = 0; i<ui->nodestreeWidget->topLevelItemCount(); i++)
122 {
123 QTreeWidgetItem *it = ui->nodestreeWidget->topLevelItem(i);
124 int itr = it->text(6).toInt();
125 ClusterNode node = cluster.nodes[itr];
126 if (checkNode(node.name))
127 {
128 cluster.nodes[itr].onOff=true;
129 it->setIcon(0, QIcon(":/computer-available22.svg"));
130 }
131 else
132 {
133 cluster.nodes[itr].onOff=false;
134 it->setIcon(0, QIcon(":/computer-unavailable22.svg"));
135 }
136 }
137
138}
139
140void ClusterWidget::onCheckServer()
141{
142 checkNs = checkNameserver();
143 if (checkNs) {
144 ui->labelNs->setPixmap(QPixmap(":/apply.svg").scaledToHeight(ui->checkRos->height()));
145 }
146 else {
147 ui->labelNs->setPixmap(QPixmap(":/close.svg").scaledToHeight(ui->checkRos->height()));
148 }
149
150 ui->checkRos->setDisabled(checkNs);
151 ui->runServerBtn->setDisabled(checkNs);
152 ui->nsNodeComboBox->setDisabled(checkNs);
153 ui->stopServerBtn->setDisabled(!checkNs);
154}
155
156void ClusterWidget::onRunServer()
157{
158 updateServerEntries();
159
161 std::string cmdRunServer = getSSHCmd(cluster.user, cluster.nsNode, cluster.ssh_options);
162 if (ui->checkRos->isChecked())
163 {
164 cmdRunServer = cmdRunServer + " yarpserver --portdb :memory: --subdb :memory: --ros >/dev/null 2>&1 &";
165 }
166 else
167 {
168 cmdRunServer = cmdRunServer + " yarpserver --portdb :memory: --subdb :memory: >/dev/null 2>&1 &";
169 }
170 if (system(cmdRunServer.c_str()) != 0)
171 {
172 std::string err = "ClusterWidget: failed to run the server on " + cluster.nsNode;
173 logger->addError(err);
174 reportErrors();
175 }
176 else
177 {
179 onCheckServer();
180 }
181}
182
183void ClusterWidget::onStopServer()
184{
185 updateServerEntries();
186
187 auto count = std::count_if(cluster.nodes.begin(), cluster.nodes.end(),
188 [](const ClusterNode& e){ return e.onOff; });
189
190 if (count > 0) {
191
192 auto reply = QMessageBox::warning(this, "Shutting down yarpserver",
193 "You have some yarprun on execution."
194 " After shutting down yarpserver you might not be able to recover them."
195 " Are you sure?",
196 QMessageBox::Yes|QMessageBox::No);
197 if (reply== QMessageBox::No) {
198 return;
199 }
200 }
201
202 std::string cmdStopServer = getSSHCmd(cluster.user, cluster.nsNode, cluster.ssh_options);
203
204 cmdStopServer = cmdStopServer + " killall yarpserver &";
205
207
208 if (system(cmdStopServer.c_str()) != 0)
209 {
210 std::string err = "ClusterWidget: failed to stop the server on " + cluster.nsNode;
211 logger->addError(err);
212 reportErrors();
213 }
214 else
215 {
217 onCheckServer();
218 }
219
220 // if it fails to stop, kill it
221 if (checkNs)
222 {
223 onKillServer();
224 }
225 else
226 {
227 std::string info = "ClusterWidget: yarpserver successfully stopped on "+ cluster.nsNode;
228 logMessage(QString(info.c_str()));
229 }
230}
231
232void ClusterWidget::onKillServer()
233{
234 updateServerEntries();
235
236 std::string cmdKillServer = getSSHCmd(cluster.user, cluster.nsNode, cluster.ssh_options);
237
238 cmdKillServer = cmdKillServer + " killall -9 yarpserver &";
239
241 if (system(cmdKillServer.c_str()) != 0)
242 {
243 std::string err = "ClusterWidget: failed to kill the server on " + cluster.nsNode;
244 logger->addError(err);
245 reportErrors();
246 }
247 else
248 {
249 std::string info = "ClusterWidget: yarpserver successfully killed on "+ cluster.nsNode;
250 logMessage(QString(info.c_str()));
251 }
252
253
254}
255
256void ClusterWidget::onRunSelected()
257{
258 QList<QTreeWidgetItem*> selectedItems = ui->nodestreeWidget->selectedItems();
259 foreach (QTreeWidgetItem *it, selectedItems)
260 {
261 int itr = it->text(6).toInt();
262 ClusterNode node = cluster.nodes[itr];
263 std::string portName = node.name;
264
265 if (portName.find('/') == std::string::npos)
266 {
267 portName.insert(0, 1, '/');
268 }
269
270 if (node.onOff)
271 {
272 continue;
273 }
274
275 std::string cmdRunYarprun = getSSHCmd(node.user, node.address, node.ssh_options);
276 if (node.display)
277 {
278 cmdRunYarprun.append(" 'export DISPLAY=").append(node.displayValue).append(" && ");
279
280 }
281 if (qobject_cast<QCheckBox*>(ui->nodestreeWidget->itemWidget((QTreeWidgetItem *)it, 5))->isChecked())
282 {
283 cmdRunYarprun.append(" yarprun --server ").append(portName).append(" --log 2>&1 2>/tmp/yarprunserver.log");
284 }
285 else
286 {
287 cmdRunYarprun.append(" yarprun --server ").append(portName).append(" 2>&1 2>/tmp/yarprunserver.log");
288 }
289
290 if (node.display)
291 {
292 cmdRunYarprun.append("'");
293 }
295 if (system(cmdRunYarprun.c_str()) != 0)
296 {
297 std::string err = "ClusterWidget: failed to run yarprun on " + node.name;
298 logger->addError(err);
299 reportErrors();
300 }
301 else
302 {
303 std::string info = "ClusterWidget: yarprun successfully executed on "+ node.name;
304 logMessage(QString(info.c_str()));
305 }
306 }
307
309 onCheckAll();
310}
311
312
313void ClusterWidget::onStopSelected()
314{
315 QList<QTreeWidgetItem*> selectedItems = ui->nodestreeWidget->selectedItems();
316 foreach (QTreeWidgetItem *it, selectedItems)
317 {
318 int itr = it->text(6).toInt();
319 ClusterNode node = cluster.nodes[itr];
320 if (!node.onOff)
321 {
322 continue;
323 }
324 std::string portName = node.name;
325 if (portName.find('/') == std::string::npos)
326 {
327 portName.insert(0, 1, '/');
328 }
329
330 std::string cmdStopYarprun = getSSHCmd(node.user, node.address, node.ssh_options);
331
332 cmdStopYarprun.append(" yarprun --exit --on ").append(portName).append(" &");
333
335 if (system(cmdStopYarprun.c_str()) != 0)
336 {
337 std::string err = "ClusterWidget: failed to stop yarprun on " + node.name;
338 logger->addError(err);
339 reportErrors();
340 }
341 else
342 {
343 std::string info = "ClusterWidget: yarprun successfully stopped on "+ node.name;
344 logMessage(QString(info.c_str()));
345 }
346 }
347
349 onCheckAll();
350}
351
352void ClusterWidget::onKillSelected()
353{
354 QList<QTreeWidgetItem*> selectedItems = ui->nodestreeWidget->selectedItems();
355 foreach (QTreeWidgetItem *it, selectedItems)
356 {
357 int itr = it->text(6).toInt();
358 ClusterNode node = cluster.nodes[itr];
359 if (!node.onOff)
360 {
361 continue;
362 }
363
364 std::string cmdKillYarprun = getSSHCmd(node.user, node.address, node.ssh_options);
365
366 cmdKillYarprun.append(" killall -9 yarprun &");
367
369 if (system(cmdKillYarprun.c_str()) != 0)
370 {
371 std::string err = "ClusterWidget: failed to kill yarprun on " + node.name;
372 logger->addError(err);
373 reportErrors();
374 }
375 else
376 {
377 std::string info = "ClusterWidget: yarprun successfully killed on "+ node.name;
378 logMessage(QString(info.c_str()));
379 }
380 }
382 onCheckAll();
383}
384
385void ClusterWidget::onExecute()
386{
387 if (ui->lineEditExecute->text().trimmed().size() == 0)
388 {
389 return;
390 }
391
392 auto nodeName = ui->executeComboBox->currentText();
393
394 if (nodeName.trimmed().size() == 0)
395 {
396 return;
397 }
398
399 auto nodeItr = std::find_if(cluster.nodes.begin(), cluster.nodes.end(),
400 [&nodeName](const ClusterNode& n){ return n.name == nodeName.toStdString(); });
401
402
403 if (nodeItr == cluster.nodes.end())
404 {
405 return;
406 }
407
408 auto node = *nodeItr;
409 auto command = ui->lineEditExecute->text().toStdString();
410
411 std::string cmdExecute = getSSHCmd(node.user, node.address, node.ssh_options);
412
413 cmdExecute.append(" ").append(command);
414
416 if (system(cmdExecute.c_str()) != 0)
417 {
418 std::string err = "ClusterWidget: failed to run "+ command + " on " + node.name;
419 logger->addError(err);
420 reportErrors();
421 }
422 else
423 {
424 std::string info = "ClusterWidget: command "+ command + " successfully executed on " + node.name;
425 logMessage(QString(info.c_str()));
426 }
427
428 ui->lineEditExecute->clear();
429}
430
431void ClusterWidget::onNodeSelectionChanged()
432{
433 if(ui->nodestreeWidget->selectedItems().isEmpty())
434 {
435 ui->runSelBtn->setDisabled(true);
436 ui->stopSelBtn->setDisabled(true);
437 ui->killSelBtn->setDisabled(true);
438 }
439 else
440 {
441 ui->runSelBtn->setDisabled(!checkNs);
442 ui->stopSelBtn->setDisabled(!checkNs);
443 ui->killSelBtn->setDisabled(!checkNs);
444 }
445}
446
447
448void ClusterWidget::onExecuteTextChanged()
449{
450 if (ui->lineEditExecute->text().trimmed().size() > 0) {
451 ui->executeBtn->setDisabled(false);
452 } else {
453 ui->executeBtn->setDisabled(true);
454 }
455}
456
457
458
459void ClusterWidget::addRow(const std::string& name,const std::string& display,
460 const std::string& user, const std::string& address,
461 bool onOff, bool log, int id)
462{
464 stringList <<""<< QString(name.c_str()) << QString(display.c_str()) << QString(user.c_str()) << QString(address.c_str())<< "" <<QString(std::to_string(id).c_str());
465 auto* it = new QTreeWidgetItem(stringList);
466 ui->nodestreeWidget->addTopLevelItem(it);
467 ui->nodestreeWidget->setItemWidget((QTreeWidgetItem *) it, 5, new QCheckBox(this));
468
469 //initialize checkboxes
470 qobject_cast<QCheckBox*>(ui->nodestreeWidget->itemWidget((QTreeWidgetItem *)it, 5))->setChecked(log);
471
472 //initialize icon
473 if (onOff)
474 {
475 it->setIcon(0, QIcon(":/computer-available22.svg"));
476 }
477 else
478 {
479 it->setIcon(0, QIcon(":/computer-unavailable22.svg"));
480 }
481
482}
483
484std::string ClusterWidget::getSSHCmd(const std::string &user, const std::string &host, const std::string &ssh_options)
485{
486 std::string cmd;
487 cmd = "ssh -f";
488 if (!ssh_options.empty())
489 {
490 cmd = cmd + " " + ssh_options;
491 }
492 if (user.empty())
493 {
494 cmd = cmd + " " + host;
495 }
496 else
497 {
498 cmd = cmd + " " + user + "@" +host;
499 }
500
501 return cmd;
502}
503
504bool ClusterWidget::checkNameserver()
505{
506 std::string name = ui->lineEditNs->text().toStdString();
507
508 if (name.empty())
509 {
510 return false;
511 }
512
513 if (name.find('/') == std::string::npos)
514 {
515 name = "/" + name;
516 }
517
518
520 {
521 yError()<<"ClusterWidget: yarpserver is not running";
522 return false;
523 }
524
525
526 yarp::os::Bottle cmd, reply;
527 cmd.addString("get");
528 cmd.addString(name);
529 cmd.addString("nameserver");
530 bool ret = yarp::os::impl::NameClient::getNameClient().send(cmd, reply);
531 if (!ret)
532 {
533 yError()<<"ClusterWidget: Cannot contact the NameClient";
534 return false;
535 }
536 if (reply.size()==6)
537 {
538 if (reply.get(5).asBool())
539 {
540 return true;
541 }
542 else
543 {
544 return false;
545 }
546
547 }
548 else
549 {
550 return false;
551 }
552}
553
554bool ClusterWidget::checkNode(const std::string &name)
555{
556 std::string portname = name;
557 if (portname.find('/') == std::string::npos)
558 {
559 portname = "/" + portname;
560 }
561
563 {
564 yError()<<"ClusterWidget: yarpserver is not running";
565 return false;
566 }
567
570 {
571 yError()<<"ClusterWidget: port"<<portname<<"is not responding";
572 return false;
573 }
574
575
576 yarp::os::Bottle cmd, reply;
577 cmd.addString("get");
578 cmd.addString(portname);
579 cmd.addString("yarprun");
580 bool ret = yarp::os::impl::NameClient::getNameClient().send(cmd, reply);
581 if (!ret)
582 {
583 yError()<<"ClusterWidget: Cannot contact the NameClient";
584 return false;
585 }
586 if (reply.size()==6)
587 {
588 if (reply.get(5).asBool())
589 {
590 return true;
591 }
592 else
593 {
594 return false;
595 }
596
597 }
598 else
599 {
600 return false;
601 }
602
603}
604
605void ClusterWidget::updateServerEntries()
606{
607 // remove all the whitespaces
608 cluster.user = ui->lineEditUser->text().simplified().trimmed().toStdString();
609 cluster.nsNode = ui->nsNodeComboBox->currentText().simplified().trimmed().toStdString();
610}
611
612void ClusterWidget::reportErrors()
613{
615 if (logger->errorCount() || logger->warningCount())
616 {
617 const char* err;
618 while((err=logger->getLastError()))
619 {
620 QString msg = QString("ClusterWidget: %1").arg(err);
621 emit logError(msg);
622 }
623 }
624}
625
627{
628 if (clusLoader)
629 {
630 delete clusLoader;
631 clusLoader = nullptr;
632 }
633 delete ui;
634}
bool ret
#define yError(...)
Definition Log.h:361
int SIGNAL(int pid, int signum)
void logMessage(QString)
void setConfigFile(const std::string &_confFile)
void logError(QString)
ClusterWidget(QWidget *parent=0)
Singleton class ErrorLogger.
Definition utility.h:58
const char * getLastError()
Definition utility.cpp:148
void addError(const char *szError)
Definition utility.cpp:126
static ErrorLogger * Instance()
Singleton class ErrorLogger.
Definition utility.cpp:98
bool parseXmlFile(Cluster &_cluster)
A simple collection of objects that can be described and transmitted in a portable way.
Definition Bottle.h:64
size_type size() const
Gets the number of elements in the bottle.
Definition Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition Bottle.cpp:246
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition Bottle.cpp:170
A mini-server for performing network communication in the background.
static bool checkNetwork()
Check if the YARP Network is up and running.
Definition Network.cpp:1370
virtual bool asBool() const
Get boolean value.
Definition Value.cpp:186
static NameClient & getNameClient()
Get an instance of the name client.
static bool getPortDetails(const std::string &portName, PortDetails &info)
Definition aboutdlg.h:11
void delay(double seconds)
Wait for a certain number of seconds.
Definition Time.cpp:111
An interface to the operating system, including Port based communication.
std::vector< ClusterNode > nodes