7#include "servermanager.h"
10#include "clientmanager.h"
11#include "drivermanager.h"
12#include "auxiliary/kspaths.h"
13#include "auxiliary/ksmessagebox.h"
15#include "ksnotification.h"
17#include <indidevapi.h>
25#include <indi_debug.h>
30ServerManager::ServerManager(
const QString &inHost,
int inPort) : host(inHost), port(inPort)
35ServerManager::~ServerManager()
42 if (serverProcess.get() !=
nullptr)
43 serverProcess->close();
46bool ServerManager::start()
49 qWarning() <<
"INDI server is currently not supported on Windows.";
52 bool connected =
false;
54 if (serverProcess.get() ==
nullptr)
58 serverProcess.reset(
new QProcess(
this));
60 QString driversDir = Options::indiDriversDir();
61 if (Options::indiDriversAreInternal())
63 QString indiServerDir;
64 if (Options::indiServerIsInternal())
67 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
69 env.
insert(
"PATH", driversDir +
':' + indiServerDir +
":/usr/local/bin:/usr/bin:/bin");
71 env.
insert(
"GSCDAT", gscDirPath);
73 insertEnvironmentPath(&env,
"INDIPREFIX",
"/../../");
74 insertEnvironmentPath(&env,
"IOLIBS",
"/../Resources/DriverSupport/gphoto/IOLIBS");
75 insertEnvironmentPath(&env,
"CAMLIBS",
"/../Resources/DriverSupport/gphoto/CAMLIBS");
77 serverProcess->setProcessEnvironment(env);
87 if (mkfifo(fifoFile.
toLatin1(), S_IRUSR | S_IWUSR) < 0)
89 emit failed(
i18n(
"Error making FIFO file %1: %2.", fifoFile, strerror(errno)));
93 indiFIFO.setFileName(fifoFile);
97 qCCritical(KSTARS_INDI) <<
"Unable to create INDI FIFO file: " << fifoFile;
98 emit failed(
i18n(
"Unable to create INDI FIFO file %1", fifoFile));
102 args <<
"-m" <<
QString::number(Options::serverTransferBufferSize()) <<
"-r" <<
"0" <<
"-f" << fifoFile;
104 qCDebug(KSTARS_INDI) <<
"Starting INDI Server: " << args <<
"-f" << fifoFile;
110 if (Options::indiServerIsInternal())
114 serverProcess->start(Options::indiServer(), args);
116 connected = serverProcess->waitForStarted();
126 emit failed(
i18n(
"INDI server failed to start: %1", serverProcess->errorString()));
129 qCDebug(KSTARS_INDI) <<
"INDI Server Started? " << connected;
139 env->
insert(variable, QDir(environmentPath).absolutePath());
144 QTextStream out(&indiFIFO);
147 if (driver->getUniqueLabel().isEmpty() && driver->getLabel().isEmpty() ==
false)
148 driver->setUniqueLabel(DriverManager::Instance()->getUniqueDeviceLabel(driver->getLabel()));
151 if (driver->getUniqueLabel().isEmpty() ==
false)
154 QString
label = driver->getUniqueLabel();
157 QMutexLocker locker(&m_DriverMutex);
158 nset = std::count_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [label](
auto & oneDriver)
160 return label == oneDriver->getUniqueLabel();
165 uniqueLabel = QString(
"%1 %2").
arg(label).
arg(nset + 1);
166 driver->setUniqueLabel(uniqueLabel);
171 QMutexLocker locker(&m_DriverMutex);
172 m_ManagedDrivers.append(driver);
173 driver->setServerManager(
this);
176 QString driversDir = Options::indiDriversDir();
177 QString indiServerDir = QFileInfo(Options::indiServer()).dir().path();
180 if (Options::indiServerIsInternal())
182 if (Options::indiDriversAreInternal())
186 QJsonObject startupRule = driver->startupRule();
187 auto PreDelay = startupRule[
"PreDelay"].toInt(0);
192 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing pre-driver delay for" << PreDelay <<
"second(s)";
193 std::this_thread::sleep_for(std::chrono::seconds(PreDelay));
197 auto PreScript = startupRule[
"PreScript"].toString();
198 if (!PreScript.isEmpty())
205 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing pre-driver script" << PreScript;
206 script.
start(PreScript, QStringList());
211 emit driverFailed(driver,
i18n(
"Pre driver startup script failed with exit code: %1", script.
exitCode()));
217 if (driver->getRemoteHost().isEmpty() ==
false)
219 QString driverString = driver->getName() +
"@" + driver->getRemoteHost() +
":" + driver->getRemotePort();
220 qCDebug(KSTARS_INDI) <<
"Starting Remote INDI Driver" << driverString;
221 out <<
"start " << driverString <<
Qt::endl;
229 <<
"/usr/local/bin" << driversDir << indiServerDir;
235 emit driverFailed(driver,
i18n(
"Driver %1 was not found on the system. Please make sure the package that "
236 "provides the '%1' binary is installed.",
237 driver->getExecutable()));
242 qCDebug(KSTARS_INDI) <<
"Starting INDI Driver" << driver->getExecutable();
244 out <<
"start " << driver->getExecutable();
245 if (driver->getUniqueLabel().isEmpty() ==
false)
246 out <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
247 if (driver->getSkeletonFile().isEmpty() ==
false)
248 out <<
" -s \"" << driversDir <<
QDir::separator() << driver->getSkeletonFile() <<
"\"";
252 driver->setServerState(
true);
254 driver->setPort(port);
257 auto PostDelay = startupRule[
"PostDelay"].toInt(0);
262 emit scriptDriverStarted(driver);
263 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing post-driver delay for" << PreDelay <<
"second(s)";
264 std::this_thread::sleep_for(std::chrono::seconds(PostDelay));
268 auto PostScript = startupRule[
"PostScript"].toString();
269 if (!PostScript.isEmpty())
276 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing post-driver script" << PreScript;
277 script.
start(PostScript, QStringList());
282 emit driverFailed(driver,
i18n(
"Post driver startup script failed with exit code: %1", script.
exitCode()));
289 QMutexLocker locker(&m_PendingMutex);
290 m_PendingDrivers.erase(std::remove_if(m_PendingDrivers.begin(), m_PendingDrivers.end(), [driver](
const auto & oneDriver)
292 return driver == oneDriver;
293 }), m_PendingDrivers.end());
295 emit driverStarted(driver);
300 QTextStream out(&indiFIFO);
301 const auto exec = driver->getExecutable();
303 qCDebug(KSTARS_INDI) <<
"Stopping INDI Driver " << exec;
305 if (driver->getUniqueLabel().isEmpty() ==
false)
306 out <<
"stop " << exec <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
308 out <<
"stop " << exec;
311 driver->setServerState(
false);
312 driver->setPort(driver->getUserPort());
315 QMutexLocker locker(&m_DriverMutex);
316 m_ManagedDrivers.erase(std::remove_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [exec](
const auto & driver)
318 return driver->getExecutable() == exec;
321 emit driverStopped(driver);
327 auto cm = driver->getClientManager();
328 const auto label = driver->getLabel();
332 qCDebug(KSTARS_INDI) <<
"Restarting INDI Driver: " <<
label;
335 cm->removeManagedDriver(driver);
344 QMutexLocker locker(&m_DriverMutex);
345 size = m_ManagedDrivers.size();
347 qCDebug(KSTARS_INDI) <<
"restartDriver with no cm, and " << size <<
" drivers. Trying to remove: " <<
label;
348 cm = DriverManager::Instance()->getClientManager(driver);
349 const auto exec = driver->getExecutable();
351 QMutexLocker locker(&m_DriverMutex);
352 m_ManagedDrivers.erase(std::remove_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [exec](
const auto & driver)
354 return driver->getExecutable() == exec;
362 auto driver = DriverManager::Instance()->findDriverByLabel(label);
365 qCDebug(KSTARS_INDI) <<
"restartDriver timer, did not find driver with label: " <<
label;
368 cm->appendManagedDriver(driver);
370 QMutexLocker locker(&m_DriverMutex);
371 if (m_ManagedDrivers.contains(driver) ==
false)
372 m_ManagedDrivers.append(driver);
374 driver->setServerManager(
this);
376 QTextStream out(&indiFIFO);
378 QString driversDir = Options::indiDriversDir();
379 QString indiServerDir = Options::indiServer();
382 if (Options::indiServerIsInternal())
384 if (Options::indiDriversAreInternal())
387 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
390 if (driver->getRemoteHost().isEmpty() ==
false)
392 QString driverString = driver->getName() +
"@" + driver->getRemoteHost() +
":" + driver->getRemotePort();
393 qCDebug(KSTARS_INDI) <<
"Restarting Remote INDI Driver" << driverString;
394 out <<
"start " << driverString;
395#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
406 <<
"/usr/local/bin" << driversDir << indiServerDir;
408 qCDebug(KSTARS_INDI) <<
"Starting INDI Driver " << driver->getExecutable();
410 out <<
"start " << driver->getExecutable();
411 if (driver->getUniqueLabel().isEmpty() ==
false)
412 out <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
413 if (driver->getSkeletonFile().isEmpty() ==
false)
414 out <<
" -s \"" << driversDir <<
QDir::separator() << driver->getSkeletonFile() <<
"\"";
418 driver->setServerState(
true);
419 driver->setPort(port);
423 emit driverRestarted(driver);
427void ServerManager::stop()
429 if (serverProcess.get() ==
nullptr)
433 QMutexLocker locker(&m_DriverMutex);
434 for (
auto &device : m_ManagedDrivers)
440 qCDebug(KSTARS_INDI) <<
"Stopping INDI Server " << host <<
"@" << port;
442 serverProcess->disconnect(
this);
444 serverBuffer.close();
446 serverProcess->terminate();
448 serverProcess->waitForFinished();
450 serverProcess.reset();
461 emit terminated(
i18n(
"Connection to INDI server %1:%2 terminated: %3.",
462 getHost(), getPort(), serverProcess.get()->errorString()));
465void ServerManager::processStandardError()
468 qWarning() <<
"INDI server is currently not supported on Windows.";
471 QString stderr = serverProcess->readAllStandardError();
473 for (
auto &msg : stderr.
split(
'\n'))
474 qCDebug(KSTARS_INDI) <<
"INDI Server: " << msg;
476 serverBuffer.write(stderr.
toLatin1());
480 QRegularExpression re(
"Driver (.*): Terminated after #0 restarts");
481 QRegularExpressionMatch
match = re.match(stderr);
482 if (
match.hasMatch())
484 QString driverExec =
match.captured(1);
485 qCCritical(KSTARS_INDI) <<
"INDI driver " << driverExec <<
" crashed!";
489 QSharedPointer<DriverInfo> crashedDriverInfo;
491 QMutexLocker locker(&m_DriverMutex);
492 auto crashedDriver = std::find_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(),
493 [driverExec](QSharedPointer<DriverInfo> dv)
495 return dv->getExecutable() == driverExec;
498 if (crashedDriver != m_ManagedDrivers.end())
499 crashedDriverInfo = *crashedDriver;
502 if (crashedDriverInfo)
508 restartDriver(crashedDriverInfo);
511 QString
label = crashedDriverInfo->getUniqueLabel();
513 label = crashedDriverInfo->getExecutable();
514 KSMessageBox::Instance()->warningContinueCancel(
i18n(
"INDI Driver <b>%1</b> crashed. Restart it?",
515 label),
i18n(
"Driver crash"), 10);
521QString ServerManager::errorString()
523 if (serverProcess.get() !=
nullptr)
524 return serverProcess->errorString();
529QString ServerManager::getLogBuffer()
531 serverBuffer.flush();
532 serverBuffer.close();
535 return serverBuffer.readAll();
541 auto host = driver->getRemoteHost().isEmpty() ? driver->getHost() : driver->getRemoteHost();
542 auto port = driver->getRemoteHost().isEmpty() ? driver->getPort() : driver->getRemotePort().toInt();
543 auto manager =
new ClientManager();
544 manager->setServer(host.toLatin1().constData(), port);
545 connect(manager, &ClientManager::newINDIProperty, [manager](INDI::Property
property)
547 if (QString(
property.getName()) ==
"CONNECTION")
548 manager->connectDevice(
property.getDeviceName());
550 manager->establishConnection();
554 manager->disconnect();
555 manager->deleteLater();
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QString label(StandardShortcut id)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QString applicationDirPath()
int exec(ProcessEventsFlags flags)
bool exists() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QVariant property(const char *name) const const
void errorOccurred(QProcess::ProcessError error)
int exitCode() const const
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardError()
void start(OpenMode mode)
void insert(const QProcessEnvironment &e)
QProcessEnvironment systemEnvironment()
QString findExecutable(const QString &executableName, const QStringList &paths)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QTextStream & endl(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)