File indexing completed on 2024-04-28 03:43:09
0001 /* 0002 SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "portselector.h" 0008 0009 #include "indipropertyswitch.h" 0010 #include "ksnotification.h" 0011 #include "ekos_debug.h" 0012 #include "kspaths.h" 0013 #include "indi/driverinfo.h" 0014 #include "indi/clientmanager.h" 0015 #include "Options.h" 0016 0017 #include <QDialogButtonBox> 0018 #include <QPushButton> 0019 0020 namespace Selector 0021 { 0022 0023 const QStringList Device::BAUD_RATES = { "9600", "19200", "38400", "57600", "115200", "230400" }; 0024 const QString Device::ACTIVE_STYLESHEET = "background-color: #004400;"; 0025 //const QString Device::ACTIVE_STYLESHEET = "border-color: #007700; font-weight:bold;"; 0026 ////////////////////////////////////////////////////////////////////////////////////////// 0027 /// 0028 ////////////////////////////////////////////////////////////////////////////////////////// 0029 Device::Device(const QSharedPointer<ISD::GenericDevice> &device, QGridLayout *grid, 0030 uint8_t row) : m_Name(device->getDeviceName()), m_Device(device), m_Grid(grid), m_Row(row) 0031 { 0032 ColorCode[IPS_IDLE] = Qt::gray; 0033 ColorCode[IPS_OK] = Qt::green; 0034 ColorCode[IPS_BUSY] = Qt::yellow; 0035 ColorCode[IPS_ALERT] = Qt::red; 0036 0037 connect(m_Device.get(), &ISD::GenericDevice::propertyUpdated, this, &Device::updateProperty); 0038 0039 if (initGUI()) 0040 syncGUI(); 0041 } 0042 0043 ////////////////////////////////////////////////////////////////////////////////////////// 0044 /// 0045 ////////////////////////////////////////////////////////////////////////////////////////// 0046 bool Device::initGUI() 0047 { 0048 INDI::Property modeProperty = m_Device->getBaseDevice().getSwitch("CONNECTION_MODE"); 0049 if (!modeProperty.isValid()) 0050 return false; 0051 0052 m_LED = new KLed; 0053 m_LED->setLook(KLed::Sunken); 0054 m_LED->setState(KLed::On); 0055 m_LED->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored); 0056 m_Grid->addWidget(m_LED, m_Row, 0, Qt::AlignVCenter); 0057 0058 m_Label = new QLabel; 0059 m_Label->setText(m_Name); 0060 m_Label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored); 0061 m_Grid->addWidget(m_Label, m_Row, 1, Qt::AlignLeft); 0062 0063 // Serial Button 0064 m_SerialB = new QPushButton(i18n("Serial")); 0065 m_SerialB->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored); 0066 m_SerialB->setToolTip(i18n("Select <b>Serial</b> if your device is connected via Serial to USB adapter.")); 0067 m_Grid->addWidget(m_SerialB, m_Row, 2); 0068 connect(m_SerialB, &QPushButton::clicked, this, [this]() 0069 { 0070 INDI::PropertyView<ISwitch> connectionMode = *(m_Device->getBaseDevice().getSwitch("CONNECTION_MODE")); 0071 IUResetSwitch(&connectionMode); 0072 connectionMode.at(0)->setState(ISS_ON); 0073 connectionMode.at(1)->setState(ISS_OFF); 0074 m_Device->sendNewProperty(&connectionMode); 0075 }); 0076 0077 // Network Button 0078 m_NetworkB = new QPushButton(i18n("Network")); 0079 m_NetworkB->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored); 0080 m_NetworkB->setToolTip(i18n("Select <b>Network</b> if your device is connected via Ethernet or WiFi.")); 0081 m_Grid->addWidget(m_NetworkB, m_Row, 3); 0082 connect(m_NetworkB, &QPushButton::clicked, this, [this]() 0083 { 0084 INDI::PropertyView<ISwitch> connectionMode = *(m_Device->getBaseDevice().getSwitch("CONNECTION_MODE")); 0085 IUResetSwitch(&connectionMode); 0086 connectionMode.at(0)->setState(ISS_OFF); 0087 connectionMode.at(1)->setState(ISS_ON); 0088 m_Device->sendNewProperty(&connectionMode); 0089 }); 0090 0091 INDI::PropertyView<ISwitch> connectionMode = *(modeProperty.getSwitch()); 0092 if (connectionMode.findWidgetByName("CONNECTION_SERIAL")) 0093 { 0094 if (m_Device->getBaseDevice().getProperty("SYSTEM_PORTS").isValid()) 0095 { 0096 m_Ports = new QComboBox; 0097 m_Ports->setEditable(true); 0098 m_Ports->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored); 0099 m_Ports->setToolTip(i18n("Select Serial port")); 0100 0101 INDI::PropertyView<ISwitch> systemPorts = *(m_Device->getBaseDevice().getSwitch("SYSTEM_PORTS")); 0102 for (auto &oneSwitch : systemPorts) 0103 m_Ports->addItem(oneSwitch.getLabel()); 0104 connect(m_Ports, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), this, 0105 [this](int index) 0106 { 0107 // Double check property still exists. 0108 INDI::Property systemPorts = m_Device->getBaseDevice().getProperty("SYSTEM_PORTS"); 0109 if (systemPorts.isValid()) 0110 { 0111 INDI::PropertyView<ISwitch> systemPortsSwitch = *(systemPorts.getSwitch()); 0112 IUResetSwitch(&systemPortsSwitch); 0113 systemPortsSwitch.at(index)->setState(ISS_ON); 0114 m_Device->sendNewProperty(&systemPortsSwitch); 0115 } 0116 }); 0117 // When combo box text changes 0118 connect(m_Ports->lineEdit(), &QLineEdit::editingFinished, this, [this]() 0119 { 0120 INDI::PropertyView<IText> port = *(m_Device->getBaseDevice().getText("DEVICE_PORT")); 0121 port.at(0)->setText(m_Ports->lineEdit()->text().toLatin1().constData()); 0122 m_Device->sendNewProperty(&port); 0123 }); 0124 } 0125 0126 if (m_Device->getBaseDevice()->getProperty("DEVICE_BAUD_RATE").isValid()) 0127 { 0128 m_BaudRates = new QComboBox; 0129 m_BaudRates->addItems(BAUD_RATES); 0130 m_BaudRates->setToolTip(i18n("Select Baud rate")); 0131 connect(m_BaudRates, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), this, 0132 [this](int index) 0133 { 0134 INDI::PropertyView<ISwitch> systemBauds = *(m_Device->getBaseDevice().getSwitch("DEVICE_BAUD_RATE")); 0135 IUResetSwitch(&systemBauds); 0136 systemBauds.at(index)->setState(ISS_ON); 0137 m_Device->sendNewProperty(&systemBauds); 0138 }); 0139 } 0140 } 0141 else 0142 m_SerialB->setEnabled(false); 0143 0144 if (connectionMode.findWidgetByName("CONNECTION_TCP")) 0145 { 0146 m_HostName = new QLineEdit; 0147 m_HostName->setPlaceholderText(i18n("Host name or IP address.")); 0148 m_HostName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored); 0149 connect(m_HostName, &QLineEdit::editingFinished, this, [this]() 0150 { 0151 INDI::PropertyView<IText> server = *(m_Device->getBaseDevice().getText("DEVICE_ADDRESS")); 0152 server.at(0)->setText(m_HostName->text().toLatin1().constData()); 0153 m_Device->sendNewProperty(&server); 0154 }); 0155 0156 m_HostPort = new QLineEdit; 0157 m_HostPort->setPlaceholderText(i18n("Port")); 0158 connect(m_HostPort, &QLineEdit::editingFinished, this, [this]() 0159 { 0160 auto server = m_Device->getProperty("DEVICE_ADDRESS").getText(); 0161 server->at(1)->setText(m_HostPort->text().toLatin1().constData()); 0162 m_Device->sendNewProperty(server); 0163 }); 0164 0165 m_HostProtocolTCP = new QPushButton("TCP"); 0166 connect(m_HostProtocolTCP, &QPushButton::clicked, this, [this]() 0167 { 0168 auto connectionType = m_Device->getProperty("CONNECTION_TYPE").getSwitch(); 0169 connectionType->reset(); 0170 connectionType->at(0)->setState(ISS_ON); 0171 m_Device->sendNewProperty(connectionType); 0172 }); 0173 0174 m_HostProtocolUDP = new QPushButton("UDP"); 0175 connect(m_HostProtocolUDP, &QPushButton::clicked, this, [this]() 0176 { 0177 auto connectionType = m_Device->getProperty("CONNECTION_TYPE").getSwitch(); 0178 connectionType->reset(); 0179 connectionType->at(1)->setState(ISS_ON); 0180 m_Device->sendNewProperty(connectionType); 0181 }); 0182 } 0183 else 0184 m_NetworkB->setEnabled(false); 0185 0186 m_ConnectB = new QPushButton; 0187 m_ConnectB->setFixedSize(QSize(28, 28)); 0188 m_ConnectB->setToolTip(i18n("Connect")); 0189 m_ConnectB->setIcon(QIcon::fromTheme("network-connect")); 0190 m_Grid->addWidget(m_ConnectB, m_Row, 8); 0191 connect(m_ConnectB, &QPushButton::clicked, m_Device.get(), &ISD::GenericDevice::Connect); 0192 0193 m_DisconnectB = new QPushButton; 0194 m_DisconnectB->setFixedSize(QSize(28, 28)); 0195 m_DisconnectB->setToolTip(i18n("Disconnect")); 0196 m_DisconnectB->setIcon(QIcon::fromTheme("network-disconnect")); 0197 m_Grid->addWidget(m_DisconnectB, m_Row, 9); 0198 connect(m_DisconnectB, &QPushButton::clicked, m_Device.get(), &ISD::GenericDevice::Disconnect); 0199 0200 setConnectionMode(static_cast<ConnectionMode>(connectionMode.findOnSwitchIndex())); 0201 0202 return true; 0203 } 0204 0205 ////////////////////////////////////////////////////////////////////////////////////////// 0206 /// 0207 ////////////////////////////////////////////////////////////////////////////////////////// 0208 void Device::syncGUI() 0209 { 0210 // Check if initGUI failed before? 0211 if (m_LED == nullptr) 0212 { 0213 // Try to initiGUI again 0214 initGUI(); 0215 0216 // If it failed again, return. 0217 if (m_LED == nullptr) 0218 return; 0219 } 0220 0221 m_LED->setColor(ColorCode.value(m_Device->getState("CONNECTION"))); 0222 0223 auto connectionMode = m_Device->getProperty("CONNECTION_MODE").getSwitch(); 0224 0225 if (connectionMode->findOnSwitchIndex() == CONNECTION_SERIAL) 0226 { 0227 m_ActiveConnectionMode = CONNECTION_SERIAL; 0228 m_SerialB->setStyleSheet(ACTIVE_STYLESHEET); 0229 m_NetworkB->setStyleSheet(QString()); 0230 0231 if (m_Ports) 0232 { 0233 auto port = m_Device->getProperty("DEVICE_PORT").getText(); 0234 m_Ports->setEditText(port->at(0)->getText()); 0235 } 0236 0237 if (m_BaudRates) 0238 { 0239 auto systemBauds = m_Device->getProperty("DEVICE_BAUD_RATE").getSwitch(); 0240 m_BaudRates->setCurrentIndex(systemBauds->findOnSwitchIndex()); 0241 } 0242 } 0243 else 0244 { 0245 m_ActiveConnectionMode = CONNECTION_NETWORK; 0246 m_SerialB->setStyleSheet(QString()); 0247 m_NetworkB->setStyleSheet(ACTIVE_STYLESHEET); 0248 0249 if (m_Device->getProperty("DEVICE_ADDRESS").isValid()) 0250 { 0251 auto server = m_Device->getProperty("DEVICE_ADDRESS").getText(); 0252 m_HostName->setText(server->at(0)->getText()); 0253 m_HostPort->setText(server->at(1)->getText()); 0254 } 0255 0256 if (m_Device->getProperty("CONNECTION_TYPE").isValid()) 0257 { 0258 auto connectionType = m_Device->getBaseDevice().getSwitch("CONNECTION_TYPE").getSwitch(); 0259 m_HostProtocolTCP->setStyleSheet(connectionType->findOnSwitchIndex() == 0 ? ACTIVE_STYLESHEET : QString()); 0260 m_HostProtocolUDP->setStyleSheet(connectionType->findOnSwitchIndex() == 1 ? ACTIVE_STYLESHEET : QString()); 0261 } 0262 } 0263 } 0264 0265 ////////////////////////////////////////////////////////////////////////////////////////// 0266 /// 0267 ////////////////////////////////////////////////////////////////////////////////////////// 0268 uint8_t Device::systemPortCount() const 0269 { 0270 if (m_Ports) 0271 return m_Ports->count(); 0272 else 0273 return 0; 0274 } 0275 0276 ////////////////////////////////////////////////////////////////////////////////////////// 0277 /// 0278 ////////////////////////////////////////////////////////////////////////////////////////// 0279 void Device::setConnectionMode(ConnectionMode mode) 0280 { 0281 m_ActiveConnectionMode = mode; 0282 0283 if (mode == CONNECTION_SERIAL) 0284 { 0285 if (m_HostName) 0286 { 0287 m_Grid->removeWidget(m_HostName); 0288 m_HostName->setParent(nullptr); 0289 m_Grid->removeWidget(m_HostPort); 0290 m_HostPort->setParent(nullptr); 0291 m_Grid->removeWidget(m_HostProtocolTCP); 0292 m_HostProtocolTCP->setParent(nullptr); 0293 m_Grid->removeWidget(m_HostProtocolUDP); 0294 m_HostProtocolUDP->setParent(nullptr); 0295 } 0296 0297 m_Grid->addWidget(m_Ports, m_Row, 4, 1, 2); 0298 m_Grid->addWidget(m_BaudRates, m_Row, 6, 1, 2); 0299 m_Grid->setColumnStretch(4, 2); 0300 } 0301 else if (mode == CONNECTION_NETWORK) 0302 { 0303 if (m_Ports) 0304 { 0305 m_Grid->removeWidget(m_Ports); 0306 m_Ports->setParent(nullptr); 0307 m_Grid->removeWidget(m_BaudRates); 0308 m_BaudRates->setParent(nullptr); 0309 } 0310 0311 m_Grid->addWidget(m_HostName, m_Row, 4); 0312 m_Grid->addWidget(m_HostPort, m_Row, 5); 0313 m_Grid->addWidget(m_HostProtocolTCP, m_Row, 6); 0314 m_Grid->addWidget(m_HostProtocolUDP, m_Row, 7); 0315 m_Grid->setColumnStretch(4, 2); 0316 } 0317 } 0318 0319 ////////////////////////////////////////////////////////////////////////////////////////// 0320 /// 0321 ////////////////////////////////////////////////////////////////////////////////////////// 0322 void Device::updateProperty(INDI::Property prop) 0323 { 0324 if (prop.isNameMatch("CONNECTION_MODE")) 0325 { 0326 auto svp = prop.getSwitch(); 0327 setConnectionMode(static_cast<ConnectionMode>(svp->findOnSwitchIndex())); 0328 syncGUI(); 0329 } 0330 else if (prop.isNameMatch("CONNECTION_TYPE") || prop.isNameMatch("SYSTEM_PORTS") || 0331 prop.isNameMatch("DEVICE_BAUD_RATE") || prop.isNameMatch("DEVICE_PORT")) 0332 syncGUI(); 0333 else if (prop.isNameMatch("CONNECTION")) 0334 { 0335 if (m_Device->isConnected()) 0336 { 0337 m_ConnectB->setStyleSheet(ACTIVE_STYLESHEET); 0338 m_DisconnectB->setStyleSheet(QString()); 0339 } 0340 else 0341 { 0342 m_ConnectB->setStyleSheet(QString()); 0343 m_DisconnectB->setStyleSheet(ACTIVE_STYLESHEET); 0344 } 0345 } 0346 } 0347 0348 ////////////////////////////////////////////////////////////////////////////////////////// 0349 /// 0350 ////////////////////////////////////////////////////////////////////////////////////////// 0351 void Device::setConnected() 0352 { 0353 syncGUI(); 0354 } 0355 0356 ////////////////////////////////////////////////////////////////////////////////////////// 0357 /// 0358 ////////////////////////////////////////////////////////////////////////////////////////// 0359 void Device::setDisconnected() 0360 { 0361 syncGUI(); 0362 } 0363 0364 ////////////////////////////////////////////////////////////////////////////////////////// 0365 /// 0366 ////////////////////////////////////////////////////////////////////////////////////////// 0367 Dialog::Dialog(QWidget *parent) : QDialog(parent) 0368 { 0369 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0370 QDialogButtonBox *buttonBox = new QDialogButtonBox(this); 0371 0372 QPushButton *connectAll = new QPushButton(i18n("Connect All"), this); 0373 buttonBox->addButton(connectAll, QDialogButtonBox::AcceptRole); 0374 buttonBox->addButton(QDialogButtonBox::Close); 0375 0376 connect(buttonBox, &QDialogButtonBox::accepted, this, &Dialog::accept); 0377 connect(buttonBox, &QDialogButtonBox::rejected, this, &Dialog::reject); 0378 0379 m_Layout = new QGridLayout; 0380 0381 mainLayout->addLayout(m_Layout); 0382 mainLayout->addStretch(); 0383 mainLayout->addWidget(buttonBox); 0384 0385 setWindowTitle(i18nc("@title:window", "Port Selector")); 0386 #ifdef Q_OS_OSX 0387 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0388 #endif 0389 } 0390 0391 ////////////////////////////////////////////////////////////////////////////////////////// 0392 /// 0393 ////////////////////////////////////////////////////////////////////////////////////////// 0394 void Dialog::addDevice(const QSharedPointer<ISD::GenericDevice> &device) 0395 { 0396 auto pos = std::find_if(m_Devices.begin(), m_Devices.end(), [device](std::unique_ptr<Device> &oneDevice) 0397 { 0398 return oneDevice->name() == device->getDeviceName(); 0399 }); 0400 0401 // New 0402 if (pos == m_Devices.end()) 0403 { 0404 std::unique_ptr<Device> dev(new Device(device, m_Layout, m_Devices.size())); 0405 m_Devices.push_back(std::move(dev)); 0406 } 0407 // Existing device 0408 else 0409 { 0410 (*pos)->syncGUI(); 0411 } 0412 0413 } 0414 0415 ////////////////////////////////////////////////////////////////////////////////////////// 0416 /// 0417 ////////////////////////////////////////////////////////////////////////////////////////// 0418 void Dialog::removeDevice(const QString &name) 0419 { 0420 m_Devices.erase(std::remove_if(m_Devices.begin(), m_Devices.end(), [name](const auto & oneDevice) 0421 { 0422 return oneDevice->name() == name; 0423 }), m_Devices.end()); 0424 } 0425 0426 ////////////////////////////////////////////////////////////////////////////////////////// 0427 /// Should Port Selector be shown automatically? 0428 /// If we only have ONE device with ONE port, then no need to bother the user 0429 /// with useless selection. 0430 ////////////////////////////////////////////////////////////////////////////////////////// 0431 bool Dialog::shouldShow() const 0432 { 0433 if (m_Devices.empty()) 0434 return false; 0435 0436 uint32_t systemPortCount = 0; 0437 uint32_t serialDevices = 0; 0438 uint32_t networkDevices = 0; 0439 0440 for (const auto &oneDevice : m_Devices) 0441 { 0442 if (oneDevice->systemPortCount() > 0) 0443 systemPortCount = oneDevice->systemPortCount(); 0444 0445 if (oneDevice->activeConnectionMode() == CONNECTION_SERIAL) 0446 serialDevices++; 0447 else if (oneDevice->activeConnectionMode() == CONNECTION_NETWORK) 0448 networkDevices++; 0449 } 0450 0451 // If we just have a single serial device with single port, no need to toggle port selector. 0452 // If have have one or more network devices, we must show the dialog for the first time. 0453 if (networkDevices == 0 && serialDevices <= 1 && systemPortCount <= 1) 0454 return false; 0455 0456 // Otherwise, show dialog 0457 return true; 0458 } 0459 }