asynchroniczna-kom

[Kurs Qt] Asynchroniczna komunikacja portów

W poprzedniej części tego kursu powiedzieliśmy sobie o synchronicznej wymianie informacji pomiędzy portami, które było całkiem przyjemne i odbywało się na zasadzie: wyślij_dane-odbierz_dane-wyślij_dane-…. Takie rozwiązanie nie zawsze jest praktyczne ponieważ zakłada że oczekiwać na dane będziemy jeżeli po wysłaniu jakiegoś sygnału.

Dzisiaj zajmiemy się asynchroniczną komunikacją pomiędzy portami, czyli taką która zakłada, że będziemy w stanie odebrać dane w dowolnej chwili działania programu.

 

Przygotowanie


Tradycyjnie przygotujmy sobie okienko naszego programu, którego zadaniem będzie mierzenie temperatury w losowych momentach + nasz program będzie mógł wysłać żądanie o zmierzenie jej w tym momencie. Jak można się domyśleć wykorzystam tutaj Arduino Uno, z którego korzystaliśmy ostatnim razem oraz czujnik temperatury LM35.

Nasze okienko jest maksymalnie proste, składa się z widgetu imitującego wyświetlacz LCD, który będzie pokazywał aktualną temperaturę, TimeEdit  do wyświetlania ostatniej aktualizacji (wyłączyłem możliwość edytowania czasu) oraz przycisku do natychmiastowego mierzenia czasu.

Wyjątkowo słowo komentarza do tego kodu: z racji, nie możemy pauzować programu za pomocą delay()  bo chcemy mieć możliwość w dowolnej chwili interakcję z programem, tzn. wymuszenie w każdej chwili pobrania temperatury, którą wyślemy jako string.

Tym razem jako znak specjalny dla arduino dostajemy !, co oznacza rozkaz podesłania temperatury oraz @ jako początek wiadomości i $ oznaczający koniec transmisji.

Nie zapomnijmy o dodaniu linii do pliku .pro:  QT += serialport w naszym projekcie.

 

Potrzebne funkcje, zmienne


Czyli coś w rodzaju, krótkiego przypomnienia tego co będziemy używać, a także wstępny wygląd plików nagłówkowych/źródłowych.

Na razie chyba nie potrzeba, żadnego komentarza ponieważ to wszystko znamy z poprzedniej lekcji.

Do tego całego pliku przyda się kilka słów wyjaśnienia.

Nie ukrywam, że znowu poszedłem na łatwiznę i aby nie zajmować się wyborem portu z menu, otwieraniem go, itp czyli zamiast zajmować się rzeczami, które zrobiliśmy ostatnio tak tutaj zakładam, że do laptopa mamy podłączone dokładnie 1 urządzenie, które może się w ten sposób komunikować inaczej dostajemy prośbę o podłączenie urządzenia lub w przypadku odmowy zamykamy program.

Pozostały kod powinien być jasny dlaczego pojawił się w tej, a nie innej formie. Czas na meritum tej lekcji.

Asynchroniczna komunikacja portów


W asynchronicznej komunikacji charakterystyczną cechą jest to, że bloki wiadomości posiadają znak początku i końca bloku, jest to o tyle ważne, że przy jej odpieraniu należy w odpowiedni sposób ją „skleić”, czyli musimy wiedzieć kiedy ona się zaczęła, a kiedy się kończy, np zdanie: „Hello World”, może być wysłane jako: „He” „ll” „o ” „Wor” „ld”, „Hello World”, albo na wiele innych sposobów, w momencie gdy mamy kilka wiadomości może się zdarzyć, że zostanie sklejone ze sobą kilka wiadomości.

Ta funkcja jest dość łatwa i zostaje wywołana już po zakończeniu wczytywania danych. Jest chyba dość trywialna więc zachęcam do samodzielnej analizy.

Ta funkcja ma za zadanie wczytanie całej liczby, jej konstrukcja jest już nam w jakiś sposób znana z poprzedniej lekcji. Zostaje ona uruchomiona przez sygnał.

