Bezprzewodowy interfejs SCPI do Meratronik V543 |
|
pięćdziesiąt twarzy Greya Z lektury wzmiankowanej książki podstawowy wniosek dla mnie taki, że monotonia czy wręcz nuda to znani wrogowie udanej zabawy. W końcu jak długo i na ile sposobów można wiązać się kablem?. Zatem w ramach szeroko pojętego urozmaicenia opisywany dalej interfejs SCPI do Meratronika V543 bazuje na rozwiązaniu bezprzewodowym. Podstawą jego jest komputerek Raspberry PI Zero w wersji z WiFi. Wybór platformy był dość prosty: o układzie ESP8266 napisano w majowej EdW, więc napoczętego przez kogoś tematu nie będę już drążyć. Do wszelkich shieldów z WiFi dedykowanych Arduino podchodzę póki co ze sporą rezerwą, ten wariant zatem także odpada. I tak pozostaje relatywnie tanie i posiadające ogromne wsparcie społecznościowe Raspberry PI, do zastosowań ‘pod zabudowę’ wersja Zero z WiFI jest wręcz idealna. Niewielka gabarytowo, posłuszna, wdzięczna w oprogramowaniu i za jedyne pięć dyszek. niepozorna Malinka W formie garstki linków wskazania na opisy co i jak skonfigurować, a ogarnięcie świeżego sklepowego modułku i przygotowanie go do dalszych prac zajmie raptem jeden wieczór. Na kolejnych etapach pracy korzystałam z tych materiałów z sieci: ⇨ https://www.raspberrypi.org/documentation/installation/installing-images/linux.md ⇨ https://techfreak.pl/raspberry-pi-zero-konfiguracja-wifi/ ⇨ http://www.science.smith.edu/dftwiki/index.php/Tutorial:_Client/Server_on_the_Raspberry_Pi ⇨ http://www.science.smith.edu/dftwiki/index.php/Tutorial:_Interrupt-Driven_Event-Counter_on_the_Raspberry_Pi ⇨ http://pi4j.com/pins/model-zero-rev1.html ⇨ https://cdn.sparkfun.com/assets/learn_tutorials/6/7/6/PiZero_1.pdf Jeżeli póki co nie chcemy nic lutować do złącz Maliny, a nie posiadamy na podorędziu kabelka USB OTG i Mini HDMI to aby dostać się do konsoli systemu pozostaje nam opisywana w linkach powyżej partyzantka z przekładaniem karty do czytnika w komputerze i edytowaniem plików na karcie. Gdy ktoś pracuje na co dzień z Linux to wszelkie narzędzia ma pod ręką niejako z pudełka i sprawa nie jest skomplikowana. Istnieje też inna alternatywna ścieżka - wstępne skonfigurowanie świeżego systemu na dużej Raspberry PI, przy użyciu klawiatury i monitora czy nawet telewizora, potem kartę można przełożyć do Zero. Ja poszłam tą właśnie drogą, ssh i wifi ustawiłam sobie wygodnie na większej siostrze, operacje typu `expand filesystem` i wgranie świeżych paczek systemowych, git i reszta – już w natywnym środowisku sprzętowym PI Zero. I jeszcze jedno - w trakcie rozkładania obrazu systemu na karcie przy pomocy polecenia `dd` tak naprawdę nie wiadomo co się dzieje. Programik dd nie należy do specjalnie wylewnych, a sam proces wgrywania zajmuje dłuższą chwilę. Warto wtenczas otworzyć sobie na boczku drugi terminal, wykonać `top` albo `ps –A` aby pozyskać identyfikator procesu dd, a potem wysyłać mu sygnał USR1, o na przykład tak: ![]() W ten sposób zagadnięte dd wyświetli na niemej dotychczas konsoli informacje ile aktualnie już pracy wykonał, a przede wszystkim – jak nam odpowie, to znaczy że nic tam nie zawisło. U mnie akurat to było pomocne, bo wiekowy nieco czytnik renomowanej firmy Tesco ma problem ze stykiem do wykrywania karty SD i czasem w trakcie dłużej trwających operacji potrafi wyciąć psikusa. Na koniec cieszymy się maleńką diodką na PI Zero, której filuterne pomrugiwanie oznacza ładowanie Linuksa, inicjację systemu i jest zaproszeniem do dalszej zabawy. użytkowanie wieczyste W moich realiach Malina ma nadany stały adres IP, trwałą dzierżawę IP zapewnia odpowiedni konfiguracja serwera DHCP wbudowanego w domowy router i rozpoznająca Malinkę po adresie MAC. Tak samo rozpoznawany jest Rigol-Cukierek. Drobna edycja pliku /etc/hosts zapewnia widoczność modułku po nazwie, nie ma sensu dla takiego drobiazgu bawić się w lokalny DNS. ![]() ![]() Mając dostęp do systemu przez ssh jesteśmy praktycznie w domu i dalsze prace będą koncentrowały się tylko i włącznie na sprawach związanych z interfejsem do Meratronika. Dobrze jest doinstalować sobie pakiet proftpd wtedy dowolnym , ulubionym klientem ftp możemy komfortowo nawigować po systemie plików Maliny, a także edytować zdalne pliki tekstowe jeżeli nasz ulubiony edytor to potrafi (na przykład Kate z KDE). Drugi przydaś to git, ja programik pisałam bezpośrednio na Malinie, a odrobina pecha przy wyłączaniu zasilacza i pstryk - po filesystemie. Tak więc git add/commit/push pozwalały zachować spokój ducha, ponieważ prace były bezpieczne, poza Maliną. wąż kusiciel kontra C Przyznam, dłuższą chwilę zastanawiałam się w czym popełnić oprogramowanie komunikacyjno-sterujące, a możliwości jest całkiem sporo. W domyślnej instalacji Linuksa na PI mamy język C/C++ (gcc v.6.3), można łatwo doinstalować Free Pascal, jest też Python, choć co odrobinę zaskakujące w wersji 2.7. I ten Python mnie kusił straszliwie, och jak mocno. Tym bardziej, że miałabym wsparcie u autora tej oto cudnej zabawki ⇨ https://www.sbprojects.ga/projects/pi-epsim, tam emulatorem pamięci steruje właśnie PI Zero i program w Pythonie. Robiąc bliźniaczy projekcik miałabym relatywnie łatwiej niż walcząc z tematem od zera. Tylko że zadziałała zasada `czym skorupka i tak dalej` i oko me zawisło na kultowej jak dla mnie książce `Programowanie zastosowań sieciowych w systemie Unix`, która praktycznie w całości bazuje na języku C. No i dylemat się rozwiązał. Nie zmienia to faktu, że połączenie Python/GPIO i tak muszę rozpoznać detalicznie, do pisania przydasiów na kolanie wydaje się być bardzo efektywne vide wzmiankowany emulator Sana, który mi ciągle nie daje spokoju. zapoznanie z GPIO Dostęp do GPIO Maliny wcale nie jest skomplikowany, a jeżeli ktoś wcześniej bawił się w Arduino to już w ogóle w temat wejdzie bezboleśnie. Kluczową sprawą jest pobranie sobie na Malinkę pakietu WiringPI ⇨ http://wiringpi.com/ i skompilowanie bibliotek. Do wywołania kompilatora/linkera dodajemy -lwiringPi , aplikacje linkują się bez problemu. Interfejsik mój prosty, to i funkcji bibliotecznych jeno garstka w użyciu, oto one: potem pojawiła się jeszcze funkcja instalująca handler przerwania dla danego pinu IO czyli: Prosty programik, który nabazgrałam w międzyczasie generował falę prostokątną na pinie 0, cały kod sprowadza się raptem do poniższych linijek: pinMode ( 0, OUTPUT ); while ( 1 ) { digitalWrite( 0, HIGH ); digitalWrite( 0, LOW ); } Jak widać dostęp do GPIO jest intuicyjny, a programowo można generować całkiem szybkie przebiegi. logika na poziomie Fakt to ogólnie znany, że malinowe GPIO pracuje z poziomami logicznymi charakterystycznymi dla logiki 3.3V i nie jest ‘five volts tolerant’. Dla odmiany logika mojego neonowego Meratronika to klasyczny TTL, interfejs z układami 74LS165 takoż. Niezbędna jest zatem konwersja 5<->3v3 dla linii interfejsu, a ową wykonałam przy pomocy łatwego do kupienia ⇨ modułku do zmiany poziomów W tym przypadku modułek-konwerter został spreparowany do pracy z kątowymi złączami igłowymi i kabelkami, tak było mi najłatwiej i w sumie najelastyczniej – w końcu to jeden wielki eksperyment i zmiany są na porządku dziennym. Robiąc docelowe rozwiązanie taki modułek najlepiej umieścić w podstawce DIL lub wręcz wlutować w płytkę. kanapka z Maliną Zacznę od tego, że schemat przyłączenia interfejsu do PI Zero wygląda jak poniżej: ![]() Konwerterek, który posiadam ma cztery kanały, pokrywa nadawane prze PI sygnały /LINE_LOAD, LINE_CLK jak i odczytywane LINE_DATA oraz LINE_READY. Świadomie zrezygnowałam z obsługi LINE_START, niech mierniczek spokojnie pracuje sobie we własnym rytmie, impulsy na LINE_READY zauważane przez Malinę w postaci przerwań zapewnią odczyt nowych wartości w stosownym momencie i nic nie zostanie przegapione. Kolejna rzecz to sygnalizacja pracy interfejsu. Oczywiście jak zobaczymy PI w naszej sieci lokalnej to już jest nieźle, a jak zalogujemy się do niej via ssh to rewelacja. Ale w trakcie normalnego użytkowania chyba warto wiedzieć czy w ogóle programik komunikacyjny ruszył, czy odczytuje miernik i czy jest komunikacja z PC na poziomie komend SCPI. Do tego celu służą widoczne na schemacie dwie diody świecące, podparte tranzystorami i zapięte go pinów 4 i 5. Ich miganie oznacza kolejne cykle odświeżenia danych przez miernik oraz kolejne obsługiwane polecenia SCPI. Nieustanna błyskoteka na tyle miernika oznacza zatem, że całość pracuje w miarę poprawnie. Dalej garść zdjęć z kolejnych etapów melanżu Maliny z podrutowanym interfejsem do V543. Wstępne przymiarki grzebyczka gold-pin Okazało się, że otworki z Malinie są zbyt małe i aby przewlec śrubki M3 muszę je nieco poszerzyć, zatem pilnik-iglak w dłoń. Miałam stres wtedy, taka drobniutka lutowanka na płytce i ostre narzędzie. Po kilku przymiarkach Malina zawisła na słupkach dystansowych na płytce interfejsu, tak na kanapkę. Orientacja PI jest taka, aby złącze zasilające mikro USB było w dół, to w moich realiach skutecznie zminimalizuje ryzyko wygięcia lub odłamania złącza z płytki PI. Obowiązkowy LED od zasilania. Może i zabawne, ale komórkowa ładowarka z której zasilane jest PI nie ma sygnalizacji, a sami ledzik na PI maleńki i przysłonięty drutami, lampka na boku w tym przypadku akurat jest dla mnie zasadna. Diody sygnalizujące cykle odczytu V543 oraz komunikację SCPI są zamontowane na brzegu, widać je w miarę dobrze choć i tak trzeba zapuścić żurawia na tył urządzenia. Test diodek na sucho: I podczas normalnej pracy interfejsu - prawa to odczyty, lewa - polecenia SCPI z terminala. A tu dygresja nie na temat – w pudełku taki oto ledzik z czasów PRL znalazłam, prostokątny, czerwony, CEMI bodajże. Ale mi się go szkoda zrobiło, bo nielutowany nigdy i wrócił do siebie. Konwerter 5/3v3 został zawieszony na króciutkich specjalnie ku temu spreparowanych kabelkach połączeniowych. Całość jest w miarę bezboleśnie rozbieralna gdyby zaszła jakaś potrzeba poprawek czy przeróbek, a jednocześnie dość pakowna, a pomimo konstrukcji natury powietrznej nic nie dynda ani się nie majta. Zrobienie zdjęć zadeczka Meratronika z zamontowanym interfejsem wymaga odrobiny akrobatyki ewentualnie wykorzystania lusterka, całość prezentuje się następująco i to jest stan na chwilę obecną docelowy. gwara meratronikowa Kod programiku dostępny jest w lokalizacji ⇨ v543.c i proszę o wyrozumiałość, to wersja `na kolanie` i jeszcze rozwojowa. O detalach implementacyjnych oprogramowania nie będę się tu zbytnio rozwodzić, informacje o programowaniu gniazdek oraz przykłady sieciowych aplikacji klient-serwer można znaleźć w literaturze, choćby w doskonałej pozycji, którą wskazałam na początku tego tekstu. Jeżeli zapoznamy się jakoś ze standardowymi funkcjami socket(), bind(), listen() i accept() to w zasadzie możemy już w sieci zrobić cokolwiek tylko przyjdzie nam do głowy. Chcę za to poruszyć teraz temat samego definiowania zestawu poleceń SCPI dla multimetru V543, czy jakiegokolwiek innego `customowego` sprzętu, bo tu nie ukrywam miałam pewien problem. Pierwsza moja myśl była taka, aby pasujące do urządzenia komendy zapożyczyć sobie z jakiegoś sprzętu, który ma SCPI zaimplementowane, na przykład `DM3000 Digital Multimeter` ⇨ MDE_Quelle5.pdf. I w zasadzie byłoby super gdyby nie to, że V543 ma jednak w porównaniu z rzeczonym multimetrem dość ubogie możliwości, zbiór poleceń należałoby ograniczyć, a takie znowu przerzedzenie spowodowało utratę spójności składni i generalnie wyglądało dziwnie. Przejrzenie dokumentacji do kolejnego miernika, tym razem `Fluke 8845` ⇨ 8845a___pmeng0200.pdf dało efekt podobny. Nie potrafiłam jakoś wyłuskać sensownego podzbioru poleceń, zawsze wychodziło jakieś pokraczne dziwadło. Pozostało zatem spreparowanie własnej autorskiej wersji dialektu SCPI, dedykowanej dokładnie mojemu mierniczkowi V543. I tu kłopot polega na tym, że trzeba zdecydować w jakiej części obróbka danych ma być po stronie miernika, a w jakiej po stronie aplikacji klienckiej, która z tych danych skorzysta. Przykład wariantowości sytuacji: I tu powstaje pytanie – jak będzie używany interfejs SCPI? Czy może systemowo? I z miernikiem będzie komunikować się inne urządzenie lub aplikacja? A może jednak na żywca z konsoli lub prostymi skryptami będzie te polecenia wydawał człowiek, czyli dalsze przetwarzanie będzie na poziomie białkowym? Tego nie wiadomo przecież, definiując składnię trzeba wypracować coś po środku. SCPI jest (w mojej opinii) interfejsem typu maszyna-maszyna a nie człowiek-maszyna, choć prostota poleceń i czytelna forma nie wykluczają jego manualnej obsługi (vide `Rigol na smyczy`). Tak po prawdzie to mając podobne zagwozdki warto sięgnąć po opracowanie opisujące standard, na przykład: ⇨ scpi-99.pdf. Finalnie stanęło na tym, że mój Meratronik będzie rozumiał następujące autorskie polecenia: TCommand scpiCommands[] = { { "*idn?", &handlerIdn }, { ":meter:mode?", &handlerMode }, { ":meter:v:range?", &handlerVRange }, { ":meter:r:range?", &handlerRRange }, { ":meter:raw?", &handlerRawData }, { ":meter:display?", &handlerDisplay }, { ":debug:exit", &handlerExit }, { NULL , NULL } };*idn? Identyfikacja urządzenia, numer seryjny przepisany z obudowy oraz copyright aby wyglądało na profeskę, przynajmniej dla tych, co się nie znają. :meter:mode? Polecenie zwraca aktualny tryb racy multimetru lub błąd w przypadku nie wciśnięcia żadnego z przycisków funkcyjnych. Zwraca wartość jest w formacie n|xx , gdzie n – identyfikator trybu wynikający z kombinacji bitów na tylnym złączu, xx –literały { R, AC, DC }, czyli w kodzie jest: const char *pszModeDesc[] = { "error", // 0 "R", // 1 "AC", // 2 "error", // 3 "DC" // 4 };:meter:v:range? Polecenie zwraca aktualnie ustawiony zakres pomiarowy napięcia lub błąd gdy miernik nie jest w trybie VAC lub VDC. Zwraca wartość jest w formacie n|xxxx|ddd na zasadach jak powyżej, dozwolone wartości zgodnie z definicją: TRangeInfo volRanges[] = { { "100V", 100 }, //0 { "1V", 10000 }, //1 { "1000V", 10 }, //2 { "10V", 1000 }, //3 { "100mV", 100 }, //4 { "error", 1 }, //5 { "error", 1 }, //6 { "error", 1 } //7 };:meter:r:range? Polecenie zwraca aktualnie ustawiony zakres pomiarowy rezystancji lub błąd gdy miernik nie jest w trybie R. Zwraca wartość jest w formacie n|xxx|ddd na zasadach jak powyżej, a dozwolone wartości takie: TRangeInfo resRanges[] = { { "100kΩ", 100 }, //0 { "1kΩ", 10000 }, //1 { "error", 1 }, //2 { "10kΩ", 1000 }, //3 { "error", 1 }, //4 { "1MΩ", 10000 }, //5 { "error", 1 }, //6 { "10MΩ", 1000 } //7 };W obu przypadkach ddd - to podzielnik, wynikający z aktualnie ustawionego zakresu, jest podstawą do sformatowania wyniku. :meter:raw? Polecenie zwraca 32-bitową liczbę w formie ośmiu cyfr szesnastkowych. Zawartość binarna zgodna z Tabelą 1 z artykułu w EdW 4/2018. :meter:display? Polecenie zwraca zawartość wyświetlacza w postaci pięciocyfrowej liczby dziesiętnej (bcd) wraz ze znakiem polaryzacji dla pomiarów DC oraz bez znaku dla R i AC. Brak kropki dziesiętnej, wynik trzeba sformatować samodzielnie. :debug:exit Komenda na użytek wewnętrzny, to zatrzymanie aplikacji, przydaje się zamiast wpisywania `killall v543`. Jak widać odpowiedzi interfejsu są zrozumiałe dla średnio rozgarniętego operatora, ale i możliwe do obróbki programowej. Wystarczy napis z informacją o zakresie czy trybie potraktować funkcją strtok() czy jej odpowiednikiem dzielącym napis na składniki w/g separatora (u mnie `|`) i mamy osobne wartości do wykorzystania. Proszę też zauważyć, że nie ma w tym zestawie polecenia *rst resetującego urządzenie. No cóż – w moim przypadku załamanie pracy aplikacji automatycznie znieczuli ją na nadchodzące polecenia, więc zdalny restart nie wchodzi w grę. Pomoże tylko interwencja przez ssh lub brutalna manipulacja przy zasilaniu. Na chwilę obecną nie pochylam się zbytnio nad zdarzeniami zachodzącymi w otoczeniu multimetru, więc brakuje obsługi poleceń *ese?, *esr? I tym podobnych. Można oczywiście zastanowić się nad zapamiętywaniem wystąpienia sygnału OVER wraz ze znacznikiem czasu czy obserwowaniem wartości MIN/MAX. Malina pracująca jako interfejs ma naprawdę sporą moc obliczeniową i można sobie używać. Testy - tu z wywołaniem exit: echo ":debug:exit" | nc neonowy 5555 ![]() Pomiary rezystancji: #testr.sh echo "*idn?" | nc neonowy 5555 echo ":meter:mode?" | nc neonowy 5555 echo ":meter:r:range?" | nc neonowy 5555 echo ":meter:display?" | nc neonowy 5555 echo ":meter:raw?" | nc neonowy 5555wywołanie z konsoli: while true; ./testr.sh; sleep .5; done ![]() Pomiary napięcia: #testv.sh echo "*idn?" | nc neonowy 5555 echo ":meter:mode?" | nc neonowy 5555 echo ":meter:v:range?" | nc neonowy 5555 echo ":meter:display?" | nc neonowy 5555 echo ":meter:raw?" | nc neonowy 5555wywołanie z konsoli: while true; ./testv.sh; sleep .5; done ![]() Na żywo praca interfejsu wygląda tak: Aby programik v543 uruchamiał się automatycznie, wystarczy dodać go do /etc/rc.local i mamy samograj. Naturalnym kierunkiem ewolucji tego sieciowego miniprojektu wydaje się być temat prezentacji danych pomiarowych na stronie www. Spróbuję zatem pokazać swoje własne podejście do zagadnień integracji sprzętu i oprogramowania. Podstawowym obwarowaniem jakie przyjęłam na potrzeby tekstu poniżej to całkowita darmowość i ogólna dostępność wszelakich narzędzi, po drugie - fizyczna przenoszalność poszczególnych komponentów rozwiązania. W szczególności całą przedstawioną poniżej zabawkę można sobie uruchomić bez Meratronika i Raspberry PI, na zwykłym PC. gotyckie łuczki i fasada z gipsu Diagramy UML powstały przy pomocy genialnego narządka on-line: ⇨ draw.io Z architekturą systemów informatycznych jest czasem tak, że to kilka pudełek połączonych kreskami, to niby komponenty i ich interfejsy i gotowe, a już niech się programiści dalej głowią jak to ugryźć. Sama występuje częstokroć po obu stronach tej barykady (jako arch i jako dev) stąd i dystans mam pewien do takich wysokopoziomowych oglądów na systemy IT, ale też świadomość jak bardzo takie rozplanowanie komponentów potrafi się przydać na wszelkich etapach pracy projektowej, choćby najprostszej, że o utrzymaniu późniejszym nie wspomnę. Spójrzmy zatem na to, co mamy poniżej: ![]() W ujęciu UML to diagram komponentów, bardzo prosty wprawdzie, ale dość dobrze prezentujący składniki naszego mini-systemiku. Centralna cześć to dwa moduły programowe - konektor SCPI oraz serwer www, wszystko to osadzone w fizycznych realiach komputerka Raspberry PI. Z postu powyżej wiadomo jak działa interface do SCPI, tu materializuje się on w postaci aplikacji v543. Skoro mamy mieć interfejs webowy to oczywistą staje się konieczność dostawienia serwera www, stąd obecność aplikacji meraHttpServer. Serwer www komunikuje się z konektorem SCPI przez proste ramki tekstowe, egzotycznym dla świata protokołem, obrobione dane udostępnia w postaci stron HTML. I w sumie tyle z lotu ptaka. Aha, do serwera www klientów może się zapiąć tylu, na ile pozwoli jego wydajność, teoretycznie jest możliwe wykorzystanie przez klienta natywnego interfejsu SCPI w sposób bezpośredni, ale tego póki co nie chcemy. kto, z kim i kiedy czyli UML Samo działanie komunikacji klient-serwer (a w naszym przypadku przeglądarka-serwer www) prezentuje poniższy diagram sekwencji z miarę zgodny z notacją UML: ![]() Wywołanie URI /info powoduje odesłanie do przeglądarki strony w HTML zawierającej podstawowe informacje o bieżącym stanie miernika, dane te są pozyskiwane przez serwer z sekwencji wywołań SCPI na programie komunikującym się z miernikiem. Oczywiście zauważamy, że powyższe to typowe wywołanie synchroniczne, trwa tyle ile sekwencja wywołań składowych plus czas potrzebny na zbudowani treści strony. Komunikacja multimetru z aplikacją v543 była nieco trudna do narysowania na diagramie sekwencji, ponieważ tak naprawdę to Meratronik inicjuje ten proces zgłaszając przerwanie, dalsza komunikacja po SPI to jego konsekwencja. Dla uproszczenia przyjęłam wielką kropę czyli 'found message', to takie jakby zdarzenie, dalej już zachodzi klasyczna sekwencja request-response. Ważne w tym natomiast jest to, że aplikacja v543 otrzymawszy zapytanie via TCP/IP zwraca wartości zapamiętane od czasu ostatniego przerwania na LINE_READY, nie jest to stan bieżący w literalnym rozumieniu. Taka optymalizacja jest ceną za szybkość pracy interfejsu i jego wielodostępność. Zmienne z cache można swobodnie odczytywać bez potrzeby blokowania rywalizujących ze sobą połączeń klienckich. Kolejny rysunek to diagram sekwencji dla wywołania URI /meter które to powoduje odesłanie stronki www ze stylizowanym w analogowych klimatach malunkiem miernika. ![]() Tu sprawa wygląda nieco inaczej, ponieważ część akcji rozgrywa się po stronie serwera, a spora część po stronie klienta, w środowisku przeglądarki. Serwer ładuje z lokalnych zasobów treść html wraz z kodem javascript i odsyła do klienta. Uruchomiony kod javascript rysuje skalę analogowego miernika, a następnie periodycznie tworzy typowe wywołanie AJAX, którym pobiera z serwera bieżącą wartość mierzoną przez urządzenie. Zauważmy - strona ładowana jest tylko raz i nie ma potrzeby odświeżania jej zawartości. Cykliczne wywołania AJAX zapewniają zmianę treści w oczekiwanym przez nas zakresie, tu - kontrolują położenie wskazówki. anatomia prostego serwera www Wspominałam, że będzie męczony Python i jestem konsekwentna - aplikacja meraHttpServer została sklecona w Python 2.7 (ponieważ taki jest preinstalowany w PI). Komplet to w sumie trzy pliki: Pliki html to de facto zasoby udostępniane przez serwer, wyniosłam je na zewnątrz z uwagi na łatwość modyfikacji bez konieczności restartu aplikacji. Można oczywiście wbić kod html na stałe w jakieś const-stringi na poziomie aplikacji, ale wtenczas tracimy możliwość komfortowej zmiany treści niejako w locie. Serwer obsługuje metodę GET protokołu HTTP, z reguły zwracając `200 OK` i żądaną w URI treść. /info Zerknijmy dokładniej na fragment treści pliku info.html: <!-- info.html --> <tr> <td nowrap align="left">*idn?</td> <td nowrap align="right">$IDN$</td> </tr>Jak widać w html wplecione są symbole-tagi typu $IDN$ i podobne, one są podmieniane na konkretne wartości podczas budowania treści po stronie serwera, robi to funkcja processInfoPage() #merahttpServer.py def processInfoPage( p ): p = p.replace( "$IDN$", scpiCall( "*idn?" ) ) p = p.replace( "$MODE$", scpiCall( ":meter:mode?" ) ) p = p.replace( "$RANGE$", scpiCall( ":meter:v:range?" ) ) p = p.replace( "$DISPLAY$", scpiCall( ":meter:display?" ) ) return pFizyczny kontakt z konektorem SCPI Meratronika zapewnia funkcyjka scpiCall(), cała seria jej wywołań pozwala pozyskać komplet danych potrzebnych do przygotowania zawartości strony. #merahttpServer.py def scpiCall( cmd ): s = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) s.connect( ("neonowy", 5555) ) s.sendall( cmd.encode() ) resp = s.recv( 1024 ) s.close() return resp.decode().strip()Dodatkowo obsługa /info dokonuje prostego pomiaru czasu budowania strony, w sumie to byłam ciekawa jak szybko na takiej małej Malince będzie ten mechanizm pracował i nie ukrywam zaskoczenie było pozytywne - wychodziły niewielkie dziesiątki milisekund, przykład poniżej: ![]() W formie objaśnienia: "(pi) neonowy - Konsola" - to ssh, w niej uruchomiony Python z meraHttpServer.py "(pi) neonowy - Konsola<2>" - to ssh, w niej uruchomiony proces v543 do komunikacji z Meratronikiem Konsola <3> to poszukiwanie id procesu serwerka do dalszych testów /meter Teraz przechodzimy do analogowej części webowego interfejsu - wywołanie URI /meter. Tu jest ciekawiej, ponieważ plik meter.html to zawartością dość prosty html, ale za to ze sporym kawałkiem javascript wewnątrz, do rysowania skali. Bibliotek do rysowania figur w przeglądarce jest cała masa, mnie w oko wpadły dwie: ⇨ http://dmitrybaranovskiy.github.io/raphael/ oraz ⇨ http://fabricjs.com/ I jakoś tak w Fabric poczułam się deko swobodniej, zatem skalę mierniczka pracowicie wydziubałam przy pomocy tejże. Oprogramowanie rysowania frontu nie było jakoś mega skomplikowane, ale przyznaje, w kilku miejscach kod mi się narowił, choćby podczas ustawiania pod kątem opisów podziałek skali. Możliwe, że takie pamiątkowe zrzutki ekranu wydadzą się zabawne, mi tam do śmiechu wtedy nie było: ![]() ![]() ![]() No i oczywiście eksperymenty z samym układem skali, chwilami holujące w kierunku pokracznego zegarka: Rysowanie unipolarnej skali to jedno, ale napędzana AJAX-em wskazówka chyba warta jest wzmianki, zatem: /* meter.html */ function checkPointerPosition() { var httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var meterNum = this.responseText; meterNum = Math.abs( meterNum / 100 ); document.getElementById("rawMeterData").innerHTML = meterNum; needleAngle = 150-meterNum; canvas.remove( needle ); needle = GetNeedle( needleAngle ); canvas.add( needle ); document.getElementById("counter").innerHTML = counter++; } }; httpRequest.open( "GET", meterUrl, true ); httpRequest.send(); }Widać od razu, że wywołanie open(..,..,true) na XMLHttpRequest jest asynchroniczne, to znaczy przeglądarka nie czeka na nadejście odpowiedzi z serwera tylko dalej kontynuuje pracę. Strona webowa jest dzięki temu responsywna, a odpowiedź z serwera zostanie uwzględniona, gdy tylko się pojawi (status:200, readyState:4). Druga zaleta z tej sztuczki taka, że generowany ruch z serwerem ogranicza się jedynie do niezbędnego zakresu potrzebnych akurat danych - nie przeładowujemy całej strony tylko po to, aby odmalować w nowym położeniu zwykłą wskazówkę. Cykliczność wywołań AJAX zapewnia podłożenie naszej funkcji do setInterval() z zadanym parametrem czasowym: /* meter.html */ setInterval( checkPointerPosition, 500 );Oczywiście, po stronie serwera wywołanie musi być obsłużone, zapewnia to kod skojarzony z URI /meterData. Stronka z miernikiem analogowym wygląda następująco: ![]() Opis okienek jak poprzednio. ![]() Life on Top Taki serial kiedyś był, sympatyczny nawet, a równie sympatyczne wydaje się proste narzędzie do monitorowania procesów systemu o nazwie top. Uruchomione ot tak sobie wyświetli stan prawie wszystkiego co żyje w systemie, informacji jest stanowczo zbyt wiele no i dynamicznie się zmieniają, procesy najbardziej obciążające CPU są właśnie na topie. Do obserwacji jak konektor v543 i serwerek meraHttpServer wpływają na zużycie mocy obliczeniowej wykorzystamy top, ale wywołany tak: top -pNNN,MMMgdzie NNN, MMM to identyfikatoy (`pid-y`) interesujących nas procesów. Pozyskać je łatwo wywołując dla odmiany polecenie ps -Afi traktując jego standardowe wyjście ogólnie lubianym grep-em. ![]() Opcja f zapewni prezentację pełnego wywołania, które uruchomiło proces, a więc np. interpreter Python wraz z nazwą skryptu, opcja A pokaże procesy nie tylko z bieżącej sesji ssh, ale wszystkie, z innych terminal także. ![]() ![]() Mając PID konektora i serwera www możemy pooglądać sobie jak pracują w naturalnym środowisku, nękane przez namolne kliknięcia w okno przeglądarki: ![]() gdy brak Meratronika No to trzeba było nie rozbierać na zegarek. To sarkazm był, a na poważnie, zaproponowana dekompozycja całości na składniki umożliwia zastępowanie jednych elementów innymi, grunt to spełnić założenia poczynione przy projekcie interfejsu. I tak na przykład konektor v543 współpracujący z prawdziwym multimetrem możemy spokojnie zastąpić jego swoistym emulatorem - małą aplikacyjką rozumiejącą zdefiniowane komendy SCPI i odpowiadającą z góry ustalonymi wartościami. Przecież serwerowi meraHttpServer jest wszystko jedno z kim rozmawia, póki tak dobrze się rozumieją, prawda? Kod prostej udawaczki Meratronika znajduje się tu: ⇨ meraemu.py Efekty pracy: ![]() ![]() I na żywo: niezdrowa ciekawość Ludzi wścibskich osobiście nie trawię, ale podsłuchiwanie jak rozmawiają ze sobą dwie aplikacje sieciowe wydaje się być interesującym zajęciem, tym bardziej że wszelkie narzędzia ku temu przydatne są w domyślnej instalacji malinowego Linuksa. Ot, wystarczy zdobyć PID emulatora i/lub serwera: ![]() a następnie uruchomić polecenie strace wskazując wybrany do podsłuchiwania proces: strace -p 673 -f -e trace=network -s 10000I wtenczas na konsoli dostaniemy: ![]() Taki prosty trace sieciowy może pomóc nam w diagnostyce aplikacji lub ewentualnie inżynierii odwrotnej, gdy przykładowo musimy napisać własną aplikację-udawaczkę, chyba warto zapoznać się z tym sympatycznym programikiem. Przedstawione tu oprogramowanie zadziała też pod Windows, cała sztuka to znieczulić systemową ogniościankę na sieciową aktywność Pythona. ![]() ![]() :debug:exit O Raspberry PI napisano już bardzo wiele, więc z moje strony może tylko tyle, że nie ma co demonizować tego systemiku i traktować jako czegoś szczególnie magicznego. To zwykły system mikroprocesorowy bazujący na dość mocnym ARM z linuksem (a'la Debian) na pokładzie. Oswojenie go tak, aby skakał przez kijek nie jest jakimś szczególnym wyzwaniem. Ma bardzo dużo zalet i sporo pracy potrafi z nas zdjąć. Ma też swoje upierdliwości, z którymi trzeba się będzie pogodzić gdy kto jednak zdecyduje się pójść w tym właśnie kierunku. Ważne natomiast jest to, że obcując z Raspberry PI możemy w pełni wykorzystać swoje dotychczasowe doświadczenia z Linux zdobyte na domowej, dużej maszynie. Próg wejścia jest naprawdę niewielki. W drugą stronę zadziała to tak samo - eksperiencja z przygód z Maliną przyda się do pracy z innymi, poważniejszymi platformami bazującymi na Linux, czy Unix. Do kompletu dochodzi ogromna ilość literatury, na wszelkie możliwe okazje, od administracji po development aplikacji sieciowych, jest w czym wybierać, a zabawa z Maliną jest przednia. #slowanawiatr, maj 2018 |
![]() |
![]() |
![]() |