Kstars

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