[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kstars/filtermanager] kstars: Filter Manager ready for test. It controls all aspects of filter chan
From:       Jasem Mutlaq <null () kde ! org>
Date:       2017-09-30 19:55:52
Message-ID: E1dyNrc-0002Vj-Vv () code ! kde ! org
[Download RAW message or body]

Git commit 0125213e0465b1c0aa8b8ee7b46795e143c5d6ca by Jasem Mutlaq.
Committed on 30/09/2017 at 19:55.
Pushed by mutlaqja into branch 'filtermanager'.

Filter Manager ready for test. It controls all aspects of filter changes including \
offset management, locked filter, and autofocus

M  +1    -0    kstars/CMakeLists.txt
M  +506  -63   kstars/ekos/auxiliary/filtermanager.cpp
M  +124  -30   kstars/ekos/auxiliary/filtermanager.h
M  +25   -9    kstars/ekos/auxiliary/filtersettings.ui
M  +27   -36   kstars/ekos/ekosmanager.cpp
M  +1    -1    kstars/ekos/ekosmanager.h

https://commits.kde.org/kstars/0125213e0465b1c0aa8b8ee7b46795e143c5d6ca

diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt
index 1314bde77..81daab106 100644
--- a/kstars/CMakeLists.txt
+++ b/kstars/CMakeLists.txt
@@ -183,6 +183,7 @@ if (INDI_FOUND)
                        ekos/auxiliary/dustcap.cpp
                        ekos/auxiliary/darklibrary.cpp
                        ekos/auxiliary/filtermanager.cpp
+                       ekos/auxiliary/filterdelegate.cpp
 
                        # Capture
                        ekos/capture/capture.cpp
diff --git a/kstars/ekos/auxiliary/filtermanager.cpp \
b/kstars/ekos/auxiliary/filtermanager.cpp index 39a408c61..5946252ff 100644
--- a/kstars/ekos/auxiliary/filtermanager.cpp
+++ b/kstars/ekos/auxiliary/filtermanager.cpp
@@ -11,111 +11,192 @@
 
 #include <QSqlTableModel>
 #include <QSqlDatabase>
+#include <QSqlRecord>
+#include <algorithm>
 
 #include <basedevice.h>
 
 #include "kstarsdata.h"
 #include "kstars.h"
 #include "auxiliary/kspaths.h"
+#include "ekos/auxiliary/filterdelegate.h"
 
 #include "indi_debug.h"
 
 #include "Options.h"
 
+namespace Ekos
+{
+
 FilterManager::FilterManager() : QDialog(KStars::Instance())
 {
     setupUi(this);
 
-    refreshFilterData();
+    connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
+    connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
 }
 
-void FilterManager::refreshFilterData()
+
+void FilterManager::refreshFilterModel()
 {
     if (m_currentFilterDevice == nullptr)
         return;
 
+    QString vendor(m_currentFilterDevice->getDeviceName());
+
     delete (filterModel);
+
     filterModel = new QSqlTableModel(this, \
KStarsData::Instance()->userdb()->GetDatabase());  filterModel->setTable("filter");
+    filterModel->setFilter(QString("vendor='%1'").arg(vendor));
     filterModel->select();
+    filterModel->setEditStrategy(QSqlTableModel::OnFieldChange);
+
+    // If it is first time, let's populate data
+    if (filterModel->rowCount() == 0)
+    {
+        for (QString filter : m_currentFilterLabels)
+            KStarsData::Instance()->userdb()->AddFilter(vendor, "", "", filter, 0, \
1.0, false, "--"); +
+        // Seems ->select() is not enough, have to create a new model.
+        delete (filterModel);
+        filterModel = new QSqlTableModel(this, \
KStarsData::Instance()->userdb()->GetDatabase()); +        \
filterModel->setTable("filter"); +        \
filterModel->setFilter(QString("vendor='%1'").arg(m_currentFilterDevice->getDeviceName()));
 +        filterModel->select();
+        filterModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
+    }
+    // Make sure all the filter colors match DB. If not update model to sync with \
INDI filter values +    else
+    {
+        for (int i = 0; i < filterModel->rowCount(); ++i)
+        {
+            QModelIndex index = filterModel->index(i, 4);
+            if (filterModel->data(index).toString() != m_currentFilterLabels[i])
+            {
+                filterModel->setData(index, m_currentFilterLabels[i]);
+            }
+        }
+    }
+
+    filterModel->setHeaderData(4, Qt::Horizontal, i18n("Filter"));
+
+    filterModel->setHeaderData(5, Qt::Horizontal, i18n("Filter exposure time during \
focus"), Qt::ToolTipRole); +    filterModel->setHeaderData(5, Qt::Horizontal, \
i18n("Exposure")); +
+    filterModel->setHeaderData(6, Qt::Horizontal, i18n("Relative offset in steps"), \
Qt::ToolTipRole); +    filterModel->setHeaderData(6, Qt::Horizontal, i18n("Offset"));
+
+    filterModel->setHeaderData(7, Qt::Horizontal, i18n("Start AutoFocus when filter \
is activated"), Qt::ToolTipRole); +    filterModel->setHeaderData(7, Qt::Horizontal, \
i18n("AutoFocus")); +
+    filterModel->setHeaderData(8, Qt::Horizontal, i18n("Lock specific filter when \
running AutoFocus"), Qt::ToolTipRole); +    filterModel->setHeaderData(8, \
Qt::Horizontal, i18n("Lock Filter"));  
     filterView->setModel(filterModel);
+    filterView->hideColumn(0);
+    filterView->hideColumn(1);
+    filterView->hideColumn(2);
+    filterView->hideColumn(3);
 
-    // Get all OAL equipment filter list
-    KStarsData::Instance()->userdb()->GetAllFilters(m_FilterList);
-    m_ActiveFilters.clear();
+    // No Edit delegate
+    noEditDelegate = new NotEditableDelegate(filterView);
+    filterView->setItemDelegateForColumn(4, noEditDelegate);
+
+    // Exposure delegate
+    exposureDelegate = new ExposureDelegate(filterView);
+    filterView->setItemDelegateForColumn(5, exposureDelegate);
+
+    // Offset delegate
+    offsetDelegate = new OffsetDelegate(filterView);
+    filterView->setItemDelegateForColumn(6, offsetDelegate);
 
-    // Get all filters for the current filter device
-    QString currentFilterDevice = QString(m_currentFilterDevice->getDeviceName());
+    // Auto Focus delegate
+    useAutoFocusDelegate = new UseAutoFocusDelegate(filterView);
+    filterView->setItemDelegateForColumn(7, useAutoFocusDelegate);
 
-    for (OAL::Filter *oneFilter : m_FilterList)
+    // Set Delegates
+    lockDelegate = new LockDelegate(m_currentFilterLabels, filterView);
+    filterView->setItemDelegateForColumn(8, lockDelegate);
+
+    reloadFilters();
+
+    connect(filterModel, &QSqlTableModel::dataChanged, [this](const QModelIndex \
&topLeft, const QModelIndex &, const QVector<int> &)  {
-        if (oneFilter->vendor() == currentFilterDevice)
-            m_ActiveFilters.append(oneFilter);
-    }
+        reloadFilters();
+        if (topLeft.column() == 5)
+            emit exposureChanged(filterModel->data(topLeft).toDouble());
+    });
 }
 
