Hi, ich möchte mit QT ein Programm schreiben, wo eine serielle Schnittstelle (FTDI) angesteuert wird. An der Schnittstelle hängt ein kleiner Mikrocontroller. Entweder sendet man ein Kommando, dann wird das ausgeführt. Das ist kein Problem. Oder aber man sendet eine Anfrage, diese wird dann beantwortet. Leider weiss man im Voraus nicht, wie viele Bytes man bekommen wird, aber man bekommt immer eine Zeile, am Schluss ist ein CR LF. Ich habe das jetzt so implementiert, dass meine eigene Klasse, welche die Funktionalität implementieren soll, einen Slot hat (rdData), und mittels connect() habe ich das readyRead()-Signal der QSerialPort-Klasse mit meiner eigenen Klasse verheiratet. Im Handler lese ich dann einfach alle verfügbaren Daten vom QSerialPort aus und hänge sie zu Testzwecken einfach man an einer Textbox an. Das funktioniert. Wie jedoch könnte ich eine kluge Synchronisation erzielen? wenn ich eine Anfrage absende, möchte ich eigentlich gern auf die Antwort warten, ohne jedoch, dass mir das GUI einfriert. Wie macht man das?
Es gibt im Grunde zwei Möglichkeiten; entweder du generierst ein Signal, wenn die Antwort da ist und machst dann weiter mit dem Programmablauf (in dem Slot, der von dem Signal aufgerufen wird), oder du lagerst die ganze Kommunikationslogik in einen Thread aus, der dann synchron einfach auf die Antwort warten kann. Was besser ist, hängt vom Anwendungsfall ab.
OK. Mir ist im Grunde nicht so wichtig, wie es genau implementiert wird. Ich glaube aber, die Lösung mit dem separaten Thread, welcher auf die Antwort warten kann, scheint mir gut. Könntest du mir ein Beispiel dazu zeigen? ich bin da definitiv nicht fit genug in C++. Wie warte ich in dem Thread z.B. elegant auf das Ende der Antwort? Das Ende kann ja, wie gesagt, am CR LF erkannt werden.
Das bekommst man ja "kostenlos" mit wenn man Signale & Slots dafür nutzt. Hier z.B. gibt deine Interface-Klasse ein Signal bei abgeschlossenem Lese-Vorgang aus, das du nutzen kannst. Geht man gegen diese asynchrone Design-Philosophie kann man das ganze einfach in einen eigenen Thread packen. Die Kommunikations-Funktionen dürfen dann selbstverständlich nicht im GUI-Thread aufgerufen werden, sonst gewinnt man dadurch nichts.
Hatte die vorausgehenden Antworten nicht gesehen... Dein ready-read bzw. das synchrone equivalent gibt dir so viel gerade im Puffer liegt. Ich würde die Daten intern in einen Puffer schreiben und dort nach dem CRLF suchen, dann entsprechend parsen und das Signal senden oder welche andere Synchronisierungsstruktur auch immer genutzt wird anstoßen und die Daten aus dem internen Puffer löschen. Damit ist das Problem der in mehrere Blöcke zerlegten Transaktionen gelöst, bzw. auch dass mehrere Transaktionen in einem Block gelesen werden.
Die Frage ist halt was du mit der Antwort machen willst. In einem Fenster anzeigen? Dann reicht definitiv ein weiteres Signal, dass eine Antwort angekommen ist, was du z.B. gegebenerfalls aus der Funktion emittierst die nach readyRead() aufgerufen wird. Je nach Ergebnis der Abfrage einen weiteren Befehl schicken, und das mit komplexer Struktur? Dann kann ein separater Thread sinnvoll sein, weil die asynchrone Logik sonst leicht in Spaghetti-Code ausartet.
Die Antwort beinhaltet zB. ein paar Messwerte, welche in einem Diagramm angezeigt werden sollen. Aber das hängt von der zuvor gesendeten Anfrage ab. Je nach dem, was halt angefragt wird, muss die Antwort anders interpretiert werden! :-/ Sollte man evtl. unterschiedliche Handler für das readyRead-Signal benutzen und diese jeweils mit connect() anders verbinden?
:
Bearbeitet durch User
Tobias P. schrieb: > Sollte man evtl. unterschiedliche Handler für das readyRead-Signal > benutzen und diese jeweils mit connect() anders verbinden? Ne, ich denke das gibt ein Chaos, weil du dann ständig connect/disconnect machen musst. Wenn du die Antworten anhand der Antwort unterscheiden kannst (z.B. Byte im Header was sagt auf welchen Befehl das eine Antwort ist), unterscheide einfach anhand dessen in dem Handler was passiert, andernfalls kannst du dir ja in einer Membervariablen merken, auf welchen Befehl du als nächstes eine Antwort erwartest.
Tobias P. schrieb: > OK. Mir ist im Grunde nicht so wichtig, wie es genau implementiert wird. > Ich glaube aber, die Lösung mit dem separaten Thread, welcher auf die > Antwort warten kann, scheint mir gut. Ich sehe da wenig Sinn drin. Denn dann hast du das gleiche Problem wieder, nur nicht bei der Kommunikation zwischen seriellem Port und GUI, sondern zwischen deinem Empfänger-Thread und dem GUI-Thread. Threads bringen hier eigentlich nur was, wenn die Verarbeitung sehr aufwändig ist und viel CPU-Zeit braucht. Das würde dann ohne Threads die GUI ausbremsen. CuTee schrieb: > Hatte die vorausgehenden Antworten nicht gesehen... > Dein ready-read bzw. das synchrone equivalent gibt dir so viel gerade im > Puffer liegt. Nein, readyRead() gibt gar nichts. Es sagt nur, dass neue Daten angekommen sind. > Ich würde die Daten intern in einen Puffer schreiben und > dort nach dem CRLF suchen, dann entsprechend parsen Oder man benutzt im mit readyRead() verbundenen Slot einfach die Funktion canReadLine(), um zu ermitteln, ob eine ganze Zeile vorhanden ist, und wenn ja, liest man sie mit readLine(). Das macht man dann in einer Schleife, für den Fall, dass inzwischen mehr als eine Zeile angekommen ist.
Hi, Vielleicht habe ich was überlesen ? Es es ist doch ganz einfach: Im Slot readyRead die Funktion canReadLine checken. Wenn wahr: readLine nutzen. Keine Threads, keine Sync-Probleme... cu N2
Ja, klar geht das. Ich hab das jetzt ja auch so implementiert. ABER ich möchte doch folgendes tun: wenn auf einen Button geklickt wird, soll eine entsprechende Anfrage gesendet werden und die Antwort soll in einem Textfeld angezeigt werden. Pseudo-Beispiel:
1 | void Button_click() |
2 | {
|
3 | QString response = serialport->send_query("version?"); |
4 | ui->textBox1->setText(response); |
5 | }
|
das geht natürlich mit dem komplett asynchronen Ansatz mit dem SIGNAL nicht wirklich. Die Frage war, ob man es irgendwie so implementieren kann, dass meine Klasse intern irgendwie das Signal verwendet, nach aussen hin aber so aussieht, als ob es nicht so wäre. (Mir fehlt da im Moment der richtige Fachbegriff für. Wie nennt man das? synchron vs. asynchron? bei Windows war das glaub ich irgendwie 'overlapped IO').
Tobias P. schrieb: > ABER ich > möchte doch folgendes tun: wenn auf einen Button geklickt wird, soll > eine entsprechende Anfrage gesendet werden und die Antwort soll in einem > Textfeld angezeigt werden. Warum möchtest du das so machen? Also auf die Antwort warten? Kannst du nicht einfach die Antwort plotten, wenn sie kommt? Was machst du wenn gar keine Antwort kommt?
Eine Möglichkeit, Code ungefähr so zu schreiben, ist mit einem Callback, also du übergibst dem send_query eine Funktion als Argument, die aufgerufen wird wenn's fertig ist. Das kannst du noch als Lambda machen und dann hast du ungefähr das.
1 | void send_query(const QByteArray& query, std::function<void(QByteArray)> onCompleted); |
2 | ... |
3 | send_query("version?", [this](const QByteArray& reply) { |
4 | ui->button->setText(QString::fromUtf8(reply)); |
5 | }); |
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.