Kstars

clientmanagerlite.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4*/
5
6#include "clientmanagerlite.h"
7
8#include "basedevice.h"
9#include "indicom.h"
10#include "inditelescopelite.h"
11#include "kspaths.h"
12#include "kstarslite.h"
13#include "Options.h"
14#include "skymaplite.h"
15#include "fitsviewer/fitsdata.h"
16#include "kstarslite/imageprovider.h"
17#include "kstarslite/skyitems/telescopesymbolsitem.h"
18
19#include <KLocalizedString>
20
21#include <QApplication>
22#include <QDebug>
23#include <QFileDialog>
24#include <QImageReader>
25#include <QJsonArray>
26#include <QJsonDocument>
27#include <QProcess>
28#include <QQmlApplicationEngine>
29#include <QQmlContext>
30#include <QTemporaryFile>
31
32const char *libindi_strings_context = "string from libindi, used in the config dialog";
33
34#ifdef Q_OS_ANDROID
35#include "libraw/libraw.h"
36#endif
37
38DeviceInfoLite::DeviceInfoLite(INDI::BaseDevice *dev) : device(dev)
39{
40}
41
42DeviceInfoLite::~DeviceInfoLite()
43{
44}
45
46ClientManagerLite::ClientManagerLite(QQmlContext& main_context) : context(main_context)
47{
48#ifdef ANDROID
49 defaultImageType = ".jpeg";
50 defaultImagesLocation = QDir(KSPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + qAppName()).path();
51#endif
52 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeNS");
53 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeWE");
54 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeCommand");
55 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles));
56}
57
58ClientManagerLite::~ClientManagerLite()
59{
60}
61
62bool ClientManagerLite::setHost(const QString &ip, unsigned int port)
63{
64 if (!isConnected())
65 {
66 setServer(ip.toStdString().c_str(), port);
67 qDebug() << ip << port;
68 if (connectServer())
69 {
70 setConnectedHost(ip + ':' + QString::number(port));
71 //Update last used server and port
72 setLastUsedServer(ip);
73 setLastUsedPort(port);
74
75 return true;
76 }
77 }
78 return false;
79}
80
81void ClientManagerLite::disconnectHost()
82{
83 disconnectServer();
84 clearDevices();
85 setConnectedHost("");
86}
87
88void ClientManagerLite::getWebManagerProfiles(const QString &ip, unsigned int port)
89{
90 if (webMProfilesReply.get() != nullptr)
91 return;
92
93 QString urlStr(QString("http://%1:%2/api/profiles").arg(ip).arg(port));
94 QNetworkRequest request { QUrl(urlStr) };
95
96 webMProfilesReply.reset(manager.get(request));
97 connect(webMProfilesReply.get(), SIGNAL(error(QNetworkReply::NetworkError)),
99 connect(webMProfilesReply.get(), &QNetworkReply::finished, this, &ClientManagerLite::webManagerReplyFinished);
100 setLastUsedServer(ip);
101 setLastUsedWebManagerPort(port);
102}
103
105{
106 if (webMStartProfileReply.get() != nullptr)
107 return;
108
109 QString urlStr("http://%1:%2/api/server/start/%3");
110
111 urlStr = urlStr.arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()).arg(profile);
112 QNetworkRequest request { QUrl(urlStr) };
113
114 webMStartProfileReply.reset(manager.post(request, QByteArray()));
115 connect(webMStartProfileReply.get(), SIGNAL(error(QNetworkReply::NetworkError)),
117 connect(webMStartProfileReply.get(), &QNetworkReply::finished,
119}
120
122{
123 if (webMStopProfileReply.get() != nullptr)
124 return;
125
126 QString urlStr(QString("http://%1:%2/api/server/stop").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()));
127 QNetworkRequest request { QUrl(urlStr) };
128
129 webMStopProfileReply.reset(manager.post(request, QByteArray()));
130 connect(webMStopProfileReply.get(), SIGNAL(error(QNetworkReply::NetworkError)),
132 connect(webMStopProfileReply.get(), &QNetworkReply::finished,
134}
135
137{
138 if (webMProfilesReply.get() != nullptr)
139 {
140 qWarning("Web Manager profile query error: %d", (int)code);
141 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager"));
142 webMProfilesReply.release()->deleteLater();
143 return;
144 }
145 if (webMStatusReply.get() != nullptr)
146 {
147 qWarning("Web Manager status query error: %d", (int)code);
148 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager"));
149 webMStatusReply.release()->deleteLater();
150 return;
151 }
152 if (webMStopProfileReply.get() != nullptr)
153 {
154 qWarning("Web Manager stop active profile error: %d", (int)code);
155 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager"));
156 webMStopProfileReply.release()->deleteLater();
157 return;
158 }
159 if (webMStartProfileReply.get() != nullptr)
160 {
161 qWarning("Web Manager start active profile error: %d", (int)code);
162 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager"));
163 webMStartProfileReply.release()->deleteLater();
164 return;
165 }
166}
167
169{
170 // Web Manager profile query
171 if (webMProfilesReply.get() != nullptr)
172 {
173 QByteArray responseData = webMProfilesReply->readAll();
174 QJsonDocument json = QJsonDocument::fromJson(responseData);
175
176 webMProfilesReply.release()->deleteLater();
177 if (!json.isArray())
178 {
179 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager"));
180 return;
181 }
182 QJsonArray array = json.array();
183
184 webMProfiles.clear();
185 for (int i = 0; i < array.size(); ++i)
186 {
187 if (array.at(i).isObject() && array.at(i).toObject().contains("name"))
188 {
189 webMProfiles += array.at(i).toObject()["name"].toString();
190 }
191 }
192 // Send a query for the network status
193 QString urlStr(QString("http://%1:%2/api/server/status").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()));
194 QNetworkRequest request { QUrl(urlStr) };
195
196 webMStatusReply.reset(manager.get(request));
197 connect(webMStatusReply.get(), SIGNAL(error(QNetworkReply::NetworkError)),
199 connect(webMStatusReply.get(), &QNetworkReply::finished, this, &ClientManagerLite::webManagerReplyFinished);
200 return;
201 }
202 // Web Manager status query
203 if (webMStatusReply.get() != nullptr)
204 {
205 QByteArray responseData = webMStatusReply->readAll();
206 QJsonDocument json = QJsonDocument::fromJson(responseData);
207
208 webMStatusReply.release()->deleteLater();
209 if (!json.isArray() || json.array().size() != 1 || !json.array().at(0).isObject())
210 {
211 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager"));
212 return;
213 }
214 QJsonObject object = json.array().at(0).toObject();
215
216 // Check the response
217 if (!object.contains("status") || !object.contains("active_profile"))
218 {
219 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager"));
220 return;
221 }
222 QString statusStr = object["status"].toString();
223 QString activeProfileStr = object["active_profile"].toString();
224
225 indiControlPage->setProperty("webMBrowserButtonVisible", true);
226 indiControlPage->setProperty("webMStatusTextVisible", true);
227 if (statusStr == "True")
228 {
229 // INDI Server is running (online)
230 indiControlPage->setProperty("webMStatusText", i18n("Web Manager Status: Online"));
231 indiControlPage->setProperty("webMActiveProfileText",
232 i18n("Active Profile: %1", activeProfileStr));
233 indiControlPage->setProperty("webMActiveProfileLayoutVisible", true);
234 indiControlPage->setProperty("webMProfileListVisible", false);
235 } else {
236 // INDI Server is not running (offline)
237 indiControlPage->setProperty("webMStatusText", i18n("Web Manager Status: Offline"));
238 indiControlPage->setProperty("webMActiveProfileLayoutVisible", false);
239 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles));
240 indiControlPage->setProperty("webMProfileListVisible", true);
241 }
242 return;
243 }
244 // Web Manager stop active profile
245 if (webMStopProfileReply.get() != nullptr)
246 {
247 webMStopProfileReply.release()->deleteLater();
248 indiControlPage->setProperty("webMStatusText", QString(i18n("Web Manager Status:")+' '+i18n("Offline")));
249 indiControlPage->setProperty("webMStatusTextVisible", true);
250 indiControlPage->setProperty("webMActiveProfileLayoutVisible", false);
251 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles));
252 indiControlPage->setProperty("webMProfileListVisible", true);
253 return;
254 }
255 // Web Manager start active profile
256 if (webMStartProfileReply.get() != nullptr)
257 {
258 webMStartProfileReply.release()->deleteLater();
259 // Send a query for the network status
260 QString urlStr("http://%1:%2/api/server/status");
261
262 urlStr = urlStr.arg(getLastUsedServer()).arg(getLastUsedWebManagerPort());
263 QNetworkRequest request { QUrl(urlStr) };
264
265 webMStatusReply.reset(manager.get(request));
266 connect(webMStatusReply.get(), SIGNAL(error(QNetworkReply::NetworkError)),
268 connect(webMStatusReply.get(), &QNetworkReply::finished,
270 // Connect to the server automatically
271 QMetaObject::invokeMethod(indiControlPage, "connectIndiServer");
272 return;
273 }
274}
275
276TelescopeLite *ClientManagerLite::getTelescope()
277{
278 for (auto& devInfo : m_devices)
279 {
280 if (devInfo->telescope.get())
281 {
282 return devInfo->telescope.get();
283 }
284 }
285 return nullptr;
286}
287
288void ClientManagerLite::setConnectedHost(const QString &connectedHost)
289{
290 m_connectedHost = connectedHost;
291 setConnected(m_connectedHost.size() > 0);
292
293 emit connectedHostChanged(connectedHost);
294}
295
296void ClientManagerLite::setConnected(bool connected)
297{
298 m_connected = connected;
299 emit connectedChanged(connected);
300}
301
302QString ClientManagerLite::syncLED(const QString &device, const QString &property, const QString &name)
303{
304 foreach (DeviceInfoLite *devInfo, m_devices)
305 {
306 if (devInfo->device->getDeviceName() == device)
307 {
308 INDI::Property prop = devInfo->device->getProperty(property.toLatin1());
309 if (prop)
310 {
311 IPState state = prop->getState();
312 if (!name.isEmpty())
313 {
314 ILight *lights = prop->getLight()->lp;
315 for (int i = 0; i < prop->getLight()->nlp; i++)
316 {
317 if (lights[i].name == name)
318 {
319 state = lights[i].s;
320 break;
321 }
322 if (i == prop->getLight()->nlp - 1)
323 return ""; // no Light with name "name" found so return empty string
324 }
325 }
326 switch (state)
327 {
328 case IPS_IDLE:
329 return "grey";
330 break;
331
332 case IPS_OK:
333 return "green";
334 break;
335
336 case IPS_BUSY:
337 return "yellow";
338 break;
339
340 case IPS_ALERT:
341 return "red";
342 break;
343
344 default:
345 return "grey";
346 break;
347 }
348 }
349 }
350 }
351 return "grey";
352}
353
354void ClientManagerLite::buildTextGUI(Property *property)
355{
356 auto tvp = property->getText();
357 if (!tvp)
358 return;
359
360 for (const auto &it: *tvp)
361 {
362 QString name = it.getName();
363 QString label = it.getLabel();
364 QString text = it.getText();
365 bool read = false;
366 bool write = false;
367 /*if (tp->label[0])
368 label = i18nc(libindi_strings_context, itp->label);
369
370 if (label == "(I18N_EMPTY_MESSAGE)")
371 label = itp->label;*/
372
373 if (label.isEmpty())
374 label = tvp->getName(); // #PS: it should be it.getName() ?
375 /*label = i18nc(libindi_strings_context, itp->name);
376
377 if (label == "(I18N_EMPTY_MESSAGE)")*/
378
379 //setupElementLabel();
380
381 /*if (tp->text[0])
382 text = i18nc(libindi_strings_context, tp->text);*/
383
384 switch (property->getPermission())
385 {
386 case IP_RW:
387 read = true;
388 write = true;
389 break;
390
391 case IP_RO:
392 read = true;
393 write = false;
394 break;
395
396 case IP_WO:
397 read = false;
398 write = true;
399 break;
400 }
401 emit createINDIText(property->getDeviceName(), property->getName(), label, name, text, read, write);
402 }
403}
404
405void ClientManagerLite::buildNumberGUI(Property *property)
406{
407 auto nvp = property->getNumber();
408 if (!nvp)
409 return;
410
411 //for (int i = 0; i < nvp->nnp; i++)
412 for (const auto &it: nvp)
413 {
414 bool scale = false;
415 char iNumber[MAXINDIFORMAT];
416
417 QString name = it.getName();
418 QString label = it.getLabel();
419 QString text;
420 bool read = false;
421 bool write = false;
422 /*if (tp->label[0])
423 label = i18nc(libindi_strings_context, itp->label);
424
425 if (label == "(I18N_EMPTY_MESSAGE)")
426 label = itp->label;*/
427
428 if (label.isEmpty())
429 label = np->getName();
430
431 numberFormat(iNumber, np.getFormat(), np.getValue());
432 text = iNumber;
433
434 /*label = i18nc(libindi_strings_context, itp->name);
435
436 if (label == "(I18N_EMPTY_MESSAGE)")*/
437
438 //setupElementLabel();
439
440 /*if (tp->text[0])
441 text = i18nc(libindi_strings_context, tp->text);*/
442
443 if (it.getStep() != 0 && (it.getMax() - it.getMin()) / it.getStep() <= 100)
444 scale = true;
445
446 switch (property->getPermission())
447 {
448 case IP_RW:
449 read = true;
450 write = true;
451 break;
452
453 case IP_RO:
454 read = true;
455 write = false;
456 break;
457
458 case IP_WO:
459 read = false;
460 write = true;
461 break;
462 }
463 emit createINDINumber(property->getDeviceName(), property->getName(), label, name, text, read, write,
464 scale);
465 }
466}
467
468void ClientManagerLite::buildMenuGUI(INDI::Property property)
469{
470 /*QStringList menuOptions;
471 QString oneOption;
472 int onItem=-1;*/
473 auto svp = property->getSwitch();
474
475 if (!svp)
476 return;
477
478 for (auto &it: *svp)
479 {
480 buildSwitch(false, &it, property);
481
482 /*if (tp->s == ISS_ON)
483 onItem = i;
484
485 lp = new INDI_E(this, dataProp);
486
487 lp->buildMenuItem(tp);
488
489 oneOption = i18nc(libindi_strings_context, lp->getLabel().toUtf8());
490
491 if (oneOption == "(I18N_EMPTY_MESSAGE)")
492 oneOption = lp->getLabel().toUtf8();
493
494 menuOptions.append(oneOption);
495
496 elementList.append(lp);*/
497 }
498}
499
500void ClientManagerLite::buildSwitchGUI(INDI::Property property, PGui guiType)
501{
502 auto svp = property->getSwitch();
503 bool exclusive = false;
504
505 if (!svp)
506 return;
507
508 if (guiType == PG_BUTTONS)
509 {
510 if (svp->getRule() == ISR_1OFMANY)
511 exclusive = true;
512 else
513 exclusive = false;
514 }
515 else if (guiType == PG_RADIO)
516 exclusive = false;
517
518 /*if (svp->p != IP_RO)
519 QObject::connect(groupB, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(newSwitch(QAbstractButton*)));*/
520
521 for (auto &it: *svp)
522 {
523 buildSwitch(true, &it, property, exclusive, guiType);
524 }
525}
526
527void ClientManagerLite::buildSwitch(bool buttonGroup, ISwitch *sw, INDI::Property property, bool exclusive,
528 PGui guiType)
529{
530 QString name = sw->name;
531 QString label = sw->label; //i18nc(libindi_strings_context, sw->label);
532
533 if (label == "(I18N_EMPTY_MESSAGE)")
534 label = sw->label;
535
536 if (label.isEmpty())
537 label = sw->name;
538 //label = i18nc(libindi_strings_context, sw->name);
539
540 if (label == "(I18N_EMPTY_MESSAGE)")
541 label = sw->name;
542
543 if (!buttonGroup)
544 {
545 bool isSelected = false;
546 if (sw->s == ISS_ON)
547 isSelected = true;
548 emit createINDIMenu(property->getDeviceName(), property->getName(), label, sw->name, isSelected);
549 return;
550 }
551
552 bool enabled = true;
553
554 if (sw->svp->p == IP_RO)
555 enabled = (sw->s == ISS_ON);
556
557 switch (guiType)
558 {
559 case PG_BUTTONS:
560 emit createINDIButton(property->getDeviceName(), property->getName(), label, name, true, true, exclusive,
561 sw->s == ISS_ON, enabled);
562 break;
563
564 case PG_RADIO:
565 emit createINDIRadio(property->getDeviceName(), property->getName(), label, name, true, true, exclusive,
566 sw->s == ISS_ON, enabled);
567 /*check_w = new QCheckBox(label, guiProp->getGroup()->getContainer());
568 groupB->addButton(check_w);
569
570 syncSwitch();
571
572 guiProp->addWidget(check_w);
573
574 check_w->show();
575
576 if (sw->svp->p == IP_RO)
577 check_w->setEnabled(sw->s == ISS_ON);
578
579 break;*/
580
581 default:
582 break;
583 }
584}
585
586void ClientManagerLite::buildLightGUI(INDI::Property property)
587{
588 auto lvp = property->getLight();
589
590 if (!lvp)
591 return;
592
593 for (auto &it: *lvp)
594 {
595 QString name = it.getName();
596 QString label = i18nc(libindi_strings_context, it.getLabel());
597
598 if (label == "(I18N_EMPTY_MESSAGE)")
599 label = it.getLabel();
600
601 if (label.isEmpty())
602 label = i18nc(libindi_strings_context, it.getName());
603
604 if (label == "(I18N_EMPTY_MESSAGE)")
605 label = it.getName();;
606
607 emit createINDILight(property->getDeviceName(), property->getName(), label, name);
608 }
609}
610
611/*void ClientManagerLite::buildBLOBGUI(INDI::Property property) {
612 IBLOBVectorProperty *ibp = property->getBLOB();
613
614 QString name = ibp->name;
615 QString label = i18nc(libindi_strings_context, ibp->label);
616
617 if (label == "(I18N_EMPTY_MESSAGE)")
618 label = ibp->label;
619
620 if (label.isEmpty())
621 label = i18nc(libindi_strings_context, ibp->name);
622
623 if (label == "(I18N_EMPTY_MESSAGE)")
624 label = ibp->name;
625
626 text = i18n("INDI DATA STREAM");
627
628 switch (property->getPermission())
629 {
630 case IP_RW:
631 setupElementRead(ELEMENT_READ_WIDTH);
632 setupElementWrite(ELEMENT_WRITE_WIDTH);
633 setupBrowseButton();
634 break;
635
636 case IP_RO:
637 setupElementRead(ELEMENT_FULL_WIDTH);
638 break;
639
640 case IP_WO:
641 setupElementWrite(ELEMENT_FULL_WIDTH);
642 setupBrowseButton();
643 break;
644 }
645
646 guiProp->addLayout(EHBox);
647}*/
648
649void ClientManagerLite::sendNewINDISwitch(const QString &deviceName, const QString &propName, const QString &name)
650{
651 foreach (DeviceInfoLite *devInfo, m_devices)
652 {
653 INDI::BaseDevice *device = devInfo->device;
654 if (device->getDeviceName() == deviceName)
655 {
656 auto property = device->getProperty(propName.toLatin1());
657 if (property)
658 {
659 auto svp = property->getSwitch();
660
661 if (!svp)
662 return;
663
664 auto sp = svp->findWidgetByName(name.toLatin1().constData());
665
666 if (!sp)
667 return;
668
669 if (sp->isNameMatch("CONNECT"))
670 {
671 svp->reset();
672 sp->setState(ISS_ON);
673 }
674
675 if (svp->getRule() == ISR_1OFMANY)
676 {
677 svp->reset();
678 sp->setState(ISS_ON);
679 }
680 else
681 {
682 if (svp->getRule() == ISR_ATMOST1)
683 {
684 ISState prev_state = sp->getState();
685 svp->reset();
686 sp->setState(prev_state);
687 }
688
689 sp->setState(sp->getState() == ISS_ON ? ISS_OFF : ISS_ON);
690 }
691 sendNewSwitch(svp);
692 }
693 }
694 }
695}
696
697void ClientManagerLite::sendNewINDINumber(const QString &deviceName, const QString &propName, const QString &numberName,
698 double value)
699{
700 foreach (DeviceInfoLite *devInfo, m_devices)
701 {
702 INDI::BaseDevice *device = devInfo->device;
703 if (device->getDeviceName() == deviceName)
704 {
705 auto np = device->getNumber(propName.toLatin1());
706 if (np)
707 {
708 auto n = np->findWIdgetByName(numberName.toLatin1());
709 if (n)
710 {
711 n->setValue(value);
712 sendNewNumber(np);
713 return;
714 }
715
716 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName;
717 return;
718 }
719
720 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName;
721 return;
722 }
723 }
724}
725
726void ClientManagerLite::sendNewINDIText(const QString &deviceName, const QString &propName, const QString &fieldName,
727 const QString &text)
728{
729 foreach (DeviceInfoLite *devInfo, m_devices)
730 {
731 INDI::BaseDevice *device = devInfo->device;
732 if (device->getDeviceName() == deviceName)
733 {
734 auto tp = device->getText(propName.toLatin1());
735 if (tp)
736 {
737 auto t = tp->findWidgetByName(fieldName.toLatin1());
738 if (t)
739 {
740 t.setText(text.toLatin1().data());
741 sendNewText(tp);
742 return;
743 }
744
745 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName;
746 return;
747 }
748
749 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName;
750 return;
751 }
752 }
753}
754
755void ClientManagerLite::sendNewINDISwitch(const QString &deviceName, const QString &propName, int index)
756{
757 if (index >= 0)
758 {
759 foreach (DeviceInfoLite *devInfo, m_devices)
760 {
761 INDI::BaseDevice *device = devInfo->device;
762 if (device->getDeviceName() == deviceName)
763 {
764 auto property = device->getProperty(propName.toStdString().c_str());
765 if (property)
766 {
767 auto svp = property->getSwitch();
768
769 if (!svp)
770 return;
771
772 if (index >= svp->count())
773 return;
774
775 auto sp = svp->at(index);
776
777 sp->reset();
778 sp->setState(ISS_ON);
779
780 sendNewSwitch(svp);
781 }
782 }
783 }
784 }
785}
786
788{
789 QString const dateTime = QDateTime::currentDateTime().toString("dd-MM-yyyy-hh-mm-ss");
790 QString const fileEnding = "kstars-lite-" + dateTime;
791 QDir const dir(KSPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + qAppName()).path();
792 QFileInfo const file(QString("%1/%2.jpeg").arg(dir.path()).arg(fileEnding));
793
794 QString const filename = QFileDialog::getSaveFileName(
795 QApplication::activeWindow(), i18nc("@title:window", "Save Image"), file.filePath(),
796 i18n("JPEG (*.jpeg);;JPG (*.jpg);;PNG (*.png);;BMP (*.bmp)"));
797
798 if (!filename.isEmpty())
799 {
800 if (displayImage.save(filename))
801 {
802 emit newINDIMessage("File " + filename + " was successfully saved");
803 return true;
804 }
805 }
806 emit newINDIMessage("Couldn't save file " + filename);
807 return false;
808}
809
810bool ClientManagerLite::isDeviceConnected(const QString &deviceName)
811{
812 INDI::BaseDevice *device = getDevice(deviceName.toStdString().c_str());
813
814 if (device != nullptr)
815 {
816 return device->isConnected();
817 }
818 return false;
819}
820
821void ClientManagerLite::connectNewDevice(const QString& device_name)
822{
823 connectDevice(qPrintable(device_name));
824}
825
826void ClientManagerLite::newDevice(INDI::BaseDevice *dp)
827{
828 setBLOBMode(B_ALSO, dp->getDeviceName());
829
830 QString deviceName = dp->getDeviceName();
831
832 if (deviceName.isEmpty())
833 {
834 qWarning() << "Received invalid device with empty name! Ignoring the device...";
835 return;
836 }
837
838 if (Options::verboseLogging())
839 qDebug() << "Received new device " << deviceName;
840 emit newINDIDevice(deviceName);
841
842 DeviceInfoLite *devInfo = new DeviceInfoLite(dp);
843 //Think about it!
844 //devInfo->telescope.reset(new TelescopeLite(dp));
845 m_devices.append(devInfo);
846 // Connect the device automatically
847 QTimer::singleShot(2000, [=]() { connectNewDevice(deviceName); });
848}
849
850void ClientManagerLite::removeDevice(BaseDevice *dp)
851{
852 emit removeINDIDevice(QString(dp->getDeviceName()));
853}
854
855void ClientManagerLite::newProperty(INDI::Property property)
856{
857 QString deviceName = property->getDeviceName();
858 QString name = property->getName();
859 QString groupName = property->getGroupName();
860 QString type = QString(property->getType());
861 QString label = property->getLabel();
862 DeviceInfoLite *devInfo = nullptr;
863
864 foreach (DeviceInfoLite *di, m_devices)
865 {
866 if (di->device->getDeviceName() == deviceName)
867 {
868 devInfo = di;
869 }
870 }
871
872 if (devInfo)
873 {
874 if ((!strcmp(property->getName(), "EQUATORIAL_EOD_COORD") ||
875 !strcmp(property->getName(), "EQUATORIAL_COORD") ||
876 !strcmp(property->getName(), "HORIZONTAL_COORD")))
877 {
878 devInfo->telescope.reset(new TelescopeLite(devInfo->device));
879 m_telescope = devInfo->telescope.get();
880 emit telescopeAdded(m_telescope);
881 // The connected signal must be emitted for already connected scopes otherwise
882 // the motion control page remains disabled.
883 if (devInfo->telescope->isConnected())
884 {
885 emit deviceConnected(devInfo->telescope->getDeviceName(), true);
886 emit telescopeConnected(devInfo->telescope.get());
887 }
888 }
889 }
890
891 emit newINDIProperty(deviceName, name, groupName, type, label);
892 PGui guiType;
893 switch (property->getType())
894 {
895 case INDI_SWITCH:
896 if (property->getSwitch()->r == ISR_NOFMANY)
897 guiType = PG_RADIO;
898 else if (property->getSwitch()->nsp > 4)
899 guiType = PG_MENU;
900 else
901 guiType = PG_BUTTONS;
902
903 if (guiType == PG_MENU)
904 buildMenuGUI(property);
905 else
906 buildSwitchGUI(property, guiType);
907 break;
908
909 case INDI_TEXT:
910 buildTextGUI(property);
911 break;
912 case INDI_NUMBER:
913 buildNumberGUI(property);
914 break;
915
916 case INDI_LIGHT:
917 buildLightGUI(property);
918 break;
919
920 case INDI_BLOB:
921 //buildBLOBGUI();
922 break;
923
924 default:
925 break;
926 }
927}
928
929void ClientManagerLite::removeProperty(INDI::Property property)
930{
931 if (property == nullptr)
932 return;
933
934 emit removeINDIProperty(property->getDeviceName(), property->getGroupName(), property->getName());
935
936 DeviceInfoLite *devInfo = nullptr;
937 foreach (DeviceInfoLite *di, m_devices)
938 {
939 if (di->device == property->getBaseDevice())
940 {
941 devInfo = di;
942 }
943 }
944
945 if (devInfo)
946 {
947 if ((!strcmp(property->getName(), "EQUATORIAL_EOD_COORD") || !strcmp(property->getName(), "HORIZONTAL_COORD")))
948 {
949 if (devInfo->telescope.get() != nullptr)
950 {
951 emit telescopeRemoved(devInfo->telescope.get());
952 }
953 KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed
954 }
955 }
956}
957
958void ClientManagerLite::newBLOB(IBLOB *bp)
959{
960 processBLOBasCCD(bp);
961 emit newLEDState(bp->bvp->device, bp->name);
962}
963
964bool ClientManagerLite::processBLOBasCCD(IBLOB *bp)
965{
966 enum blobType
967 {
968 BLOB_IMAGE,
969 BLOB_FITS,
970 BLOB_CR2,
971 BLOB_OTHER
972 } BType;
973
974 BType = BLOB_OTHER;
975
976 QString format(bp->format);
977 QString deviceName = bp->bvp->device;
978
979 QByteArray fmt = QString(bp->format).toLower().remove('.').toUtf8();
980
981 // If it's not FITS or an image, don't process it.
982 if ((QImageReader::supportedImageFormats().contains(fmt)))
983 BType = BLOB_IMAGE;
984 else if (format.contains("fits"))
985 BType = BLOB_FITS;
986 else if (format.contains("cr2"))
987 BType = BLOB_CR2;
988
989 if (BType == BLOB_OTHER)
990 {
991 return false;
992 }
993
994 QString currentDir = QDir(KSPaths::writableLocation(QStandardPaths::TempLocation) + "/" + qAppName()).path();
995
996 int nr, n = 0;
997 QTemporaryFile tmpFile(QDir::tempPath() + "/fitsXXXXXX");
998
999 if (currentDir.endsWith('/'))
1000 currentDir.chop(1);
1001
1002 if (QDir(currentDir).exists() == false)
1003 QDir().mkpath(currentDir);
1004
1005 QString filename(currentDir + '/');
1006
1007 if (true)
1008 {
1009 tmpFile.setAutoRemove(false);
1010
1011 if (!tmpFile.open())
1012 {
1013 qDebug() << "ISD:CCD Error: Unable to open " << filename << endl;
1014 //emit BLOBUpdated(nullptr);
1015 return false;
1016 }
1017
1018 QDataStream out(&tmpFile);
1019
1020 for (nr = 0; nr < (int)bp->size; nr += n)
1021 n = out.writeRawData(static_cast<char *>(bp->blob) + nr, bp->size - nr);
1022
1023 tmpFile.close();
1024
1025 filename = tmpFile.fileName();
1026 }
1027 else
1028 {
1029 //Add support for batch mode
1030 }
1031
1032 strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME);
1033 bp->aux2 = BLOBFilename;
1034
1035 /* Test images
1036 BType = BLOB_IMAGE;
1037 filename = "/home/polaris/Pictures/351181_0.jpeg";
1038 */
1039 /*Test CR2
1040 BType = BLOB_CR2;
1041
1042 filename = "/home/polaris/test.CR2";
1043 filename = "/storage/emulated/0/test.CR2";*/
1044
1045 if (BType == BLOB_IMAGE || BType == BLOB_CR2)
1046 {
1047 if (BType == BLOB_CR2)
1048 {
1049#ifdef Q_OS_ANDROID
1050 LibRaw RawProcessor;
1051#define OUT RawProcessor.imgdata.params
1052 OUT.user_qual = 0; // -q
1053 OUT.use_camera_wb = 1; // -w
1054 OUT.highlight = 5; // -H
1055 OUT.bright = 8; // -b
1056#undef OUT
1057
1058 QString rawFileName = filename;
1059 rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1String("/")));
1060 QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName);
1061 QTemporaryFile jpgPreview(templateName);
1062 jpgPreview.setAutoRemove(false);
1063 jpgPreview.open();
1064 jpgPreview.close();
1065 QString jpeg_filename = jpgPreview.fileName();
1066
1067 RawProcessor.open_file(filename.toLatin1());
1068 RawProcessor.unpack();
1069 RawProcessor.dcraw_process();
1070 RawProcessor.dcraw_ppm_tiff_writer(jpeg_filename.toLatin1());
1071 QFile::remove(filename);
1072 filename = jpeg_filename;
1073#else
1074 if (QStandardPaths::findExecutable("dcraw").isEmpty() == false &&
1075 QStandardPaths::findExecutable("cjpeg").isEmpty() == false)
1076 {
1077 QProcess dcraw;
1078 QString rawFileName = filename;
1079 rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1String("/")));
1080 QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName);
1081 QTemporaryFile jpgPreview(templateName);
1082 jpgPreview.setAutoRemove(false);
1083 jpgPreview.open();
1084 jpgPreview.close();
1085 QString jpeg_filename = jpgPreview.fileName();
1086
1087 QString cmd = QString("/bin/sh -c \"dcraw -c -q 0 -w -H 5 -b 8 %1 | cjpeg -quality 80 > %2\"")
1088 .arg(filename)
1089 .arg(jpeg_filename);
1090 dcraw.start(cmd);
1091 dcraw.waitForFinished();
1092 QFile::remove(filename); //Delete raw
1093 filename = jpeg_filename;
1094 }
1095 else
1096 {
1097 emit newINDIMessage(
1098 i18n("Unable to find dcraw and cjpeg. Please install the required tools to convert CR2 to JPEG."));
1099 emit newINDIBLOBImage(deviceName, false);
1100 return false;
1101 }
1102#endif
1103 }
1104
1105 displayImage.load(filename);
1106 QFile::remove(filename);
1107 KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage);
1108 emit newINDIBLOBImage(deviceName, true);
1109 return true;
1110 }
1111 else if (BType == BLOB_FITS)
1112 {
1113 displayImage = FITSData::FITSToImage(filename);
1114 QFile::remove(filename);
1115 KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage);
1116 emit newINDIBLOBImage(deviceName, true);
1117 return true;
1118 }
1119 emit newINDIBLOBImage(deviceName, false);
1120 return false;
1121}
1122
1123void ClientManagerLite::newSwitch(ISwitchVectorProperty *svp)
1124{
1125 for (int i = 0; i < svp->nsp; ++i)
1126 {
1127 ISwitch *sw = &(svp->sp[i]);
1128 if (QString(sw->name) == QString("CONNECT"))
1129 {
1130 emit deviceConnected(svp->device, sw->s == ISS_ON);
1131 if (m_telescope && m_telescope->getDeviceName() == svp->device)
1132 {
1133 if (sw->s == ISS_ON)
1134 {
1135 emit telescopeConnected(m_telescope);
1136 } else {
1137 emit telescopeDisconnected();
1138 }
1139 }
1140 }
1141 if (sw != nullptr)
1142 {
1143 emit newINDISwitch(svp->device, svp->name, sw->name, sw->s == ISS_ON);
1144 emit newLEDState(svp->device, svp->name);
1145 }
1146 }
1147}
1148
1149void ClientManagerLite::newNumber(INumberVectorProperty *nvp)
1150{
1151 if ((!strcmp(nvp->name, "EQUATORIAL_EOD_COORD") || !strcmp(nvp->name, "HORIZONTAL_COORD")))
1152 {
1153 KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed
1154 }
1155
1156 QString deviceName = nvp->device;
1157 QString propName = nvp->name;
1158 for (int i = 0; i < nvp->nnp; ++i)
1159 {
1160 INumber num = nvp->np[i];
1161 char buf[MAXINDIFORMAT];
1162 numberFormat(buf, num.format, num.value);
1163 QString numberName = num.name;
1164
1165 emit newINDINumber(deviceName, propName, numberName, QString(buf).trimmed());
1166 emit newLEDState(deviceName, propName);
1167 }
1168}
1169
1170void ClientManagerLite::newText(ITextVectorProperty *tvp)
1171{
1172 QString deviceName = tvp->device;
1173 QString propName = tvp->name;
1174 for (int i = 0; i < tvp->ntp; ++i)
1175 {
1176 IText text = tvp->tp[i];
1177 QString fieldName = text.name;
1178
1179 emit newINDIText(deviceName, propName, fieldName, text.text);
1180 emit newLEDState(deviceName, propName);
1181 }
1182}
1183
1184void ClientManagerLite::newLight(ILightVectorProperty *lvp)
1185{
1186 emit newINDILight(lvp->device, lvp->name);
1187 emit newLEDState(lvp->device, lvp->name);
1188}
1189
1190void ClientManagerLite::newMessage(INDI::BaseDevice *dp, int messageID)
1191{
1192 emit newINDIMessage(QString::fromStdString(dp->messageQueue(messageID)));
1193}
1194
1195void ClientManagerLite::serverDisconnected(int exit_code)
1196{
1197 Q_UNUSED(exit_code)
1198 clearDevices();
1199 setConnected(false);
1200}
1201
1202void ClientManagerLite::clearDevices()
1203{
1204 //Delete all created devices
1205 foreach (DeviceInfoLite *devInfo, m_devices)
1206 {
1207 if (devInfo->telescope.get() != nullptr)
1208 {
1209 emit telescopeRemoved(devInfo->telescope.get());
1210 }
1211 delete devInfo;
1212 }
1213 m_devices.clear();
1214}
1215
1216QString ClientManagerLite::getLastUsedServer()
1217{
1218 return Options::lastServer();
1219}
1220
1221void ClientManagerLite::setLastUsedServer(const QString &server)
1222{
1223 if (getLastUsedServer() != server)
1224 {
1225 Options::setLastServer(server);
1226 lastUsedServerChanged();
1227 }
1228}
1229
1230int ClientManagerLite::getLastUsedPort()
1231{
1232 return Options::lastServerPort();
1233}
1234
1235void ClientManagerLite::setLastUsedPort(int port)
1236{
1237 if (getLastUsedPort() != port)
1238 {
1239 Options::setLastServerPort(port);
1240 lastUsedPortChanged();
1241 }
1242}
1243
1244int ClientManagerLite::getLastUsedWebManagerPort()
1245{
1246 return Options::lastWebManagerPort();
1247}
1248
1249void ClientManagerLite::setLastUsedWebManagerPort(int port)
1250{
1251 if (getLastUsedWebManagerPort() != port)
1252 {
1253 Options::setLastWebManagerPort(port);
1254 lastUsedWebManagerPortChanged();
1255 }
1256}
Q_INVOKABLE QString syncLED(const QString &device, const QString &property, const QString &name="")
syncLED
Q_INVOKABLE void webManagerReplyError(QNetworkReply::NetworkError code)
Handle the errors of the async Web Manager requests.
Q_INVOKABLE void webManagerReplyFinished()
Do actions when async Web Manager requests are finished.
Q_INVOKABLE void getWebManagerProfiles(const QString &ip, unsigned int port)
Get the profiles from Web Manager.
Q_INVOKABLE bool saveDisplayImage()
saveDisplayImage
Q_INVOKABLE void startWebManagerProfile(const QString &profile)
Start an INDI server with a Web Manager profile.
Q_INVOKABLE void stopWebManagerProfile()
Stop the INDI server with an active Web Manager profile.
void addImage(const QString &id, QImage image)
Add image to the list of images with the given id.
SkyMapLite * map() const
Definition kstarslite.h:80
static KStarsLite * Instance()
Definition kstarslite.h:77
ImageProvider * imageProvider() const
Definition kstarslite.h:89
void notificationMessage(QString msg)
Once this signal is emitted, notification with text msg will appear on the screen.
device handle controlling telescope.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
QVariant read(const QByteArray &data, int versionOverride=0)
QString name(StandardAction id)
QString label(StandardShortcut id)
QWidget * activeWindow()
const char * constData() const const
char * data()
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
bool mkpath(const QString &dirPath) const const
QString path() const const
QString tempPath()
bool remove()
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString filePath() const const
bool load(QIODevice *device, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
QList< QByteArray > supportedImageFormats()
QJsonValue at(qsizetype i) const const
qsizetype size() const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isArray() const const
bool contains(QLatin1StringView key) const const
bool isObject() const const
QJsonObject toObject() const const
void append(QList< T > &&value)
void clear()
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
bool setProperty(const char *name, QVariant &&value)
void start(OpenMode mode)
bool waitForFinished(int msecs)
void setContextProperty(const QString &name, QObject *value)
void update()
QString findExecutable(const QString &executableName, const QStringList &paths)
QString arg(Args &&... args) const const
void chop(qsizetype n)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
qsizetype size() const const
QByteArray toLatin1() const const
QString toLower() const const
std::string toStdString() const const
QByteArray toUtf8() const const
QTextStream & endl(QTextStream &stream)
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 11:51:34 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.