-void FilterManager::addFilter(ISD::GDInterface *filter)
+void FilterManager::reloadFilters()
 {
-    m_filterDevices.append(filter);
-    m_currentFilterDevice = filter;
+    qDeleteAll(m_ActiveFilters);
+    currentFilter = nullptr;
+    targetFilter = nullptr;
+    lockedFilter = nullptr;
+    m_ActiveFilters.clear();
+    operationQueue.clear();
+
+    for (int i = 0; i < filterModel->rowCount(); ++i)
+    {
+        QSqlRecord record = filterModel->record(i);
+        QString id        = record.value("id").toString();
+        QString vendor    = record.value("Vendor").toString();
+        QString model     = record.value("Model").toString();
+        QString type      = record.value("Type").toString();
+        QString color     = record.value("Color").toString();
+        double exposure   = record.value("Exposure").toDouble();
+        int offset        = record.value("Offset").toInt();
+        QString lockedFilter  = record.value("LockedFilter").toString();
+        bool useAutoFocus = record.value("UseAutoFocus").toInt() == 1;
+        OAL::Filter *o    = new OAL::Filter(id, model, vendor, type, color, \
exposure, offset, useAutoFocus, lockedFilter); +        m_ActiveFilters.append(o);
+    }
 }
 
 void FilterManager::setCurrentFilter(ISD::GDInterface *filter)
 {
-    m_currentFilterDevice = filter;
-    m_currentFilterList.clear();
+    if (m_currentFilterDevice == filter)
+        return;
+    else
+        m_currentFilterDevice->disconnect(this);
+
+    filterNameLabel->setText(filter->getDeviceName());
 
-    m_currentFilterList = getFilterLabelsForDevice(filter);
-    m_currentFilterPosition = getFilterPositionForDevice(filter);
+    m_currentFilterDevice = filter;
+    m_currentFilterLabels.clear();
 
-    m_currentFilterName = filter->getBaseDevice()->getText("FILTER_NAME");
-    m_currentFilterSlot = filter->getBaseDevice()->getNumber("FILTER_SLOT");
+    m_FilterNameProperty = filter->getBaseDevice()->getText("FILTER_NAME");
+    m_FilterPositionProperty = filter->getBaseDevice()->getNumber("FILTER_SLOT");
 
-    for (ISD::GDInterface *oneFilter : m_FilterList)
-        oneFilter->disconnect(this);
+    m_currentFilterPosition = getFilterPosition(true);
+    m_currentFilterLabels = getFilterLabels(true);
 
     connect(filter, SIGNAL(textUpdated(ITextVectorProperty*)), this, \
                SLOT(processText(ITextVectorProperty*)));
     connect(filter, SIGNAL(numberUpdated(INumberVectorProperty*)), this, \
                SLOT(processNumber(INumberVectorProperty*)));
-}
+    connect(filter, SIGNAL(switchUpdated(ISwitchVectorProperty*)), this, \
SLOT(processSwitch(ISwitchVectorProperty*)));  
-void FilterManager::addFocuser(ISD::GDInterface *focuser)
-{
-    m_focuserDevices.append(dynamic_cast<ISD::Focuser*>(focuser));
-    m_currentFocuserDevice = dynamic_cast<ISD::Focuser*>(focuser);
-}
+    refreshFilterModel();
 
-void FilterManager::setCurrentFocuser(ISD::GDInterface *focuser)
-{
-    m_currentFocuserDevice = dynamic_cast<ISD::Focuser*>(focuser);
+    lastFilterOffset = m_ActiveFilters[m_currentFilterPosition-1]->offset();
 }
 
