File indexing completed on 2024-12-01 06:35:11

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq (mutlaqja AT ikarustech DOT com)
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include <indicom.h>
0008 #include <base64.h>
0009 #include <basedevice.h>
0010 
0011 #include <QFrame>
0012 #include <QCheckBox>
0013 #include <QLabel>
0014 #include <QPushButton>
0015 #include <QLayout>
0016 #include <QButtonGroup>
0017 #include <QSocketNotifier>
0018 #include <QDateTime>
0019 #include <QSplitter>
0020 #include <QLineEdit>
0021 #include <QDebug>
0022 #include <QComboBox>
0023 #include <QStatusBar>
0024 #include <QMenu>
0025 #include <QTabWidget>
0026 #include <QTextEdit>
0027 
0028 #include <KLed>
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 
0032 #include "kstars.h"
0033 #include "skymap.h"
0034 #include "Options.h"
0035 #include "skyobjects/skyobject.h"
0036 #include "dialogs/timedialog.h"
0037 #include "geolocation.h"
0038 
0039 #include "indiproperty.h"
0040 #include "indidevice.h"
0041 #include "indigroup.h"
0042 #include "indielement.h"
0043 
0044 #include <indi_debug.h>
0045 
0046 const char *libindi_strings_context = "string from libindi, used in the config dialog";
0047 
0048 INDI_D::INDI_D(QWidget *parent, INDI::BaseDevice baseDevice, ClientManager *in_cm) : QWidget(parent)
0049 {
0050 #ifdef Q_OS_OSX
0051     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0052 #endif
0053     m_BaseDevice = baseDevice;
0054     m_ClientManager = in_cm;
0055 
0056     m_Name = m_BaseDevice.getDeviceName();
0057 
0058     QHBoxLayout *layout = new QHBoxLayout(this);
0059 
0060     deviceVBox = new QSplitter(Qt::Vertical, this);
0061 
0062     groupContainer = new QTabWidget(this);
0063 
0064     msgST_w = new QTextEdit(this);
0065     msgST_w->setReadOnly(true);
0066     msgST_w->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
0067 
0068     deviceVBox->addWidget(groupContainer);
0069     deviceVBox->addWidget(msgST_w);
0070     deviceVBox->setStretchFactor(0, 2);
0071 
0072     layout->addWidget(deviceVBox);
0073 }
0074 
0075 bool INDI_D::buildProperty(INDI::Property prop)
0076 {
0077     if (!prop.isValid())
0078         return false;
0079 
0080     QString groupName(prop.getGroupName());
0081 
0082     if (prop.getDeviceName() != m_Name)
0083         return false;
0084 
0085     INDI_G *pg = getGroup(groupName);
0086 
0087     if (pg == nullptr)
0088     {
0089         pg = new INDI_G(this, groupName);
0090         groupsList.append(pg);
0091         groupContainer->addTab(pg, i18nc(libindi_strings_context, groupName.toUtf8()));
0092     }
0093 
0094     return pg->addProperty(prop);
0095 }
0096 
0097 #if 0
0098 bool INDI_D::removeProperty(INDI::Property prop)
0099 {
0100     if (prop == nullptr)
0101         return false;
0102 
0103     QString groupName(prop->getGroupName());
0104 
0105     if (strcmp(prop->getDeviceName(), m_BaseDevice->getDeviceName()))
0106     {
0107         // qDebug() << Q_FUNC_INFO << "Ignoring property " << prop->getName() << " for device " << prop->getgetDeviceName() << " because our device is "
0108         //     << dv->getDeviceName() << Qt::endl;
0109         return false;
0110     }
0111 
0112     // qDebug() << Q_FUNC_INFO << "Received new property " << prop->getName() << " for our device " << dv->getDeviceName() << Qt::endl;
0113 
0114     INDI_G *pg = getGroup(groupName);
0115 
0116     if (pg == nullptr)
0117         return false;
0118 
0119     bool removeResult = pg->removeProperty(prop->getName());
0120 
0121     if (pg->size() == 0 && removeResult)
0122     {
0123         //qDebug() << Q_FUNC_INFO << "Removing tab for group " << pg->getName() << " with an index of " << groupsList.indexOf(pg) << Qt::endl;
0124         groupContainer->removeTab(groupsList.indexOf(pg));
0125         groupsList.removeOne(pg);
0126         delete (pg);
0127     }
0128 
0129     return removeResult;
0130 }
0131 #endif
0132 
0133 bool INDI_D::removeProperty(INDI::Property prop)
0134 {
0135     if (prop.getDeviceName() != m_Name)
0136         return false;
0137 
0138     for (auto &oneGroup : groupsList)
0139     {
0140         for (auto &oneProperty : oneGroup->getProperties())
0141         {
0142             if (prop.getName() == oneProperty->getName())
0143             {
0144                 bool rc = oneGroup->removeProperty(prop.getName());
0145                 if (oneGroup->size() == 0)
0146                 {
0147                     int index = groupsList.indexOf(oneGroup);
0148                     groupContainer->removeTab(index);
0149                     delete groupsList.takeAt(index);
0150                 }
0151                 return rc;
0152             }
0153         }
0154     }
0155 
0156     return false;
0157 }
0158 
0159 bool INDI_D::updateProperty(INDI::Property prop)
0160 {
0161     switch (prop.getType())
0162     {
0163         case INDI_SWITCH:
0164             return updateSwitchGUI(prop);
0165         case INDI_NUMBER:
0166             return updateNumberGUI(prop);
0167         case INDI_TEXT:
0168             return updateTextGUI(prop);
0169         case INDI_LIGHT:
0170             return updateLightGUI(prop);
0171         case INDI_BLOB:
0172             return updateBLOBGUI(prop);
0173         default:
0174             return false;
0175     }
0176 }
0177 
0178 bool INDI_D::updateSwitchGUI(INDI::Property prop)
0179 {
0180     INDI_P *guiProp = nullptr;
0181 
0182     if (m_Name != prop.getDeviceName())
0183         return false;
0184 
0185     for (const auto &pg : groupsList)
0186     {
0187         if ((guiProp = pg->getProperty(prop.getName())) != nullptr)
0188             break;
0189     }
0190 
0191     if (guiProp == nullptr || guiProp->isRegistered() == false)
0192         return false;
0193 
0194     guiProp->updateStateLED();
0195 
0196     if (guiProp->getGUIType() == PG_MENU)
0197         guiProp->updateMenuGUI();
0198     else
0199     {
0200         for (const auto &lp : guiProp->getElements())
0201             lp->syncSwitch();
0202     }
0203 
0204     return true;
0205 }
0206 
0207 bool INDI_D::updateTextGUI(INDI::Property prop)
0208 {
0209     INDI_P *guiProp = nullptr;
0210 
0211     if (m_Name != prop.getDeviceName())
0212         return false;
0213 
0214     for (const auto &pg : groupsList)
0215     {
0216         auto p = pg->getProperty(prop.getName());
0217         if (p != nullptr)
0218         {
0219             guiProp = p;
0220             break;
0221         }
0222     }
0223 
0224     if (guiProp == nullptr)
0225         return false;
0226 
0227     guiProp->updateStateLED();
0228 
0229     for (const auto &lp : guiProp->getElements())
0230         lp->syncText();
0231 
0232     return true;
0233 }
0234 
0235 bool INDI_D::updateNumberGUI(INDI::Property prop)
0236 {
0237     INDI_P *guiProp = nullptr;
0238 
0239     if (m_Name != prop.getDeviceName())
0240         return false;
0241 
0242     for (const auto &pg : groupsList)
0243     {
0244         auto p = pg->getProperty(prop.getName());
0245         if (p != nullptr)
0246         {
0247             guiProp = p;
0248             break;
0249         }
0250     }
0251 
0252     if (guiProp == nullptr)
0253         return false;
0254 
0255     guiProp->updateStateLED();
0256 
0257     for (const auto &lp : guiProp->getElements())
0258         lp->syncNumber();
0259 
0260     return true;
0261 }
0262 
0263 bool INDI_D::updateLightGUI(INDI::Property prop)
0264 {
0265     INDI_P *guiProp = nullptr;
0266 
0267     if (m_Name != prop.getDeviceName())
0268         return false;
0269 
0270     for (const auto &pg : groupsList)
0271     {
0272         auto p = pg->getProperty(prop.getName());
0273         if (p != nullptr)
0274         {
0275             guiProp = p;
0276             break;
0277         }
0278     }
0279 
0280     if (guiProp == nullptr)
0281         return false;
0282 
0283     guiProp->updateStateLED();
0284 
0285     for (const auto &lp : guiProp->getElements())
0286         lp->syncLight();
0287 
0288     return true;
0289 }
0290 
0291 bool INDI_D::updateBLOBGUI(INDI::Property prop)
0292 {
0293     INDI_P *guiProp = nullptr;
0294 
0295     if (m_Name != prop.getDeviceName())
0296         return false;
0297 
0298     for (const auto &pg : groupsList)
0299     {
0300         auto p = pg->getProperty(prop.getName());
0301         if (p != nullptr)
0302         {
0303             guiProp = p;
0304             break;
0305         }
0306     }
0307 
0308     if (guiProp == nullptr)
0309         return false;
0310 
0311     guiProp->updateStateLED();
0312 
0313     return true;
0314 }
0315 
0316 void INDI_D::updateMessageLog(INDI::BaseDevice idv, int messageID)
0317 {
0318     if (idv.getDeviceName() != m_BaseDevice.getDeviceName())
0319         return;
0320 
0321     QString message = QString::fromStdString(m_BaseDevice.messageQueue(messageID));
0322     QString formatted = message;
0323 
0324     // TODO the colors should be from the color scheme
0325     if (message.mid(21, 2) == "[E")
0326         formatted = QString("<span style='color:red'>%1</span>").arg(message);
0327     else if (message.mid(21, 2) == "[W")
0328         formatted = QString("<span style='color:orange'>%1</span>").arg(message);
0329     else if (message.mid(21, 2) != "[I")
0330     {
0331         // Debug message
0332         qCDebug(KSTARS_INDI) << idv.getDeviceName() << ":" << message.mid(21);
0333         return;
0334     }
0335 
0336     if (Options::showINDIMessages())
0337         KStars::Instance()->statusBar()->showMessage(i18nc("INDI message shown in status bar", "%1", message), 0);
0338 
0339     msgST_w->ensureCursorVisible();
0340     msgST_w->insertHtml(i18nc("Message shown in INDI control panel", "%1", formatted));
0341     msgST_w->insertPlainText("\n");
0342     QTextCursor c = msgST_w->textCursor();
0343     c.movePosition(QTextCursor::Start);
0344     msgST_w->setTextCursor(c);
0345 
0346     qCInfo(KSTARS_INDI) << idv.getDeviceName() << ": " << message.mid(21);
0347 }
0348 
0349 //INDI_D::~INDI_D()
0350 //{
0351 //    while (!groupsList.isEmpty())
0352 //        delete groupsList.takeFirst();
0353 //}
0354 
0355 INDI_G *INDI_D::getGroup(const QString &groupName) const
0356 {
0357     for (const auto &pg : groupsList)
0358     {
0359         if (pg->getName() == groupName)
0360             return pg;
0361     }
0362 
0363     return nullptr;
0364 }
0365 
0366 void INDI_D::clearMessageLog()
0367 {
0368     msgST_w->clear();
0369 }