Kstars

focusmodule.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "focusmodule.h"
8#include "focus.h"
9
10#include "Options.h"
11#include "auxiliary/ksmessagebox.h"
12#include "ekos/auxiliary/opticaltrainmanager.h"
13#include "kstarsdata.h"
14#include "kspaths.h"
15
16#include "ekos_focus_debug.h"
17
18#define TAB_BUTTON_SIZE 20
19
20namespace Ekos
21{
22
23FocusModule::FocusModule()
24{
25 setupUi(this);
26
27 // FIXME: disable closing of tabs, since it might create crashes
28 focusTabs->setTabsClosable(false);
29 // Connect the close request signal to the slot
30 connect(focusTabs, &QTabWidget::tabCloseRequested, this, &FocusModule::checkCloseFocuserTab);
31 // Adding the "New Tab" tab
32 QWidget *newTab = new QWidget;
33 QPushButton *addButton = new QPushButton;
34 addButton->setIcon(QIcon::fromTheme("list-add"));
35 addButton->setFixedSize(TAB_BUTTON_SIZE, TAB_BUTTON_SIZE);
36 addButton->setToolTip(i18n("<p>Add additional focuser</p><p><b>WARNING</b>: This feature is experimental!</p>"));
37 connect(addButton, &QPushButton::clicked, this, [this]()
38 {
39 FocusModule::addFocuser();
40 });
41
42 focusTabs->addTab(newTab, "");
43 focusTabs->tabBar()->setTabButton(0, QTabBar::RightSide, addButton);
44
45 // Create an autofocus CSV file, dated at startup time
46 m_FocusLogFileName = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("focuslogs/autofocus-" +
47 QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss") + ".txt");
48 m_FocusLogFile.setFileName(m_FocusLogFileName);
49
50 // Create main focuser
51 addFocuser();
52}
53
54FocusModule::~FocusModule()
55{
56 m_FocusLogFile.close();
57}
58
59QSharedPointer<Focus> &FocusModule::focuser(int i)
60{
61 if (i < m_Focusers.count())
62 return m_Focusers[i];
63 else
64 {
65 qCWarning(KSTARS_EKOS_FOCUS) << "Unknown focuser ID:" << i;
66 return m_Focusers[0];
67 }
68
69}
70
71QSharedPointer<Focus> FocusModule::mainFocuser()
72{
73 if (m_Focusers.size() <= 0)
74 {
75 QSharedPointer<Focus> newFocuser;
76 newFocuser.reset(new Focus(0));
77 m_Focusers.append(newFocuser);
78 }
79 return m_Focusers[0];
80}
81
82void FocusModule::checkFocus(double requiredHFR, const QString &trainname)
83{
84 bool found = false;
85 // publish to all known focusers using the same optical train (should be only one)
86 for (auto focuser : m_Focusers)
87 if (trainname == "" || focuser->opticalTrain() == trainname)
88 {
89 focuser->checkFocus(requiredHFR);
90 found = true;
91 }
92
93 if (!found)
94 {
95 QSharedPointer newFocuser = addFocuser(trainname);
96 newFocuser->checkFocus(requiredHFR);
97 }
98}
99
100void FocusModule::runAutoFocus(const AutofocusReason autofocusReason, const QString &reasonInfo, const QString &trainname)
101{
102 bool found = false;
103 // publish to all known focusers using the same optical train (should be only one)
104 for (auto focuser : m_Focusers)
105 if (trainname == "" || focuser->opticalTrain() == trainname)
106 {
107 focuser->runAutoFocus(autofocusReason, reasonInfo);
108 found = true;
109 }
110
111 if (!found)
112 {
113 QSharedPointer newFocuser = addFocuser(trainname);
114 newFocuser->runAutoFocus(autofocusReason, reasonInfo);
115 }
116}
117
118void FocusModule::resetFrame(const QString &trainname)
119{
120 bool found = false;
121 // publish to all known focusers using the same optical train (should be only one)
122 for (auto focuser : m_Focusers)
123 if (trainname == "" || focuser->opticalTrain() == trainname)
124 {
125 focuser->resetFrame();
126 found = true;
127 }
128
129 if (!found)
130 {
131 QSharedPointer newFocuser = addFocuser(trainname);
132 newFocuser->resetFrame();
133 }
134}
135
136void FocusModule::abort(const QString &trainname)
137{
138 bool found = false;
139 // publish to all known focusers using the same optical train (should be only one)
140 for (auto focuser : m_Focusers)
141 if (trainname == "" || focuser->opticalTrain() == trainname)
142 {
143 focuser->abort();
144 found = true;
145 }
146
147 if (!found)
148 {
149 QSharedPointer newFocuser = addFocuser(trainname);
150 newFocuser->abort();
151 }
152}
153
154void FocusModule::adaptiveFocus(const QString &trainname)
155{
156 bool found = false;
157 // publish to all known focusers using the same optical train (should be only one)
158 for (auto focuser : m_Focusers)
159 if (trainname == "" || focuser->opticalTrain() == trainname)
160 {
161 focuser->adaptiveFocus();
162 found = true;
163 }
164
165 if (!found)
166 {
167 QSharedPointer newFocuser = addFocuser(trainname);
168 newFocuser->adaptiveFocus();
169 }
170}
171
172void FocusModule::meridianFlipStarted(const QString &trainname)
173{
174 bool found = false;
175 // publish to all known focusers using the same optical train (should be only one)
176 for (auto focuser : m_Focusers)
177 if (trainname == "" || focuser->opticalTrain() == trainname)
178 {
179 focuser->meridianFlipStarted();
180 found = true;
181 }
182
183 if (!found)
184 {
185 QSharedPointer newFocuser = addFocuser(trainname);
186 newFocuser->meridianFlipStarted();
187 }
188}
189
190void FocusModule::setMountStatus(ISD::Mount::Status newState)
191{
192 // publish to all known focusers using the same optical train (should be only one)
193 for (auto focuser : m_Focusers)
194 focuser->setMountStatus(newState);
195}
196
197void FocusModule::setMountCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha)
198{
199 // publish to all known focusers using the same optical train (should be only one)
200 for (auto focuser : m_Focusers)
201 focuser->setMountCoords(position, pierSide, ha);
202}
203
204bool FocusModule::addTemperatureSource(const QSharedPointer<ISD::GenericDevice> &device)
205{
206 if (device.isNull())
207 return false;
208
209 for (auto &oneSource : m_TemperatureSources)
210 {
211 if (oneSource->getDeviceName() == device->getDeviceName())
212 return false;
213 }
214
215 m_TemperatureSources.append(device);
216
217 // publish new list of temperature sources to all focusers
218 for (auto focuser : m_Focusers)
219 focuser->updateTemperatureSources(m_TemperatureSources);
220
221 return true;
222}
223
224void FocusModule::syncCameraInfo(const char* devicename)
225{
226 // publish the change to all focusers
227 for (auto focuser : m_Focusers)
228 if (focuser->camera() == devicename)
229 focuser->syncCameraInfo();
230}
231
232void FocusModule::clearLog()
233{
234 m_LogText.clear();
235 emit newLog(QString());
236}
237
238void FocusModule::appendLogText(const QString &logtext)
239{
240 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2",
241 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), logtext));
242
243 qCInfo(KSTARS_EKOS_FOCUS) << logtext;
244
245 emit newLog(logtext);
246}
247
248void FocusModule::appendFocusLogText(const QString &lines)
249{
250 if (Options::focusLogging())
251 {
252
253 if (!m_FocusLogFile.exists())
254 {
255 // Create focus-specific log file and write the header record
256 QDir dir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
257 dir.mkpath("focuslogs");
258 m_FocusLogEnabled = m_FocusLogFile.open(QIODevice::WriteOnly | QIODevice::Text);
259 if (m_FocusLogEnabled)
260 {
261 QTextStream header(&m_FocusLogFile);
262 header << "date, time, position, temperature, filter, HFR, altitude\n";
263 header.flush();
264 }
265 else
266 qCWarning(KSTARS_EKOS_FOCUS) << "Failed to open focus log file: " << m_FocusLogFileName;
267 }
268
269 if (m_FocusLogEnabled)
270 {
271 QTextStream out(&m_FocusLogFile);
272 out << QDateTime::currentDateTime().toString("yyyy-MM-dd, hh:mm:ss, ") << lines;
273 out.flush();
274 }
275 }
276
277}
278
279void FocusModule::removeDevice(const QSharedPointer<ISD::GenericDevice> &deviceRemoved)
280{
281 // Check in Temperature Sources.
282 for (auto &oneSource : m_TemperatureSources)
283 if (oneSource->getDeviceName() == deviceRemoved->getDeviceName())
284 m_TemperatureSources.removeAll(oneSource);
285
286 // publish the change to all focusers
287 for (auto focuser : m_Focusers)
288 focuser->removeDevice(deviceRemoved);
289}
290
291
292void FocusModule::initFocuser(QSharedPointer<Focus> newFocuser)
293{
294 connect(newFocuser.get(), &Focus::focuserChanged, this, &FocusModule::updateFocuser);
295 connect(newFocuser.get(), &Focus::suspendGuiding, this, &FocusModule::suspendGuiding);
296 connect(newFocuser.get(), &Focus::resumeGuiding, this, &FocusModule::resumeGuiding);
297 connect(newFocuser.get(), &Focus::resumeGuiding, this, &FocusModule::resumeGuiding);
298 connect(newFocuser.get(), &Focus::newStatus, this, &FocusModule::newStatus);
299 connect(newFocuser.get(), &Focus::focusAdaptiveComplete, this, &FocusModule::focusAdaptiveComplete);
300 connect(newFocuser.get(), &Focus::newHFR, this, &FocusModule::newHFR);
301 connect(newFocuser.get(), &Focus::newFocusTemperatureDelta, this, &FocusModule::newFocusTemperatureDelta);
302 connect(newFocuser.get(), &Focus::inSequenceAF, this, &FocusModule::inSequenceAF);
303 connect(newFocuser.get(), &Focus::newLog, this, &FocusModule::appendLogText);
304 connect(newFocuser.get(), &Focus::newFocusLog, this, &FocusModule::appendFocusLogText);
305}
306
307QSharedPointer<Focus> FocusModule::addFocuser(const QString &trainname)
308{
309 QSharedPointer<Focus> newFocuser;
310 newFocuser.reset(new Focus(m_Focusers.count()));
311
312 // create the new tab and bring it to front
313 const int tabIndex = focusTabs->insertTab(std::max(0, focusTabs->count() - 1), newFocuser.get(), "new Focuser");
314 focusTabs->setCurrentIndex(tabIndex);
315 // make the tab first tab non closeable
316 if (tabIndex == 0)
317 focusTabs->tabBar()->setTabButton(0, QTabBar::RightSide, nullptr);
318
319 // find an unused train for additional tabs
320 const QString train = tabIndex == 0 ? "" : findUnusedOpticalTrain();
321
322 m_Focusers.append(newFocuser);
323 // select an unused train
324 if (train != "")
325 newFocuser->opticalTrainCombo->setCurrentText(train);
326
327 // set the weather sources
328 newFocuser->updateTemperatureSources(m_TemperatureSources);
329 // set the optical train
330 if (trainname != "" && newFocuser->opticalTrainCombo->findText(trainname))
331 newFocuser->opticalTrainCombo->setCurrentText(trainname);
332
333 // update the tab text
334 updateFocuser(tabIndex, true);
335 initFocuser(newFocuser);
336
337 return newFocuser;
338}
339
340void FocusModule::updateFocuser(int tabID, bool isValid)
341{
342 if (isValid)
343 {
344 if (tabID < focusTabs->count() && tabID < m_Focusers.count() && !m_Focusers[tabID].isNull())
345 {
346 const QString name = m_Focusers[tabID]->m_Focuser != nullptr ?
347 m_Focusers[tabID]->m_Focuser->getDeviceName() :
348 "no focuser";
349 focusTabs->setTabText(tabID, name);
350 }
351 else
352 qCWarning(KSTARS_EKOS_FOCUS) << "Unknown focuser ID:" << tabID;
353 }
354 else
355 focusTabs->setTabText(focusTabs->currentIndex(), "no focuser");
356}
357
358void FocusModule::closeFocuserTab(int tabIndex)
359{
360 // ignore close event from the "Add" tab
361 if (tabIndex == focusTabs->count() - 1)
362 return;
363
364 focusTabs->removeTab(tabIndex);
365 // select the next one on the left
366 focusTabs->setCurrentIndex(std::max(0, tabIndex - 1));
367 // clear the focuser
368 auto focuser = m_Focusers.at(tabIndex);
369 focuser->disconnect(this);
370 focuser->disconnectSyncSettings();
371 m_Focusers.removeAt(tabIndex);
372}
373
374void FocusModule::checkCloseFocuserTab(int tabIndex)
375{
376 if (m_Focusers[tabIndex]->isBusy())
377 {
378 // if accept has been clicked, abort and close the tab
379 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, &tabIndex]()
380 {
381 KSMessageBox::Instance()->disconnect(this);
382 m_Focusers[tabIndex]->abort();
383 closeFocuserTab(tabIndex);
384 });
385 // if cancel has been clicked, do not close the tab
386 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
387 {
388 KSMessageBox::Instance()->disconnect(this);
389 });
390
391 KSMessageBox::Instance()->warningContinueCancel(i18n("Camera %1 is busy. Abort to close?",
392 m_Focusers[tabIndex]->m_Focuser->getDeviceName()), i18n("Stop capturing"), 30, false, i18n("Abort"));
393 }
394 else
395 {
396 closeFocuserTab(tabIndex);
397
398 }
399}
400
401const QString FocusModule::findUnusedOpticalTrain()
402{
403 QList<QString> names = OpticalTrainManager::Instance()->getTrainNames();
404 foreach(auto focuser, m_Focusers)
405 names.removeAll(focuser->opticalTrain());
406
407 if (names.isEmpty())
408 return "";
409 else
410 return names.first();
411}
412
413}
The sky coordinates of a point in the sky.
Definition skypoint.h:45
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
QString i18nc(const char *context, const char *text, const TYPE &arg...)
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
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardAction id)
void clicked(bool checked)
void setIcon(const QIcon &icon)
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
void accepted()
void rejected()
QString filePath(const QString &fileName) const const
bool exists(const QString &fileName)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QIcon fromTheme(const QString &name)
void clear()
T & first()
iterator insert(const_iterator before, parameter_type value)
bool isEmpty() const const
qsizetype removeAll(const AT &t)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T * get() const const
bool isNull() const const
void tabCloseRequested(int index)
QWidget(QWidget *parent, Qt::WindowFlags f)
void setFixedSize(const QSize &s)
void setupUi(QWidget *widget)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.