-QStringList FilterManager::getFilterLabels(ISD::GDInterface *filter)
+QStringList FilterManager::getFilterLabels(bool forceRefresh)
 {
-    if (filter == nullptr)
-        return m_currentFilterList;
-
-    return getFilterLabelsForDevice(filter);
-}
-
-QStringList FilterManager::getFilterLabelsForDevice(ISD::GDInterface *filter)
-{
-    if (filter == m_currentFilterDevice && m_currentFilterList.isEmpty() == false)
-        return m_currentFilterList;
-
-    ITextVectorProperty *name = filter->getBaseDevice()->getText("FILTER_NAME");
-    INumberVectorProperty *slot = filter->getBaseDevice()->getNumber("FILTER_SLOT");
+    if (forceRefresh == false)
+        return m_currentFilterLabels;
 
     QStringList filterList;
 
     QStringList filterAlias = Options::filterAlias();
 
-    for (int i = 0; i < slot->np[0].max; i++)
+    for (int i = 0; i < m_FilterPositionProperty->np[0].max; i++)
     {
         QString item;
 
-        if (name != nullptr && (i < name->ntp))
-            item = name->tp[i].text;
+        if (m_FilterNameProperty != nullptr && (i < m_FilterNameProperty->ntp))
+            item = m_FilterNameProperty->tp[i].text;
         else if (i < filterAlias.count() && filterAlias[i].isEmpty() == false)
             item = filterAlias.at(i);
         else
@@ -127,33 +208,395 @@ QStringList \
FilterManager::getFilterLabelsForDevice(ISD::GDInterface *filter)  return filterList;
 }
 
-int FilterManager::getFilterPosition(ISD::GDInterface *filter)
+int FilterManager::getFilterPosition(bool forceRefresh)
 {
-    if (filter == nullptr)
+    if (forceRefresh == false)
         return m_currentFilterPosition;
 
-    return getFilterPositionForDevice(filter);
+    return static_cast<int>(m_FilterPositionProperty->np[0].value);
 }
 
-int FilterManager::getFilterPositionForDevice(ISD::GDInterface *filter)
+bool FilterManager::setFilterPosition(uint8_t position, FilterPolicy policy)
 {
-    if (filter == m_currentFilterDevice)
-        return static_cast<int>(m_currentFilterSlot->np[0].value);
+    // Position 1 to Max
+    if (position > m_ActiveFilters.count())
+        return false;
 
-    INumberVectorProperty *slot = filter->getBaseDevice()->getNumber("FILTER_SLOT");
-    if (slot == nullptr)
+    m_Policy = policy;
+    currentFilter= m_ActiveFilters[m_currentFilterPosition - 1];
+    targetFilter = m_ActiveFilters[position-1];
+
+    if (currentFilter == targetFilter)
     {
-        qCWarning(KSTARS_INDI) << "Unable to find FILTER_SLOT in" << \
                filter->getBaseDevice();
-        return -1;
+        emit ready();
+        return true;
     }
 
-    return static_cast<int>(slot->np[0].value);
+    lockedFilter = nullptr;
+    if (targetFilter->useAutoFocus() && targetFilter->lockedFilter() != "--")
+    {
+        QString color = targetFilter->lockedFilter();
+        // Search for locked filter by filter color name
+        auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), \
[color](OAL::Filter *oneFilter) +        {
+                return (oneFilter->color() == color);
+    });
+        if (pos != m_ActiveFilters.end())
+            lockedFilter = *pos;
+    }
+
+    buildOperationQueue(FILTER_CHANGE);
+
+    executeOperationQueue();
+
+    return true;
 }
 
