Kstars

opticaltrainmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "opticaltrainmanager.h"
8#include <kstars_debug.h>
9
10#include "ksnotification.h"
11#include "kstarsdata.h"
12#include "kstars.h"
13#include "indi/indilistener.h"
14#include "ekos/auxiliary/profilesettings.h"
15#include "oal/equipmentwriter.h"
16
17#include <QTimer>
18#include <QSqlTableModel>
19#include <QSqlDatabase>
20#include <QSqlRecord>
21
22#include <basedevice.h>
23
24#include <algorithm>
25
26namespace Ekos
27{
28
29OpticalTrainManager *OpticalTrainManager::m_Instance = nullptr;
30
31////////////////////////////////////////////////////////////////////////////
32///
33////////////////////////////////////////////////////////////////////////////
34OpticalTrainManager *OpticalTrainManager::Instance()
35{
36 if (m_Instance == nullptr)
37 m_Instance = new OpticalTrainManager();
38
39 return m_Instance;
40}
41
42////////////////////////////////////////////////////////////////////////////
43///
44////////////////////////////////////////////////////////////////////////////
45void OpticalTrainManager::release()
46{
47 delete(m_Instance);
48 m_Instance = nullptr;
49}
50
51////////////////////////////////////////////////////////////////////////////
52///
53////////////////////////////////////////////////////////////////////////////
54OpticalTrainManager::OpticalTrainManager() : QDialog(KStars::Instance())
55{
56#ifdef Q_OS_OSX
57 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
58#endif
59
60 setupUi(this);
61
62 connect(this, &QDialog::finished, this, [this]()
63 {
64 emit configurationRequested(false);
65 });
66
67 // Mount Combo
68 connect(mountComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
69 [this]()
70 {
71 updateOpticalTrainValue(mountComboBox, "mount");
72 });
73
74 // DustCap Combo
75 connect(dustCapComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
76 [this]()
77 {
78 updateOpticalTrainValue(dustCapComboBox, "dustcap");
79 });
80
81 // Light Box
82 connect(lightBoxComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
83 [this]()
84 {
85 updateOpticalTrainValue(lightBoxComboBox, "lightbox");
86 });
87
88 // Scope / Lens
89 connect(scopeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
90 [this]()
91 {
92 updateOpticalTrainValue(scopeComboBox, "scope");
93 });
94
95 // Reducer
96 connect(reducerSpinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
97 [this](double value)
98 {
99 updateOpticalTrainValue(value, "reducer");
100 });
101
102 // Rotator
103 connect(rotatorComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
104 [this]()
105 {
106 updateOpticalTrainValue(rotatorComboBox, "rotator");
107 });
108
109 // Focuser
110 connect(focusComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
111 [this]()
112 {
113 updateOpticalTrainValue(focusComboBox, "focuser");
114 });
115
116 // Filter Wheel
117 connect(filterComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
118 [this]()
119 {
120 updateOpticalTrainValue(filterComboBox, "filterwheel");
121 });
122
123 // Camera
124 connect(cameraComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
125 [this]()
126 {
127 updateOpticalTrainValue(cameraComboBox, "camera");
128 });
129
130 // Guider
131 connect(guiderComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
132 [this]()
133 {
134 updateOpticalTrainValue(guiderComboBox, "guider");
135 });
136
137 connect(addB, &QPushButton::clicked, this, [this]()
138 {
139 addOpticalTrain(m_TrainNames.count(), i18n("New Train"));
140 m_OpticalTrainsModel->select();
141 refreshModel();
142 trainNamesList->setCurrentRow(trainNamesList->count() - 1);
143 selectOpticalTrain(trainNamesList->currentItem());
144 });
145
146 connect(removeB, &QPushButton::clicked, this, [this]()
147 {
148 if (trainNamesList->currentItem() != nullptr)
149 {
150 removeOpticalTrain(trainNamesList->currentItem()->text());
151 removeB->setEnabled(false);
152 }
153 });
154
155 connect(resetB, &QPushButton::clicked, this, &OpticalTrainManager::reset);
156
158 {
159 QScopedPointer<EquipmentWriter> writer(new EquipmentWriter());
160 writer->loadEquipment();
161 writer->exec();
162 refreshOpticalElements();
163 });
164
166 {
167 selectOpticalTrain(item);
168 });
170 {
171 renameCurrentOpticalTrain(item->text());
172 });
174 {
175 if (row >= 0)
176 selectOpticalTrain(trainNamesList->currentItem());
177 });
178
179 m_CheckMissingDevicesTimer.setInterval(5000);
180 m_CheckMissingDevicesTimer.setSingleShot(true);
181 connect(&m_CheckMissingDevicesTimer, &QTimer::timeout, this, &OpticalTrainManager::checkMissingDevices);
182
183 m_DelegateTimer.setInterval(1000);
184 m_DelegateTimer.setSingleShot(true);
185 connect(&m_DelegateTimer, &QTimer::timeout, this, [this]()
186 {
187 if (m_Profile)
188 setProfile(m_Profile);
189 });
190
191 initModel();
192}
193
194////////////////////////////////////////////////////////////////////////////
195///
196////////////////////////////////////////////////////////////////////////////
197void OpticalTrainManager::initModel()
198{
199 auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
200 m_OpticalTrainsModel = new QSqlTableModel(this, userdb);
201 connect(m_OpticalTrainsModel, &QSqlTableModel::dataChanged, this, [this]()
202 {
203 m_OpticalTrains.clear();
204 for (int i = 0; i < m_OpticalTrainsModel->rowCount(); ++i)
205 {
206 QVariantMap recordMap;
207 QSqlRecord record = m_OpticalTrainsModel->record(i);
208 for (int j = 0; j < record.count(); j++)
209 recordMap[record.fieldName(j)] = record.value(j);
210
211 m_OpticalTrains.append(recordMap);
212 }
213
214 m_TrainNames.clear();
215 for (auto &oneTrain : m_OpticalTrains)
216 m_TrainNames << oneTrain["name"].toString();
217
218 trainNamesList->clear();
219 trainNamesList->addItems(m_TrainNames);
221 emit updated();
222 });
223}
224
225void OpticalTrainManager::syncDevices()
226{
227 syncDelegatesToDevices();
228 if (m_Profile)
229 {
230 refreshModel();
231 emit updated();
232 }
233}
234
235////////////////////////////////////////////////////////////////////////////
236///
237////////////////////////////////////////////////////////////////////////////
238void OpticalTrainManager::refreshModel()
239{
240 KStars::Instance()->data()->userdb()->GetOpticalTrains(m_Profile->id, m_OpticalTrains);
241 m_TrainNames.clear();
242 for (auto &oneTrain : m_OpticalTrains)
243 m_TrainNames << oneTrain["name"].toString();
244
245 trainNamesList->clear();
246 trainNamesList->addItems(m_TrainNames);
247}
248
249////////////////////////////////////////////////////////////////////////////
250///
251////////////////////////////////////////////////////////////////////////////
252void OpticalTrainManager::syncActiveDevices()
253{
254 for (auto &oneTrain : m_OpticalTrains)
255 {
256 auto train = oneTrain["name"].toString();
258
259 if (getGenericDevice(train, Mount, device))
260 syncActiveProperties(oneTrain, device);
261 if (getGenericDevice(train, Camera, device))
262 syncActiveProperties(oneTrain, device);
263 if (getGenericDevice(train, GuideVia, device))
264 syncActiveProperties(oneTrain, device);
265 if (getGenericDevice(train, Focuser, device))
266 syncActiveProperties(oneTrain, device);
267 if (getGenericDevice(train, FilterWheel, device))
268 syncActiveProperties(oneTrain, device);
269 if (getGenericDevice(train, Rotator, device))
270 syncActiveProperties(oneTrain, device);
271 if (getGenericDevice(train, DustCap, device))
272 syncActiveProperties(oneTrain, device);
273 if (getGenericDevice(train, LightBox, device))
274 syncActiveProperties(oneTrain, device);
275 }
276
277 // For non-train specific devices, we only sync them with primary train
279 if (!m_OpticalTrains.isEmpty())
280 {
281 auto name = m_OpticalTrains[0]["name"].toString();
282 if (getGenericDevice(name, Dome, device))
283 syncActiveProperties(m_OpticalTrains[0], device);
284 if (getGenericDevice(name, Weather, device))
285 syncActiveProperties(m_OpticalTrains[0], device);
286 if (getGenericDevice(name, GPS, device))
287 syncActiveProperties(m_OpticalTrains[0], device);
288 }
289}
290
291////////////////////////////////////////////////////////////////////////////
292///
293////////////////////////////////////////////////////////////////////////////
294void OpticalTrainManager::syncActiveProperties(const QVariantMap &train, const QSharedPointer<ISD::GenericDevice> &device)
295{
296 auto tvp = device->getProperty("ACTIVE_DEVICES");
297 if (!tvp)
298 return;
299
300 auto name = train["name"].toString();
301
302 for (auto &it : *tvp.getText())
303 {
305 QString elementText = it.getText();
306 if (it.isNameMatch("ACTIVE_TELESCOPE"))
307 {
308 auto activeDevice = train["mount"].toString();
309 if (activeDevice == "--")
310 elementText.clear();
311 else if (activeDevice != elementText)
312 {
314 if (getGenericDevice(name, Mount, genericDevice))
315 devs.append(genericDevice);
316 }
317 }
318 else if (it.isNameMatch("ACTIVE_DOME"))
319 {
320 devs = INDIListener::devicesByInterface(INDI::BaseDevice::DOME_INTERFACE);
321 }
322 else if (it.isNameMatch("ACTIVE_GPS"))
323 {
324 devs = INDIListener::devicesByInterface(INDI::BaseDevice::GPS_INTERFACE);
325 }
326 else if (it.isNameMatch("ACTIVE_ROTATOR"))
327 {
328 auto activeDevice = train["rotator"].toString();
329 if (activeDevice == "--")
330 elementText.clear();
331 else if (activeDevice != elementText)
332 {
334 if (getGenericDevice(name, Rotator, genericDevice))
335 devs.append(genericDevice);
336 }
337 }
338 else if (it.isNameMatch("ACTIVE_FOCUSER"))
339 {
340 auto activeDevice = train["focuser"].toString();
341 if (activeDevice == "--")
342 elementText.clear();
343 else if (activeDevice != elementText)
344 {
346 if (getGenericDevice(name, Focuser, genericDevice))
347 devs.append(genericDevice);
348 }
349 }
350 else if (it.isNameMatch("ACTIVE_FILTER"))
351 {
352 auto activeDevice = train["filterwheel"].toString();
353 if (activeDevice == "--")
354 elementText.clear();
355 else if (activeDevice != elementText)
356 {
358 if (getGenericDevice(name, FilterWheel, genericDevice))
359 devs.append(genericDevice);
360 }
361 }
362
363 if (!devs.empty())
364 {
365 if (it.getText() != devs.first()->getDeviceName())
366 {
367 it.setText(devs.first()->getDeviceName().toLatin1().constData());
368 device->sendNewProperty(tvp.getText());
369 }
370 }
371 // Clear element if required
372 else if (elementText.isEmpty() && !QString(it.getText()).isEmpty())
373 {
374 it.setText("");
375 device->sendNewProperty(tvp.getText());
376 }
377 }
378
379}
380
381////////////////////////////////////////////////////////////////////////////
382///
383////////////////////////////////////////////////////////////////////////////
384void OpticalTrainManager::setProfile(const QSharedPointer<ProfileInfo> &profile)
385{
386 m_DelegateTimer.stop();
387
388 // Load optical train model
389 if (m_Profile != profile)
390 {
391 m_Profile = profile;
392 refreshModel();
393 }
394
395 // Are we still updating delegates? If yes, return.
396 if (syncDelegatesToDevices())
397 {
398 m_CheckMissingDevicesTimer.start();
399
400 // Start delegate timer to ensure no more changes are pending.
401 m_DelegateTimer.start();
402
403 syncActiveDevices();
404 }
405 else
406 {
407 checkOpticalTrains();
408 }
409}
410
411////////////////////////////////////////////////////////////////////////////
412///
413////////////////////////////////////////////////////////////////////////////
414void OpticalTrainManager::checkOpticalTrains()
415{
416 if (m_OpticalTrains.empty())
417 {
418 generateOpticalTrains();
419 refreshModel();
420 if (!m_OpticalTrains.empty())
421 {
422 auto primaryTrainID = m_OpticalTrains[0]["id"].toUInt();
423 ProfileSettings::Instance()->setOneSetting(ProfileSettings::PrimaryOpticalTrain, primaryTrainID);
424 ProfileSettings::Instance()->setOneSetting(ProfileSettings::CaptureOpticalTrain, primaryTrainID);
425 ProfileSettings::Instance()->setOneSetting(ProfileSettings::FocusOpticalTrain, primaryTrainID);
426 ProfileSettings::Instance()->setOneSetting(ProfileSettings::MountOpticalTrain, primaryTrainID);
427 ProfileSettings::Instance()->setOneSetting(ProfileSettings::AlignOpticalTrain, primaryTrainID);
428 ProfileSettings::Instance()->setOneSetting(ProfileSettings::DarkLibraryOpticalTrain, primaryTrainID);
429 if (m_OpticalTrains.count() > 1)
430 ProfileSettings::Instance()->setOneSetting(ProfileSettings::GuideOpticalTrain, m_OpticalTrains[1]["id"].toInt());
431 else
432 ProfileSettings::Instance()->setOneSetting(ProfileSettings::GuideOpticalTrain, primaryTrainID);
433 }
434
435 emit updated();
436 show();
437 raise();
438 emit configurationRequested(true);
439 }
440 else
441 {
442 m_CheckMissingDevicesTimer.start();
443 emit updated();
444 }
445}
446////////////////////////////////////////////////////////////////////////////
447/// This method tries to guess possible optical train configuration
448////////////////////////////////////////////////////////////////////////////
449void OpticalTrainManager::generateOpticalTrains()
450{
451 // We should have primary train
452 addOpticalTrain(0, i18n("Primary"));
453 // Check if need secondary train
454 if (cameraComboBox->count() > 2)
455 addOpticalTrain(1, i18n("Secondary"));
456 // Check if need tertiary train
457 if (cameraComboBox->count() > 3)
458 addOpticalTrain(2, i18n("Tertiary"));
459}
460
461////////////////////////////////////////////////////////////////////////////
462///
463////////////////////////////////////////////////////////////////////////////
464QString OpticalTrainManager::addOpticalTrain(uint8_t index, const QString &name)
465{
466 QVariantMap train;
467 train["profile"] = m_Profile->id;
468 train["name"] = uniqueTrainName(name);
469
470 train["mount"] = mountComboBox->itemText(mountComboBox->count() - 1);
471 train["dustcap"] = dustCapComboBox->itemText(dustCapComboBox->count() - 1);
472 train["lightbox"] = lightBoxComboBox->itemText(lightBoxComboBox->count() - 1);
473 train["reducer"] = 1.0;
474 train["rotator"] = rotatorComboBox->itemText(rotatorComboBox->count() - 1);
475 train["focuser"] = focusComboBox->itemText(focusComboBox->count() - 1);
476 train["filterwheel"] = filterComboBox->itemText(filterComboBox->count() - 1);
477 train["guider"] = guiderComboBox->itemText(guiderComboBox->count() - 1);
478
480 if (KStars::Instance()->data()->userdb()->getLastOpticalElement(opticalElement))
481 train["scope"] = opticalElement["name"].toString();
482
483 train["camera"] = "--";
484 // Primary train
485 if (index == 0 && cameraComboBox->count() > 1)
486 train["camera"] = cameraComboBox->itemText(1);
487 // Any other trains
488 else if (index > 0)
489 {
490 // For 2nd train and beyond, we get the N camera appropiate for this train if one exist.
491 // We add + 1 because first element in combobox is "--"
492 auto cameraIndex = index + 1;
493 if (cameraComboBox->count() >= cameraIndex)
494 train["camera"] = cameraComboBox->itemText(cameraIndex);
495 }
496
497 KStarsData::Instance()->userdb()->AddOpticalTrain(train);
498 return train["name"].toString();
499}
500
501////////////////////////////////////////////////////////////////////////////
502///
503////////////////////////////////////////////////////////////////////////////
504void OpticalTrainManager::addOpticalTrain(const QJsonObject &value)
505{
506 auto newTrain = value.toVariantMap();
507 newTrain["profile"] = m_Profile->id;
508 KStarsData::Instance()->userdb()->AddOpticalTrain(newTrain);
509
510 refreshTrains();
511}
512
513////////////////////////////////////////////////////////////////////////////
514///
515////////////////////////////////////////////////////////////////////////////
516bool OpticalTrainManager::setOpticalTrainValue(const QString &name, const QString &field, const QVariant &value)
517{
518 for (auto &oneTrain : m_OpticalTrains)
519 {
520 if (oneTrain["name"].toString() == name)
521 {
522 // If value did not change, just return true
523 if (oneTrain[field] == value)
524 return true;
525
526 // Update field and database.
527 oneTrain[field] = value;
528 KStarsData::Instance()->userdb()->UpdateOpticalTrain(oneTrain, oneTrain["id"].toInt());
529 syncActiveDevices();
530 emit updated();
531 return true;
532 }
533 }
534 return false;
535}
536
537////////////////////////////////////////////////////////////////////////////
538///
539////////////////////////////////////////////////////////////////////////////
540void OpticalTrainManager::renameCurrentOpticalTrain(const QString &name)
541{
542 if (m_CurrentOpticalTrain != nullptr && (*m_CurrentOpticalTrain)["name"] != name)
543 {
544 auto pos = trainNamesList->currentRow();
545 // ensure train name uniqueness
546 auto unique = uniqueTrainName(name);
547 // update the train database entry
548 setOpticalTrainValue((*m_CurrentOpticalTrain)["name"].toString(), "name", unique);
549 // propagate the unique name to the current selection
550 trainNamesList->currentItem()->setText(unique);
551 // refresh the trains
552 refreshTrains();
553 // refresh selection
554 selectOpticalTrain(unique);
555 trainNamesList->setCurrentRow(pos);
556 }
557}
558
559////////////////////////////////////////////////////////////////////////////
560///
561////////////////////////////////////////////////////////////////////////////
562bool OpticalTrainManager::setOpticalTrain(const QJsonObject &train)
563{
564 auto oneOpticalTrain = getOpticalTrain(train["id"].toInt());
565 if (!oneOpticalTrain.empty())
566 {
567 KStarsData::Instance()->userdb()->UpdateOpticalTrain(train.toVariantMap(), oneOpticalTrain["id"].toInt());
568 refreshTrains();
569 return true;
570 }
571 return false;
572}
573
574////////////////////////////////////////////////////////////////////////////
575///
576////////////////////////////////////////////////////////////////////////////
577bool OpticalTrainManager::removeOpticalTrain(const QString &name)
578{
579 for (auto &oneTrain : m_OpticalTrains)
580 {
581 if (oneTrain["name"].toString() == name)
582 {
583 auto id = oneTrain["id"].toInt();
584 KStarsData::Instance()->userdb()->DeleteOpticalTrain(id);
585 KStarsData::Instance()->userdb()->DeleteOpticalTrainSettings(id);
586 refreshTrains();
587 selectOpticalTrain(nullptr);
588 return true;
589 }
590 }
591
592 return false;
593}
594
595////////////////////////////////////////////////////////////////////////////
596///
597////////////////////////////////////////////////////////////////////////////
598bool OpticalTrainManager::syncDelegatesToDevices()
599{
600 auto changed = false;
601
602 // Mounts
603 auto mounts = INDIListener::devicesByInterface(INDI::BaseDevice::TELESCOPE_INTERFACE);
604 QStringList values;
605 for (auto &oneMount : mounts)
606 values << oneMount->getDeviceName();
607 changed |= !values.empty() && values != m_MountNames;
608 m_MountNames = values;
609 auto currentMount = mountComboBox->currentText();
610 mountComboBox->clear();
611 mountComboBox->addItems(QStringList() << "--" << values);
612 mountComboBox->setCurrentText(currentMount);
613
614 // Dust Caps
615 values.clear();
616 auto dustcaps = INDIListener::devicesByInterface(INDI::BaseDevice::DUSTCAP_INTERFACE);
617 for (auto &oneCap : dustcaps)
618 values << oneCap->getDeviceName();
619 changed |= !values.empty() && values != m_DustCapNames;
620 m_DustCapNames = values;
621 auto currentCap = dustCapComboBox->currentText();
622 dustCapComboBox->clear();
623 dustCapComboBox->addItems(QStringList() << "--" << values);
624 dustCapComboBox->setCurrentText(currentCap);
625
626 // Light Boxes
627 values.clear();
628 auto lightboxes = INDIListener::devicesByInterface(INDI::BaseDevice::LIGHTBOX_INTERFACE);
629 for (auto &oneBox : lightboxes)
630 values << oneBox->getDeviceName();
631 changed |= !values.empty() && values != m_LightBoxNames;
632 auto currentLightBox = lightBoxComboBox->currentText();
633 m_LightBoxNames = values;
634 lightBoxComboBox->clear();
635 lightBoxComboBox->addItems(QStringList() << "--" << values);
636 lightBoxComboBox->setCurrentText(currentLightBox);
637
638 // Scopes
639 values = KStars::Instance()->data()->userdb()->getOpticalElementNames();
640 changed |= !values.empty() && values != m_ScopeNames;
641 m_ScopeNames = values;
642 auto currentScope = scopeComboBox->currentText();
643 scopeComboBox->clear();
644 scopeComboBox->addItems(QStringList() << "--" << values);
645 scopeComboBox->setCurrentText(currentScope);
646
647 // Rotators
648 values.clear();
649 auto rotators = INDIListener::devicesByInterface(INDI::BaseDevice::ROTATOR_INTERFACE);
650 for (auto &oneRotator : rotators)
651 values << oneRotator->getDeviceName();
652 changed |= !values.empty() && values != m_RotatorNames;
653 m_RotatorNames = values;
654 auto currentRotator = rotatorComboBox->currentText();
655 rotatorComboBox->clear();
656 rotatorComboBox->addItems(QStringList() << "--" << values);
657 rotatorComboBox->setCurrentText(currentRotator);
658
659 // Focusers
660 values.clear();
661 auto focusers = INDIListener::devicesByInterface(INDI::BaseDevice::FOCUSER_INTERFACE);
662 for (auto &oneFocuser : focusers)
663 values << oneFocuser->getDeviceName();
664 changed |= !values.empty() && values != m_FocuserNames;
665 m_FocuserNames = values;
666 auto currentFocuser = focusComboBox->currentText();
667 focusComboBox->clear();
668 focusComboBox->addItems(QStringList() << "--" << values);
669 focusComboBox->setCurrentText(currentFocuser);
670
671 // Filter Wheels
672 values.clear();
673 auto filterwheels = INDIListener::devicesByInterface(INDI::BaseDevice::FILTER_INTERFACE);
674 for (auto &oneFilterWheel : filterwheels)
675 values << oneFilterWheel->getDeviceName();
676 changed |= !values.empty() && values != m_FilterWheelNames;
677 m_FilterWheelNames = values;
678 auto currentFilter = filterComboBox->currentText();
679 filterComboBox->clear();
680 filterComboBox->addItems(QStringList() << "--" << values);
681 filterComboBox->setCurrentText(currentFilter);
682
683 // Cameras
684 values.clear();
685 auto cameras = INDIListener::devicesByInterface(INDI::BaseDevice::CCD_INTERFACE);
686 for (auto &oneCamera : cameras)
687 values << oneCamera->getDeviceName();
688 changed |= !values.empty() && values != m_CameraNames;
689 m_CameraNames = values;
690 auto currentCamera = cameraComboBox->currentText();
691 cameraComboBox->clear();
692 cameraComboBox->addItems(QStringList() << "--" << values);
693 cameraComboBox->setCurrentText(currentCamera);
694
695 // Guiders
696 values.clear();
697 auto guiders = INDIListener::devicesByInterface(INDI::BaseDevice::GUIDER_INTERFACE);
698 for (auto &oneGuider : guiders)
699 values << oneGuider->getDeviceName();
700 changed |= !values.empty() && values != m_GuiderNames;
701 m_GuiderNames = values;
702 auto currentGuider = guiderComboBox->currentText();
703 guiderComboBox->clear();
704 guiderComboBox->addItems(QStringList() << "--" << values);
705 guiderComboBox->setCurrentText(currentGuider);
706
707 return changed;
708}
709
710////////////////////////////////////////////////////////////////////////////
711///
712////////////////////////////////////////////////////////////////////////////
713QString OpticalTrainManager::uniqueTrainName(QString name)
714{
715 QString result = name;
716 int nr = 1;
717 while (m_TrainNames.contains(result))
718 result = QString("%1 (%2)").arg(name).arg(nr++);
719
720 return result;
721}
722
723////////////////////////////////////////////////////////////////////////////
724///
725////////////////////////////////////////////////////////////////////////////
726bool OpticalTrainManager::selectOpticalTrain(QListWidgetItem *item)
727{
728 if (item != nullptr && selectOpticalTrain(item->text()))
729 {
730 item->setFlags(item->flags() | Qt::ItemIsEditable);
731 return true;
732 }
733 return false;
734}
735
736////////////////////////////////////////////////////////////////////////////
737///
738////////////////////////////////////////////////////////////////////////////
739QString OpticalTrainManager::findTrainContainingDevice(const QString &name, Role role)
740{
741 for (auto &oneTrain : m_OpticalTrains)
742 {
743 auto train = oneTrain["name"].toString();
744
745 switch (role)
746 {
747 case Mount:
748 if (oneTrain["mount"].toString() == name)
749 return train;
750 break;
751 case Camera:
752 if (oneTrain["camera"].toString() == name)
753 return train;
754 break;
755 case Rotator:
756 if (oneTrain["rotator"].toString() == name)
757 return train;
758 break;
759 case GuideVia:
760 if (oneTrain["guider"].toString() == name)
761 return train;
762 break;
763 case DustCap:
764 if (oneTrain["dustcap"].toString() == name)
765 return train;
766 break;
767 case Scope:
768 if (oneTrain["scope"].toString() == name)
769 return train;
770 break;
771 case FilterWheel:
772 if (oneTrain["filterwheel"].toString() == name)
773 return train;
774 break;
775 case Focuser:
776 if (oneTrain["focuser"].toString() == name)
777 return train;
778 break;
779 case Reducer:
780 if (oneTrain["reducer"].toString() == name)
781 return train;
782 break;
783 case LightBox:
784 if (oneTrain["lightbox"].toString() == name)
785 return train;
786 break;
787 }
788
789 }
790
791 return QString();
792}
793
794////////////////////////////////////////////////////////////////////////////
795///
796////////////////////////////////////////////////////////////////////////////
797bool OpticalTrainManager::selectOpticalTrain(const QString &name)
798{
799 for (auto &oneTrain : m_OpticalTrains)
800 {
801 if (oneTrain["name"].toString() == name)
802 {
803 m_Persistent = false;
804 m_CurrentOpticalTrain = &oneTrain;
805 mountComboBox->setCurrentText(oneTrain["mount"].toString());
806 dustCapComboBox->setCurrentText(oneTrain["dustcap"].toString());
807 lightBoxComboBox->setCurrentText(oneTrain["lightbox"].toString());
808 scopeComboBox->setCurrentText(oneTrain["scope"].toString());
809 reducerSpinBox->setValue(oneTrain["reducer"].toDouble());
810 rotatorComboBox->setCurrentText(oneTrain["rotator"].toString());
811 focusComboBox->setCurrentText(oneTrain["focuser"].toString());
812 filterComboBox->setCurrentText(oneTrain["filterwheel"].toString());
813 cameraComboBox->setCurrentText(oneTrain["camera"].toString());
814 guiderComboBox->setCurrentText(oneTrain["guider"].toString());
815 removeB->setEnabled(m_OpticalTrains.length() > 1);
816 trainConfigBox->setEnabled(true);
817 m_Persistent = true;
818 return true;
819 }
820 }
821
822 // none found
823 m_Persistent = false;
824 m_CurrentOpticalTrain = nullptr;
825 mountComboBox->setCurrentText("--");
826 dustCapComboBox->setCurrentText("--");
827 lightBoxComboBox->setCurrentText("--");
828 scopeComboBox->setCurrentText("--");
829 reducerSpinBox->setValue(1.0);
830 rotatorComboBox->setCurrentText("--");
831 focusComboBox->setCurrentText("--");
832 filterComboBox->setCurrentText("--");
833 cameraComboBox->setCurrentText("--");
834 guiderComboBox->setCurrentText("--");
835 removeB->setEnabled(false);
836 trainConfigBox->setEnabled(false);
837 m_Persistent = true;
838 return false;
839}
840
841////////////////////////////////////////////////////////////////////////////
842///
843////////////////////////////////////////////////////////////////////////////
844void OpticalTrainManager::openEditor(const QString &name)
845{
846 selectOpticalTrain(name);
847 QList<QListWidgetItem*> matches = trainNamesList->findItems(name, Qt::MatchExactly);
848 if (matches.count() > 0)
849 trainNamesList->setCurrentItem(matches.first());
850 emit configurationRequested(true);
851 show();
852}
853
854////////////////////////////////////////////////////////////////////////////
855///
856////////////////////////////////////////////////////////////////////////////
857bool OpticalTrainManager::getGenericDevice(const QString &train, Role role, QSharedPointer<ISD::GenericDevice> &generic)
858{
859 for (auto &oneTrain : m_OpticalTrains)
860 {
861 if (oneTrain["name"].toString() == train)
862 {
863 switch (role)
864 {
865 case Mount:
866 return INDIListener::findDevice(oneTrain["mount"].toString(), generic);
867 case Camera:
868 return INDIListener::findDevice(oneTrain["camera"].toString(), generic);
869 case Rotator:
870 return INDIListener::findDevice(oneTrain["rotator"].toString(), generic);
871 case GuideVia:
872 return INDIListener::findDevice(oneTrain["guider"].toString(), generic);
873 case DustCap:
874 return INDIListener::findDevice(oneTrain["dustcap"].toString(), generic);
875 case FilterWheel:
876 return INDIListener::findDevice(oneTrain["filterwheel"].toString(), generic);
877 case Focuser:
878 return INDIListener::findDevice(oneTrain["focuser"].toString(), generic);
879 case LightBox:
880 return INDIListener::findDevice(oneTrain["lightbox"].toString(), generic);
881 case Dome:
882 {
883 auto devices = INDIListener::devicesByInterface(INDI::BaseDevice::DOME_INTERFACE);
884 if (!devices.empty())
885 {
886 generic = devices[0];
887 return true;
888 }
889 else
890 return false;
891 }
892 break;
893 case GPS:
894 {
895 auto devices = INDIListener::devicesByInterface(INDI::BaseDevice::GPS_INTERFACE);
896 if (!devices.empty())
897 {
898 generic = devices[0];
899 return true;
900 }
901 else
902 return false;
903 }
904 default:
905 break;
906 }
907 }
908 }
909
910 return false;
911}
912
913////////////////////////////////////////////////////////////////////////////
914///
915////////////////////////////////////////////////////////////////////////////
916ISD::Mount *OpticalTrainManager::getMount(const QString &name)
917{
918 for (auto &oneTrain : m_OpticalTrains)
919 {
920 if (oneTrain["name"].toString() == name)
921 {
923 if (INDIListener::findDevice(oneTrain["mount"].toString(), generic))
924 return generic->getMount();
925 }
926 }
927
928 return nullptr;
929}
930
931////////////////////////////////////////////////////////////////////////////
932///
933////////////////////////////////////////////////////////////////////////////
934ISD::DustCap *OpticalTrainManager::getDustCap(const QString &name)
935{
936 for (auto &oneTrain : m_OpticalTrains)
937 {
938 if (oneTrain["name"].toString() == name)
939 {
941 if (INDIListener::findDevice(oneTrain["dustcap"].toString(), generic))
942 return generic->getDustCap();
943 }
944 }
945
946 return nullptr;
947}
948
949////////////////////////////////////////////////////////////////////////////
950///
951////////////////////////////////////////////////////////////////////////////
952ISD::LightBox *OpticalTrainManager::getLightBox(const QString &name)
953{
954 for (auto &oneTrain : m_OpticalTrains)
955 {
956 if (oneTrain["name"].toString() == name)
957 {
959 if (INDIListener::findDevice(oneTrain["lightbox"].toString(), generic))
960 return generic->getLightBox();
961 }
962 }
963
964 return nullptr;
965}
966
967////////////////////////////////////////////////////////////////////////////
968///
969////////////////////////////////////////////////////////////////////////////
970QJsonObject OpticalTrainManager::getScope(const QString &name)
971{
973 for (auto &oneTrain : m_OpticalTrains)
974 {
975 if (oneTrain["name"].toString() == name)
976 {
977 if (KStars::Instance()->data()->userdb()->getOpticalElementByName(oneTrain["scope"].toString(), oneOpticalElement))
978 return oneOpticalElement;
979 }
980 }
981
982 return oneOpticalElement;
983}
984
985////////////////////////////////////////////////////////////////////////////
986///
987////////////////////////////////////////////////////////////////////////////
988double OpticalTrainManager::getReducer(const QString &name)
989{
990 for (auto &oneTrain : m_OpticalTrains)
991 {
992 if (oneTrain["name"].toString() == name)
993 return oneTrain["reducer"].toDouble();
994 }
995
996 return 1;
997}
998
999////////////////////////////////////////////////////////////////////////////
1000///
1001////////////////////////////////////////////////////////////////////////////
1002ISD::Rotator *OpticalTrainManager::getRotator(const QString &name)
1003{
1004 for (auto &oneTrain : m_OpticalTrains)
1005 {
1006 if (oneTrain["name"].toString() == name)
1007 {
1009 if (INDIListener::findDevice(oneTrain["rotator"].toString(), generic))
1010 return generic->getRotator();
1011 }
1012 }
1013
1014 return nullptr;
1015}
1016
1017////////////////////////////////////////////////////////////////////////////
1018///
1019////////////////////////////////////////////////////////////////////////////
1020ISD::Focuser *OpticalTrainManager::getFocuser(const QString &name)
1021{
1022 for (auto &oneTrain : m_OpticalTrains)
1023 {
1024 if (oneTrain["name"].toString() == name)
1025 {
1027 if (INDIListener::findDevice(oneTrain["focuser"].toString(), generic))
1028 return generic->getFocuser();
1029 }
1030 }
1031
1032 return nullptr;
1033}
1034
1035////////////////////////////////////////////////////////////////////////////
1036///
1037////////////////////////////////////////////////////////////////////////////
1038ISD::FilterWheel *OpticalTrainManager::getFilterWheel(const QString &name)
1039{
1040 for (auto &oneTrain : m_OpticalTrains)
1041 {
1042 if (oneTrain["name"].toString() == name)
1043 {
1045 if (INDIListener::findDevice(oneTrain["filterwheel"].toString(), generic))
1046 return generic->getFilterWheel();
1047 }
1048 }
1049
1050 return nullptr;
1051}
1052
1053////////////////////////////////////////////////////////////////////////////
1054///
1055////////////////////////////////////////////////////////////////////////////
1056ISD::Camera *OpticalTrainManager::getCamera(const QString &name)
1057{
1058 for (auto &oneTrain : m_OpticalTrains)
1059 {
1060 if (oneTrain["name"].toString() == name)
1061 {
1063 if (INDIListener::findDevice(oneTrain["camera"].toString(), generic))
1064 return generic->getCamera();
1065 }
1066 }
1067
1068 return nullptr;
1069}
1070
1071////////////////////////////////////////////////////////////////////////////
1072///
1073////////////////////////////////////////////////////////////////////////////
1074ISD::Guider *OpticalTrainManager::getGuider(const QString &name)
1075{
1076 for (auto &oneTrain : m_OpticalTrains)
1077 {
1078 if (oneTrain["name"].toString() == name)
1079 {
1081 if (INDIListener::findDevice(oneTrain["guider"].toString(), generic))
1082 return generic->getGuider();
1083 }
1084 }
1085
1086 return nullptr;
1087}
1088
1089////////////////////////////////////////////////////////////////////////////
1090///
1091////////////////////////////////////////////////////////////////////////////
1092ISD::AdaptiveOptics *OpticalTrainManager::getAdaptiveOptics(const QString &name)
1093{
1094 // FIXME not implmeneted yet.
1095 // Need to add to database later
1096 for (auto &oneTrain : m_OpticalTrains)
1097 {
1098 if (oneTrain["name"].toString() == name)
1099 {
1101 if (INDIListener::findDevice(oneTrain["adaptiveoptics"].toString(), generic))
1102 return generic->getAdaptiveOptics();
1103 }
1104 }
1105
1106 return nullptr;
1107}
1108
1109////////////////////////////////////////////////////////////////////////////
1110///
1111////////////////////////////////////////////////////////////////////////////
1112const QVariantMap OpticalTrainManager::getOpticalTrain(uint8_t id) const
1113{
1114 for (auto &oneTrain : m_OpticalTrains)
1115 {
1116 if (oneTrain["id"].toInt() == id)
1117 return oneTrain;
1118 }
1119
1120 return QVariantMap();
1121}
1122
1123////////////////////////////////////////////////////////////////////////////
1124///
1125////////////////////////////////////////////////////////////////////////////
1126bool OpticalTrainManager::exists(uint8_t id) const
1127{
1128 for (auto &oneTrain : m_OpticalTrains)
1129 {
1130 if (oneTrain["id"].toInt() == id)
1131 return true;
1132 }
1133
1134 return false;
1135}
1136
1137////////////////////////////////////////////////////////////////////////////
1138///
1139////////////////////////////////////////////////////////////////////////////
1140const QVariantMap OpticalTrainManager::getOpticalTrain(const QString &name) const
1141{
1142 for (auto &oneTrain : m_OpticalTrains)
1143 {
1144 if (oneTrain["name"].toString() == name)
1145 return oneTrain;
1146 }
1147
1148 return QVariantMap();
1149}
1150
1151////////////////////////////////////////////////////////////////////////////
1152///
1153////////////////////////////////////////////////////////////////////////////
1154void OpticalTrainManager::refreshTrains()
1155{
1156 refreshModel();
1157 emit updated();
1158}
1159
1160////////////////////////////////////////////////////////////////////////////
1161///
1162////////////////////////////////////////////////////////////////////////////
1163void OpticalTrainManager::refreshOpticalElements()
1164{
1165 m_ScopeNames = KStars::Instance()->data()->userdb()->getOpticalElementNames();
1166 syncDelegatesToDevices();
1167}
1168
1169////////////////////////////////////////////////////////////////////////////
1170///
1171////////////////////////////////////////////////////////////////////////////
1172int OpticalTrainManager::id(const QString &name) const
1173{
1174 for (auto &oneTrain : m_OpticalTrains)
1175 {
1176 if (oneTrain["name"].toString() == name)
1177 return oneTrain["id"].toUInt();
1178 }
1179
1180 return -1;
1181}
1182
1183////////////////////////////////////////////////////////////////////////////
1184///
1185////////////////////////////////////////////////////////////////////////////
1186QString OpticalTrainManager::name(int id) const
1187{
1188 for (auto &oneTrain : m_OpticalTrains)
1189 {
1190 if (oneTrain["id"].toInt() == id)
1191 return oneTrain["name"].toString();
1192 }
1193
1194 return QString();
1195}
1196
1197////////////////////////////////////////////////////////////////////////////
1198///
1199////////////////////////////////////////////////////////////////////////////
1200void OpticalTrainManager::checkMissingDevices()
1201{
1202 // Double check the sanity of the train. If devices are added or missing, then we need to show it to alert the user.
1203 auto devices = getMissingDevices();
1204 if (!devices.empty())
1205 {
1206 if (devices.count() == 1)
1207 {
1208 KSNotification::event(QLatin1String("IndiServerMessage"),
1209 i18n("Missing device detected (%1). Please reconfigure the optical trains before proceeding any further.",
1210 devices.first()),
1211 KSNotification::General, KSNotification::Warn);
1212 }
1213 else
1214 {
1215 KSNotification::event(QLatin1String("IndiServerMessage"),
1216 i18n("Missing devices detected (%1). Please reconfigure the optical trains before proceeding any further.",
1217 devices.join(", ")),
1218 KSNotification::General, KSNotification::Warn);
1219 }
1220 show();
1221 raise();
1222 emit configurationRequested(true);
1223 }
1224}
1225
1226////////////////////////////////////////////////////////////////////////////
1227///
1228////////////////////////////////////////////////////////////////////////////
1229QStringList OpticalTrainManager::getMissingDevices() const
1230{
1231 auto missing = QStringList();
1232 for (auto &oneTrain : m_OpticalTrains)
1233 {
1234 auto mount = oneTrain["mount"].toString();
1235 if (mount != "--" && m_MountNames.contains(mount) == false)
1236 missing << mount;
1237
1238 auto camera = oneTrain["camera"].toString();
1239 if (camera != "--" && m_CameraNames.contains(camera) == false)
1240 missing << camera;
1241
1242 auto dustcap = oneTrain["dustcap"].toString();
1243 if (dustcap != "--" && m_DustCapNames.contains(dustcap) == false)
1244 missing << dustcap;
1245
1246 auto lightbox = oneTrain["lightbox"].toString();
1247 if (lightbox != "--" && m_LightBoxNames.contains(lightbox) == false)
1248 missing << lightbox;
1249
1250 auto focuser = oneTrain["focuser"].toString();
1251 if (focuser != "--" && m_FocuserNames.contains(focuser) == false)
1252 missing << focuser;
1253
1254 auto filterwheel = oneTrain["filterwheel"].toString();
1255 if (filterwheel != "--" && m_FilterWheelNames.contains(filterwheel) == false)
1257
1258 auto guider = oneTrain["guider"].toString();
1259 if (guider != "--" && m_GuiderNames.contains(guider) == false)
1260 missing << guider;
1261
1262 }
1263
1264 return missing;
1265}
1266
1267////////////////////////////////////////////////////////////////////////////
1268///
1269////////////////////////////////////////////////////////////////////////////
1270void Ekos::OpticalTrainManager::updateOpticalTrainValue(QComboBox *cb, const QString &element)
1271{
1272 if (trainNamesList->currentItem() != nullptr && m_Persistent == true)
1273 setOpticalTrainValue(trainNamesList->currentItem()->text(), element, cb->currentText());
1274}
1275
1276////////////////////////////////////////////////////////////////////////////
1277///
1278////////////////////////////////////////////////////////////////////////////
1279void OpticalTrainManager::updateOpticalTrainValue(double value, const QString &element)
1280{
1281 if (trainNamesList->currentItem() != nullptr && m_Persistent == true)
1282 setOpticalTrainValue(trainNamesList->currentItem()->text(), element, value);
1283
1284}
1285
1286////////////////////////////////////////////////////////////////////////////
1287/// Reset optical train to default values.
1288////////////////////////////////////////////////////////////////////////////
1289void OpticalTrainManager::reset()
1290{
1291 if (m_CurrentOpticalTrain != nullptr)
1292 {
1293 auto id = m_CurrentOpticalTrain->value("id");
1294 auto name = m_CurrentOpticalTrain->value("name");
1295 int row = trainNamesList->currentRow();
1296 m_CurrentOpticalTrain->clear();
1297
1298 m_CurrentOpticalTrain->insert("id", id);
1299 m_CurrentOpticalTrain->insert("name", name);
1300 m_CurrentOpticalTrain->insert("mount", "--");
1301 m_CurrentOpticalTrain->insert("camera", "--");
1302 m_CurrentOpticalTrain->insert("rotator", "--");
1303 m_CurrentOpticalTrain->insert("guider", "--");
1304 m_CurrentOpticalTrain->insert("dustcap", "--");
1305 m_CurrentOpticalTrain->insert("scope", "--");
1306 m_CurrentOpticalTrain->insert("filterwheel", "--");
1307 m_CurrentOpticalTrain->insert("focuser", "--");
1308 m_CurrentOpticalTrain->insert("reducer", 1);
1309 m_CurrentOpticalTrain->insert("lightbox", "--");
1310
1311 KStarsData::Instance()->userdb()->UpdateOpticalTrain(*m_CurrentOpticalTrain, id.toInt());
1312 refreshTrains();
1313 selectOpticalTrain(name.toString());
1314 trainNamesList->setCurrentRow(row);
1315 }
1316}
1317
1318}
AdaptiveOptics class handles control of INDI AdaptiveOptics devices.
Camera class controls an INDI Camera device.
Definition indicamera.h:47
Handles operation of a remotely controlled dust cover cap.
Definition indidustcap.h:23
Focuser class handles control of INDI focuser devices.
Definition indifocuser.h:21
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
Definition indimount.h:27
Rotator class handles control of INDI Rotator devices.
Definition indirotator.h:20
This is the main window for KStars.
Definition kstars.h:91
static KStars * Instance()
Definition kstars.h:123
Information on telescope used in observation.
Definition scope.h:18
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:78
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
QString name(StandardShortcut id)
void clicked(bool checked)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void currentIndexChanged(int index)
void finished(int result)
void valueChanged(double d)
Int toInt() const const
QVariantMap toVariantMap() const const
void clear()
bool empty() const const
void currentRowChanged(int currentRow)
void itemChanged(QListWidgetItem *item)
void itemClicked(QListWidgetItem *item)
Qt::ItemFlags flags() const const
void setFlags(Qt::ItemFlags flags)
QString text() const const
QSqlDatabase database(const QString &connectionName, bool open)
int count() const const
QString fieldName(int index) const const
QVariant value(const QString &name) const const
QString arg(Args &&... args) const const
bool isEmpty() const const
ItemIsEditable
MatchExactly
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.