Kstars

profileeditor.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "profileeditor.h"
8
9#include "geolocation.h"
10#include "kstarsdata.h"
11#include "ksnotification.h"
12#include "Options.h"
13#include "guide/guide.h"
14#include "indi/driverinfo.h"
15#include "indi/drivermanager.h"
16#include "profilescriptdialog.h"
17#include "ui_indihub.h"
18
19#include "ekos_debug.h"
20
21#include <QNetworkInterface>
22#include <QListView>
23#include <QDesktopServices>
24
25ProfileEditorUI::ProfileEditorUI(QWidget *p) : QFrame(p)
26{
27 setupUi(this);
28}
29
30ProfileEditor::ProfileEditor(QWidget *w) : QDialog(w)
31{
32 setObjectName("profileEditorDialog");
33#ifdef Q_OS_MACOS
34 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
35#endif
36 ui = new ProfileEditorUI(this);
37
38 pi = nullptr;
39
40 // Initialize device map
41 m_Devices =
42 {
43 {"Mount", {ui->mountCombo, new QStandardItemModel(this), QString(), {KSTARS_TELESCOPE}}},
44 {"CCD", {ui->ccdCombo, new QStandardItemModel(this), QString(), {KSTARS_CCD}}},
45 {"Guider", {ui->guiderCombo, new QStandardItemModel(this), QString(), {KSTARS_CCD}}},
46 {"Focuser", {ui->focuserCombo, new QStandardItemModel(this), QString(), {KSTARS_FOCUSER}}},
47 {"Filter", {ui->filterCombo, new QStandardItemModel(this), QString(), {KSTARS_FILTER}}},
48 {"AO", {ui->AOCombo, new QStandardItemModel(this), QString(), {KSTARS_ADAPTIVE_OPTICS}}},
49 {"Dome", {ui->domeCombo, new QStandardItemModel(this), QString(), {KSTARS_DOME}}},
50 {"Weather", {ui->weatherCombo, new QStandardItemModel(this), QString(), {KSTARS_WEATHER}}},
51 {
52 "Aux1", {
53 ui->aux1Combo, new QStandardItemModel(this), QString(),
54 {KSTARS_AUXILIARY, KSTARS_CCD, KSTARS_FOCUSER, KSTARS_FILTER, KSTARS_WEATHER, KSTARS_SPECTROGRAPHS, KSTARS_DETECTORS}
55 }
56 },
57 {
58 "Aux2", {
59 ui->aux2Combo, new QStandardItemModel(this), QString(),
60 {KSTARS_AUXILIARY, KSTARS_CCD, KSTARS_FOCUSER, KSTARS_FILTER, KSTARS_WEATHER, KSTARS_SPECTROGRAPHS, KSTARS_DETECTORS}
61 }
62 },
63 {
64 "Aux3", {
65 ui->aux3Combo, new QStandardItemModel(this), QString(),
66 {KSTARS_AUXILIARY, KSTARS_CCD, KSTARS_FOCUSER, KSTARS_FILTER, KSTARS_WEATHER, KSTARS_SPECTROGRAPHS, KSTARS_DETECTORS}
67 }
68 },
69 {
70 "Aux4", {
71 ui->aux4Combo, new QStandardItemModel(this), QString(),
72 {KSTARS_AUXILIARY, KSTARS_CCD, KSTARS_FOCUSER, KSTARS_FILTER, KSTARS_WEATHER, KSTARS_SPECTROGRAPHS, KSTARS_DETECTORS}
73 }
74 }
75 };
76
77 QVBoxLayout *mainLayout = new QVBoxLayout;
78 mainLayout->addWidget(ui);
79 setLayout(mainLayout);
80
81 setWindowTitle(i18nc("@title:window", "Profile Editor"));
82
83 // Create button box and link it to save and reject functions
85
86 buttonBox->setObjectName("dialogButtons");
87 mainLayout->addWidget(buttonBox);
88 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
89 connect(buttonBox, SIGNAL(accepted()), this, SLOT(saveProfile()));
90
91 connect(ui->scriptsB, &QPushButton::clicked, this, &ProfileEditor::executeScriptEditor);
92
93 connect(ui->openWebManagerB, &QPushButton::clicked, this, [this]()
94 {
95 QUrl url(QString("http://" + ui->remoteHost->text() + ":8624"));
96 QDesktopServices::openUrl(url);
97 });
98
99 connect(ui->INDIWebManagerCheck, &QCheckBox::toggled, this, [&](bool enabled)
100 {
101 ui->openWebManagerB->setEnabled(enabled);
102 ui->remoteDrivers->setEnabled(enabled || ui->localMode->isChecked());
103 ui->scriptsB->setEnabled(enabled || ui->localMode->isChecked());
104 });
105
106 connect(ui->guideTypeCombo, SIGNAL(activated(int)), this, SLOT(updateGuiderSelection(int)));
107
108 connect(ui->scanB, &QPushButton::clicked, this, &ProfileEditor::scanNetwork);
109
110#ifdef Q_OS_WIN
111 ui->remoteMode->setChecked(true);
112 ui->localMode->setEnabled(false);
113 setRemoteMode(true);
114#else
115 connect(ui->remoteMode, SIGNAL(toggled(bool)), this, SLOT(setRemoteMode(bool)));
116#endif
117
118 connect(ui->indihubB, &QPushButton::clicked, this, &ProfileEditor::showINDIHub);
119
120 // Load all drivers
121 loadDrivers();
122
123 // Shared tooltips
124 ui->remoteDrivers->setToolTip(ui->remoteDriversLabel->toolTip());
125 ui->aux1Combo->setToolTip(ui->aux1Label->toolTip());
126 ui->aux2Combo->setToolTip(ui->aux2Label->toolTip());
127 ui->aux3Combo->setToolTip(ui->aux3Label->toolTip());
128 ui->aux4Combo->setToolTip(ui->aux4Label->toolTip());
129 ui->filterCombo->setToolTip(ui->filterLabel->toolTip());
130 ui->AOCombo->setToolTip(ui->AOLabel->toolTip());
131 ui->domeCombo->setToolTip(ui->domeLabel->toolTip());
132 ui->weatherCombo->setToolTip(ui->weatherLabel->toolTip());
133 ui->localMode->setToolTip(ui->modeLabel->toolTip());
134 ui->remoteMode->setToolTip(ui->modeLabel->toolTip());
135 ui->remoteHostLabel->setToolTip(ui->remoteHost->toolTip());
136 ui->remotePortLabel->setToolTip(ui->remotePort->toolTip());
137 ui->externalGuidePortLabel->setToolTip(ui->externalGuidePort->toolTip());
138 ui->INDIWebManagerPortLabel->setToolTip(ui->INDIWebManagerPort->toolTip());
139 ui->guideTypeCombo->setToolTip(ui->guidingTypeLabel->toolTip());
140 ui->externalGuideHostLabel->setToolTip(ui->externalGuideHost->toolTip());
141}
142
143void ProfileEditor::saveProfile()
144{
145 bool newProfile = (pi.isNull());
146
147 if (ui->profileIN->text().isEmpty())
148 {
149 KSNotification::error(i18n("Cannot save an empty profile."));
150 return;
151 }
152
153 if (newProfile)
154 {
155 QList<QSharedPointer<ProfileInfo>> existingProfiles;
156 KStarsData::Instance()->userdb()->GetAllProfiles(existingProfiles);
157 for (auto &profileInfo : existingProfiles)
158 {
159 if (ui->profileIN->text() == profileInfo->name)
160 {
161 KSNotification::error(i18n("Profile name already exists."));
162 return;
163 }
164 }
165 int id = KStarsData::Instance()->userdb()->AddProfile(ui->profileIN->text());
166 pi.reset(new ProfileInfo(id, ui->profileIN->text()));
167 }
168 else
169 pi->name = ui->profileIN->text();
170
171 // Local Mode
172 if (ui->localMode->isChecked())
173 {
174 pi->host.clear();
175 pi->port = -1;
176 pi->INDIWebManagerPort = -1;
177 //pi->customDrivers = ui->customDriversIN->text();
178 }
179 // Remote Mode
180 else
181 {
182 pi->host = ui->remoteHost->text().trimmed();
183 pi->port = ui->remotePort->text().toInt();
184 if (ui->INDIWebManagerCheck->isChecked())
185 pi->INDIWebManagerPort = ui->INDIWebManagerPort->text().toInt();
186 else
187 pi->INDIWebManagerPort = -1;
188 //pi->customDrivers.clear();
189 }
190
191 // City Info
192 if (ui->loadSiteCheck->isEnabled() && ui->loadSiteCheck->isChecked())
193 {
194 pi->city = KStarsData::Instance()->geo()->name();
195 pi->province = KStarsData::Instance()->geo()->province();
196 pi->country = KStarsData::Instance()->geo()->country();
197 }
198 else
199 {
200 pi->city.clear();
201 pi->province.clear();
202 pi->country.clear();
203 }
204
205 pi->indihub = m_INDIHub;
206
207 // Auto Connect
208 pi->autoConnect = ui->autoConnectCheck->isChecked();
209 // Port Selector
210 pi->portSelector = ui->portSelectorCheck->isChecked();
211
212 // Guider Type
213 pi->guidertype = ui->guideTypeCombo->currentIndex();
214 if (pi->guidertype != Ekos::Guide::GUIDE_INTERNAL)
215 {
216 pi->guiderhost = ui->externalGuideHost->text();
217 pi->guiderport = ui->externalGuidePort->text().toInt();
218
219 if (pi->guidertype == Ekos::Guide::GUIDE_PHD2)
220 {
221 Options::setPHD2Host(pi->guiderhost);
222 Options::setPHD2Port(pi->guiderport);
223 }
224 else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER)
225 {
226 Options::setLinGuiderHost(pi->guiderhost);
227 Options::setLinGuiderPort(pi->guiderport);
228 }
229 }
230
231 if (ui->mountCombo->currentText().isEmpty() || ui->mountCombo->currentText() == "--")
232 pi->drivers.remove("Mount");
233 else
234 pi->drivers["Mount"] = ui->mountCombo->currentText();
235
236 if (ui->ccdCombo->currentText().isEmpty() || ui->ccdCombo->currentText() == "--")
237 pi->drivers.remove("CCD");
238 else
239 pi->drivers["CCD"] = ui->ccdCombo->currentText();
240
241 if (ui->guiderCombo->currentText().isEmpty() || ui->guiderCombo->currentText() == "--")
242 pi->drivers.remove("Guider");
243 else
244 pi->drivers["Guider"] = ui->guiderCombo->currentText();
245
246 if (ui->focuserCombo->currentText().isEmpty() || ui->focuserCombo->currentText() == "--")
247 pi->drivers.remove("Focuser");
248 else
249 pi->drivers["Focuser"] = ui->focuserCombo->currentText();
250
251 if (ui->filterCombo->currentText().isEmpty() || ui->filterCombo->currentText() == "--")
252 pi->drivers.remove("Filter");
253 else
254 pi->drivers["Filter"] = ui->filterCombo->currentText();
255
256 if (ui->AOCombo->currentText().isEmpty() || ui->AOCombo->currentText() == "--")
257 pi->drivers.remove("AO");
258 else
259 pi->drivers["AO"] = ui->AOCombo->currentText();
260
261 if (ui->domeCombo->currentText().isEmpty() || ui->domeCombo->currentText() == "--")
262 pi->drivers.remove("Dome");
263 else
264 pi->drivers["Dome"] = ui->domeCombo->currentText();
265
266 if (ui->weatherCombo->currentText().isEmpty() || ui->weatherCombo->currentText() == "--")
267 pi->drivers.remove("Weather");
268 else
269 pi->drivers["Weather"] = ui->weatherCombo->currentText();
270
271 if (ui->aux1Combo->currentText().isEmpty() || ui->aux1Combo->currentText() == "--")
272 pi->drivers.remove("Aux1");
273 else
274 pi->drivers["Aux1"] = ui->aux1Combo->currentText();
275
276 if (ui->aux2Combo->currentText().isEmpty() || ui->aux2Combo->currentText() == "--")
277 pi->drivers.remove("Aux2");
278 else
279 pi->drivers["Aux2"] = ui->aux2Combo->currentText();
280
281 if (ui->aux3Combo->currentText().isEmpty() || ui->aux3Combo->currentText() == "--")
282 pi->drivers.remove("Aux3");
283 else
284 pi->drivers["Aux3"] = ui->aux3Combo->currentText();
285
286 if (ui->aux4Combo->currentText().isEmpty() || ui->aux4Combo->currentText() == "--")
287 pi->drivers.remove("Aux4");
288 else
289 pi->drivers["Aux4"] = ui->aux4Combo->currentText();
290
291 pi->remotedrivers = ui->remoteDrivers->text();
292
293 KStarsData::Instance()->userdb()->SaveProfile(pi);
294
295 // Ekos manager will reload and new profiles will be created
296 if (newProfile)
297 pi.clear();
298
299 accept();
300}
301
302void ProfileEditor::setRemoteMode(bool enable)
303{
304 //This is needed to reload the drivers because some may not be available locally
305 loadDrivers();
306
307 ui->remoteHost->setEnabled(enable);
308 ui->remoteHostLabel->setEnabled(enable);
309 ui->remotePort->setEnabled(enable);
310 ui->remotePortLabel->setEnabled(enable);
311
312 //ui->customLabel->setEnabled(!enable);
313 //ui->customDriversIN->setEnabled(!enable);
314
315 ui->mountCombo->setEditable(enable);
316 ui->ccdCombo->setEditable(enable);
317 ui->guiderCombo->setEditable(enable);
318 ui->focuserCombo->setEditable(enable);
319 ui->filterCombo->setEditable(enable);
320 ui->AOCombo->setEditable(enable);
321 ui->domeCombo->setEditable(enable);
322 ui->weatherCombo->setEditable(enable);
323 ui->aux1Combo->setEditable(enable);
324 ui->aux2Combo->setEditable(enable);
325 ui->aux3Combo->setEditable(enable);
326 ui->aux4Combo->setEditable(enable);
327
328 ui->remoteDrivers->setEnabled(!enable);
329
330 ui->loadSiteCheck->setEnabled(enable);
331
332 ui->INDIWebManagerCheck->setEnabled(enable);
333 if (enable == false)
334 ui->INDIWebManagerCheck->setChecked(false);
335 ui->INDIWebManagerPort->setEnabled(enable);
336
337 ui->scriptsB->setEnabled(!enable || ui->INDIWebManagerCheck->isChecked());
338}
339
340void ProfileEditor::setPi(const QSharedPointer<ProfileInfo> &newProfile)
341{
342 pi = newProfile;
343
344 ui->profileIN->setText(pi->name);
345
346 ui->loadSiteCheck->setChecked(!pi->city.isEmpty());
347 ui->autoConnectCheck->setChecked(pi->autoConnect);
348 ui->portSelectorCheck->setChecked(pi->portSelector);
349
350 if (pi->city.isEmpty() == false)
351 {
352 if (pi->province.isEmpty())
353 ui->loadSiteCheck->setText(ui->loadSiteCheck->text() + QString(" (%1, %2)").arg(pi->country, pi->city));
354 else
355 ui->loadSiteCheck->setText(ui->loadSiteCheck->text() +
356 QString(" (%1, %2, %3)").arg(pi->country, pi->province, pi->city));
357 }
358
359 if (pi->host.isEmpty() == false)
360 {
361 ui->remoteHost->setText(pi->host);
362 ui->remotePort->setText(QString::number(pi->port));
363
364 ui->remoteMode->setChecked(true);
365
366 if (pi->INDIWebManagerPort > 0)
367 {
368 ui->INDIWebManagerCheck->setChecked(true);
369 ui->INDIWebManagerPort->setText(QString::number(pi->INDIWebManagerPort));
370 }
371 else
372 {
373 ui->INDIWebManagerCheck->setChecked(false);
374 ui->INDIWebManagerPort->setText("8624");
375 }
376 }
377
378 if (pi->remotedrivers.isEmpty() == false)
379 ui->remoteDrivers->setText(pi->remotedrivers);
380
381 ui->guideTypeCombo->setCurrentIndex(pi->guidertype);
382 updateGuiderSelection(ui->guideTypeCombo->currentIndex());
383 if (pi->guidertype == Ekos::Guide::GUIDE_PHD2)
384 {
385 Options::setPHD2Host(pi->guiderhost);
386 Options::setPHD2Port(pi->guiderport);
387 }
388 else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER)
389 {
390 Options::setLinGuiderHost(pi->guiderhost);
391 Options::setLinGuiderPort(pi->guiderport);
392 }
393
394 QMapIterator<QString, QString> i(pi->drivers);
395
396 while (i.hasNext())
397 {
398 int row = 0;
399 i.next();
400
401 QString key = i.key();
402 QString value = i.value();
403
404 if (key == "Mount")
405 {
406 // If driver doesn't exist, let's add it to the list
407 if ((row = ui->mountCombo->findText(value)) == -1)
408 {
409 ui->mountCombo->addItem(value);
410 row = ui->mountCombo->count() - 1;
411 }
412
413 // Set index to our driver
414 ui->mountCombo->setCurrentIndex(row);
415 }
416 else if (key == "CCD")
417 {
418 if ((row = ui->ccdCombo->findText(value)) == -1)
419 {
420 ui->ccdCombo->addItem(value);
421 row = ui->ccdCombo->count() - 1;
422 }
423
424 ui->ccdCombo->setCurrentIndex(row);
425 }
426 else if (key == "Guider")
427 {
428 if ((row = ui->guiderCombo->findText(value)) == -1)
429 {
430 ui->guiderCombo->addItem(value);
431 row = ui->guiderCombo->count() - 1;
432 }
433
434 ui->guiderCombo->setCurrentIndex(row);
435 }
436 else if (key == "Focuser")
437 {
438 if ((row = ui->focuserCombo->findText(value)) == -1)
439 {
440 ui->focuserCombo->addItem(value);
441 row = ui->focuserCombo->count() - 1;
442 }
443
444 ui->focuserCombo->setCurrentIndex(row);
445 }
446 else if (key == "Filter")
447 {
448 if ((row = ui->filterCombo->findText(value)) == -1)
449 {
450 ui->filterCombo->addItem(value);
451 row = ui->filterCombo->count() - 1;
452 }
453
454 ui->filterCombo->setCurrentIndex(row);
455 }
456 else if (key == "AO")
457 {
458 if ((row = ui->AOCombo->findText(value)) == -1)
459 {
460 ui->AOCombo->addItem(value);
461 row = ui->AOCombo->count() - 1;
462 }
463
464 ui->AOCombo->setCurrentIndex(row);
465 }
466 else if (key == "Dome")
467 {
468 if ((row = ui->domeCombo->findText(value)) == -1)
469 {
470 ui->domeCombo->addItem(value);
471 row = ui->domeCombo->count() - 1;
472 }
473
474 ui->domeCombo->setCurrentIndex(row);
475 }
476 else if (key == "Weather")
477 {
478 if ((row = ui->weatherCombo->findText(value)) == -1)
479 {
480 ui->weatherCombo->addItem(value);
481 row = ui->weatherCombo->count() - 1;
482 }
483
484 ui->weatherCombo->setCurrentIndex(row);
485 }
486 else if (key == "Aux1")
487 {
488 if ((row = ui->aux1Combo->findText(value)) == -1)
489 {
490 ui->aux1Combo->addItem(value);
491 row = ui->aux1Combo->count() - 1;
492 }
493
494 ui->aux1Combo->setCurrentIndex(row);
495 }
496 else if (key == "Aux2")
497 {
498 if ((row = ui->aux2Combo->findText(value)) == -1)
499 {
500 ui->aux2Combo->addItem(value);
501 row = ui->aux2Combo->count() - 1;
502 }
503
504 ui->aux2Combo->setCurrentIndex(row);
505 }
506 else if (key == "Aux3")
507 {
508 if ((row = ui->aux3Combo->findText(value)) == -1)
509 {
510 ui->aux3Combo->addItem(value);
511 row = ui->aux3Combo->count() - 1;
512 }
513
514 ui->aux3Combo->setCurrentIndex(row);
515 }
516 else if (key == "Aux4")
517 {
518 if ((row = ui->aux4Combo->findText(value)) == -1)
519 {
520 ui->aux4Combo->addItem(value);
521 row = ui->aux4Combo->count() - 1;
522 }
523
524 ui->aux4Combo->setCurrentIndex(row);
525 }
526 }
527
528 m_INDIHub = pi->indihub;
529}
530
531QString ProfileEditor::getTooltip(const QSharedPointer<DriverInfo> &driver)
532{
533 bool locallyAvailable = false;
534 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE"))
535 locallyAvailable = driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool();
536 QString toolTipText;
537 if (!locallyAvailable)
538 toolTipText = i18n(
539 "<nobr>Available as <b>Remote</b> Driver. To use locally, install the corresponding driver.<nobr/>");
540 else
541 toolTipText = i18n("<nobr><b>Label</b>: %1 &#9473; <b>Driver</b>: %2 &#9473; <b>Exec</b>: %3<nobr/>",
542 driver->getLabel(), driver->getName(), driver->getExecutable());
543
544 return toolTipText;
545}
546
547void ProfileEditor::loadDrivers()
548{
549 const bool isLocal = ui->localMode->isChecked();
550
551 // Save current selections and update models
552 for (auto &device : m_Devices)
553 {
554 device.selectedDriver = device.combo->currentText();
555 delete device.model;
556 device.model = new QStandardItemModel(this);
557 populateManufacturerCombo(device.model, device.combo, device.selectedDriver, isLocal, device.families);
558 }
559}
560
561void ProfileEditor::setProfileName(const QString &name)
562{
563 ui->profileIN->setText(name);
564}
565
566void ProfileEditor::setAuxDrivers(const QStringList &aux)
567{
568 QStringList auxList(aux);
569
570 if (auxList.isEmpty())
571 return;
572 ui->aux1Combo->setCurrentText(auxList.first());
573 auxList.removeFirst();
574
575 if (auxList.isEmpty())
576 return;
577 ui->aux2Combo->setCurrentText(auxList.first());
578 auxList.removeFirst();
579
580 if (auxList.isEmpty())
581 return;
582 ui->aux3Combo->setCurrentText(auxList.first());
583 auxList.removeFirst();
584
585 if (auxList.isEmpty())
586 return;
587 ui->aux4Combo->setCurrentText(auxList.first());
588}
589
590void ProfileEditor::setHostPort(const QString &host, const QString &port)
591{
592 ui->remoteMode->setChecked(true);
593 ui->remoteHost->setText(host);
594 ui->remotePort->setText(port);
595}
596
597void ProfileEditor::setWebManager(bool enabled, const QString &port)
598{
599 ui->INDIWebManagerCheck->setChecked(enabled);
600 ui->INDIWebManagerPort->setText(port);
601}
602
603void ProfileEditor::setGuiderType(int type)
604{
605 ui->guideTypeCombo->setCurrentIndex(type);
606 if (type != Ekos::Guide::GUIDE_INTERNAL)
607 {
608 ui->externalGuideHostLabel->setEnabled(true);
609 ui->externalGuideHost->setEnabled(true);
610 ui->externalGuidePortLabel->setEnabled(true);
611 ui->externalGuidePort->setEnabled(true);
612 }
613}
614
615void ProfileEditor::setConnectionOptionsEnabled(bool enable)
616{
617 // Enable or disable connection related options
618 ui->modeLabel->setEnabled(enable);
619 ui->localMode->setEnabled(enable);
620 ui->remoteMode->setEnabled(enable);
621 ui->remoteHostLabel->setEnabled(enable);
622 ui->remoteHost->setEnabled(enable);
623 ui->remotePortLabel->setEnabled(enable);
624 ui->remotePort->setEnabled(enable);
625 ui->INDIWebManagerCheck->setEnabled(enable);
626 ui->INDIWebManagerPort->setEnabled(enable);
627 ui->INDIWebManagerPortLabel->setEnabled(enable);
628 ui->guidingTypeLabel->setEnabled(enable);
629 ui->guideTypeCombo->setEnabled(enable);
630 ui->remoteDrivers->setEnabled(enable);
631
632 updateGuiderSelection(ui->guideTypeCombo->currentIndex());
633
634 if (enable == false)
635 ui->mountCombo->setFocus();
636}
637
638void ProfileEditor::updateGuiderSelection(int id)
639{
640
641 if (id == Ekos::Guide::GUIDE_INTERNAL)
642 {
643 ui->externalGuideHost->setText("localhost");
644 ui->externalGuidePort->clear();
645
646 ui->externalGuideHost->setEnabled(false);
647 ui->externalGuideHostLabel->setEnabled(false);
648 ui->externalGuidePort->setEnabled(false);
649 ui->externalGuidePortLabel->setEnabled(false);
650 return;
651 }
652
653 QString host;
654 int port = -1;
655
656 ui->externalGuideHost->setEnabled(true);
657 ui->externalGuideHostLabel->setEnabled(true);
658 ui->externalGuidePort->setEnabled(true);
659 ui->externalGuidePortLabel->setEnabled(true);
660
661 if (pi && pi->guidertype == id)
662 {
663 host = pi->guiderhost;
664 port = pi->guiderport;
665 }
666
667 if (id == Ekos::Guide::GUIDE_PHD2)
668 {
669 if (host.isEmpty())
670 host = Options::pHD2Host();
671 if (port < 0)
672 port = Options::pHD2Port();
673 }
674 else if (id == Ekos::Guide::GUIDE_LINGUIDER)
675 {
676 if (host.isEmpty())
677 host = Options::linGuiderHost();
678 if (port < 0)
679 port = Options::linGuiderPort();
680 }
681
682 ui->externalGuideHost->setText(host);
683 ui->externalGuidePort->setText(QString::number(port));
684
685}
686
687void ProfileEditor::setSettings(const QJsonObject &profile)
688{
689 ui->profileIN->setText(profile["name"].toString());
690 ui->autoConnectCheck->setChecked(profile["auto_connect"].toBool(true));
691 ui->portSelectorCheck->setChecked(profile["port_selector"].toBool(false));
692 ui->localMode->setChecked(profile["mode"].toString() == "local");
693 ui->remoteMode->setChecked(profile["mode"].toString() == "remote");
694 ui->remoteHost->setText(profile["remote_host"].toString("localhost"));
695 ui->remotePort->setText(profile["remote_port"].toString("7624"));
696 ui->guideTypeCombo->setCurrentText(profile["guiding"].toString(i18n("Internal")));
697 ui->externalGuideHost->setText(profile["remote_guiding_host"].toString(("localhost")));
698 ui->externalGuidePort->setText(profile["remote_guiding_port"].toString("4400"));
699 ui->INDIWebManagerCheck->setChecked(profile["use_web_manager"].toBool());
700 ui->remoteDrivers->setText(profile["remote"].toString(ui->remoteDrivers->text()));
701
702 m_INDIHub = profile["indihub"].toInt(m_INDIHub);
703
704 const bool isLocal = ui->localMode->isChecked();
705
706 // Helper function to set combo box value
707 auto setComboValue = [this, isLocal](QComboBox * combo, const QString & value)
708 {
709 if (value.isEmpty() || value == "--")
710 {
711 combo->setCurrentIndex(0);
712 return;
713 }
714
715 // For local mode, search through manufacturer groups
716 if (isLocal)
717 {
718 QAbstractItemModel *model = combo->model();
719 // Search through manufacturer groups
720 for (int i = 0; i < model->rowCount(); i++)
721 {
722 QModelIndex parent = model->index(i, 0);
723 // Skip non-manufacturer items (like --)
724 if (model->hasChildren(parent))
725 {
726 // Search through drivers under this manufacturer
727 for (int j = 0; j < model->rowCount(parent); j++)
728 {
729 QModelIndex child = model->index(j, 0, parent);
730 if (model->data(child).toString() == value)
731 {
732 // Set the manufacturer as root and select the driver
734 combo->setCurrentIndex(j);
735 combo->setRootModelIndex(QModelIndex());
736 return;
737 }
738 }
739 }
740 }
741 }
742 else
743 {
744 // For remote mode, first try to find the driver
745 int index = combo->findText(value);
746 if (index >= 0)
747 combo->setCurrentIndex(index);
748 else
749 {
750 // If not found, add it
751 combo->addItem(value);
752 combo->setCurrentIndex(combo->count() - 1);
753 }
754 }
755 };
756
757 // Set all device combos
758 setComboValue(ui->mountCombo, profile["mount"].toString("--"));
759 setComboValue(ui->ccdCombo, profile["ccd"].toString("--"));
760 setComboValue(ui->guiderCombo, profile["guider"].toString("--"));
761 setComboValue(ui->focuserCombo, profile["focuser"].toString("--"));
762 setComboValue(ui->filterCombo, profile["filter"].toString("--"));
763 setComboValue(ui->AOCombo, profile["ao"].toString("--"));
764 setComboValue(ui->domeCombo, profile["dome"].toString("--"));
765 setComboValue(ui->weatherCombo, profile["weather"].toString("--"));
766 setComboValue(ui->aux1Combo, profile["aux1"].toString("--"));
767 setComboValue(ui->aux2Combo, profile["aux2"].toString("--"));
768 setComboValue(ui->aux3Combo, profile["aux3"].toString("--"));
769 setComboValue(ui->aux4Combo, profile["aux4"].toString("--"));
770}
771
772void ProfileEditor::scanNetwork()
773{
774 delete (m_ProgressDialog);
775 m_ProgressDialog = new QProgressDialog(this);
776 m_ProgressDialog->setWindowTitle(i18nc("@title:window", "Scanning Network"));
777 m_ProgressDialog->setLabelText(i18n("Scanning network for INDI Web Managers..."));
778 connect(m_ProgressDialog, &QProgressDialog::canceled, this, [this]()
779 {
780 m_CancelScan = true;
781 clearAllRequests();
782 });
783 m_ProgressDialog->setMinimum(0);
784 m_ProgressDialog->setMaximum(0);
785 m_ProgressDialog->show();
786 m_ProgressDialog->raise();
787
788 m_CancelScan = false;
789
790 QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
791 std::sort(addresses.begin(), addresses.end(), [](const QHostAddress & a, const QHostAddress & b) -> bool
792 { return a.toString() < b.toString();});
793
794 for(QHostAddress address : addresses)
795 {
796 if (address.isLoopback() || address.protocol() & QAbstractSocket::IPv6Protocol)
797 continue;
798
799 QString ipv4 = address.toString();
800
801 if (ipv4.startsWith("10.250"))
802 {
803 scanIP("10.250.250.1");
804 }
805 else
806 {
807 QString prefixIP = ipv4.remove(ipv4.lastIndexOf("."), 10);
808 // Blind search all over subnet
809 // TODO better subnet detection instead of assuming it finishes at 254
810 for (int i = 1; i <= 254; i++)
811 {
812 scanIP(prefixIP + "." + QString::number(i));
813 }
814 }
815 }
816
817}
818
819void ProfileEditor::scanIP(const QString &ip)
820{
821 QUrl url(QString("http://%1:8624/api/server/status").arg(ip));
822
823 qCDebug(KSTARS_EKOS) << "Scanning" << url;
824
825 QNetworkReply *response = m_Manager.get(QNetworkRequest(url));
826 m_Replies.append(response);
827 connect(response, &QNetworkReply::finished, [this, response, ip]()
828 {
829 m_Replies.removeOne(response);
830 response->deleteLater();
831 if (m_CancelScan)
832 return;
833 if (response->error() == QNetworkReply::NoError)
834 {
835 clearAllRequests();
836 m_ProgressDialog->close();
837 ui->remoteHost->setText(ip);
838
839 qCDebug(KSTARS_EKOS) << "Found Web Manager server at" << ip;
840
841 KSNotification::info(i18n("Found INDI Web Manager at %1", ip));
842 }
843 });
844}
845
846void ProfileEditor::clearAllRequests()
847{
848 for (QNetworkReply *oneReply : m_Replies)
849 {
850 oneReply->abort();
851 oneReply->deleteLater();
852 }
853
854 m_Replies.clear();
855}
856
857void ProfileEditor::showINDIHub()
858{
859 QDialog hub;
860 Ui::INDIHub indihub;
861 indihub.setupUi(&hub);
862
863 indihub.modeButtonGroup->setId(indihub.offR, 0);
864 indihub.modeButtonGroup->setId(indihub.solorR, 1);
865 indihub.modeButtonGroup->setId(indihub.shareR, 2);
866 indihub.modeButtonGroup->setId(indihub.roboticR, 3);
867
868 indihub.logoLabel->setPixmap(QIcon(":/icons/indihub_logo.svg").pixmap(QSize(128, 128)));
869
870 indihub.modeButtonGroup->button(m_INDIHub)->setChecked(true);
871 connect(indihub.closeB, &QPushButton::clicked, &hub, &QDialog::close);
872
873 hub.exec();
874
875 m_INDIHub = indihub.modeButtonGroup->checkedId();
876}
877
878void ProfileEditor::populateManufacturerCombo(QStandardItemModel *model, QComboBox *combo, const QString &selectedDriver,
879 bool isLocal, const QList<DeviceFamily> &families)
880{
881 if (isLocal)
882 {
883 QStandardItem *selectedItem = nullptr;
884 model->appendRow(new QStandardItem("--"));
885 for (QSharedPointer<DriverInfo>driver : DriverManager::Instance()->getDrivers())
886 {
887 if (!families.contains(driver->getType()))
888 continue;
889
890 QString manufacturer = driver->manufacturer();
891 QList<QStandardItem*> manufacturers = model->findItems(manufacturer);
892
893 QStandardItem *parentItem = nullptr;
894 if (model->findItems(manufacturer).empty())
895 {
896 parentItem = new QStandardItem(manufacturer);
897 parentItem->setSelectable(false);
898 model->appendRow(parentItem);
899 }
900 else
901 {
902 parentItem = manufacturers.first();
903 }
904
905 QStandardItem *item = new QStandardItem(driver->getLabel());
906 item->setData(getTooltip(driver), Qt::ToolTipRole);
907 parentItem->appendRow(item);
908 if (selectedDriver == driver->getLabel())
909 selectedItem = item;
910 }
911 QTreeView *view = new QTreeView(this);
912 view->setModel(model);
914 combo->setView(view);
915 combo->setModel(model);
916 if (selectedItem)
917 {
918 // JM: Only way to make it the QTreeView sets the current index
919 // in the combo way
920
921 QModelIndex index = model->indexFromItem(selectedItem);
922
923 // First set current index to the child
924 combo->setRootModelIndex(index.parent());
925 combo->setModelColumn(index.column());
926 combo->setCurrentIndex(index.row());
927
928 // Now reset
929 combo->setRootModelIndex(QModelIndex());
930 view->setCurrentIndex(index);
931 }
932 }
933 else
934 {
935 QIcon remoteIcon = QIcon::fromTheme("network-modem");
936 combo->setView(new QListView(this));
937 model->appendRow(new QStandardItem("--"));
938 QIcon icon;
939 for (QSharedPointer<DriverInfo>driver : DriverManager::Instance()->getDrivers())
940 {
941 if (!families.contains(driver->getType()))
942 continue;
943
944 bool locallyAvailable = false;
945 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE"))
946 locallyAvailable = driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool();
947 icon = locallyAvailable ? QIcon() : remoteIcon;
948
949 QStandardItem *mount = new QStandardItem(icon, driver->getLabel());
950 mount->setData(getTooltip(driver), Qt::ToolTipRole);
951 model->appendRow(mount);
952 }
953 combo->setModel(model);
954 combo->setCurrentText(selectedDriver);
955 }
956}
957
958void ProfileEditor::executeScriptEditor()
959{
960 if (pi == nullptr)
961 return;
962 QStringList currentDrivers;
963 for (auto &oneCombo : ui->driversGroupBox->findChildren<QComboBox *>())
964 currentDrivers << oneCombo->currentText();
965 currentDrivers.removeAll("--");
966 currentDrivers.removeAll("");
967 for (auto &oneDriver : ui->remoteDrivers->text().split(","))
968 currentDrivers << oneDriver;
969 currentDrivers.sort();
970 ProfileScriptDialog dialog(currentDrivers, pi->scripts, this);
971 dialog.exec();
972 auto settings = dialog.jsonSettings();
973 pi->scripts = QJsonDocument(settings).toJson(QJsonDocument::Compact);
974}
QString country() const
QString province() const
QString name() const
bool GetAllProfiles(QList< QSharedPointer< ProfileInfo > > &profiles)
GetAllProfiles Return all profiles in a QList.
KSUserDB * userdb()
Definition kstarsdata.h:223
GeoLocation * geo()
Definition kstarsdata.h:238
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
PostalAddress address(const QVariant &location)
void clicked(bool checked)
void toggled(bool checked)
virtual QVariant data(const QModelIndex &index, int role) const const=0
virtual bool hasChildren(const QModelIndex &parent) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
void setCurrentIndex(const QModelIndex &index)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
void setCurrentIndex(int index)
void setCurrentText(const QString &text)
int findText(const QString &text, Qt::MatchFlags flags) const const
QAbstractItemModel * model() const const
void setModelColumn(int visibleColumn)
virtual void setModel(QAbstractItemModel *model)
void setRootModelIndex(const QModelIndex &index)
void setView(QAbstractItemView *itemView)
QDialog(QWidget *parent, Qt::WindowFlags f)
virtual void accept()
virtual int exec()
QIcon fromTheme(const QString &name)
iterator begin()
bool contains(const AT &value) const const
iterator end()
T & first()
qsizetype removeAll(const AT &t)
int column() const const
QModelIndex parent() const const
int row() const const
QList< QHostAddress > allAddresses()
NetworkError error() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QObject * parent() const const
void setObjectName(QAnyStringView name)
void appendRow(QStandardItem *item)
virtual void setData(const QVariant &value, int role)
void setSelectable(bool selectable)
void appendRow(QStandardItem *item)
QList< QStandardItem * > findItems(const QString &text, Qt::MatchFlags flags, int column) const const
QModelIndex indexFromItem(const QStandardItem *item) const const
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)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
void sort(Qt::CaseSensitivity cs)
ToolTipRole
AscendingOrder
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
virtual void setModel(QAbstractItemModel *model) override
void sortByColumn(int column, Qt::SortOrder order)
QString toString() const const
bool close()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:54:28 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.