-bool FilterManager::setFilterPosition(uint8_t position, ISD::GDInterface *filter)
+void FilterManager::processNumber(INumberVectorProperty *nvp)
 {
-    if (filter == nullptr)
-        return m_currentFilterDevice->runCommand(INDI_SET_FILTER, &position);
+    if (nvp->s != IPS_OK || strcmp(nvp->name, "FILTER_SLOT") || \
m_currentFilterDevice == nullptr || strcmp(nvp->device, \
m_currentFilterDevice->getDeviceName())) +        return;
+
+    m_FilterPositionProperty = nvp;
+
+    if (m_currentFilterPosition != \
static_cast<int>(m_FilterPositionProperty->np[0].value)) +    {
+        m_currentFilterPosition = \
static_cast<int>(m_FilterPositionProperty->np[0].value); +        emit \
positionChanged(m_currentFilterPosition); +    }
+
+    if (state == FILTER_CHANGE)
+        executeOperationQueue();
+    // If filter is changed externally, record its current offset as the starting \
offset. +    else if (state == FILTER_IDLE)
+        lastFilterOffset = m_ActiveFilters[m_currentFilterPosition-1]->offset();
+
+    // Check if we have to apply Focus Offset
+    // Focus offsets are always applied first
+
+
 
-    return filter->runCommand(INDI_SET_FILTER, &position);
+    // Check if we have to start Auto Focus
+    // If new filter position changed, and new filter policy is to perform \
auto-focus then autofocus is initiated. +
+    // Capture Module
+    // 3x L ---> 3x HA ---> 3x L
+    // Capture calls setFilterPosition("L").
+    // 0. Change filter to desired filter "L"
+    // 1. Is there any offset from last offset that needs to be applied?
+    // 1.1 Yes --> Apply focus offset and wait until position is changed:
+    // 1.1.1 Position complete, now check for useAutoFocus policy (#2).
+    // 1.1.2 Position failed, retry?
+    // 1.2 No  --> Go to #2
+    // 2. Is there autofocus policy for current filter?
+    // 2.1. Yes --> Check filter lock policy
+    // 2.1.1 If filter lock is another filter --> Change filter
+    // 2.1.2 If filter lock is same filter --> proceed to 2.3
+    // 2.2 No --> Process to 2.3
+    // 2.3 filter lock policy filter is applied, start autofocus.
+    // 2.4 Autofocus complete. Check filter lock policy
+    // 2.4.1 If filter lock policy was applied --> revert filter
+    // 2.4.1.1 If filter offset policy is applicable --> Apply offset
+    // 2.4.1.2 If no filter offset policy is applicable --> Go to 2.5
+    // 2.4.2 No filter lock policy, go to 2.5
+    // 2.5 All complete, emit ready()
+
+    // Example. Current filter L. setFilterPosition("HA"). AutoFocus = YES. HA lock \
policy: L, HA offset policy: +100 with respect to L +    // Operation Stack. \
offsetDiff = 100 +    // If AutoFocus && current filter = lock policy filter
+    // AUTO_FOCUS (on L)
+    // CHANGE_FILTER (to HA)
+    // APPLY_OFFSET: +100
+
+    // Example. Current filter L. setFilterPosition("HA"). AutoFocus = No. HA lock \
policy: L, HA offset policy: +100 with respect to L +    // Operation Stack. \
offsetDiff = 100 +    // CHANGE_FILTER (to HA)
+    // APPLY_OFFSET: +100
+
+
+
+
+    // Example. Current filter R. setFilterPosition("B"). AutoFocus = YES. B lock \
policy: "--", B offset policy: +70 with respect to L +    // R offset = -50 with \
respect to L +    // FILTER_CHANGE (to B)
+    // FILTER_OFFSET (+120)
+    // AUTO_FOCUS
+
+    // Example. Current filter R. setFilterPosition("HA"). AutoFocus = YES. HA lock \
policy: L, HA offset policy: +100 with respect to L +    // R offset = -50 with \
respect to L +    // Operation Stack. offsetDiff = +150
+    // CHANGE_FILTER (to L)
+    // APPLY_OFFSET: +50 (L - R)
+    // AUTO_FOCUS
+    // CHANGE_FILTER (HA)
+    // APPLY_OFFSET: +100
+
+
+
+    // Example. Current filter R. setFilterPosition("HA"). AutoFocus = No. HA lock \
policy: L, HA offset policy: +100 with respect to L +    // R offset = -50 with \
respect to L +    // Operation Stack. offsetDiff = +150
+    // CHANGE_FILTER (to HA)
+    // APPLY_OFFSET: +150 (HA - R)
+
+
+
+    // Example. Current filter L. setFilterPosition("R"). AutoFocus = Yes. R lock \
policy: R, R offset policy: -50 with respect to L +    // Operation Stack. offsetDiff \
= -50 +    // CHANGE_FILTER (to R)
+    // APPLY_OFFSET: -50 (R - L)
+    // AUTO_FOCUS
+
+
+}
+
+void FilterManager::processText(ITextVectorProperty *tvp)
+{
+    if (strcmp(tvp->name, "FILTER_NAME") || m_currentFilterDevice == nullptr || \
strcmp(tvp->device, m_currentFilterDevice->getDeviceName())            ) +        \
return;     +
+    m_FilterNameProperty = tvp;
+
+    QStringList newFilterLabels = getFilterLabels(true);
+
+    if (newFilterLabels != m_currentFilterLabels)
+    {
+        m_currentFilterLabels = newFilterLabels;
+
+        refreshFilterModel();
+
+        emit labelsChanged(newFilterLabels);
+    }
+}
+
+void FilterManager::processSwitch(ISwitchVectorProperty *svp)
+{
+    if (m_currentFilterDevice == nullptr || strcmp(svp->device, \
m_currentFilterDevice->getDeviceName())) +        return;
+
+}
+
+void FilterManager::buildOperationQueue(FilterState operation)
+{
+    operationQueue.clear();
+    m_useLockedFilter = false;
+    m_useTargetFilter = false;
+
+    switch (operation)
+    {
+    case FILTER_CHANGE:
+
+        if ( (m_Policy & LOCK_POLICY) && lockedFilter != nullptr && lockedFilter != \
currentFilter) +            m_useLockedFilter = true;
+        if ( (m_Policy & CHANGE_POLICY) && ((m_Policy & ~LOCK_POLICY) || \
lockedFilter == nullptr) && targetFilter != currentFilter) +            \
m_useTargetFilter = true; +
+        if (m_useLockedFilter || m_useTargetFilter)
+        {            
+            operationQueue.enqueue(FILTER_CHANGE);
+            if (m_Policy & OFFSET_POLICY)
+                operationQueue.enqueue(FILTER_OFFSET);
+        }
+
+        if ( (m_Policy & AUTOFOCUS_POLICY) && targetFilter->useAutoFocus())
+        {
+            operationQueue.enqueue(FILTER_AUTOFOCUS);
+
+            if (lockedFilter != nullptr && lockedFilter != targetFilter)
+            {
+                m_useTargetFilter = true;
+                if (m_Policy & (CHANGE_POLICY|LOCK_POLICY))
+                    operationQueue.enqueue(FILTER_CHANGE);
+                if (m_Policy & OFFSET_POLICY)
+                    operationQueue.enqueue(FILTER_OFFSET);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+bool FilterManager::executeOperationQueue()
+{
+    if (operationQueue.isEmpty())
+    {
+        state = FILTER_IDLE;
+        emit newStatus(state);
+        emit ready();
+        return false;
+    }
+
+    FilterState nextOperation = operationQueue.dequeue();
+
+    bool actionRequired = true;
+
+    switch (nextOperation)
+    {
+    case FILTER_CHANGE:
+    {
+        state = FILTER_CHANGE;
+        if (m_useLockedFilter)
+            targetFilterPosition = m_ActiveFilters.indexOf(lockedFilter) + 1;
+        else if (m_useTargetFilter)
+            targetFilterPosition = m_ActiveFilters.indexOf(targetFilter) + 1;
+        m_currentFilterDevice->runCommand(INDI_SET_FILTER, &targetFilterPosition);
+        emit newStatus(state);
+    }
+        break;
+
+    case FILTER_OFFSET:
+    {
+        state = FILTER_OFFSET;
+        if (m_useLockedFilter)
+        {
+            targetFilterOffset = lockedFilter->offset() - lastFilterOffset;
+            lastFilterOffset   = lockedFilter->offset();
+            currentFilter = lockedFilter;
+            m_useLockedFilter = false;
+        }
+        else if (m_useTargetFilter)
+        {
+            targetFilterOffset = targetFilter->offset() - lastFilterOffset;
+            lastFilterOffset   = targetFilter->offset();
+            currentFilter = targetFilter;
+            m_useTargetFilter = false;
+        }
+        if (targetFilterOffset == 0)
+            actionRequired = false;
+        else
+        {
+            emit newFocusOffset(targetFilterOffset);
+            emit newStatus(state);
+        }
+    }
+        break;
+
+    case FILTER_AUTOFOCUS:
+        state = FILTER_AUTOFOCUS;
+        emit newStatus(state);
+        emit checkFocus(0.01);
+        break;
+
+    default:
+        break;
+    }
+
+    // If an additional action is required, return return and continue later
+    if (actionRequired)
+        return true;
+    // Othereise, continue processing the queue
+    else
+        return executeOperationQueue();
+}
+
+bool FilterManager::executeOneOperation(FilterState operation)
+{
+    bool actionRequired = false;
+
+    switch (operation)
+    {
+    default:
+        break;
+    }
+
+    return actionRequired;
+}
+
+void FilterManager::setFocusOffsetComplete()
+{
+    if (state == FILTER_OFFSET)
+        executeOperationQueue();
+}
+
+double FilterManager::getFilterExposure(const QString &name) const
+{
+    QString color = name;
+    if (color.isEmpty())
+        color = m_currentFilterLabels[m_currentFilterPosition-1];
+    // Search for locked filter by filter color name
+    auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), \
[color](OAL::Filter *oneFilter) +    {return (oneFilter->color() == color);});
+
+    if (pos != m_ActiveFilters.end())
+        return (*pos)->exposure();
+
+    // Default value
+    return 1;
+}
+
+bool FilterManager::setFilterExposure(double exposure)
+{
+     QString color = m_currentFilterLabels[m_currentFilterPosition-1];
+     for (int i=0; i < m_ActiveFilters.count(); i++)
+     {
+         if (color == m_ActiveFilters[i]->color())
+         {
+             filterModel->setData(filterModel->index(i, 5), exposure);
+             filterModel->submitAll();
+             refreshFilterModel();
+             return true;
+         }
+     }
+
+     return false;
+}
+
+QString FilterManager::getFilterLock(const QString &name) const
+{
+    // Search for locked filter by filter color name
+    auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), \
[name](OAL::Filter *oneFilter) +    {return (oneFilter->color() == name);});
+
+    if (pos != m_ActiveFilters.end())
+        return (*pos)->lockedFilter();
+
+    // Default value
+    return "--";
+}
+
+void FilterManager::removeDevice(ISD::GDInterface *device)
+{    
+    if (device == m_currentFilterDevice)
+    {
+        m_FilterNameProperty = nullptr;
+        m_FilterPositionProperty = nullptr;
+        m_currentFilterDevice = nullptr;
+        m_currentFilterLabels.clear();
+        m_currentFilterPosition = 0;
+        qDeleteAll(m_ActiveFilters);
+        m_ActiveFilters.clear();
+        delete(filterModel);
+        filterModel = nullptr;
+    }
+}
+
+void FilterManager::setFocusStatus(Ekos::FocusState focusState)
+{
+    if (state == FILTER_AUTOFOCUS)
+    {
+        switch (focusState)
+        {
+            case FOCUS_COMPLETE:
+            executeOperationQueue();
+            break;
+
+            case FOCUS_FAILED:
+            if (++retries == 3)
+            {
+                retries = 0;
+                emit failed();
+                return;
+            }
+            // Restart again
+            emit checkFocus(0.01);
+            break;
+
+        default:
+            break;
+
+        }
+    }
+}
 }
