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();
155 int nset = std::count_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [label](
auto & oneDriver)
157 return label == oneDriver->getUniqueLabel();
161 uniqueLabel = QString(
"%1 %2").
arg(label).
arg(nset + 1);
162 driver->setUniqueLabel(uniqueLabel);
166 m_ManagedDrivers.append(driver);
167 driver->setServerManager(
this);
169 QString driversDir = Options::indiDriversDir();
170 QString indiServerDir = QFileInfo(Options::indiServer()).dir().path();
173 if (Options::indiServerIsInternal())
175 if (Options::indiDriversAreInternal())
179 QJsonObject startupRule = driver->startupRule();
180 auto PreDelay = startupRule[
"PreDelay"].toInt(0);
185 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing pre-driver delay for" << PreDelay <<
"second(s)";
186 std::this_thread::sleep_for(std::chrono::seconds(PreDelay));
190 auto PreScript = startupRule[
"PreScript"].toString();
191 if (!PreScript.isEmpty())
198 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing pre-driver script" << PreScript;
199 script.
start(PreScript, QStringList());
204 emit driverFailed(driver,
i18n(
"Pre driver startup script failed with exit code: %1", script.
exitCode()));
210 if (driver->getRemoteHost().isEmpty() ==
false)
212 QString driverString = driver->getName() +
"@" + driver->getRemoteHost() +
":" + driver->getRemotePort();
213 qCDebug(KSTARS_INDI) <<
"Starting Remote INDI Driver" << driverString;
214 out <<
"start " << driverString <<
Qt::endl;
222 <<
"/usr/local/bin" << driversDir << indiServerDir;
228 emit driverFailed(driver,
i18n(
"Driver %1 was not found on the system. Please make sure the package that "
229 "provides the '%1' binary is installed.",
230 driver->getExecutable()));
235 qCDebug(KSTARS_INDI) <<
"Starting INDI Driver" << driver->getExecutable();
237 out <<
"start " << driver->getExecutable();
238 if (driver->getUniqueLabel().isEmpty() ==
false)
239 out <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
240 if (driver->getSkeletonFile().isEmpty() ==
false)
241 out <<
" -s \"" << driversDir <<
QDir::separator() << driver->getSkeletonFile() <<
"\"";
245 driver->setServerState(
true);
247 driver->setPort(port);
250 auto PostDelay = startupRule[
"PostDelay"].toInt(0);
255 emit scriptDriverStarted(driver);
256 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing post-driver delay for" << PreDelay <<
"second(s)";
257 std::this_thread::sleep_for(std::chrono::seconds(PostDelay));
261 auto PostScript = startupRule[
"PostScript"].toString();
262 if (!PostScript.isEmpty())
269 qCDebug(KSTARS_INDI) << driver->getUniqueLabel() <<
": Executing post-driver script" << PreScript;
270 script.
start(PostScript, QStringList());
275 emit driverFailed(driver,
i18n(
"Post driver startup script failed with exit code: %1", script.
exitCode()));
281 m_PendingDrivers.
erase(std::remove_if(m_PendingDrivers.begin(), m_PendingDrivers.end(), [driver](
const auto & oneDriver)
283 return driver == oneDriver;
284 }), m_PendingDrivers.end());
285 emit driverStarted(driver);
290 QTextStream out(&indiFIFO);
291 const auto exec = driver->getExecutable();
293 qCDebug(KSTARS_INDI) <<
"Stopping INDI Driver " << exec;
295 if (driver->getUniqueLabel().isEmpty() ==
false)
296 out <<
"stop " << exec <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
298 out <<
"stop " << exec;
301 driver->setServerState(
false);
302 driver->setPort(driver->getUserPort());
304 m_ManagedDrivers.erase(std::remove_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [exec](
const auto & driver)
306 return driver->getExecutable() == exec;
308 emit driverStopped(driver);
314 auto cm = driver->getClientManager();
315 const auto label = driver->getLabel();
319 qCDebug(KSTARS_INDI) <<
"Restarting INDI Driver: " <<
label;
322 cm->removeManagedDriver(driver);
329 qCDebug(KSTARS_INDI) <<
"restartDriver with no cm, and " << m_ManagedDrivers.size() <<
" drivers. Trying to remove: " <<
331 cm = DriverManager::Instance()->getClientManager(driver);
332 const auto exec = driver->getExecutable();
333 m_ManagedDrivers.erase(std::remove_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [exec](
const auto & driver)
335 return driver->getExecutable() == exec;
342 auto driver = DriverManager::Instance()->findDriverByLabel(label);
345 qCDebug(KSTARS_INDI) <<
"restartDriver timer, did not find driver with label: " <<
label;
348 cm->appendManagedDriver(driver);
349 if (m_ManagedDrivers.contains(driver) ==
false)
350 m_ManagedDrivers.append(driver);
351 driver->setServerManager(
this);
353 QTextStream out(&indiFIFO);
355 QString driversDir = Options::indiDriversDir();
356 QString indiServerDir = Options::indiServer();
359 if (Options::indiServerIsInternal())
361 if (Options::indiDriversAreInternal())
364 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
367 if (driver->getRemoteHost().isEmpty() ==
false)
369 QString driverString = driver->getName() +
"@" + driver->getRemoteHost() +
":" + driver->getRemotePort();
370 qCDebug(KSTARS_INDI) <<
"Restarting Remote INDI Driver" << driverString;
371 out <<
"start " << driverString;
372#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
383 <<
"/usr/local/bin" << driversDir << indiServerDir;
385 qCDebug(KSTARS_INDI) <<
"Starting INDI Driver " << driver->getExecutable();
387 out <<
"start " << driver->getExecutable();
388 if (driver->getUniqueLabel().isEmpty() ==
false)
389 out <<
" -n \"" << driver->getUniqueLabel() <<
"\"";
390 if (driver->getSkeletonFile().isEmpty() ==
false)
391 out <<
" -s \"" << driversDir <<
QDir::separator() << driver->getSkeletonFile() <<
"\"";
395 driver->setServerState(
true);
396 driver->setPort(port);
400 emit driverRestarted(driver);
404void ServerManager::stop()
406 if (serverProcess.get() ==
nullptr)
409 for (
auto &device : m_ManagedDrivers)
414 qCDebug(KSTARS_INDI) <<
"Stopping INDI Server " << host <<
"@" << port;
416 serverProcess->disconnect(
this);
418 serverBuffer.close();
420 serverProcess->terminate();
422 serverProcess->waitForFinished();
424 serverProcess.reset();
435 emit terminated(
i18n(
"Connection to INDI server %1:%2 terminated: %3.",
436 getHost(), getPort(), serverProcess.get()->errorString()));
439void ServerManager::processStandardError()
442 qWarning() <<
"INDI server is currently not supported on Windows.";
445 QString stderr = serverProcess->readAllStandardError();
447 for (
auto &msg : stderr.
split(
'\n'))
448 qCDebug(KSTARS_INDI) <<
"INDI Server: " << msg;
450 serverBuffer.write(stderr.
toLatin1());
454 QRegularExpression re(
"Driver (.*): Terminated after #0 restarts");
455 QRegularExpressionMatch
match = re.match(stderr);
456 if (
match.hasMatch())
458 QString driverExec =
match.captured(1);
459 qCCritical(KSTARS_INDI) <<
"INDI driver " << driverExec <<
" crashed!";
463 auto crashedDriver = std::find_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(),
464 [driverExec](QSharedPointer<DriverInfo> dv)
466 return dv->getExecutable() == driverExec;
469 if (crashedDriver != m_ManagedDrivers.end())
475 restartDriver(*crashedDriver);
478 QString
label = (*crashedDriver)->getUniqueLabel();
480 label = (*crashedDriver)->getExecutable();
481 KSMessageBox::Instance()->warningContinueCancel(
i18n(
"INDI Driver <b>%1</b> crashed. Restart it?",
482 label),
i18n(
"Driver crash"), 10);
488QString ServerManager::errorString()
490 if (serverProcess.get() !=
nullptr)
491 return serverProcess->errorString();
496QString ServerManager::getLogBuffer()
498 serverBuffer.flush();
499 serverBuffer.close();
502 return serverBuffer.readAll();
508 auto host = driver->getRemoteHost().isEmpty() ? driver->getHost() : driver->getRemoteHost();
509 auto port = driver->getRemoteHost().isEmpty() ? driver->getPort() : driver->getRemotePort().toInt();
510 auto manager =
new ClientManager();
511 manager->setServer(host.toLatin1().constData(), port);
512 connect(manager, &ClientManager::newINDIProperty, [manager](INDI::Property
property)
514 if (QString(
property.getName()) ==
"CONNECTION")
515 manager->connectDevice(
property.getDeviceName());
517 manager->establishConnection();
521 manager->disconnect();
522 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
iterator erase(iterator it)
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)