Strumento di configurazione NetworkManager di Raspberry Pi 4

L'obiettivo era quello di scrivere un'applicazione Qt per il Raspberry Pi 4 che può essere utilizzata per passare da un punto di accesso WLAN all'altro e memorizzare le credenziali associate.
Ho usato un'immagine raspbian-buster-lite e un'installazione Qt come descritto in Qt sul Raspberry Pi 4 come punto di partenza.
Inoltre, ho installato il NetworkManager, che può essere utilizzato dal comando shell (nmcli ...) Creare, configurare e gestire le connessioni di rete.
Informazioni al riguardo sono disponibili alla voce https://wiki.debian.org/de/NetworkManager o https://developer.gnome.org/NetworkManager/stable/nmcli.html.
Il NetworkManager offre un comando che può essere utilizzato per avviare un processo di monitoraggio, che commenta quindi le modifiche alle varie interfacce (wlan0 o eth0) (ad es. non disponibile, disconnesso, connessione, connessione, ...).
Volevo utilizzare questo monitoraggio per visualizzare i diversi stati delle posizioni di rete nella GUI. Sono emersi 2 problemi:

  • se diversi comandi nmcli sono stati emessi in rapida successione, il feedback sui diversi stati è arrivato con un ritardo temporale e non poteva essere visualizzato in tempo reale nella GUI.
  • Il feedback dei comandi NMCLI è stato inviato in diversi slot e potrebbe quindi essere mal coordinato.

Funzione di monitoraggio

Prima di tutto, abbiamo bisogno della funzione di monitoraggio (monitorDevices) e della funzione slot pubblico, che intercetta e valuta l'output di monitoraggio e quindi invia i messaggi di stato alla GUI, ad esempio.
Nella funzione "monitorDevices", che si avvia automaticamente all'avvio dell'applicazione, il comando nmcli viene avviato con sudo: sudo nmcli monitor
L'istruzione "setProcessChannelMode(QProcess::MergedChannels)" afferma che "Standard Output" e "Standard Error" vengono visualizzati insieme in un canale e possono quindi essere valutati con una funzione.
La riga "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" viene utilizzata ogni volta che viene visualizzato un messaggio (readyReadStandardOutput), viene inviata allo slot "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:>

Funzione di valutazione

La valutazione dei messaggi viene quindi effettuata nella funzione "processOutput". Il processo mittente di QProcess intercetta tutti i messaggi di tutti i comandi nmcli, non solo quelli del comando monitoring.

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:>

Avviare un altro processo nmcli

Se ora si avvia un altro comando nmcli con la funzione seguente, l'output viene intercettato dalla funzione di monitoraggio di cui sopra e può quindi essere valutato e ulteriormente elaborato in "outputProcess".
Questa funzione passa a una connessione di rete esistente e la avvia. È importante non includere un "set_wifi_process->waitForReadyRead()" o "set_wifi_process->waitForFinished()", perché l'output di tutti i messaggi verrà bloccato fino al termine di questo processo.

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()));
}

La riga "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" inoltra i messaggi di output di questo processo a "processOutput" e può essere valutata nuovamente in una posizione centrale. </:code3:>