diff --git a/kstars/ekos/auxiliary/filtermanager.h \
b/kstars/ekos/auxiliary/filtermanager.h index 9928e2850..6c5dd3ed2 100644
--- a/kstars/ekos/auxiliary/filtermanager.h
+++ b/kstars/ekos/auxiliary/filtermanager.h
@@ -10,69 +10,135 @@
 #pragma once
 
 #include <QDialog>
+#include <QSqlDatabase>
+#include <QQueue>
+#include <QPointer>
+
 #include <indi/indistd.h>
 #include <indi/indifocuser.h>
 #include <oal/filter.h>
-#include <QSqlDatabase>
+
+#include "ekos/ekos.h"
 
 #include "ui_filtersettings.h"
 
 class QSqlTableModel;
+class LockDelegate;
+class NotEditableDelegate;
+class ExposureDelegate;
+class OffsetDelegate;
+class UseAutoFocusDelegate;
+
+namespace Ekos
+{
 
 class FilterManager : public QDialog, public Ui::FilterSettings
 {
+    Q_OBJECT
 public:
+
+    typedef enum
+    {
+        CHANGE_POLICY    = 1 << 0,
+        LOCK_POLICY      = 1 << 1,
+        OFFSET_POLICY    = 1 << 2,
+        AUTOFOCUS_POLICY = 1 << 3,
+        ALL_POLICIES     = CHANGE_POLICY | OFFSET_POLICY | LOCK_POLICY | \
AUTOFOCUS_POLICY +    } FilterPolicy;
+
     FilterManager();
-    void refreshFilterData();
 
-    QStringList getFilterLabels(ISD::GDInterface *filter=nullptr);
+    void refreshFilterModel();
+
+    QStringList getFilterLabels(bool forceRefresh=false);
+
+    int getFilterPosition(bool forceRefresh=false);
+
+    // The target position and offset
+    int getTargetFilterPosition() { return targetFilterPosition; }
+    int getTargetFilterOffset() { return targetFilterOffset; }
+
+    /**
+     * @brief getFilterExposure Get optimal exposure time for the specified filter
+     * @param name filter to obtain exposure time for
+     * @return exposure time in seconds.
+     */
+    double getFilterExposure(const QString &name = QString()) const;
+    bool setFilterExposure(double exposure);
 
-    bool setFilterPosition(uint8_t position, ISD::GDInterface *filter=nullptr);
-    int getFilterPosition(ISD::GDInterface *filter=nullptr);
+    /**
+     * @brief getFilterLock Return filter that should be used when running autofocus \
for the supplied filter +     * For example, "Red" filter can be locked to use "Lum" \
when doing autofocus. "Green" filter can be locked to "--" +     * which means that \
no filter change is necessary. +     * @param name filter which we need to query its \
locked filter. +     * @return locked filter. "--" indicates no locked filter and \
whatever current filter should be used. +     *
+     */
+    QString getFilterLock(const QString &name) const;
 
-    void addFilter(ISD::GDInterface *filter);
-    void setCurrentFilter(ISD::GDInterface *filter);
+    void setCurrentFilter(ISD::GDInterface *filter);    
 
-    void addFocuser(ISD::GDInterface *focuser);
-    void setCurrentFocuser(ISD::GDInterface *focuser);
+
+    /**
+     * @brief applyFilterFocusPolicies Check if we need to apply any filter policies \
for focus operations. +     */
+    void applyFilterFocusPolicies();
 
 public slots:
-    void updateFilterNames();
-    void updateFilterPosition();
+    // Position. if applyPolicy is true then all filter offsets and autofocus & lock \
policies are applied. +    bool setFilterPosition(uint8_t position, FilterPolicy \
policy = ALL_POLICIES); +    // Offset
+    void setFocusOffsetComplete();
+    // Remove Device
+    void removeDevice(ISD::GDInterface *device);
+    // Refresh Filters after model update
+    void reloadFilters();
+    // Focus Status
+    void setFocusStatus(Ekos::FocusState focusState);
 
 signals:
     // Emitted only when there is a change in the filter slot number
-    void currentFilterPositionChanged(int);
+    void positionChanged(int);
     // Emitted when filter change operation completed successfully including any \
                focus offsets or auto-focus operation
-    void currentFilterPositionCompleted(int);
-    // Emitted when filter labels are updated for the current filter
-    void currentFilterLabelsChanged(QStringList);
-
-private slots:
-    // Apply changes to database
-    void apply();
+    void labelsChanged(QStringList);
+    // Emitted when filter exposure duration changes
+    void exposureChanged(double);
+    // Emitted when filter change completed including all required actions    
+    void ready();
+    // Emitted when operation fails
+    void failed();
+    // Status signal
+    void newStatus(FilterState state);
+    // Check Focus
+    void checkFocus(double);
+    // New Focus offset requested
+    void newFocusOffset(int16_t);
+
+private slots:    
     void processText(ITextVectorProperty *tvp);
     void processNumber(INumberVectorProperty *nvp);
     void processSwitch(ISwitchVectorProperty *svp);
 
 private:
 
-    QStringList getFilterLabelsForDevice(ISD::GDInterface *filter);
-    int getFilterPositionForDevice(ISD::GDInterface *filter);
-
     // Filter Wheel Devices
-    QList<ISD::GDInterface*> m_filterDevices;
     ISD::GDInterface *m_currentFilterDevice = { nullptr };
-    QStringList m_currentFilterList;
+
+    // Position and Labels
+    QStringList m_currentFilterLabels;
     int m_currentFilterPosition = { -1 };
+    double m_currentFilterExposure = { -1 };
 
     // Filter Structure
     QList<OAL::Filter *> m_ActiveFilters;
-    QList<OAL::Filter *> m_FilterList;
+    OAL::Filter *targetFilter = { nullptr };
+    OAL::Filter *currentFilter = { nullptr };
+    OAL::Filter *lockedFilter = { nullptr };
+    bool m_useLockedFilter = { false };
+    bool m_useTargetFilter = { false };
 
-    // Focusers
-    QList<ISD::Focuser *> m_focuserDevices;
-    ISD::Focuser *m_currentFocuserDevice = { nullptr };
+    // Autofocus retries
+    uint8_t retries = { 0 };
 
     int16_t lastFilterOffset { 0 };
 
@@ -80,6 +146,34 @@ private:
     QSqlTableModel *filterModel = { nullptr };
 
     // INDI Properties of current active filter
-    ITextVectorProperty *m_currentFilterName { nullptr };
-    INumberVectorProperty *m_currentFilterSlot { nullptr };
+    ITextVectorProperty *m_FilterNameProperty { nullptr };
+    INumberVectorProperty *m_FilterPositionProperty { nullptr };
+
+    // Operation stack
+    void buildOperationQueue(FilterState operation);
+    bool executeOperationQueue();
+    bool executeOneOperation(FilterState operation);
+
+    // Update model
+    void syncDBToINDI();
+
+    // Operation Queue
+    QQueue<FilterState> operationQueue;
+
+    FilterState state = { FILTER_IDLE };
+
+    int targetFilterPosition = { -1 };
+    int targetFilterOffset = { - 1 };
+
+    // Delegates
+    QPointer<LockDelegate> lockDelegate;
+    QPointer<NotEditableDelegate> noEditDelegate;
+    QPointer<ExposureDelegate> exposureDelegate;
+    QPointer<OffsetDelegate> offsetDelegate;
+    QPointer<UseAutoFocusDelegate> useAutoFocusDelegate;
+
+    // Policies
+    FilterPolicy m_Policy = { ALL_POLICIES };
 };
+
+}
diff --git a/kstars/ekos/auxiliary/filtersettings.ui \
b/kstars/ekos/auxiliary/filtersettings.ui index 61f60b3ce..e4df7085f 100644
--- a/kstars/ekos/auxiliary/filtersettings.ui
+++ b/kstars/ekos/auxiliary/filtersettings.ui
@@ -19,17 +19,17 @@
      <item>
       <widget class="QLabel" name="label">
        <property name="text">
