File indexing completed on 2024-07-21 06:28:22

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 
0006     Handle INDI Standard properties.
0007 */
0008 
0009 #include "indilistener.h"
0010 
0011 #include "clientmanager.h"
0012 #include "deviceinfo.h"
0013 #include "kstars.h"
0014 #include "Options.h"
0015 
0016 #include "auxiliary/ksnotification.h"
0017 
0018 #include <knotification.h>
0019 
0020 #include <basedevice.h>
0021 #include <indi_debug.h>
0022 
0023 INDIListener *INDIListener::_INDIListener = nullptr;
0024 
0025 INDIListener *INDIListener::Instance()
0026 {
0027     if (_INDIListener == nullptr)
0028     {
0029         _INDIListener = new INDIListener(KStars::Instance());
0030         qRegisterMetaType<INDI::BaseDevice>("INDI::BaseDevice");
0031     }
0032 
0033     return _INDIListener;
0034 }
0035 
0036 bool INDIListener::findDevice(const QString &name, QSharedPointer<ISD::GenericDevice> &device)
0037 {
0038     auto all = devices();
0039     auto it = std::find_if(all.begin(), all.end(), [name](auto & oneDevice)
0040     {
0041         return oneDevice->getDeviceName() == name;
0042     });
0043     if (it == all.end())
0044         return false;
0045 
0046     device = *it;
0047     return true;
0048 }
0049 
0050 INDIListener::INDIListener(QObject *parent) : QObject(parent) {}
0051 
0052 bool INDIListener::getDevice(const QString &name, QSharedPointer<ISD::GenericDevice> &device) const
0053 {
0054     for (auto &oneDevice : m_Devices)
0055     {
0056         if (oneDevice->getDeviceName() == name)
0057         {
0058             device = oneDevice;
0059             return true;
0060         }
0061     }
0062     return false;
0063 }
0064 
0065 void INDIListener::addClient(ClientManager *cm)
0066 {
0067     qCDebug(KSTARS_INDI)
0068             << "INDIListener: Adding a new client manager to INDI listener..";
0069 
0070     clients.append(cm);
0071 
0072     connect(cm, &ClientManager::newINDIDevice, this, &INDIListener::processDevice);
0073     connect(cm, &ClientManager::removeINDIDevice, this, &INDIListener::removeDevice);
0074 
0075     connect(cm, &ClientManager::newINDIProperty, this, &INDIListener::registerProperty);
0076     connect(cm, &ClientManager::updateINDIProperty, this, &INDIListener::updateProperty);
0077     connect(cm, &ClientManager::removeINDIProperty, this, &INDIListener::removeProperty);
0078 
0079     connect(cm, &ClientManager::newINDIMessage, this,
0080             &INDIListener::processMessage);
0081     connect(cm, &ClientManager::newINDIUniversalMessage, this,
0082             &INDIListener::processUniversalMessage);
0083 }
0084 
0085 void INDIListener::removeClient(ClientManager *cm)
0086 {
0087     qCDebug(KSTARS_INDI) << "INDIListener: Removing client manager for server"
0088                          << cm->getHost() << "@" << cm->getPort();
0089 
0090     cm->disconnect(this);
0091     clients.removeOne(cm);
0092 
0093     auto managedDrivers = cm->getManagedDrivers();
0094     for (auto &oneDriverInfo : managedDrivers)
0095         cm->removeManagedDriver(oneDriverInfo);
0096 }
0097 
0098 void INDIListener::processDevice(DeviceInfo *dv)
0099 {
0100     ClientManager *cm = qobject_cast<ClientManager *>(sender());
0101     Q_ASSERT_X(cm, __FUNCTION__, "Client manager is not valid.");
0102 
0103     qCDebug(KSTARS_INDI) << "INDIListener: New device" << dv->getDeviceName();
0104 
0105     QSharedPointer<ISD::GenericDevice> gd;
0106     gd.reset(new ISD::GenericDevice(*dv, cm, this));
0107 
0108     auto isConnected = gd->isConnected();
0109     // If already connected, we need to process all the properties as well
0110     // Register all existing properties
0111     for (auto &oneProperty : dv->getBaseDevice().getProperties())
0112     {
0113         gd->registerProperty(oneProperty);
0114         if (isConnected)
0115         {
0116             switch (oneProperty.getType())
0117             {
0118                 case INDI_SWITCH:
0119                     gd->processSwitch(oneProperty.getSwitch());
0120                     break;
0121                 case INDI_NUMBER:
0122                     gd->processNumber(oneProperty.getNumber());
0123                     break;
0124                 case INDI_TEXT:
0125                     gd->processText(oneProperty.getText());
0126                     break;
0127                 case INDI_LIGHT:
0128                     gd->processLight(oneProperty.getLight());
0129                     break;
0130                 default:
0131                     break;
0132             }
0133         }
0134     }
0135 
0136     m_Devices.append(std::move(gd));
0137     emit newDevice(gd);
0138 
0139 }
0140 
0141 void INDIListener::removeDevice(const QString &deviceName)
0142 {
0143     qCDebug(KSTARS_INDI) << "INDIListener: Removing device" << deviceName;
0144 
0145     for (auto oneDevice : qAsConst(m_Devices))
0146     {
0147         if (oneDevice->getDeviceName() == deviceName)
0148         {
0149             // Remove from list first
0150             m_Devices.removeOne(oneDevice);
0151             // Then emit a signal to inform subscribers that this device is removed.
0152             emit deviceRemoved(oneDevice);
0153             // Delete this device later.
0154             oneDevice->deleteLater();
0155             return;
0156         }
0157     }
0158 }
0159 
0160 void INDIListener::registerProperty(INDI::Property prop)
0161 {
0162     if (!prop.getRegistered())
0163         return;
0164 
0165     qCDebug(KSTARS_INDI) << "<" << prop.getDeviceName() << ">: <" << prop.getName()
0166                          << ">";
0167 
0168     for (auto &oneDevice : m_Devices)
0169     {
0170         if (oneDevice->getDeviceName() == prop.getDeviceName())
0171         {
0172             oneDevice->registerProperty(prop);
0173             return;
0174         }
0175     }
0176 }
0177 
0178 void INDIListener::removeProperty(INDI::Property prop)
0179 {
0180     for (auto &oneDevice : m_Devices)
0181     {
0182         if (oneDevice->getDeviceName() == prop.getDeviceName())
0183         {
0184             oneDevice->removeProperty(prop);
0185             return;
0186         }
0187     }
0188 }
0189 
0190 void INDIListener::updateProperty(INDI::Property prop)
0191 {
0192     for (auto &oneDevice : m_Devices)
0193     {
0194         if (oneDevice->getDeviceName() == prop.getDeviceName())
0195         {
0196             oneDevice->updateProperty(prop);
0197             break;
0198         }
0199     }
0200 }
0201 
0202 void INDIListener::processMessage(INDI::BaseDevice dp, int messageID)
0203 {
0204     for (auto &oneDevice : m_Devices)
0205     {
0206         if (oneDevice->getDeviceName() == dp.getDeviceName())
0207         {
0208             oneDevice->processMessage(messageID);
0209             break;
0210         }
0211     }
0212 }
0213 
0214 void INDIListener::processUniversalMessage(const QString &message)
0215 {
0216     QString displayMessage = message;
0217     // Remove timestamp info as it is not suitable for message box
0218     int colonIndex = displayMessage.indexOf(": ");
0219     if (colonIndex > 0)
0220         displayMessage = displayMessage.mid(colonIndex + 2);
0221 
0222     // Special case for Alignment since it is not tied to a device
0223     if (displayMessage.startsWith("[ALIGNMENT]"))
0224     {
0225         qCDebug(KSTARS_INDI) << "AlignmentSubSystem:" << displayMessage;
0226         return;
0227     }
0228 
0229     if (Options::messageNotificationINDI())
0230     {
0231         KSNotification::event(QLatin1String("IndiServerMessage"), displayMessage,
0232                               KSNotification::INDI, KSNotification::Warn);
0233     }
0234     else
0235     {
0236         KSNotification::transient(displayMessage, i18n("INDI Server Message"));
0237     }
0238 }