logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

filtrowanie danych z rs232-odbiór w programie w delphi

bartsam 08 Sty 2006 14:41 2751 4
REKLAMA
  • #1 2159220
    bartsam
    Poziom 15  
    Posty: 75
    Pomógł: 7
    Ocena: 3
    Witam wszystkich.
    Mam urządzenie (AVR+bascom) które wysyła po rs'ie dane o ogólnie rzecz mówiąc stanie czujników, podzespołów.
    Dane odbierane w terminalu wyglądają jak poniżej:
    W:sterownik v5.14 - 05.12.11
    U:6.12V
    I:42.0
    H:poziomo - poza zakresem
    h:0
    V:pionowo - poza zakresem
    v:0
    V:pionowo - w górę
    v:9
    U:6.15V
    I:43.75
    V:pionowo - w górę
    v:79
    U:6.15V
    I:39.5
    H:poziomo - w lewo
    h:19
    V:pionowo - w górę - dalej nie można
    v:80
    U:6.15V
    I:42.75
    H:poziomo - w lewo - dalej nie można
    h:84
    V:pionowo - w górę
    v:79
    U:6.20V
    I:zbyt ciemno
    U:6.20V
    U:6.20V
    R:PC - w lewo
    U:6.23V
    

    przy czym kolejność i częstość występowania komunikatów i ich długość są różne (od 7..8 znaków do 37).
    Używając terminala wszystko działa jak należy (pod kątem takiej komunikacji pisałem program dla AVRa), natomiast sprawa komplikuje
    się przy odfiltrowaniu poszczególnych linijek w Delphi 7.

    Przykładowo fragment:
    (używam CreateFile)
    var
        bufodb:array[0..95] of char;
        bufW: array[0..47] of char;
        bufI: array[0..47] of char;
    
    {prędkość= 9600bps, 1 startu, 1 stopu, brak parzystości
    czyli 1200Bps , dla użytkownika 1200 * 8/10 = 960 (na 1 sek)
    wątek lub timer co 100 ms
    }
    begin
    zeroMemory(@bufodb,SizeOf(bufodb));
    ClearCommError(hCOM,Errors,@ComStat);
    
    if ComStat.cbInQue>0 then
       ReadFile(hCOM,bufodb,ComStat.cbInQue,nrRead,nil);
    
     repeat
        if bufodb[i]='W' then begin
          i:=i+2;
          a:=0;
          repeat
            bufW[a]:=bufodb[i];
            inc(a);
            inc(i);
            until bufodb[i]=#13;
          end;
    
        if bufodb[i]='I' then begin
          i:=i+2;
          a:=0;
          repeat
            bufI[a]:=bufodb[i];
            inc(a);
            inc(i);
            until bufodb[i]=#13;
          end;
    
    form1.Label1.Caption:=bufW;
    form1.Label2.Caption:=bufI;
    end;



    Ale .. jest to do kitu. Taki algorytm nie daje sobie dobrze rady z rozdzielaniem komunikatów, także zawiesza się po kilku, kilkunastu sekundach. Próbowałem wielu kombinacji pętli if + repeat lub while..
    .. i nie umiem sobie z tym poradzić, by każda linijka wysyłanego komunikatu trafiała do odpowiedniej label.

    Może lepszym rozwiązaniem byłoby zapełnianie bufora do znaku wystąpienia EOL (13,10) i czyszczenie w celu ponownego zapełniania?
    - a między czasie przetworzenie bufora..
    Tylko jak to zrobić.. ?

    A może trzeba do rzeczy podejść zupełnie inaczej - przepisać bascomowy program tak, aby dopiero po zapytaniu (z PC) AVR odpowiadał - wysyłając odp. komunikat.. Tak chyba byłoby najprościej, ale wtedy
    stracę możliwość ciągłego odbierania w terminalu.

    Wolałbym filtrować te dane na wejściu - tylko nie wiem jak się zabrać za to od innej strony ?

    Liczę na Wasze doświadczenie i prosiłbym o skierowanie mojego toku myślenia w prawidłowym kierunku.
  • REKLAMA
  • #2 2159636
    Sam Sung
    Poziom 33  
    Posty: 2023
    Pomógł: 227
    Ocena: 602
    Pomysł z czytaniem całych linijek do jednego bufora wydaje mi się dobry. Po wczytaniu całej linijki program może sprawdzić pierwszy znak i na jego podstawie przekazać zawartość bufora do odpowiedniej procedury obsługi. Po powrocie wczytywałby następną linijkę. Jeśli w czasie obsługi jednego komunikatu przyjdzie następny, to chyba nic się nie stanie - po prostu poczeka sobie chwilę w buforze systemu.
  • REKLAMA
  • #3 2159654
    elektryk
    Poziom 42  
    Posty: 11029
    Pomógł: 439
    Ocena: 241
    A nie prościej wysyłać informacje zakodowaną binarnie, z jakimś nagłówkiem oznaczającym początek danych.
  • REKLAMA
  • Pomocny post
    #4 2174558
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    Napisałem parser dla twoich wymagań.. cały bufor to jeden string.. dane zamiast pobierać z rs232 to są pobierane z pliku (dla lepszego testu w porcjach o losowym rozmiarze od 1 do 10 bajtów) i przekazywane do parsera... jedyne co musisz zmienić to źródło danych przekazywanych do AppendToBuf oraz samą obsługe odpowiedzi -> zamienić writeln na co kolwiek innego..

    program Parser;
    // Parser by BoskiDialer
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils;
    
    // ----------------------------------------
    // -- Wlasciwy kod parsera.. wystarczy
    // -- go skopiowac do docelowej aplikacji
    // ----------------------------------------
    
    const CrLf = #13#10;
    var vBuffer: string;
    
    procedure AppendToBuf(AData: string);
    
      function Localize(ABuff,AString: String): integer;
      var vI: integer;
      begin
        result := -1;
        for vI := 1 to length(ABuff)-length(AString)+1 do
          if CompareMem(@ABuff[vI],@AString[1],length(AString)) then
          begin
            result := vI;
            break;
          end;
      end;
    
    var vLine: string;
        vLineLength: integer;
        vCmd: Byte;
    begin
      vBuffer := vBuffer + AData;
      repeat
        vLineLength := Localize(vBuffer,CrLf)-1;    // zlokalizowanie konca linii (crlf)
        if vLineLength<=0 then                      // jesli nieznaleziono konca..
          exit;
    
        vLine := Copy(vBuffer,1,vLineLength);       // pobranie pierwszej linii
        Delete(vBuffer,1,vLineLength+length(CrLf)); // usuniecie tej linii z buforu
    
        // sprawdzenie poprawnosci linii.....
        if not ((vLineLength >= 3) and (vLine[2] = ':')) then
          continue;
        vCmd := ord(vLine[1]);                      // pobranie typu odpowiedzi
        Delete(vLine,1,2);                          // usuniecie pierwszych 2 znakow
    
        // obsluga odpowiedzi
        case vCmd of
          ord('W'): Writeln('Wersja softu:   '+vLine);
          ord('U'): Writeln('Napiecie:       '+vLine);
          ord('I'): Writeln('Natezenie:      '+vLine);
          ord('H'): Writeln('Horizontal Str: '+vLine);
          ord('h'): Writeln('Horizontal Int: '+vLine);
          ord('V'): Writeln('Vertical Str:   '+vLine);
          ord('v'): Writeln('Vertical Int:   '+vLine);
          ord('R'): Writeln('PC:             '+vLine);
          else
                    Writeln('Nieznane:       '+chr(vCmd)+' - '+vLine);
        end;
      until false;
    end;
    
    // ----------------------------------------
    // -- Koniec wlasciwego kodu parsera..
    // ----------------------------------------
    
    var vFile: file;
        vBuff: string;
        vReadLen: integer;
        vReaded: integer;
    begin
      // skojazenie zmiennej z plikiem
      AssignFile(vFile,'text.txt');
      try
        // otwarcie do odczytu
        Reset(vFile,1);
        randomize;
        repeat
          vReadLen := random(10)+1;    // ile by tu bajtow odczytac.... ?
          SetLength(vBuff,vReadLen);   // zmiana rozmiaru buforu tymczasowego
          BlockRead(vFile,vBuff[1],vReadLen,vReaded); // odczyt do buforu tymczasowego
          SetLength(vBuff,vReaded);    // jesli nieodczytano calosci to utnij bufor
          AppendToBuf(vBuff);          // przetworzenie
        until vReaded < vReadLen;      // jesli nieodczytano calosci to koncz
      finally
        CloseFile(vFile);              // zamknij plik
      end;
      AppendToBuf(CrLf);               // dodanie crlf do konca buforu
      writeln('Zakonczono przeczesywanie pliku');
      readln;
    end.
    

    plik text.txt zawiera "kod" (te linie z terminala) z pierwszego postu

    ;--- dodane:
    >parser
    Wersja softu:   sterownik v5.14 - 05.12.11
    Napiecie:       6.12V
    Natezenie:      42.0
    Horizontal Str: poziomo - poza zakresem
    Horizontal Int: 0
    Vertical Str:   pionowo - poza zakresem
    Vertical Int:   0
    Vertical Str:   pionowo - w gore
    Vertical Int:   9
    Napiecie:       6.15V
    Natezenie:      43.75
    Vertical Str:   pionowo - w gore
    Vertical Int:   79
    Napiecie:       6.15V
    Natezenie:      39.5
    Horizontal Str: poziomo - w lewo
    Horizontal Int: 19
    Vertical Str:   pionowo - w gore - dalej nie mozna
    Vertical Int:   80
    Napiecie:       6.15V
    Natezenie:      42.75
    Horizontal Str: poziomo - w lewo - dalej nie mozna
    Horizontal Int: 84
    Vertical Str:   pionowo - w gore
    Vertical Int:   79
    Napiecie:       6.20V
    Natezenie:      zbyt ciemno
    Napiecie:       6.20V
    Napiecie:       6.20V
    PC:             PC - w lewo
    Napiecie:       6.23V
    Zakonczono przeczesywanie pliku
    
    
    >
  • #5 2178844
    bartsam
    Poziom 15  
    Posty: 75
    Pomógł: 7
    Ocena: 3
    BoskiDialer, jesteś niesamowity, ogromną niespodziankę mi zrobiłeś :) :D Dziękuję bardzo za taki trud i za poświęcony czas. Zajmę się kodem, mam nadzieję, że rozwiąże problem. W każdym razie będę analizować do skutku. pozdrawiam!:)
REKLAMA