File indexing completed on 2024-04-14 03:42:34

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "clientmanager.h"
0008 
0009 #include "deviceinfo.h"
0010 #include "drivermanager.h"
0011 #include "guimanager.h"
0012 #include "indilistener.h"
0013 #include "Options.h"
0014 #include "servermanager.h"
0015 
0016 #include <indi_debug.h>
0017 #include <QTimer>
0018 
0019 ClientManager::ClientManager()
0020 {
0021     connect(this, &ClientManager::newINDIProperty, this, &ClientManager::processNewProperty, Qt::UniqueConnection);
0022     connect(this, &ClientManager::removeBLOBManager, this, &ClientManager::processRemoveBLOBManager, Qt::UniqueConnection);
0023 }
0024 
0025 bool ClientManager::isDriverManaged(const QSharedPointer<DriverInfo> &driver)
0026 {
0027     return std::any_of(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [driver](const auto & oneDriver)
0028     {
0029         return driver == oneDriver;
0030     });
0031 }
0032 
0033 void ClientManager::newDevice(INDI::BaseDevice dp)
0034 {
0035     //setBLOBMode(B_ALSO, dp->getDeviceName());
0036     // JM 2018.09.27: ClientManager will no longer handle BLOB, just messages.
0037     // We relay the BLOB handling to BLOB Manager to better manage concurrent connections with large data
0038     setBLOBMode(B_NEVER, dp.getDeviceName());
0039 
0040     if (QString(dp.getDeviceName()).isEmpty())
0041     {
0042         qCWarning(KSTARS_INDI) << "Received invalid device with empty name! Ignoring the device...";
0043         return;
0044     }
0045 
0046     qCDebug(KSTARS_INDI) << "Received new device" << dp.getDeviceName();
0047 
0048     // First iteration find unique matches
0049     for (auto &oneDriverInfo : m_ManagedDrivers)
0050     {
0051         if (oneDriverInfo->getUniqueLabel() == QString(dp.getDeviceName()))
0052         {
0053             oneDriverInfo->setUniqueLabel(dp.getDeviceName());
0054             DeviceInfo *devInfo = new DeviceInfo(oneDriverInfo, dp);
0055             oneDriverInfo->addDevice(devInfo);
0056             emit newINDIDevice(devInfo);
0057             return;
0058         }
0059     }
0060 
0061     // Second iteration find partial matches
0062 
0063     for (auto &oneDriverInfo : m_ManagedDrivers)
0064     {
0065         auto dvName = oneDriverInfo->getName().split(' ').first();
0066         if (dvName.isEmpty())
0067             dvName = oneDriverInfo->getName();
0068         if (/*dv->getUniqueLabel() == dp->getDeviceName() ||*/
0069             QString(dp.getDeviceName()).startsWith(dvName, Qt::CaseInsensitive) ||
0070             ((oneDriverInfo->getDriverSource() == HOST_SOURCE || oneDriverInfo->getDriverSource() == GENERATED_SOURCE)))
0071         {
0072             oneDriverInfo->setUniqueLabel(dp.getDeviceName());
0073             DeviceInfo *devInfo = new DeviceInfo(oneDriverInfo, dp);
0074             oneDriverInfo->addDevice(devInfo);
0075             emit newINDIDevice(devInfo);
0076             return;
0077         }
0078     }
0079 }
0080 
0081 void ClientManager::newProperty(INDI::Property property)
0082 {
0083     // Do not emit the signal if the server is disconnected or disconnecting (deadlock between signals)
0084     if (!isServerConnected())
0085     {
0086         IDLog("Received new property %s for disconnected device %s, discarding\n", property.getName(), property.getDeviceName());
0087         return;
0088     }
0089 
0090     //IDLog("Received new property %s for device %s\n", prop->getName(), prop->getgetDeviceName());
0091     emit newINDIProperty(property);
0092 }
0093 
0094 void ClientManager::updateProperty(INDI::Property property)
0095 {
0096     emit updateINDIProperty(property);
0097 }
0098 
0099 void ClientManager::removeProperty(INDI::Property prop)
0100 {
0101     const QString name = prop.getName();
0102     const QString device = prop.getDeviceName();
0103     emit removeINDIProperty(prop);
0104 
0105     // If BLOB property is removed, remove its corresponding property if one exists.
0106     if (blobManagers.empty() == false && prop.getType() == INDI_BLOB && prop.getPermission() != IP_WO)
0107         emit removeBLOBManager(device, name);
0108 }
0109 
0110 void ClientManager::processRemoveBLOBManager(const QString &device, const QString &property)
0111 {
0112     auto manager = std::find_if(blobManagers.begin(), blobManagers.end(), [device, property](auto & oneManager)
0113     {
0114         const auto bProperty = oneManager->property("property").toString();
0115         const auto bDevice = oneManager->property("device").toString();
0116         return (device == bDevice && property == bProperty);
0117     });
0118 
0119     if (manager != blobManagers.end())
0120     {
0121         (*manager)->disconnectServer();
0122         (*manager)->deleteLater();
0123         blobManagers.removeOne(*manager);
0124     }
0125 }
0126 
0127 void ClientManager::processNewProperty(INDI::Property prop)
0128 {
0129     // Only handle RW and RO BLOB properties
0130     if (prop.getType() == INDI_BLOB && prop.getPermission() != IP_WO)
0131     {
0132         BlobManager *bm = new BlobManager(this, getHost(), getPort(), prop.getDeviceName(), prop.getName());
0133         connect(bm, &BlobManager::propertyUpdated, this, &ClientManager::updateINDIProperty);
0134         connect(bm, &BlobManager::connected, this, [prop, this]()
0135         {
0136             if (prop && prop.getRegistered())
0137                 emit newBLOBManager(prop.getDeviceName(), prop);
0138         });
0139         blobManagers.append(bm);
0140     }
0141 }
0142 
0143 void ClientManager::disconnectAll()
0144 {
0145     disconnectServer();
0146     for (auto &oneManager : blobManagers)
0147         oneManager->disconnectServer();
0148 }
0149 
0150 void ClientManager::removeDevice(INDI::BaseDevice dp)
0151 {
0152     QString deviceName = dp.getDeviceName();
0153 
0154     QMutableListIterator<BlobManager*> it(blobManagers);
0155     while (it.hasNext())
0156     {
0157         auto &oneManager = it.next();
0158         if (oneManager->property("device").toString() == deviceName)
0159         {
0160             oneManager->disconnect();
0161             it.remove();
0162         }
0163     }
0164 
0165     for (auto &driverInfo : m_ManagedDrivers)
0166     {
0167         for (auto &deviceInfo : driverInfo->getDevices())
0168         {
0169             if (deviceInfo->getDeviceName() == deviceName)
0170             {
0171                 qCDebug(KSTARS_INDI) << "Removing device" << deviceName;
0172 
0173                 emit removeINDIDevice(deviceName);
0174 
0175                 driverInfo->removeDevice(deviceInfo);
0176 
0177                 if (driverInfo->isEmpty())
0178                 {
0179                     driverInfo->setClientState(false);
0180                     m_ManagedDrivers.removeOne(driverInfo);
0181                 }
0182 
0183                 return;
0184             }
0185         }
0186     }
0187 }
0188 
0189 void ClientManager::newMessage(INDI::BaseDevice dp, int messageID)
0190 {
0191     emit newINDIMessage(dp, messageID);
0192 }
0193 
0194 void ClientManager::newUniversalMessage(std::string message)
0195 {
0196     emit newINDIUniversalMessage(QString::fromStdString(message));
0197 }
0198 
0199 
0200 void ClientManager::appendManagedDriver(const QSharedPointer<DriverInfo> &driver)
0201 {
0202     qCDebug(KSTARS_INDI) << "Adding managed driver" << driver->getName();
0203 
0204     m_ManagedDrivers.append(driver);
0205 
0206     driver->setClientManager(this);
0207 
0208     sManager = driver->getServerManager();
0209 }
0210 
0211 void ClientManager::removeManagedDriver(const QSharedPointer<DriverInfo> &driver)
0212 {
0213     if (m_ManagedDrivers.empty())
0214     {
0215         qCDebug(KSTARS_INDI) << "removeManagedDriver: no managed drivers!";
0216         return;
0217     }
0218 
0219     qCDebug(KSTARS_INDI) << "Removing managed driver" << driver->getName();
0220 
0221     driver->setClientState(false);
0222     m_ManagedDrivers.removeOne(driver);
0223 
0224     for (auto &di : driver->getDevices())
0225     {
0226         // #1 Remove from GUI Manager
0227         GUIManager::Instance()->removeDevice(di->getDeviceName());
0228 
0229         // #2 Remove from INDI Listener
0230         INDIListener::Instance()->removeDevice(di->getDeviceName());
0231 
0232         // #3 Remove device from Driver Info
0233         driver->removeDevice(di);
0234     }
0235 }
0236 
0237 void ClientManager::serverConnected()
0238 {
0239     qCDebug(KSTARS_INDI) << "INDI server connected.";
0240 
0241     for (auto &oneDriverInfo : m_ManagedDrivers)
0242     {
0243         oneDriverInfo->setClientState(true);
0244         if (sManager)
0245             oneDriverInfo->setHostParameters(sManager->getHost(), sManager->getPort());
0246     }
0247 
0248     m_PendingConnection = false;
0249     m_ConnectionRetries = MAX_RETRIES;
0250 
0251     emit started();
0252 }
0253 
0254 void ClientManager::serverDisconnected(int exitCode)
0255 {
0256     if (m_PendingConnection)
0257         qCDebug(KSTARS_INDI) << "INDI server connection refused.";
0258     else
0259         qCDebug(KSTARS_INDI) << "INDI server disconnected. Exit code:" << exitCode;
0260 
0261     for (auto &oneDriverInfo : m_ManagedDrivers)
0262     {
0263         oneDriverInfo->setClientState(false);
0264         oneDriverInfo->reset();
0265     }
0266 
0267     if (m_PendingConnection)
0268     {
0269         // Should we retry again?
0270         if (m_ConnectionRetries-- > 0)
0271         {
0272             // Connect again in 1 second.
0273             QTimer::singleShot(1000, this, [this]()
0274             {
0275                 qCDebug(KSTARS_INDI) << "Retrying connection again...";
0276                 if (connectServer() == false)
0277                     serverDisconnected(0);
0278                 else
0279                     m_PendingConnection = false;
0280             });
0281         }
0282         // Nope cannot connect to server.
0283         else
0284         {
0285             m_PendingConnection = false;
0286             m_ConnectionRetries = MAX_RETRIES;
0287             emit failed(i18n("Failed to connect to INDI server %1:%2", getHost(), getPort()));
0288         }
0289     }
0290     // Did server disconnect abnormally?
0291     else if (exitCode < 0)
0292         emit terminated(i18n("Connection to INDI host at %1 on port %2 lost. Server disconnected: %3", getHost(), getPort(),
0293                              exitCode));
0294 }
0295 
0296 const QList<QSharedPointer<DriverInfo>> &ClientManager::getManagedDrivers() const
0297 {
0298     return m_ManagedDrivers;
0299 }
0300 
0301 void ClientManager::establishConnection()
0302 {
0303     qCDebug(KSTARS_INDI)
0304             << "INDI: Connecting to local INDI server on port " << getPort() << " ...";
0305 
0306     m_PendingConnection = true;
0307     m_ConnectionRetries = 2;
0308 
0309     if (connectServer() == false)
0310         serverDisconnected(0);
0311     else
0312         m_PendingConnection = false;
0313 }
0314 
0315 const QSharedPointer<DriverInfo> &ClientManager::findDriverInfoByName(const QString &name)
0316 {
0317     auto pos = std::find_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [name](QSharedPointer<DriverInfo> oneDriverInfo)
0318     {
0319         return oneDriverInfo->getName() == name;
0320     });
0321 
0322     return *pos;
0323 }
0324 
0325 const QSharedPointer<DriverInfo> &ClientManager::findDriverInfoByLabel(const QString &label)
0326 {
0327     auto pos = std::find_if(m_ManagedDrivers.begin(), m_ManagedDrivers.end(), [label](QSharedPointer<DriverInfo> oneDriverInfo)
0328     {
0329         return oneDriverInfo->getLabel() == label;
0330     });
0331 
0332     return *pos;
0333 }
0334 
0335 void ClientManager::setBLOBEnabled(bool enabled, const QString &device, const QString &property)
0336 {
0337     for(auto &bm : blobManagers)
0338     {
0339         if (bm->property("device") == device && (property.isEmpty() || bm->property("property") == property))
0340         {
0341             bm->setEnabled(enabled);
0342             return;
0343         }
0344     }
0345 }
0346 
0347 bool ClientManager::isBLOBEnabled(const QString &device, const QString &property)
0348 {
0349     for(auto &bm : blobManagers)
0350     {
0351         if (bm->property("device") == device && bm->property("property") == property)
0352             return bm->property("enabled").toBool();
0353     }
0354 
0355     return false;
0356 }