Raspberry Pi 4 NetworkManager Configuratie Tool

Het doel was om een Qt-applicatie voor de Raspberry Pi 4 te schrijven die kan worden gebruikt om te schakelen tussen verschillende WLAN-toegangspunten en de bijbehorende inloggegevens op te slaan.
Ik heb een raspbian-buster-lite image en een Qt installatie zoals beschreven in Qt op de Raspberry Pi 4 als uitgangspunt gebruikt.
Daarnaast heb ik de NetworkManager geïnstalleerd, die kan worden gebruikt door shell command (nmcli ...) Netwerkverbindingen maken, configureren en beheren.
Informatie hierover is te vinden onder https://wiki.debian.org/de/NetworkManager of https://developer.gnome.org/NetworkManager/stable/nmcli.html.
De NetworkManager biedt een commando dat kan worden gebruikt om een monitoringproces te starten, dat vervolgens commentaar geeft op wijzigingen in de verschillende interfaces (wlan0 of eth0) (bijv. niet beschikbaar, losgekoppeld, verbindend, verbonden, ...).
Ik wilde deze monitoring gebruiken om de verschillende statussen van de netwerklocaties in de GUI weer te geven. Er kwamen 2 problemen naar voren:

  • als meerdere NMcli-opdrachten snel achter elkaar werden uitgegeven, kwam de feedback over de verschillende statussen met een tijdsvertraging binnen en kon deze niet live in de GUI worden weergegeven.
  • De feedback van de NMCLI-commando's werd in verschillende slots verzonden en kon dus slecht worden gecoördineerd.

Bewakingsfunctie

Allereerst hebben we de monitoringfunctie (monitorDevices) en de openbare slotfunctie nodig, die de bewakingsuitvoer onderschept en evalueert en vervolgens de statusberichten bijvoorbeeld naar de GUI stuurt.
In de functie "monitorDevices", die automatisch start wanneer de toepassing wordt gestart, wordt het nmcl-commando gestart met sudo: sudo nmcli monitor
De verklaring "setProcessChannelMode(QProcess::MergedChannels)" stelt dat "Standard Output" en "Standard Error" samen in een kanaal worden weergegeven en dus kunnen worden geëvalueerd met een functie.
De regel "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" wordt gebruikt wanneer een bericht wordt weergegeven (readyReadStandardOutput), het wordt verzonden naar de sleuf "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:>

Evaluatiefunctie

De evaluatie van de berichten wordt vervolgens uitgevoerd in de functie "processOutput". De QProcess senderProcess onderschept alle berichten van alle nmcli-commando's - niet alleen die van het bewakingscommando.

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

Start een ander nmcli-proces

Als u nu een ander nmcli-commando start met de onderstaande functie, wordt de uitvoer onderschept door de bovenstaande bewakingsfunctie en kan deze vervolgens worden geëvalueerd en verder worden verwerkt in het "outputProcess".
Deze functie schakelt over naar een bestaande netwerkverbinding en start deze. Het is belangrijk om geen "set_wifi_process->waitForReadyRead()" of "set_wifi_process->waitForFinished()" op te nemen, omdat dan de uitvoer van alle berichten wordt geblokkeerd totdat dit proces is voltooid.

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

De regel "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" stuurt de outputberichten van dit proces terug naar de "processOutput" en kan daar op een centrale locatie weer geëvalueerd worden. </:code3:>