Ferramenta de configuração do Raspberry Pi 4 NetworkManager

O objetivo era escrever um aplicativo Qt para o Raspberry Pi 4 que pudesse ser usado para alternar entre diferentes pontos de acesso WLAN e armazenar as credenciais associadas.
Eu usei uma imagem raspbian-buster-lite e uma instalação Qt como descrito no Qt no Raspberry Pi 4 como ponto de partida.
Além disso, eu instalei o NetworkManager, que pode ser usado pelo comando shell (nmcli ...) Criar, configurar e gerenciar conexões de rede.
Informações sobre isso podem ser encontradas em https://wiki.debian.org/de/NetworkManager ou https://developer.gnome.org/NetworkManager/stable/nmcli.html.
O NetworkManager oferece um comando que pode ser usado para iniciar um processo de monitoramento, que então comenta sobre as alterações nas várias interfaces (wlan0 ou eth0) (por exemplo, indisponível, desconectado, conectando, conectado, ...).
Eu queria usar esse monitoramento para exibir os diferentes status dos locais de rede na GUI. Surgiram 2 problemas:

  • se vários comandos nmcli foram emitidos em rápida sucessão, então o feedback sobre os diferentes status chegou com um atraso de tempo e não pôde ser exibido ao vivo na GUI.
  • O feedback dos comandos NMCLI foi enviado em diferentes slots e, portanto, poderia ser mal coordenado.

Função de monitoramento

Em primeiro lugar, precisamos da função de monitoramento (monitorDevices) e da função de slot público, que intercepta e avalia a saída de monitoramento e, em seguida, envia as mensagens de status para a GUI, por exemplo.
Na função "monitorDevices", que é iniciada automaticamente quando o aplicativo é iniciado, o comando nmcli é iniciado com sudo: sudo nmcli monitor
A instrução "setProcessChannelMode(QProcess::MergedChannels)" afirma que "Standard Output" e "Standard Error" são exibidos juntos em um canal e, portanto, podem ser avaliados com uma função.
A linha "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" é usada sempre que uma mensagem é exibida (readyReadStandardOutput), ela é enviada para o 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:>

Função de avaliação

A avaliação das mensagens é então realizada na função "processOutput". O QProcess senderProcess intercepta todas as mensagens de todos os comandos nmcli - não apenas as do comando de monitoramento.

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

Iniciar outro processo nmcli

Se você agora iniciar outro comando nmcli com a função abaixo, a saída é interceptada pela função de monitoramento acima e pode então ser avaliada e processada posteriormente no "outputProcess".
Esta função alterna para uma conexão de rede existente e a inicia. É importante não incluir um "set_wifi_process->waitForReadyRead()" ou "set_wifi_process->waitForFinished()", porque então a saída de todas as mensagens será bloqueada até que esse processo seja concluído.

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

A linha "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput())" encaminha as mensagens de saída desse processo de volta para o "processOutput" e pode ser avaliada lá novamente em um local central. </:code3:>