1 /*
2     SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "filtermanager.h"
8 #include <kstars_debug.h>
9 
10 #include "indi_debug.h"
11 #include "kstarsdata.h"
12 #include "kstars.h"
13 #include "Options.h"
14 #include "auxiliary/kspaths.h"
15 #include "auxiliary/ksmessagebox.h"
16 #include "ekos/auxiliary/filterdelegate.h"
17 
18 #include <QTimer>
19 #include <QSqlTableModel>
20 #include <QSqlDatabase>
21 #include <QSqlRecord>
22 
23 #include <basedevice.h>
24 
25 #include <algorithm>
26 
27 namespace Ekos
28 {
29 
FilterManager()30 FilterManager::FilterManager() : QDialog(KStars::Instance())
31 {
32 #ifdef Q_OS_OSX
33     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
34 #endif
35 
36     setupUi(this);
37 
38     connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
39     connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
40 
41     QSqlDatabase userdb = QSqlDatabase::cloneDatabase(KStarsData::Instance()->userdb()->GetDatabase(), "filter_db");
42     userdb.open();
43 
44     kcfg_FlatSyncFocus->setChecked(Options::flatSyncFocus());
45     connect(kcfg_FlatSyncFocus, &QCheckBox::toggled, [this]()
46     {
47         Options::setFlatSyncFocus(kcfg_FlatSyncFocus->isChecked());
48     });
49 
50     filterModel = new QSqlTableModel(this, userdb);
51     filterView->setModel(filterModel);
52 
53     // No Edit delegate
54     noEditDelegate = new NotEditableDelegate(filterView);
55     filterView->setItemDelegateForColumn(4, noEditDelegate);
56 
57     // Exposure delegate
58     exposureDelegate = new ExposureDelegate(filterView);
59     filterView->setItemDelegateForColumn(5, exposureDelegate);
60 
61     // Offset delegate
62     offsetDelegate = new OffsetDelegate(filterView);
63     filterView->setItemDelegateForColumn(6, offsetDelegate);
64 
65     // Auto Focus delegate
66     useAutoFocusDelegate = new UseAutoFocusDelegate(filterView);
67     filterView->setItemDelegateForColumn(7, useAutoFocusDelegate);
68 
69     // Set Delegates
70     lockDelegate = new LockDelegate(m_currentFilterLabels, filterView);
71     filterView->setItemDelegateForColumn(8, lockDelegate);
72 
73     // Absolute Focus Position
74     filterView->setItemDelegateForColumn(9, noEditDelegate);
75 
76     connect(filterModel, &QSqlTableModel::dataChanged, [this](const QModelIndex & topLeft, const QModelIndex &,
77             const QVector<int> &)
78     {
79         reloadFilters();
80         if (topLeft.column() == 5)
81             emit exposureChanged(filterModel->data(topLeft).toDouble());
82     });
83 }
84 
refreshFilterModel()85 void FilterManager::refreshFilterModel()
86 {
87     if (m_currentFilterDevice == nullptr || m_currentFilterLabels.empty())
88         return;
89 
90     QString vendor(m_currentFilterDevice->getDeviceName());
91 
92     //QSqlDatabase::removeDatabase("filter_db");
93     //QSqlDatabase userdb = QSqlDatabase::cloneDatabase(KStarsData::Instance()->userdb()->GetDatabase(), "filter_db");
94     //userdb.open();
95 
96     //delete (filterModel);
97 
98     //filterModel = new QSqlTableModel(this, userdb);
99     if (!filterModel)
100     {
101         QSqlDatabase userdb = QSqlDatabase::cloneDatabase(KStarsData::Instance()->userdb()->GetDatabase(), "filter_db");
102         userdb.open();
103         filterModel = new QSqlTableModel(this, userdb);
104         filterView->setModel(filterModel);
105     }
106     filterModel->setTable("filter");
107     filterModel->setFilter(QString("vendor='%1'").arg(vendor));
108     filterModel->select();
109     filterModel->setEditStrategy(QSqlTableModel::OnFieldChange);
110 
111     // If we have an existing table but it doesn't match the number of current filters
112     // then we remove it.
113     if (filterModel->rowCount() > 0 && filterModel->rowCount() != m_currentFilterLabels.count())
114     {
115         for (int i = 0; i < filterModel->rowCount(); i++)
116             filterModel->removeRow(i);
117 
118         filterModel->select();
119     }
120 
121     // If it is first time, let's populate data
122     if (filterModel->rowCount() == 0)
123     {
124         for (QString &filter : m_currentFilterLabels)
125             KStarsData::Instance()->userdb()->AddFilter(vendor, "", "", filter, 0, 1.0, false, "--", 0);
126 
127         filterModel->select();
128         // Seems ->select() is not enough, have to create a new model.
129         /*delete (filterModel);
130         filterModel = new QSqlTableModel(this, userdb);
131         filterModel->setTable("filter");
132         filterModel->setFilter(QString("vendor='%1'").arg(m_currentFilterDevice->getDeviceName()));
133         filterModel->select();
134         filterModel->setEditStrategy(QSqlTableModel::OnManualSubmit);*/
135     }
136     // Make sure all the filter colors match DB. If not update model to sync with INDI filter values
137     else
138     {
139         for (int i = 0; i < filterModel->rowCount(); ++i)
140         {
141             QModelIndex index = filterModel->index(i, 4);
142             if (filterModel->data(index).toString() != m_currentFilterLabels[i])
143             {
144                 filterModel->setData(index, m_currentFilterLabels[i]);
145             }
146         }
147     }
148 
149     lockDelegate->setCurrentFilterList(m_currentFilterLabels);
150 
151     filterModel->setHeaderData(4, Qt::Horizontal, i18n("Filter"));
152 
153     filterModel->setHeaderData(5, Qt::Horizontal, i18n("Filter exposure time during focus"), Qt::ToolTipRole);
154     filterModel->setHeaderData(5, Qt::Horizontal, i18n("Exposure"));
155 
156     filterModel->setHeaderData(6, Qt::Horizontal, i18n("Relative offset in steps"), Qt::ToolTipRole);
157     filterModel->setHeaderData(6, Qt::Horizontal, i18n("Offset"));
158 
159     filterModel->setHeaderData(7, Qt::Horizontal, i18n("Start Auto Focus when filter is activated"), Qt::ToolTipRole);
160     filterModel->setHeaderData(7, Qt::Horizontal, i18n("Auto Focus"));
161 
162     filterModel->setHeaderData(8, Qt::Horizontal, i18n("Lock specific filter when running Auto Focus"), Qt::ToolTipRole);
163     filterModel->setHeaderData(8, Qt::Horizontal, i18n("Lock Filter"));
164 
165     filterModel->setHeaderData(9, Qt::Horizontal,
166                                i18n("Flat frames are captured at this focus position. It is updated automatically by focus process if enabled."),
167                                Qt::ToolTipRole);
168     filterModel->setHeaderData(9, Qt::Horizontal, i18n("Flat Focus Position"));
169 
170     filterView->hideColumn(0);
171     filterView->hideColumn(1);
172     filterView->hideColumn(2);
173     filterView->hideColumn(3);
174 
175     reloadFilters();
176 }
177 
reloadFilters()178 void FilterManager::reloadFilters()
179 {
180     qDeleteAll(m_ActiveFilters);
181     currentFilter = nullptr;
182     targetFilter = nullptr;
183     m_ActiveFilters.clear();
184     operationQueue.clear();
185 
186     for (int i = 0; i < filterModel->rowCount(); ++i)
187     {
188         QSqlRecord record = filterModel->record(i);
189         QString id        = record.value("id").toString();
190         QString vendor    = record.value("Vendor").toString();
191         QString model     = record.value("Model").toString();
192         QString type      = record.value("Type").toString();
193         QString color     = record.value("Color").toString();
194         double exposure   = record.value("Exposure").toDouble();
195         int offset        = record.value("Offset").toInt();
196         QString lockedFilter  = record.value("LockedFilter").toString();
197         bool useAutoFocus = record.value("UseAutoFocus").toInt() == 1;
198         int absFocusPos   = record.value("AbsoluteFocusPosition").toInt();
199         OAL::Filter *o    = new OAL::Filter(id, model, vendor, type, color, exposure, offset, useAutoFocus, lockedFilter,
200                                             absFocusPos);
201         m_ActiveFilters.append(o);
202     }
203 }
204 
setCurrentFilterWheel(ISD::GDInterface * filter)205 void FilterManager::setCurrentFilterWheel(ISD::GDInterface *filter)
206 {
207     if (m_currentFilterDevice == filter)
208         return;
209     else if (m_currentFilterDevice)
210         m_currentFilterDevice->disconnect(this);
211 
212     m_currentFilterDevice = filter;
213 
214     connect(filter, &ISD::GenericDevice::textUpdated, this, &FilterManager::processText);
215     connect(filter, &ISD::GenericDevice::numberUpdated, this, &FilterManager::processNumber);
216     connect(filter, &ISD::GenericDevice::switchUpdated, this, &FilterManager::processSwitch);
217     connect(filter, &ISD::GDInterface::Disconnected, [&]()
218     {
219         m_currentFilterLabels.clear();
220         m_currentFilterPosition = -1;
221         //m_currentFilterDevice = nullptr;
222         m_FilterNameProperty = nullptr;
223         m_FilterPositionProperty = nullptr;
224     });
225 
226     m_FilterNameProperty = nullptr;
227     m_FilterPositionProperty = nullptr;
228     m_FilterConfirmSet = nullptr;
229     initFilterProperties();
230 }
231 
initFilterProperties()232 void FilterManager::initFilterProperties()
233 {
234     if (m_FilterNameProperty && m_FilterPositionProperty)
235     {
236         if (m_FilterConfirmSet == nullptr)
237             m_FilterConfirmSet = m_currentFilterDevice->getBaseDevice()->getSwitch("CONFIRM_FILTER_SET");
238         return;
239     }
240 
241     filterNameLabel->setText(m_currentFilterDevice->getDeviceName());
242 
243     m_currentFilterLabels.clear();
244 
245     m_FilterNameProperty = m_currentFilterDevice->getBaseDevice()->getText("FILTER_NAME");
246     m_FilterPositionProperty = m_currentFilterDevice->getBaseDevice()->getNumber("FILTER_SLOT");
247     m_FilterConfirmSet = m_currentFilterDevice->getBaseDevice()->getSwitch("CONFIRM_FILTER_SET");
248 
249     m_currentFilterPosition = getFilterPosition(true);
250     m_currentFilterLabels = getFilterLabels(true);
251 
252     if (m_currentFilterLabels.isEmpty() == false)
253         refreshFilterModel();
254 
255     if (m_currentFilterPosition >= 1 && m_currentFilterPosition <= m_ActiveFilters.count())
256         lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
257 }
258 
getFilterLabels(bool forceRefresh)259 QStringList FilterManager::getFilterLabels(bool forceRefresh)
260 {
261     if (forceRefresh == false || m_FilterNameProperty == nullptr)
262         return m_currentFilterLabels;
263 
264     QStringList filterList;
265 
266     QStringList filterAlias = Options::filterAlias();
267 
268     for (int i = 0; i < m_FilterPositionProperty->np[0].max; i++)
269     {
270         QString item;
271 
272         if (m_FilterNameProperty != nullptr && (i < m_FilterNameProperty->ntp))
273             item = m_FilterNameProperty->tp[i].text;
274         else if (i < filterAlias.count() && filterAlias[i].isEmpty() == false)
275             item = filterAlias.at(i);
276 
277         filterList.append(item);
278     }
279 
280     return filterList;
281 }
282 
getFilterPosition(bool forceRefresh)283 int FilterManager::getFilterPosition(bool forceRefresh)
284 {
285     if (forceRefresh == false)
286         return m_currentFilterPosition;
287 
288     return static_cast<int>(m_FilterPositionProperty->np[0].value);
289 }
290 
setFilterPosition(uint8_t position,FilterPolicy policy)291 bool FilterManager::setFilterPosition(uint8_t position, FilterPolicy policy)
292 {
293     // Position 1 to Max
294     if (position > m_ActiveFilters.count())
295         return false;
296 
297     m_Policy = policy;
298     currentFilter = m_ActiveFilters[m_currentFilterPosition - 1];
299     targetFilter = m_ActiveFilters[position - 1];
300 
301     if (currentFilter == targetFilter)
302     {
303         emit ready();
304         return true;
305     }
306 
307     buildOperationQueue(FILTER_CHANGE);
308 
309     executeOperationQueue();
310 
311     return true;
312 }
313 
processNumber(INumberVectorProperty * nvp)314 void FilterManager::processNumber(INumberVectorProperty *nvp)
315 {
316     if (nvp->s != IPS_OK || strcmp(nvp->name, "FILTER_SLOT") || m_currentFilterDevice == nullptr
317             || (nvp->device != m_currentFilterDevice->getDeviceName()))
318         return;
319 
320     m_FilterPositionProperty = nvp;
321 
322     if (m_currentFilterPosition != static_cast<int>(m_FilterPositionProperty->np[0].value))
323     {
324         m_currentFilterPosition = static_cast<int>(m_FilterPositionProperty->np[0].value);
325         emit positionChanged(m_currentFilterPosition);
326     }
327 
328     if (state == FILTER_CHANGE)
329         executeOperationQueue();
330     // If filter is changed externally, record its current offset as the starting offset.
331     else if (state == FILTER_IDLE && m_ActiveFilters.count() >= m_currentFilterPosition)
332         lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
333 
334     // Check if we have to apply Focus Offset
335     // Focus offsets are always applied first
336 
337 
338 
339     // Check if we have to start Auto Focus
340     // If new filter position changed, and new filter policy is to perform auto-focus then autofocus is initiated.
341 
342     // Capture Module
343     // 3x L ---> 3x HA ---> 3x L
344     // Capture calls setFilterPosition("L").
345     // 0. Change filter to desired filter "L"
346     // 1. Is there any offset from last offset that needs to be applied?
347     // 1.1 Yes --> Apply focus offset and wait until position is changed:
348     // 1.1.1 Position complete, now check for useAutoFocus policy (#2).
349     // 1.1.2 Position failed, retry?
350     // 1.2 No  --> Go to #2
351     // 2. Is there autofocus policy for current filter?
352     // 2.1. Yes --> Check filter lock policy
353     // 2.1.1 If filter lock is another filter --> Change filter
354     // 2.1.2 If filter lock is same filter --> proceed to 2.3
355     // 2.2 No --> Process to 2.3
356     // 2.3 filter lock policy filter is applied, start autofocus.
357     // 2.4 Autofocus complete. Check filter lock policy
358     // 2.4.1 If filter lock policy was applied --> revert filter
359     // 2.4.1.1 If filter offset policy is applicable --> Apply offset
360     // 2.4.1.2 If no filter offset policy is applicable --> Go to 2.5
361     // 2.4.2 No filter lock policy, go to 2.5
362     // 2.5 All complete, emit ready()
363 
364     // Example. Current filter L. setFilterPosition("HA"). AutoFocus = YES. HA lock policy: L, HA offset policy: +100 with respect to L
365     // Operation Stack. offsetDiff = 100
366     // If AutoFocus && current filter = lock policy filter
367     // AUTO_FOCUS (on L)
368     // CHANGE_FILTER (to HA)
369     // APPLY_OFFSET: +100
370 
371     // Example. Current filter L. setFilterPosition("HA"). AutoFocus = No. HA lock policy: L, HA offset policy: +100 with respect to L
372     // Operation Stack. offsetDiff = 100
373     // CHANGE_FILTER (to HA)
374     // APPLY_OFFSET: +100
375 
376 
377 
378 
379     // Example. Current filter R. setFilterPosition("B"). AutoFocus = YES. B lock policy: "--", B offset policy: +70 with respect to L
380     // R offset = -50 with respect to L
381     // FILTER_CHANGE (to B)
382     // FILTER_OFFSET (+120)
383     // AUTO_FOCUS
384 
385     // Example. Current filter R. setFilterPosition("HA"). AutoFocus = YES. HA lock policy: L, HA offset policy: +100 with respect to L
386     // R offset = -50 with respect to L
387     // Operation Stack. offsetDiff = +150
388     // CHANGE_FILTER (to L)
389     // APPLY_OFFSET: +50 (L - R)
390     // AUTO_FOCUS
391     // CHANGE_FILTER (HA)
392     // APPLY_OFFSET: +100
393 
394 
395 
396     // Example. Current filter R. setFilterPosition("HA"). AutoFocus = No. HA lock policy: L, HA offset policy: +100 with respect to L
397     // R offset = -50 with respect to L
398     // Operation Stack. offsetDiff = +150
399     // CHANGE_FILTER (to HA)
400     // APPLY_OFFSET: +150 (HA - R)
401 
402 
403 
404     // Example. Current filter L. setFilterPosition("R"). AutoFocus = Yes. R lock policy: R, R offset policy: -50 with respect to L
405     // Operation Stack. offsetDiff = -50
406     // CHANGE_FILTER (to R)
407     // APPLY_OFFSET: -50 (R - L)
408     // AUTO_FOCUS
409 
410 
411 }
412 
processText(ITextVectorProperty * tvp)413 void FilterManager::processText(ITextVectorProperty *tvp)
414 {
415     if (strcmp(tvp->name, "FILTER_NAME") || m_currentFilterDevice == nullptr
416             || (tvp->device != m_currentFilterDevice->getDeviceName())            )
417         return;
418 
419     m_FilterNameProperty = tvp;
420 
421     QStringList newFilterLabels = getFilterLabels(true);
422 
423     if (newFilterLabels != m_currentFilterLabels)
424     {
425         m_currentFilterLabels = newFilterLabels;
426 
427         refreshFilterModel();
428 
429         emit labelsChanged(newFilterLabels);
430     }
431 }
432 
processSwitch(ISwitchVectorProperty * svp)433 void FilterManager::processSwitch(ISwitchVectorProperty *svp)
434 {
435     if (m_currentFilterDevice == nullptr || (svp->device != m_currentFilterDevice->getDeviceName()))
436         return;
437 
438 }
439 
buildOperationQueue(FilterState operation)440 void FilterManager::buildOperationQueue(FilterState operation)
441 {
442     operationQueue.clear();
443     m_useTargetFilter = false;
444 
445     switch (operation)
446     {
447         case FILTER_CHANGE:
448         {
449             if ( (m_Policy & CHANGE_POLICY) && targetFilter != currentFilter)
450                 m_useTargetFilter = true;
451 
452             if (m_useTargetFilter)
453             {
454                 operationQueue.enqueue(FILTER_CHANGE);
455                 if (m_FocusReady && (m_Policy & OFFSET_POLICY))
456                     operationQueue.enqueue(FILTER_OFFSET);
457             }
458 
459             if (m_FocusReady && (m_Policy & AUTOFOCUS_POLICY) && targetFilter->useAutoFocus())
460                 operationQueue.enqueue(FILTER_AUTOFOCUS);
461         }
462         break;
463 
464         default:
465             break;
466     }
467 }
468 
executeOperationQueue()469 bool FilterManager::executeOperationQueue()
470 {
471     if (operationQueue.isEmpty())
472     {
473         state = FILTER_IDLE;
474         emit newStatus(state);
475         emit ready();
476         return false;
477     }
478 
479     FilterState nextOperation = operationQueue.dequeue();
480 
481     bool actionRequired = true;
482 
483     switch (nextOperation)
484     {
485         case FILTER_CHANGE:
486         {
487             if (m_ConfirmationPending)
488                 return true;
489 
490             state = FILTER_CHANGE;
491             if (m_useTargetFilter)
492                 targetFilterPosition = m_ActiveFilters.indexOf(targetFilter) + 1;
493             m_currentFilterDevice->runCommand(INDI_SET_FILTER, &targetFilterPosition);
494             emit newStatus(state);
495 
496             if (m_FilterConfirmSet)
497             {
498                 //                if (KMessageBox::questionYesNo(KStars::Instance(),
499                 //                        i18n("Set filter to %1. Is filter set?", targetFilter->color()),
500                 //                        i18n("Confirm Filter")) == KMessageBox::Yes)
501                 //                    m_currentFilterDevice->runCommand(INDI_CONFIRM_FILTER);
502 
503                 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this]()
504                 {
505                     //QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr);
506                     KSMessageBox::Instance()->disconnect(this);
507                     m_ConfirmationPending = false;
508                     m_currentFilterDevice->runCommand(INDI_CONFIRM_FILTER);
509                 });
510                 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
511                 {
512                     //QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr);
513                     KSMessageBox::Instance()->disconnect(this);
514                     m_ConfirmationPending = false;
515                 });
516 
517                 m_ConfirmationPending = true;
518 
519                 KSMessageBox::Instance()->questionYesNo(i18n("Set filter to %1. Is filter set?", targetFilter->color()),
520                                                         i18n("Confirm Filter"));
521             }
522         }
523         break;
524 
525         case FILTER_OFFSET:
526         {
527             state = FILTER_OFFSET;
528             if (m_useTargetFilter)
529             {
530                 targetFilterOffset = targetFilter->offset() - lastFilterOffset;
531                 lastFilterOffset   = targetFilter->offset();
532                 currentFilter = targetFilter;
533                 m_useTargetFilter = false;
534             }
535             if (targetFilterOffset == 0)
536                 actionRequired = false;
537             else
538             {
539                 emit newFocusOffset(targetFilterOffset, false);
540                 emit newStatus(state);
541             }
542         }
543         break;
544 
545         case FILTER_AUTOFOCUS:
546             state = FILTER_AUTOFOCUS;
547             qCDebug(KSTARS) << "FilterManager.cpp is triggering autofocus.";
548             emit newStatus(state);
549             emit checkFocus(0.01);
550             break;
551 
552         default:
553             break;
554     }
555 
556     // If an additional action is required, return return and continue later
557     if (actionRequired)
558         return true;
559     // Otherwise, continue processing the queue
560     else
561         return executeOperationQueue();
562 }
563 
executeOneOperation(FilterState operation)564 bool FilterManager::executeOneOperation(FilterState operation)
565 {
566     bool actionRequired = false;
567 
568     switch (operation)
569     {
570         default:
571             break;
572     }
573 
574     return actionRequired;
575 }
576 
setFocusOffsetComplete()577 void FilterManager::setFocusOffsetComplete()
578 {
579     if (state == FILTER_OFFSET)
580         executeOperationQueue();
581 }
582 
getFilterExposure(const QString & name) const583 double FilterManager::getFilterExposure(const QString &name) const
584 {
585     if (m_currentFilterLabels.empty() ||
586             m_currentFilterPosition < 1 ||
587             m_currentFilterPosition > m_currentFilterLabels.count())
588         return 1;
589 
590     QString color = name;
591     if (color.isEmpty())
592         color = m_currentFilterLabels[m_currentFilterPosition - 1];
593     // Search for locked filter by filter color name
594     auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), [color](OAL::Filter * oneFilter)
595     {
596         return (oneFilter->color() == color);
597     });
598 
599     if (pos != m_ActiveFilters.end())
600         return (*pos)->exposure();
601 
602     // Default value
603     return 1;
604 }
605 
setFilterExposure(int index,double exposure)606 bool FilterManager::setFilterExposure(int index, double exposure)
607 {
608     if (m_currentFilterLabels.empty())
609         return false;
610 
611     QString color = m_currentFilterLabels[index];
612     for (int i = 0; i < m_ActiveFilters.count(); i++)
613     {
614         if (color == m_ActiveFilters[i]->color())
615         {
616             filterModel->setData(filterModel->index(i, 5), exposure);
617             filterModel->submitAll();
618             refreshFilterModel();
619             return true;
620         }
621     }
622 
623     return false;
624 }
625 
setFilterAbsoluteFocusPosition(int index,int absFocusPos)626 bool FilterManager::setFilterAbsoluteFocusPosition(int index, int absFocusPos)
627 {
628     if (index < 0 || index >= m_currentFilterLabels.count())
629         return false;
630 
631     QString color = m_currentFilterLabels[index];
632     for (int i = 0; i < m_ActiveFilters.count(); i++)
633     {
634         if (color == m_ActiveFilters[i]->color())
635         {
636             filterModel->setData(filterModel->index(i, 9), absFocusPos);
637             filterModel->submitAll();
638             refreshFilterModel();
639             return true;
640         }
641     }
642 
643     return false;
644 }
645 
getFilterLock(const QString & name) const646 QString FilterManager::getFilterLock(const QString &name) const
647 {
648     // Search for locked filter by filter color name
649     auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), [name](OAL::Filter * oneFilter)
650     {
651         return (oneFilter->color() == name);
652     });
653 
654     if (pos != m_ActiveFilters.end())
655         return (*pos)->lockedFilter();
656 
657     // Default value
658     return "--";
659 }
660 
removeDevice(ISD::GDInterface * device)661 void FilterManager::removeDevice(ISD::GDInterface *device)
662 {
663     if (m_currentFilterDevice && (m_currentFilterDevice->getDeviceName() == device->getDeviceName()))
664     {
665         m_FilterNameProperty = nullptr;
666         m_FilterPositionProperty = nullptr;
667         m_currentFilterDevice = nullptr;
668         m_currentFilterLabels.clear();
669         m_currentFilterPosition = 0;
670         qDeleteAll(m_ActiveFilters);
671         m_ActiveFilters.clear();
672         delete(filterModel);
673         filterModel = nullptr;
674     }
675 }
676 
setFocusStatus(Ekos::FocusState focusState)677 void FilterManager::setFocusStatus(Ekos::FocusState focusState)
678 {
679     if (state == FILTER_AUTOFOCUS)
680     {
681         switch (focusState)
682         {
683             case FOCUS_COMPLETE:
684                 executeOperationQueue();
685                 break;
686 
687             case FOCUS_FAILED:
688                 if (++retries == 3)
689                 {
690                     retries = 0;
691                     emit failed();
692                     return;
693                 }
694                 // Restart again
695                 emit checkFocus(0.01);
696                 break;
697 
698             default:
699                 break;
700 
701         }
702     }
703 }
704 
syncAbsoluteFocusPosition(int index)705 bool FilterManager::syncAbsoluteFocusPosition(int index)
706 {
707     if (index < 0 || index > m_ActiveFilters.count())
708     {
709         qCWarning(KSTARS_INDI) << __FUNCTION__ << "index" << index << "is out of bounds.";
710         return false;
711     }
712 
713     int absFocusPos = m_ActiveFilters[index]->absoluteFocusPosition();
714 
715     if (m_FocusAbsPosition == absFocusPos)
716     {
717         m_FocusAbsPositionPending = false;
718         return true;
719     }
720     else if (m_FocusAbsPositionPending == false)
721     {
722         m_FocusAbsPositionPending = true;
723         emit newFocusOffset(absFocusPos, true);
724     }
725 
726     return false;
727 }
728 
setFilterNames(const QStringList & newLabels)729 bool FilterManager::setFilterNames(const QStringList &newLabels)
730 {
731     if (m_currentFilterDevice == nullptr || m_currentFilterLabels.empty())
732         return false;
733 
734     m_currentFilterDevice->runCommand(INDI_SET_FILTER_NAMES, const_cast<QStringList*>(&newLabels));
735     return true;
736 }
737 
toJSON()738 QJsonObject FilterManager::toJSON()
739 {
740     if (!m_currentFilterDevice)
741         return QJsonObject();
742 
743     QJsonArray filters;
744 
745     for (int i = 0; i < filterModel->rowCount(); ++i)
746     {
747         QJsonObject oneFilter =
748         {
749             {"index", i},
750             {"label", filterModel->data(filterModel->index(i, FM_LABEL)).toString()},
751             {"exposure", filterModel->data(filterModel->index(i, FM_EXPOSURE)).toDouble()},
752             {"offset", filterModel->data(filterModel->index(i, FM_OFFSET)).toInt()},
753             {"autofocus", filterModel->data(filterModel->index(i, FM_AUTO_FOCUS)).toBool()},
754             {"lock", filterModel->data(filterModel->index(i, FM_LOCK_FILTER)).toString()},
755             {"flat", filterModel->data(filterModel->index(i, FM_FLAT_FOCUS)).toInt()},
756         };
757 
758         filters.append(oneFilter);
759     }
760 
761     QJsonObject data =
762     {
763         {"device", m_currentFilterDevice->getDeviceName()},
764         {"filters", filters}
765     };
766 
767     return data;
768 
769 }
770 
setFilterData(const QJsonObject & settings)771 void FilterManager::setFilterData(const QJsonObject &settings)
772 {
773     if (!m_currentFilterDevice)
774         return;
775 
776     if (settings["device"].toString() != m_currentFilterDevice->getDeviceName())
777         return;
778 
779     QJsonArray filters = settings["filters"].toArray();
780     QStringList labels = getFilterLabels();
781 
782     for (auto oneFilterRef : filters)
783     {
784         QJsonObject oneFilter = oneFilterRef.toObject();
785         int row = oneFilter["index"].toInt();
786 
787         labels[row] = oneFilter["label"].toString();
788         filterModel->setData(filterModel->index(row, FM_LABEL), oneFilter["label"].toString());
789         filterModel->setData(filterModel->index(row, FM_EXPOSURE), oneFilter["exposure"].toDouble());
790         filterModel->setData(filterModel->index(row, FM_OFFSET), oneFilter["offset"].toInt());
791         filterModel->setData(filterModel->index(row, FM_AUTO_FOCUS), oneFilter["autofocus"].toBool());
792         filterModel->setData(filterModel->index(row, FM_LOCK_FILTER), oneFilter["lock"].toString());
793         filterModel->setData(filterModel->index(row, FM_FLAT_FOCUS), oneFilter["flat"].toInt());
794     }
795 
796     setFilterNames(labels);
797     filterModel->submitAll();
798     refreshFilterModel();
799 }
800 
801 }
802