Målet var at skrive en Qt-applikation til Raspberry Pi 4, der kan bruges til at skifte mellem forskellige WLAN-adgangspunkter og gemme de tilhørende legitimationsoplysninger.
Jeg brugte et raspbian-buster-lite billede og en Qt installation som beskrevet i Qt på Raspberry Pi 4 som udgangspunkt.
Derudover har jeg installeret NetworkManager, som kan bruges af shell-kommandoen (nmcli ...) Opret, konfigurer og administrer netværksforbindelser.
Oplysninger om dette kan findes under https://wiki.debian.org/de/NetworkManager eller https://developer.gnome.org/NetworkManager/stable/nmcli.html.
NetworkManager tilbyder en kommando, der kan bruges til at starte en overvågningsproces, som derefter kommenterer ændringer i de forskellige grænseflader (wlan0 eller eth0) (f.eks. Utilgængelig, afbrudt, forbinder, tilsluttet, ...).
Jeg ønskede at bruge denne overvågning til at vise de forskellige statusser for netværksplaceringerne i GUI'en. Der opstod 2 problemer:
- Hvis flere NMCLI-kommandoer blev udstedt hurtigt efter hinanden, ankom feedbacken om de forskellige statusser med en tidsforsinkelse og kunne ikke vises live i GUI'en.
- Feedbacken fra NMCLI-kommandoerne blev sendt i forskellige slots og kunne derfor være dårligt koordineret.
Overvågningsfunktion
Først og fremmest har vi brug for overvågningsfunktionen (monitorDevices) og public slot-funktionen, som opfanger og evaluerer overvågningsoutputtet og derefter sender statusmeddelelserne til f.eks. GUI.
I funktionen "monitorDevices", som starter automatisk, når applikationen starter, startes nmcli-kommandoen med sudo: sudo nmcli monitor
Udsagnet "setProcessChannelMode(QProcess::MergedChannels)" angiver, at "Standard Output" og "Standard Error" vises sammen i en kanal og dermed kan evalueres med en funktion.
Linjen "connect(device_monitoring_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" bruges, når en meddelelse vises (readyReadStandardOutput), den sendes til 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:>
Evalueringsfunktion
Evalueringen af meddelelserne udføres derefter i funktionen "processOutput". QProcess senderProcess opfanger alle meddelelser fra alle nmcli kommandoer - ikke kun dem fra overvågningskommandoen.
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 en anden nmcli proces
Hvis du nu starter en anden nmcli-kommando med funktionen nedenfor, opfanges outputtet af ovenstående overvågningsfunktion og kan derefter evalueres og behandles yderligere i "outputProcess".
Denne funktion skifter til en eksisterende netværksforbindelse og starter den. Det er vigtigt ikke at inkludere en "set_wifi_process->waitForReadyRead ()" eller "set_wifi_process->waitForFinished()", for så blokeres outputtet af alle meddelelser, indtil denne proces er afsluttet.
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()));
}
Linjen "connect(set_wifi_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()))" videresender outputmeddelelserne fra denne proces tilbage til "processOutput" og kan evalueres der igen på et centralt sted. </:code3:>