-        <string>FW:</string>
+        <string>Filter Wheel</string>
        </property>
       </widget>
      </item>
      <item>
-      <widget class="QComboBox" name="FWCombo">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
+      <widget class="QLabel" name="filterNameLabel">
+       <property name="styleSheet">
+        <string notr="true">font-weight:bold;</string>
+       </property>
+       <property name="text">
+        <string/>
        </property>
       </widget>
      </item>
@@ -49,7 +49,23 @@
     </layout>
    </item>
    <item>
-    <widget class="QTableView" name="filterView"/>
+    <widget class="QTableView" name="filterView">
+     <property name="sizeAdjustPolicy">
+      <enum>QAbstractScrollArea::AdjustToContents</enum>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
 +     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::SingleSelection</enum>
+     </property>
+     <attribute name="horizontalHeaderStretchLastSection">
+      <bool>true</bool>
+     </attribute>
+     <attribute name="verticalHeaderStretchLastSection">
+      <bool>false</bool>
+     </attribute>
+    </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
@@ -97,7 +113,7 @@
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel</set>
+      <set>QDialogButtonBox::Close</set>
      </property>
     </widget>
    </item>
diff --git a/kstars/ekos/ekosmanager.cpp b/kstars/ekos/ekosmanager.cpp
index 2d3ec32dc..c017f7049 100644
--- a/kstars/ekos/ekosmanager.cpp
+++ b/kstars/ekos/ekosmanager.cpp
@@ -106,10 +106,7 @@ EkosManager::EkosManager(QWidget *parent) : QDialog(parent)
     connect(clearB, SIGNAL(clicked()), this, SLOT(clearLog()));
 
     // Summary
