LabVIEWForum.de - Verarbeiten von ESC-Sequenzen (VT100)

LabVIEWForum.de

Normale Version: Verarbeiten von ESC-Sequenzen (VT100)
Du siehst gerade eine vereinfachte Darstellung unserer Inhalte. Normale Ansicht mit richtiger Formatierung.
Seiten: 1 2 3
Hallo zusammen,

es gibt doch immer wieder noch Elektroniken, die eine serielle Kommunikation mit Escape-Sequenzen anbieten.
Die Kommunikation ist keine Problem und ich schreibe die Zeichen dann einfach in eine Tabelle.
Das funktioniert soweit...

Wenn ich aber auch die Formatierung (z.B. Farbe oder invertiert) berücksichtigen will, muss ich ja mit einem
Eigenschaftsknoten das jeweilige Tabellenfeld ändern.
Und das geht viel zu langsam! Confused

Wie könnte ich das Sub-Vi "AsciiToTable" beschleunigen?

Gruß
Nominas
Hallo Nominas,

du weist schon, dass sich Bilder nicht Debuggen lassen. Weil du die Bilder jedoch aus LabVIEW heraus im PNG Format gespeichert hast (da ist dann der Code mit dabei), geht es gerade noch. Umständlich ist es dennoch.

Auf Details will ich jetzt gar nicht eingehen, denn da gibt es noch so manch Seltames in deinen VIs und so manche Verbesserungsmöglichkeit. Wenn du so etwas machst, wie mit der Tabelle, dann musst du für das VI mit der Tabelle vorher das Property "Defer Panel Updates" setzen und anschließend wieder zurücksetzen. Das verhindert, dass die Tabelle nach jeder kleinen Änderung komplett aktualisiert wird.
Hallo Martin,

das tut mir leid, an debuggen hatte ich gar nicht gedacht.
Eher, dass jemand den Code anschaut und irgendetwas dummes sieht, was ich selber die ganze Zeit übersehe.
(Du hast sowas ja angedeutet... Blush)

Vielen Dank für den Tipp mit "Defer Panel Updates"!
Jetzt dauert das SubVI nur noch Sekunden und nicht mehr Minuten.

Leider läuft der Buffer immer noch voll.

Vielleicht hast Du (oder jemand andres) noch eine Idee?

Ich hab' auch extra aufgeräumt! Rolleyes


Gruß
Nominas
Hallo Nominas,

Zitat:Leider läuft der Buffer immer noch voll.
Vielleicht hast Du (oder jemand andres) noch eine Idee?
Bei solchen "buffer overflow"-Problemen gibt es eine "einfache" Option: man baut sich einen Zwischenpuffer ein!

Also:
1.- Den seriellen Port so schnell wie möglich bedienen. (Tipp: verzichte nach Möglichkeit auf BytesAtPort, siehe auch hier.)
2. Daten in einen Zwischenbuffer packen: es bietet sich eine Queue an.
3. Daten auswerten und in eine Repräsentation umsortieren, die sich für die Table-Anzeige anbietet.
4a. Und jetzt der wichtigste Punkt: die Table nicht "andauernd" neu zeichnen, sondern nur 5-10× pro Sekunde!
4b. Und die Table natürlich nicht in der gleichen Schleife updaten, in der du die serielle Schnittstelle bedienen willst!

Andere Anmerkungen:
- FP-Elemente, die mit dem ConnPane verbunden sind, sollten nicht in irgendwelchen Strukturen versteckt werden. (Beispiel: Anzeige-Referenz und "read buffer" im subVI.)
- Die Panel-Referenz muss man nicht in jeder Iteration erneut auslesen, das kann man einmal vor der Schleife erledigen.
- In der Case-Struktur, in der du die Befehle für dein VT100 erstellt: man kann natürlich aus einem U8 ein Array of U8 bauen und das dann in einen String wandeln. Man kann aber auch eine String-Konstante in \-Code- (oder Hex-)-Anzeige verwenden und einfach den gewünschten Wert eintippen…
- Zu Punkt 2: du liest anscheinend "nur" Text und Farbinformationen aus. Damit könntest du den Zwischenbuffer als 2D-Array of strings und 2D-Array of color(s) (oder als 2D-Array of cluster[string color(s)] ) organisieren…
Hallo Nominas,

die meisten Zellen der Tabelle sind schwarz auf weiß. Korrekt?

- Wozu also jedes mal jede Zelle einzeln aktualisieren. Aktualisiere alle Zellen der Tabelle zunächst mit schwarz auf Weiß und dann änderst du nur noch die Zellen, welche davon abweichen.

