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

KDE's Doxygen guidelines are available online.