Kstars

filtermanager.cpp
1/*
2 SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "buildfilteroffsets.h"
8#include <kstars_debug.h>
9
10#include "indi_debug.h"
11#include "kstarsdata.h"
12#include "kstars.h"
13#include "ekos/focus/focus.h"
14#include "ekos/manager.h"
15#include "Options.h"
16#include "auxiliary/kspaths.h"
17#include "auxiliary/ksmessagebox.h"
18#include "ekos/auxiliary/tabledelegate.h"
19
20#include <QTimer>
21#include <QSqlTableModel>
22#include <QSqlDatabase>
23#include <QSqlRecord>
24
25#include <basedevice.h>
26
27#include <algorithm>
28
29namespace Ekos
30{
31
32FilterManager::FilterManager(QWidget *parent) : QDialog(parent)
33{
34#ifdef Q_OS_MACOS
35 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
36#endif
37
38 setupUi(this);
39
40 connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
41 connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
42
43 connect(buildOffsetsButton, &QPushButton::clicked, this, &FilterManager::buildFilterOffsets);
44
45 kcfg_FlatSyncFocus->setChecked(Options::flatSyncFocus());
46 connect(kcfg_FlatSyncFocus, &QCheckBox::toggled, this, [this]()
47 {
48 Options::setFlatSyncFocus(kcfg_FlatSyncFocus->isChecked());
49 });
50
51 // 30 second timeout for filter change
52 m_FilterChangeTimeout.setSingleShot(true);
53 m_FilterChangeTimeout.setInterval(30000);
54 connect(&m_FilterChangeTimeout, &QTimer::timeout, this, &FilterManager::checkFilterChangeTimeout);
55
56 createFilterModel();
57
58 // No Edit delegate
59 noEditDelegate = new NotEditableDelegate(m_FilterView);
60 m_FilterView->setItemDelegateForColumn(FM_LABEL, noEditDelegate);
61
62 // Exposure delegate
63 exposureDelegate = new DoubleDelegate(m_FilterView, 0.001, 3600, 1);
64 m_FilterView->setItemDelegateForColumn(FM_EXPOSURE, exposureDelegate);
65
66 // Offset delegate
67 offsetDelegate = new IntegerDelegate(m_FilterView, -10000, 10000, 1);
68 m_FilterView->setItemDelegateForColumn(FM_OFFSET, offsetDelegate);
69
70 // Auto Focus delegate
71 useAutoFocusDelegate = new ToggleDelegate(m_FilterView);
72 m_FilterView->setItemDelegateForColumn(FM_AUTO_FOCUS, useAutoFocusDelegate);
73
74 // Set Delegates
75 lockDelegate = new ComboDelegate(m_FilterView);
76 m_FilterView->setItemDelegateForColumn(FM_LOCK_FILTER, lockDelegate);
77 lockDelegate->setValues(getLockDelegates());
78
79 // Last AF solution delegate. Set by Autofocus but make editable in case bad data
80 // corrections need to be made
81 lastAFSolutionDelegate = new IntegerDelegate(m_FilterView, 0, 1000000, 1);
82 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_SOLUTION, lastAFSolutionDelegate);
83
84 // Last AF solution temperature delegate
85 lastAFTempDelegate = new DoubleDelegate(m_FilterView, -60.0, 60.0, 1.0);
86 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_TEMP, lastAFTempDelegate);
87
88 // Last AF solution altitude delegate
89 lastAFAltDelegate = new DoubleDelegate(m_FilterView, 0.0, 90.0, 1.0);
90 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_ALT, lastAFAltDelegate);
91
92 // Ticks / °C delegate
93 ticksPerTempDelegate = new DoubleDelegate(m_FilterView, -10000.0, 10000.0, 1.0);
94 m_FilterView->setItemDelegateForColumn(FM_TICKS_PER_TEMP, ticksPerTempDelegate);
95
96 // Ticks / °Altitude delegate
97 ticksPerAltDelegate = new DoubleDelegate(m_FilterView, -10000.0, 10000.0, 1.0);
98 m_FilterView->setItemDelegateForColumn(FM_TICKS_PER_ALT, ticksPerAltDelegate);
99
100 // Wavelength delegate
101 wavelengthDelegate = new IntegerDelegate(m_FilterView, 200, 1000, 50);
102 m_FilterView->setItemDelegateForColumn(FM_WAVELENGTH, wavelengthDelegate);
103}
104
105void FilterManager::createFilterModel()
106{
107 QSqlDatabase userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
108
109 m_FilterModel = new QSqlTableModel(this, userdb);
110 m_FilterView->setModel(m_FilterModel);
111 m_FilterModel->setTable("filter");
112 m_FilterModel->setEditStrategy(QSqlTableModel::OnFieldChange);
113
114 m_FilterModel->setHeaderData(FM_LABEL, Qt::Horizontal, i18n("Filter"));
115
116 m_FilterModel->setHeaderData(FM_EXPOSURE, Qt::Horizontal, i18n("Filter exposure time during focus"), Qt::ToolTipRole);
117 m_FilterModel->setHeaderData(FM_EXPOSURE, Qt::Horizontal, i18n("Exposure"));
118
119 m_FilterModel->setHeaderData(FM_OFFSET, Qt::Horizontal, i18n("Relative offset in steps"), Qt::ToolTipRole);
120 m_FilterModel->setHeaderData(FM_OFFSET, Qt::Horizontal, i18n("Offset"));
121
122 m_FilterModel->setHeaderData(FM_AUTO_FOCUS, Qt::Horizontal, i18n("Start Auto Focus when filter is activated"),
124 m_FilterModel->setHeaderData(FM_AUTO_FOCUS, Qt::Horizontal, i18n("Auto Focus"));
125
126 m_FilterModel->setHeaderData(FM_LOCK_FILTER, Qt::Horizontal, i18n("Lock specific filter when running Auto Focus"),
128 m_FilterModel->setHeaderData(FM_LOCK_FILTER, Qt::Horizontal, i18n("Lock Filter"));
129
130 m_FilterModel->setHeaderData(FM_LAST_AF_SOLUTION, Qt::Horizontal,
131 i18n("Last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
132 m_FilterModel->setHeaderData(FM_LAST_AF_SOLUTION, Qt::Horizontal, i18n("Last AF Solution"));
133
134 m_FilterModel->setHeaderData(FM_LAST_AF_TEMP, Qt::Horizontal,
135 i18n("The temperature of the last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
136 m_FilterModel->setHeaderData(FM_LAST_AF_TEMP, Qt::Horizontal, i18n("Last AF Temp (°C)"));
137
138 m_FilterModel->setHeaderData(FM_LAST_AF_ALT, Qt::Horizontal,
139 i18n("The altitude of the last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
140 m_FilterModel->setHeaderData(FM_LAST_AF_ALT, Qt::Horizontal, i18n("Last AF Alt (°Alt)"));
141
142 m_FilterModel->setHeaderData(FM_TICKS_PER_TEMP, Qt::Horizontal,
143 i18n("The number of ticks per °C increase in temperature. +ve for outward focuser movement"), Qt::ToolTipRole);
144 m_FilterModel->setHeaderData(FM_TICKS_PER_TEMP, Qt::Horizontal, i18n("Ticks / °C"));
145
146 m_FilterModel->setHeaderData(FM_TICKS_PER_ALT, Qt::Horizontal,
147 i18n("The number of ticks per degree increase in altitude. +ve for outward focuser movement"), Qt::ToolTipRole);
148 m_FilterModel->setHeaderData(FM_TICKS_PER_ALT, Qt::Horizontal, i18n("Ticks / °Alt"));
149
150 m_FilterModel->setHeaderData(FM_WAVELENGTH, Qt::Horizontal, i18n("Mid-point wavelength of filter in nm"), Qt::ToolTipRole);
151 m_FilterModel->setHeaderData(FM_WAVELENGTH, Qt::Horizontal, i18n("Wavelength"));
152
153 connect(m_FilterModel, &QSqlTableModel::dataChanged, this, &FilterManager::updated);
154
155 connect(m_FilterModel, &QSqlTableModel::dataChanged, this, [this](const QModelIndex & topLeft, const QModelIndex &,
156 const QVector<int> &)
157 {
158 reloadFilters();
159 if (topLeft.column() == FM_EXPOSURE)
160 emit exposureChanged(m_FilterModel->data(topLeft).toDouble());
161 else if (topLeft.column() == FM_LOCK_FILTER)
162 {
163 // Don't allow the lock filter to be set to the current filter - circular dependancy
164 // Don't allow the lock to be set if this filter is a lock for another filter - nested dependancy
165 QString lock = m_FilterModel->data(m_FilterModel->index(topLeft.row(), topLeft.column())).toString();
166 QString filter = m_FilterModel->data(m_FilterModel->index(topLeft.row(), FM_LABEL)).toString();
167 bool alreadyALock = false;
168 for (int i = 0; i < m_ActiveFilters.count(); i++)
169 {
170 if (m_ActiveFilters[i]->lockedFilter() == filter)
171 {
172 alreadyALock = true;
173 break;
174 }
175 }
176 if (alreadyALock || (lock == filter))
177 {
178 m_FilterModel->setData(m_FilterModel->index(topLeft.row(), topLeft.column()), NULL_FILTER);
179 }
180 // Update the acceptable values in the lockDelegate
181 lockDelegate->setValues(getLockDelegates());
182 }
183 else if (topLeft.column() == FM_TICKS_PER_TEMP)
184 emit ticksPerTempChanged();
185 else if (topLeft.column() == FM_TICKS_PER_ALT)
186 emit ticksPerAltChanged();
187 else if (topLeft.column() == FM_WAVELENGTH)
188 emit wavelengthChanged();
189 });
190}
191
192void FilterManager::refreshFilterModel()
193{
194 if (m_FilterWheel == nullptr || m_currentFilterLabels.empty())
195 return;
196
197 // In case filter model was cleared due to a device disconnect
198 if (m_FilterModel == nullptr)
199 createFilterModel();
200
201 QString vendor(m_FilterWheel->getDeviceName());
202 m_FilterModel->setFilter(QString("vendor='%1'").arg(vendor));
203 m_FilterModel->select();
204
205 m_FilterView->hideColumn(0);
206 m_FilterView->hideColumn(1);
207 m_FilterView->hideColumn(2);
208 m_FilterView->hideColumn(3);
209
210 // If we have an existing table but it doesn't match the number of current filters
211 // then we remove it.
212 if (m_FilterModel->rowCount() > 0 && m_FilterModel->rowCount() != m_currentFilterLabels.count())
213 {
214 for (int i = 0; i < m_FilterModel->rowCount(); i++)
215 m_FilterModel->removeRow(i);
216
217 m_FilterModel->select();
218 }
219
220 // If it is first time, let's populate data
221 if (m_FilterModel->rowCount() == 0)
222 {
223 filterProperties *fp = new filterProperties(vendor, "", "", "");
224 for (QString &filter : m_currentFilterLabels)
225 {
226 fp->color = filter;
227 KStarsData::Instance()->userdb()->AddFilter(fp);
228 }
229
230 m_FilterModel->select();
231 }
232 // Make sure all the filter colors match DB. If not update model to sync with INDI filter values
233 else
234 {
235 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
236 {
237 QModelIndex index = m_FilterModel->index(i, FM_LABEL);
238 if (m_FilterModel->data(index).toString() != m_currentFilterLabels[i])
239 {
240 m_FilterModel->setData(index, m_currentFilterLabels[i]);
241 }
242 }
243 }
244
245 lockDelegate->setValues(getLockDelegates());
246
247 reloadFilters();
248 resizeDialog();
249}
250
251void FilterManager::resizeDialog()
252{
253 // Resize the columns to the data and then the dialog to the rows and columns
254 m_FilterView->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
255 int width = m_FilterView->horizontalHeader()->length() + 50;
256 int height = label->height() + m_FilterView->verticalHeader()->length() + label_2->height() + buttonBox->height() + 100;
257 this->resize(width, height);
258}
259// This function processes the list of active filters and returns a list of filters that could be lock filters
260// i.e. that don't themselves have locks.
261QStringList FilterManager::getLockDelegates()
262{
263 QStringList lockDelegates;
264
265 for (int i = 0; i < m_ActiveFilters.count(); i++)
266 {
267 if (m_ActiveFilters[i]->lockedFilter() == NULL_FILTER)
268 lockDelegates.append(m_ActiveFilters[i]->color());
269 }
270 return lockDelegates;
271}
272
273void FilterManager::reloadFilters()
274{
275 qDeleteAll(m_ActiveFilters);
276 currentFilter = nullptr;
277 targetFilter = nullptr;
278 m_ActiveFilters.clear();
279 operationQueue.clear();
280
281 filterProperties *fp = new filterProperties("", "", "", "");
282
283 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
284 {
285 QSqlRecord record = m_FilterModel->record(i);
286 QString id = record.value("id").toString();
287
288 fp->vendor = record.value("Vendor").toString();
289 fp->model = record.value("Model").toString();
290 fp->type = record.value("Type").toString();
291 fp->color = record.value("Color").toString();
292 fp->exposure = record.value("Exposure").toDouble();
293 fp->offset = record.value("Offset").toInt();
294 fp->lockedFilter = record.value("LockedFilter").toString();
295 fp->useAutoFocus = record.value("UseAutoFocus").toInt() == 1;
296 fp->absFocusPos = record.value("AbsoluteFocusPosition").toInt();
297 fp->focusTemperature = record.value("FocusTemperature").toDouble();
298 fp->focusAltitude = record.value("FocusAltitude").toDouble();
299 fp->focusTicksPerTemp = record.value("FocusTicksPerTemp").toDouble();
300 fp->focusTicksPerAlt = record.value("FocusTicksPerAlt").toDouble();
301 fp->wavelength = record.value("Wavelength").toInt();
302 OAL::Filter *o = new OAL::Filter(id, fp);
303 m_ActiveFilters.append(o);
304 }
305}
306
307void FilterManager::setFilterWheel(ISD::FilterWheel *filter)
308{
309 // Return if same device and we already initialized the properties.
310 if (m_FilterWheel == filter && m_FilterNameProperty && m_FilterPositionProperty)
311 return;
312 else if (m_FilterWheel)
313 m_FilterWheel->disconnect(this);
314
315 m_FilterWheel = filter;
316
317 m_FilterNameProperty = nullptr;
318 m_FilterPositionProperty = nullptr;
319 m_FilterConfirmSet = nullptr;
320
321 if (!m_FilterWheel)
322 return;
323
324 connect(m_FilterWheel, &ISD::ConcreteDevice::propertyUpdated, this, &FilterManager::updateProperty);
325 connect(m_FilterWheel, &ISD::ConcreteDevice::Disconnected, this, &FilterManager::processDisconnect);
326
327 refreshFilterProperties();
328}
329
330void FilterManager::refreshFilterProperties()
331{
332 if (m_FilterNameProperty && m_FilterPositionProperty)
333 {
334 if (m_FilterConfirmSet == nullptr)
335 m_FilterConfirmSet = m_FilterWheel->getSwitch("CONFIRM_FILTER_SET");
336
337 // All filters are synced up?
338 if (m_currentFilterLabels.count() == m_FilterNameProperty->ntp)
339 return;
340 }
341
342 filterNameLabel->setText(m_FilterWheel->getDeviceName());
343
344 m_currentFilterLabels.clear();
345
346 m_FilterNameProperty = m_FilterWheel->getText("FILTER_NAME");
347 m_FilterPositionProperty = m_FilterWheel->getNumber("FILTER_SLOT");
348 m_FilterConfirmSet = m_FilterWheel->getSwitch("CONFIRM_FILTER_SET");
349
350 refreshFilterLabels();
351 refreshFilterPosition();
352
353 if (m_currentFilterPosition >= 1 && m_currentFilterPosition <= m_ActiveFilters.count())
354 lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
355}
356
357QStringList FilterManager::getFilterLabels(bool forceRefresh)
358{
359 if ((!m_currentFilterLabels.empty() && forceRefresh == false) || !m_FilterNameProperty || !m_FilterPositionProperty)
360 return m_currentFilterLabels;
361
362 QStringList filterList;
363
364 for (int i = 0; i < m_FilterPositionProperty->np[0].max; i++)
365 {
366 if (m_FilterNameProperty != nullptr && (i < m_FilterNameProperty->ntp))
367 filterList.append(m_FilterNameProperty->tp[i].text);
368 }
369
370 return filterList;
371}
372
373int FilterManager::getFilterPosition(bool forceRefresh)
374{
375 if (forceRefresh == false || m_FilterPositionProperty == nullptr)
376 return m_currentFilterPosition;
377
378 return static_cast<int>(m_FilterPositionProperty->np[0].value);
379}
380
381void FilterManager::refreshFilterLabels()
382{
383 QList filters = getFilterLabels(true);
384
385 if (filters != m_currentFilterLabels)
386 {
387 m_currentFilterLabels = filters;
388 refreshFilterModel();
389
390 emit labelsChanged(filters);
391
392 // refresh position after filter changes
393 refreshFilterPosition();
394 }
395}
396
397void FilterManager::refreshFilterPosition()
398{
399
400 int pos = getFilterPosition(true);
401 if (pos != m_currentFilterPosition)
402 {
403 m_currentFilterPosition = pos;
404 emit positionChanged(pos);
405 }
406}
407
408bool FilterManager::setFilterPosition(uint8_t position, FilterPolicy policy)
409{
410 // Position 1 to Max
411 if (position > m_ActiveFilters.count())
412 return false;
413
414 m_Policy = policy;
415 currentFilter = m_ActiveFilters[m_currentFilterPosition - 1];
416 targetFilter = m_ActiveFilters[position - 1];
417
418 if (currentFilter == targetFilter)
419 {
420 emit ready();
421 return true;
422 }
423
424 buildOperationQueue(FILTER_CHANGE);
425
426 executeOperationQueue();
427
428 return true;
429}
430
431
432void FilterManager::updateProperty(INDI::Property prop)
433{
434 if (m_FilterWheel == nullptr || m_FilterWheel->getDeviceName() != prop.getDeviceName())
435 return;
436
437 if (prop.isNameMatch("FILTER_NAME"))
438 {
439 auto tvp = prop.getText();
440 m_FilterNameProperty = tvp;
441
442 refreshFilterLabels();
443 }
444 else if (prop.isNameMatch("FILTER_SLOT"))
445 {
446 m_FilterChangeTimeout.stop();
447
448 auto nvp = prop.getNumber();
449 // If filter fails to change position while we request that
450 // fail immediately.
451 if (state == FILTER_CHANGE && nvp->s == IPS_ALERT)
452 {
453 emit failed();
454 return;
455 }
456
457 if (nvp->s != IPS_OK)
458 return;
459
460 m_FilterPositionProperty = nvp;
461 refreshFilterPosition();
462
463 if (state == FILTER_CHANGE)
464 executeOperationQueue();
465 // If filter is changed externally, record its current offset as the starting offset.
466 else if (state == FILTER_IDLE && m_ActiveFilters.count() >= m_currentFilterPosition)
467 lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
468 }
469}
470
471
472void FilterManager::processDisconnect()
473{
474 m_currentFilterLabels.clear();
475 m_currentFilterPosition = -1;
476 m_FilterNameProperty = nullptr;
477 m_FilterPositionProperty = nullptr;
478}
479
480void FilterManager::buildOperationQueue(FilterState operation)
481{
482 operationQueue.clear();
483 m_useTargetFilter = false;
484
485 switch (operation)
486 {
487 case FILTER_CHANGE:
488 {
489 if ( (m_Policy & CHANGE_POLICY) && targetFilter != currentFilter)
490 m_useTargetFilter = true;
491
492 if (m_useTargetFilter)
493 {
494 operationQueue.enqueue(FILTER_CHANGE);
495 if (m_FocusReady && (m_Policy & OFFSET_POLICY))
496 operationQueue.enqueue(FILTER_OFFSET);
497 else
498 {
499 // Keep track of filter and offset either here or after the offset has been processed
500 lastFilterOffset = targetFilter->offset();
501 currentFilter = targetFilter;
502 }
503
504 }
505
506 if (m_FocusReady && (m_Policy & AUTOFOCUS_POLICY) && targetFilter->useAutoFocus())
507 operationQueue.enqueue(FILTER_AUTOFOCUS);
508 }
509 break;
510
511 default:
512 break;
513 }
514}
515
516bool FilterManager::executeOperationQueue()
517{
518 if (operationQueue.isEmpty())
519 {
520 state = FILTER_IDLE;
521 emit newStatus(state);
522 emit ready();
523 return false;
524 }
525
526 FilterState nextOperation = operationQueue.dequeue();
527
528 bool actionRequired = true;
529
530 switch (nextOperation)
531 {
532 case FILTER_CHANGE:
533 {
534 m_FilterChangeTimeout.stop();
535
536 if (m_ConfirmationPending)
537 return true;
538
539 state = FILTER_CHANGE;
540 if (m_useTargetFilter)
541 targetFilterPosition = m_ActiveFilters.indexOf(targetFilter) + 1;
542 m_FilterWheel->setPosition(targetFilterPosition);
543
544 emit newStatus(state);
545
546 if (m_FilterConfirmSet)
547 {
548 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this]()
549 {
550 KSMessageBox::Instance()->disconnect(this);
551 m_ConfirmationPending = false;
552 m_FilterWheel->confirmFilter();
553 });
554 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
555 {
556 KSMessageBox::Instance()->disconnect(this);
557 m_ConfirmationPending = false;
558 });
559
560 m_ConfirmationPending = true;
561
562 KSMessageBox::Instance()->questionYesNo(i18n("Set filter to %1. Is filter set?", targetFilter->color()),
563 i18n("Confirm Filter"));
564 }
565 // If automatic filter change with filter wheel, we start operation timeout
566 else m_FilterChangeTimeout.start();
567 }
568 break;
569
570 case FILTER_OFFSET:
571 {
572 state = FILTER_OFFSET;
573 if (m_useTargetFilter)
574 {
575 targetFilterOffset = targetFilter->offset() - lastFilterOffset;
576 lastFilterOffset = targetFilter->offset();
577 currentFilter = targetFilter;
578 m_useTargetFilter = false;
579 }
580 if (targetFilterOffset == 0)
581 actionRequired = false;
582 else
583 {
584 emit newFocusOffset(targetFilterOffset, false);
585 emit newStatus(state);
586 }
587 }
588 break;
589
590 case FILTER_AUTOFOCUS:
591 state = FILTER_AUTOFOCUS;
592 qCDebug(KSTARS) << "FilterManager.cpp is triggering autofocus.";
593 emit newStatus(state);
594 emit runAutoFocus(AutofocusReason::FOCUS_FILTER, "");
595 break;
596
597 default:
598 break;
599 }
600
601 // If an additional action is required, return return and continue later
602 if (actionRequired)
603 return true;
604 // Otherwise, continue processing the queue
605 else
606 return executeOperationQueue();
607}
608
609bool FilterManager::executeOneOperation(FilterState operation)
610{
611 bool actionRequired = false;
612
613 switch (operation)
614 {
615 default:
616 break;
617 }
618
619 return actionRequired;
620}
621
622void FilterManager::setFocusOffsetComplete()
623{
624 if (state == FILTER_OFFSET)
625 executeOperationQueue();
626}
627
628double FilterManager::getFilterExposure(const QString &name) const
629{
630 auto filterDetails = getFilterByName(name);
631 if (filterDetails)
632 return filterDetails->exposure();
633
634 // Default value
635 return 1;
636}
637
638bool FilterManager::setFilterExposure(int index, double exposure)
639{
640 if (m_currentFilterLabels.empty())
641 return false;
642
643 QString color = m_currentFilterLabels[index];
644 for (int i = 0; i < m_ActiveFilters.count(); i++)
645 {
646 if (color == m_ActiveFilters[i]->color())
647 {
648 m_FilterModel->setData(m_FilterModel->index(i, FM_EXPOSURE), exposure);
649 m_FilterModel->submitAll();
650 refreshFilterModel();
651 return true;
652 }
653 }
654
655 return false;
656}
657
658int FilterManager::getFilterOffset(const QString &name) const
659{
660 int offset = INVALID_VALUE;
661 auto filterDetails = getFilterByName(name);
662 if (filterDetails)
663 offset = filterDetails->offset();
664
665 return offset;
666}
667
668bool FilterManager::setFilterOffset(QString color, int offset)
669{
670 if (m_currentFilterLabels.empty())
671 return false;
672
673 for (int i = 0; i < m_ActiveFilters.count(); i++)
674 {
675 if (color == m_ActiveFilters[i]->color())
676 {
677 m_FilterModel->setData(m_FilterModel->index(i, FM_OFFSET), offset);
678 m_FilterModel->submitAll();
679 refreshFilterModel();
680 return true;
681 }
682 }
683
684 return false;
685}
686
687bool FilterManager::getFilterAbsoluteFocusDetails(const QString &name, int &focusPos, double &focusTemp,
688 double &focusAlt) const
689{
690 auto filterDetails = getFilterByName(name);
691 if (filterDetails)
692 {
693 focusPos = filterDetails->absoluteFocusPosition();
694 focusTemp = filterDetails->focusTemperature();
695 focusAlt = filterDetails->focusAltitude();
696 return true;
697 }
698
699 return false;
700}
701
702bool FilterManager::setFilterAbsoluteFocusDetails(int index, int focusPos, double focusTemp, double focusAlt)
703{
704 if (index < 0 || index >= m_currentFilterLabels.count())
705 return false;
706
707 QString color = m_currentFilterLabels[index];
708 for (int i = 0; i < m_ActiveFilters.count(); i++)
709 {
710 if (color == m_ActiveFilters[i]->color())
711 {
712 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_SOLUTION), focusPos);
713 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_TEMP), focusTemp);
714 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_ALT), focusAlt);
715 m_FilterModel->submitAll();
716 refreshFilterModel();
717 return true;
718 }
719 }
720
721 return false;
722}
723
724QString FilterManager::getFilterLock(const QString &name) const
725{
726 auto filterDetails = getFilterByName(name);
727 if (filterDetails)
728 return filterDetails->lockedFilter();
729
730 // Default value
731 return NULL_FILTER;
732}
733
734bool FilterManager::setFilterLock(int index, QString name)
735{
736 if (m_currentFilterLabels.empty())
737 return false;
738
739 QString color = m_currentFilterLabels[index];
740 for (int i = 0; i < m_ActiveFilters.count(); i++)
741 {
742 if (color == m_ActiveFilters[i]->color())
743 {
744 m_FilterModel->setData(m_FilterModel->index(i, FM_LOCK_FILTER), name);
745 m_FilterModel->submitAll();
746 refreshFilterModel();
747 return true;
748 }
749 }
750
751 return false;
752}
753
754int FilterManager::getFilterWavelength(const QString &name) const
755{
756 auto filterDetails = getFilterByName(name);
757 if (filterDetails)
758 return filterDetails->wavelength();
759
760 // Default value
761 return 500;
762}
763
764double FilterManager::getFilterTicksPerTemp(const QString &name) const
765{
766 auto filterDetails = getFilterByName(name);
767 if (filterDetails)
768 return filterDetails->focusTicksPerTemp();
769
770 // Something's wrong so return 0
771 return 0.0;
772}
773
774double FilterManager::getFilterTicksPerAlt(const QString &name) const
775{
776 auto filterDetails = getFilterByName(name);
777 if (filterDetails)
778 return filterDetails->focusTicksPerAlt();
779
780 // Something's wrong so return 0
781 return 0.0;
782}
783
784OAL::Filter * FilterManager::getFilterByName(const QString &name) const
785{
786 if (m_currentFilterLabels.empty() ||
787 m_currentFilterPosition < 1 ||
788 m_currentFilterPosition > m_currentFilterLabels.count())
789 return nullptr;
790
791 QString color = name;
792 if (color.isEmpty())
793 color = m_currentFilterLabels[m_currentFilterPosition - 1];
794
795 auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), [color](OAL::Filter * oneFilter)
796 {
797 return (oneFilter->color() == color);
798 });
799
800 if (pos != m_ActiveFilters.end())
801 return (*pos);
802 else
803 return nullptr;
804}
805
806void FilterManager::removeDevice(const QSharedPointer<ISD::GenericDevice> &device)
807{
808 if (m_FilterWheel && (m_FilterWheel->getDeviceName() == device->getDeviceName()))
809 {
810 m_FilterNameProperty = nullptr;
811 m_FilterPositionProperty = nullptr;
812 m_FilterWheel = nullptr;
813 m_currentFilterLabels.clear();
814 m_currentFilterPosition = 0;
815 qDeleteAll(m_ActiveFilters);
816 m_ActiveFilters.clear();
817 delete(m_FilterModel);
818 m_FilterModel = nullptr;
819 }
820}
821
822void FilterManager::setFocusStatus(Ekos::FocusState focusState)
823{
824 if (state == FILTER_AUTOFOCUS)
825 {
826 switch (focusState)
827 {
828 case FOCUS_COMPLETE:
829 executeOperationQueue();
830 break;
831
832 case FOCUS_FAILED:
833 if (++retries == 3)
834 {
835 retries = 0;
836 emit failed();
837 return;
838 }
839 // Restart again
840 emit runAutoFocus(AutofocusReason::FOCUS_FILTER, "");
841 break;
842
843 default:
844 break;
845
846 }
847 }
848}
849
850bool FilterManager::syncAbsoluteFocusPosition(int index)
851{
852 if (m_FocusReady == false)
853 {
854 qCWarning(KSTARS_INDI) << __FUNCTION__ << "No Focuser detected.";
855 return true;
856 }
857 else if (index < 0 || index > m_ActiveFilters.count())
858 {
859 // We've been asked to set the focus position but something's wrong because
860 // the passed in filter index is bad. Give up and return true - returning false
861 // just results in an infinite retry loop.
862 qCWarning(KSTARS_INDI) << __FUNCTION__ << "index" << index << "is out of bounds.";
863 return true;
864 }
865
866 // By default filter absolute focus offset is zero
867 // JM 2023.07.03: So if it is zero, we return immediately.
868 auto absFocusPos = m_ActiveFilters[index]->absoluteFocusPosition();
869
870 if (m_FocusAbsPosition == absFocusPos || absFocusPos <= 0)
871 {
872 m_FocusAbsPositionPending = false;
873 return true;
874 }
875 else if (m_FocusAbsPositionPending == false)
876 {
877 m_FocusAbsPositionPending = true;
878 emit newFocusOffset(absFocusPos, true);
879 }
880
881 return false;
882}
883
884bool FilterManager::setFilterNames(const QStringList &newLabels)
885{
886 if (m_FilterWheel == nullptr || m_currentFilterLabels.empty())
887 return false;
888
889 m_FilterWheel->setLabels(newLabels);
890 return true;
891}
892
893QJsonObject FilterManager::toJSON()
894{
895 if (!m_FilterWheel)
896 return QJsonObject();
897
898 QJsonArray filters;
899
900 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
901 {
902 QJsonObject oneFilter =
903 {
904 {"index", i},
905 {"label", m_FilterModel->data(m_FilterModel->index(i, FM_LABEL)).toString()},
906 {"exposure", m_FilterModel->data(m_FilterModel->index(i, FM_EXPOSURE)).toDouble()},
907 {"offset", m_FilterModel->data(m_FilterModel->index(i, FM_OFFSET)).toInt()},
908 {"autofocus", m_FilterModel->data(m_FilterModel->index(i, FM_AUTO_FOCUS)).toBool()},
909 {"lock", m_FilterModel->data(m_FilterModel->index(i, FM_LOCK_FILTER)).toString()},
910 {"lastafsolution", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_SOLUTION)).toInt()},
911 {"lastaftemp", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_TEMP)).toDouble()},
912 {"lastafalt", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_ALT)).toDouble()},
913 {"tickspertemp", m_FilterModel->data(m_FilterModel->index(i, FM_TICKS_PER_TEMP)).toDouble()},
914 {"ticksperalt", m_FilterModel->data(m_FilterModel->index(i, FM_TICKS_PER_ALT)).toDouble()},
915 {"wavelength", m_FilterModel->data(m_FilterModel->index(i, FM_WAVELENGTH)).toInt()},
916 };
917
918 filters.append(oneFilter);
919 }
920
921 QJsonObject data =
922 {
923 {"device", m_FilterWheel->getDeviceName()},
924 {"filters", filters}
925 };
926
927 return data;
928
929}
930
931void FilterManager::setFilterData(const QJsonObject &settings)
932{
933 if (!m_FilterWheel)
934 return;
935
936 if (settings["device"].toString() != m_FilterWheel->getDeviceName())
937 return;
938
939 QJsonArray filters = settings["filters"].toArray();
940 QStringList labels = getFilterLabels();
941
942 for (auto oneFilterRef : filters)
943 {
944 QJsonObject oneFilter = oneFilterRef.toObject();
945 int row = oneFilter["index"].toInt();
946
947 labels[row] = oneFilter["label"].toString();
948 m_FilterModel->setData(m_FilterModel->index(row, FM_LABEL), oneFilter["label"].toString());
949 m_FilterModel->setData(m_FilterModel->index(row, FM_EXPOSURE), oneFilter["exposure"].toDouble());
950 m_FilterModel->setData(m_FilterModel->index(row, FM_OFFSET), oneFilter["offset"].toInt());
951 m_FilterModel->setData(m_FilterModel->index(row, FM_AUTO_FOCUS), oneFilter["autofocus"].toBool());
952 m_FilterModel->setData(m_FilterModel->index(row, FM_LOCK_FILTER), oneFilter["lock"].toString());
953 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_SOLUTION), oneFilter["lastafsolution"].toInt());
954 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_TEMP), oneFilter["lastaftemp"].toDouble());
955 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_ALT), oneFilter["lastafalt"].toDouble());
956 m_FilterModel->setData(m_FilterModel->index(row, FM_TICKS_PER_TEMP), oneFilter["tickspertemp"].toDouble());
957 m_FilterModel->setData(m_FilterModel->index(row, FM_TICKS_PER_ALT), oneFilter["ticksperalt"].toDouble());
958 m_FilterModel->setData(m_FilterModel->index(row, FM_WAVELENGTH), oneFilter["wavelength"].toInt());
959 }
960
961 m_FilterModel->submitAll();
962 setFilterNames(labels);
963
964 refreshFilterModel();
965}
966
967void FilterManager::buildFilterOffsets()
968{
969 // Launch the Build Filter Offsets utility. The utility uses a sync call to launch the dialog
970 QSharedPointer<FilterManager> filterManager;
971 Ekos::Manager::Instance()->getFilterManager(m_FilterWheel->getDeviceName(), filterManager);
972 BuildFilterOffsets bfo(filterManager);
973}
974
975void FilterManager::signalRunAutoFocus(AutofocusReason autofocusReason, const QString &reasonInfo)
976{
977 // BuildFilterOffsets signalled runAutoFocus so pass signal to Focus
978 emit runAutoFocus(autofocusReason, reasonInfo);
979}
980
981void FilterManager::autoFocusComplete(FocusState completionState, int currentPosition, double currentTemperature,
982 double currentAlt)
983{
984 // Focus signalled Autofocus completed so pass signal to BuildFilterOffsets
985 emit autoFocusDone(completionState, currentPosition, currentTemperature, currentAlt);
986}
987
988void FilterManager::signalAbortAutoFocus()
989{
990 // BuildFilterOffsets signalled abortAutoFocus so pass signal to Focus
991 emit abortAutoFocus();
992}
993
994void FilterManager::checkFilterChangeTimeout()
995{
996 if (state == FILTER_CHANGE)
997 {
998 qCWarning(KSTARS) << "FilterManager.cpp filter change timed out.";
999 emit failed();
1000 }
1001}
1002}
bool AddFilter(const filterProperties *fp)
Add a new filter to the database.
KSUserDB * userdb()
Definition kstarsdata.h:217
Information of user filters.
Definition filter.h:48
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
QString name(StandardAction id)
const QList< QKeySequence > & close()
QString label(StandardShortcut id)
void clicked(bool checked)
void toggled(bool checked)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void accepted()
void rejected()
void append(const QJsonValue &value)
void append(QList< T > &&value)
int column() const const
int row() const const
bool disconnect(const QMetaObject::Connection &connection)
QSqlDatabase database(const QString &connectionName, bool open)
QVariant value(const QString &name) const const
QChar * data()
bool isEmpty() const const
qsizetype length() const const
ToolTipRole
Horizontal
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.