Index: .gitignore =================================================================== diff -u --- .gitignore (revision 0) +++ .gitignore (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -0,0 +1,2 @@ + +.vscode/ Index: leahi.pro =================================================================== diff -u -rf20fa155dca799c332189f8d3932e35b5c4ef84a -r59135abca5be03c9156d3847ccc1befa7132309e --- leahi.pro (.../leahi.pro) (revision f20fa155dca799c332189f8d3932e35b5c4ef84a) +++ leahi.pro (.../leahi.pro) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -148,6 +148,7 @@ \ # ---------- Models - TD - Data //// ----- @LEAHIZED sources/model/td/data/MTDOpModeData.h \ \ # ---------- Views - TD - Data - States //// ----- @LEAHIZED + sources/view/settings/VNetworkModel.h \ sources/view/td/data/VTDOpModeData.h \ \ # ---------- Models - confirm sources/model/confirm/MDuetConfirmHDi.h \ @@ -409,7 +410,8 @@ sources/utility/format.h \ sources/utility/types.h \ sources/utility/encryption.h \ - sources/utility/qrcodegen.h + sources/utility/qrcodegen.h \ + sources/wifi/WifiInterface.h SOURCES += \ \ # common @@ -583,6 +585,7 @@ sources/view/VGeneralEvent.cpp \ sources/device/DeviceView.cpp \ \ # ---------- Views - TD - States Data + sources/view/settings/VNetworkModel.cpp \ sources/view/td/data/VTDOpModeData.cpp \ \ # ---------- Views - CONFIRM sources/view/confirm/VConfirm.cpp \ @@ -692,7 +695,8 @@ sources/utility/format.cpp \ sources/utility/types.cpp \ sources/utility/encryption.cpp \ - sources/utility/qrcodegen.cpp + sources/utility/qrcodegen.cpp \ + sources/wifi/WifiInterface.cpp RESOURCES += \ leahi.qrc @@ -705,7 +709,8 @@ VER_REVIS_DEV=\\\"$$system( date "+%m%d%H%M" )\\\" \ VER_APPLY=\\\"$$system( touch -m main.cpp )\\\" \ VER_BRANCH=\\\"$$system( git rev-parse --abbrev-ref HEAD )\\\" \ - QML_PATH=\\\"$$PWD/sources/gui\\\" + CURRENT_DIR=\\\"$$PWD\\\" \ + QML_PATH=\\\"${CURRENT_DIR}/sources/gui\\\" # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = Index: main.cpp =================================================================== diff -u -rf20fa155dca799c332189f8d3932e35b5c4ef84a -r59135abca5be03c9156d3847ccc1befa7132309e --- main.cpp (.../main.cpp) (revision f20fa155dca799c332189f8d3932e35b5c4ef84a) +++ main.cpp (.../main.cpp) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -50,6 +50,7 @@ #include "GuiController.h" #include "Logger.h" #include "DeviceController.h" +#include "WifiInterface.h" #include "BluetoothInterface.h" #include "CloudSyncController.h" //AMIR @@ -638,6 +639,9 @@ //! - Initializing Main Timer _MainTimer.init(); + //! - Initializing WiFi Interface + _WifiInterface.init(Threads::_Wifi_Thread); + //! - Initializing Bluetooth Interface _BluetoothInterface.init(Threads::_Bluetooth_Thread); Index: sources/gui/GuiGlobals.cpp =================================================================== diff -u -rf20fa155dca799c332189f8d3932e35b5c4ef84a -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision f20fa155dca799c332189f8d3932e35b5c4ef84a) +++ sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -45,6 +45,7 @@ #include "VAdjustmentServiceMode.h" #include "VAdjustmentServiceDates.h" #include "VDateTime.h" +#include "VNetworkModel.h" #include "VAdjustmentAlarmVolume.h" #include "VBluetooth.h" #include "VDuetRoWaterDG.h" Index: sources/gui/qml/main.qml =================================================================== diff -u -r00a8e3ead69ac7cbf292d4ee2d76b867769909c4 -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/gui/qml/main.qml (.../main.qml) (revision 00a8e3ead69ac7cbf292d4ee2d76b867769909c4) +++ sources/gui/qml/main.qml (.../main.qml) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -49,6 +49,7 @@ import VAdjustmentServiceMode 0.1 import VAdjustmentServiceDates 0.1 import VDateTime 0.1 +import VNetworkModel 0.1 import VAdjustmentAlarmVolume 0.1 import VBluetooth 0.1 import VDuetRoWaterDG 0.1 @@ -144,6 +145,7 @@ VDGPOSTData { id: vDGPOSTData } VDevice { id: vDevice } + VNetworkModel { id: vNetwork } VSettings { id: vSettings } VLocalization { id: vLocalization } VAdjustmentVersions { id: vAdjustmentVersions } Index: sources/gui/qml/pages/settings/SettingsWiFi.qml =================================================================== diff -u -ra5760947d3ed0d2748ba023a1c25e3c6aa0b1de1 -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/gui/qml/pages/settings/SettingsWiFi.qml (.../SettingsWiFi.qml) (revision a5760947d3ed0d2748ba023a1c25e3c6aa0b1de1) +++ sources/gui/qml/pages/settings/SettingsWiFi.qml (.../SettingsWiFi.qml) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -6,8 +6,8 @@ * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file SettingsWiFi.qml - * \author (last) Behrouz NematiPour - * \date (last) 18-Jul-2023 + * \author (last) Nico Ramirez + * \date (last) 13-June-2025 * \author (original) Behrouz NematiPour * \date (original) 06-May-2021 * @@ -53,14 +53,13 @@ confirmEnabled : isValid confirmVisible : false // since the static setting is done one by one seems confirm is not needed for now. firstFocusInput : _ipAddress - notificationText: vDevice.status + notificationText: vNetwork.status onConfirmClicked: { - // ===================== Device Controller: FIXME - // vNetwork.doConfirm( - // _ipAddress .textInput.text , - // _gateway .textInput.text , - // _subnetmask .textInput.text , - // _dns .textInput.text ) + vNetwork.doConfirm( + _ipAddress .textInput.text , + _gateway .textInput.text , + _subnetmask .textInput.text , + _dns .textInput.text ) } Column { id : _ipColumn @@ -71,56 +70,52 @@ TextEntry { id : _ipAddress nextInput : _gateway // helps user to tap on enter to go to the next entry, keyboard visibility is handled by TextEntry. - textInput.text : "ipAddress" //TODO: Wifi + textInput.text : vNetwork.ipAddress label.text : qsTr("IP Address") textInput.width : entryWidth label.width : labelWidth hasCursor : false // set false for now to disable the static IP entry for phase 1 validator : ipValidator onEnterPressed : { - // ===================== Device Controller: FIXME - // vNetwork.doSetIPAddress(textInput.text) + vNetwork.doSetIPAddress(textInput.text) } } TextEntry { id : _gateway nextInput : _subnetmask // helps user to tap on enter to go to the next entry, keyboard visibility is handled by TextEntry. - textInput.text : "gateway" //TODO: Wifi + textInput.text : vNetwork.gateway textInput.width : entryWidth label.width : labelWidth label.text : qsTr("Gateway") hasCursor : false // set false for now to disable the static IP entry for phase 1 validator : ipValidator onEnterPressed : { - // ===================== Device Controller: FIXME - // vNetwork.doSetGateway(textInput.text) + vNetwork.doSetGateway(textInput.text) } } TextEntry { id : _subnetmask nextInput : _dns // helps user to tap on enter to go to the next entry, keyboard visibility is handled by TextEntry. - textInput.text : "subnetMask" //TODO: Wifi + textInput.text : vNetwork.subnetMask textInput.width : entryWidth label.width : labelWidth label.text : qsTr("Subnet Mask") hasCursor : false // set false for now to disable the static IP entry for phase 1 validator : ipValidator onEnterPressed : { - // ===================== Device Controller: FIXME - // vNetwork.doSetSubnetMask(textInput.text) + vNetwork.doSetSubnetMask(textInput.text) } } TextEntry { id : _dns - textInput.text : "dns" //TODO: Wifi + textInput.text : vNetwork.dns textInput.width : entryWidth label.width : labelWidth label.text : qsTr("DNS") hasCursor : false // set false for now to disable the static IP entry for phase 1 validator : ipValidator onEnterPressed : { - // ===================== Device Controller: FIXME - // vNetwork.doSetDNS(textInput.text) + vNetwork.doSetDNS(textInput.text) } } } @@ -155,15 +150,18 @@ text.text : qsTr("SCAN") width : 350 isDefault : true - enabled : vDevice.wifiListEnabled - onClicked : vDevice.wifiList = "" + // enabled : vDevice.wifiListEnabled + // onClicked : vDevice.wifiList = "" + enabled : !vNetwork.scanInProgress + onClicked : vNetwork.doScan() } ScrollBar { flickable : _networkList anchors.fill: _networkList } ListView { id : _networkList - model : vDevice + // model : vDevice + model : vNetwork clip : true spacing : 7 y : _root.topMargin @@ -236,19 +234,17 @@ property string macAddress : "" property string separator : "\n\n" readonly property string spaceSeparator : " " - notificationText : vDevice.status + notificationText : vNetwork.status message : qsTr("Do you want to disconnect from `%1`?").arg(ssid) title : isPassword ? qsTr("Join") + separator + ssid + spaceSeparator + qsTr("Password") : qsTr("Disconnect") + separator + ssid onConfirmClicked : { if ( isPassword ) { if( passwordCurrent.length > 0 ) { - // ===================== Device Controller: FIXME - // vNetwork.doJoinNetwork ( macAddress, passwordCurrent ) + vNetwork.doJoinNetwork ( macAddress, passwordCurrent ) pop() } } else { - // ===================== Device Controller: FIXME - // vNetwork.doDisconnectNetwork( macAddress ) + vNetwork.doDisconnectNetwork( macAddress ) pop() } } Index: sources/model/hd/alarm/MAlarmMapping.cpp =================================================================== diff -u -r2ad0fc5c1215088ee0e4ea7f9b2bc367c4ed2fd9 -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/model/hd/alarm/MAlarmMapping.cpp (.../MAlarmMapping.cpp) (revision 2ad0fc5c1215088ee0e4ea7f9b2bc367c4ed2fd9) +++ sources/model/hd/alarm/MAlarmMapping.cpp (.../MAlarmMapping.cpp) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -164,7 +164,8 @@ /*0128*/case GuiAlarmID::ALARM_ID_UI_RESERVED_128 : { result = QObject::tr("UI Reserved failure." ); break; } /* 128*/ /*0129*/case GuiAlarmID::ALARM_ID_UI_RESERVED_129 : { result = QObject::tr("UI Reserved failure." ); break; } /* 129*/ /*0130*/case GuiAlarmID::ALARM_ID_UI_RESERVED_130 : { result = QObject::tr("UI Reserved failure." ); break; } /* 130*/ -/*0131*/case GuiAlarmID::NUM_OF_ALARM_IDS : { result = QObject::tr("Number of alarms." ); break; } /* 131*/ +/*0131*/case GuiAlarmID::ALARM_ID_BATTERY_LOW : { result = QObject::tr("TD Battery Low warning." ); break; } /* 131*/ +/*0132*/case GuiAlarmID::NUM_OF_ALARM_IDS : { result = QObject::tr("Number of alarms." ); break; } /* 132*/ default: result = QObject::tr("ALARM_ID_UNDEFINED [%1]").arg(int(vAlarmID)); Index: sources/storage/StorageGlobals.cpp =================================================================== diff -u -rf2e4eba6e85c5d36537be782926f23cc9dc01037 -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision f2e4eba6e85c5d36537be782926f23cc9dc01037) +++ sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -159,7 +159,8 @@ // therefore in manufacturing or update mode we are still running scripts as root. return QCoreApplication::applicationDirPath() + ( ( gEnableManufacturing || gEnableUpdating )? "/scripts/" : "/scripts/"); #else - return "/home/denali/Projects/application/scripts/"; + return QStringLiteral("%1%2") .arg(CURRENT_DIR) + .arg("/scripts/scripts/");; #endif } Index: sources/view/VView.h =================================================================== diff -u -rd862dfcd402206e33b314c458e41c13b684a4565 -r59135abca5be03c9156d3847ccc1befa7132309e --- sources/view/VView.h (.../VView.h) (revision d862dfcd402206e33b314c458e41c13b684a4565) +++ sources/view/VView.h (.../VView.h) (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -135,6 +135,7 @@ REGISTER_TYPE( VAdjustmentServiceMode ) \ REGISTER_TYPE( VAdjustmentServiceDates ) \ REGISTER_TYPE( VDateTime ) \ + REGISTER_TYPE( VNetworkModel ) \ REGISTER_TYPE( VAdjustmentAlarmVolume ) \ REGISTER_TYPE( VBluetooth ) \ REGISTER_TYPE( VDuetRoWaterDG ) \ Index: sources/view/settings/VNetworkModel.cpp =================================================================== diff -u --- sources/view/settings/VNetworkModel.cpp (revision 0) +++ sources/view/settings/VNetworkModel.cpp (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -0,0 +1,480 @@ +/*! + * + * Copyright (c) 2021-2024 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file VNetworkModel.cpp + * \author (last) Behrouz NematiPour + * \date (last) 08-Dec-2023 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ + +// Qt +#include + +// Project +#include "VNetworkModel.h" +#include "WifiInterface.h" +#include "Logger.h" + +using namespace View; + +VNetworkModel::VNetworkModel(QAbstractListModel *parent) : QAbstractListModel(parent) { + startTimer(_interval); + initConnections(); + _devWlan = WifiInterface::iface(); +} + +/*! + * \brief VNetworkModel::initConnections + * Makes the necessary connections. Called inside VIEW_DEF_CLASS + */ +void VNetworkModel::initConnections() +{ + // incoming + connect(&_WifiInterface, SIGNAL(didAddNetwork (const WifiNetworkData &)), + this, SLOT( onAddNetwork (const WifiNetworkData &))); + + connect(&_WifiInterface, SIGNAL(didScanStatusChanged(bool)), + this, SLOT( onScanStatusChanged(bool))); + + connect(&_WifiInterface, SIGNAL( didConnectToNetwork(const WifiNetworkData &)), + this, SLOT( onConnectToNetwork(const WifiNetworkData &))); + + connect(&_WifiInterface, SIGNAL(didDisconnectNetwork(const WifiNetworkData &)), + this, SLOT( onDisconnectNetwork(const WifiNetworkData &))); + + connect(&_WifiInterface, SIGNAL( didError (const QString &)), + this, SLOT(onStatusChanged (const QString &))); + + connect(&_WifiInterface, SIGNAL(didStatusChanged (const QString &)), + this, SLOT( onStatusChanged (const QString &))); + + // incoming - static IP address assignment + connect(&_WifiInterface, SIGNAL(didSetStaticIPAddress ()), + this, SLOT( onSetStaticIPAddressSuccess())); + + connect(&_WifiInterface, SIGNAL(didSetGateway ()), + this, SLOT( onSetGatewaySuccess())); + + connect(&_WifiInterface, SIGNAL(didSetSubnetMask ()), + this, SLOT( onSetSubnetMaskSuccess())); + + connect(&_WifiInterface, SIGNAL(didSetDNS ()), + this, SLOT( onSetDNSSuccess())); + + // outgoing + connect(this, SIGNAL(didScan()), + &_WifiInterface, SLOT( doScan())); + + connect(this, SIGNAL(didJoinNetwork (const WifiNetworkData &, const QString &)), + &_WifiInterface, SLOT( doJoinNetwork (const WifiNetworkData &, const QString &))); + + connect(this, SIGNAL(didDisconnectNetwork(const WifiNetworkData &)), + &_WifiInterface, SLOT( doDisconnectNetwork(const WifiNetworkData &))); + + connect(this, SIGNAL(didRequestIPSettings()), + &_WifiInterface, SLOT( doRequestIPSettings())); + + // outgoing - static IP address assignment + connect(this, SIGNAL(didRequestSetIPAddress (const QString &)), + &_WifiInterface, SLOT( doRequestSetIPAddress (const QString &))); + + connect(this, SIGNAL(didRequestSetGateway (const QString &)), + &_WifiInterface, SLOT( doRequestSetGateway (const QString &))); + + connect(this, SIGNAL(didRequestSetSubnetMask (const QString &)), + &_WifiInterface, SLOT( doRequestSetSubnetMask (const QString &))); + + connect(this, SIGNAL(didRequestSetDNS (const QString &)), + &_WifiInterface, SLOT( doRequestSetDNS (const QString &))); +} + +/*! + * \brief VNetworkModel::addNetwork + * Adds a network to the network model + * \param network (Network) - the network to add to the model + */ +void VNetworkModel::addNetwork(const WifiNetworkData &vNetwork) +{ + LOG_DEBUG(QString("Adding network with SSID: %1").arg(vNetwork.ssid())); + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + _networks << vNetwork; + endInsertRows(); + // For testing w/out a display: in main.qml call doScan() using Component.onCompleted, + // then call doJoinNetwork here when the desired network's mac address has been detected +} + +/*! + * \brief VNetworkModel::removeRows + * Removes all rows from the model + */ +void VNetworkModel::removeAllRows() +{ + beginRemoveRows(QModelIndex(), 0, rowCount()); + _networks.clear(); + endRemoveRows(); +} + +/*! + * \brief VNetworkModel::doInit + * \details Initializes the view or what needs to be done when for example getting into the WiFi setting screen (do scan for now). + */ +void VNetworkModel::doInit() +{ + doScan(); +} + +/*! + * \brief VNetworkModel::rowCount + * Gets the number of networks + * \param parent - (QModelIndex) the parent QModelIndex + * \return (int) - the number of networks + */ +int VNetworkModel::rowCount(const QModelIndex & parent) const +{ + Q_UNUSED(parent); + return _networks.count(); +} + +/*! + * \brief VNetworkModel::data + * Returns the network properties at the specified index + * \param index (QModelIndex) contains the row of data to lookup + * \param role - (int) the property index to return. See NetworkDataRole + * \return (QVariant) - the value for the specified network property + */ +QVariant VNetworkModel::data(const QModelIndex & index, int role) const +{ + if (index.row() < 0 || index.row() >= _networks.count()) + return QVariant(); + + const WifiNetworkData &network = _networks[index.row()]; + + switch (role) + { + case MacAddressRole: + return network.macAddress(); + case SSIDRole: + return network.ssid(); + case SecurityTypesRole: + return MWifiNetwork::securityTypesToStringList(network.securityTypes()).join("/"); + case StatusRole: + return network.status(); + case SignalLevelRole: + return network.signalLevel(); + case NetworkSupportedRole: { + bool isSecurityTypeSupported = (!network.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WEP) && + !network.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA_TKIP) && + !network.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA_TKIP_AES) && + !network.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::UNSUPPORTED)); + bool isPasswordProtected = network.requirePassword(); + return isSecurityTypeSupported && isPasswordProtected; + } + } + + return QVariant(); +} + +/*! + * \brief VNetworkModel::roleNames + * Translates how to access specific properties of the data for QML from the NetworkDataRole enum + * \return (QHash) - maps enums to property names + */ +QHash VNetworkModel::roleNames() const { + QHash roles; + roles[MacAddressRole] = "macAddress"; + roles[SSIDRole] = "ssid"; + roles[SecurityTypesRole] = "securityTypes"; + roles[StatusRole] = "status"; + roles[SignalLevelRole] = "signalLevel"; + roles[NetworkSupportedRole] = "isNetworkSupported"; + return roles; +} + +/*! + * \brief VNetworkModel::doScan + * Handles when a user clicks the Scan button + */ +void VNetworkModel::doScan() { + LOG_DEBUG("Clearing networks before re-scanning."); + removeAllRows(); + status(tr("Scanning...")); + emit didScan(); +} + +/*! + * \brief VNetworkModel::onAddNetwork + * Slot that receives a request to add a new network + * \param vNetwork - (Network) the new network to add + */ +void VNetworkModel::onAddNetwork(const WifiNetworkData &vNetwork) +{ + if (!_networks.contains(vNetwork)) + { + LOG_DEBUG(QString("Adding network with SSID: %1.").arg(vNetwork.ssid())); + addNetwork(vNetwork); + } + else + { + LOG_DEBUG(QString("Skipping adding network with SSID: %1. It already exists").arg(vNetwork.ssid())); + } +} + +/*! + * \brief VNetworkModel::onScanStatusChanged + * Called when the scan status changes. Updates QML with the current state + * \param vScanning - (bool) true if scanning, false otherwise + */ +void VNetworkModel::onScanStatusChanged(const bool &vScanning) +{ + if (!vScanning) + status(tr("Scan Finished")); + + scanInProgress(vScanning); +} + +/*! + * \brief VNetworkModel::doCheckIfConnected + * Checks whether the network specified by the mac address is connected or not + * \param vMacAddress - (QString) the unique mac address for the network + */ +bool VNetworkModel::doCheckIfConnected(const QString &vMacAddress) +{ + if (vMacAddress == macAddress()) + return true; + return false; +} + +/*! + * \brief VNetworkModel::doJoinNetwork + * Handles request from qml to join a network + * \param vMacAddress - (QString) the mac address of the network to join + * \param vPassword - (QString) the password for the network provided by the user + */ +void VNetworkModel::doJoinNetwork(const QString &vMacAddress, const QString &vPassword) +{ + clearSelectedNetwork(); + for (const WifiNetworkData &network : _networks) + { + if (network.macAddress() == vMacAddress) + { + status(tr("Connecting to %1...").arg(network.ssid())); + emit didJoinNetwork(network, vPassword); + return; + } + } +} + +/*! + * \brief VNetworkModel::doDisconnectNetwork + * Handles request from qml to disconnect from a network + * \param vMacAddress - (QString) the mac address of the network to disconnect from + */ +void VNetworkModel::doDisconnectNetwork(const QString &vMacAddress) +{ + for (const WifiNetworkData &network : _networks) + { + if (network.macAddress() == vMacAddress) + { + status(tr("Disconnecting from %1...").arg(network.ssid())); + emit didDisconnectNetwork(network); + return; + } + } +} + +/*! + * \brief VNetworkModel::onConnectedToNetwork + * Called when we have connected to a network. + * \param vNetwork - (Network) the network we have connected to + */ +void VNetworkModel::onConnectToNetwork(const WifiNetworkData &vNetwork) +{ + LOG_DEBUG(QString("Connected to %1.").arg(vNetwork.ssid())); + ipAddress(vNetwork.mIPSettings.mIPAddress.trimmed()); + gateway(vNetwork.mIPSettings.mGateway.trimmed()); + subnetMask(vNetwork.mIPSettings.mSubnetMask.trimmed()); + dns(vNetwork.mIPSettings.mDNS.trimmed()); + ssid(vNetwork.ssid().trimmed()); + macAddress(vNetwork.macAddress().trimmed()); + status(tr("Connected to %1.").arg(vNetwork.ssid()).trimmed()); +} + +/*! + * \brief VNetworkModel::onDisconnectedNetwork + * Called when we have disconnected from a network. + * \param vNetwork - (Network) the network we have disconnected from + */ +void VNetworkModel::onDisconnectNetwork(const WifiNetworkData &vNetwork) +{ + LOG_DEBUG(QString("Disconnected from %1.").arg(vNetwork.ssid())); + clearSelectedNetwork(); + status(tr("Disconnected from %1.").arg(vNetwork.ssid())); +} + +/*! + * \brief VNetworkModel::doRequestIPSettings + * Called when QML requests the IP settings + */ +void VNetworkModel::doRequestIPSettings() +{ + emit didRequestIPSettings(); +} + +/*! + * \brief VNetworkModel::onStatusChanged + * Called when the wifi interface status changes + * \param vMessage - (QString) the error message + */ +void VNetworkModel::onStatusChanged(const QString &vMessage) +{ + status(vMessage); +} + +/*! + * \brief VNetworkModel::doSetIPAddress + * \param vIPAddress (QString) the ip address + */ +void VNetworkModel::doSetIPAddress(const QString &vIPAddress) +{ + emit didRequestSetIPAddress(vIPAddress.trimmed()); +} + +/*! + * \brief VNetworkModel::doSetGateway + * \param vGateway (QString) the gateway + */ +void VNetworkModel::doSetGateway(const QString &vGateway) +{ + emit didRequestSetGateway(vGateway.trimmed()); +} + +/*! + * \brief VNetworkModel::doSetSubnetMask + * \param vSubnetMask (QString) the subnet mask + */ +void VNetworkModel::doSetSubnetMask(const QString &vSubnetMask) +{ + emit didRequestSetSubnetMask(vSubnetMask.trimmed()); +} + +/*! + * \brief VNetworkModel::doSetDNS + * \param vDNS (QString) the DNS + */ +void VNetworkModel::doSetDNS(const QString &vDNS) +{ + emit didRequestSetDNS(vDNS.trimmed()); +} + +/*! + * \brief VNetworkModel::timerEvent + * \details The overloaded parent QObject timer event handler function + */ +void VNetworkModel::timerEvent(QTimerEvent *) +{ + checkEthernet(); + checkWireless(); +} + +/*! + * \brief VNetworkModel::clearWifiSettings + * Clears the selected network settings + */ +void VNetworkModel::clearSelectedNetwork() +{ + ipAddress(""); + gateway(""); + subnetMask(""); + dns(""); + ssid(""); + macAddress(""); +} + +/*! + * \brief VNetworkModel::checkEthernet + * \details check for Ethernet using VNetworkModel::checkDevNet + */ +void VNetworkModel::checkEthernet() +{ + const QNetworkInterface dev = QNetworkInterface::interfaceFromName(_devEth); + QString devIP = tr("No Ethernet"); + + // The QNetworkInterface object can be valid, but not running+up. + // Check the object's flags to determine running+up state. + bool eth0IsUp = dev.flags().testFlag(QNetworkInterface::IsUp ); + bool eth0IsRunning = dev.flags().testFlag(QNetworkInterface::IsRunning); + + if ( dev.isValid() && (eth0IsUp && eth0IsRunning)) { + auto addresses = dev.addressEntries(); + if ( addresses.count() ) { + devIP = addresses.first().ip().toString(); + } + } + _ethernetIP = devIP; + emit ethernetIPChanged( _ethernetIP ); +} + +/*! + * \brief VNetworkModel::checkWireless + * \details check for Ethernet using VNetworkModel::checkDevNet + */ +void VNetworkModel::checkWireless() +{ + const QNetworkInterface dev = QNetworkInterface::interfaceFromName(_devWlan); + QString devIP = tr("No WiFi"); + if ( dev.isValid() ) { + auto addresses = dev.addressEntries(); + if ( addresses.count() ) { + if ( dev.flags().testFlag(QNetworkInterface::IsUp) && dev.flags().testFlag(QNetworkInterface::IsRunning) ) { + devIP = addresses.first().ip().toString(); + } + else { + devIP = tr("WiFi Disconnect"); + } + } + } + _wirelessIP = devIP; + emit wirelessIPChanged( _wirelessIP ); +} + +/*! + * \brief VNetworkModel::onSetIPAddressSuccess + * Called when the IP address has been set + */ +void VNetworkModel::onSetStaticIPAddressSuccess() +{ + status(tr("Successfully set the IP address.")); +} + +/*! + * \brief VNetworkModel::onSetGatewaySuccess + * Called when the gateway has been set + */ +void VNetworkModel::onSetGatewaySuccess() +{ + status(tr("Successfully set the gateway.")); +} + +/*! + * \brief VNetworkModel::onSetSubnetMaskSuccess + * Called when the subnet mask has been set + */ +void VNetworkModel::onSetSubnetMaskSuccess() +{ + status(tr("Successfully set the subnet mask.")); +} + +/*! + * \brief VNetworkModel::onSetDNSSuccess + * Called when the DNS has been set + */ +void VNetworkModel::onSetDNSSuccess() +{ + status(tr("Successfully set the DNS.")); +} Index: sources/view/settings/VNetworkModel.h =================================================================== diff -u --- sources/view/settings/VNetworkModel.h (revision 0) +++ sources/view/settings/VNetworkModel.h (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -0,0 +1,131 @@ +/*! + * + * Copyright (c) 2021-2024 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file VNetworkModel.h + * \author (last) Behrouz NematiPour + * \date (last) 08-Dec-2023 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ +#pragma once + +// Qt +#include +#include + +// Project +#include "main.h" +#include "VView.h" +#include "MWifiNetwork.h" + + +// forward declarations +class tst_views; + +namespace View { + +/*! + * \brief The VNetworkModel class + * Interface between QML and the networks + * \details Exposes networks to QML and provides an interface to connect and disconnect from those networks. + * References: https://doc.qt.io/qt-5.12/qtquick-modelviewsdata-cppmodels.html + * + */ +class VNetworkModel : public QAbstractListModel +{ + Q_OBJECT + + int _interval = 1000; + + const char *_devEth = "eth0" ; // default for the device + QString _devWlan = "wlan0"; // default for the device + + PROPERTY(bool , isIdle , true ) + PROPERTY(bool , scanInProgress , false) + PROPERTY(QString , status , "") + PROPERTY(QString , ipAddress , "") + PROPERTY(QString , gateway , "") + PROPERTY(QString , subnetMask , "") + PROPERTY(QString , dns , "") + PROPERTY(QString , ssid , "") + PROPERTY(QString , macAddress , "") + PROPERTY(QString , ethernetIP , "") + PROPERTY(QString , wirelessIP , "") + +public: + // Note: VIEW_DEC_CLASS(VNetworkModel) requires QObject as the parent, so it's necessary to define it here + // Otherwise a VIEW_DEC_CLASS macro could allow specifying the parent class with QObject as the default + explicit VNetworkModel(QAbstractListModel *parent = nullptr); + + enum NetworkDataRole { + MacAddressRole = Qt::UserRole + 1 , + SSIDRole , + SecurityTypesRole , + StatusRole , + SignalLevelRole , + NetworkSupportedRole , + }; + + void addNetwork (const WifiNetworkData &network); + int rowCount (const QModelIndex &parent = QModelIndex() ) const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole ) const override; + void removeAllRows(); +signals: + void didScan(); + void didJoinNetwork(const WifiNetworkData, const QString); + void didDisconnectNetwork(const WifiNetworkData); + void didRequestIPSettings(); + + // set IP addresses + void didRequestSetIPAddress(const QString); + void didRequestSetGateway(const QString); + void didRequestSetSubnetMask(const QString); + void didRequestSetDNS(const QString); + +public slots: + void doInit(); + void doScan(); + bool doCheckIfConnected(const QString &vMacAddress); + void doJoinNetwork(const QString &vMacAddress, const QString &vPassword); + void doDisconnectNetwork(const QString &vMacAddress); + void doRequestIPSettings(); + + // static IP address assignment + void doSetIPAddress(const QString &vIPAddress); + void doSetGateway(const QString &vGateway); + void doSetSubnetMask(const QString &vSubnetMask); + void doSetDNS(const QString &vDNS); + +protected: + void timerEvent(QTimerEvent *event) override; + QHash roleNames() const override; + +private: + void initConnections(); + void clearSelectedNetwork(); + + QString checkDevNet(const char *vDevNet, const QString &vMessage); + void checkEthernet(); + void checkWireless(); + + QList _networks; + +private slots: + void onAddNetwork(const WifiNetworkData &vNetwork); + void onScanStatusChanged(const bool &vScanning); + void onStatusChanged(const QString &vNewStatus); + void onConnectToNetwork(const WifiNetworkData &vNetwork); + void onDisconnectNetwork(const WifiNetworkData &vNetwork); + + // static IP address assignment confirmation + void onSetStaticIPAddressSuccess(); + void onSetGatewaySuccess(); + void onSetSubnetMaskSuccess(); + void onSetDNSSuccess(); +}; +} Index: sources/wifi/WifiInterface.cpp =================================================================== diff -u --- sources/wifi/WifiInterface.cpp (revision 0) +++ sources/wifi/WifiInterface.cpp (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -0,0 +1,1009 @@ +/*! + * + * Copyright (c) 2021-2025 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file WifiInterface.cpp + * \author (last) Nico Ramirez + * \date (last) 17-Jun-2025 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ +#include "WifiInterface.h" + +// Qt +#include +#include +#include +#include + +// Project + +#include "main.h" +#include "Logger.h" + +using namespace Storage; + +WifiInterface::WifiInterface(QObject *parent) : QObject(parent) +{ + _processScan .setParent(this); + _processDisconnectNetwork .setParent(this); + _processReadIPSettings .setParent(this); + _processReadGateway .setParent(this); + _processReadDNS .setParent(this); + _processGenerateWPASupplicant .setParent(this); + _processStartWPASupplicant .setParent(this); + _processRequestAutoAssignedIP .setParent(this); + _processSetStaticIPAddress .setParent(this); + _processSetStaticGateway .setParent(this); + _processSetStaticSubnetMask .setParent(this); + _processSetDNS .setParent(this); + _processReadNetworkInfo .setParent(this); +} + + +/*! + * \brief WifiInterface::hasConnectedToWifi + * Checks whether we have previously connected to a WiFi network + * \return (bool) true if we have, false otherwise + */ +bool WifiInterface::hasConnectedToWifi() +{ + if (!QFile(_wpaSupplicantConfPath).exists()) + return false; + + return true; +} + +/*! + * \brief WifiInterface::rejoinLastWifiNetwork + * Attempts to rejoin the network we last connected to + */ +void WifiInterface::rejoinLastWifiNetwork() +{ + if (_processStartWPASupplicant.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processStartWPASupplicant.objectName())); + return; + } + // TODO + // QString script; if ( ! checkScript(script, Wifi_Start_WPA_Supplicant) ) return; + // _processStartWPASupplicant.start(script, + // QStringList() << iface() + // << _wpaSupplicantConfPath); +} + +void WifiInterface::initConnections() +{ + connect(this, SIGNAL(didStatusChanged(const QString)), + this, SLOT( onStatusChanged(const QString))); + + connect(this, SIGNAL( didError(const QString)), + this, SLOT( onStatusChanged(const QString))); + + connect(&_processScan , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedScan(int, QProcess::ExitStatus))); + + connect(&_processReadIPSettings , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedReadIP(int, QProcess::ExitStatus))); + + connect(&_processReadGateway , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedReadGateway(int, QProcess::ExitStatus))); + + connect(&_processReadDNS , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedReadDNS(int, QProcess::ExitStatus))); + + connect(&_processGenerateWPASupplicant , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedGenerateWPASupplicant(int, QProcess::ExitStatus))); + + connect(&_processStartWPASupplicant , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedStartWPASupplicant(int, QProcess::ExitStatus))); + + connect(&_processRequestAutoAssignedIP , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedRequestAutoAssignedIP(int, QProcess::ExitStatus))); + + connect(&_processDisconnectNetwork , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedDisconnectNetwork(int, QProcess::ExitStatus))); + + connect(&_processSetStaticIPAddress , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedSetIPAddress(int, QProcess::ExitStatus))); + + connect(&_processReadNetworkInfo , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedReadNetworkInfo(int, QProcess::ExitStatus))); + + connect(&_processSetStaticGateway , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedSetGateway(int, QProcess::ExitStatus))); + + connect(&_processSetStaticSubnetMask , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedSetSubnetMask(int, QProcess::ExitStatus))); + + connect(&_processSetDNS , SIGNAL( finished(int, QProcess::ExitStatus)), + this , SLOT( onProcessFinishedSetDNS(int, QProcess::ExitStatus))); + + connect(this , SIGNAL( didRequestAutoAssignedIP()), + this , SLOT( onRequestAutoAssignedIP())); + +} + +/*! + * \brief WifiInterface::init + * \details Initialized the Class by calling the init() method first + * And initializes the thread vThread by calling initThread + * on success init(). + * \param vThread - the thread + * \return returns the return value of the init() method + */ +bool WifiInterface::init(QThread &vThread) +{ + if (!init()) return false; + initThread(vThread); + return true; +} + + +/*! + * \brief ApplicationController::initThread + * \details Moves this object into the thread vThread. + * And checks that this method is called from main thread. + * Also connects quitThread to application aboutToQuit. + * \param vThread - the thread + */ +void WifiInterface::initThread(QThread &vThread) +{ + // runs in main thread + Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); + _thread = &vThread; + _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); + connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); + _thread->start(); + moveToThread(_thread); +} + +/*! + * \brief MessageAcknowModel::quit + * \details quits the class + * Calls quitThread + */ +void WifiInterface::quit() +{ + // disabled coco begin validated: Application termination is not correctly done in coco!!! + // it has been tested and works perfectly fine in normal run. + quitThread(); +} +// disabled coco end + +/*! + * \brief MessageAcknowModel::quitThread + * \details Moves this object to main thread to be handled by QApplication + * And to be destroyed there. + */ +void WifiInterface::quitThread() +{ + // disabled coco begin validated: Application termination is not correctly done in coco!!! + // it has been tested and works perfectly fine in normal run. + + if (!_thread) return; + + // runs in thread + moveToThread(qApp->thread()); +} +// disabled coco end + + +/*! + * \brief WifiInterface::init + * \details Initializes the class by setting the connections + * \return true on first initialization, false if it has already been initialized + */ +bool WifiInterface::init() +{ + if (_init) return false; + _init = true; + + initConnections(); + LOG_DEBUG(tr("%1 Initialized").arg(metaObject()->className())); + return true; +} + +/*! + * \brief WifiInterface::ondoStart + * Starts the WifiInterface + */ +void WifiInterface::ondoStart() +{ + if (hasConnectedToWifi()) + rejoinLastWifiNetwork(); +} + +/*! + * \brief WifiInterface::doScan + * Scans for WiFi Access Points + */ +void WifiInterface::doScan() +{ + if (_scanRunning) { + LOG_DEBUG("WiFi network scan is already running."); + return; + } + if (_processScan.state() != QProcess::NotRunning) { + LOG_DEBUG("The Running WiFi Scan Ignored."); + return; + } + + QString script; if ( ! checkScript(script, Scripts_Wifi_Scan) ) return; + _scanRunning = true; + emit didScanStatusChanged(_scanRunning); + _processScan.start(script, QStringList() << iface()); + LOG_DEBUG("WiFi Scan Started"); +} + +/*! + * \brief WifiInterface::onScanFinished + * Called when the scan process has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedScan(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitCode) + Q_UNUSED(vExitStatus) + QString out = _processScan.readAllStandardOutput(); + QString err = _processScan.readAllStandardError(); + + qDebug() << "************* OUT: " << out; + qDebug() << "************* err: " << err; + + LOG_DEBUG(QString("stdout: %1").arg(out)); + LOG_DEBUG(QString("stderr: %1").arg(err)); + _scanRunning = false; + emit didScanStatusChanged(_scanRunning); + parseWifiScan(out); + LOG_DEBUG("WiFi Scan Finished"); +} + +/*! + * \brief getTextBetweenDelimiters + * Gets the text between two delimiters + * \param vText - (QString) the text to search + * \param vLeftDelim - (QString) the left delimiter + * \param vRightDelim - (QString) the right delimiter + * \return (QString) the text between the delimiters if found, "" otherwise + */ +QString WifiInterface::getTextBetweenDelimiters(const QString &vText, const QString &vLeftDelim, const QString &vRightDelim) +{ + QString result = ""; + QString rightHalf = ""; + QStringList tokenList = vText.split(vLeftDelim); + if (tokenList.length() > 1) + rightHalf = tokenList[1]; + + tokenList = rightHalf.split(vRightDelim); + if (tokenList.length() > 1) + result = tokenList[0]; + return result; +} + +/*! + * \brief Network::parseWifiScan + * Extract desired information from the WiFi scan output. Sorts by signal stength + * + * \param vOutput - (QString) output collected from QProcess execution + */ +QList WifiInterface::parseWifiScan(const QString &vOutput) +{ + QList networks; + QString outputNoReturns = vOutput; + outputNoReturns = outputNoReturns.replace("\r", ""); + QStringList temp = outputNoReturns.split("Cell"); + const QString searchTermSignalLevel = "Signal level="; + const QString searchTermMacAddress = "Address:"; + const QString searchTermSSID = "ESSID:"; + + for (const QString &cell : temp) + { + if (cell.contains(searchTermSSID) && cell.contains(searchTermMacAddress) && cell.contains(searchTermSignalLevel)) + { + QString ssid = getTextBetweenDelimiters(cell, searchTermSSID, "\n").replace("\"", "").trimmed(); + if (ssid == "") continue; + QString macAddress = getTextBetweenDelimiters(cell, searchTermMacAddress, "\n").trimmed(); + int signalLevel = getTextBetweenDelimiters(cell, searchTermSignalLevel, "dBm").trimmed().toInt(); + QList securityTypes; + bool requiresKey = QRegularExpression("Encryption key:on").match(cell).hasMatch(); + + QStringList infoElements = cell.split("IE: "); + infoElements.takeFirst(); // remove the header + for (const QString &element : infoElements) + { + if ( element.startsWith("Unknown:") ) continue; + bool isWPA2 = QRegularExpression(".*WPA2 ").match(element).hasMatch(); + bool isWPA = QRegularExpression(".*WPA ").match(element).hasMatch(); + bool supportsAES = QRegularExpression("Pairwise Ciphers.*CCMP").match(element).hasMatch(); + bool requiresPSK = QRegularExpression("Authentication Suites.*PSK").match(element).hasMatch(); + bool supportsTKIP = QRegularExpression("Pairwise Ciphers.*TKIP").match(element).hasMatch(); + + // NOTE: the iwlist command does not have the SAE and displayes it as unknown but the (8) is the SAE which comes from the driver. + bool isWPA3Only = false; + bool supportsSAE = QRegularExpression("Authentication Suites.*unknown \\(8\\)").match(element).hasMatch(); + // NOTE: if it only supports SAE it means that is the WPA3-only + // NOTE: iwlist is not reporting the WPA3 SSID as WPA3 so the only indication is the SAE which belongs only to WPA3. + // IMPORTANT: ******************************************************************* + // NOTE: we do not support the OWE and ENTERPRISE yet and it is personal for now. + // ****************************************************************************** + if ( supportsSAE ) { + isWPA3Only = QRegularExpression("Authentication Suites \\(1\\) : unknown \\(8\\)").match(element).hasMatch(); + } + //DEBUG:qDebug() << element << supportsSAE; + + MWifiNetwork::SECURITY_TYPE securityType = MWifiNetwork::SECURITY_TYPE::UNSUPPORTED; + + + // NOTE: order of the if is very IMPORTANT + if ( supportsSAE ) // the wpa3 has the highest priority, and if supported we connect with wpa3 + securityType = isWPA3Only ? MWifiNetwork::SECURITY_TYPE::WPA3_SAE_ONLY: + MWifiNetwork::SECURITY_TYPE::WPA3_WPA2_TRNS; + else if (isWPA2 && requiresPSK && supportsAES && !supportsTKIP) + securityType = MWifiNetwork::SECURITY_TYPE::WPA2_AES; + else if (isWPA && requiresPSK && supportsAES && supportsTKIP) + securityType = MWifiNetwork::SECURITY_TYPE::WPA_TKIP_AES; + else if (isWPA && requiresPSK && supportsTKIP) + securityType = MWifiNetwork::SECURITY_TYPE::WPA_TKIP; + else if (isWPA && requiresPSK && supportsAES) + securityType = MWifiNetwork::SECURITY_TYPE::WPA_AES; + else if (!isWPA && !isWPA2 && !requiresPSK && !supportsAES && !supportsTKIP && requiresKey) + securityType = MWifiNetwork::SECURITY_TYPE::WEP; + + if (securityType != MWifiNetwork::SECURITY_TYPE::UNSUPPORTED) + securityTypes.append(securityType); + } + // if no type can be found. + // we assume it was WEP which we don't support anyway. + if ( securityTypes.isEmpty() ) { + securityTypes.append(MWifiNetwork::SECURITY_TYPE::WEP); + } + + //DEBUG:qDebug() << macAddress << ssid << securityTypes << signalLevel << requiresKey; + WifiNetworkData network(macAddress, ssid, securityTypes, MWifiNetwork::STATUS::NOT_CONNECTED, signalLevel, requiresKey); + networks.append(network); + emit didAddNetwork(network); + } + } + LOG_DEBUG(QString("WifiInterface::%1 Finished").arg(__FUNCTION__)); + return networks; +} + +/*! + * \brief WifiInterface::onStatusChanged + * Ensures any status changes are logged + * \param vMessage (QString) the message detail + */ +void WifiInterface::onStatusChanged(const QString &vMessage) +{ + LOG_DEBUG(vMessage); +} + +/*! + * \brief WifiInterface::doJoinNetwork + * Handles request to join a network + * \param vMacAddress - (QString) the mac address of the network to join + * \param vPassword - (QString) the password for the network provided by the user + */ +void WifiInterface::doJoinNetwork(const WifiNetworkData &vNetwork, const QString &vPassword) +{ + _network = vNetwork; + _useDHCP = true; + LOG_DEBUG(QString("Joining Network %1").arg(vNetwork.ssid())); + if (_processGenerateWPASupplicant.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processGenerateWPASupplicant.objectName())); + return; + } + QString securityType; + if (vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA3_WPA2_TRNS) + || vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA3_SAE_ONLY)) + securityType = "wpa3"; + else if (vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA2_AES)) + securityType = "wpa2"; + else if (vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA_TKIP_AES) + || vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA_AES) + || vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WPA_TKIP)) + securityType = "wpa"; + else if (vNetwork.securityTypes().contains(MWifiNetwork::SECURITY_TYPE::WEP)) + securityType = "wep"; + else + { + LOG_DEBUG(QString("Network %1 has an unsupported security type.").arg(vNetwork.ssid())); + emit didError(QString("Network %1 has an unsupported security type.").arg(vNetwork.ssid())); + } + + // TODO + Q_UNUSED(vPassword); // REMOVE!! + // QString script; if ( ! checkScript(script, Wifi_Generate_WPA_Supplicant) ) return; + // _processGenerateWPASupplicant.start(script, + // QStringList() << vNetwork.ssid() + // << vPassword + // << iface() + // << securityType); +} + +/*! + * \brief WifiInterface::onProcessFinishedGenerateWPASupplicant + * Called when the process that generates the WPA supplicant file has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedGenerateWPASupplicant(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError("Could not configure network."); + return; + } + + if (_processStartWPASupplicant.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processStartWPASupplicant.objectName())); + return; + } + // TODO + // QString script; if ( ! checkScript(script, Wifi_Start_WPA_Supplicant) ) return; + // _processStartWPASupplicant.start(script, + // QStringList() << iface() + // << _wpaSupplicantConfPath); +} + +/*! + * \brief WifiInterface::onProcessFinishedStartWPASupplicant + * Called when the process that starts the WPA supplicant has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedStartWPASupplicant(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError("Could not configure network."); + return; + } + emit didRequestAutoAssignedIP(); +} + +/*! + * \brief WifiInterface::onRequestAutoAssignedIP + * Handles the request for an auto assigned IP address + */ +void WifiInterface::onRequestAutoAssignedIP() +{ + if (_processRequestAutoAssignedIP.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processRequestAutoAssignedIP.objectName())); + return; + } + QString progressDot = QString(".").repeated(_dhcpAttempts - _dhcpAttemptsLeft); + emit didStatusChanged(tr("%1.Obtaining IP Address.%1").arg(progressDot)); + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Get_Auto_Assigned_IP) ) return; + // _processRequestAutoAssignedIP.start(script, QStringList() << iface()); +} + +/*! + * \brief WifiInterface::onProcessFinishedRequestAutoAssignedIP + * Called when the process that requests an auto-assigned IP has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedRequestAutoAssignedIP(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + if (_dhcpAttemptsLeft > 0) + { + _dhcpAttemptsLeft--; + emit didRequestAutoAssignedIP(); + } else + { + emit didError(QString("Could not connect to %1").arg(_network.ssid())); + _dhcpAttemptsLeft = _dhcpAttempts; + } + return; + } + _dhcpAttemptsLeft = _dhcpAttempts; + doRequestIPSettings(); +} + +/*! + * \brief WifiInterface::doRequestIPSettings + * Handles a request to read the IP settings of the device. + */ +void WifiInterface::doRequestIPSettings() +{ + if (_processReadIPSettings.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processReadIPSettings.objectName())); + return; + } + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Read_IP_Settings) ) return; + // _processReadIPSettings.start(script, + // QStringList() << iface()); +} + + +/*! + * \brief WifiInterface::runProcessGetNetworkInfo + * Handles a request to read the network info (ssid + macAddress) + */ +void WifiInterface::runProcessGetNetworkInfo() +{ + if (_processReadNetworkInfo.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processReadNetworkInfo.objectName())); + return; + } + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Read_Network_Info) ) return; + // _processReadNetworkInfo.start(script, + // QStringList() << iface()); +} + + +/*! + * \brief WifiInterface::onProcessFinishedReadIP + * Parses the IP settings of the device when the process has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedReadIP(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitCode); + Q_UNUSED(vExitStatus); + QString output = _processReadIPSettings.readAllStandardOutput(); + _network.mIPSettings.mIPAddress = parseIP(output); + _network.mIPSettings.mBroadcast = parseBroadcast(output); + _network.mIPSettings.mSubnetMask = parseSubnetMask(output); + + runProcessGetNetworkInfo(); +} + +/*! + * \brief WifiInterface::onProcessFinishedReadNetworkInfo + * Parses the network mac address of the device when the process has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedReadNetworkInfo(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitCode); + Q_UNUSED(vExitStatus); + + QString output = _processReadNetworkInfo.readAllStandardOutput(); + const QString networkMacAddress = parseNetworkMacAddress(output); + _network.macAddress(networkMacAddress.trimmed()); + + const QString networkSSid = parseNetworkSSID(output); + _network.ssid(networkSSid.trimmed()); + + LOG_DEBUG(QString("Network SSID : '%1' MacAddress: '%2'.") + .arg(networkSSid) + .arg(networkMacAddress)); + + if (_processReadGateway.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processReadGateway.objectName())); + return; + } + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Read_Gateway) ) return; + // _processReadGateway.start(script, QStringList()); +} + +/*! + * \brief WifiInterface::onProcessFinishedReadGateway + * Parses the gateway of the device when the process has finished then + * initiates reading the DNS + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedReadGateway(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitCode); + Q_UNUSED(vExitStatus); + _network.mIPSettings.mGateway = parseGateway(_processReadGateway.readAllStandardOutput()); + if (_processReadDNS.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processReadDNS.objectName())); + return; + } + // TODO + // QString script; if ( ! checkScript(script, Wifi_Read_DNS) ) return; + // _processReadDNS.start(script, QStringList()); +} + +/*! + * \brief WifiInterface::onProcessFinishedReadDNS + * Parses the DNS of the device when the process has finished then + * emits the newtwork with all the ip address information filled + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedReadDNS(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitCode); + Q_UNUSED(vExitStatus); + _network.mIPSettings.mDNS = parseDNS(_processReadDNS.readAllStandardOutput()); + if (!_useDHCP) + { + // Use current static IP settings defaults + doRequestSetIPAddress(_network.mIPSettings.mIPAddress); + doRequestSetGateway(_network.mIPSettings.mGateway); + doRequestSetSubnetMask(_network.mIPSettings.mSubnetMask); + doRequestSetDNS(_network.mIPSettings.mDNS); + } + emit didConnectToNetwork(_network); +} + +/*! + * \brief WifiInterface::doDisconnectNetwork + * Disconnects from the specified network + * \param vNetwork - (Network) the network to disconnect from + */ +void WifiInterface::doDisconnectNetwork(const WifiNetworkData &vNetwork) +{ + LOG_DEBUG(QString("Disconnecting from Network %1").arg(vNetwork.ssid())); + if (_processDisconnectNetwork.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processDisconnectNetwork.objectName())); + return; + } + QString script; if ( ! checkScript(script, Scripts_Wifi_Disconnect) ) return; + _processDisconnectNetwork.start(script, + QStringList() << iface()); +} + +/*! + * \brief WifiInterface::onProcessFinishedDisconnectNetwork + * Called when we disconnect from the WiFi network + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedDisconnectNetwork(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError(tr("Failed to disconnect from %1").arg(_network.ssid())); + return; + } + emit didDisconnectNetwork(_network); + _network.clearSettings(); +} + +/*! + * \brief WifiInterface::parseIP + * Parses the IP from the IP address output + * \param vOutput - (QString) the console output + * \return (QString) the IP Address only + */ +QString WifiInterface::parseIP(const QString &vOutput) +{ + const QString leftDelim = "inet addr:"; + const QString rightDelim = "Bcast:"; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseBroadcast + * Parses the Broadcast from the IP address output + * \param vOutput - (QString) the console output + * \return (QString) the Broadcast IP if found, "" otherwise + */ +QString WifiInterface::parseBroadcast(const QString &vOutput) +{ + const QString leftDelim = "Bcast:"; + const QString rightDelim = "Mask:"; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseSubnetMask + * Parses the subnet mask from the provided output + * \param vOutput - (QString) the console output + * \return (QString) the subnet mask if found, "" otherwise + */ +QString WifiInterface::parseSubnetMask(const QString &vOutput) +{ + const QString leftDelim = "Mask:"; + const QString rightDelim = "\n"; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseNetworkMacAddress + * Parses the network macAddress from the provided output + * \param vOutput - (QString) the console output + * \return (QString) the subnet mask if found, "" otherwise + */ +QString WifiInterface::parseNetworkMacAddress(const QString &vOutput) +{ + const QString leftDelim = "Access Point:"; + const QString rightDelim = "Bit Rate="; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseNetworkSSID + * Parses the network ssid from the provided output + * \param vOutput - (QString) the console output + * \return (QString) the subnet mask if found, "" otherwise + */ +QString WifiInterface::parseNetworkSSID(const QString &vOutput) +{ + const QString leftDelim = "ESSID:\""; + const QString rightDelim = "\""; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseGateway + * Parses the gateway from the provided output + * \param vOutput - (QString) the console output + * \return (QString) the gateway if found, "" otherwise + */ +QString WifiInterface::parseGateway(const QString &vOutput) +{ + const QString leftDelim = "default via"; + const QString rightDelim = "dev"; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::parseDNS + * Parses the DNS from the provided output + * \param vOutput - (QString) the console output + * \return (QString) the first DNS found, "" otherwise + */ +QString WifiInterface::parseDNS(const QString &vOutput) +{ + const QString leftDelim = "nameserver"; + const QString rightDelim = "\n"; + return getTextBetweenDelimiters(vOutput, leftDelim, rightDelim); +} + +/*! + * \brief WifiInterface::doRequestSetIPAddress + * Issues command to set a static IP address + * \param vIPAddress - (QString) the IP address + */ +void WifiInterface::doRequestSetIPAddress(const QString &vIPAddress) +{ + Q_UNUSED(vIPAddress) + LOG_DEBUG(QString("Handling request to set static IP address: %1").arg(vIPAddress)); + if (_processSetStaticIPAddress.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processSetStaticIPAddress.objectName())); + return; + } + // TODO + // QString script; if ( ! checkScript(script, Wifi_Set_Static_IP) ) return; + // _useDHCP = false; + // _processSetStaticIPAddress.start(script, + // QStringList() << iface() << vIPAddress); +} + +/*! + * \brief WifiInterface::onProcessFinishedSetIPAddress + * Called when settings a static IP address finishes + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedSetIPAddress(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError(tr("Failed to set static IP Address")); + return; + } + emit didSetStaticIPAddress(); +} + +/*! + * \brief WifiInterface::doRequestSetGateway + * Issues command to set a static gateway + * \param vGateway - (QString) the gateway + */ +void WifiInterface::doRequestSetGateway(const QString &vGateway) +{ + Q_UNUSED(vGateway) + LOG_DEBUG(QString("Handling request to set static gateway: %1").arg(vGateway)); + + if (_processSetStaticGateway.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processSetStaticGateway.objectName())); + return; + } + + //TODO + // QString script; if ( ! checkScript(script, Wifi_Set_Gateway) ) return; + // _useDHCP = false; + // _processSetStaticGateway.start(script, + // QStringList() << vGateway); +} + +/*! + * \brief WifiInterface::onProcessFinishedSetGateway + * Called when the the process to set the gateway has finished + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedSetGateway(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError(tr("Failed to set gateway.")); + return; + } + emit didSetGateway(); +} + +/*! + * \brief WifiInterface::doRequestSetSubnetMask + * Issues command to set the subnet mask + * \param vSubnetMask - (QString) the subnet mask + */ +void WifiInterface::doRequestSetSubnetMask(const QString &vSubnetMask) +{ + Q_UNUSED(vSubnetMask) + LOG_DEBUG(QString("Handling request to set static subnet mask: %1").arg(vSubnetMask)); + if (_processSetStaticSubnetMask.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processSetStaticSubnetMask.objectName())); + return; + } + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Set_SubnetMask) ) return; + // _useDHCP = false; + // _processSetStaticSubnetMask.start(script, + // QStringList() << iface() << vSubnetMask); +} + +/*! + * \brief WifiInterface::onProcessFinishedSetSubnetMask + * Called when the process to set the subnet mask finishes + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedSetSubnetMask(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError(tr("Failed to set subnet mask.")); + return; + } + emit didSetSubnetMask(); +} + +/*! + * \brief WifiInterface::doRequestSetDNS + * Issues a command to set the DNS + * \param vDNS - (QString) the DNS + */ +void WifiInterface::doRequestSetDNS(const QString &vDNS) +{ + LOG_DEBUG(QString("Handling request to set DNS: %1").arg(vDNS)); + if (_processSetDNS.state() != QProcess::NotRunning) + { + LOG_DEBUG(QString("Rejecting request for %1. %2 is already running.") + .arg(__FUNCTION__) + .arg(_processSetDNS.objectName())); + return; + } + + // TODO + // QString script; if ( ! checkScript(script, Wifi_Set_DNS ) ) return; + // _processSetDNS.start(script, + // QStringList() << vDNS); +} + +/*! + * \brief WifiInterface::onProcessFinishedSetDNS + * Called when the process that finishes setting the DNS completes + * \param vExitCode - (int) the exit code + * \param vExitStatus - (QProcess::ExitStatus) the exit status + */ +void WifiInterface::onProcessFinishedSetDNS(int vExitCode, QProcess::ExitStatus vExitStatus) +{ + Q_UNUSED(vExitStatus) + if (vExitCode != 0) + { + emit didError(tr("Failed to set DNS.")); + return; + } + emit didSetDNS(); +} + +/*! + * \brief WifiInterface::securityTypesToStringList + * Converts a QList of network security types to a QStringList + * \param securityTypes (QList) the list of security type + * \return (QStringList) the security types as a string list + */ +QStringList WifiInterface::securityTypesToStringList(const QList &securityTypes) +{ + QStringList securityTypesStrList; + for (const MWifiNetwork::SECURITY_TYPE &type : securityTypes) + { + securityTypesStrList.append(QString("%1").arg(type)); + } + return securityTypesStrList; +} + +/*! + * \brief variantListToSecurityTypes + * Converts a QVariantList of security types to a QList + * \param securityTypesVar (QVariantList) security types + * \return (QList) the list of network security types + */ +QList WifiInterface::variantListToSecurityTypes(const QList &securityTypesVar) +{ + QList securityTypes; + for (const QVariant &type : securityTypesVar) + { + bool ok = false; + MWifiNetwork::SECURITY_TYPE val = MWifiNetwork::SECURITY_TYPE(type.toInt(&ok)); + if (ok) + securityTypes.append(val); + } + return securityTypes; +} + +/*! + * \brief WifiInterface::checkScript + * \details Prepends the script folder path to the script name and checks if the script exists and is executable. + * \param vScript : The script name with full path which will be set in this parameter passed argument. + * \param vShellScript : The shell script name + * \return true if succeeds and false otherwise */ +bool WifiInterface::checkScript(QString &vScript, const QString &vShellScript) +{ + vScript = Storage::Scripts_Path_Name() + vShellScript; + QFileInfo info(vScript); + if ( ! info.exists () ) { LOG_DEBUG(QString("script %1 does not exist." ).arg(vScript)); return false; } + if ( ! info.isExecutable() ) { LOG_DEBUG(QString("script %1 is not executable." ).arg(vScript)); return false; } + return true; +} Index: sources/wifi/WifiInterface.h =================================================================== diff -u --- sources/wifi/WifiInterface.h (revision 0) +++ sources/wifi/WifiInterface.h (revision 59135abca5be03c9156d3847ccc1befa7132309e) @@ -0,0 +1,162 @@ +/*! + * + * Copyright (c) 2021-2025 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file WifiInterface.h + * \author (last) Nico Ramirez + * \date (last) 17-Jun-2025 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ +#pragma once + +// Qt +#include +#include +#include + +// Project +#include "main.h" +#include "StorageGlobals.h" +#include "MWifiNetwork.h" + +// define +#define _WifiInterface WifiInterface::I() + +using namespace Model; + +class tst_utilities; + +/*! + * \brief The WifiInterface class + * Communicates with Access Points + * \details + * - Scans for Access Points + * - Authenticates with Access Points + * - Disconnects from Access Points + */ +class WifiInterface : public QObject +{ + Q_OBJECT + + friend class ::tst_utilities; + + QThread *_thread = nullptr; + bool _init = false; + bool _scanRunning = false; + bool _useDHCP = true; + int _defaultTimeout = 5000; + int _dhcpTimeout = 10000; + int _scanTimeout = 30000; + int _processCounter = 0; + int _dhcpAttemptsLeft = 5; + const quint8 _dhcpAttempts = 5; + + QProcess _processScan; + QProcess _processDisconnectNetwork; + QProcess _processReadIPSettings; + QProcess _processReadGateway; + QProcess _processReadDNS; + QProcess _processGenerateWPASupplicant; + QProcess _processStartWPASupplicant; + QProcess _processRequestAutoAssignedIP; + QProcess _processSetStaticIPAddress; + QProcess _processSetStaticGateway; + QProcess _processSetStaticSubnetMask; + QProcess _processSetDNS; + QProcess _processReadNetworkInfo; + WifiNetworkData _network; + + const QString _wpaSupplicantConfPath = QString("/etc/wpa_supplicant/wpa_supplicant-%1.conf").arg(iface()); + +// Singleton +SINGLETON(WifiInterface) + +public: + + static QString iface() { + #ifdef BUILD_FOR_DESKTOP + return "wlp4s0"; + #else + return "wlan0"; + #endif + } + +private: + bool checkScript(QString &vScript, const QString &vShellScript); + QString getTextBetweenDelimiters(const QString &vText, const QString &vLeftDelim, const QString &vRightDelim); + QList parseWifiScan(const QString &vOutput); + QString parseIP(const QString &vOutput); + QString parseBroadcast(const QString &vOutput); + QString parseSubnetMask(const QString &vOutput); + QString parseGateway(const QString &vOutput); + QString parseDNS(const QString &vOutput); + QString parseNetworkMacAddress(const QString &vOutput); + QString parseNetworkSSID(const QString &vOutput); + void initConnections(); + void initThread(QThread &vThread); + void quitThread(); + bool hasConnectedToWifi(); + void rejoinLastWifiNetwork(); + QStringList securityTypesToStringList(const QList &securityTypes); + QList variantListToSecurityTypes(const QList &securityTypesVar); + void runProcessGetNetworkInfo (); + +public slots: + bool init(QThread &vThread); + bool init(); + void quit(); + + void doScan (); + void doJoinNetwork (const WifiNetworkData &vNetwork, const QString &vPassword); + void doDisconnectNetwork (const WifiNetworkData &vNetwork); + void doRequestIPSettings (); + + // static IP address assignment + void doRequestSetIPAddress (const QString &vIPAddress ); + void doRequestSetGateway (const QString &vGateway ); + void doRequestSetSubnetMask (const QString &vSubnetMask ); + void doRequestSetDNS (const QString &vDNS ); + +signals: + void didStart(QPrivateSignal); + void didAddNetwork (const WifiNetworkData & ); + void didDisconnectNetwork (const WifiNetworkData & ); + void didRejectRequest (const QString &vFunction, const QString &vReason); + void didBusyChanged ( bool vBusy); + void didStatusChanged (const QString &vNewStatus ); + void didRequestAutoAssignedIP (); + // static IP address settings + void didSetStaticIPAddress (); + void didSetGateway (); + void didSetSubnetMask (); + void didSetDNS (); + + // emitted from separate threads + void didScanStatusChanged ( bool ); + void didConnectToNetwork (const WifiNetworkData &); + void didError (const QString &); + +private slots: + void onStatusChanged(const QString &vMessage); + void onRequestAutoAssignedIP(); + void onProcessFinishedScan (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedReadIP (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedReadGateway (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedReadDNS (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedGenerateWPASupplicant (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedStartWPASupplicant (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedRequestAutoAssignedIP (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedDisconnectNetwork (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedSetIPAddress (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedSetGateway (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedSetSubnetMask (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedSetDNS (int vExitCode, QProcess::ExitStatus vExitStatus); + void onProcessFinishedReadNetworkInfo (int vExitCode, QProcess::ExitStatus vExitStatus); + + SAFE_CALL(doStart) +};