-    // previewPixmap = new QPixmap(QPixmap(":/images/noimage.png"));
-
-    // Filter Manager
-    filterManager.reset(new FilterManager());
+    // previewPixmap = new QPixmap(QPixmap(":/images/noimage.png"));    
 
     // Profiles
     connect(addProfileB, SIGNAL(clicked()), this, SLOT(addProfile()));
@@ -312,6 +309,9 @@ void EkosManager::reset()
 {
     qCDebug(KSTARS_EKOS) << "Resetting Ekos Manager...";
 
+    // Filter Manager
+    filterManager.reset(new Ekos::FilterManager());
+
     nDevices = 0;
 
     useGuideHead = false;
@@ -1137,8 +1137,6 @@ void EkosManager::setFilter(ISD::GDInterface *filterDevice)
 
     alignProcess->addFilter(filterDevice);
 
-    filterManager->addFilter(filterDevice);
-
     if (Options::defaultAlignFW().isEmpty() == false)
         alignProcess->setFilter(Options::defaultAlignFW(), -1);
 }
@@ -1151,9 +1149,7 @@ void EkosManager::setFocuser(ISD::GDInterface *focuserDevice)
 
     initFocus();
 
-    focusProcess->addFocuser(focuserDevice);
-
-    filterManager->addFocuser(focuserDevice);
+    focusProcess->addFocuser(focuserDevice);    
 
     appendLogText(i18n("%1 focuser is online.", focuserDevice->getDeviceName()));
 }
@@ -1236,6 +1232,7 @@ void EkosManager::removeDevice(ISD::GDInterface *devInterface)
             break;
     }
 
+
     appendLogText(i18n("%1 is offline.", devInterface->getDeviceName()));
 
     // #1 Remove from Generic Devices
@@ -1269,7 +1266,7 @@ void EkosManager::processNewText(ITextVectorProperty *tvp)
 {
     if (!strcmp(tvp->name, "FILTER_NAME"))
     {
-        filterManager->updateFilterNames();
+        //filterManager->updateFilterNames();
 
         /*if (captureProcess.get() != nullptr)
             captureProcess->checkFilter();
@@ -1322,10 +1319,10 @@ void EkosManager::processNewNumber(INumberVectorProperty \
*nvp)  return;
     }
 
+    /*
     if (!strcmp(nvp->name, "FILTER_SLOT"))
-    {
-        filterManager->updateFilterNames();
-        /*if (captureProcess.get() != nullptr)
+    {        
+        if (captureProcess.get() != nullptr)
             captureProcess->checkFilter();
 
         if (focusProcess.get() != nullptr)
@@ -1333,8 +1330,9 @@ void EkosManager::processNewNumber(INumberVectorProperty *nvp)
 
         if (alignProcess.get() != nullptr)
             alignProcess->checkFilter();
-            */
+
     }
+    */
 }
 
 void EkosManager::processNewProperty(INDI::Property *prop)
@@ -1436,10 +1434,10 @@ void EkosManager::processNewProperty(INDI::Property *prop)
         return;
     }
 
+    /*
     if (!strcmp(prop->getName(), "FILTER_NAME"))
-    {
-        filterManager->updateFilterNames();
-        /*if (captureProcess.get() != nullptr)
+    {        
+        if (captureProcess.get() != nullptr)
             captureProcess->checkFilter();
 
         if (focusProcess.get() != nullptr)
@@ -1447,10 +1445,11 @@ void EkosManager::processNewProperty(INDI::Property *prop)
 
         if (alignProcess.get() != nullptr)
             alignProcess->checkFilter();
-            */
+
 
         return;
     }
