Überlangen String analysieren

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    Aufgrund technischer Veränderungen ist der Mailverkehr innerhalb des Forums (Private Nachrichten) nur noch eingeschränkt möglich. Die Einschränkung ist notwendig, um zusätzliche Betriebskosten für das Forum zu vermeiden. Näheres zu den Hintergründen im Thread "Aktuelles zum Forum".Wir bitten um Verständnis.

    Hinweis kann nach Kenntnisnahme deaktiviert werden!

    • Überlangen String analysieren

      Hallo allerseits,

      ich stehe gerade vor dem Problem, daß mir ein externes Gerät auf die Statusanfrage per UART einen bis zu 450 Zeichen langen String zurückgibt, in dem ich in der 2. Hälfte nach einer gewissen Zeichenfolge suchen muß, um den darauf folgenden numerischen Wert zur weiteren Verarbeitung auszulesen.

      Der UART-Puffer ist mit 255 auf das Maximum gesetzt und weil ich unverzüglich nach der Anfrage mit dem Einlesen beginne, kann ich die derzeit 404 Zeichen vollständig einlesen und in einen entsprechenden String konvertieren und ausdrucken.
      Das verwüstet zwar die Stacks, aber soweit ist die Kommunikation mit dem Gerät erstmal gesichert hergestellt.

      Jetzt suche ich eine Idee, wie ich beim Inkey(#2) erst dann anfange, einen String anzulegen, wenn relevante Daten anliegen. Leider ist die genaue Position vom Betriebszustand des externen Geräts abhängig und ich muß z.B. auf "FuellSt" filtern , um den darauf folgenden numerischen Wert einzulesen. Einzelne Zeichen kann ich ja testen und entsprechend verwerfen, aber wie prüfe ich auf eine kurze Zeichenfolge? Hier mal ein Beispielstring, den ich aus der Ausgabe des AVR auf ein Terminal herauskopiert habe:

      Tst 19.2C Tint 19.3C Twt 18.0C Tdcdc 16.2C Tpcb 20.2C Tres -50.9C Ubat 12.05V Uaus 12.05V Uklemm 12.05V Ust 0.53V Uzell 25mV IntSteiU 0.000e+00n Iaus 0.00A Ist 0.01A FuellSt 0.0n LastError 31n Error 31n StBtrb 28.84h SysBtrb 2818.97h PcStack 0n PcMedien 0n AdW 0n ResF 0n DHV 120.00n DC-DC 0.00% ULF1 0.0V Tbtl -51.0C Uref 2.51V Ubb 0.00V FS 0n SysTime 2010-09-14, 07:39:05

      Hab jetzt überall mal ein bißchen gesucht und geblättert, aber niemanden mit einem ähnlichen Problem gefunden. Workaround wäre, die ersten 2xx Zeichen blind zu verwerfen, dann eine bestimmte Menge einzulesen, die den relevanten Teil enthält und dann den String zu schließen und mit den üblichen Mitteln zu durchsuchen und zu extrahieren. Oder die doppelten 0x20 zwischen den Textblöcken zu zählen und daraus den Startpunkt des String zu generieren.

      Hat jemand eine bessere Idee?

      Vielen Dank und viele Grüße

      Frank
    • Zur Analyse des Problemes fehlen etliche Zusammenhänge, Informationen. Z.B. mit welchem Tempo wird übertragen, wie lange sind die Pausen zwischen den Übertragungen?
      Woran ist eindeutig der Beginn einer Übertragung erkennbar? Trifft das auszuwertende Teil immer an der gleichen Informationlänge ein?

      Ist es zwingend notwendig das Ganze per Bascom erledigen zu wollen, oder könntest Du Bascom nur den wesentlichen Teil zur Analyse anbieten? Unwichtiges also auslagern
      in z.B. Phyton3 und von diesem eine Stringdatei für Bascom generieren lassen. Das wirft mithin die Frage auf, wie zeitlich dringend ist denn die Analyse nach jeweiligem senden?

      Ich zumindest wäre so flexibel, wenn es mir zeitlich sehr wichtig und dringend wäre, einen modernen Saleae Logikanalyser mit entsprechend eingestellter Auswertung zu verwenden.
      Das ist oftmals schneller als ein Programm zu schreiben, bzw auf Programmfehlersuche zu gehen.

      Zurück zu deinem Programm. Auffallend für mich ist, das Dich interessierende Teilstück fängt ausschließlich mit Fuell an. Folglich würde genügen jedes eintreffende Zeichen auf "F" zu prüfen, den zu definierenden Puffer auf die zu erwartende Länge plus 4 zu definieren und nur das in den Puffer zu schaufeln, was mit ASCII F anfängt.

      Entschuldige meine Meinung betreffend ungenügenden Zusammenhängen. Ist nicht als Kritik gedacht, sondern aufmerksam machen.
    • Moin,

      erstmal DANKE für den Hinweis mit dem ersten 'F' im String! Das ist mir vorher nicht aufgefallen und bringt natürlich eine große Vereinfachung.
      Der eingehende String, wie oben aufgeführt, ist bedingt durch die realen numerischen Werte längenvariabel (0.00 zu 54.50 z.B.) und ich weiß derzeit nicht,
      ob z.B. im Fehlerfall neue Werte mit dazu kommen.
      Und weil es nicht so einfach sein soll, gibt die neuere Generation der externen Geräte auch einen veränderten String mit z.T. verkürzten Präfixen aus.
      In dem Fall sollte es aber gut gehen, weil 'FuellSt' zu 'FS' wird.
      Ansonsten ist das Problem ausschließlich mit und innerhalb eines ATMega2560 zu lösen, der über ein externes Signal das nächste externe Gerät abfragt und
      die Auswertung über eine weitere Schnittstelle überträgt. Das alles in 9600Bd. Das geht also auch nur mit BASCOM zu lösen.

      MfG

      Frank
    • F64098 wrote:

      UART-Puffer ist mit 255 auf das Maximum gesetzt
      Du kannst auch ein Byte-Array nehmen mit einer Länge von >=450 Zeichen und den String dort direkt im UART Interrupt einlesen.

      bitlogger wrote:

      jedes eintreffende Zeichen auf "F" zu prüfen
      und dann den gewünschten String prüfen

      Tempstr = "Fuell"
      L = Len(Tempstr)
      For I = 1 To Arraylänge
      Teststr = Mid(Eingangsarray , i , L)
      If Teststr = Tempstr Then
      Datenstr = Mid(Eingangsarray , (i+l+x) , 3)
      Füllstand = Val(Datenstring)

      usw

      Gruß, Michael
    • Michael wrote:

      Du kannst auch ein Byte-Array nehmen mit einer Länge von >=450 Zeichen und den String dort direkt im UART Interrupt einlesen.

      Diese Interrupt-Routine müßte ich mir aber selbst schreiben, Du meinst jetzt nicht die (interne?) Routine, die schon den in der Config gesetzten Puffer befüllt?

      Ansonsten auch Dir vielen Dank für den Schnipsel zum Auswerten, das hätte ich komplizierter geschrieben.


      MfG

      Frank
    • Die interne stoppt leider wenn der Puffer voll ist, jedoch könnte er von der Main laufend auf den richtigen Parameter gescannt werden. Dann müsste er nicht so groß sein.

      F64098 wrote:

      Diese Interrupt-Routine müßte ich mir aber selbst schreiben,
      Das wäre auch keine 'Raketentechnik' ;) z.B.

      Source Code

      1. On Urxc Rx_isr
      2. Enable Urxc
      3. Enable Interrupts
      4. Dim Rx_char As Byte 'Eingangszeichen
      5. Dim Rx_ready As Byte 'Übertragung komplett Flag
      6. Dim Rx_len As Word 'Länge des Eingangs
      7. Const Rx_maxlen = 500 'Puffergröße
      8. Dim Rx_array(rx_maxlen + 1) As Byte
      9. Dim Rx_string As String * Rx_maxlen At Rx_array Overlay
      10. Rx_isr:
      11. Rx_char = UDR
      12. if Rx_char = 10 then 'Lf = Ende Übertragung
      13. Rx_redy = 1
      14. Rx_Char = 0 '0 zum Stringabschluss
      15. else
      16. if Rx_len < Rx_maxlen then 'Überlauf abfangen
      17. incr Rx_len
      18. end if
      19. end if
      20. Rx_String(Rx_len) = Rx_char
      21. Return
      Display All
      Bei nur 9.6kBaud hätte die Routine genug Zeit den Eingang selber zu untersuchen. (Falls er nicht mit 128kHz getaktet wird :D )
      Das wäre viel ressourcenschonender als der riesen Puffer und die zeitraubende Suchroutine.

      The post was edited 1 time, last by Pluto25 ().

    • Bei 450 Zeichen mit 9600 Baud dauert das eine kleine Weile, wenn man auf alle Zeichen wartet (ca. 0,47s).

      Man könnte das auch quasi parallel zur Hauptschleife, also ohne Verzögerung, einlesen.
      Man liest dabei einfach vom Empfangsbuffer zeichenweise ein und entscheidet, ob es wichtig ist.

      Da benötigt man dann kaum weiteren Speicher und kann weiterhin den gepufferten UART-Empfang verwenden.
      Auch das Array/Speicher/String mit extra 450 Zeichenkann entfallen.
      Lediglich 2 Strings mit ca. 10 Zeichen länge werden benötigt, um den Namen und den Wert Zwischenzuspeichern.

      Das hängt aber von deinen Anforderungen ab, wie du es haben möchtest.
    • Erstmal vielen Dank für eure Mühe und die passenden Codeschnipsel!
      Das hilft mir auf jeden Fall weiter.

      Ich überlege nur die ganze Zeit, wie man üblicherweise aus durchlaufenden Datenströmen relevante Daten ausfischt.
      Alte Pager haben mit mickrigen µC mit 1MHz Takt aus 2400Bd-Streams ihre Daten gefischt und mußten jedes relevante 32bit-Paket auf mehrere
      Übereinstimmungen mit programmierten Rufadressen prüfen und haben das auch hinbekommen.
      Der ATMega ist mit seinen 16MHz deutlich zu schnell für die 9600Bd. Ohne ein waitms1 vor dem Inkey lutscht er den Puffer schneller leer, als er nachgefüllt wird.

      Idealerweise könnte ich den Stream am UART auch in einzelne Pakete zerlegen und mit jedem einlaufenden Wert gleich eine passende Variable füllen.
      Nur leider interessieren mich 90% der Daten überhaupt nicht bzw. sind redundant, von daher wäre das nur eine Fleißübung, wenn ich mal zuviel Zeit haben sollte.

      Ich werde das, wie von Mitch vorgeschlagen, "on the fly" aus dem Stream ausfiltern und sofort in Variablen konvertieren. Mich interessieren der Füllstand und evtl. Fehlercodes.
      Spannung und Strom messe ich selbst und der ganze Rest der Infos ist mir (erstmal) buggy.

      Der erste eintreffende Buchstabe ist bei den 2 verschiedenen Gerätevarianten unterschiedlich und anhand dessen könnte ich auch gleich in die passende Routine verzweigen und
      müßte den Gerätetyp garnicht erst irgendwo in der Konfig festschreiben.


      MfG

      Frank

      Post by F64098 ().

      This post was deleted by stefanhamburg: doppelt ().
    • Die Usart Routine könnte die Bezeichnung und den Wert in je eine Variable speichern und der Main mitteilen das ein Wert angekommen ist. Dann braucht die Main kein Wait und kann den Wert einordnen sobald er da ist. So lassen sich beliebig viele Werte einsammeln. Die selbe Variable kann auch mit dem Wert nach 'FuellSt' und 'ST' gefüllt werden falls sich der Sender ändert. Dem Mega ist egal ob er einen oder zwanzig Werte sammelt. Bleibt nur die Frage welche Werte könnten mal interessant sein?
      z.B. Hier sammelt ein Mega8 13 Werte von dehnen er selber zwei braucht, zwei muß ich wissen und der Rest ist nur 'nice to have' Obwohl ich die nur ca 2x pro Monat anschaue, würde ich sie doch vermissen wenn er sie nicht kennen würde.
      Die 'Fleißarbeit' ist nur ein weiterer Case Eintrag
      z.B.

      Source Code

      1. select case Rx_name
      2. case is = "ST"
      3. Fuel= Rx_data
      4. case is = "FuellSt"
      5. Fuel= Rx_data
      6. case is ="Ubat"
      7. batt = Rx_data

      The post was edited 1 time, last by Pluto25 ().

    • Der Ansatz könnte sein, erst mal nach dem ersten passenden Zeichen zu Suchen.
      Wurde das gefunden, muss danach sofort das zweite passende Zeichen kommen, danach das dritte usw., wenn das nicht zusammenkommt wird das ganze abgebrochen und bisherige Erkenntnisse verworfen und man sucht wieder nach dem ersten passenden Zeichen.
      Mit dieser Methode kann man auch sehr lange Strings einlesen, muss die aber nicht erst komplett einlesen um diese zu bewerten und die Prüfstrings können sehr klein sein.
      Man muss nur Zeitmäßig so schnell sein dass der "kleine" Eingangsbuffer" nicht überlaufen kann und in der Folge dann Zeichen verloren gehen können.
    • Da braucht man dann aber Unterschleifen, da immer bündelweise Werte kommen, die mit dem gleichen Großbuchstaben anfangen (z.B. T... oder U...), aber dann verschiedene Werte darstellen.
      Ein reiner Abbruch würde zum Verlust von Pakten führen. Eine Art Sync ist die Tatsache, daß ein neuer Wert mit 0x20 0x20 beginnt und nach dem Präfix ein weiteres 0x20 kommt,
      bevor der numerische Wert übertragen wird.
      Es ist auf jeden Fall so, daß der Stream erstmal angefordert werden muß. Ich schicke ein 'LOG' und bekomme dann unverzüglich Antwort.
      Der Startzeitpunkt ist also bekannt und ich befinde mich da schon in einer entsprechenden Subroutine.

      MfG

      Frank
    • F64098 wrote:

      da immer bündelweise Werte kommen, die mit dem gleichen Großbuchstaben anfangen
      Dann muss man halt die Auswertung verfeinern.
      Ich kenne deinen Ergebnisstring ja jetzt nicht ich denke , dass der bis auf die Länge ident im Aufbau ist (vor allem was die Abfolge der einzelnen Parameter betrifft wie z.B. LOGUB9.34T120.34T28.05WERT56 oder eben
      LOGUB12.5T19.0T28.65WERT5
      Also die Parameternamen sich immer ident in der jeweiligen Stringlänge (kann ja auch länger sein als jetzt nur zwei Zeichenwie im Besipiel) als auch in deren Bezeichnung, einzig die Parameterwerte sind in variabler Länge.
      Im Beispiel prüfe ich zuerst ob der Teilstring "UB" auftaucht und lese dann solange bis dass "T" kommt und übergebe das der ersten "Zwischenwariable".
      Dann lese ich das nächste Zeichen ein welches ja entsprechend dem Beispiel "1" sein muss und lese dann wieder solange bis wieder "T" kommt, speichere den ermittelten Wert in eine weitere "Zwischenvariable" und kontrolliere das nächste Zeichen auf "2" und so weiter.
      Wenn du die Ausgabe selbst anstoßen kannst sollte das ja problemlos ablaufen selbst wenn es idente Parameternamen geben sollte, denn dann muss ja zumindest deren Abfolge ident sein
      Ist dieser Ergebnisstring eine einzige "Wurst" oder gäbe es auch eindeutige Trennzeichen zwischen den einzelnen Parametern?
      Kannst du mal so einen Ergebnisstring zeigen (mit normalen Terminalprogramm anstoßen)?
    • Hi, probiere doch mal einen parser. Ich hab das mal mit der Auswertung eines Zeichenstroms aus einem gps modul gemacht
      GPS - NMEA Daten Auswertung
      Hier wird auch auf den Startstring gewartet, wenn der kommt, werden die darauf folgenden Zeichen weiter verarbeitet. Geht gut.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • F64098 wrote:

      Derzeit bin ich nur an FuellSt und Error
      Dann wie ich schon kurz angerissen habe, einfach Zeichen um Zeichen einlesen und zuerst das "F" suchen (das wäre zwar sogar zufällig der erste gesuchte Parameter aber sicherheitshalber prüft man da weiter), wird das gefunden die Suche fortsetzen mit "u", ist dass das unmittelbar nächste Zeichen weitersuchen nach "e" oder wenn nicht eben alles verwerfen und wieder von vorne beginnen solange Daten einlangen usw. kommst du so erfolgreich bis zum "t" von "FuellSt"dann hast du den Parameter "FuellSt" gefunden und liest die nächsten Zeichen bis zum "n" ein (und merkst dir diese in einer entsprechenden Variable). Sollte das erwartete "n" nicht eintreffen brichst du z.B. nach zehn Zeichen oder dem Ende der Übertragung (was halt zuerst eintritt) ab, verwirfst den gesamten Datenstrom (solange lesen bis keine Daten mehr kommen, Buffer leeren usw.) und forderst die Daten nochmals (genauso wenn du den Parameternamen nicht findest). Das ist dann aber schon die Fehlerbehandlung.