Kstars

portselector.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "portselector.h"
8 
9 #include "indipropertyswitch.h"
10 #include "ksnotification.h"
11 #include "ekos_debug.h"
12 #include "kspaths.h"
13 #include "indi/driverinfo.h"
14 #include "indi/clientmanager.h"
15 #include "Options.h"
16 
17 #include <QDialogButtonBox>
18 #include <QPushButton>
19 
20 namespace Selector
21 {
22 
23 const QStringList Device::BAUD_RATES = { "9600", "19200", "38400", "57600", "115200", "230400" };
24 const QString Device::ACTIVE_STYLESHEET = "background-color: #004400;";
25 //const QString Device::ACTIVE_STYLESHEET = "border-color: #007700; font-weight:bold;";
26 //////////////////////////////////////////////////////////////////////////////////////////
27 ///
28 //////////////////////////////////////////////////////////////////////////////////////////
29 Device::Device(ISD::GenericDevice *device, QGridLayout *grid, uint8_t row) : m_Name(device->getDeviceName()), m_Device(device), m_Grid(grid), m_Row(row)
30 {
31  ColorCode[IPS_IDLE] = Qt::gray;
32  ColorCode[IPS_OK] = Qt::green;
33  ColorCode[IPS_BUSY] = Qt::yellow;
34  ColorCode[IPS_ALERT] = Qt::red;
35 
36  connect(m_Device, &ISD::GenericDevice::switchUpdated, this, &Device::processSwitch);
37  connect(m_Device, &ISD::GenericDevice::textUpdated, this, &Device::processText);
38  connect(m_Device, &ISD::GenericDevice::Connected, this, &Device::setConnected);
39  connect(m_Device, &ISD::GenericDevice::Disconnected, this, &Device::setDisconnected);
40 
41  if (initGUI())
42  syncGUI();
43 }
44 
45 //////////////////////////////////////////////////////////////////////////////////////////
46 ///
47 //////////////////////////////////////////////////////////////////////////////////////////
48 bool Device::initGUI()
49 {
50  INDI::Property modeProperty = m_Device->getBaseDevice()->getSwitch("CONNECTION_MODE");
51  if (!modeProperty.isValid())
52  return false;
53 
54  m_LED = new KLed;
55  m_LED->setLook(KLed::Sunken);
56  m_LED->setState(KLed::On);
57  m_LED->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored);
58  m_Grid->addWidget(m_LED, m_Row, 0, Qt::AlignVCenter);
59 
60  m_Label = new QLabel;
61  m_Label->setText(m_Name);
62  m_Label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored);
63  m_Grid->addWidget(m_Label, m_Row, 1, Qt::AlignLeft);
64 
65  // Serial Button
66  m_SerialB = new QPushButton(i18n("Serial"));
67  m_SerialB->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored);
68  m_SerialB->setToolTip(i18n("Select <b>Serial</b> if your device is connected via Serial to USB adapter."));
69  m_Grid->addWidget(m_SerialB, m_Row, 2);
70  connect(m_SerialB, &QPushButton::clicked, this, [this]()
71  {
72  INDI::PropertyView<ISwitch> connectionMode = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_MODE"));
73  IUResetSwitch(&connectionMode);
74  connectionMode.at(0)->setState(ISS_ON);
75  connectionMode.at(1)->setState(ISS_OFF);
76  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&connectionMode);
77  });
78 
79  // Network Button
80  m_NetworkB = new QPushButton(i18n("Network"));
81  m_NetworkB->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored);
82  m_NetworkB->setToolTip(i18n("Select <b>Network</b> if your device is connected via Ethernet or WiFi."));
83  m_Grid->addWidget(m_NetworkB, m_Row, 3);
84  connect(m_NetworkB, &QPushButton::clicked, this, [this]()
85  {
86  INDI::PropertyView<ISwitch> connectionMode = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_MODE"));
87  IUResetSwitch(&connectionMode);
88  connectionMode.at(0)->setState(ISS_OFF);
89  connectionMode.at(1)->setState(ISS_ON);
90  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&connectionMode);
91  });
92 
93  INDI::PropertyView<ISwitch> connectionMode = *(modeProperty->getSwitch());
94  if (connectionMode.findWidgetByName("CONNECTION_SERIAL"))
95  {
96  if (m_Device->getBaseDevice()->getProperty("SYSTEM_PORTS").isValid())
97  {
98  m_Ports = new QComboBox;
99  m_Ports->setEditable(true);
100  m_Ports->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored);
101  m_Ports->setToolTip(i18n("Select Serial port"));
102 
103  INDI::PropertyView<ISwitch> systemPorts = *(m_Device->getBaseDevice()->getSwitch("SYSTEM_PORTS"));
104  for (auto &oneSwitch : systemPorts)
105  m_Ports->addItem(oneSwitch.getLabel());
106  connect(m_Ports, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), this,
107  [this](int index)
108  {
109  // Double check property still exists.
110  INDI::Property systemPorts = m_Device->getBaseDevice()->getProperty("SYSTEM_PORTS");
111  if (systemPorts.isValid())
112  {
113  INDI::PropertyView<ISwitch> systemPortsSwitch = *(systemPorts->getSwitch());
114  IUResetSwitch(&systemPortsSwitch);
115  systemPortsSwitch.at(index)->setState(ISS_ON);
116  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&systemPortsSwitch);
117  }
118  });
119  // When combo box text changes
120  connect(m_Ports->lineEdit(), &QLineEdit::editingFinished, this, [this]()
121  {
122  INDI::PropertyView<IText> port = *(m_Device->getBaseDevice()->getText("DEVICE_PORT"));
123  port.at(0)->setText(m_Ports->lineEdit()->text().toLatin1().constData());
124  m_Device->getDriverInfo()->getClientManager()->sendNewText(&port);
125  });
126  }
127 
128  if (m_Device->getBaseDevice()->getProperty("DEVICE_BAUD_RATE").isValid())
129  {
130  m_BaudRates = new QComboBox;
131  m_BaudRates->addItems(BAUD_RATES);
132  m_BaudRates->setToolTip(i18n("Select Baud rate"));
133  connect(m_BaudRates, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), this,
134  [this](int index)
135  {
136  INDI::PropertyView<ISwitch> systemBauds = *(m_Device->getBaseDevice()->getSwitch("DEVICE_BAUD_RATE"));
137  IUResetSwitch(&systemBauds);
138  systemBauds.at(index)->setState(ISS_ON);
139  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&systemBauds);
140  });
141  }
142  }
143  else
144  m_SerialB->setEnabled(false);
145 
146  if (connectionMode.findWidgetByName("CONNECTION_TCP"))
147  {
148  m_HostName = new QLineEdit;
149  m_HostName->setPlaceholderText(i18n("Host name or IP address."));
150  m_HostName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored);
151  connect(m_HostName, &QLineEdit::editingFinished, this, [this]()
152  {
153  INDI::PropertyView<IText> server = *(m_Device->getBaseDevice()->getText("DEVICE_ADDRESS"));
154  server.at(0)->setText(m_HostName->text().toLatin1().constData());
155  m_Device->getDriverInfo()->getClientManager()->sendNewText(&server);
156  });
157 
158  m_HostPort = new QLineEdit;
159  m_HostPort->setPlaceholderText(i18n("Port"));
160  connect(m_HostPort, &QLineEdit::editingFinished, this, [this]()
161  {
162  INDI::PropertyView<IText> server = *(m_Device->getBaseDevice()->getText("DEVICE_ADDRESS"));
163  server.at(1)->setText(m_HostPort->text().toLatin1().constData());
164  m_Device->getDriverInfo()->getClientManager()->sendNewText(&server);
165  });
166 
167  m_HostProtocolTCP = new QPushButton("TCP");
168  connect(m_HostProtocolTCP, &QPushButton::clicked, this, [this]()
169  {
170  INDI::PropertyView<ISwitch> connectionType = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_TYPE"));
171  IUResetSwitch(&connectionType);
172  connectionType.at(0)->setState(ISS_ON);
173  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&connectionType);
174  });
175 
176  m_HostProtocolUDP = new QPushButton("UDP");
177  connect(m_HostProtocolUDP, &QPushButton::clicked, this, [this]()
178  {
179  INDI::PropertyView<ISwitch> connectionType = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_TYPE"));
180  IUResetSwitch(&connectionType);
181  connectionType.at(1)->setState(ISS_ON);
182  m_Device->getDriverInfo()->getClientManager()->sendNewSwitch(&connectionType);
183  });
184  }
185  else
186  m_NetworkB->setEnabled(false);
187 
188  m_ConnectB = new QPushButton;
189  m_ConnectB->setFixedSize(QSize(28, 28));
190  m_ConnectB->setToolTip(i18n("Connect"));
191  m_ConnectB->setIcon(QIcon::fromTheme("network-connect"));
192  m_Grid->addWidget(m_ConnectB, m_Row, 8);
193  connect(m_ConnectB, &QPushButton::clicked, m_Device, &ISD::GenericDevice::Connect);
194 
195  m_DisconnectB = new QPushButton;
196  m_DisconnectB->setFixedSize(QSize(28, 28));
197  m_DisconnectB->setToolTip(i18n("Disconnect"));
198  m_DisconnectB->setIcon(QIcon::fromTheme("network-disconnect"));
199  m_Grid->addWidget(m_DisconnectB, m_Row, 9);
200  connect(m_DisconnectB, &QPushButton::clicked, m_Device, &ISD::GenericDevice::Disconnect);
201 
202  setConnectionMode(static_cast<ConnectionMode>(connectionMode.findOnSwitchIndex()));
203 
204  return true;
205 }
206 
207 //////////////////////////////////////////////////////////////////////////////////////////
208 ///
209 //////////////////////////////////////////////////////////////////////////////////////////
210 void Device::syncGUI()
211 {
212  // Check if initGUI failed before?
213  if (m_LED == nullptr)
214  {
215  // Try to initiGUI again
216  initGUI();
217 
218  // If it failed again, return.
219  if (m_LED == nullptr)
220  return;
221  }
222 
223  m_LED->setColor(ColorCode.value(m_Device->getState("CONNECTION")));
224 
225  INDI::PropertyView<ISwitch> connectionMode = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_MODE"));
226 
227  if (connectionMode.findOnSwitchIndex() == CONNECTION_SERIAL)
228  {
229  m_ActiveConnectionMode = CONNECTION_SERIAL;
230  m_SerialB->setStyleSheet(ACTIVE_STYLESHEET);
231  m_NetworkB->setStyleSheet(QString());
232 
233  if (m_Ports)
234  {
235  INDI::PropertyView<IText> port = *(m_Device->getBaseDevice()->getText("DEVICE_PORT"));
236  m_Ports->setEditText(port.at(0)->getText());
237  }
238 
239  if (m_BaudRates)
240  {
241  INDI::PropertyView<ISwitch> systemBauds = *(m_Device->getBaseDevice()->getSwitch("DEVICE_BAUD_RATE"));
242  m_BaudRates->setCurrentIndex(systemBauds.findOnSwitchIndex());
243  }
244  }
245  else
246  {
247  m_ActiveConnectionMode = CONNECTION_NETWORK;
248  m_SerialB->setStyleSheet(QString());
249  m_NetworkB->setStyleSheet(ACTIVE_STYLESHEET);
250 
251  if (m_Device->getBaseDevice()->getProperty("DEVICE_ADDRESS").isValid())
252  {
253  INDI::PropertyView<IText> server = *(m_Device->getBaseDevice()->getText("DEVICE_ADDRESS"));
254  m_HostName->setText(server.at(0)->getText());
255  m_HostPort->setText(server.at(1)->getText());
256  }
257 
258  if (m_Device->getBaseDevice()->getProperty("CONNECTION_TYPE").isValid())
259  {
260  INDI::PropertyView<ISwitch> connectionType = *(m_Device->getBaseDevice()->getSwitch("CONNECTION_TYPE"));
261  m_HostProtocolTCP->setStyleSheet(connectionType.findOnSwitchIndex() == 0 ? ACTIVE_STYLESHEET : QString());
262  m_HostProtocolUDP->setStyleSheet(connectionType.findOnSwitchIndex() == 1 ? ACTIVE_STYLESHEET : QString());
263  }
264  }
265 
266  if (m_Device->isConnected())
267  {
268  m_ConnectB->setStyleSheet(ACTIVE_STYLESHEET);
269  m_DisconnectB->setStyleSheet(QString());
270  }
271  else
272  {
273  m_ConnectB->setStyleSheet(QString());
274  m_DisconnectB->setStyleSheet(ACTIVE_STYLESHEET);
275  }
276 }
277 
278 //////////////////////////////////////////////////////////////////////////////////////////
279 ///
280 //////////////////////////////////////////////////////////////////////////////////////////
281 uint8_t Device::systemPortCount() const
282 {
283  if (m_Ports)
284  return m_Ports->count();
285  else
286  return 0;
287 }
288 
289 //////////////////////////////////////////////////////////////////////////////////////////
290 ///
291 //////////////////////////////////////////////////////////////////////////////////////////
292 void Device::setConnectionMode(ConnectionMode mode)
293 {
294  m_ActiveConnectionMode = mode;
295 
296  if (mode == CONNECTION_SERIAL)
297  {
298  if (m_HostName)
299  {
300  m_Grid->removeWidget(m_HostName);
301  m_HostName->setParent(nullptr);
302  m_Grid->removeWidget(m_HostPort);
303  m_HostPort->setParent(nullptr);
304  m_Grid->removeWidget(m_HostProtocolTCP);
305  m_HostProtocolTCP->setParent(nullptr);
306  m_Grid->removeWidget(m_HostProtocolUDP);
307  m_HostProtocolUDP->setParent(nullptr);
308  }
309 
310  m_Grid->addWidget(m_Ports, m_Row, 4, 1, 2);
311  m_Grid->addWidget(m_BaudRates, m_Row, 6, 1, 2);
312  m_Grid->setColumnStretch(4, 2);
313  }
314  else if (mode == CONNECTION_NETWORK)
315  {
316  if (m_Ports)
317  {
318  m_Grid->removeWidget(m_Ports);
319  m_Ports->setParent(nullptr);
320  m_Grid->removeWidget(m_BaudRates);
321  m_BaudRates->setParent(nullptr);
322  }
323 
324  m_Grid->addWidget(m_HostName, m_Row, 4);
325  m_Grid->addWidget(m_HostPort, m_Row, 5);
326  m_Grid->addWidget(m_HostProtocolTCP, m_Row, 6);
327  m_Grid->addWidget(m_HostProtocolUDP, m_Row, 7);
328  m_Grid->setColumnStretch(4, 2);
329  }
330 }
331 
332 //////////////////////////////////////////////////////////////////////////////////////////
333 ///
334 //////////////////////////////////////////////////////////////////////////////////////////
335 void Device::processSwitch(ISwitchVectorProperty *svp)
336 {
337  QString prop = svp->name;
338  if (prop == "CONNECTION_MODE")
339  {
340  setConnectionMode(static_cast<ConnectionMode>(IUFindOnSwitchIndex(svp)));
341  syncGUI();
342  }
343  else if (prop == "CONNECTION_TYPE" || prop == "SYSTEM_PORTS" ||
344  prop == "DEVICE_BAUD_RATE")
345  syncGUI();
346 }
347 
348 //////////////////////////////////////////////////////////////////////////////////////////
349 ///
350 //////////////////////////////////////////////////////////////////////////////////////////
351 void Device::processText(ITextVectorProperty *tvp)
352 {
353  QString prop = tvp->name;
354  if (prop == "DEVICE_PORT")
355  syncGUI();
356 }
357 
358 //////////////////////////////////////////////////////////////////////////////////////////
359 ///
360 //////////////////////////////////////////////////////////////////////////////////////////
361 void Device::setConnected()
362 {
363  syncGUI();
364 }
365 
366 //////////////////////////////////////////////////////////////////////////////////////////
367 ///
368 //////////////////////////////////////////////////////////////////////////////////////////
369 void Device::setDisconnected()
370 {
371  syncGUI();
372 }
373 
374 //////////////////////////////////////////////////////////////////////////////////////////
375 ///
376 //////////////////////////////////////////////////////////////////////////////////////////
377 Dialog::Dialog(QWidget *parent) : QDialog(parent)
378 {
379  QVBoxLayout *mainLayout = new QVBoxLayout(this);
380  QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
381 
382  QPushButton *connectAll = new QPushButton(i18n("Connect All"), this);
383  buttonBox->addButton(connectAll, QDialogButtonBox::AcceptRole);
385 
386  connect(buttonBox, &QDialogButtonBox::accepted, this, &Dialog::accept);
387  connect(buttonBox, &QDialogButtonBox::rejected, this, &Dialog::reject);
388 
389  m_Layout = new QGridLayout;
390 
391  mainLayout->addLayout(m_Layout);
392  mainLayout->addStretch();
393  mainLayout->addWidget(buttonBox);
394 
395  setWindowTitle(i18nc("@title:window", "Port Selector"));
396 }
397 
398 //////////////////////////////////////////////////////////////////////////////////////////
399 ///
400 //////////////////////////////////////////////////////////////////////////////////////////
401 void Dialog::addDevice(ISD::GenericDevice *device)
402 {
403  auto pos = std::find_if(m_Devices.begin(), m_Devices.end(), [device](std::unique_ptr<Device> &oneDevice)
404  {
405  return oneDevice->name() == device->getDeviceName();
406  });
407 
408  // New
409  if (pos == m_Devices.end())
410  {
411  std::unique_ptr<Device> dev(new Device(device, m_Layout, m_Devices.size()));
412  m_Devices.push_back(std::move(dev));
413  }
414  // Existing device
415  else
416  {
417  (*pos)->syncGUI();
418  }
419 
420 }
421 
422 //////////////////////////////////////////////////////////////////////////////////////////
423 ///
424 //////////////////////////////////////////////////////////////////////////////////////////
425 void Dialog::removeDevice(const QString &name)
426 {
427  m_Devices.erase(std::remove_if(m_Devices.begin(), m_Devices.end(), [name](const auto & oneDevice)
428  {
429  return oneDevice->name() == name;
430  }), m_Devices.end());
431 }
432 
433 //////////////////////////////////////////////////////////////////////////////////////////
434 /// Should Port Selector be shown automatically?
435 /// If we only have ONE device with ONE port, then no need to bother the user
436 /// with useless selection.
437 //////////////////////////////////////////////////////////////////////////////////////////
438 bool Dialog::shouldShow() const
439 {
440  if (m_Devices.empty())
441  return false;
442 
443  uint32_t systemPortCount = 0;
444  uint32_t serialDevices = 0;
445  uint32_t networkDevices = 0;
446 
447  for (const auto &oneDevice : m_Devices)
448  {
449  if (oneDevice->systemPortCount() > 0)
450  systemPortCount = oneDevice->systemPortCount();
451 
452  if (oneDevice->activeConnectionMode() == CONNECTION_SERIAL)
453  serialDevices++;
454  else if (oneDevice->activeConnectionMode() == CONNECTION_NETWORK)
455  networkDevices++;
456  }
457 
458  // If we just have a single serial device with single port, no need to toggle port selector.
459  // If have have one or more network devices, we must show the dialog for the first time.
460  if (networkDevices == 0 && serialDevices <= 1 && systemPortCount <= 1)
461  return false;
462 
463  // Otherwise, show dialog
464  return true;
465 }
466 }
void setPlaceholderText(const QString &)
AlignVCenter
void setText(const QString &)
void clicked(bool checked)
QIcon fromTheme(const QString &name)
void editingFinished()
void setLook(Look look)
void addStretch(int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void addItems(const QStringList &texts)
QString i18n(const char *text, const TYPE &arg...)
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
QCA_EXPORT QVariant getProperty(const QString &name)
void setFixedSize(const QSize &s)
void setEditable(bool editable)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void addLayout(QLayout *layout, int stretch)
QString getText(const QString &caption, const QString &label, const QString &value=QString(), bool *ok=nullptr, QWidget *parent=nullptr, QValidator *validator=nullptr, const QString &mask=QString(), const QString &whatsThis=QString(), const QStringList &completionList=QStringList())
void activated(int index)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.