+    */
 
     if (!strcmp(prop->getName(), "ASTROMETRY_SOLVER"))
     {
@@ -1663,12 +1662,6 @@ void EkosManager::initCapture()
                 SLOT(setFocusStatus(Ekos::FocusState)), Qt::UniqueConnection);
         connect(focusProcess.get(), &Ekos::Focus::newHFR, captureProcess.get(), \
&Ekos::Capture::setHFR, Qt::UniqueConnection);  
-        // Adjust focus position
-        connect(captureProcess.get(), SIGNAL(newFocusOffset(int16_t)), \
                focusProcess.get(), SLOT(adjustRelativeFocus(int16_t)),
-                Qt::UniqueConnection);
-        connect(focusProcess.get(), SIGNAL(focusPositionAdjusted()), \
                captureProcess.get(), SLOT(preparePreCaptureActions()),
-                Qt::UniqueConnection);
-
         // Meridian Flip
         connect(captureProcess.get(), SIGNAL(meridianFlipStarted()), \
focusProcess.get(), SLOT(resetFrame()), Qt::UniqueConnection);  }
@@ -1735,6 +1728,8 @@ void EkosManager::initAlign()
         toolsWidget->setTabIcon(index, icon);
     }
 
+    alignProcess->setFilterManager(filterManager);
+
     if (captureProcess.get() != nullptr)
     {
         // Align Status
@@ -1752,9 +1747,6 @@ void EkosManager::initAlign()
 
     if (focusProcess.get() != nullptr)
     {
-        // Filter lock
-        //connect(focusProcess.get(), \
                SIGNAL(filterLockUpdated(ISD::GDInterface*,int)), alignProcess.get(),
-                //SLOT(setLockedFilter(ISD::GDInterface*,int)), \
                Qt::UniqueConnection);
         connect(focusProcess.get(), SIGNAL(newStatus(Ekos::FocusState)), \
alignProcess.get(), SLOT(setFocusStatus(Ekos::FocusState)),  Qt::UniqueConnection);
     }
@@ -1788,6 +1780,14 @@ void EkosManager::initFocus()
     connect(focusProcess.get(), SIGNAL(newProfilePixmap(QPixmap&)), this, \
                SLOT(updateFocusProfilePixmap(QPixmap&)));
     connect(focusProcess.get(), SIGNAL(newHFR(double)), this, \
SLOT(updateCurrentHFR(double)));  
+    focusProcess->setFilterManager(filterManager);
+    connect(filterManager.data(), SIGNAL(checkFocus(double)), focusProcess.get(), \
SLOT(checkFocus(double)), Qt::UniqueConnection); +    connect(focusProcess.get(), \
SIGNAL(newStatus(Ekos::FocusState)), filterManager.data(), \
SLOT(setFocusStatus(Ekos::FocusState)), Qt::UniqueConnection); +    \
connect(filterManager.data(), SIGNAL(newFocusOffset(int16_t)), focusProcess.get(), \
SLOT(adjustRelativeFocus(int16_t)), +            Qt::UniqueConnection);
+    connect(focusProcess.get(), SIGNAL(focusPositionAdjusted()), \
filterManager.data(), SLOT(setFocusOffsetComplete()), +            \
Qt::UniqueConnection); +
     if (Options::ekosLeftIcons())
     {
         QTransform trans;
@@ -1815,12 +1815,6 @@ void EkosManager::initFocus()
                 SLOT(setFocusStatus(Ekos::FocusState)), Qt::UniqueConnection);
         connect(focusProcess.get(), &Ekos::Focus::newHFR, captureProcess.get(), \
&Ekos::Capture::setHFR, Qt::UniqueConnection);  
-        // Adjust focus position
-        connect(captureProcess.get(), SIGNAL(newFocusOffset(int16_t)), \
                focusProcess.get(), SLOT(adjustRelativeFocus(int16_t)),
-                Qt::UniqueConnection);
-        connect(focusProcess.get(), SIGNAL(focusPositionAdjusted()), \
                captureProcess.get(), SLOT(preparePreCaptureActions()),
-                Qt::UniqueConnection);
-
         // Meridian Flip
         connect(captureProcess.get(), SIGNAL(meridianFlipStarted()), \
focusProcess.get(), SLOT(resetFrame()), Qt::UniqueConnection);  }
@@ -1834,9 +1828,6 @@ void EkosManager::initFocus()
 
     if (alignProcess.get() != nullptr)
     {
-        // Filter lock
-        //connect(focusProcess.get(), \
                SIGNAL(filterLockUpdated(ISD::GDInterface*,int)), alignProcess.get(),
-                //SLOT(setLockedFilter(ISD::GDInterface*,int)), \
                Qt::UniqueConnection);
         connect(focusProcess.get(), SIGNAL(newStatus(Ekos::FocusState)), \
alignProcess.get(), SLOT(setFocusStatus(Ekos::FocusState)),  Qt::UniqueConnection);
     }
diff --git a/kstars/ekos/ekosmanager.h b/kstars/ekos/ekosmanager.h
index 74433c207..214987f69 100644
--- a/kstars/ekos/ekosmanager.h
+++ b/kstars/ekos/ekosmanager.h
@@ -287,7 +287,7 @@ class EkosManager : public QDialog, public Ui::EkosManager
     QList<std::shared_ptr<ProfileInfo>> profiles;
 
     // Filter Manager
-    QSharedPointer<FilterManager> filterManager;
+    QSharedPointer<Ekos::FilterManager> filterManager;
 
     // Mount Summary
     QProgressIndicator *mountPI { nullptr };


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic