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

KDE's Doxygen guidelines are available online.