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

ComPort - jak poprawnie odbierać dane przy użyciu zdarzenia EvRx80Full?

Wilku 14 Mar 2007 23:13 3066 15
REKLAMA
  • #1 3679506
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    Powróciłem ostatnio do ComPort i znów jest pare niejasności. O ile nadawanie danych w dowolnej ilości nie nastręcza problemów to już odbiór tak. Sterownik wysyła mi do kompa stałą liczbę danych - 14B. Ustawiłem odpowiedni bufor i postanowiłem skorzystać ze zdarzenia EvRx80Full. Niestety jakoś mi niedziała. Mimo poprawnego połączenia i wysyłania 14B, zdarzenie wogóle nie jest generwane (oczywiście jest ono włączone). Czy ktoś wogóle korzystał z tego zdarzenia? Mogę oczywiście kazać sterownikowi wysłać dane, zaczekać odpowiedni czas i odczytać to co w buforze. Ale myślałem że po to mam zdarzenia......
    Może ktoś opisze algorytm jak odbiera większe ilości bajtów.
  • REKLAMA
  • #2 3680016
    MirekCz
    Poziom 35  
    Posty: 2220
    Pomógł: 330
    Ocena: 62
    Najprostsza metoda:

    1.Periodic Timer co 10ms

    2.W timerze ściągasz dostępne dane z coma do bufora i sprawdzasz czy jest już cała paczka (tzn. czy pojawił się znak specjalny i/lub przyszła określona liczba znaków jak w twoim przypadku)

    3.Jeżeli bufor spełnia warunek z pkt.2 , to odpowiednią jego część oddzielasz (np. pierwsze 14 bajtów) i przesyłasz do funkcji analizującej dane

    Program spokojnie sobie śmiga między pkt2. i 3, co zabiera minimalny czas pracy procesora a zapewnia szybką odpowiedź na sygnały z zewnątrz (przeważnie <20ms, jednak jeżeli w tle pracuje jakiś bardzo czasochłonny proces to timer może być wywoływany nie co 10ms, a co kilkaset ms... nie polecam grać w intensywne gry w międzyczasie lub co gorsza robić defragmentacji dysku)
  • #3 3680045
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    W podobnym stylu mam to zrobione. Jednak wierci mnie to że są do tego odpowiednie zdarzenia, ale nie chcą mi działać. Napełnienie bufora w 80% byłoby tu idealne. Jednak jak tego użyć?
  • #4 3701059
    mirekk36
    Poziom 42  
    Posty: 9195
    Pomógł: 964
    Ocena: 2289
    Witam,

    oczywiście, że lepiej korzystać ze zdarzeń jeśli komponent takimi dysponuje ;) ... proponuję ci przyjrzeć się w tym przypadku zdarzeniu OnRxChar

    piszesz, że twój sterownik wysyła stałą liczbę bajtów - ale czy wysyła jakiś znak początku i końca tej ramki danych?

    tak więc w tym zdarzeniu możesz próbować rozpoznawać początek i koniec ramki oraz kiedy się ona "złoży" w całość a następnie przekazywać daną ramkę z tego zdarzenia właśnie do jakiejś specjalnej procedury, która to dalej obrabia.

    Możesz jednak jeszcze bardziej sobie ułatwić pracę jeśli korzystasz z tego fajnego pakieciku komponentów, otóż zapewne w tej zakładce poza ComPort widzisz jeszcze coś takiego jak ComDataPacket - postaw sobie na formę również ten komponent, podłącz go do ComPort'u - zdefiniuj w "propertisach" StastString oraz StopString (jeśli ta twoja ramka jak pytałem wcześniej wysyła jakieś znaki początku i końca) a następnie skorzystaj ze zdarzenia tegoż komponentu o nazwie OnPacket .... możesz jeszcze wyłączyć aby z pobranego pakietu automatycznie usuwane były znaki startu i stopu - dzięki czemu zawsze gdy wleci twoja ramka w całości to precyzyjnie bez praktycznie pisania dodatkowych linii kodu odbędzie się zdarzenie, które "wypluje" dokładnie "ciało" tejże ramki ;) ... poezja w takim przypadku (żadnych Timerów itp) - do szybkich i podręcznych rozwiązań nadaje się SUPER

    powodzenia
  • #5 3716057
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    Właśnie się za to zabrałem. Ramki na końcu nie posiadają znaku stopu tylko sumę kontrolną, która jak wiadomo się zmienia. Czy bez tego to ruszy - nic nie wpisałem w 'stopstring'. Jak do tej pory nie bardzo chce mi to działać. Nie jestem pewien co wpisać w StrartString. Ramka rozpoczyna się 0x24 - $ w ASCII.
  • REKLAMA
  • #6 3716085
    mirekk36
    Poziom 42  
    Posty: 9195
    Pomógł: 964
    Ocena: 2289
    no tak jeśli nie zdefiniujesz w ComDataPacket znaku końca to on nie zadziała poprawnie, nie ma co się dziwić. Rozumiem, że nawet po zakończeniu ramki i tej sumie kontrolnej nie leci żaden chociaż #13 (ENTER) ???

    jeśli nie masz jasnego końca ramki a za to zawsze równą liczbę bajtów to skorzystaj z pierwszego pomysłu jaki ci podsunąłem czyli zdarzenia OnRxChar w ComPort i tam czekaj na początek czyli # następnie masz counter przylatujących bajtów więc sam "ręcznie" możesz składać sobie przylatującą ramkę i i ją obsługiwać dodatkową procedurą lub wysyłać za pomocą PostMessage własny komunikat i tam tam tę ramkę obsługiwać - też będzie działać wyśmienicie

    powodzenia
  • #7 3716150
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    #13 na końcu nie ma. W takim przypadku pozostaje onrxchar. Nie rozumiem tylko tego z PostMessage. Do tej pory po wykryciu początku transmisji włączałem timer, aby dane spokojnie doleciały i dopiero w timerze (zazwyczaj 15ms) je analizowałem. Dla bezpieczeństwa można jeszcze rozbudować nagłówek (sterownik podłączony do PC własnej konstrukcji).
  • #8 3716589
    mirekk36
    Poziom 42  
    Posty: 9195
    Pomógł: 964
    Ocena: 2289
    Cytat:
    const
    MyMESSAGE = WM_USER + 100;


    type
    TMyMESSAGE = record
    Msg: Cardinal;
    Message: PChar;
    Length: Longint;
    Result: Longint;
    end;


    // tą sekcję umiść w miejscu gdzie masz definicje zmiennych i procedur TForm1
    // typu public i private - czyli dodajemy jeszcze sekcję protected
    protected
    procedure HandleMyMessage(var Msg: TMyMESSAGE); message MyMESSAGE;



    var
    FrameLength: Integer = tu podajesz dlugosc swojej ramki w bajtach
    FMessage: String = '';
    Znacznik: Boolean = False;



    procedure TForm1.HandleMyMessage(var Msg: TMyMESSAGE);
    var
    AMsg: String;
    begin
    AMsg := Msg.Message;
    StrDispose(Msg.Message);
    AMsg := trim(AMsg);

    // ..... Tu masz w AMsg swoją całą kompletną ramkę, która zaczyna się od #
    // a kończy się sumą kontrolną i ma długość ustaloną w zmiennej FrameLength
    // - możesz więc dalej już robić sobie z nią w tym miejscu co ci się
    // żywnie podoba

    end;



    procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
    var
    c: char;
    i: Integer;
    buffer: String;
    PStr: PChar;
    begin
    SetLength(buffer, Count);
    ComPort.ReadStr(buffer, Count);

    for i := 1 to length(buffer) do begin
    c := buffer[i];

    // zawsze gdy pojawi się znak # zacznie się odliczanie i "składania" znaków ramki
    if (c = '#') and (not Znacznik) then Znacznik := True else Znacznik := False;

    if Znacznik then begin
    FMessage := FMessage + c;
    if Length(FMessage) = FrameLength then begin
    PStr := StrNew(PChar(FMessage));
    PostMessage(Handle, MyMESSAGE, Integer(PStr), 0);

    end else begin
    FMessage := '';
    Znacznik := False;
    end;
    end;
    end;


    mam nadzieję, że poradzisz sobie z tym gdzie umieścić i jak poszczególne procedury, typy, stałe i deklaracje zmiennych u siebie w swoim programie.
    Napisałem to wszystko tak tylko ogólnie i być może jeszcze gdzieś może siedzieć jakiś złośliwy "bug" ;) .... ale przynajmniej widzisz jaką drogą można iść w tym kierunku.

    Oczywiście kod wkleiłem tutaj i dlatego nie ma żadnych wcięć tak jak się należy robić - przez to może być to troszkę mało czytelne szczególnie tam gdzie są if-y itp ..... ale cóż zrobić - ja też się brzydzę jak patrzę na to co zrobił z tym HTML ;)

    pozdrawiam
  • REKLAMA
  • #9 3719026
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    Dzięki za kod. Popróbuje z tym co podesłałeś. Nie będę jeszcze zamykał tematu, może ktoś o coś jeszcze zapyta albo dołoży swoje rozwiązania.
  • #10 6447990
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    Po 2 latach powrót do tematu, z tym że są drobne różnice. Dane przychodzą w losowych odstępach czasu. Ramka składa się z 13B. Zadanie programu to odebrać ramkę, porównać dane i odpowiednio odpowiedzieć. Jako że urządzeń zewnętrznych może być kilkaset to i ramek może przyjść sporo. Transmisji pośredniczy układ który kolejkuje te ramki i do PC wysyłane są bez zakłóceń ikolizji. Mam problem z poprawnym odczytywaniem ramek z bufora komponentu. Np. zdarzenie OnRxChar jest generowane dwukrotnie w ciągu przesyłania jednej ramki, więc średnio się nadaje. Ma ktoś może sposób na tego typu transmisję? Może jest jakoś dostępna liczba bajtów w buforze? Wtedy można użyć timera do odczytu kolejnych bajtów.
  • #11 6448398
    Konto nie istnieje
    Konto nie istnieje  
  • #12 6450655
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    W tym momencie robię tak że w timerze co 10ms sprawdzam wartość InputCount i jeśli >0 odczytuje jeden byte : comport1.Read(buf13,1); i wsuwam do mojego bufora, który następnie 'obrabiam'. Problem w tym, że zmienna InputCount nie wydaje się być automatycznie zmniejszana po moim odczycie jednego bajtu. Objawia się to tym że program odsyła kilka ramek zamiast jednej. Rozwiązaniem byłoby mieć informację ile bajtów jest w buforze. Reszt już jakoś poleci. Ze zdarzeniem OnRxChar dałem sobie spokój....
  • #13 6450891
    Konto nie istnieje
    Konto nie istnieje  
  • REKLAMA
  • #14 6451724
    daborowski
    Poziom 12  
    Posty: 30
    Ocena: 7
    Właśnie pracuję nad podobnym problemem i wydaje się, że idealnym jest komponent ComDataPacket i zdarzenie OnPacket

    
    procedure TForm1.ComDataPacket1Packet(Sender: TObject; const Str: string);
    begin
    Memo2.Lines.Add(Str);
    end;


    i ustawienie własności "Size" w ComDataPacket na pożądaną wielkość w tym wypadku 13, nie trzeba podawać wartości stringu początkowego i końcowego, co 13 bajtow zostanie zwrocona zawartosc bufora do zmiennej Str
  • #15 6451840
    Wilku
    Poziom 17  
    Posty: 330
    Pomógł: 5
    Zrobiłem to jednak na timerze. Cyklicznie odczytuje po 13B z bufora comport'a, wsuwam do swojego bufora, jeśli zidentyfikuję ramkę to ją obrabiam i koniec timera. Kolejne zdarzenie od Timera i kolejne dane do obróbki, o ile są oczywiście w buforze comport'a. Problem leżał w kilku drobnych błędach, które wyjaśnił analizator stanów, oraz w ograniczeniu urządzenia pośredniczącego. Wybrany sposób, jak do tej pory wydaje mi się dobry, bo mimo szczerych chęci nie zawiesiłem systemu nad którym pracuje.
  • #16 6451877
    Konto nie istnieje
    Konto nie istnieje  

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy problemów z odbiorem danych przez komponent ComPort przy użyciu zdarzenia EvRx80Full, które nie generuje się mimo poprawnego połączenia i wysyłania stałej liczby bajtów (14B). Proponowane rozwiązania obejmują stosowanie zdarzenia OnRxChar do ręcznego składania ramek danych, zwłaszcza gdy ramki nie mają wyraźnego znaku końca, a jedynie sumę kontrolną. Zalecane jest monitorowanie początku ramki (np. znak 0x24 - '$') i liczenie bajtów do pełnej ramki. Alternatywnie, można użyć komponentu ComDataPacket z ustawioną właściwością Size na długość ramki (np. 13B) i obsługą zdarzenia OnPacket, co pozwala na automatyczne odbieranie pełnych pakietów bez konieczności definiowania znaków startu i stopu. W przypadku transmisji z wieloma urządzeniami i losowymi odstępami czasowymi, stosowanie timera do cyklicznego odczytu bufora ComPort (np. co 10 ms) i przetwarzania dostępnych danych jest skuteczne. Problemy z wartością InputCount, która nie zmniejsza się po odczycie bajtów, mogą powodować wielokrotne przetwarzanie tych samych danych. Zaleca się stosowanie pętli w zdarzeniu OnRxChar do odczytu całych ramek oraz ewentualne przesuwanie bufora w przypadku pojawienia się błędnych danych. Połączenie zdarzenia OnRxChar z timerem może zwiększyć niezawodność odbioru. Podsumowując, najlepszą praktyką jest ręczne zarządzanie buforem i składanie ramek na podstawie znaku startu i długości ramki lub wykorzystanie ComDataPacket z ustawioną wielkością pakietu, co upraszcza obsługę transmisji szeregowej bez znaków stopu.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA