Celem było napisanie aplikacji Qt dla Raspberry Pi 4, która może być używana do przełączania się między różnymi punktami dostępowymi WLAN i przechowywania powiązanych poświadczeń.
Użyłem obrazu raspbian-buster-lite i instalacji Qt opisanej w Qt na Raspberry Pi 4 jako punktu wyjścia.
Ponadto zainstalowałem Menedżera sieci, który może być używany przez polecenie shell (nmcli ...) Twórz i konfiguruj połączenia sieciowe oraz zarządzaj nimi.
Informacje na ten temat można znaleźć w https://wiki.debian.org/de/NetworkManager lub https://developer.gnome.org/NetworkManager/stable/nmcli.html.
Menedżer sieci oferuje polecenie, którego można użyć do rozpoczęcia procesu monitorowania, który następnie komentuje zmiany w różnych interfejsach (wlan0 lub eth0) (np. niedostępny, rozłączony, łączony, połączony, ...).
Chciałem użyć tego monitorowania do wyświetlania różnych stanów lokalizacji sieciowych w GUI. Pojawiły się 2 problemy:

  • Jeśli kilka poleceń NMCLI zostało wydanych w krótkich odstępach czasu, informacje zwrotne o różnych statusach nadeszły z opóźnieniem czasowym i nie mogły być wyświetlane na żywo w GUI.
  • Informacje zwrotne poleceń NMCLI były wysyłane w różnych szczelinach i dlatego mogły być słabo skoordynowane.

Funkcja monitorowania

Przede wszystkim potrzebujemy funkcji monitorowania (monitorDevices) i funkcji publicznego gniazda, która przechwytuje i ocenia wyjście monitorowania, a następnie wysyła komunikaty o stanie na przykład do GUI.
W funkcji "monitorDevices", która uruchamia się automatycznie po uruchomieniu aplikacji, polecenie nmcli jest uruchamiane za pomocą sudo: sudo nmcli monitor
Instrukcja "setProcessChannelMode(QProcess::MergedChannels)" stwierdza, że "Standard Output" i "Standard Error" są wyświetlane razem w kanale i dlatego mogą być obliczane za pomocą funkcji.
Linia "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" jest używana za każdym razem, gdy wyświetlany jest komunikat (readyReadStandardOutput), jest on wysyłany do gniazda "processOutput".

void CmdLauncher::monitorDevices() {
QStringList device_monitoring_on = {"nmcli", "monitor"};
device_monitoring_process = new QProcess(this);
device_monitoring_process->setProcessChannelMode(QProcess::MergedChannels);
device_monitoring_process->start("sudo", device_monitoring_on);
connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
}

</:code1:>

Funkcja oceny

Ocena komunikatów jest następnie przeprowadzana w funkcji "processOutput". QProcess senderProcess przechwytuje wszystkie komunikaty wszystkich poleceń nmcli - nie tylko te z polecenia monitorowania.

void CmdLauncher::processOutput() {
QProcess* senderProcess = qobject_cast<QProcess*>(sender());
senderProcess->setReadChannel(QProcess::StandardOutput);
QString message = QString::fromLocal8Bit(senderProcess->readAllStandardOutput());

qDebug() << "CmdLauncher::processOutput message: " << message << endl;
if (message.contains("Error:")) {
    message.remove(QString("\n"));
    error_messages = message;
    emit getErrorMessagesChanged();
    if (message.contains(QString("Error: unknown connection"))) {
        secrets_required = true;
        emit getSecretsRequiredChanged();
    }
}
// wifi
if (message.contains("wlan0: connected") || message.contains("wlan0:connected")) {
    wifi_device_state = "Wifi-Connected";
    emit getWifiDeviceStateChanged();
    error_messages = "";
    emit getErrorMessagesChanged();
    rescanWifi();
    testInternetConnection();
}

}

</:code2:>

Rozpocznij kolejny proces nmcli

Jeśli teraz uruchomisz kolejne polecenie nmcli z poniższą funkcją, wyjście zostanie przechwycone przez powyższą funkcję monitorowania, a następnie może zostać ocenione i dalej przetwarzane w "outputProcess".
Ta funkcja przełącza się na istniejące połączenie sieciowe i uruchamia je. Ważne jest, aby nie dołączać "set_wifi_process->waitForReadyRead()" lub "set_wifi_process->waitForFinished()", ponieważ wtedy dane wyjściowe wszystkich wiadomości zostaną zablokowane do momentu zakończenia tego procesu.

void CmdLauncher::setWifi(QString ssid) {
    error_messages = "";
    emit getErrorMessagesChanged();

    QString uuid = "";
    for (int i = 0 ; i  networkConnections.length() ; i++) {
        QStringList connections = networkConnections[i].split(":");
        if (connections[1] == ssid)
        {
            uuid = connections[2];
        }
    }

    QStringList args_wifi_exist = {"nmcli", "connection", "up", uuid};
    set_wifi_process = new QProcess(this);
    set_wifi_process->setProcessChannelMode(QProcess::MergedChannels);
    set_wifi_process->start("sudo", args_wifi_exist);
    connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
}

Linia "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" przekazuje komunikaty wyjściowe tego procesu z powrotem do "processOutput" i może być tam ponownie oceniona w centralnej lokalizacji. </:code3:>

Walter Prechtl

Walter Prechtl

Aktualizacja na stronie: 12. March 2024
Czas czytania: 4 minuty