7#include "drivermanager.h"
9#include "config-kstars.h"
11#include "clientmanager.h"
12#include "driverinfo.h"
13#include "guimanager.h"
14#include "indilistener.h"
18#include "kstarsdata.h"
20#include "servermanager.h"
21#include "ui_indihostconf.h"
22#include "auxiliary/ksnotification.h"
24#include <basedevice.h>
28#include <KActionCollection>
29#include <KNotifications/KNotification>
33#include <QtConcurrent>
34#include <indi_debug.h>
36#define INDI_MAX_TRIES 2
37#define ERRMSG_SIZE 1024
39DriverManagerUI::DriverManagerUI(
QWidget *parent) :
QFrame(parent)
57 &DriverManagerUI::makePortEditable);
60void DriverManagerUI::makePortEditable(
QTreeWidgetItem *selectedItem,
int column)
63 if (column == ::DriverManager::LOCAL_PORT_COLUMN)
67 localTreeWidget->editItem(selectedItem, ::DriverManager::LOCAL_PORT_COLUMN);
71INDIDBus *DriverManager::_INDIDBus =
nullptr;
75 if (_DriverManager ==
nullptr)
83 return _DriverManager;
86void DriverManager::release()
88 delete (_DriverManager);
90 _DriverManager =
nullptr;
100 currentPort = Options::serverPortStart() - 1;
103 ui =
new DriverManagerUI(
this);
105 setLayout(mainLayout);
106 setWindowTitle(
i18nc(
"@title:window",
"Device Manager"));
126 if (Options::indiDriversDir().isEmpty())
127 Options::setIndiDriversDir(
137 updateCustomDrivers();
140 ui->localTreeWidget->setEnabled(
false);
144DriverManager::~DriverManager()
151 if (driver.isNull() || driver->getDriverSource() == GENERATED_SOURCE)
155 ServerMode mode = connectionMode;
160 if (driver->getDriverSource() != HOST_SOURCE)
162 if (ui->localTreeWidget->currentItem())
163 currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN);
168 item->setText(LOCAL_VERSION_COLUMN, driver->getVersion());
171 mode = manager->getMode();
173 dState = driver->getServerState();
177 if (driver->getAuxInfo().contains(
"LOCALLY_AVAILABLE"))
179 driver->getAuxInfo().value(
"LOCALLY_AVAILABLE",
false).toBool();
188 item->setIcon(LOCAL_STATUS_COLUMN,
189 dState ? ui->runningPix : ui->stopPix);
199 item->setIcon(LOCAL_MODE_COLUMN, ui->serverMode);
207 item->setIcon(LOCAL_MODE_COLUMN,
QIcon());
208 item->setText(LOCAL_PORT_COLUMN,
QString::number(driver->getUserPort()));
218 item->setIcon(LOCAL_STATUS_COLUMN,
219 cState ? ui->runningPix : ui->stopPix);
229 item->setIcon(LOCAL_MODE_COLUMN, ui->localMode);
238 item->setIcon(LOCAL_MODE_COLUMN,
QIcon());
239 const auto port = driver->getUserPort() == -1 ?
QString() :
QString::
number(driver->getUserPort());
240 item->setText(LOCAL_PORT_COLUMN, port);
249 ui->serverLogText->clear();
250 ui->serverLogText->append(driver->getServerBuffer());
259 if (driver->getClientState())
261 item->setIcon(HOST_STATUS_COLUMN, ui->connected);
267 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
289 if ((
dv->getHost() ==
idv->getHost() &&
dv->getPort() ==
idv->getPort()))
292 if (
dv->getClientState() ||
dv->getServerState())
296 i18n(
"Driver %1 is already running, do you want to restart it?",
328 if (
uList.empty() ==
false)
341 qCDebug(
KSTARS_INDI) <<
"INDI: Starting local drivers...";
348 port =
qdv.at(0)->getPort();
349 auto host =
qdv.at(0)->getHost();
353 port = getINDIPort(port);
357 emit serverFailed(host, port,
i18n(
"Cannot start INDI server: port error."));
363 if (serverManager ==
nullptr)
365 emit serverFailed(host, port,
i18n(
"Failed to create local INDI server"));
369 servers.
append(serverManager);
370 serverManager->setPendingDrivers(
qdv);
371 serverManager->setMode(connectionMode);
378 serverManager->start();
382void DriverManager::startLocalDrivers(
ServerManager *serverManager)
386 QtConcurrent::run(serverManager, &ServerManager::startDriver, serverManager->pendingDrivers().first());
391 emit driverStarted(driver);
393 auto manager = driver->getServerManager();
395 if (manager->pendingDrivers().count() > 0)
397 QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first());
402 if (connectionMode == SERVER_ONLY)
408 startClientManager(manager->managedDrivers(), manager->getHost(), manager->getPort());
413 emit driverFailed(driver, message);
415 qCWarning(
KSTARS_INDI) <<
"Driver" << driver->getName() <<
"failed to start. Retrying in 5 seconds...";
419 auto manager = driver->getServerManager();
421 if (manager->pendingDrivers().count() > 0)
423 QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first());
434 clientManager->appendManagedDriver(
dv);
442 GUIManager::Instance()->addClient(clientManager);
443 INDIListener::Instance()->addClient(clientManager);
445 clientManager->establishConnection();
450 qCDebug(
KSTARS_INDI) <<
"INDI: Stopping local drivers...";
460 cm->removeManagedDriver(
dv);
462 if (
cm->count() == 0)
464 GUIManager::Instance()->removeClient(
cm);
465 INDIListener::Instance()->removeClient(
cm);
466 cm->disconnectServer();
494 currentPort = Options::serverPortStart() - 1;
499void DriverManager::disconnectClients()
501 for (
auto &clientManager : clients)
503 clientManager->disconnectAll();
504 clientManager->disconnect();
508void DriverManager::clearServers()
510 for (
auto &serverManager : servers)
511 serverManager->
stop();
516void DriverManager::activateRunService()
518 processLocalTree(
true);
521void DriverManager::activateStopService()
523 processLocalTree(
false);
526void DriverManager::activateHostConnection()
528 processRemoteTree(
true);
531void DriverManager::activateHostDisconnection()
533 processRemoteTree(
false);
538 auto cm = driver->getClientManager();
544 auto host = driver->getHost();
545 auto port = driver->getPort();
546 auto it = std::find_if(clients.
begin(), clients.
end(), [host, port](
const auto &
oneClient)
548 return oneClient->getHost() == host && oneClient->getPort() == port;
551 if (
it != clients.
end())
558void DriverManager::updateLocalTab()
560 if (ui->localTreeWidget->currentItem() ==
nullptr)
567 return currentDriver == oneDriver->getLabel();
569 if (device != driversList.
end())
570 processDeviceStatus(*device);
573void DriverManager::updateClientTab()
581 int port = item->
text(HOST_PORT_COLUMN).
toInt();
583 auto device = std::find_if(driversList.
begin(), driversList.
end(), [hostname, port](
const auto &
oneDriver)
585 return hostname == oneDriver->getName() && port == oneDriver->getPort();
587 if (device != driversList.
end())
588 processDeviceStatus(*device);
591void DriverManager::processLocalTree(
bool dState)
598 connectionMode = ui->localR->isChecked() ? SERVER_CLIENT : SERVER_ONLY;
602 for (
auto &device : driversList)
607 if (item->
text(LOCAL_NAME_COLUMN) == device->getLabel() &&
608 device->getServerState() !=
dState)
615 if (port == -1 && item->
text(LOCAL_PORT_COLUMN).
isEmpty() ==
false)
621 KSNotification::error(
i18n(
"Invalid port entry: %1", item->
text(LOCAL_PORT_COLUMN)));
626 device->setHostParameters(
"localhost", port);
640void DriverManager::setClientFailed(
const QString &message)
644 if (client ==
nullptr)
647 auto host = client->getHost();
648 auto port = client->getPort();
652 emit clientFailed(host, port, message);
654 GUIManager::Instance()->removeClient(client);
655 INDIListener::Instance()->removeClient(client);
657 KSNotification::event(
QLatin1String(
"IndiServerMessage"), message, KSNotification::INDI);
660 client->deleteLater();
662 if (connectionMode == SERVER_CLIENT)
663 activateStopService();
669void DriverManager::setClientTerminated(
const QString &message)
673 if (client ==
nullptr)
676 auto host = client->getHost();
677 auto port = client->getPort();
681 emit clientTerminated(host, port, message);
683 GUIManager::Instance()->removeClient(client);
684 INDIListener::Instance()->removeClient(client);
686 KSNotification::event(
QLatin1String(
"IndiServerMessage"), message, KSNotification::INDI, KSNotification::Alert);
689 client->deleteLater();
696void DriverManager::setServerStarted()
699 qCDebug(
KSTARS_INDI) <<
"INDI: INDI Server started locally on port " << manager->getPort();
700 startLocalDrivers(manager);
701 emit serverStarted(manager->getHost(), manager->getPort());
704void DriverManager::setServerFailed(
const QString &message)
708 if (server ==
nullptr)
711 for (
auto &
dv : driversList)
713 if (
dv->getServerManager() == server)
719 if (server->getMode() == SERVER_ONLY)
720 KSNotification::error(message);
722 emit serverFailed(server->getHost(), server->getPort(), message);
724 server->deleteLater();
729void DriverManager::setServerTerminated(
const QString &message)
733 if (server ==
nullptr)
736 for (
auto &
dv : driversList)
738 if (
dv->getServerManager() == server)
744 if (server->getMode() == SERVER_ONLY)
745 KSNotification::error(message);
747 emit serverTerminated(server->getHost(), server->getPort(), message);
749 server->deleteLater();
754void DriverManager::processRemoteTree(
bool dState)
760 for (
auto &
dv : driversList)
762 if (
dv->getDriverSource() != HOST_SOURCE)
768 if (currentItem->
text(HOST_NAME_COLUMN) ==
dv->getName() &&
769 currentItem->
text(HOST_PORT_COLUMN).
toInt() ==
dv->getPort())
777 connectRemoteHost(
dv);
780 disconnectRemoteHost(
dv);
787 auto host = driver->getHost();
788 auto port = driver->getPort();
792 clientManager->appendManagedDriver(driver);
800 GUIManager::Instance()->addClient(clientManager);
801 INDIListener::Instance()->addClient(clientManager);
803 clientManager->establishConnection();
806void DriverManager::setClientStarted()
809 if (clientManager ==
nullptr)
812 clients.
append(clientManager);
817 i18n(
"Connected to INDI server"));
819 emit clientStarted(clientManager->getHost(), clientManager->getPort());
830 GUIManager::Instance()->removeClient(clientManager);
831 INDIListener::Instance()->removeClient(clientManager);
841void DriverManager::resizeDeviceColumn()
843 ui->localTreeWidget->resizeColumnToContents(0);
846void DriverManager::updateMenuActions()
854 if (clients.
size() > 0)
868 qWarning() <<
"INDI server is currently not supported on Windows.";
871 int lastPort = Options::serverPortEnd();
872 bool success =
false;
877 currentPort = Options::serverPortStart();
893 for (; currentPort <=
lastPort; currentPort++)
906bool DriverManager::readINDIHosts()
965 dv->setDriverSource(HOST_SOURCE);
967 connect(
dv.get(), &DriverInfo::deviceStateChanged,
this, [
dv,
this]()
969 processDeviceStatus(dv);
976 item->
setIcon(HOST_STATUS_COLUMN, ui->disconnected);
995bool DriverManager::readXMLDrivers()
1002 if (Options::indiDriversAreInternal())
1009 KSNotification::error(
i18n(
"Unable to find INDI drivers directory: %1\nPlease "
1010 "make sure to set the correct "
1011 "path in KStars configuration",
1021 for (
auto &fileInfo :
list)
1026 processXMLDriver(fileInfo.absoluteFilePath());
1035void DriverManager::processXMLDriver(
const QString &driverName)
1037 QFile file(driverName);
1040 KSNotification::error(
i18n(
"Failed to open INDI Driver file: %1", driverName));
1044 char errmsg[ERRMSG_SIZE];
1051 driverSource = PRIMARY_XML;
1053 driverSource = THIRD_PARTY_XML;
1066 if (!buildDeviceGroup(ep,
errmsg))
1073 if (!buildDeviceGroup(root,
errmsg))
1090bool DriverManager::buildDeviceGroup(
XMLEle *root,
char errmsg[])
1096 DeviceFamily groupType = KSTARS_TELESCOPE;
1107 snprintf(
errmsg, ERRMSG_SIZE,
"Tag %.64s does not have a group attribute",
1113 groupType = DeviceFamilyLabels.key(groupName);
1117 if (groupType == KSTARS_CCD || groupType ==
KSTARS_VIDEO)
1134 if (!buildDriverElement(ep, group, groupType,
errmsg))
1142 DeviceFamily groupType,
char errmsg[])
1150 QString manufacturer(
"Others");
1160 snprintf(
errmsg, ERRMSG_SIZE,
"Tag %.64s does not have a label attribute",
1168 if (findDriverByLabel(label) !=
nullptr)
1205 snprintf(
errmsg, ERRMSG_SIZE,
"Tag %.64s does not have a name attribute",
1230 device->
setText(LOCAL_NAME_COLUMN, label);
1232 device->
setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1235 device->
setText(LOCAL_VERSION_COLUMN, version);
1236 device->
setText(LOCAL_PORT_COLUMN, port);
1239 if (groupType == KSTARS_TELESCOPE && driversStringList.
contains(driver) ==
false)
1240 driversStringList.
append(driver);
1244 dv->setLabel(label);
1245 dv->setVersion(version);
1246 dv->setExecutable(driver);
1247 dv->setManufacturer(manufacturer);
1248 dv->setSkeletonFile(
skel);
1249 dv->setType(groupType);
1250 dv->setDriverSource(driverSource);
1251 dv->setUserPort(port.
isEmpty() ? 7624 : port.toInt());
1254 connect(
dv.get(), &DriverInfo::deviceStateChanged,
this, [
dv,
this]()
1256 processDeviceStatus(dv);
1264bool DriverManager::checkDriverAvailability(
const QString &driver)
1267 if (Options::indiServerIsInternal())
1280void DriverManager::updateCustomDrivers()
1282 for (
const QVariantMap &
oneDriver : m_CustomDrivers->customDrivers())
1286 dv->setUniqueLabel(
dv->getLabel());
1290 dv->setType(DeviceFamilyLabels.key(
oneDriver[
"Family"].toString()));
1291 dv->setDriverSource(CUSTOM_SOURCE);
1304void DriverManager::updateCustomDrivers()
1322 KStarsData::Instance()->logObject()->readAll();
1325 foreach (
OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
1329 if (s->driver() ==
i18n(
"None"))
1333 if ((
drv = findDriverByLabel(label)))
1335 if (s->aperture() > 0 && s->focalLength() > 0)
1337 vMap.insert(
"TELESCOPE_APERTURE", s->aperture());
1338 vMap.insert(
"TELESCOPE_FOCAL_LENGTH", s->focalLength());
1342 drv->setExecutable(s->driver());
1347 driver = s->driver();
1352 device->
setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1357 dv->setLabel(label);
1358 dv->setExecutable(driver);
1359 dv->setVersion(version);
1360 dv->setType(KSTARS_TELESCOPE);
1361 dv->setDriverSource(EM_XML);
1363 if (s->aperture() > 0 && s->focalLength() > 0)
1365 vMap.insert(
"TELESCOPE_APERTURE", s->aperture());
1366 vMap.insert(
"TELESCOPE_FOCAL_LENGTH", s->focalLength());
1378 if (
dev->getDriverSource() != EM_XML ||
dev->getClientState())
1381 if (KStarsData::Instance()->logObject()->findScopeByName(
dev->getName()))
1401void DriverManager::addINDIHost()
1417 KSNotification::error(
i18n(
"Error: the port number is invalid."));
1427 if (
hostItem->getName() == host->getName() &&
1428 hostItem->getPort() == host->getPort())
1430 KSNotification::error(
i18n(
"Host: %1 Port: %2 already exists.",
1435 hostItem->setDriverSource(HOST_SOURCE);
1439 processDeviceStatus(hostItem);
1445 item->
setIcon(HOST_STATUS_COLUMN, ui->disconnected);
1453void DriverManager::modifyINDIHost()
1462 if (currentItem ==
nullptr)
1465 for (
auto &host : driversList)
1467 if (currentItem->
text(HOST_NAME_COLUMN) == host->getName() &&
1468 currentItem->
text(HOST_PORT_COLUMN).
toInt() == host->getPort())
1470 hostConf.nameIN->setText(host->getName());
1471 hostConf.hostname->setText(host->getHost());
1476 host->setName(
hostConf.nameIN->text());
1477 host->setHostParameters(
hostConf.hostname->text(),
1490void DriverManager::removeINDIHost()
1492 if (ui->clientTreeWidget->currentItem() ==
nullptr)
1496 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) ==
1498 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN).toInt() ==
1501 if (host->getClientState())
1503 KSNotification::error(
1504 i18n(
"You need to disconnect the client before removing it."));
1510 i18n(
"Are you sure you want to remove the %1 client?",
1511 ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)),
1512 i18n(
"Delete Confirmation"),
1517 delete (ui->clientTreeWidget->currentItem());
1524void DriverManager::saveHosts()
1531 .filePath(
"indihosts.xml"));
1535 KSNotification::sorry(
i18n(
"Unable to write to file 'indihosts.xml'\nAny changes "
1536 "to INDI hosts configurations will not be saved."),
1537 i18n(
"Could Not Open File"));
1546 if (host->getDriverSource() != HOST_SOURCE)
1565 auto driver = std::find_if(driversList.
begin(), driversList.
end(), [name](
auto &
oneDriver)
1567 return oneDriver->getName() == name;
1570 if (driver != driversList.
end())
1578 auto driver = std::find_if(driversList.
begin(), driversList.
end(), [label](
auto &
oneDriver)
1580 return oneDriver->getLabel() == label;
1583 if (driver != driversList.
end())
1593 return oneDriver->getLabel() == exec;
1596 if (driver != driversList.
end())
1607 for (
auto &
cm : clients)
1609 auto devices =
cm->getDevices();
1611 for (
auto &
dv : devices)
1613 if (label ==
QString(
dv.getDeviceName()))
1623QJsonArray DriverManager::getDriverList()
const
1627 for (
const auto &
drv : driversList)
1639 return oneServer->restartDriver(driver);
ClientManager manages connection to INDI server, creation of devices, and receiving/sending propertie...
void disconnectAll()
disconnectAll Disconnect from server and disconnect all BLOB Managers.
void removeManagedDriver(const QSharedPointer< DriverInfo > &driver)
removeManagedDriver Remove managed driver from pool of drivers managed by this client manager.
Handles adding new drivers to database.
DriverInfo holds all metadata associated with a particular INDI driver.
DriverManager is the primary class to handle all operations related to starting and stopping INDI dri...
void getUniqueHosts(const QList< QSharedPointer< DriverInfo > > &dList, QList< QList< QSharedPointer< DriverInfo > > > &uHosts)
getUniqueHosts Given a list of DriverInfos, extract all the host:port information from all the driver...
Collection of INDI DBus functions.
static void beep(const QString &reason=QString())
static KStars * Instance()
Information on telescope used in observation.
ServerManager is responsible for starting and shutting local INDI servers.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KDB_EXPORT KDbVersionInfo version()
KIOCORE_EXPORT QString number(KIO::filesize_t size)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
QString name(StandardShortcut id)
NETWORKMANAGERQT_EXPORT QString hostname()
const char * constData() const const
QString applicationDirPath()
QString path() const const
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
bool isEmpty() const const
bool removeOne(const AT &t)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * sender() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void expanded(const QModelIndex &index)
void setText(int column, const QString &text)
QString text(int column) const const