Konfigurační nástroj Raspberry Pi 4 NetworkManager

Cílem bylo napsat Qt aplikaci pro Raspberry Pi 4, která může být použita k přepínání mezi různými přístupovými body WLAN a ukládání přidružených přihlašovacích údajů.
Jako výchozí bod jsem použil raspbian-buster-lite obraz a Qt instalaci, jak je popsáno v Qt na Raspberry Pi 4 .
Kromě toho jsem nainstaloval NetworkManager, který lze použít příkazem shellu (nmcli ...) Vytvářet, konfigurovat a spravovat síťová připojení.
Informace o tom lze nalézt v https://wiki.debian.org/de/NetworkManager nebo https://developer.gnome.org/NetworkManager/stable/nmcli.html.
NetworkManager nabízí příkaz, který lze použít ke spuštění monitorovacího procesu, který pak komentuje změny různých rozhraní (wlan0 nebo eth0) (např. nedostupné, odpojené, připojující, připojené, ...).
Chtěl jsem použít tento monitoring k zobrazení různých stavů síťových umístění v grafickém uživatelském rozhraní. Objevily se 2 problémy:

  • pokud bylo vydáno několik příkazů nmcli v rychlém sledu, pak zpětná vazba o různých stavech přišla s časovým zpožděním a nemohla být zobrazena živě v GUI.
  • Zpětná vazba příkazů NMCLI byla vysílána v různých slotech a mohla být tedy špatně koordinována.

Funkce monitorování

Nejprve potřebujeme monitorovací funkci (monitorDevices) a funkci veřejného slotu, která zachycuje a vyhodnocuje monitorovací výstup a poté posílá stavové zprávy například do GUI.
Ve funkci "monitorDevices", která se spustí automaticky při spuštění aplikace, se spustí příkaz nmcli pomocí příkazu sudo: sudo nmcli monitor
Příkaz "setProcessChannelMode(QProcess::MergedChannels)" uvádí, že "Standardní výstup" a "Standardní chyba" jsou zobrazeny společně v kanálu a mohou být tedy vyhodnoceny funkcí.
Řádek "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" se používá vždy, když se zobrazí zpráva (readyReadStandardOutput), je odeslána do slotu "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:>

Vyhodnocovací funkce

Vyhodnocení zpráv se pak provádí ve funkci "processOutput". QProcess senderProcess zachycuje všechny zprávy všech příkazů nmcli - nejen zprávy monitorovacího příkazu.

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

Spuštění dalšího procesu nmcli

Pokud nyní spustíte další příkaz nmcli s níže uvedenou funkcí, výstup je zachycen výše uvedenou monitorovací funkcí a může být následně vyhodnocen a dále zpracován v "outputProcess".
Tato funkce přepne na existující síťové připojení a spustí jej. Je důležité nezahrnovat "set_wifi_process->waitForReadyRead()" nebo "set_wifi_process->waitForFinished()", protože pak bude výstup všech zpráv blokován, dokud nebude tento proces dokončen.

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

Řádek "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" předává výstupní zprávy tohoto procesu zpět do "processOutput" a může být znovu vyhodnocen na centrálním místě. </:code3:>