Kstars

indistd.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5
6 Handle INDI Standard properties.
7*/
8
9#include "indistd.h"
10
11#include "clientmanager.h"
12#include "driverinfo.h"
13#include "deviceinfo.h"
14#include "imageviewer.h"
15#include "indi_debug.h"
16#include "kstars.h"
17#include "kstarsdata.h"
18#include "Options.h"
19#include "skymap.h"
20
21#include "indimount.h"
22#include "indicamera.h"
23#include "indiguider.h"
24#include "indifocuser.h"
25#include "indifilterwheel.h"
26#include "indidome.h"
27#include "indigps.h"
28#include "indiweather.h"
29#include "indiadaptiveoptics.h"
30#include "indidustcap.h"
31#include "indilightbox.h"
32#include "indidetector.h"
33#include "indirotator.h"
34#include "indispectrograph.h"
35#include "indicorrelator.h"
36#include "indiauxiliary.h"
37
38#include "genericdeviceadaptor.h"
39#include <indicom.h>
40#include <QImageReader>
41#include <QStatusBar>
42
43namespace ISD
44{
45
46GDSetCommand::GDSetCommand(INDI_PROPERTY_TYPE inPropertyType, const QString &inProperty, const QString &inElement,
47 QVariant qValue, QObject *parent)
48 : QObject(parent), propType(inPropertyType), indiProperty((inProperty)), indiElement(inElement), elementValue(qValue)
49{
50}
51
52uint8_t GenericDevice::m_ID = 1;
53GenericDevice::GenericDevice(DeviceInfo &idv, ClientManager *cm, QObject *parent) : GDInterface(parent)
54{
55 // Register DBus
56 new GenericDeviceAdaptor(this);
57 QDBusConnection::sessionBus().registerObject(QString("/KStars/INDI/GenericDevice/%1").arg(getID()), this);
58
59 m_DeviceInfo = &idv;
60 m_DriverInfo = idv.getDriverInfo();
61 m_BaseDevice = idv.getBaseDevice();
62 m_ClientManager = cm;
63
64 Q_ASSERT_X(m_BaseDevice, __FUNCTION__, "Base device is invalid.");
65 Q_ASSERT_X(m_ClientManager, __FUNCTION__, "Client manager is invalid.");
66
67 m_Name = m_BaseDevice.getDeviceName();
68
69 setObjectName(m_Name);
70
71 m_DriverInterface = m_BaseDevice.getDriverInterface();
72 m_DriverVersion = m_BaseDevice.getDriverVersion();
73
74 registerDBusType();
75
76 // JM 2020-09-05: In case KStars time change, update driver time if applicable.
77 connect(KStarsData::Instance()->clock(), &SimClock::timeChanged, this, [this]()
78 {
79 if (Options::useTimeUpdate() && Options::useKStarsSource())
80 {
81 if (isConnected())
82 {
83 auto tvp = m_BaseDevice.getText("TIME_UTC");
84 if (tvp && tvp.getPermission() != IP_RO)
85 updateTime();
86 }
87 }
88 });
89
90 m_ReadyTimer = new QTimer(this);
91 m_ReadyTimer->setInterval(250);
92 m_ReadyTimer->setSingleShot(true);
93 connect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout, Qt::UniqueConnection);
94
95 m_TimeUpdateTimer = new QTimer(this);
96 m_TimeUpdateTimer->setInterval(5000);
97 m_TimeUpdateTimer->setSingleShot(true);
98 connect(m_TimeUpdateTimer, &QTimer::timeout, this, &GenericDevice::checkTimeUpdate, Qt::UniqueConnection);
99
100 m_LocationUpdateTimer = new QTimer(this);
101 m_LocationUpdateTimer->setInterval(5000);
102 m_LocationUpdateTimer->setSingleShot(true);
103 connect(m_LocationUpdateTimer, &QTimer::timeout, this, &GenericDevice::checkLocationUpdate, Qt::UniqueConnection);
104
105}
106
107GenericDevice::~GenericDevice()
108{
109 for (auto &metadata : streamFileMetadata)
110 metadata.file->close();
111}
112
113void GenericDevice::handleTimeout()
114{
115 generateDevices();
116 // N.B. JM 2022.10.15: Do not disconnect timer.
117 // It is possible that other properties can come later.
118 // Even they do not make it in the 250ms window. Increasing timeout value alone
119 // to 1000ms or more would improve the situation but is not sufficient to account for
120 // unexpected delays. Therefore, the best solution is to keep the timer active.
121 //m_ReadyTimer->disconnect(this);
122 m_Ready = true;
123 emit ready();
124}
125
126void GenericDevice::checkTimeUpdate()
127{
128 auto tvp = getProperty("TIME_UTC");
129 if (tvp)
130 {
131 auto timeTP = tvp.getText();
132 // If time still empty, then force update.
133 if (timeTP && timeTP->getPermission() != IP_RO && timeTP->getState() == IPS_IDLE)
134 updateTime();
135 }
136
137}
138
139void GenericDevice::checkLocationUpdate()
140{
141 auto nvp = getProperty("GEOGRAPHIC_COORD");
142 if (nvp)
143 {
144 auto locationNP = nvp.getNumber();
145 // If time still empty, then force update.
146 if (locationNP && locationNP->getPermission() != IP_RO && locationNP->getState() == IPS_IDLE)
147 updateLocation();
148 }
149}
150
151void GenericDevice::registerDBusType()
152{
153#ifndef KSTARS_LITE
154 static bool isRegistered = false;
155
156 if (isRegistered == false)
157 {
158 qRegisterMetaType<ISD::ParkStatus>("ISD::ParkStatus");
160 isRegistered = true;
161 }
162#endif
163}
164
165const QString &GenericDevice::getDeviceName() const
166{
167 return m_Name;
168}
169
170void GenericDevice::registerProperty(INDI::Property prop)
171{
172 if (!prop.getRegistered())
173 return;
174
175 m_ReadyTimer->start();
176
177 const QString name = prop.getName();
178
179 // In case driver already started
180 if (name == "CONNECTION")
181 {
182 auto svp = prop.getSwitch();
183
184 // Still connecting/disconnecting...
185 if (!svp || svp->getState() == IPS_BUSY)
186 return;
187
188 auto conSP = svp->findWidgetByName("CONNECT");
189
190 if (!conSP)
191 return;
192
193 if (m_Connected == false && svp->getState() == IPS_OK && conSP->getState() == ISS_ON)
194 {
195 m_Connected = true;
196 emit Connected();
197 createDeviceInit();
198 }
199 else if (m_Connected && conSP->getState() == ISS_OFF)
200 {
201 m_Connected = false;
203 }
204 else
205 m_Ready = (svp->s == IPS_OK && conSP->s == ISS_ON);
206 }
207 else if (name == "DRIVER_INFO")
208 {
209 auto tvp = prop.getText();
210 if (tvp)
211 {
212 auto tp = tvp->findWidgetByName("DRIVER_INTERFACE");
213 if (tp)
214 {
215 m_DriverInterface = static_cast<uint32_t>(atoi(tp->getText()));
216 emit interfaceDefined();
217 }
218
219 tp = tvp->findWidgetByName("DRIVER_VERSION");
220 if (tp)
221 {
222 m_DriverVersion = QString(tp->getText());
223 }
224 }
225 }
226 else if (name == "SYSTEM_PORTS")
227 {
228 // Check if our current port is set to one of the system ports. This indicates that the port
229 // is not mapped yet to a permenant designation
230 auto svp = prop.getSwitch();
231 auto port = m_BaseDevice.getText("DEVICE_PORT");
232 if (svp && port)
233 {
234 for (const auto &it : *svp)
235 {
236 if (it.isNameMatch(port.at(0)->getText()))
237 {
238 emit systemPortDetected();
239 break;
240 }
241 }
242 }
243 }
244 else if (name == "TIME_UTC" && Options::useTimeUpdate())
245 {
246 const auto &tvp = prop.getText();
247
248 if (tvp)
249 {
250 if (Options::useKStarsSource() && tvp->getPermission() != IP_RO)
251 updateTime();
252 else
253 m_TimeUpdateTimer->start();
254 }
255 }
256 else if (name == "GEOGRAPHIC_COORD" && Options::useGeographicUpdate())
257 {
258 if (Options::useKStarsSource() && prop.getPermission() != IP_RO)
259 updateLocation();
260 else
261 m_LocationUpdateTimer->start();
262 }
263 else if (name == "WATCHDOG_HEARTBEAT")
264 {
265 if (watchDogTimer == nullptr)
266 {
267 watchDogTimer = new QTimer(this);
268 connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog()));
269 }
270
271 if (m_Connected && prop.getNumber()->at(0)->getValue() > 0)
272 {
273 // Send immediately a heart beat
274 m_ClientManager->sendNewProperty(prop);
275 }
276 }
277
278 emit propertyDefined(prop);
279}
280
281void GenericDevice::updateProperty(INDI::Property prop)
282{
283 switch (prop.getType())
284 {
285 case INDI_SWITCH:
286 processSwitch(prop);
287 break;
288 case INDI_NUMBER:
289 processNumber(prop);
290 break;
291 case INDI_TEXT:
292 processText(prop);
293 break;
294 case INDI_LIGHT:
295 processLight(prop);
296 break;
297 case INDI_BLOB:
298 processBLOB(prop);
299 break;
300 default:
301 break;
302 }
303}
304
305void GenericDevice::removeProperty(INDI::Property prop)
306{
307 emit propertyDeleted(prop);
308}
309
310void GenericDevice::processSwitch(INDI::Property prop)
311{
312 if (prop.isNameMatch("CONNECTION"))
313 {
314 // Still connecting/disconnecting...
315 if (prop.getState() == IPS_BUSY)
316 return;
317
318 auto connectionOn = prop.getSwitch()->findWidgetByName("CONNECT");
319 if (m_Connected == false && prop.getState() == IPS_OK && connectionOn->getState() == ISS_ON)
320 {
321 m_Ready = false;
322 connect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout, Qt::UniqueConnection);
323
324 m_Connected = true;
325 emit Connected();
326 createDeviceInit();
327
328 if (watchDogTimer != nullptr)
329 {
330 auto nvp = m_BaseDevice.getNumber("WATCHDOG_HEARTBEAT");
331 if (nvp && nvp.at(0)->getValue() > 0)
332 {
333 // Send immediately
334 m_ClientManager->sendNewProperty(nvp);
335 }
336 }
337
338 m_ReadyTimer->start();
339 }
340 else if (m_Connected && connectionOn->getState() == ISS_OFF)
341 {
342 disconnect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout);
343 m_Connected = false;
344 m_Ready = false;
346 }
347 else
348 m_Ready = (prop.getState() == IPS_OK && connectionOn->getState() == ISS_ON);
349 }
350
351 emit propertyUpdated(prop);
352}
353
354void GenericDevice::processNumber(INDI::Property prop)
355{
356 QString deviceName = getDeviceName();
357 auto nvp = prop.getNumber();
358
359 if (prop.isNameMatch("GEOGRAPHIC_COORD") && prop.getState() == IPS_OK &&
360 ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) ||
361 (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE))))
362 {
363 // Update KStars Location once we receive update from INDI, if the source is set to DEVICE
364 dms lng, lat;
365 double elev = 0;
366
367 auto np = nvp->findWidgetByName("LONG");
368 if (!np)
369 return;
370
371 // INDI Longitude convention is 0 to 360. We need to turn it back into 0 to 180 EAST, 0 to -180 WEST
372 if (np->value < 180)
373 lng.setD(np->value);
374 else
375 lng.setD(np->value - 360.0);
376
377 np = nvp->findWidgetByName("LAT");
378 if (!np)
379 return;
380
381 lat.setD(np->value);
382
383 // Double check we have valid values
384 if (lng.Degrees() == 0 && lat.Degrees() == 0)
385 {
386 qCWarning(KSTARS_INDI) << "Ignoring invalid device coordinates.";
387 return;
388 }
389
390 np = nvp->findWidgetByName("ELEV");
391 if (np)
392 elev = np->value;
393
394 auto geo = KStars::Instance()->data()->geo();
395 std::unique_ptr<GeoLocation> tempGeo;
396
398 if (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)
399 newLocationName = i18n("GPS Location");
400 else
401 newLocationName = i18n("Mount Location");
402
403 if (geo->name() != newLocationName)
404 {
405 double TZ0 = geo->TZ0();
406 TimeZoneRule *rule = geo->tzrule();
407 tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", TZ0, rule, elev));
408 geo = tempGeo.get();
409 }
410 else
411 {
412 geo->setLong(lng);
413 geo->setLat(lat);
414 }
415
416 qCInfo(KSTARS_INDI) << "Setting location from device:" << deviceName << "Longitude:" << lng.toDMSString() << "Latitude:" <<
417 lat.toDMSString();
418
419 KStars::Instance()->data()->setLocation(*geo);
420 }
421 else if (nvp->isNameMatch("WATCHDOG_HEARTBEAT"))
422 {
423 if (watchDogTimer == nullptr)
424 {
425 watchDogTimer = new QTimer(this);
426 connect(watchDogTimer, &QTimer::timeout, this, &GenericDevice::resetWatchdog);
427 }
428
429 auto value = nvp->at(0)->getValue();
430 if (m_Connected && value > 0)
431 {
432 // Reset timer 5 seconds before it is due
433 // To account for any networking delays
434 double nextMS = qMax(100.0, (value - 5) * 1000);
435 watchDogTimer->start(nextMS);
436 }
437 else if (value == 0)
438 watchDogTimer->stop();
439 }
440
441 emit propertyUpdated(prop);
442}
443
444void GenericDevice::processText(INDI::Property prop)
445{
446 auto tvp = prop.getText();
447 // If DRIVER_INFO is updated after being defined, make sure to re-generate concrete devices accordingly.
448 if (tvp->isNameMatch("DRIVER_INFO"))
449 {
450 auto tp = tvp->findWidgetByName("DRIVER_INTERFACE");
451 if (tp)
452 {
453 m_DriverInterface = static_cast<uint32_t>(atoi(tp->getText()));
454 emit interfaceDefined();
455
456 // If devices were already created but we receieved an update to DRIVER_INTERFACE
457 // then we need to re-generate the concrete devices to account for the change.
458 if (m_ConcreteDevices.isEmpty() == false)
459 {
460 // If we generated ANY concrete device due to interface update, then we emit ready immediately.
461 if (generateDevices())
462 emit ready();
463 }
464 }
465
466 tp = tvp->findWidgetByName("DRIVER_VERSION");
467 if (tp)
468 {
469 m_DriverVersion = QString(tp->text);
470 }
471
472 }
473 // Update KStars time once we receive update from INDI, if the source is set to DEVICE
474 else if (tvp->isNameMatch("TIME_UTC") && tvp->s == IPS_OK &&
475 ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) ||
476 (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE))))
477 {
478 int d, m, y, min, sec, hour;
479 float utcOffset;
482
483 auto tp = tvp->findWidgetByName("UTC");
484
485 if (!tp)
486 {
487 qCWarning(KSTARS_INDI) << "UTC property missing from TIME_UTC";
488 return;
489 }
490
491 sscanf(tp->getText(), "%d%*[^0-9]%d%*[^0-9]%dT%d%*[^0-9]%d%*[^0-9]%d", &y, &m, &d, &hour, &min, &sec);
492 indiDate.setDate(y, m, d);
493 indiTime.setHMS(hour, min, sec);
494
496
497 tp = tvp->findWidgetByName("OFFSET");
498
499 if (!tp)
500 {
501 qCWarning(KSTARS_INDI) << "Offset property missing from TIME_UTC";
502 return;
503 }
504
505 sscanf(tp->getText(), "%f", &utcOffset);
506
507 qCInfo(KSTARS_INDI) << "Setting UTC time from device:" << getDeviceName() << indiDateTime.toString();
508
509 KStars::Instance()->data()->changeDateTime(indiDateTime);
510 KStars::Instance()->data()->syncLST();
511
512 auto geo = KStars::Instance()->data()->geo();
513 if (geo->tzrule())
514 utcOffset -= geo->tzrule()->deltaTZ();
515
516 // TZ0 is the timezone WTIHOUT any DST offsets. Above, we take INDI UTC Offset (with DST already included)
517 // and subtract from it the deltaTZ from the current TZ rule.
518 geo->setTZ0(utcOffset);
519 }
520
521 emit propertyUpdated(prop);
522}
523
524void GenericDevice::processLight(INDI::Property prop)
525{
526 emit propertyUpdated(prop);
527}
528
529void GenericDevice::processMessage(int messageID)
530{
531 emit messageUpdated(messageID);
532}
533
534bool GenericDevice::processBLOB(INDI::Property prop)
535{
536 // Ignore write-only BLOBs since we only receive it for state-change
537 if (prop.getPermission() == IP_WO)
538 return false;
539
540 auto bvp = prop.getBLOB();
541 auto bp = bvp->at(0);
542
543 // If any concrete device processed the blob then we return
544 for (auto oneConcreteDevice : m_ConcreteDevices)
545 {
546 if (oneConcreteDevice->processBLOB(prop))
547 return true;
548 }
549
550 INDIDataTypes dataType;
551
552 if (!strcmp(bp->getFormat(), ".ascii"))
553 dataType = DATA_ASCII;
554 else
555 dataType = DATA_OTHER;
556
557 QString currentDir = Options::fitsDir();
558 int nr, n = 0;
559
560 if (currentDir.endsWith('/'))
561 currentDir.truncate(sizeof(currentDir) - 1);
562
563 QString filename(currentDir + '/');
564
565 QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss");
566
567 filename += QString("%1_").arg(bp->getLabel()) + ts + QString(bp->getFormat()).trimmed();
568
569 // Text Streaming
570 if (dataType == DATA_ASCII)
571 {
572 // First time, create a data file to hold the stream.
573
574 auto it = std::find_if(streamFileMetadata.begin(), streamFileMetadata.end(), [bvp, bp](const StreamFileMetadata & data)
575 {
576 return (bvp->getDeviceName() == data.device && bvp->getName() == data.property && bp->getName() == data.element);
577 });
578
579 QFile *streamDatafile = nullptr;
580
581 // New stream data file
582 if (it == streamFileMetadata.end())
583 {
584 StreamFileMetadata metadata;
585 metadata.device = bvp->getDeviceName();
586 metadata.property = bvp->getName();
587 metadata.element = bp->getName();
588
589 // Create new file instance
590 // Since it's a child of this class, don't worry about deallocating it
591 streamDatafile = new QFile(this);
592 metadata.file = streamDatafile;
593
594 streamFileMetadata.append(metadata);
595 }
596 else
597 streamDatafile = (*it).file;
598
599 // Try to get
600
602 for (nr = 0; nr < bp->getSize(); nr += n)
603 n = out.writeRawData(static_cast<char *>(bp->getBlob()) + nr, bp->getSize() - nr);
604
605 out.writeRawData((const char *)"\n", 1);
606 streamDatafile->flush();
607
608 }
609 else
610 {
611 QFile fits_temp_file(filename);
613 {
614 qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << fits_temp_file.fileName();
615 return false;
616 }
617
619
620 for (nr = 0; nr < bp->getSize(); nr += n)
621 n = out.writeRawData(static_cast<char *>(bp->getBlob()) + nr, bp->getSize() - nr);
622
623 fits_temp_file.flush();
624 fits_temp_file.close();
625
626 auto fmt = QString(bp->getFormat()).toLower().remove('.').toUtf8();
628 {
629 QUrl url(filename);
630 url.setScheme("file");
631 auto iv = new ImageViewer(url, QString(), KStars::Instance());
632 if (iv)
633 iv->show();
634 }
635 }
636
637 if (dataType == DATA_OTHER)
638 KStars::Instance()->statusBar()->showMessage(i18n("Data file saved to %1", filename), 0);
639
640 emit propertyUpdated(prop);
641 return true;
642}
643
644bool GenericDevice::setConfig(INDIConfig tConfig)
645{
646 auto svp = m_BaseDevice.getSwitch("CONFIG_PROCESS");
647
648 if (!svp)
649 return false;
650
651 const char *strConfig = nullptr;
652
653 switch (tConfig)
654 {
655 case LOAD_LAST_CONFIG:
656 strConfig = "CONFIG_LOAD";
657 break;
658
659 case SAVE_CONFIG:
660 strConfig = "CONFIG_SAVE";
661 break;
662
663 case LOAD_DEFAULT_CONFIG:
664 strConfig = "CONFIG_DEFAULT";
665 break;
666
667 case PURGE_CONFIG:
668 strConfig = "CONFIG_PURGE";
669 break;
670 }
671
672 svp.reset();
673 if (strConfig)
674 {
675 auto sp = svp.findWidgetByName(strConfig);
676 if (!sp)
677 return false;
678 sp->setState(ISS_ON);
679 }
680
681 m_ClientManager->sendNewProperty(svp);
682
683 return true;
684}
685
686void GenericDevice::createDeviceInit()
687{
688 if (Options::showINDIMessages())
689 KStars::Instance()->statusBar()->showMessage(i18n("%1 is online.", m_Name), 0);
690
691 KStars::Instance()->map()->forceUpdateNow();
692}
693
694/*********************************************************************************/
695/* Update the Driver's Time */
696/*********************************************************************************/
697void GenericDevice::updateTime()
698{
699 QString offset, isoTS;
700
701 offset = QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2);
702
703 //QTime newTime( KStars::Instance()->data()->ut().time());
704 //QDate newDate( KStars::Instance()->data()->ut().date());
705
706 //isoTS = QString("%1-%2-%3T%4:%5:%6").arg(newDate.year()).arg(newDate.month()).arg(newDate.day()).arg(newTime.hour()).arg(newTime.minute()).arg(newTime.second());
707
708 isoTS = KStars::Instance()->data()->ut().toString(Qt::ISODate).remove('Z');
709
710 /* Update Date/Time */
711 auto timeUTC = m_BaseDevice.getText("TIME_UTC");
712
713 if (timeUTC)
714 {
715 auto timeEle = timeUTC.findWidgetByName("UTC");
716 if (timeEle)
717 timeEle->setText(isoTS.toLatin1().constData());
718
719 auto offsetEle = timeUTC.findWidgetByName("OFFSET");
720 if (offsetEle)
721 offsetEle->setText(offset.toLatin1().constData());
722
723 if (timeEle && offsetEle)
724 {
725 qCInfo(KSTARS_INDI) << "Updating" << getDeviceName() << "Time UTC:" << isoTS << "Offset:" << offset;
726 m_ClientManager->sendNewProperty(timeUTC);
727 }
728 }
729}
730
731/*********************************************************************************/
732/* Update the Driver's Geographical Location */
733/*********************************************************************************/
734void GenericDevice::updateLocation()
735{
736 GeoLocation *geo = KStars::Instance()->data()->geo();
737 double longNP;
738
739 if (geo->lng()->Degrees() >= 0)
740 longNP = geo->lng()->Degrees();
741 else
742 longNP = dms(geo->lng()->Degrees() + 360.0).Degrees();
743
744 auto nvp = m_BaseDevice.getNumber("GEOGRAPHIC_COORD");
745
746 if (!nvp)
747 return;
748
749 auto np = nvp.findWidgetByName("LONG");
750
751 if (!np)
752 return;
753
754 np->setValue(longNP);
755
756 np = nvp.findWidgetByName("LAT");
757 if (!np)
758 return;
759
760 np->setValue(geo->lat()->Degrees());
761
762 np = nvp.findWidgetByName("ELEV");
763 if (!np)
764 return;
765
766 np->setValue(geo->elevation());
767
768 qCInfo(KSTARS_INDI) << "Updating" << getDeviceName() << "Location Longitude:" << longNP << "Latitude:" << geo->lat()->Degrees() << "Elevation:" << geo->elevation();
769
770 m_ClientManager->sendNewProperty(nvp);
771}
772
773void GenericDevice::Connect()
774{
775 m_ClientManager->connectDevice(m_Name.toLatin1().constData());
776}
777
778void GenericDevice::Disconnect()
779{
780 m_ClientManager->disconnectDevice(m_Name.toLatin1().constData());
781}
782
783bool GenericDevice::setProperty(QObject *setPropCommand)
784{
785 GDSetCommand *indiCommand = static_cast<GDSetCommand *>(setPropCommand);
786
787 //qDebug() << Q_FUNC_INFO << "We are trying to set value for property " << indiCommand->indiProperty << " and element" << indiCommand->indiElement << " and value " << indiCommand->elementValue;
788
789 auto prop = m_BaseDevice.getProperty(indiCommand->indiProperty.toLatin1().constData());
790
791 if (!prop)
792 return false;
793
794 switch (indiCommand->propType)
795 {
796 case INDI_SWITCH:
797 {
798 auto svp = prop.getSwitch();
799
800 if (!svp)
801 return false;
802
803 auto sp = svp->findWidgetByName(indiCommand->indiElement.toLatin1().constData());
804
805 if (!sp)
806 return false;
807
808 if (svp->getRule() == ISR_1OFMANY || svp->getRule() == ISR_ATMOST1)
809 svp->reset();
810
811 sp->setState(indiCommand->elementValue.toInt() == 0 ? ISS_OFF : ISS_ON);
812
813 //qDebug() << Q_FUNC_INFO << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off");
814 m_ClientManager->sendNewProperty(svp);
815
816 return true;
817 }
818
819 case INDI_NUMBER:
820 {
821 auto nvp = prop.getNumber();
822
823 if (!nvp)
824 return false;
825
826 auto np = nvp->findWidgetByName(indiCommand->indiElement.toLatin1().constData());
827
828 if (!np)
829 return false;
830
831 double value = indiCommand->elementValue.toDouble();
832
833 if (value == np->getValue())
834 return true;
835
836 np->setValue(value);
837
838 //qDebug() << Q_FUNC_INFO << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off");
839 m_ClientManager->sendNewProperty(nvp);
840 }
841 break;
842 // TODO: Add set property for other types of properties
843 default:
844 break;
845 }
846
847 return true;
848}
849
850bool GenericDevice::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max,
851 double *step)
852{
853 auto nvp = m_BaseDevice.getNumber(propName.toLatin1());
854
855 if (!nvp)
856 return false;
857
858 auto np = nvp.findWidgetByName(elementName.toLatin1());
859
860 if (!np)
861 return false;
862
863 *min = np->getMin();
864 *max = np->getMax();
865 *step = np->getStep();
866
867 return true;
868}
869
870IPState GenericDevice::getState(const QString &propName)
871{
872 return m_BaseDevice.getPropertyState(propName.toLatin1().constData());
873}
874
875IPerm GenericDevice::getPermission(const QString &propName)
876{
877 return m_BaseDevice.getPropertyPermission(propName.toLatin1().constData());
878}
879
880INDI::Property GenericDevice::getProperty(const QString &propName)
881{
882 return m_BaseDevice.getProperty(propName.toLatin1().constData());
883}
884
885bool GenericDevice::setJSONProperty(const QString &propName, const QJsonArray &propElements)
886{
887 for (auto &oneProp : * (m_BaseDevice.getProperties()))
888 {
889 if (propName == QString(oneProp.getName()))
890 {
891 switch (oneProp.getType())
892 {
893 case INDI_SWITCH:
894 {
895 auto svp = oneProp.getSwitch();
896 if (svp->getRule() == ISR_1OFMANY || svp->getRule() == ISR_ATMOST1)
897 svp->reset();
898
899 for (auto oneElement : propElements)
900 {
902 auto sp = svp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData());
903 if (sp)
904 {
905 sp->setState(static_cast<ISState>(oneElementObject["state"].toInt()));
906 }
907 }
908
909 m_ClientManager->sendNewProperty(svp);
910 return true;
911 }
912
913 case INDI_NUMBER:
914 {
915 auto nvp = oneProp.getNumber();
916 for (const auto &oneElement : propElements)
917 {
919 auto np = nvp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData());
920 if (np)
921 {
922 double newValue = oneElementObject["value"].toDouble(std::numeric_limits<double>::quiet_NaN());
923 if (std::isnan(newValue))
924 {
925 f_scansexa(oneElementObject["value"].toString().toLatin1().constData(), &newValue);
926 }
927 np->setValue(newValue);
928 }
929 }
930
931 m_ClientManager->sendNewProperty(nvp);
932 return true;
933 }
934
935 case INDI_TEXT:
936 {
937 auto tvp = oneProp.getText();
938 for (const auto &oneElement : propElements)
939 {
941 auto tp = tvp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData());
942 if (tp)
943 tp->setText(oneElementObject["text"].toString().toLatin1().constData());
944 }
945
946 m_ClientManager->sendNewProperty(tvp);
947 return true;
948 }
949
950 case INDI_BLOB:
951 // TODO
952 break;
953
954 default:
955 break;
956 }
957 }
958 }
959
960 return false;
961}
962
963bool GenericDevice::getJSONProperty(const QString &propName, QJsonObject &propObject, bool compact)
964{
965 for (auto oneProp : m_BaseDevice.getProperties())
966 {
967 if (propName == oneProp.getName())
968 {
969 switch (oneProp.getType())
970 {
971 case INDI_SWITCH:
972 switchToJson(oneProp, propObject, compact);
973 return true;
974
975 case INDI_NUMBER:
976 numberToJson(oneProp, propObject, compact);
977 return true;
978
979 case INDI_TEXT:
980 textToJson(oneProp, propObject, compact);
981 return true;
982
983 case INDI_LIGHT:
984 lightToJson(oneProp, propObject, compact);
985 return true;
986
987 case INDI_BLOB:
988 // TODO
989 break;
990
991 default:
992 break;
993 }
994 }
995 }
996
997 return false;
998}
999
1000bool GenericDevice::getJSONBLOB(const QString &propName, const QString &elementName, QJsonObject &blobObject)
1001{
1002 auto blobProperty = m_BaseDevice.getProperty(propName.toLatin1().constData());
1003 if (!blobProperty.isValid())
1004 return false;
1005
1006 auto oneBLOB = blobProperty.getBLOB()->findWidgetByName(elementName.toLatin1().constData());
1007 if (!oneBLOB)
1008 return false;
1009
1010 // Now convert to base64 and send back.
1011 QByteArray data = QByteArray::fromRawData(static_cast<const char *>(oneBLOB->getBlob()), oneBLOB->getBlobLen());
1012
1014 blobObject.insert("property", propName);
1015 blobObject.insert("element", elementName);
1016 blobObject.insert("size", encoded.size());
1017 blobObject.insert("data", encoded);
1018
1019 return true;
1020}
1021
1022void GenericDevice::resetWatchdog()
1023{
1024 auto nvp = m_BaseDevice.getNumber("WATCHDOG_HEARTBEAT");
1025
1026 if (nvp)
1027 // Send heartbeat to driver
1028 m_ClientManager->sendNewProperty(nvp);
1029}
1030
1031bool GenericDevice::findConcreteDevice(uint32_t interface, QSharedPointer<ConcreteDevice> &device)
1032{
1033 if (m_ConcreteDevices.contains(interface))
1034 {
1035 device = m_ConcreteDevices[interface];
1036 return true;
1037 }
1038 return false;
1039}
1040
1041ISD::Mount *GenericDevice::getMount()
1042{
1043 if (m_ConcreteDevices.contains(INDI::BaseDevice::TELESCOPE_INTERFACE))
1044 return dynamic_cast<ISD::Mount*>(m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].get());
1045 return nullptr;
1046}
1047
1048ISD::Camera *GenericDevice::getCamera()
1049{
1050 if (m_ConcreteDevices.contains(INDI::BaseDevice::CCD_INTERFACE))
1051 return dynamic_cast<ISD::Camera*>(m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].get());
1052 return nullptr;
1053}
1054
1055ISD::Guider *GenericDevice::getGuider()
1056{
1057 if (m_ConcreteDevices.contains(INDI::BaseDevice::GUIDER_INTERFACE))
1058 return dynamic_cast<ISD::Guider*>(m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].get());
1059 return nullptr;
1060}
1061
1062ISD::Focuser *GenericDevice::getFocuser()
1063{
1064 if (m_ConcreteDevices.contains(INDI::BaseDevice::FOCUSER_INTERFACE))
1065 return dynamic_cast<ISD::Focuser*>(m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].get());
1066 return nullptr;
1067}
1068
1069ISD::FilterWheel *GenericDevice::getFilterWheel()
1070{
1071 if (m_ConcreteDevices.contains(INDI::BaseDevice::FILTER_INTERFACE))
1072 return dynamic_cast<ISD::FilterWheel*>(m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].get());
1073 return nullptr;
1074}
1075
1076ISD::Dome *GenericDevice::getDome()
1077{
1078 if (m_ConcreteDevices.contains(INDI::BaseDevice::DOME_INTERFACE))
1079 return dynamic_cast<ISD::Dome*>(m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].get());
1080 return nullptr;
1081}
1082
1083ISD::GPS *GenericDevice::getGPS()
1084{
1085 if (m_ConcreteDevices.contains(INDI::BaseDevice::GPS_INTERFACE))
1086 return dynamic_cast<ISD::GPS*>(m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].get());
1087 return nullptr;
1088}
1089
1090ISD::Weather *GenericDevice::getWeather()
1091{
1092 if (m_ConcreteDevices.contains(INDI::BaseDevice::WEATHER_INTERFACE))
1093 return dynamic_cast<ISD::Weather*>(m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].get());
1094 return nullptr;
1095}
1096
1097ISD::AdaptiveOptics *GenericDevice::getAdaptiveOptics()
1098{
1099 if (m_ConcreteDevices.contains(INDI::BaseDevice::AO_INTERFACE))
1100 return dynamic_cast<ISD::AdaptiveOptics*>(m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].get());
1101 return nullptr;
1102}
1103
1104ISD::DustCap *GenericDevice::getDustCap()
1105{
1106 if (m_ConcreteDevices.contains(INDI::BaseDevice::DUSTCAP_INTERFACE))
1107 return dynamic_cast<ISD::DustCap*>(m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].get());
1108 return nullptr;
1109}
1110
1111ISD::LightBox *GenericDevice::getLightBox()
1112{
1113 if (m_ConcreteDevices.contains(INDI::BaseDevice::LIGHTBOX_INTERFACE))
1114 return dynamic_cast<ISD::LightBox*>(m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].get());
1115 return nullptr;
1116}
1117
1118ISD::Detector *GenericDevice::getDetector()
1119{
1120 if (m_ConcreteDevices.contains(INDI::BaseDevice::DETECTOR_INTERFACE))
1121 return dynamic_cast<ISD::Detector*>(m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].get());
1122 return nullptr;
1123}
1124
1125ISD::Rotator *GenericDevice::getRotator()
1126{
1127 if (m_ConcreteDevices.contains(INDI::BaseDevice::ROTATOR_INTERFACE))
1128 return dynamic_cast<ISD::Rotator*>(m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].get());
1129 return nullptr;
1130}
1131
1132ISD::Spectrograph *GenericDevice::getSpectrograph()
1133{
1134 if (m_ConcreteDevices.contains(INDI::BaseDevice::SPECTROGRAPH_INTERFACE))
1135 return dynamic_cast<ISD::Spectrograph*>(m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].get());
1136 return nullptr;
1137}
1138
1139ISD::Correlator *GenericDevice::getCorrelator()
1140{
1141 if (m_ConcreteDevices.contains(INDI::BaseDevice::CORRELATOR_INTERFACE))
1142 return dynamic_cast<ISD::Correlator*>(m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].get());
1143 return nullptr;
1144}
1145
1146ISD::Auxiliary *GenericDevice::getAuxiliary()
1147{
1148 if (m_ConcreteDevices.contains(INDI::BaseDevice::AUX_INTERFACE))
1149 return dynamic_cast<ISD::Auxiliary*>(m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].get());
1150 return nullptr;
1151}
1152
1153bool GenericDevice::generateDevices()
1154{
1155 auto generated = false;
1156 // Mount
1157 if (m_DriverInterface & INDI::BaseDevice::TELESCOPE_INTERFACE &&
1158 m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].isNull())
1159 {
1160 auto mount = new ISD::Mount(this);
1161 mount->setObjectName("Mount:" + objectName());
1162 generated = true;
1163 m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].reset(mount);
1164 mount->registeProperties();
1165 if (m_Connected)
1166 {
1167 mount->processProperties();
1168 emit newMount(mount);
1169 }
1170 else
1171 {
1172 connect(mount, &ISD::ConcreteDevice::ready, this, [this, mount]()
1173 {
1174 emit newMount(mount);
1175 });
1176 }
1177 }
1178
1179 // Camera
1180 if (m_DriverInterface & INDI::BaseDevice::CCD_INTERFACE &&
1181 m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].isNull())
1182 {
1183 auto camera = new ISD::Camera(this);
1184 camera->setObjectName("Camera:" + objectName());
1185 generated = true;
1186 m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].reset(camera);
1187 camera->registeProperties();
1188 if (m_Connected)
1189 {
1190 camera->processProperties();
1191 emit newCamera(camera);
1192 emit ready();
1193 }
1194 else
1195 {
1196 connect(camera, &ISD::ConcreteDevice::ready, this, [this, camera]()
1197 {
1198 emit newCamera(camera);
1199 });
1200 }
1201 }
1202
1203 // Guider
1204 if (m_DriverInterface & INDI::BaseDevice::GUIDER_INTERFACE &&
1205 m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].isNull())
1206 {
1207 auto guider = new ISD::Guider(this);
1208 guider->setObjectName("Guider:" + objectName());
1209 generated = true;
1210 m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].reset(guider);
1211 guider->registeProperties();
1212 if (m_Connected)
1213 {
1214 guider->processProperties();
1215 emit newGuider(guider);
1216 }
1217 else
1218 {
1219 connect(guider, &ISD::ConcreteDevice::ready, this, [this, guider]()
1220 {
1221 emit newGuider(guider);
1222 });
1223 }
1224 }
1225
1226 // Focuser
1227 if (m_DriverInterface & INDI::BaseDevice::FOCUSER_INTERFACE &&
1228 m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].isNull())
1229 {
1230 auto focuser = new ISD::Focuser(this);
1231 focuser->setObjectName("Focuser:" + objectName());
1232 generated = true;
1233 m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].reset(focuser);
1234 focuser->registeProperties();
1235 if (m_Connected)
1236 {
1237 focuser->processProperties();
1238 emit newFocuser(focuser);
1239 }
1240 else
1241 {
1242 connect(focuser, &ISD::ConcreteDevice::ready, this, [this, focuser]()
1243 {
1244 emit newFocuser(focuser);
1245 });
1246 }
1247 }
1248
1249 // Filter Wheel
1250 if (m_DriverInterface & INDI::BaseDevice::FILTER_INTERFACE &&
1251 m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].isNull())
1252 {
1253 auto filterWheel = new ISD::FilterWheel(this);
1254 filterWheel->setObjectName("FilterWheel:" + objectName());
1255 generated = true;
1256 m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].reset(filterWheel);
1257 filterWheel->registeProperties();
1258 if (m_Connected)
1259 {
1260 filterWheel->processProperties();
1261 emit newFilterWheel(filterWheel);
1262 }
1263 else
1264 {
1265 connect(filterWheel, &ISD::ConcreteDevice::ready, this, [this, filterWheel]()
1266 {
1267 emit newFilterWheel(filterWheel);
1268 });
1269 }
1270 }
1271
1272 // Dome
1273 if (m_DriverInterface & INDI::BaseDevice::DOME_INTERFACE &&
1274 m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].isNull())
1275 {
1276 auto dome = new ISD::Dome(this);
1277 dome->setObjectName("Dome:" + objectName());
1278 generated = true;
1279 m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].reset(dome);
1280 dome->registeProperties();
1281 if (m_Connected)
1282 {
1283 dome->processProperties();
1284 emit newDome(dome);
1285 }
1286 else
1287 {
1288 connect(dome, &ISD::ConcreteDevice::ready, this, [this, dome]()
1289 {
1290 emit newDome(dome);
1291 });
1292 }
1293 }
1294
1295 // GPS
1296 if (m_DriverInterface & INDI::BaseDevice::GPS_INTERFACE &&
1297 m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].isNull())
1298 {
1299 auto gps = new ISD::GPS(this);
1300 gps->setObjectName("GPS:" + objectName());
1301 generated = true;
1302 m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].reset(gps);
1303 gps->registeProperties();
1304 if (m_Connected)
1305 {
1306 gps->processProperties();
1307 emit newGPS(gps);
1308 }
1309 else
1310 {
1311 connect(gps, &ISD::ConcreteDevice::ready, this, [this, gps]()
1312 {
1313 emit newGPS(gps);
1314 });
1315 }
1316 }
1317
1318 // Weather
1319 if (m_DriverInterface & INDI::BaseDevice::WEATHER_INTERFACE &&
1320 m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].isNull())
1321 {
1322 auto weather = new ISD::Weather(this);
1323 weather->setObjectName("Weather:" + objectName());
1324 generated = true;
1325 m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].reset(weather);
1326 weather->registeProperties();
1327 if (m_Connected)
1328 {
1329 weather->processProperties();
1330 emit newWeather(weather);
1331 }
1332 else
1333 {
1334 connect(weather, &ISD::ConcreteDevice::ready, this, [this, weather]()
1335 {
1336 emit newWeather(weather);
1337 });
1338 }
1339 }
1340
1341 // Adaptive Optics
1342 if (m_DriverInterface & INDI::BaseDevice::AO_INTERFACE &&
1343 m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].isNull())
1344 {
1345 auto ao = new ISD::AdaptiveOptics(this);
1346 ao->setObjectName("AdaptiveOptics:" + objectName());
1347 generated = true;
1348 m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].reset(ao);
1349 ao->registeProperties();
1350 if (m_Connected)
1351 {
1352 ao->processProperties();
1353 emit newAdaptiveOptics(ao);
1354 }
1355 else
1356 {
1357 connect(ao, &ISD::ConcreteDevice::ready, this, [this, ao]()
1358 {
1359 emit newAdaptiveOptics(ao);
1360 });
1361 }
1362 }
1363
1364 // Dust Cap
1365 if (m_DriverInterface & INDI::BaseDevice::DUSTCAP_INTERFACE &&
1366 m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].isNull())
1367 {
1368 auto dustCap = new ISD::DustCap(this);
1369 dustCap->setObjectName("DustCap:" + objectName());
1370 generated = true;
1371 m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].reset(dustCap);
1372 dustCap->registeProperties();
1373 if (m_Connected)
1374 {
1375 dustCap->processProperties();
1376 emit newDustCap(dustCap);
1377 }
1378 else
1379 {
1380 connect(dustCap, &ISD::ConcreteDevice::ready, this, [this, dustCap]()
1381 {
1382 emit newDustCap(dustCap);
1383 });
1384 }
1385 }
1386
1387 // Light box
1388 if (m_DriverInterface & INDI::BaseDevice::LIGHTBOX_INTERFACE &&
1389 m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].isNull())
1390 {
1391 auto lightBox = new ISD::LightBox(this);
1392 lightBox->setObjectName("LightBox:" + objectName());
1393 generated = true;
1394 m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].reset(lightBox);
1395 lightBox->registeProperties();
1396 if (m_Connected)
1397 {
1398 lightBox->processProperties();
1399 emit newLightBox(lightBox);
1400 }
1401 else
1402 {
1403 connect(lightBox, &ISD::ConcreteDevice::ready, this, [this, lightBox]()
1404 {
1405 emit newLightBox(lightBox);
1406 });
1407 }
1408 }
1409
1410 // Rotator
1411 if (m_DriverInterface & INDI::BaseDevice::ROTATOR_INTERFACE &&
1412 m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].isNull())
1413 {
1414 auto rotator = new ISD::Rotator(this);
1415 rotator->setObjectName("Rotator:" + objectName());
1416 generated = true;
1417 m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].reset(rotator);
1418 rotator->registeProperties();
1419 if (m_Connected)
1420 {
1421 rotator->processProperties();
1422 emit newRotator(rotator);
1423 }
1424 else
1425 {
1426 connect(rotator, &ISD::ConcreteDevice::ready, this, [this, rotator]()
1427 {
1428 emit newRotator(rotator);
1429 });
1430 }
1431 }
1432
1433 // Detector
1434 if (m_DriverInterface & INDI::BaseDevice::DETECTOR_INTERFACE &&
1435 m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].isNull())
1436 {
1437 auto detector = new ISD::Detector(this);
1438 detector->setObjectName("Detector:" + objectName());
1439 generated = true;
1440 m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].reset(detector);
1441 detector->registeProperties();
1442 if (m_Connected)
1443 {
1444 detector->processProperties();
1445 emit newDetector(detector);
1446 }
1447 else
1448 {
1449 connect(detector, &ISD::ConcreteDevice::ready, this, [this, detector]()
1450 {
1451 emit newDetector(detector);
1452 });
1453 }
1454 }
1455
1456 // Spectrograph
1457 if (m_DriverInterface & INDI::BaseDevice::SPECTROGRAPH_INTERFACE &&
1458 m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].isNull())
1459 {
1460 auto spectrograph = new ISD::Spectrograph(this);
1461 spectrograph->setObjectName("Spectrograph:" + objectName());
1462 generated = true;
1463 m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].reset(spectrograph);
1464 spectrograph->registeProperties();
1465 if (m_Connected)
1466 {
1467 spectrograph->processProperties();
1468 emit newSpectrograph(spectrograph);
1469 }
1470 else
1471 {
1472 connect(spectrograph, &ISD::ConcreteDevice::ready, this, [this, spectrograph]()
1473 {
1474 emit newSpectrograph(spectrograph);
1475 });
1476 }
1477 }
1478
1479 // Correlator
1480 if (m_DriverInterface & INDI::BaseDevice::CORRELATOR_INTERFACE &&
1481 m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].isNull())
1482 {
1483 auto correlator = new ISD::Correlator(this);
1484 correlator->setObjectName("Correlator:" + objectName());
1485 generated = true;
1486 m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].reset(correlator);
1487 correlator->registeProperties();
1488 if (m_Connected)
1489 {
1490 correlator->processProperties();
1491 emit newCorrelator(correlator);
1492 }
1493 else
1494 {
1495 connect(correlator, &ISD::ConcreteDevice::ready, this, [this, correlator]()
1496 {
1497 emit newCorrelator(correlator);
1498 });
1499 }
1500 }
1501
1502 // Auxiliary
1503 if (m_DriverInterface & INDI::BaseDevice::AUX_INTERFACE &&
1504 m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].isNull())
1505 {
1506 auto aux = new ISD::Auxiliary(this);
1507 aux->setObjectName("Auxiliary:" + objectName());
1508 generated = true;
1509 m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].reset(aux);
1510 aux->registeProperties();
1511 if (m_Connected)
1512 {
1513 aux->processProperties();
1514 emit newAuxiliary(aux);
1515 }
1516 else
1517 {
1518 connect(aux, &ISD::ConcreteDevice::ready, this, [this, aux]()
1519 {
1520 emit newAuxiliary(aux);
1521 });
1522 }
1523 }
1524
1525 return generated;
1526}
1527
1528void GenericDevice::sendNewProperty(INDI::Property prop)
1529{
1530 m_ClientManager->sendNewProperty(prop);
1531}
1532
1533void switchToJson(INDI::Property prop, QJsonObject &propObject, bool compact)
1534{
1535 auto svp = prop.getSwitch();
1537 for (int i = 0; i < svp->count(); i++)
1538 {
1539 QJsonObject oneSwitch = {{"name", svp->at(i)->getName()}, {"state", svp->at(i)->getState()}};
1540 if (!compact)
1541 oneSwitch.insert("label", svp->at(i)->getLabel());
1542 switches.append(oneSwitch);
1543 }
1544
1545 propObject = {{"device", svp->getDeviceName()}, {"name", svp->getName()}, {"state", svp->getState()}, {"switches", switches}};
1546 if (!compact)
1547 {
1548 propObject.insert("label", svp->getLabel());
1549 propObject.insert("group", svp->getGroupName());
1550 propObject.insert("perm", svp->getPermission());
1551 propObject.insert("rule", svp->getRule());
1552 }
1553}
1554
1555void numberToJson(INDI::Property prop, QJsonObject &propObject, bool compact)
1556{
1557 auto nvp = prop.getNumber();
1559 for (int i = 0; i < nvp->count(); i++)
1560 {
1561 QJsonObject oneNumber = {{"name", nvp->at(i)->getName()}, {"value", nvp->at(i)->getValue()}};
1562 if (!compact)
1563 {
1564 oneNumber.insert("label", nvp->at(i)->getLabel());
1565 oneNumber.insert("min", nvp->at(i)->getMin());
1566 oneNumber.insert("max", nvp->at(i)->getMax());
1567 oneNumber.insert("step", nvp->at(i)->getStep());
1568 oneNumber.insert("format", nvp->at(i)->getFormat());
1569 }
1570 numbers.append(oneNumber);
1571 }
1572
1573 propObject = {{"device", nvp->getDeviceName()}, {"name", nvp->getName()}, {"state", nvp->getState()}, {"numbers", numbers}};
1574 if (!compact)
1575 {
1576 propObject.insert("label", nvp->getLabel());
1577 propObject.insert("group", nvp->getGroupName());
1578 propObject.insert("perm", nvp->getPermission());
1579 }
1580}
1581
1582void textToJson(INDI::Property prop, QJsonObject &propObject, bool compact)
1583{
1584 auto tvp = prop.getText();
1586 for (int i = 0; i < tvp->count(); i++)
1587 {
1588 QJsonObject oneText = {{"name", tvp->at(i)->getName()}, {"text", tvp->at(i)->getText()}};
1589 if (!compact)
1590 {
1591 oneText.insert("label", tvp->at(i)->getLabel());
1592 }
1593 Texts.append(oneText);
1594 }
1595
1596 propObject = {{"device", tvp->getDeviceName()}, {"name", tvp->getName()}, {"state", tvp->getState()}, {"texts", Texts}};
1597 if (!compact)
1598 {
1599 propObject.insert("label", tvp->getLabel());
1600 propObject.insert("group", tvp->getGroupName());
1601 propObject.insert("perm", tvp->getPermission());
1602 }
1603}
1604
1605void lightToJson(INDI::Property prop, QJsonObject &propObject, bool compact)
1606{
1607 auto lvp = prop.getLight();
1609 for (int i = 0; i < lvp->count(); i++)
1610 {
1611 QJsonObject oneLight = {{"name", lvp->at(i)->getName()}, {"state", lvp->at(i)->getState()}};
1612 if (!compact)
1613 {
1614 oneLight.insert("label", lvp->at(i)->getLabel());
1615 }
1616 Lights.append(oneLight);
1617 }
1618
1619 propObject = {{"device", lvp->getDeviceName()}, {"name", lvp->getName()}, {"state", lvp->getState()}, {"lights", Lights}};
1620 if (!compact)
1621 {
1622 propObject.insert("label", lvp->getLabel());
1623 propObject.insert("group", lvp->getGroupName());
1624 }
1625}
1626
1627void propertyToJson(INDI::Property prop, QJsonObject &propObject, bool compact)
1628{
1629 switch (prop.getType())
1630 {
1631 case INDI_SWITCH:
1632 switchToJson(prop, propObject, compact);
1633 break;
1634 case INDI_TEXT:
1635 textToJson(prop, propObject, compact);
1636 break;
1637 case INDI_NUMBER:
1638 numberToJson(prop, propObject, compact);
1639 break;
1640 case INDI_LIGHT:
1641 lightToJson(prop, propObject, compact);
1642 break;
1643 default:
1644 break;
1645 }
1646}
1647}
1648
1649#ifndef KSTARS_LITE
1650QDBusArgument &operator<<(QDBusArgument &argument, const ISD::ParkStatus &source)
1651{
1652 argument.beginStructure();
1654 argument.endStructure();
1655 return argument;
1656}
1657
1658const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::ParkStatus &dest)
1659{
1660 int a;
1661 argument.beginStructure();
1662 argument >> a;
1663 argument.endStructure();
1664 dest = static_cast<ISD::ParkStatus>(a);
1665 return argument;
1666}
1667#endif
ClientManager manages connection to INDI server, creation of devices, and receiving/sending propertie...
DeviceInfo is simple class to hold DriverInfo and INDI::BaseDevice associated with a particular devic...
Definition deviceinfo.h:21
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
AdaptiveOptics class handles control of INDI AdaptiveOptics devices.
Auxiliary class handles control of INDI Auxiliary devices.
Camera class controls an INDI Camera device.
Definition indicamera.h:47
Correlator class handles control of INDI Correlator devices.
Detector class handles control of INDI Detector devices.
Class handles control of INDI dome devices.
Definition indidome.h:23
Handles operation of a remotely controlled dust cover cap.
Definition indidustcap.h:23
Focuser class handles control of INDI focuser devices.
Definition indifocuser.h:21
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
Definition indimount.h:27
Rotator class handles control of INDI Rotator devices.
Definition indirotator.h:20
Spectrograph class handles control of INDI Spectrograph devices.
Focuser class handles control of INDI Weather devices.
Definition indiweather.h:24
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
static KStars * Instance()
Definition kstars.h:123
void timeChanged()
The time has changed (emitted by setUTC() )
This class provides the information needed to determine whether Daylight Savings Time (DST; a....
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition dms.h:179
const double & Degrees() const
Definition dms.h:141
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
ISD is a collection of INDI Standard Devices.
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KDB_EXPORT void getProperties(const KDbLookupFieldSchema *lookup, QMap< QByteArray, QVariant > *values)
GeoCoordinates geo(const QVariant &location)
const QList< QKeySequence > & close()
QString name(StandardShortcut id)
QCA_EXPORT QVariant getProperty(const QString &name)
const char * constData() const const
QByteArray fromRawData(const char *data, qsizetype size)
QByteArray toBase64(Base64Options options) const const
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
Int toInt() const const
QList< QByteArray > supportedImageFormats()
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & setNum(double n, char format, int precision)
qsizetype size() const const
QByteArray toLatin1() const const
QString toLower() const const
QByteArray toUtf8() const const
QString trimmed() const const
void truncate(qsizetype position)
UniqueConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
void setScheme(const QString &scheme)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.