Kstars

capturecountswidget.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "capturecountswidget.h"
9#include "Options.h"
10#include "ekos/manager.h"
11#include "ekos/scheduler/schedulerjob.h"
12#include "ekos/scheduler/schedulermodulestate.h"
13#include "ekos/capture/capture.h"
14#include "ekos/capture/sequencejob.h"
15
16#include <ekos_capture_debug.h>
17
18using Ekos::SequenceJob;
19
20CaptureCountsWidget::CaptureCountsWidget(QWidget *parent) : QWidget(parent)
21{
22 setupUi(this);
23 // switch between stacked views
24 connect(switchToGraphicsButton, &QPushButton::clicked, this, [this]()
25 {
26 textView->setVisible(false);
27 graphicalView->setVisible(true);
28 Options::setUseGraphicalCountsDisplay(true);
29 });
30 connect(switchToTextButton, &QPushButton::clicked, this, [this]()
31 {
32 textView->setVisible(true);
33 graphicalView->setVisible(false);
34 Options::setUseGraphicalCountsDisplay(false);
35 });
36
37 // start with the last used view
38 graphicalView->setVisible(Options::useGraphicalCountsDisplay());
39 textView->setVisible(!Options::useGraphicalCountsDisplay());
40
41 // setup graphical view
42 gr_sequenceProgressBar->setDecimals(0);
43 gr_overallProgressBar->setDecimals(0);
44
45 reset();
46}
47
48void CaptureCountsWidget::setCurrentTrainName(const QString &name)
49{
50 m_currentTrainName = name;
51 showCurrentCameraInfo();
52}
53
54void CaptureCountsWidget::updateExposureProgress(Ekos::SequenceJob *job, const QString &devicename)
55{
56 imageCountDown[devicename].setHMS(0, 0, 0);
57 imageCountDown[devicename] = imageCountDown[devicename].addSecs(int(std::round(job->getExposeLeft())));
58 if (imageCountDown[devicename].hour() == 23)
59 imageCountDown[devicename].setHMS(0, 0, 0);
60
61 imageProgress->setRange(0, int(std::ceil(job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble())));
62 imageProgress->setValue(int(std::ceil(job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble() - job->getExposeLeft())));
63 gr_imageProgress->setRange(0, int(std::ceil(job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble())));
64 gr_imageProgress->setValue(imageProgress->value());
65
66 frameRemainingTime->setText(imageCountDown[devicename].toString("hh:mm:ss"));
67 gr_frameRemainingTime->setText(frameRemainingTime->text());
68}
69
70void CaptureCountsWidget::updateDownloadProgress(double timeLeft, const QString &trainname)
71{
72 imageCountDown[trainname].setHMS(0, 0, 0);
73 imageCountDown[trainname] = imageCountDown[trainname].addSecs(int(std::ceil(timeLeft)));
74 frameRemainingTime->setText(imageCountDown[trainname].toString("hh:mm:ss"));
75}
76
77void CaptureCountsWidget::shareSchedulerState(QSharedPointer<Ekos::SchedulerModuleState> state)
78{
79 m_schedulerModuleState = state;
80}
81
82void CaptureCountsWidget::updateCaptureCountDown(int delta)
83{
84 // update counters of all devices
85 for (const QString &devicename : overallCountDown.keys())
86 {
87 overallCountDown[devicename] = overallCountDown[devicename].addSecs(delta);
88 jobCountDown[devicename] = jobCountDown[devicename].addSecs(delta);
89 sequenceCountDown[devicename] = sequenceCountDown[devicename].addSecs(delta);
90
91 // ensure that count downs do not overshoot
92 if (overallCountDown[devicename].hour() == 23)
93 overallCountDown[devicename].setHMS(0, 0, 0);
94 if (jobCountDown[devicename].hour() == 23)
95 jobCountDown[devicename].setHMS(0, 0, 0);
96 if (sequenceCountDown[devicename].hour() == 23)
97 sequenceCountDown[devicename].setHMS(0, 0, 0);
98 }
99
100 // do not change overall remaining time if scheduler is in endless loop
101 if (m_schedulerModuleState == nullptr || m_schedulerModuleState->activeJob() == nullptr ||
102 m_schedulerModuleState->activeJob()->getCompletionCondition() != Ekos::FINISH_LOOP)
103 {
104 overallRemainingTime->setText(overallCountDown[m_currentTrainName].toString("hh:mm:ss"));
105 gr_overallRemainingTime->setText(overallRemainingTime->text());
106 }
107 if (!m_captureProcess->isActiveJobPreview())
108 {
109 jobRemainingTime->setText(jobCountDown[m_currentTrainName].toString("hh:mm:ss"));
110 sequenceRemainingTime->setText(sequenceCountDown[m_currentTrainName].toString("hh:mm:ss"));
111 gr_sequenceRemainingTime->setText(sequenceRemainingTime->text());
112 }
113 else
114 {
115 jobRemainingTime->setText("--:--:--");
116 sequenceRemainingTime->setText("--:--:--");
117 gr_sequenceRemainingTime->setText("--:--:--");
118 }
119}
120
121void CaptureCountsWidget::reset()
122{
123 // reset graphical view
124 gr_imageProgress->setValue(0);
125 gr_frameLabel->setText("");
126 gr_frameRemainingTime->setText("--:--:--");
127 gr_frameDetailsLabel->setText("");
128 gr_sequenceLabel->setText(i18n("Sequence"));
129 gr_sequenceProgressBar->setValue(0);
130 gr_sequenceRemainingTime->setText("--:--:--");
131 gr_overallLabel->setText(i18n("Overall"));
132 gr_overallProgressBar->setValue(0);
133 gr_overallRemainingTime->setText("--:--:--");
134
135 // reset text view
136 imageProgress->setValue(0);
137 setFrameInfo("");
138 frameRemainingTime->setText("");
139
140 overallRemainingTime->setText("--:--:--");
141 jobRemainingTime->setText("--:--:--");
142 sequenceRemainingTime->setText("--:--:--");
143}
144
145namespace
146{
147QString frameLabel(const QString &type, const QString &filter)
148{
149 if (type == "Light")
150 {
151 if (filter.size() == 0)
152 return type;
153 else
154 return filter;
155 }
156 else if (type == "Flat")
157 {
158 if (filter.size() == 0)
159 return type;
160 else
161 return QString("%1 %2").arg(filter).arg(type);
162 }
163 else
164 return type;
165}
166}
167
168void CaptureCountsWidget::setFrameInfo(const QString frametype, const QString filter, const double exptime, const int xBin,
169 const int yBin, const double gain)
170{
171 if (frametype == "")
172 {
173 frameInfoLabel->setText("");
174 frameDetailsLabel->setText("");
175 gr_frameRemainingTime->setText("");
176 }
177 else
178 {
179 frameInfoLabel->setText(QString("%1").arg(frameLabel(frametype, filter)));
180 gr_frameLabel->setText(frameInfoLabel->text());
181 QString details = "";
182 if (exptime > 0)
183 details.append(QString("%1: %2 sec").arg(i18n("Exposure")).arg(exptime, 0, 'f', exptime < 1 ? 2 : exptime < 5 ? 1 : 0));
184 if (xBin > 0 && yBin > 0)
185 details.append(QString(", bin: %1x%2").arg(xBin).arg(yBin));
186 if (gain >= 0)
187 details.append(QString(", gain: %1").arg(gain, 0, 'f', 1));
188
189 frameDetailsLabel->setText(details);
190 gr_frameDetailsLabel->setText(details);
191 }
192}
193
194void CaptureCountsWidget::updateCaptureStatus(Ekos::CaptureState status, bool isPreview, const QString &trainname)
195{
196 overallCountDown[trainname].setHMS(0, 0, 0);
197 bool infinite_loop = false;
198 int total_remaining_time = 0, total_completed = 0, total_count = 0;
199 double total_percentage = 0;
200 // use this value if no scheduler is running and job name otherwise
201 QString total_label = "Total";
202
203 // find the corresponding camera
204 QSharedPointer<Ekos::Camera> selected_cam;
205 for (QSharedPointer<Ekos::Camera> camera : m_captureProcess->cameras())
206 {
207 if (camera->opticalTrain() == trainname)
208 {
209 selected_cam = camera;
210 break;
211 }
212 }
213
214 if (selected_cam.isNull())
215 {
216 qCWarning(KSTARS_EKOS_CAPTURE) << "No matching camera found" << m_currentTrainName;
217 return;
218 }
219
220 // determine total number of frames and completed ones - used either for
221 // total numbers if scheduler is not used - and for job figures in the text
222 // display if the scheduler is used
223 double capture_total_percentage = selected_cam->state()->progressPercentage();
224 int capture_remaining_time = selected_cam->state()->overallRemainingTime();
225 int capture_total_count = 0, capture_total_completed = 0;
226 for (int i = 0; i < selected_cam->state()->allJobs().count(); i++)
227 {
228 capture_total_count += selected_cam->state()->jobImageCount(i);
229 capture_total_completed += selected_cam->state()->jobImageProgress(i);
230 }
231
232
233 if (m_schedulerModuleState != nullptr && m_schedulerModuleState->activeJob() != nullptr)
234 {
235 total_label = m_schedulerModuleState->activeJob()->getName();
236 // FIXME: accessing the completed count might be one too low due to concurrency of updating the count and this loop
237 total_completed = m_schedulerModuleState->activeJob()->getCompletedCount();
238 total_count = m_schedulerModuleState->activeJob()->getSequenceCount();
239 infinite_loop = (m_schedulerModuleState->activeJob()->getCompletionCondition() == Ekos::FINISH_LOOP);
240 if (total_count > 0)
241 total_percentage = (100 * total_completed) / total_count;
242 if (m_schedulerModuleState->activeJob()->getEstimatedTime() > 0)
243 total_remaining_time = int(m_schedulerModuleState->activeJob()->getEstimatedTime());
244 }
245 else
246 {
247 total_percentage = capture_total_percentage;
248 total_remaining_time = capture_remaining_time;
249 total_count = capture_total_count;
250 total_completed = capture_total_completed;
251 }
252
253 switch (status)
254 {
256 // do nothing
257 break;
259 reset();
260 break;
261 default:
262 if (infinite_loop == true)
263 {
264 overallRemainingTime->setText("--:--:--");
265 gr_overallProgressBar->setValue(0);
266 gr_overallRemainingTime->setText(overallRemainingTime->text());
267 }
268 else
269 {
270 overallCountDown[trainname] = overallCountDown[trainname].addSecs(total_remaining_time);
271 gr_overallProgressBar->setValue(total_percentage);
272 }
273
274 // display overall remainings
275 if (isPreview)
276 overallLabel->setText(QString("%1").arg(total_label));
277 else
278 overallLabel->setText(QString("%1 (%2/%3)")
279 .arg(total_label)
280 .arg(total_completed)
281 .arg(infinite_loop ? QString("-") : QString::number(total_count)));
282 gr_overallLabel->setText(overallLabel->text());
283
284 // update job remaining time if run from the scheduler
285 bool show_job_progress = (m_schedulerModuleState != nullptr && m_schedulerModuleState->activeJob() != nullptr);
286 jobLabel->setVisible(show_job_progress);
287 jobRemainingTime->setVisible(show_job_progress);
288 if (show_job_progress)
289 {
290 jobCountDown[trainname].setHMS(0, 0, 0);
291 jobCountDown[trainname] = jobCountDown[trainname].addSecs(selected_cam->state()->overallRemainingTime());
292 jobLabel->setText(QString("Job (%1/%2)")
293 .arg(capture_total_completed)
294 .arg(capture_total_count));
295 }
296
297 // update sequence remaining time
298 sequenceCountDown[trainname].setHMS(0, 0, 0);
299 sequenceCountDown[trainname] = sequenceCountDown[trainname].addSecs(selected_cam->state()->activeJobRemainingTime());
300 }
301}
302
303void CaptureCountsWidget::updateJobProgress(CaptureProcessOverlay::FrameData data, const QString &trainname)
304{
305 m_currentFrame[trainname] = data;
306
307 // display informations if they come frome the currently selected camera device
308 if (trainname == m_currentTrainName)
309 showCurrentCameraInfo();
310}
311
312void CaptureCountsWidget::showCurrentCameraInfo()
313{
314 if (!m_currentFrame.contains(m_currentTrainName))
315 {
316 qCWarning(KSTARS_EKOS_CAPTURE) << "No frame info available for" << m_currentTrainName;
317 return;
318 }
319
320 auto data = m_currentFrame[m_currentTrainName];
321
322 if (data.jobType == SequenceJob::JOBTYPE_PREVIEW)
323 setFrameInfo(i18n("Preview"), data.filterName, data.exptime, data.binning.x(), data.binning.y(), data.gain);
324 else
325 setFrameInfo(CCDFrameTypeNames[data.frameType], data.filterName, data.exptime, data.binning.x(),
326 data.binning.y(), data.gain);
327
328 // display sequence progress in the graphical view
329 gr_sequenceProgressBar->setRange(0, data.count);
330 gr_sequenceProgressBar->setValue(data.completed);
331 if (data.jobType == SequenceJob::JOBTYPE_PREVIEW)
332 sequenceLabel->setText(QString("%1").arg(frameLabel(CCDFrameTypeNames[data.frameType], data.filterName)));
333 else
334 sequenceLabel->setText(QString("%1 (%3/%4)")
335 .arg(frameLabel(CCDFrameTypeNames[data.frameType], data.filterName)).arg(data.completed).arg(data.count));
336
337 gr_sequenceLabel->setText(sequenceLabel->text());
338}
339
340
341void CaptureCountsWidget::setEnabled(bool enabled)
342{
344 overallLabel->setEnabled(enabled);
345 gr_overallLabel->setEnabled(enabled);
346}
Q_SCRIPTABLE bool isActiveJobPreview()
DBUS interface function.
Definition capture.h:212
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
CaptureState
Capture states.
Definition ekos.h:92
@ CAPTURE_ABORTED
Definition ekos.h:99
@ CAPTURE_IDLE
Definition ekos.h:93
QString name(GameStandardAction id)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KGuiItem reset()
void clicked(bool checked)
bool contains(const Key &key) const const
QList< Key > keys() const const
int x() const const
int y() const const
bool isNull() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
double toDouble(bool *ok) const const
void setEnabled(bool)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.