Kstars

observatory.cpp
1 /* Ekos Observatory Module
2  SPDX-FileCopyrightText: Wolfgang Reissenberger <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "kstarsdata.h"
8 
9 #include "observatory.h"
10 
11 #include "ekos_observatory_debug.h"
12 
13 namespace Ekos
14 {
15 Observatory::Observatory()
16 {
17  setupUi(this);
18 
19  // status control
20  mObservatoryModel = new ObservatoryModel();
21  setObseratoryStatusControl(mObservatoryModel->statusControl());
22  // update UI for status control
23  connect(useDomeCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged);
24  connect(useShutterCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged);
25  connect(useWeatherCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged);
26  connect(mObservatoryModel, &Ekos::ObservatoryModel::newStatus, this, &Ekos::Observatory::observatoryStatusChanged);
27  // ready button deactivated
28  // connect(statusReadyButton, &QPushButton::clicked, mObservatoryModel, &Ekos::ObservatoryModel::makeReady);
29  statusReadyButton->setEnabled(false);
30 
31  setDomeModel(new ObservatoryDomeModel());
32  setWeatherModel(new ObservatoryWeatherModel());
33 }
34 
35 void Observatory::setObseratoryStatusControl(ObservatoryStatusControl control)
36 {
37  if (mObservatoryModel != nullptr)
38  {
39  useDomeCB->setChecked(control.useDome);
40  useShutterCB->setChecked(control.useShutter);
41  useWeatherCB->setChecked(control.useWeather);
42  }
43 }
44 
45 
46 void Observatory::setDomeModel(ObservatoryDomeModel *model)
47 {
48  mObservatoryModel->setDomeModel(model);
49  if (model != nullptr)
50  {
51  connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome);
52  connect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome);
53  connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus);
54  connect(model, &Ekos::ObservatoryDomeModel::newParkStatus, this, &Ekos::Observatory::setDomeParkStatus);
55  connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus);
56  connect(model, &Ekos::ObservatoryDomeModel::azimuthPositionChanged, this, &Ekos::Observatory::domeAzimuthChanged);
57  connect(model, &Ekos::ObservatoryDomeModel::newAutoSyncStatus, this, &Ekos::Observatory::showAutoSync);
58 
59  // motion controls
60  connect(motionMoveAbsButton, &QCheckBox::clicked, [this]()
61  {
62  mObservatoryModel->getDomeModel()->setAzimuthPosition(absoluteMotionSB->value());
63  });
64 
65  connect(motionMoveRelButton, &QCheckBox::clicked, [this]()
66  {
67  mObservatoryModel->getDomeModel()->setRelativePosition(relativeMotionSB->value());
68  });
69 
70  // abort button
71  connect(motionAbortButton, &QPushButton::clicked, model, &ObservatoryDomeModel::abort);
72 
73  // weather controls
74  connect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged);
75  connect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged);
76  connect(weatherWarningDelaySB, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int i)
77  {
78  Q_UNUSED(i)
79  weatherWarningSettingsChanged();
80  });
81 
82  connect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged);
83  connect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged);
84  connect(weatherAlertDelaySB, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int i)
85  {
86  Q_UNUSED(i)
87  weatherAlertSettingsChanged();
88  });
89  }
90  else
91  {
92  shutdownDome();
93 
94 
95  disconnect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged);
96  disconnect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged);
97  connect(weatherWarningDelaySB, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int i)
98  {
99  Q_UNUSED(i)
100  weatherWarningSettingsChanged();
101  });
102 
103  disconnect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged);
104  disconnect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged);
105  connect(weatherAlertDelaySB, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int i)
106  {
107  Q_UNUSED(i)
108  weatherWarningSettingsChanged();
109  });
110  }
111 }
112 
113 void Observatory::initDome()
114 {
115  domeBox->setEnabled(true);
116 
117  if (getDomeModel() != nullptr)
118  {
119  connect(getDomeModel(), &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText);
120 
121  // dome motion buttons
122  connect(motionCWButton, &QPushButton::clicked, [ = ](bool checked)
123  {
124  getDomeModel()->moveDome(true, checked);
125  });
126  connect(motionCCWButton, &QPushButton::clicked, [ = ](bool checked)
127  {
128  getDomeModel()->moveDome(false, checked);
129  });
130 
131  if (getDomeModel()->canPark())
132  {
133  connect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park);
134  connect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark);
135  domePark->setEnabled(true);
136  domeUnpark->setEnabled(true);
137  }
138  else
139  {
140  domePark->setEnabled(false);
141  domeUnpark->setEnabled(false);
142  }
143 
144  if (getDomeModel()->isRolloffRoof())
145  {
146  SlavingBox->setVisible(false);
147  domeAzimuthPosition->setText(i18nc("Not Applicable", "N/A"));
148  enableMotionControl(true);
149  }
150  else
151  {
152  // initialize the dome motion controls
153  domeAzimuthChanged(getDomeModel()->azimuthPosition());
154 
155  // slaving
156  showAutoSync(getDomeModel()->isAutoSync());
157  connect(slavingEnableButton, &QPushButton::clicked, this, [this]()
158  {
159  enableAutoSync(true);
160  });
161  connect(slavingDisableButton, &QPushButton::clicked, this, [this]()
162  {
163  enableAutoSync(false);
164  });
165  }
166 
167  // shutter handling
168  if (getDomeModel()->hasShutter())
169  {
170  shutterBox->setVisible(true);
171  shutterBox->setEnabled(true);
172  connect(shutterOpen, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::openShutter);
173  connect(shutterClosed, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::closeShutter);
174  shutterClosed->setEnabled(true);
175  shutterOpen->setEnabled(true);
176  setShutterStatus(getDomeModel()->shutterStatus());
177  useShutterCB->setVisible(true);
178  }
179  else
180  {
181  shutterBox->setVisible(false);
182  weatherWarningShutterCB->setVisible(false);
183  weatherAlertShutterCB->setVisible(false);
184  useShutterCB->setVisible(false);
185  }
186 
187  // abort button should always be available
188  motionAbortButton->setEnabled(true);
189 
190  statusDefinitionBox->setVisible(true);
191  statusDefinitionBox->setEnabled(true);
192 
193  // update the dome parking status
194  setDomeParkStatus(getDomeModel()->parkStatus());
195 
196  // enable the UI controls for dome weather actions
197  initWeatherActions(getWeatherModel() != nullptr && getWeatherModel()->isActive());
198  }
199 
200 }
201 
202 void Observatory::shutdownDome()
203 {
204  shutterBox->setEnabled(false);
205  shutterBox->setVisible(false);
206  domePark->setEnabled(false);
207  domeUnpark->setEnabled(false);
208  shutterClosed->setEnabled(false);
209  shutterOpen->setEnabled(false);
210 
211  disconnect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park);
212  disconnect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark);
213 
214  // disable the UI controls for dome weather actions
215  initWeatherActions(false);
216  statusDefinitionBox->setVisible(false);
217  domeBox->setEnabled(false);
218 }
219 
220 void Observatory::setDomeStatus(ISD::Dome::Status status)
221 {
222  qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome status to " << status;
223 
224  switch (status)
225  {
226  case ISD::Dome::DOME_ERROR:
227  appendLogText(i18n("%1 error. See INDI log for details.",
228  getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
229  motionCWButton->setChecked(false);
230  motionCCWButton->setChecked(false);
231  break;
232 
233  case ISD::Dome::DOME_IDLE:
234  motionCWButton->setChecked(false);
235  motionCWButton->setEnabled(true);
236  motionCCWButton->setChecked(false);
237  motionCCWButton->setEnabled(true);
238 
239  appendLogText(i18n("%1 is idle.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
240  break;
241 
242  case ISD::Dome::DOME_MOVING_CW:
243  motionCWButton->setChecked(true);
244  motionCWButton->setEnabled(false);
245  motionCCWButton->setChecked(false);
246  motionCCWButton->setEnabled(true);
247  if (getDomeModel()->isRolloffRoof())
248  {
249  domeAzimuthPosition->setText(i18n("Opening"));
250  toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park"));
251  appendLogText(i18n("Rolloff roof opening..."));
252  }
253  else
254  {
255  appendLogText(i18n("Dome is moving clockwise..."));
256  }
257  break;
258 
259  case ISD::Dome::DOME_MOVING_CCW:
260  motionCWButton->setChecked(false);
261  motionCWButton->setEnabled(true);
262  motionCCWButton->setChecked(true);
263  motionCCWButton->setEnabled(false);
264  if (getDomeModel()->isRolloffRoof())
265  {
266  domeAzimuthPosition->setText(i18n("Closing"));
267  toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark"));
268  appendLogText(i18n("Rolloff roof is closing..."));
269  }
270  else
271  {
272  appendLogText(i18n("Dome is moving counter clockwise..."));
273  }
274  break;
275 
276  case ISD::Dome::DOME_PARKED:
277  setDomeParkStatus(ISD::PARK_PARKED);
278 
279  appendLogText(i18n("%1 is parked.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
280  break;
281 
282  case ISD::Dome::DOME_PARKING:
283  toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark"));
284  motionCWButton->setEnabled(true);
285 
286  if (getDomeModel()->isRolloffRoof())
287  domeAzimuthPosition->setText(i18n("Closing"));
288  else
289  enableMotionControl(false);
290 
291  motionCWButton->setChecked(false);
292  motionCCWButton->setChecked(true);
293 
294  appendLogText(i18n("%1 is parking...", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
295  break;
296 
297  case ISD::Dome::DOME_UNPARKING:
298  toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park"));
299  motionCCWButton->setEnabled(true);
300 
301  if (getDomeModel()->isRolloffRoof())
302  domeAzimuthPosition->setText(i18n("Opening"));
303  else
304  enableMotionControl(false);
305 
306  motionCWButton->setChecked(true);
307  motionCCWButton->setChecked(false);
308 
309  appendLogText(i18n("%1 is unparking...", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
310  break;
311 
312  case ISD::Dome::DOME_TRACKING:
313  enableMotionControl(true);
314  motionCWButton->setEnabled(true);
315  motionCCWButton->setChecked(true);
316  appendLogText(i18n("%1 is tracking.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome")));
317  break;
318  }
319 }
320 
321 void Observatory::setDomeParkStatus(ISD::ParkStatus status)
322 {
323  qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome park status to " << status;
324  switch (status)
325  {
326  case ISD::PARK_UNPARKED:
327  activateButton(domePark, i18n("Park"));
328  buttonPressed(domeUnpark, i18n("Unparked"));
329  motionCWButton->setChecked(false);
330  motionCWButton->setEnabled(true);
331  motionCCWButton->setChecked(false);
332 
333  if (getDomeModel()->isRolloffRoof())
334  domeAzimuthPosition->setText(i18n("Open"));
335  else
336  enableMotionControl(true);
337  break;
338 
339  case ISD::PARK_PARKED:
340  buttonPressed(domePark, i18n("Parked"));
341  activateButton(domeUnpark, i18n("Unpark"));
342  motionCWButton->setChecked(false);
343  motionCCWButton->setChecked(false);
344  motionCCWButton->setEnabled(false);
345 
346  if (getDomeModel()->isRolloffRoof())
347  domeAzimuthPosition->setText(i18n("Closed"));
348  else
349  enableMotionControl(false);
350  break;
351 
352  default:
353  break;
354  }
355 }
356 
357 
358 void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status)
359 {
360  qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting shutter status to " << status;
361 
362  switch (status)
363  {
364  case ISD::Dome::SHUTTER_OPEN:
365  buttonPressed(shutterOpen, i18n("Opened"));
366  activateButton(shutterClosed, i18n("Close"));
367  appendLogText(i18n("Shutter is open."));
368  break;
369 
370  case ISD::Dome::SHUTTER_OPENING:
371  toggleButtons(shutterOpen, i18n("Opening"), shutterClosed, i18n("Close"));
372  appendLogText(i18n("Shutter is opening..."));
373  break;
374 
375  case ISD::Dome::SHUTTER_CLOSED:
376  buttonPressed(shutterClosed, i18n("Closed"));
377  activateButton(shutterOpen, i18n("Open"));
378  appendLogText(i18n("Shutter is closed."));
379  break;
380  case ISD::Dome::SHUTTER_CLOSING:
381  toggleButtons(shutterClosed, i18n("Closing"), shutterOpen, i18n("Open"));
382  appendLogText(i18n("Shutter is closing..."));
383  break;
384  default:
385  break;
386  }
387 }
388 
389 void Observatory::enableWeather(bool enable)
390 {
391  weatherBox->setEnabled(enable);
392  clearGraphHistory->setVisible(enable);
393  clearGraphHistory->setEnabled(enable);
394  autoscaleValuesCB->setVisible(enable);
395  sensorData->setVisible(enable);
396  sensorGraphs->setVisible(enable);
397 }
398 
399 void Observatory::clearSensorDataHistory()
400 {
401  std::map<QString, QVector<QCPGraphData>*>::iterator it;
402 
403  for (it = sensorGraphData.begin(); it != sensorGraphData.end(); ++it)
404  {
405  QVector<QCPGraphData>* graphDataVector = it->second;
406  if (graphDataVector->size() > 0)
407  {
408  // we keep only the last one
409  QCPGraphData last = graphDataVector->last();
410  graphDataVector->clear();
411  QDateTime when = QDateTime();
412  when.setTime_t(static_cast<uint>(last.key));
413  updateSensorGraph(it->first, when, last.value);
414  }
415  }
416 
417  // force an update to the current graph
418  if (!selectedSensorID.isEmpty())
419  selectedSensorChanged(selectedSensorID);
420 }
421 
422 void Observatory::setWeatherModel(ObservatoryWeatherModel *model)
423 {
424  mObservatoryModel->setWeatherModel(model);
425 
426  // disable the weather UI
427  enableWeather(false);
428 
429  if (model != nullptr)
430  {
431  connect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather);
432  connect(model, &Ekos::ObservatoryWeatherModel::newWeatherData, this, &Ekos::Observatory::newWeatherData);
433  }
434  else
435  shutdownWeather();
436 
437  // make invisible, since not implemented yet
438  weatherWarningSchedulerCB->setVisible(false);
439  weatherAlertSchedulerCB->setVisible(false);
440 }
441 
442 void Observatory::enableMotionControl(bool enabled)
443 {
444  MotionBox->setEnabled(enabled);
445 
446  // absolute motion controls
447  if (getDomeModel()->canAbsoluteMove())
448  {
449  motionMoveAbsButton->setEnabled(enabled);
450  absoluteMotionSB->setEnabled(enabled);
451  }
452  else
453  {
454  motionMoveAbsButton->setEnabled(false);
455  absoluteMotionSB->setEnabled(false);
456  }
457 
458  // relative motion controls
459  if (getDomeModel()->canRelativeMove())
460  {
461  motionMoveRelButton->setEnabled(enabled);
462  relativeMotionSB->setEnabled(enabled);
463  motionCWButton->setEnabled(enabled);
464  motionCCWButton->setEnabled(enabled);
465  }
466  else
467  {
468  motionMoveRelButton->setEnabled(false);
469  relativeMotionSB->setEnabled(false);
470  motionCWButton->setEnabled(false);
471  motionCCWButton->setEnabled(false);
472  }
473 
474  // special case for rolloff roofs
475  if (getDomeModel()->isRolloffRoof())
476  {
477  motionCWButton->setText(i18n("Open"));
478  motionCCWButton->setText(i18n("Close"));
479  motionCWButton->setEnabled(enabled);
480  motionCCWButton->setEnabled(enabled);
481  motionMoveAbsButton->setVisible(false);
482  motionMoveRelButton->setVisible(false);
483  absoluteMotionSB->setVisible(false);
484  relativeMotionSB->setVisible(false);
485  }
486 }
487 
488 void Observatory::enableAutoSync(bool enabled)
489 {
490  if (getDomeModel() == nullptr)
491  showAutoSync(false);
492  else
493  {
494  getDomeModel()->setAutoSync(enabled);
495  showAutoSync(enabled);
496  }
497 }
498 
499 void Observatory::showAutoSync(bool enabled)
500 {
501  slavingEnableButton->setChecked(enabled);
502  slavingDisableButton->setChecked(! enabled);
503 }
504 
505 void Observatory::initWeather()
506 {
507  // initialize the weather sensor data group box
508  sensorDataBoxLayout = new QGridLayout();
509  sensorData->setLayout(sensorDataBoxLayout);
510 
511  enableWeather(true);
512  initSensorGraphs();
513 
514  connect(weatherWarningBox, &QGroupBox::clicked, getWeatherModel(), &ObservatoryWeatherModel::setWarningActionsActive);
515  connect(weatherAlertBox, &QGroupBox::clicked, getWeatherModel(), &ObservatoryWeatherModel::setAlertActionsActive);
516 
517  connect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus);
518  connect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather);
519  connect(clearGraphHistory, &QPushButton::clicked, this, &Observatory::clearSensorDataHistory);
520  connect(autoscaleValuesCB, &QCheckBox::clicked, [this](bool checked)
521  {
522  getWeatherModel()->setAutoScaleValues(checked);
523  this->refreshSensorGraph();
524  });
525  connect(&weatherStatusTimer, &QTimer::timeout, [this]()
526  {
527  weatherWarningStatusLabel->setText(getWeatherModel()->getWarningActionsStatus());
528  weatherAlertStatusLabel->setText(getWeatherModel()->getAlertActionsStatus());
529  });
530 
531  weatherBox->setEnabled(true);
532  autoscaleValuesCB->setChecked(getWeatherModel()->autoScaleValues());
533  weatherWarningBox->setChecked(getWeatherModel()->getWarningActionsActive());
534  weatherAlertBox->setChecked(getWeatherModel()->getAlertActionsActive());
535  setWeatherStatus(getWeatherModel()->status());
536  setWarningActions(getWeatherModel()->getWarningActions());
537  setAlertActions(getWeatherModel()->getAlertActions());
538 
539  initWeatherActions(true);
540  weatherStatusTimer.start(1000);
541  if (getWeatherModel()->refresh() == false)
542  appendLogText(i18n("Refreshing weather data failed."));
543  // avoid double init
544  disconnect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather);
545 }
546 
547 void Observatory::shutdownWeather()
548 {
549  weatherStatusTimer.stop();
550  setWeatherStatus(ISD::Weather::WEATHER_IDLE);
551  enableWeather(false);
552  // disable the UI controls for weather actions
553  initWeatherActions(false);
554  // catch re-connect
555  if (getWeatherModel() != nullptr)
556  connect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather);
557 
558 }
559 
560 void Observatory::initWeatherActions(bool enabled)
561 {
562  if (enabled && getDomeModel() != nullptr && getDomeModel()->isActive())
563  {
564  // make the entire box visible
565  weatherActionsBox->setVisible(true);
566  weatherActionsBox->setEnabled(true);
567 
568  // enable warning and alert action control
569  weatherAlertDomeCB->setEnabled(true);
570  weatherWarningDomeCB->setEnabled(true);
571 
572  // only domes with shutters need shutter action controls
573  if (getDomeModel()->hasShutter())
574  {
575  weatherAlertShutterCB->setEnabled(true);
576  weatherWarningShutterCB->setEnabled(true);
577  }
578  else
579  {
580  weatherAlertShutterCB->setEnabled(false);
581  weatherWarningShutterCB->setEnabled(false);
582  }
583  }
584  else
585  {
586  weatherActionsBox->setVisible(false);
587  weatherActionsBox->setEnabled(false);
588  }
589 }
590 
591 
592 void Observatory::updateSensorGraph(QString sensor_label, QDateTime now, double value)
593 {
594  // we assume that labels are unique and use the full label as identifier
595  QString id = sensor_label;
596 
597  // lazy instantiation of the sensor data storage
598  if (sensorGraphData[id] == nullptr)
599  {
600  sensorGraphData[id] = new QVector<QCPGraphData>();
601  sensorRanges[id] = value > 0 ? 1 : (value < 0 ? -1 : 0);
602  }
603 
604  // store the data
605  sensorGraphData[id]->append(QCPGraphData(static_cast<double>(now.toTime_t()), value));
606 
607  // add data for the graphs we display
608  if (selectedSensorID == id)
609  {
610  // display first point in scattered style
611  if (sensorGraphData[id]->size() == 1)
612  sensorGraphs->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black, 0), QBrush(Qt::green),
613  5));
614  else
615  sensorGraphs->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone));
616 
617  // display data point
618  sensorGraphs->graph()->addData(sensorGraphData[id]->last().key, sensorGraphData[id]->last().value);
619 
620  // determine where the x axis is relatively to the value ranges
621  if ((sensorRanges[id] > 0 && value < 0) || (sensorRanges[id] < 0 && value > 0))
622  sensorRanges[id] = 0;
623 
624  refreshSensorGraph();
625  }
626 }
627 
628 void Observatory::updateSensorData(const std::vector<ISD::Weather::WeatherData> &data)
629 {
630  QDateTime now = KStarsData::Instance()->lt();
631 
632  for (auto &oneEntry : data)
633  {
634  QString const id = oneEntry.label;
635 
636  if (sensorDataWidgets[id] == nullptr)
637  {
638  QPushButton* labelWidget = new QPushButton(oneEntry.label);
640  labelWidget->setCheckable(true);
641  labelWidget->setStyleSheet("QPushButton:checked\n{\nbackground-color: maroon;\nborder: 1px outset;\nfont-weight:bold;\n}");
642  // we need the object name since the label may contain '&' for keyboard shortcuts
643  labelWidget->setObjectName(oneEntry.label);
644 
645  QLineEdit* valueWidget = new QLineEdit(QString().setNum(oneEntry.value, 'f', 2));
646  // fix width to enable stretching of the graph
647  valueWidget->setMinimumWidth(96);
648  valueWidget->setMaximumWidth(96);
649  valueWidget->setReadOnly(true);
650  valueWidget->setAlignment(Qt::AlignRight);
651 
652  sensorDataWidgets[id] = new QPair<QAbstractButton*, QLineEdit*>(labelWidget, valueWidget);
653 
654  sensorDataBoxLayout->addWidget(labelWidget, sensorDataBoxLayout->rowCount(), 0);
655  sensorDataBoxLayout->addWidget(valueWidget, sensorDataBoxLayout->rowCount() - 1, 1);
656 
657  // initial graph selection
658  if (!selectedSensorID.isEmpty() && id.indexOf('(') > 0 && id.indexOf('(') < id.indexOf(')'))
659  {
660  selectedSensorID = id;
661  labelWidget->setChecked(true);
662  }
663 
664  sensorDataNamesGroup->addButton(labelWidget);
665  }
666  else
667  {
668  sensorDataWidgets[id]->first->setText(QString(oneEntry.label));
669  sensorDataWidgets[id]->second->setText(QString().setNum(oneEntry.value, 'f', 2));
670  }
671 
672  // store sensor data unit if necessary
673  updateSensorGraph(oneEntry.label, now, oneEntry.value);
674  }
675 }
676 
677 void Observatory::mouseOverLine(QMouseEvent *event)
678 {
679  double key = sensorGraphs->xAxis->pixelToCoord(event->localPos().x());
680  QCPGraph *graph = qobject_cast<QCPGraph *>(sensorGraphs->plottableAt(event->pos(), false));
681 
682  if (graph)
683  {
684  int index = sensorGraphs->graph(0)->findBegin(key);
685  double value = sensorGraphs->graph(0)->dataMainValue(index);
686  QDateTime when = QDateTime::fromTime_t(sensorGraphs->graph(0)->dataMainKey(index));
687 
689  event->globalPos(),
690  i18n("%1 = %2 @ %3", selectedSensorID, value, when.toString("hh:mm")));
691  }
692  else
693  {
695  }
696 }
697 
698 
699 void Observatory::refreshSensorGraph()
700 {
701 
702  sensorGraphs->rescaleAxes();
703 
704  // restrict the y-Axis to the values range
705  if (getWeatherModel()->autoScaleValues() == false)
706  {
707  if (sensorRanges[selectedSensorID] > 0)
708  sensorGraphs->yAxis->setRangeLower(0);
709  else if (sensorRanges[selectedSensorID] < 0)
710  sensorGraphs->yAxis->setRangeUpper(0);
711  }
712 
713  sensorGraphs->replot();
714 }
715 
716 void Observatory::selectedSensorChanged(QString id)
717 {
718  QVector<QCPGraphData> *data = sensorGraphData[id];
719 
720  if (data != nullptr)
721  {
722  // copy the graph data to the graph container
723  QCPGraphDataContainer *container = new QCPGraphDataContainer();
724  for (QVector<QCPGraphData>::iterator it = data->begin(); it != data->end(); ++it)
725  container->add(QCPGraphData(it->key, it->value));
726 
727  sensorGraphs->graph()->setData(QSharedPointer<QCPGraphDataContainer>(container));
728  selectedSensorID = id;
729  refreshSensorGraph();
730  }
731 }
732 
733 void Observatory::setWeatherStatus(ISD::Weather::Status status)
734 {
735  QString label;
736  if (status != m_WeatherStatus)
737  {
738  switch (status)
739  {
740  case ISD::Weather::WEATHER_OK:
741  label = "security-high";
742  appendLogText(i18n("Weather is OK"));
743  break;
744  case ISD::Weather::WEATHER_WARNING:
745  label = "security-medium";
746  appendLogText(i18n("Weather Warning"));
747  break;
748  case ISD::Weather::WEATHER_ALERT:
749  label = "security-low";
750  appendLogText(i18n("Weather Alert"));
751  break;
752  default:
753  label = "";
754  break;
755  }
756 
757  weatherStatusLabel->setPixmap(QIcon::fromTheme(label).pixmap(QSize(28, 28)));
758  m_WeatherStatus = status;
759  }
760 
761  // update weather sensor data
762  updateSensorData(getWeatherModel()->getWeatherData());
763 
764 }
765 
766 void Observatory::initSensorGraphs()
767 {
768  // set some pens, brushes and backgrounds:
769  sensorGraphs->xAxis->setBasePen(QPen(Qt::white, 1));
770  sensorGraphs->yAxis->setBasePen(QPen(Qt::white, 1));
771  sensorGraphs->xAxis->setTickPen(QPen(Qt::white, 1));
772  sensorGraphs->yAxis->setTickPen(QPen(Qt::white, 1));
773  sensorGraphs->xAxis->setSubTickPen(QPen(Qt::white, 1));
774  sensorGraphs->yAxis->setSubTickPen(QPen(Qt::white, 1));
775  sensorGraphs->xAxis->setTickLabelColor(Qt::white);
776  sensorGraphs->yAxis->setTickLabelColor(Qt::white);
777  sensorGraphs->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
778  sensorGraphs->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
779  sensorGraphs->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
780  sensorGraphs->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
781  sensorGraphs->xAxis->grid()->setSubGridVisible(true);
782  sensorGraphs->yAxis->grid()->setSubGridVisible(true);
783  sensorGraphs->xAxis->grid()->setZeroLinePen(Qt::NoPen);
784  sensorGraphs->yAxis->grid()->setZeroLinePen(Qt::NoPen);
785  sensorGraphs->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
786  sensorGraphs->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
787  QLinearGradient plotGradient;
788  plotGradient.setStart(0, 0);
789  plotGradient.setFinalStop(0, 350);
790  plotGradient.setColorAt(0, QColor(80, 80, 80));
791  plotGradient.setColorAt(1, QColor(50, 50, 50));
792  sensorGraphs->setBackground(plotGradient);
793  QLinearGradient axisRectGradient;
794  axisRectGradient.setStart(0, 0);
795  axisRectGradient.setFinalStop(0, 350);
796  axisRectGradient.setColorAt(0, QColor(80, 80, 80));
797  axisRectGradient.setColorAt(1, QColor(30, 30, 30));
798  sensorGraphs->axisRect()->setBackground(axisRectGradient);
799 
801  dateTicker->setDateTimeFormat("hh:mm");
802  dateTicker->setTickCount(2);
803  sensorGraphs->xAxis->setTicker(dateTicker);
804 
805  // allow dragging in all directions
806  sensorGraphs->setInteraction(QCP::iRangeDrag, true);
807  sensorGraphs->setInteraction(QCP::iRangeZoom);
808 
809  // create the universal graph
810  QCPGraph *graph = sensorGraphs->addGraph();
811  graph->setPen(QPen(Qt::darkGreen, 2));
812  graph->setBrush(QColor(10, 100, 50, 70));
813 
814  // ensure that the 0-line is visible
815  sensorGraphs->yAxis->setRangeLower(0);
816 
817  sensorDataNamesGroup = new QButtonGroup();
818  // enable changing the displayed sensor
819  connect(sensorDataNamesGroup, static_cast<void (QButtonGroup::*)(QAbstractButton*)>(&QButtonGroup::buttonClicked), [this](
820  QAbstractButton * button)
821  {
822  selectedSensorChanged(button->objectName());
823  });
824 
825  // show current temperature below the mouse
826  connect(sensorGraphs, &QCustomPlot::mouseMove, this, &Ekos::Observatory::mouseOverLine);
827 
828 }
829 
830 
831 void Observatory::weatherWarningSettingsChanged()
832 {
833  struct WeatherActions actions;
834  actions.parkDome = weatherWarningDomeCB->isChecked();
835  actions.closeShutter = weatherWarningShutterCB->isChecked();
836  // Fixme: not implemented yet
837  actions.stopScheduler = false;
838  actions.delay = static_cast<unsigned int>(weatherWarningDelaySB->value());
839 
840  getWeatherModel()->setWarningActions(actions);
841 }
842 
843 void Observatory::weatherAlertSettingsChanged()
844 {
845  struct WeatherActions actions;
846  actions.parkDome = weatherAlertDomeCB->isChecked();
847  actions.closeShutter = weatherAlertShutterCB->isChecked();
848  // Fixme: not implemented yet
849  actions.stopScheduler = false;
850  actions.delay = static_cast<unsigned int>(weatherAlertDelaySB->value());
851 
852  getWeatherModel()->setAlertActions(actions);
853 }
854 
855 void Observatory::observatoryStatusChanged(bool ready)
856 {
857  // statusReadyButton->setEnabled(!ready);
858  statusReadyButton->setChecked(ready);
859  emit newStatus(ready);
860 }
861 
862 void Observatory::domeAzimuthChanged(double position)
863 {
864  domeAzimuthPosition->setText(QString::number(position, 'f', 2));
865 }
866 
867 
868 void Observatory::setWarningActions(WeatherActions actions)
869 {
870  if (getDomeModel() != nullptr)
871  weatherWarningDomeCB->setChecked(actions.parkDome);
872  else
873  weatherWarningDomeCB->setChecked(actions.parkDome);
874 
875  if (getDomeModel() != nullptr && getDomeModel()->hasShutter())
876  weatherWarningShutterCB->setChecked(actions.closeShutter);
877  else
878  weatherWarningShutterCB->setChecked(actions.closeShutter);
879 
880  weatherWarningDelaySB->setValue(static_cast<int>(actions.delay));
881 }
882 
883 
884 void Observatory::setAlertActions(WeatherActions actions)
885 {
886  if (getDomeModel() != nullptr)
887  weatherAlertDomeCB->setChecked(actions.parkDome);
888  else
889  weatherAlertDomeCB->setChecked(false);
890 
891  if (getDomeModel() != nullptr && getDomeModel()->hasShutter())
892  weatherAlertShutterCB->setChecked(actions.closeShutter);
893  else
894  weatherAlertShutterCB->setChecked(false);
895 
896  weatherAlertDelaySB->setValue(static_cast<int>(actions.delay));
897 }
898 
899 void Observatory::toggleButtons(QPushButton *buttonPressed, QString titlePressed, QPushButton *buttonCounterpart,
900  QString titleCounterpart)
901 {
902  buttonPressed->setEnabled(false);
903  buttonPressed->setText(titlePressed);
904 
905  buttonCounterpart->setEnabled(true);
906  buttonCounterpart->setChecked(false);
907  buttonCounterpart->setCheckable(false);
908  buttonCounterpart->setText(titleCounterpart);
909 }
910 
911 void Observatory::activateButton(QPushButton *button, QString title)
912 {
913  button->setEnabled(true);
914  button->setCheckable(false);
915  button->setText(title);
916 }
917 
918 void Observatory::buttonPressed(QPushButton *button, QString title)
919 {
920  button->setEnabled(false);
921  button->setCheckable(true);
922  button->setChecked(true);
923  button->setText(title);
924 
925 }
926 
927 
928 void Observatory::statusControlSettingsChanged()
929 {
930  ObservatoryStatusControl control;
931  control.useDome = useDomeCB->isChecked();
932  control.useShutter = useShutterCB->isChecked();
933  control.useWeather = useWeatherCB->isChecked();
934  mObservatoryModel->setStatusControl(control);
935 }
936 
937 
938 void Observatory::appendLogText(const QString &text)
939 {
940  m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2",
941  KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text));
942 
943  qCInfo(KSTARS_EKOS_OBSERVATORY) << text;
944 
945  emit newLog(text);
946 }
947 
948 void Observatory::clearLog()
949 {
950  m_LogText.clear();
951  emit newLog(QString());
952 }
953 
954 }
@ ssCircle
\enumimage{ssCircle.png} a circle
Definition: qcustomplot.h:2478
void setColorAt(qreal position, const QColor &color)
void showText(const QPoint &pos, const QString &text, QWidget *w)
void setFinalStop(const QPointF &stop)
AlignRight
void setPen(const QPen &pen)
void setCheckable(bool)
QString number(int n, int base)
const KStarsDateTime & lt() const
Definition: kstarsdata.h:150
void setAlignment(Qt::Alignment flag)
T & last()
void setSizePolicy(QSizePolicy)
Ekos is an advanced Astrophotography tool for Linux. It is based on a modular extensible framework to...
Definition: align.cpp:70
QVector::iterator begin()
void add(const QCPDataContainer< DataType > &data)
Definition: qcustomplot.h:2818
void buttonClicked(QAbstractButton *button)
void clicked(bool checked)
QIcon fromTheme(const QString &name)
Specialized axis ticker for calendar dates and times as axis ticks.
Definition: qcustomplot.h:1744
void setStyleSheet(const QString &styleSheet)
void setChecked(bool)
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5440
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:5409
The generic data container for one-dimensional plottables.
Definition: qcustomplot.h:2560
void setMaximumWidth(int maxw)
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2444
void clear()
QString i18n(const char *text, const TYPE &arg...)
void setTime_t(uint seconds)
char * toString(const T &value)
void clicked(bool checked)
void timeout()
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void setReadOnly(bool)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void setEnabled(bool)
QString label(StandardShortcut id)
QVector::iterator end()
QDateTime fromTime_t(uint seconds)
uint toTime_t() const const
void setObjectName(const QString &name)
@ esSpikeArrow
A filled arrow head with an indented back.
Definition: qcustomplot.h:1492
void hideText()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
int size() const const
void mouseMove(QMouseEvent *event)
void setMinimumWidth(int minw)
void valueChanged(int i)
QString toString(Qt::DateFormat format) const const
@ ssNone
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
Definition: qcustomplot.h:2474
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
Definition: qcustomplot.h:256
void setBrush(const QBrush &brush)
void setText(const QString &text)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
Definition: qcustomplot.h:255
void setStart(const QPointF &start)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Aug 14 2022 04:13:58 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.