- Die Texte zunächst in ein Array schreiben (du kennst die Größe des Arrays) und dann nicht per PropertyNode schreiben, sondern direkt mit dem Terminal verbinden.

- Das Parsen der Escape-Sequenzen optimieren.
Vielen Dank für eure Mühe!

Aber das bringt mich noch nicht wirklich weiter...
Entweder ich verstehe irgendwas nicht oder ich habe die Problematik nicht gut genug erklärt.
Da ich am Ersten nichts ändern kann, probiere ich es mit dem Zweiten.

Das µC-System sendet mir den Aufbau einer Anzeige. Einmal die komplette Seite mit Texten, danach nur bestimmte Werte und Status-Meldungen.
Seite: \1B[2J\1B[1;70f13.12.2022\1B[7m\1B[2;10fMesswert-Uebersicht\1B[0m\1B[5;5fTemperatur\s1\s-\sxx.x\s\B0C\r\n\s\s\s\sTemperatur\s2\s-\sxx.x\s\B0C\1B[5;30fDruck\s\s\s\s\s\s1\s-\sxxxx\smbar\1B[6;30fDruck\s\s\s\s\s\s2\s-\sxxxx\smbar\1B[10;5f\1B[0mKein\sFehler
Werte: \1B[5;20f12.3\1B[6;20f21.5\1B[5;45f\s123\1B[6;45f2345\1B[10;5f\1B[7mFehler\s0815\1B[0m

Nach meinem Verständnis muss man dann alle Elemente (ESC-Sequenzen) durchgehen und schauen, was zu tun ist.
Dafür bilde ich ein Array, anhand von \1B (=ESC), prüfe, ob das Element einen bekannten String enthält und führe den jeweiligen Befehl aus.
[2J = Anzeige löschen
[1;70f13.12.2022 = gehe an Zeile 1, Spalte 70 und schreibe ab dort die Zeichen 13.12.2022
[7m = ab jetzt soll die Anzeige invertiert erfolgen (hier könnte auch direkt Text folgen)
[2;10fMesswert-Uebersicht = gehe an Zeile 2, Spalte 10 und schreibe ab dort die Zeichen Messwert-Uebersicht
[0m = ab jetzt soll die Anzeige normal sein
[5;5fTemperatur 1 - xx.x °C\r\n Temperatur 2 - xx.x °C = wie oben, aber hier sind auch noch CarriageReturn \r und LineFeed \n dabei, die entsprechend interpretiert werden müssen.

Damit man das nachvollziehen kann, habe ich das vi "VT100-Beispiel" angehängt. Mit dem Button "Fehler" kann man die Anzeige eines Fehlers, in invertierter Anzeige, simulieren.

Nach meinem Verständnis ergeben sich daraus folgende Eckpunkte:
- Man muss diese Tabelle nehmen, denn die Befehle können an beliebige Koordinaten springen. Z.B. bei den Messwerten. Bei einem 2D-Array habe ich nicht die Formatierung einzelner Zellen.
- Man muss die Anzeige-Texte Zeichen für Zeichen interpretieren, denn bei CR oder LF muss kein Zeichen dargestellt, sondern die aktuelle Koordinate geändert werden
- Man muss sich merken, welche Formatierungs- oder Anzeige-Vorgaben gerade gelten, denn die können beim nächsten Mal an derselben Stelle anders sein

(In meiner aktuellen Anwendung habe ich natürlich viel mehr Text bzw. Befehle.)


Zu euren Vorschlägen und Anregungen

1.- Den seriellen Port so schnell wie möglich bedienen. (Tipp: verzichte nach Möglichkeit auf BytesAtPort, siehe auch hier.) und 2. Daten in einen Zwischenbuffer packen: es bietet sich eine Queue an.
>> Wenn ich das Lesen des Ports und die Auswertung in zwei Schleifen mit Queue aufteile ist die Auswertung doch immer noch zu langsam, nur dass sich die Strings in der Queue sammeln

3. Daten auswerten und in eine Repräsentation umsortieren, die sich für die Table-Anzeige anbietet.
>> Da ich ja glaube, dass ich in jedes zu bearbeitende Feld gehen muss, um Inhalt und Formatierung zu setzen, weiß ich nicht, wie das aussehen könnte...

4a. Und jetzt der wichtigste Punkt: die Table nicht "andauernd" neu zeichnen, sondern nur 5-10× pro Sekunde!
>> Mit einer realistischen Menge Inhalt, dauert das Parsen so lange, dass "gefühlt" nur einmal pro Sekunde aktualisiert wird...

4b. Und die Table natürlich nicht in der gleichen Schleife updaten, in der du die serielle Schnittstelle bedienen willst!
>> Check

- Wozu also jedes mal jede Zelle einzeln aktualisieren. Aktualisiere alle Zellen der Tabelle zunächst mit schwarz auf Weiß und dann änderst du nur noch die Zellen, welche davon abweichen.
>> Da die Formatierung bei jeder Aktualisierung anders sein kann, siehe Fehler, muss ich das doch jedes mal aktualisieren.

- Die Texte zunächst in ein Array schreiben (du kennst die Größe des Arrays) und dann nicht per PropertyNode schreiben, sondern direkt mit dem Terminal verbinden.
>> Dann müsste ich einmal die Inhalte und dann noch die Formatierung durchgehen. Da sehe ich den Vorteil nicht.

- Das Parsen der Escape-Sequenzen optimieren.
>> Das ist eigentlich meine Ausgangsfrage. Vielleicht wird es mit dem Beispiel und der obigen Beschreibung deutlicher, aber ich selber sehe halt keine bessere Möglichkeit.


Andere Anmerkungen:
- FP-Elemente, die mit dem ConnPane verbunden sind, sollten nicht in irgendwelchen Strukturen versteckt werden. (Beispiel: Anzeige-Referenz und "read buffer" im subVI.)
- Die Panel-Referenz muss man nicht in jeder Iteration erneut auslesen, das kann man einmal vor der Schleife erledigen.
>> Zu den beiden Punkten: Ich bin der Meinung, dass LabVIEW bei jedem Tunnel erstmal eine Kopie der Daten macht. Ist dieser Gedanke schon falsch?
>> Denn deshalb habe ich gedacht, dass ich die FP-Elemente in den NoError-Case schiebe, dann spare ich eine Daten-Kopie und im Error-Case werden sie definitiv nicht benötigt.
>> Und mit dem Gedanken habe ich auch die Panel-Referenz in der Schleife...

- In der Case-Struktur, in der du die Befehle für dein VT100 erstellt: man kann natürlich aus einem U8 ein Array of U8 bauen und das dann in einen String wandeln. Man kann aber auch eine String-Konstante in \-Code- (oder Hex-)-Anzeige verwenden und einfach den gewünschten Wert eintippen…
>> Ich muss ja aus dem String, der evtl. nach einem Befehl kommt ein Array machen, siehe oben. Daher verstehe ich diese Anmerkung nicht...

- Zu Punkt 2: du liest anscheinend "nur" Text und Farbinformationen aus. Damit könntest du den Zwischenbuffer als 2D-Array of strings und 2D-Array of color(s) (oder als 2D-Array of cluster[string color(s)] ) organisieren…
>> Da das Lesen vom seriellen Port schnell genug geht, könnte man das in der Schleife aufteilen. Aber ich kann mir grad nicht vorstellen, wie ich die Koordinaten handhaben soll...
>> Außerdem gibt es ja noch andere Befehle, bzw. dann CSI-Sequenzen. In meiner Anwendung mindestens noch "2J" für das Löschen der Anzeige.
>> Und für anderen µC-Elektroniken könnte man das ja ergänzen.


Es tut mir leid, vielleicht hätte ich die Aufgabe von Anfang an ausführlicher erklären sollen.
Aber wenn man sich länger mit einem Thema beschäftig, glaubt man gerne, dass das ja jedem klar ist...

Nochmals vielen Dank für eure Zeit!

Gruß
Nominas
Hallo Nominas,

Zitat:Aber das bringt mich noch nicht wirklich weiter...
Entweder ich verstehe irgendwas nicht oder ich habe die Problematik nicht gut genug erklärt.
Was ich vorgeschlagen habe, läuft auf das hier hinaus: trenne die Verwaltung der Daten von der Anzeige auf dem Frontpanel!

Damit erreichst du:
- du kannst schnell genug Daten vom seriellen Port lesen
- du kannst die Daten auswerten und in einem Array zwischenspeichern: da dies NICHT auf dem Frontpanel angezeigt wird, ist das auch sehr schnell
- für die eigentliche Anzeige liest du "immer mal wieder" (z.B. 5mal die Sekunde) diesen Zwischenspeicher aus und aktualisierst die Anzeige entsprechend - in einer entkoppelten/parallelen Schleife...

Noch ein Tipp:
Nicht immer muss die Art, wie man Daten zwischenspeichert, exakt die gleiche sein, wie man diese Daten dann auf dem FP darstellt. Auch hier hat man die Möglichkeit, sich jeweils "optimale" Datenstrukturen zu überlegen: siehe meinen Vorschlag zu einem 2D-Array of Cluster of [String, Farbe(n)]…

Zitat:- Man muss diese Tabelle nehmen, denn die Befehle können an beliebige Koordinaten springen. Z.B. bei den Messwerten. Bei einem 2D-Array habe ich nicht die Formatierung einzelner Zellen.
- Man muss die Anzeige-Texte Zeichen für Zeichen interpretieren, denn bei CR oder LF muss kein Zeichen dargestellt, sondern die aktuelle Koordinate geändert werden
- Man muss sich merken, welche Formatierungs- oder Anzeige-Vorgaben gerade gelten, denn die können beim nächsten Mal an derselben Stelle anders sein
Ich hoffe, mein Ansatz ist jetzt klarer: der Zwischenpuffer nimmt alle diese Angaben entgegen (Text,Farben).
Über die Indizes der Elemente im 2D-Array kannst du sehr einfach die Koordinaten abbilden.
Und nochmal: dieser Zwischenbuffer (Array of…) ist NICHT die Anzeige auf dem FP!

Zitat:>> Wenn ich das Lesen des Ports und die Auswertung in zwei Schleifen mit Queue aufteile ist die Auswertung doch immer noch zu langsam, nur dass sich die Strings in der Queue sammeln
Das ist sehr wahrscheinlich nur deshalb zu langsam, weil du jedesmal die Tabelle neu zeichnen willst - was einfach Irrsinn ist!
Niemand ist in der Lage, mit mehr als 24Hz Bilder wahrzunehmen - und Text in einer Daten-Tabelle sinnvoll zu erfassen, benötigt mehr Zeit als das "Ansehen" von Bildern eines Kinofilms…

Zitat:>> Zu den beiden Punkten: Ich bin der Meinung, dass LabVIEW bei jedem Tunnel erstmal eine Kopie der Daten macht. Ist dieser Gedanke schon falsch?
Ja.

Zitat:>> Ich muss ja aus dem String, der evtl. nach einem Befehl kommt ein Array machen, siehe oben. Daher verstehe ich diese Anmerkung nicht...
Schau mal hier:
[attachment=62456]
Die Stringkonstante finde ich einfacher als eine U8-Konstante mit zwei weiteren Funktionen, um exakt den gleichen Wert zu erzeugen…
(Macht dein VI nicht schneller, aber mMn besser lesbar.)

Sowas hier ist ist auch sehr ineffizient:
[attachment=62457]
Wenn nach einer Formatierung noch 20 Zeichen Text kommen, dann wird dieser Text Zeichen für Zeichen einzeln in die Tabelle eingetragen - und die Tabelle für jedes Zeichen einem Update unterzogen!
Warum schickst du den Text nicht mit einmal in die passende Position in der Tabelle!?

Deshalb nochmal mein Vorschlag: trenne die Auswertung und (Zwischen-)Speicherung der Daten von der Darstellung auf dem FP!
Hallo zusammen,

ich habe den Anmerkungen von Gerd nicht wirklich etwas hinzuzufügen.

Nur zwei Dinge:
Das Parsen zu optimieren ist jetzt noch nicht notwendig. Das bringt sicherlich auch noch ein bisschen was. Es ist aber sicherlich kein großer Sprung nach vorne.

Du gehst mit der Tabelle ja jetzt schon Kompromisse ein. Sind da nicht vielleicht andere Kompromisse auch möglich?
Falls du diesbezüglich offen bist, dann könntest du doch auch einen String nehmen. Das kommt einem Terminal doch viel näher und der einzige Kompromiss den du dann machen musst, ist die Hintergrundfarbe. Das geht beim String nicht. Das könntest du durch Fettschrift, Unterstreichen (oder bestimmte Farben) ersetzen. Der Vorteil bei einem String wäre, dass das IMHO deutlich schneller gehen müsste als ständig jede einzelne Zelle einer Tabelle einzustellen. Außerdem lässt sich das auch lesbar umsetzen, so wie das z.B. bei dem X-Control Markup String (https://lvs-tools.co.uk/software/utiliti...-library/) gemacht wird. Wenn du diesen X-Control nimmst, dann müsstest du nicht viel mehr machen, als deine Escape Sequenzen zu übersetzen und bist schon fertig. - ja ok ... da hängt noch ein bisschen Kleinkram mit dran und dann geht es auch nicht sooo schnell mit der Umsetzung Dodgy
Hallo zusammen,

es wird Zeit für ein Update Smile

Zunächst grundsätzliches:
Ich bin der Meinung, dass die Tabelle die sinnvollste Möglichkeit ist, um so ein VT100-Terminal darzustellen.
Zum einen wegen der Koordinaten, zum anderen wegen der Formatierung.
Daher ist es auch notwendig das Array zu bilden und Zeichen für Zeichen in die Tabelle einzutragen.
Durch "Defer Panel Updates" (siehe Tipp von Martin in #2) wird die Tabelle dabei nicht mit jedem Zeichen aktualisiert.

Gerd:
Zitat:...trenne die Verwaltung der Daten von der Anzeige auf dem Frontpanel!
Martin:
Zitat:- Wozu also jedes mal jede Zelle einzeln aktualisieren.

Mit diesen Hinweisen kam ich nach einigen Versuchen auf ein Array mit Clustern [Char, Graphic, Changed].
Damit werden nur die Tabellen-Felder welche sich geändert haben aktualisiert.

Jetzt bin ich aber auf das Problem mit dem Error 1604 gestoßen:
https://forums.ni.com/t5/LabVIEW/Error-C...176/page/2
Auch nachdem ich den Fehler einfach ignoriert habe, funktioniert das DropDown "Comand" nicht richtig.

Ich will das nunmal nicht mit einem reinen String machen, aber Martin hatte etwas von X-Control geschrieben,
daher habe ich mir das mal angeschaut...
Das hat so gut funktioniert, dass ich letztendlich die zweite Schleife wieder weggelassen habe.
Im xControl steckt aber auch eine Schleife, vielleicht wird diese parallel ausgeführt...


Das Projekt ist längst noch nicht ausgereift. Es ist nur für meine spezielle Anwendung erstellt und getestet.
Aber ich hoffe, das jemand darauf aufbauen kann und seine Weiterentwicklung hier wieder veröffentlicht.


Bei der Elektronik, die ich hier habe, sendet ein USB-Baustein ca. alle 500ms genau 3968 Bytes.
Deswegen habe ich doch noch das "Bytes at port" verwendet und warte 0,2s, wenn keine Bytes anstehen.
Außerdem werden ESC- (bzw. CSI-) Sequenzen abgeschnitten, darum dieses Suchen nach dem letzten "[\1B".


Da hier mehrere Ideen zusammen gekommen sind und meine Version noch nicht fertig ist,
markiere ich mal keinen Beitrag als Lösung.


Vielen Dank an Martin und Gerd!


Gruß
Nominas
Hallo Nominas,

Zitat:Bei der Elektronik, die ich hier habe, sendet ein USB-Baustein ca. alle 500ms genau 3968 Bytes.
Deswegen habe ich doch noch das "Bytes at port" verwendet und warte 0,2s, wenn keine Bytes anstehen.
Warum liest du dann nicht einfach 3968 Bytes mit einem Timeout von 600ms? Dann brauchst du kein BytesAtPort und keine Wartezeit…

Zitat:Außerdem werden ESC- (bzw. CSI-) Sequenzen abgeschnitten, darum dieses Suchen nach dem letzten "[\1B".
Aus Gründen der Code-Lesbarkeit solltest du, wann immer nicht der Default-Displaystyle genutzt wird, diesen Displaystyle auch anzeigen lassen.
In deinem Fall also für diese ganzen ESC-Sequencen…

Zitat:Mit diesen Hinweisen kam ich nach einigen Versuchen auf ein Array mit Clustern [Char, Graphic, Changed].
Du hast das Array um eine Zeile/Spalte zu groß definiert…
Und es fehlt die Typdefinition für diesen Cluster!

Wieso wird die Cursorposition mit dem Formatstring "%f" geparst? Das sollten doch Integer-Werte sein - du erzwingst ja selbst die Integer-Darstellung am ScanFromString!?

Zitat:Ich bin der Meinung, dass die Tabelle die sinnvollste Möglichkeit ist, um so ein VT100-Terminal darzustellen.
Zum einen wegen der Koordinaten, zum anderen wegen der Formatierung.
Um einen Terminal-Screen zu simulieren, könnte man auch eine einfache String-Anzeige verwenden und dort einen non-proportional font (wie z.B. Consolas) verwenden!
Dann den String-Indicator einfach auf 24×80 Zeichen in der Größe einstellen…
Seiten: 1 2 3
Referenz-URLs