Jej konstrukcja jest dość wadliwa bo zakłada, że zawsze pierwszym elementem jest @ (i tak powinno być, chyba że jakieś dane by się zagubiły), a ostatnim $, co już nie zawsze może się zdarzyć i można to nieco poprawić poprzez wyszukanie fraz zaczynających się od @ i kończących $ (możesz to potraktować jako zadanie domowe).

 

Podsumowanie


Dziś w tej nieco krótkiej lekcji wykonaliśmy program, który może komunikować się w sposób asynchroniczny z innym urządzeniem.

W kolejnej lekcji najprawdopodobniej odstawimy tymczasowo Arduino, bo zajmiemy się komunikacją z bazą danych (chyba że macie inną propozycję).

Jak zwykle w razie pytań do tej lekcji, problemów, albo po prostu chęci wyrażenia swojego zdania zapraszam do systemu komentarzy.

[GitHub]


  • fullmoonguy

    W oknie o komunikacie: „Błąd!”,”Nie znaleziono pojedynczego portu, spróbować ponownie?” Wybieram „No”. Otrzymuje błąd: http://pokazywarka.pl/n568nw/

    • Z jakiej wersji Qt korzystasz, ja testowałem program pod tym kątem i nie był problemów. Może coś zmienili w sposobie działania funkcji deleteLater()

      Pozdrawiam

      • fullmoonguy

        QT Creator 3.4.2 bazujący na QT 5.5.0 (MVC 2013, 32 bitowy). Istnieje jakieś eleganckie rozwiązanie na zamknięcie tej aplikacji ? Bo mógłbym zrobić powiedzmy wszystkie przyciski nieaktywne w formatce po za jakimś „zamknij”, ale to bez sensu.

        • deleteLater było właśnie eleganckim rozwiązaniem, w takim razie polecam aktywować jakiś timer i zamknąć program po jakimś krótkim czasie

          • fullmoonguy

            Póki co odpuściłem temat zamykania programu w tym przypadku, ale wrócę do tego niebawem. Zastanawia mnie inna rzecz. Zrobiłem program, który przesyła dane z 3 czujników co sekundę. Wszystko jest aktualizowane w metodzie ‚update’ i wyświetlane na trzech komponentach QLCDNumber. Program napisany jest tak, że wyświetlanie na QLCDNumber odbywa się tylko jeżeli zmieniła się wartość czujnika (aby nie obciążać aplikacji). Niestety dane odświeżają się średnio co 4 sekundy. Domyślam się, że wyświetlanie jest obsługiwane przez system operacyjny jako taka rzecz o najmniejszym priorytecie. Jednak zależy mi na tym, by dane były aktualizowane i wyświetlane jak najszybciej. Co trzeba zrobić ? Czy muszę napisać program w oparciu o wątki ?

          • Ciężko jest mi się wypowiedzieć nie wiedząc jak do końca działa u ciebie metoda update, kiedy dokładnie zostaje wywołana? (po sygnale readyRead, czy może uruchamiasz ją jakoś inaczej).

          • fullmoonguy

            Wrzucam te fragmenty kodu w których dokonywałem modyfikacji: http://pastebin.com/7ikhzmNE

          • A masz pewność, że dane wysyłane przez mikrokontroler zmieniane są szybciej niż ta średnia 4 sekund?

          • fullmoonguy

            Tak. Dane to nic innego jak zmienne unsigned int które dekrementuje poprzez wciskanie przycisków. Zanim próbowałem pisać program w QT najpierw obserwowałem wszystko w terminalu TeraTerm. Co sekundę pojawia mi się kolejna ramka na terminalu a gdy wcisnę jeden z trzech przycisków, dekrementuje się jedna z trzech zmiennych które przesyłam do PC. Zmienne oczywiście są zmniejszane natychmiast po zwolnieniu przycisku.

          • Skoro masz pewność, że dane za każdym razem są inne i to nie jest wina powtarzalnych danych to może spróbujmy to obejść od tej strony: mówiłeś, że masz 3 czujniki. Czekasz aż wysyłanie danych zostanie zakończone? Np. Wysyłasz dane z czujnika A, w tym czasie czujnik B ma nowe dane do wysłania. Czekasz aż zostaną wysłane i odebrane w całości dane z czujnika A i wysyłasz dopiero dane z czujnika B?