Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

ObjectPascal/Delphi/Lazarus - Utrata danych przy transmisji przy użyciu TComPort

dawid.barracuda 02 Nov 2016 16:20 1737 34
  • #1
    dawid.barracuda
    Level 13  
    Szanowni Forumowicze,
    pracuję sobie nad przesyłaniem danych z różnych czujników za pomocą FTDI (w module MMusb232 firmy Propox) do komputera, czyli wykorzystuję wirtualny port COM. Do FTDI dane wysyła atmega32a.
    Zebrane dane odbieram i obrabiam programem pisanym w ObjectPascal w Lazarusie. Do odbioru wykorzystuję komponent TComPort z biblioteki CPortLib.

    Przechodząc do meritum - dane, które odbieram w programie nie zawsze są kompletne. Powinienem odebrać 14 bajtów, a zdarza się, że odbiorę ich mniej, albo przyjdą jakieś śmieci. Transmisja jako taka przebiega bez zakłóceń, gdyż w Realtermie widzę wszystkie dane jak należy, gdy przez TComPort część danych czasami jest gubiona. Co istotne, najczęściej dane do programu przychodzą kompletne, gubienie ich jest sporadyczne. Niepokoi mnie to jednak, gdyż Realterm pokazuje, że wszystko jest w porządku. Nie wiem teraz, czy jest to wina moja czy komponentu.

    Oto procedura jaką odbieram dane z COMa:
    Code: delphi
    Log in, to see the code


    Czym takie działanie może być spowodowane? Proszę uprzejmie o wskazówki i pozdrawiam.
  • Helpful post
    #2
    sq9etc
    Level 12  
    Nie znam tego komponentu. Ja w Delphi używam TapdCOMPort z pakietu Async Professional.
    Domyślam się, że procedura TMainWindow.SerialRxChar jest procedurą obsługi zdarzenia nadejścia danych do bufora komponentu. Jeśli tak jest, powinieneś bufor RxBuffer zadeklarować na zewnątrz tej procedury i w każdym wywołaniu TMainWindow.SerialRxChar doklejać do niego kolejne przychodzące dane. Może się zdarzyć tak, że pełna paczka danych, której oczekujesz zostanie rozbita na dwa zdarzenia i wtedy Twoje podejście może doprowadzić do tego, że pewnych danych nie odbierzesz prawidłowo.
  • Helpful post
    #3
    arnoldziq
    Moderator of Programming
    Dokładnie jak kolega wspomniał powyżej.
    Poza tym, dodam tylko od siebie, że trochę kolega autor tematu namieszał z różnymi typami danych.
    W zdarzeniu nadejścia znaku ASCII ( OnRxChar ) czyta kolega dane do bufora zbudowanego z tablicy byte?
    To "niewielka" niekonsekwencja :)
    Sugeruję zdecydować się na konkretny typ danych (znaki albo bufor danych) i zastosować odpowiednie procedury odczytu nadchodzących danych.
    function ReadStr(var Str: string; Count: Integer): Integer; w zdarzeniu OnRxChar lub
    function Read(var Buffer; Count: Integer): Integer; w zdarzeniu OnRxBuf
  • #4
    dawid.barracuda
    Level 13  
    Ja tak myślałem, że coś z tym OnRxChar jest nie tak w moim przypadku :)
    Przyznam się, że nie mogłem sobie poradzić z innym zdarzeniem, bo nie mogłem znaleźć innego przykładu, aż do dzisiaj.
    Czułem od początku, że powinienem użyć albo OnRxBuf (bo odbieram bufor danych składający się z tej samej ilości bajtów, oraz zaczynający się i kończący konkretną liczbą) albo komponentu TComDataPacket i tam zdarzenia OnPacket. I w końcu udało mi się coś odebrać używając TComDataPacket.
    Sytuacja wygląda lepiej, ale dalej zdarza się, że odbieram uszkodzoną paczkę (jest ich jednak teraz znacznie mniej).

    Oto co nakodziłem, żeby obsłużyć ten komponent:
    1) Przed otwarciem portu definiuję znaki początku i końca paczki:
    Code: delphi
    Log in, to see the code


    2) Procedura odbioru OnPacket:
    Code: delphi
    Log in, to see the code

    Niepokoi mnie dalej, że odbieram dane jako string, ale bez problemu przerobiłem sobie poszczególne elementy stringa na liczby. Pytanie, czy tak jest poprawnie?
    Sugerowałem się tym, że ten komponent służy do odbierania paczek danych. Spróbuję też na OnRxBuf, ale to jak wrócę do domu :)

    Używając powyższego zauważyłem istotnie zmniejszenie się zepsutych paczek, ale dzieje się inna rzecz, mianowicie niektóre paczki jakie wyświetlam sobie na ListBoxie mają przeniesione pierwsze trzy bajty na koniec. Pokazuję i objaśniam:
    1) Prawidłowy pakiet:
    StartString:=#1 ->1 107 4 139 57 42 44 85 255 58 212 63 4<-StopString:=#4, całość to 13 bajtów.
    2) Uszkodzony pakiet:
    139 57 42 44 85 255 58 212 63 4 (1 107 4) -> początek (pierwsze 3 bajty) przeniesiony na koniec.

    Co więcej, całość działa w miarę przyzwoicie jak ustawię parametr Size obiektu DataPacket na 14, a bajtów mam 13 w całej paczce.

    Czym oba powyższe przypadki mogą być spowodowane?
  • Helpful post
    #5
    arnoldziq
    Moderator of Programming
    dawid.barracuda wrote:
    Pytanie, czy tak jest poprawnie?
    Jak działa, to pewnie że tak :)
    dawid.barracuda wrote:
    1) Prawidłowy pakiet:
    StartString:=#1 ->1 107 4 139 57 42 44 85 255 58 212 63 4<-StopString:=#4, całość to 13 bajtów.
    Wg mnie opis "Prawidłowy pakiet" to spora nadinterpretacja.
    Dla mnie "prawidłowym", to jest właśnie ten:
    dawid.barracuda wrote:
    (1 107 4)

    Marker początku się zgadza(#1) i marker końca się zgadza (#4).
    Nie widzi kolega nic dziwnego w tych danych; #1 ->1 107 => 4 <= 139 .... ?
    Dla ułatwienia zaznaczyłem strzałeczkami :P
  • #6
    dawid.barracuda
    Level 13  
    Domyślam się, że chodzi o to iż w paczce pojawił się znak końca paczki. Ale czy tego problemu nie załatwia nam określenie rozmiaru w parametrze Size komponentu ComDataPacket?
  • Helpful post
    #7
    arnoldziq
    Moderator of Programming
    dawid.barracuda wrote:
    Ale czy tego problemu nie załatwia nam określenie rozmiaru w parametrze Size komponentu ComDataPacket?

    Załóżmy, że jesteśmy tym komponentem. Mamy dane; marker start, marker koniec i rozmiar ramki.
    1. przychodzą jakieś dane
    2. czekamy na marker start
    3. marker znaleziony, więc rozpoczynamy zapis ramki.
    4. czekamy na marker końca lub/i koniec ramki
    5. przychodzi marker końca -> zamykamy ramkę i czyścimy bufor przychodzący
    6. Zostały pobrane tylko 3 bajty -> za mało, żeby wypełnić do rozmiaru ramki
    7. zaczynamy sczytywać dane od początku
    8. przychodzi marker końca, ale nie ma markera początku -> czytamy dalej
    9. rozmiar ramki rozstał wyczerpany -> wynik, jak na przykładzie nr 2 :/


    Tego komponentu można używać na 3 sposoby:
    1. dajemy tylko marker startu i końca -> jeżeli gdzieś w środku ramki pojawi się jeden lub drugi, dane idą w cholerę :/
    2. dajemy marker początku, końca i rozmiar ramki -> w przypadku wystąpienia markera początku/końca wewnątrz ramki, dane są przesuwane (opisywany przez kolegę przypadek)
    3. dajemy tylko rozmiar pakietu danych (ramki) bez markerów -> niby najlepsze rozwiązanie, ale w przypadku "zagubienia" jednego znaku w ramce danych, wszystkie kolejne pakiety są poszatkowane :/

    Która metodą wybrać i jakie ograniczenia wprowadzić pozostawiam koledze.
    Wg mnie, metoda nr 2 jest najlepsza i najpewniejsza, ale wymaga takiego przetworzenia danych przed wysyłką, żeby się markery pojawiały tam gdzie trzeba a nie w środku pakietu.


    P.S.: Trzeba to sprawdzić, ale jest jeszcze 4 możliwość -> podanie rozmiaru ramki i tylko markera startu. W ten sposób każde x bajtów, po każdym markerze będzie traktowane jako osobna ramka. Ale jak przypadek wystąpienia markera startu wewnątrz ramki wpłynie na ten komponent, to mogę tylko zgadywać.
  • #8
    dawid.barracuda
    Level 13  
    Ja również wybrałbym metodę 2., ale musiałbym coś zaczarować z ramką danych i tak ustawić markery, żeby nigdy się nie pojawiły w środku ramki. Mam taki pomysł, proszę o komentarz do niego :)
    Otóż pomyślałem, żeby zapisać dane (poza markerami, czyli 11 bajtów w środku) korzystając tylko z 7 bitów na 8 dostępnych w zmiennej jednobajtowej. Wtedy na marker mogę użyć liczb w postaci 0b10000000 i większych. Jak chciałbym to zrobić? Otóż myślałem, żeby rozciągnąć te dane przesunięciem bitowym w prawo o jedną pozycję. Będę jednak musiał wtedy zwiększyć paczkę danych, bo teraz używam 11 razy 8bit czyli 88 bitów. Po rozciągnięciu wszystkiego o jeden w prawo będę potrzebował 11 bitów więcej do zapisania danych, czyli 99 bitów -> 13 bajtów. Nie mogę przecież pozwolić na utracenie LSB każdego bajtu.
    Taka jest idea, pozostaje mi napisanie kodu na uC, który przed puszczeniem danych odpowiednio je przygotuje. Co o tym Koledzy sądzą? :)
  • #9
    arnoldziq
    Moderator of Programming
    Przepraszam, ale uważam że pomysł jest "lekko" nie trafiony :P
    Jaki sens mają wszelkiego rodzaju skomplikowane operacje "bitowe", skoro szybciej i łatwiej zakodować te dane do dwu-bajtowych liczb hexa-decymalnych? Wtedy (pomimo zwiększenia narzutu danych) mamy pełną kontrolę nad "rodzajem" wysyłanych danych a także ze znalezieniem wolnych znaków początku i końca nie powinno być problemu.
  • #10
    dawid.barracuda
    Level 13  
    Ok, przyjmuję z pokorą :D
    Ale chyba nie do końca rozumiem ideę. Mam scalać np. dwie dane jednobajtowe w jedną dwubajtową i tak wysyłać?
  • #11
    arnoldziq
    Moderator of Programming
    Nie, wręcz odwrotnie :)
    Chodzi o zakodowanie każdego bajtu informacji (0-255) do 2-bajtowego ciągu hex ($00-$FF) i wysłaniu go jako ciąg znaków.
    Np.
    Dane wyjściowe : 12,127,33,0,7
    Hex : 0C,7F,21,00,07
    Wyjście jako znaki : 0,C,7,F,2,1,0,0,0,7
    Wyjście jako bajty : 48,67,55,70,50,49,48,48,55

    Plusy
    - zawsze parzysta liczba danych
    - otrzymane znaki ASCII są zawsze z zakresu 48-70 - każda inna wartość może być użyta jako marker, suma kontrolna itd.
    Minusy
    - wysyłamy 2 razy więcej danych
  • #12
    dawid.barracuda
    Level 13  
    Ok, teraz rozumiem :)
    Ten minus w postaci podwojonej ilości przesyłanych danych mnie trochę niepokoi i zaraz wyjaśnię czemu.
    Otóż całość mojego 'projektu' to urządzenie z czujnikami, które przesyła bezprzewodowo dane za pomocą nrf24l01+ do centrali, która to jest podłączona przez USB (wirtualnego COMa) do PC. Jednorazowo mogę puścić chyba 31 bajtów nrf-em. Więc z 13 bajtów robi mi się 26 do przesłania i mam mały zapas na rozbudowę urządzenia... Ale w sumie mogę puścić dane w postaci dziesiętnej w eter, a zamienić na ascii-hex (czyli podwoić ich ilość) jak radziłeś wyżej dopiero w centrali zaraz przed puszczeniem na RS232... więc ten minus niejako mogę tak zredukować do "plusa ujemnego", a nie pełnego minusa :D
    Może trochę zboczę z tematu, ale mam też takie pytanie - otóż nrf automatycznie kończy nadawanie jak spotka 0x00 w buforze. Jakiej sztuczki księgowej mogę użyć, żeby nie dopuścić do puszczenia zera do bufora nadawczego nrf'a? To się co prawda zdarza bardzo rzadko, że pojawi się tam zero, ale jednak może się zdarzyć. Nie mogę też dodać 1 do każdego bajtu danych, gdyż jak któraś dana będzie równa 255 to mi się sypie od razu całość, bo zmienne mam typu uint8_t.
    I jeszcze jedno - czy komponenty do obsługi COMa z pakietu Async Professional są lepsze od tych z CPortLib?
  • Helpful post
    #13
    sq9etc
    Level 12  
    Jest jeszcze jeden sposób, kiedyś coś takiego implementowałem w programie, który komunikował się z pewnym modułem.
    Owszem był narzut, ale nie dwukrotny. Oczywiście w skrajnym wypadku mógł być maksymalnie dwukrotny.
    Było to tak:
    Kodowanie:
    Jeżeli dane są w zakresie 0..EF, to bierzemy je wprost.
    Jeżeli dane są z zakresu F0..FF to wstawiamy bajt specjalny o kodzie F0 i kolejny bajt, który jest negacją bajtu oryginalnego. Widać, że w przypadku bajtów z tego zakresu podwaja się nam ilość danych.

    Dekodowanie:
    Jeżeli dane są w zakresie 0..EF, to bierzemy je wprost.
    Jeżeli znajdziemy w danych F0, to go ignorujemy, natomiast bajt po nim negujemy i otrzymujemy bajt oryginalny.

    W tym podejściu widać, że bajty specjalne są z zakresu F0..FF.
    U Ciebie jest inaczej, ale ten sposób "kodowania" da się przystosować. Możesz jako bajty specjalne traktować zakres 0..F, a jako sygnalizator bajt F.
  • #14
    dawid.barracuda
    Level 13  
    Negując wartości większe od 0xEF nigdy nie zbliżę się do 0xFF więc czy są jakieś przeszkody żeby dodać offset = 1 do każdego elementu przed wysyłką danych nrf-em?

    Dodano po 1 [godziny] 18 [minuty]:

    Myślę cały czas nad tym zerem no i bolą mnie jednak te miejsca charakterystyczne. Bo co będzie jak trafię bajt 0xEF i zwiększę go o 1? Dostanę 0xF0 czyli wlecę w zakres, gdzie neguję bity. Samo w sobie to jeszcze nie jest najgorsze, bo negując drugi raz i obejmując 1 dostanę oryginał ale co wtedy ze znakiem specjalnym?
  • #15
    arnoldziq
    Moderator of Programming
    Prostota, Panowie, prostota !
    Jest bardzo dużo metod kodowania, dekodowania, itd, itp. Problem polega na tym, że każda z nich ma swoje plusy i minusy. Trzeba wybrać taką metodę, która ma więcej plusów niż minusów.
    Ale jak już wspomniałem, żadne operacje bitowe, nie są w stanie zagwarantować, że dowolny wybrany marker nie powtórzy się wewnątrz danych.
    Oczywiście wybór należy do autora projektu.
  • Helpful post
    #16
    sq9etc
    Level 12  
    dawid.barracuda wrote:
    Negując wartości większe od 0xEF nigdy nie zbliżę się do 0xFF więc czy są jakieś przeszkody żeby dodać offset = 1 do każdego elementu przed wysyłką danych nrf-em?

    Tak jest w moim przypadku.

    W Twoim cała treść zawiera się między znakami #01 (SOH) i #04 (EOT), więc musisz zapewnić żeby nie wystąpiły one we właściwych danych.
    Za sygnalizator przyjmujesz bajt #0F.
    Wszystko powyżej (#10.. #FF) puszczasz bez zmian.
    Wszystko od #00 do #0F konwertujesz w następujący sposób:
    Wstawiasz bajt #0F i dodatkowo drugi bajt jak poniżej:
    BajtZanegowany = Negacja(BajtOryginalny)

    Tak wygląda kodowanie. Natomiast w dekodowaniu robisz odwrotnie.
    Wszystko powyżej (#10.. #FF) puszczasz bez zmian.
    Gdy w danych znajdziesz #0F, to go ignorujesz, natomiast bajt występujący po nim negujesz i otrzymujesz bajt oryginalny:
    BajtOryginalny = Negacja(BajtZanegowany )

    Przykład:
    Gdy kodujesz, to jeżeli w danych będzie bajt #01, to po tej operacji zostanie on zastąpiony przez dwa bajty:
    #0F,#FE.

    Przy dekodowaniu #0F służy nam jako sygnalizator, więc go odrzucamy, a #FE negujemy otrzymując #01.
  • Helpful post
    #17
    willyvmm
    Level 29  
    Zdefiniuj sobie ramkę np:

    Nagłówek:
    1 -do 4 magic byte.
    1 bajt rozmiar. (moze być wiecej)

    Twoje dane w ilosci okreslonej poprzednio.

    Ew. Suma kontrolna. (polecam uzyc)

    Proste. Maly znany i staly narzut. Masz dodatkowo możliwość kontroli poprawnosci danych. Nie jesteś na sztywno zwiazany z pakietem danych, możesz zdefiniować timeout i odbirerac dane z kilku pakietów nrf i skleic je w ramke.
    Sprawdzone w wielu zastosowaniach.

    Keep it simle.
  • Helpful post
    #18
    krru
    Level 33  
    arnoldziq wrote:
    dawid.barracuda wrote:
    Pytanie, czy tak jest poprawnie?
    Jak działa, to pewnie że tak :)


    W przypadku oprogramowania to jest nieprawda - jedno- czy kilku-krotne poprawne zadziałanie programu o niczym takim nie świadczy.
    W szczególności transmisja danych i protokoły to dziedzina, w której trzeba starannie wszystko zaprojektować, bo trudno przetestować wszystkie przypadki, a mamy dwa komputery, które o sobie wiedzą tyle ile prześlą. Do tego błędy transmisji, czasy przesłania danych (nie zawsze połączenie szeregowe jest bezpośrednie, może iść przez modem, przez tunel po IP itp). Chociażby fakt, że nie zawsze pierwsze przychodzące dane to początek ramki - strona nadająca mogła zacząć nadawać zanim strona odbierająca zaczęła słuchać.
  • #19
    dawid.barracuda
    Level 13  
    sq9etc czemu kodujemy od #00 do #0F a nie do #04? Resztę algorytmu rozumiem :)
    willyvmm - mówisz o tym, żeby nrfem przesyłać dane w kilku pakietach a nie koniecznie w jednym tzn. włączyć nadawanie kilka razy a ramkę sklejać przed puszczeniem do PC?
    krru - pierwszy raz zajmuję się na poważnie transmisją danych i tworzeniem jakichś własnych (co prawda prostych) protokołów, a muszę to zrobić dobrze, więc każda opinia od Was na ten temat jest dla mnie niezwykle istotna :)
  • Helpful post
    #20
    willyvmm
    Level 29  
    Mniej-więcej.
    Zdefiniuj sobie protokół, ramkę danych itd co tam potrzebujesz i co pasuje do twoich danych. Nie narzucaj sobie ograniczeń których nie ma.
  • #21
    dawid.barracuda
    Level 13  
    willyvmm - piszesz o sumie kontrolnej. Wyciągam ją z czujników jeśli tylko mają taką mozliwosc. Ale czy Tobie chodzi o stworzenie sumy kontrolnej do mojej całej paczki danych?
  • Helpful post
    #22
    rb401
    Level 38  
    W kwestii pakietowania danych np. do RS232 stosunkowo niedawno pojawił się ciekawy algorytm:

    Consistent Overhead Byte Stuffing

    wyróżniającym się prostotą i stałym narzutem 1 bajtu dla pakietów mniejszych od 254B (plus 1 bajt oddzielający pakiety 0x00), zupełnie niezależnie od pierwotnej zawartości. I wynikającą z tego wszystkiego łatwą detekcją początku/końca pakietu.

    Moim zdaniem algorytm warty zainteresowania, w świetle proponowanych wcześniej tutaj metod.

    dawid.barracuda wrote:
    Może trochę zboczę z tematu, ale mam też takie pytanie - otóż nrf automatycznie kończy nadawanie jak spotka 0x00 w buforze.


    Wydaje mi się że przy algorytmie COBS ta właściwość akurat bardzo się przyda. W pakietach zakodowanych COBS jest gwarantowane że nigdy nie będzie bajtów zerowych a zero jako znacznik końca pakietu skończy nadawanie i o to chodzi (a to że to zero jest nadawane to bardzo dobrze bo druga strona wie że pakiet się skończył).
  • #23
    dawid.barracuda
    Level 13  
    rb401 - dziękuję serdecznie za tę wskazówkę :) Artykuł przeczytałem, algorytm rozumiem. W istocie załatwia mi dwa problemy:
    1) Nie martwię się transmisją zera nrf-em, a wręcz wykorzystuję tę cechę jako zaletę.
    2) Znak końca ramki - doklejam sobie to zero w centrali na koniec ramki (bo nrf zero mi utnie) i wykorzystuję zero jako StopString:=#0 w TComDataPacket.

    Zostaje jedna kwestia - znak początku ramki StartString. Wiadomo, że muszę wybrać jakąś daną, która nie pojawi mi się w paczce danych. Wobec tego, czy mogę użyć za StartString tego samego co za StopString czyli #0? Czy wtedy komponent głupieje, bo nie rozróżnia początku od końca?

    I jak jest z tą sumą kontrolną? Jak pisałem, wyciągam sumy kontrolne z czujników jeśli mają taką opcję i na tej podstawie liczę CRC, ale czy można zrobić taki numer własnego autorstwa do całej mojej ramki danych i sprawdzać jej poprawność? Choć może powinienem zadać pytanie 'jak to zrobić' bo nie chce mi się wierzyć, że na to sposobu nie ma :)
  • Helpful post
    #24
    rb401
    Level 38  
    dawid.barracuda wrote:
    Wobec tego, czy mogę użyć za StartString tego samego co za StopString czyli #0? Czy wtedy komponent głupieje, bo nie rozróżnia początku od końca?


    Możliwe.
    Ale zaglądnąłem do źródeł TComDataPacket i widzę tam że specjalnie jest obsługiwany wariant gdy StartString jest zdefiniowany jako pusty string. Wtedy wszystko co przychodzi jest traktowane jako zawartość pakietu aż do wystąpienia StopString z jego konsekwencjami. Więc może tą drogą pójść. Może taka właśnie sytuacja kiedy znaki początku i końca są takie same, jest uwzględniona w filozofii TComDataPacket.
    Ale osobiście tego nie sprawdzałem.

    Generalnie jednak mam pewne wątpliwości czy ten TComDataPacket jest w ogóle tu potrzebny. Dekodowanie COBS, które i tak trzeba zrobić, można by zrobić na bieżąco w prostej procedurce przetwarzającej przychodzące dane znak po znaku i odpowiednio rozpoznając ledwo kilka występujących w tym algorytmie stanów (start, stop, kopiowanie niezerowych danych na wyjście, odbiór bajtu "zerowego").
    Przyjmując założenie że jednostkowy przesył będzie nie większy niż 254B algorytm się znacznie upraszcza względem podanego w przykładach np. w Wikipedii.

    dawid.barracuda wrote:
    I jak jest z tą sumą kontrolną? Jak pisałem, wyciągam sumy kontrolne z czujników jeśli mają taką opcję i na tej podstawie liczę CRC


    Liczysz powiedzmy CRC i doklejasz do danych ale koniecznie robisz to przed zakodowaniem COBS. A po drugiej stronie najpierw dekodujesz COBS a później sobie sprawdzasz sumy.
    Tak że nie widzę jakiegokolwiek problemu.

    Co do obsługi pakietów COBS to mam jeszcze taki pomysł. Nie używasz TComDataPacket, a w TComPort ustawiasz EventChar rowny #0 (zresztą tak jest domyślnie).
    I piszesz tylko obsługę zdarzenia OnRxFlag. Jeśli w buforze będzie więcej znaków niż to zero to dekodujesz zawartość bufora odbiorczego algorytmem COBS i masz w tym momencie kompletny odkodowany pakiet (wtedy wywołujesz jego obsługę) albo rozpoznaną sytuację np. jakiś nieczytelnych śmieci lub powtórzonych zer i wtedy nic nie robisz.
    Tak będzie chyba najprościej. Bufor jest duży (u mnie domyślnie ma 1kB) tak że nic się nie zgubi.
  • #25
    sq9etc
    Level 12  
    dawid.barracuda wrote:
    sq9etc czemu kodujemy od #00 do #0F a nie do #04? Resztę algorytmu rozumiem


    Tak na wszelki wypadek. W przyszłości może się okazać, że potrzeba jednak więcej bajtów zarezerwowanych.
  • #26
    dawid.barracuda
    Level 13  
    Rozumiem.
    Zacząłem się zastanawiać nad pewną rzeczą. Mam np. funkcję:
    Read(var Buffer; Count: Integer): Integer;

    O ile za var Buffer wiem, że podstawiam swój bufor, to w sumie nie wiem o co chodzi z Count. Zawsze to wstawiałem na zasadzie "bo tak było na przykładzie", ale co ten parametr dokładnie oznacza?
  • Helpful post
    #27
    rb401
    Level 38  
    dawid.barracuda wrote:
    "bo tak było na przykładzie", ale co ten parametr dokładnie oznacza?


    Do tego komponentu jest nawet help. Ja też z początku myślałem że nie ma i trzeba po źródłach patrzeć.
    Jeśli jesteś w Windows a przez Lazarusa nie ma dojścia do niego, to tam gdzie go doinstalowałeś jest folder help (podwójny?) i tam jest plik .hlp.

    O metodzie Read jest np.:


    Quote:
    Reads data from input buffer.

    function Read(var Buffer; Count: Integer): Integer;

    Description
    Call Read function to read Count bytes into Buffer variable. The function does not return until Count bytes are read or timeout elapses.

    The return value is the number of bytes that are actually read.

    Note
    Buffer variable must be large enough to hold Count bytes.


    i chyba trudno mi w tej chwili coś dodać od siebie.
  • #28
    dawid.barracuda
    Level 13  
    No dobra, to wiele wyjaśnia.
    Napisałem taką procedurę wg Twoich wskazówek:
    Code: c
    Log in, to see the code

    Odbieram tak pojedynczo bajty. Czy może zrobić tak, że za Count wstawiam ilość bajtów w paczce? Sposób powyżej powoduje bardzo wolny odbiór danych.

    Dodano po 4 [minuty]:

    Dodam, że przy takim odbiorze nie zauważyłem ubytków w odebranych danych.

    Dodano po 9 [minuty]:

    Rozwinąłem procedurę do takiej:
    Code: c
    Log in, to see the code


    Nie zauważyłem głupot w odbiorze no i w tempie pojawiania się poprzednio pojedynczych znaków mam teraz cale odebrane stringi. Czy o takie rozwiązanie Tobie chodziło czy może o jeszcze co innego?
    Mimo to odnoszę wrażenie, że całość i tak działa wolniej od Realterma, a nie widzę powodu, dla którego tak miałoby być. Spowolniam czymś swój program?
  • #29
    rb401
    Level 38  
    dawid.barracuda wrote:
    Czy o takie rozwiązanie Tobie chodziło czy może o jeszcze co innego?


    Chodzi mi o to (na razie rozważmy prosty, niepowikłany przypadek), że pojawia się zdarzenie OnRxFlag, które dla nas jest sygnałem że przyszedł kompletny pakiet COBS.
    W obsłudze możemy sprawdzić metodą InputCount ile mamy w buforze wejściowym. Jeśli mamy tyle ile wynosi przyjęta długość pakietu, plus to kończące zero to jest ok (choć tu nie musimy zakładać jakieś długości pakietu bo wychodzi to i tak z metody dekodowania). I w tym momencie już możemy dekodować COBS. Albo czytając znak po znaku i odpowiednio interpretując informację zapisujemy zdekodowany pakiet w jakimś globalnym buforze, albo czytając hurtem wszystko z bufora wejściowego do jakiegoś tymczasowego metodą Read z parametrem Count odczytanym metodą InputCount i wtedy dekodujemy sobie z naszego bufora (tak chyba będzie szybciej).
    Jak dojdziemy w dekodowaniu do kończącego zera, to albo ustawiamy jakąś globalną flagę, że w naszym buforze jest pakiet do obrobienia, albo przed wyjściem z obsługi OnRxFlag robimy całą obsługę (np. wyświetlenie).


    Ale to jest sytuacja gdy założymy że pakiety przychodzą pojedynczo z jakimś tam wyraźnym odstępem czasowym jeden od drugiego.
    Mam pewne (niezweryfikowane) obawy, że jeśli pakiety będą przysyłane szybko, "na styk", to w momencie obsługi zdarzenia OnRxFlag w buforze może się znaleźć jeszcze trochę znaków po tym zerze. I w takim wypadku trzeba by trochę staranniej do tego podejść.

    Niestety nie mam w tej chwili czasu na własne eksperymenty, choć ten akurat temat mnie szczególnie interesuje, ze względu na to że w kolejce mam taki projekt akurat gdzie takie niezawodne pakietowanie będzie mi potrzebne. I też akurat na styku Lazarus plus system embedded.


    Co do czytania pakietu jako łańcucha znakowego, to mam jakieś przeczucie że w standardzie COBS zostało wybrane zero jako separator, całkowicie świadomie (bo może to być każda inna liczba). Dlatego że w C/C++ może być wygodnie to obsłużone standardowymi funkcjami dotyczącymi łańcuchów znakowych).

    dawid.barracuda wrote:

    Memo1.Text:=Memo1.Text+RevS + #13#10;


    wydaje mi się że efektywniejsze będzie:
    Memo1.Lines.Add(RevS);
    Po za tym w pierwszym podejściu za każdym znakiem miałeś przerysowanie komponentu Memo1. Może właśnie dlatego tak wolno było a samo Read po jednym znaku nie było temu winne.
  • #30
    dawid.barracuda
    Level 13  
    Zająłem się COBSem i wyniki - póki co - wydają się być satysfakcjonujące.
    Przed wysyłką koduję dane kodem, który jest podany w artykule na Wiki. I wszystko pięknie leci nrf'em, nie ma żadnych zer oprócz tego kończącego pakiet. Wobec tego paczka, którą odbiera nrf obsługiwany przez procesor przy PC jest uboższa o to zero. W związku z tym do PC wysyłam odebraną paczkę i zero zaraz za nią.
    Ma to uważam swoje plusy - paczka danych odbierana w programie musi mieć zawsze stałą liczbę znaków i zero na końcu. Skonfigurowałem więc komponent DataPacket w taki sposób, że StartString jest ustawiony na '0', co oznacza, że program reaguje na cokolwiek jako początek pakietu. Jako EndString dałem '#0', czyli znak końca pakietu. Do tego ustawiłem rozmiar pakietu (w moim przypadku 16). I przy takim ustawieniu jak na razie wszystko śmiga, pakietów nie gubi, a przynajmniej jak na razie tego nie zauważyłem. Musiałem jedynie przerobić funkcję dekodującą pakiet COBS. Zrobiłem to tak:
    Code: delphi
    Log in, to see the code


    Chciałem to zrobić w postaci funkcji, ale Delphi nie chce mi skompilować programu, w którym parametrami funkcji są wskaźniki. Szukałem informacji na ten temat, ale nic nie znalazłem. Jest to w ogóle możliwe w tym języku?