Korzystając ze środowiska programistycznego LabVIEW, warto zapoznać się z dostępnymi bibliotekami dodatkowych bloczków, rozszerzającymi funkcjonalność środowiska. Dzięki nim często może okazać się, że bloczek, którego aktualnie brakuje nam w domyślnej palecie LabVIEW, został już przez kogoś opracowany i udostępniony publicznie, nie ma więc potrzeby aby oprogramowywać go samodzielnie.

Najlepszym sposobem na zarządzanie dodatkami w LabVIEW jest skorzystanie z programu VI Package Manager, opracowanego przez firmę JKI, dostępnego na stronie http://jki.net/vipm.

Program ten ułatwia instalowanie dodatków z publicznie dostępnych repozytoriów, jak również śledzi potencjalne konflikty oraz aktualizacje.

Po zainstalowaniu programu VIPM i skonfigurowaniu połączenia z LabVIEW, uzyskujemy dostęp do listy dodatków możliwych do zainstalowania:

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17vobpqer1m1q1kcv53114mi16mb3.png
Rysunek 1 VI Package Manager

Biblioteką ułatwiającą pracę w LabVIEW, zawierającą bogaty zbiór przydatnych w praktyce bloczków, jest OpenG Toolkit. Jest to ponadto biblioteka dostępna bezpłatnie.

Poszczególne składniki tej biblioteki możemy doinstalować do LabVIEW, odnajdując je na liście programu VIPM. Aby zainstalować naraz wszystkie składniki OpenG, odszukujemy na liście pozycję o nazwie „OpenG Toolkit”,  a następnie wybieramy opcje Install Package(s).

Po zainstalowaniu biblioteki OpenG, uzyskujemy dostęp do dodatkowej palety na diagramie blokowym.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17vobqc2r1mie1ktv1ia31pqqa8h5.png
Rysunek 2 Paleta OpenG

Przykładowe, przydatne bloczki z biblioteki OpenG:

Wait (ms)__ogtk.vi

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17vobr0ikboje3559c1s1dq5r7.png
Rysunek 3 Bloczek Wait (ms) z OpenG Toolkit

Bloczek opóźnienia z dodaną obsługą błędów. Często stosowany w LabVIEW bloczek wprowadzający opóźnienie nie posiada terminali na klaster błędu, przez co utrudnione jest wymuszenie kolejności jego wykonania, konieczne do tego są dodatkowe struktury. Bloczek z biblioteki OpenG łata tę lukę, ponadto w sytuacji błędu nie wykonuje się, nie wprowadzając dodatkowych opóźnień (opcja konfigurowalna).

Random Number Within Range__ogtk.vi

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17vobr0ikdcq1drrpom1gq713ls8.png
Rysunek 4 Bloczek Random Number Within Range z OpenG Toolkit

Bloczek zwraca liczbę losową z przedziału określonego przez parametry wejściowe. Standardowy dostępny w LabVIEW bloczek Random Number zwraca jedynie wartości z przedziału od 0 do 1.

Slice String 1__ogtk.vi

Bloczek umożliwia wyodrębnienie podzbioru napisu w sposób emulujący notację języka Python – poprzez podanie dwóch liczb (i:j) ustalających sposób określenia początku i długości napisu.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17vobr0il114vf61lv415gcm29.png
Rysunek 5 Bloczek Slice String z OpenG Toolkit

Propozycje ćwiczeń
1.    Zapoznać się z dostępnymi bibliotekami rozszerzającymi funkcjonalność środowiska LabVIEW, zarówno poprzez program VIPM, jak również na stronie: http://www.ni.com/labviewtools/

Dostępna w środowisku LabVIEW struktura zdarzeniowa (Event Structure), pozwala nam w wygodny sposób oprogramować interakcję użytkownika z kontrolkami znajdującymi się na panelu frontowym. Dzięki tej strukturze nie ma potrzeby ciągłego odpytywanie poszczególnych kontrolek w celu stwierdzenia zmiany ich stanu (tzw. polling). Oprócz tego, zdarzenia są rejestrowane nawet w sytuacji, gdy program zajęty jest wykonywaniem innych operacji, co w przypadku pollingu może łatwo spowodować „przegapienie” przez program zmiany stanu kontrolki.

Co jednak zrobić, jeżeli chcemy strukturę zdarzeniową wykorzystać do obsługi przycisku zewnętrznego, podłączonego do wejścia cyfrowego?

Załóżmy, że mamy prosty program zdarzeniowy, w którym naciśnięcie przycisku „Process” powoduje wykonanie operacji wylosowania liczby, natomiast przycisk „Stop” zamyka program.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse012dj76koegbl050g3.png
Rysunek 1 Panel frontowy programu

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse0g7f6j41jfv1m3i1p8r4.png
Rysunek 2 Zdarzenie "Stop"

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse01sjg18eq8h4l3t13v55.png
Rysunek 3 Zdarzenie "Process"

Chcemy rozbudować ten program w taki sposób, aby funkcjonalność przycisku „Process” na panelu frontowym była zdublowana przez przycisk fizyczny, podłączony do wejścia cyfrowego urządzenia typu Multifunctional I/O.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqh2bgvri1v071bs099e3s9h.png
Rysunek 4 Przykład podłączenia przycisku do urządzenia Multifunctional I/O

Aby umożliwić odczyt sygnałów z wejść cyfrowych, należy upewnić się, że zainstalowana jest biblioteka akwizycji danych DAQmx.

W pierwszym kroku dodamy do programu dodatkową pętlę równoległą, w której będziemy odczytywać stan jednego z wejść cyfrowych urządzenia. W tym celu korzystamy z kontrolek palety Measurement I/O -> NI-DAQmx.

Umieszczamy na diagramie blokowym kolejno bloczki:

  • DAQmx Create Task – utworzenie nowego zadania pomiarowego.

  • DAQmx Create Channel – utworzenie nowego kanału. Określamy, które z wejść cyfrowych ma być odczytywane. W przykładzie była to linia Dev1/port1/line3. Jako instancję bloczka ustawiamy Digital Input.

  • DAQmx Start Task – uruchomienie zadania pomiarowego.

  • DAQmx Read – odczyt danych z portu. Jako instancję wybieramy Digital -> Single Channel -> Single Sample -> Boolean (1 Line).

  • DAQmx Stop Task – zatrzymanie zadania pomiarowego.

  • DAQmx Clear Task – usunięcie zadania pomiarowego.

Bloczek  DAQmx Read zapętlamy w pętli while. Cykl działania pętli ustawiamy na 25 milisekund (bloczek  Wait until next ms multiple). Tworzymy dodatkową kontrolkę umożliwiająca zatrzymanie pętli równoległej – DI Stop. Wartość tej kontrolki przed pętlą ustawiamy na false. W zdarzeniu „Stop” pętli głównej zapisujemy do tej kontrolki wartość true, co umożliwi zatrzymanie pętli równoległej. Kontrolkę DI Stop chowamy, aby nie była widoczna na panelu frontowym.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse01lesb8sp6c57p4ea6.png
Rysunek 5 Odczyt stanu wejścia cyfrowego   

Aby zrealizować zdarzeniową obsługę zmiany stanu linii cyfrowej, skorzystamy z mechanizmu zdarzeń użytkownika. W tym celu klikamy prawym przyciskiem myszy na brzeg struktury Event i wybieramy opcję Show Dynamic Event Terminals.

Kontrolki do obsługi zdarzeń użytkownika znajdują się w palecie Dialog & User Interface -> Events.

Nowe zdarzenie użytkownika tworzymy umieszczając na diagramie blokowym bloczek Create User Event. W LabVIEW, oprócz informacji o wystąpieniu zdarzenia, możemy przesłać również dodatkowego dane, potrzebne do jego obsłużenia. Zdefiniowany typ danych należy podłączyć do wejścia user event data type bloczka Create User Event. W naszym przypadku nie ma potrzeby definiowania takiego typu, ponieważ wystarczająca dla nas będzie informacja, że zdarzenie wystąpiło. Ponieważ jednak wejście to jest wymagane, podłączamy do niego jeden z podstawowych typów danych – stałą boolowską.  Aktywujemy etykietę tej stałej (poprzez kliknięcie prawym przyciskiem myszy i wybór opcji Visible items -> Label) i wpisujemy do niej nazwę (np. DI) , która będzie jednocześnie nazwą zdarzenia użytkownika. Ułatwi to jego identyfikację na liście zdarzeń.

Aby zarejestrować nowo utworzone zdarzenie, umieszczamy na diagramie blokowym bloczek Register For Events i podpinamy do jego wejścia wyjście z dodanego wcześniej bloczka Create User Event. Wyjście event registration refnum podłączamy do wejścia rejestracji zdarzeń dynamicznych struktury Event.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse15nh8hn12ucor41o6d7.png
Rysunek 6 Utworzenie i zarejestrowanie zdarzenia użytkownika

Aby nowo utworzone zdarzenie było obsługiwane przez ten sam kod, co zdarzenie dla przycisku „Process”, klikamy prawym przyciskiem myszy na nagłówku struktury Event i wybieramy opcję Edit Events Handled By This Case… Następnie klikamy przycisk Add Event i dodajemy do listy zdarzenie Dynamic <DI>.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse1783157714vt0482s8.png
Rysunek 7 Dodanie obsługi zdarzenia użytkownika

Nowo dodane zdarzenie użytkownika chcemy uruchamiać w sytuacji, gdy stan wejścia cyfrowego zmieni się z 0 na 1. Aby wykryć zmianę stanu linii stosujemy mechanizm rejestru przesuwnego, przy pomocy którego porównujemy aktualnie odczytaną wartość z wartością z poprzedniej iteracji pętli while. Jeżeli aktualna wartość jest większa niż poprzednia to znaczy, że mamy do czynienia z narastającym zboczem sygnału i należy wywołać zdarzenie.

Wywołanie zdarzenia użytkownika realizujemy przy pomocy bloczka Generate User Event, do którego podpinamy nitkę z utworzonym wcześniej zdarzeniem użytkownika oraz jakąkolwiek wartość odpowiadającą określonemu wcześniej typowi danych zdarzenia.

Na końcu, kiedy użytkownik wybierze opcję „Stop”, poza pętlą while niszczymy utworzone wcześniej zdarzenie użytkownika przy pomocy bloczka Destroy User Event.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17umqgse1v15tbho1k1g3k1teu9.png
Rysunek 8 Ostateczny diagram programu obsługi przycisku

Propozycje ćwiczeń i modyfikacji
1. Do sprawdzania stanu przycisku zastosować Stosowy Rejestr Przesuwny (Stacked Shift Register)
2. Zmodyfikować program tak, aby zdarzenie wywoływane było po przytrzymaniu przycisku przez określony czas.
3. Dodać do programu obsługę drugiego przycisku.
4. Dodać do programu obsługę błędów

28

(0 odpowiedzi, napisanych Programowanie w LabVIEW)

Do komunikacji szeregowej w środowisku LabVIEW wykorzystujemy bloczki z grupy Instrument I/O -> Serial lub Instrument I/O -> VISA. Aby bloczki te były dostępne, należy upewnić się, że zainstalowane zostały sterowniki Device Drivers.

Zaczynamy od umieszczenia na diagramie blokowym kontrolek realizujących nawiązanie oraz zamknięcię sesji połączenia z portem szeregowym. Są to odpowiednio VISA Open oraz VISA Close dostępne w palecie VISA -> VISA Advanced.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li318lb55ilp11nje1i623.png
Rysunek 1 kontrolki realizujące połączenie z portem szeregowym

Zastosowanie powyższych kontrolek spowoduje nawiązanie połączenia z użyciem domyślnych parametrów (baud rate: 9600, data bits: 8, parity: none, stop bits: 1, flow control: none).

Jeżeli chcemy komunikować się z wykorzystaniem parametrów transmisji innych niż domyślne, zamiast z VISA Open możemy skorzystać z kontrolki VISA Configure Serial Port, dostępnej w palecie Serial.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li411r6pt510pku1o2t54.png
Rysunek 2 Konfiguracja parametrów portu szeregowego

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li4f1j1c1j1qiq1qus1qf05.png
Rysunek 3 Odbieranie danych z portu szeregowego

Aby zrealizować odbieranie danych z portu szeregowego, pomiędzy kontrolki otwarcia i zamknięcia sesji wstawiamy pętlę while, w której będziemy sprawdzać, czy w buforze portu znajdują się dane gotowe do odczytu. W tym celu stosujemy bloczek Bytes at port, dostępny w palecie Serial. Jeżeli wartość zwrócona przez ten bloczek jest większa od 0, przekazujemy ją do kontrolki VISA Read, co spowoduje odczytanie z portu takiej liczby bajtów, jaka aktualnie znajduje się w nim do odczytu. Odczytany w ten sposób komunikat możemy wyświetlić lub przesłać do dalszego przetwarzania.

Stosujemy w tym przypadku standardowy szablon aplikacji z pętlą while, z okresem taktowania określonym przez kontrolkę Wait until nex ms multiple, warunkiem stopu aktywnym w momencie naciśnięcia przez użytkownika przycisku Stop na panelu frontowym.

Odporność na błędy

Praktyka pokazuje, że taki sposób odczytu danych z portu szeregowego nie jest odporny na mogące pojawić się w trakcie komunikacji zakłócenia. Na przykład możliwa jest sytuacja, w której komunikat o całkowitej długości 10 bajtów, pojawi się w buforze w dwóch partiach, po 6 i 4 bajty. Wówczas kontrolka Bytes at port zwróci najpierw wartość 6, a w kolejnej iteracji wartość 4, co spowoduje odczytanie i przesłanie przez program do dalszego przetwarzania dwóch osobnych fragmentów, zamiast całego komunikatu. Może to spowodować błąd lub niepoprawne rozpoznanie treści komunikatu przez kolejne bloczki.

Aby uchronić się przed taką sytuacją, stosujemy podczas odczytu z portu szeregowego dodatkowy bufor, w którym przechowujemy dane do momentu otrzymania kompletnego komunikatu. W tym celu konieczne jest określenie, jakim znakiem kończy się dany komunikat, najczęściej jest to znak końca linii ‘\n’.

Aby zrealizować doklejanie każdego nowo odczytanego napisu do bufora, stosujemy mechanizm rejestru przesuwnego. Przed uruchomieniem pętli while jako wartość początkową rejestru ustawiamy stałą będącą pustym napisem (Empty String Constant). Każdorazowo odczytane dane z portu szeregowego doklejamy do bufora przy pomocy kontrolki Concatenate Strings. Jednocześnie sprawdzamy, czy ostatni znak odczytanego napisu jest równy znakowi końca linii. W tym celu stosujemy kontrolkę String Subset, której jako offset podajemy długość napisu określoną przez bloczek String Length, a następnie pomniejszoną o 1, a jako length wartość 1. Jeżeli stwierdzimy, że otrzymaliśmy cały komunikat, wyświetlamy go lub przesyłamy do dalszego przetwarzania, a do rejestru przesuwnego przesyłamy pusty napis. Jeżeli komunikat jeszcze nie jest kompletny, aktualnie znajdujące się w buforze dane nie są przetwarzane, lecz przesyłane poprzez rejestr przesuwny na wejście kolejnej iteracji pętli while.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li52gqti1nq91gbnt106.png
Rysunek 4 Odbieranie danych z wykorzystaniem bufora

Odbieranie oraz wysyłanie danych

Jeżeli chcemy w programie umożliwić oprócz odbierania danych również ich nadawanie poprzez wysyłanie wpisywanych przez użytkownika komunikatów, możemy do tego celu wykorzystać strukturę zdarzeniową Event Structure.
Do odbierania danych wykorzystujemy zdarzenie Timeout, którego parametr czasowy określamy, tak jak poprzednio ustawiony okres taktowania pętli while.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li5qo1deoi801408jcc7.png
Rysunek 5 Zdarzeniowe odbieranie danych

Wysyłanie danych z kontrolki tekstowej Message będzie realizowane po naciśnięciu przez użytkownika przycisku Send. W tym celu dodajemy do listy zdarzeń struktury Event zdarzenie „Send” : Mouse up. Wewnątrz diagramu dla tego zdarzenia umieszczamy bloczek VISA Write, na którego wejście przesyłamy wpisany przez użytkownika komunikat z doklejonymi znakami powrotu karetki oraz końca linii: „\r\n”. Dodatkowo przed wysłaniem komunikatu sprawdzamy, czy nie jest on pusty przy pomocy kontrolki Empty String/Path?. W przypadku zaistnienia takiej sytuacji zapis nie zostanie zrealizowany.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukc1li5i0k1vlv280q581a7i8.png
Rysunek 6 Zdarzeniowe wysyłanie danych

Propozycje ćwiczeń i modyfikacji
1.    Zmodyfikować program tak, aby zamiast wyświetlania ostatnio odczytanego komunikatu, wyświetlana była cała historia komunikacji z automatycznym przewijaniem w miarę otrzymywania kolejnych komunikatów.
2.    Przetestować komunikację pomiędzy dwoma instancjami programu uruchomionymi na jednym komputerze z wykorzystaniem emulatora portów szeregowych COM0COM.
3.    Do sprawdzenia ostatniego znaku komunikatu wykorzystać bloczek Slice String, znajdujący się w bibliotece OpenG, instalowanej przez program VI Package Manager.

29

(0 odpowiedzi, napisanych Programowanie w LabVIEW)

Środowisko programistyczne LabVIEW posiada wiele skrótów oraz udogodnień kontekstowych, uaktywnianych poprzez kliknięcie prawym klawiszem myszy w odpowiednim miejscu.

Warto znać te skróty, aby zwiększyć efektywność korzystania z LabVIEW.
Poniżej zamieszczone zostały często wykorzystywane i przydatne funkcje.

Szybki dostęp do palety

Klikając prawym przyciskiem myszy na kontrolkę z danej palety lub na przewód określonego typu, możemy uzyskać szybki dostęp do odpowiadającej mu palety bloczków. Jest to szczególnie przydatne w sytuacji gdy dostęp do palety wymaga przejścia przez kilka poziomów w głównej palecie funkcji.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfed1ubf1mcd3ns1ph81v385.png
Rysunek 1 Szybki dostęp do palety Cluster, Class & Variant po kliknięciu na przewód typu Cluster

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfedi921t5qnbt1pgi1th06.png
Rysunek 2 Szybki dostęp do palety VISA po kliknięciu na VI z tej palety

Zmiana tła panelu frontowego

Aby zmienić tło panelu frontowego, klikamy prawym przyciskiem myszy w obszarze poziomego lub pionowego paska przewijania i wybieramy opcję Properties. W wywołanym oknie uzyskamy możliwość ustawienia tła na jeden z predefiniowanych lub własny obrazek.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfeek94nvj1p1v119a13c8.png
Rysunek 3 Panel frontowy ze zmienionym tłem

Zamiana podłączenia wejść kontrolki
Jeżeli chcemy zamienić ze sobą miejscami przewody podłączone do dwuwejściowej kontrolki (na przykład odejmowanie lub dzielenie z palety Numeric), wystarczy kliknąć w pobliżu tych wejść z przytrzymanym prawym klawiszem Alt (kursor zmieni kształt na strzałkę z pętelką).

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfee1d0v3kud6uq5881u7.png
Rysunek 4 Zamiana podłączenia wejść kontrolki

Rozciągalne kontrolki

Wiele kontrolek w LabVIEW posiada możliwość zmiany rozmiaru, w celu zwiększenia liczby dostępnych wejść lub wyjść. Dzięki temu nie ma potrzeby powielania ich na diagramie blokowym. Możliwość ta jest szczególnie przydatna podczas pracy z typami złożonymi, jak tablice lub klastry. Warto zwrócić uwagę na diagram kontrolki w okienku pomocy kontekstowej (CTRL+H), ponieważ opcja ta jest w przypadku tego typu kontrolek zaznaczona.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfeddup1tij1tsk1bah8rg3.png
Rysunek 5 Wykorzystanie opcji zmiany rozmiaru kontrolki Index Array

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ukbdfed1lsueeh1c1v16rk1lfi4.png
Rysunek 6 Sposób oznaczenia możliwości zmiany rozmiaru kontrolki w oknie pomocy kontekstowej

30

(0 odpowiedzi, napisanych Programowanie w LabVIEW)

Wstęp

Środowisko programistyczne LabVIEW znajduje zastosowanie w automatyce, robotyce, mechatronice, czyli w obszarach, w których mamy do czynienia z pomiarami, sterowaniem, akwizycją i przetwarzaniem danych oraz potrzebą integracji tych funkcji w jednym programie.

Cechy wyróżniające środowisko LabVIEW:

  • Graficzny język programowania (G) – opracowanie programu sprowadza się do łączenia bloczków na diagramie blokowym,

  • Programowanie wysokiego poziomu – LabVIEW wyposażone jest w dużą liczbę bibliotek i bloczków funkcyjnych, które pozwalają programiście skupić się na realizacji zadania stawianego przed programem, a nie na szczegółach implementacji (zarządzanie pamięcią, obsługa sterowników urządzeń),

  • Szybkie prototypowanie – w LabVIEW jednocześnie powstaje zarówno diagram blokowy, określający logikę działania programu, jak również panel frontowy zawierający elementy interfejsu użytkownika. Takie podejście znacząco zwiększa efektywność tworzenia aplikacji i umożliwia powstanie pierwszego prototypu w bardzo krótkim czasie.


Instalacja środowiska programistycznego LabVIEW

Środowisko programistyczne LabVIEW można pobrać w pełni funkcjonalnej wersji 30-dniowej ze strony www.ni.com/labview. Dla studentów uczelni technicznych dostępne są specjalne klucze licencyjne, umożliwiające zniesienie ograniczeń czasowych i wykorzystywanie pełni możliwości środowiska do celów edukacyjnych. Producent LabVIEW – firma National Instruments – organizuje ponadto internetowe szkolenia wprowadzające w tematykę LabVIEW, podczas których również można otrzymać edukacyjny klucz licencyjny.

Z uwagi na dużą liczbę dostępnych pakietów i bibliotek dodatkowych, podczas instalacji warto wybrać jedynie te funkcje, które będą rzeczywiście wykorzystywane, aby niepotrzebnie nie obciążać systemu operacyjnego usługami działającymi w tle. W razie potrzeby dodatkowe biblioteki można doinstalować w każdej chwili.
Do nauki podstaw, wystarczająca jest instalacja samego środowiska, bez dodatkowych bibliotek (opcja LabVIEW Core podczas instalacji).

Pierwszy program w LabVIEW

Po uruchomieniu LabVIEW w wersji 2012, pojawia się następujący ekran startowy:
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86r1le1unt11lr1mdltcc3.png
Rysunek 1 Ekran startowy LabVIEW

Pracę nad nowym programem najlepiej zacząć od utworzenia nowego projektu. Projekt ułatwia nam zapanowanie nad wszystkimi programami i podprogramami (a każdy z nich zapisywany jest w osobnym pliku na dysku), jak również jest niezbędny do tego, aby utworzyć dla opracowanej aplikacji plik exe.

W celu utworzenia nowego projektu, z menu File wybieramy opcję „Create Project…”. LabVIEW następnie wyświetla kreator, który umożliwia skorzystanie z dostępnych szablonów. Tym razem jednak zaczniemy od pustego projektu (opcja Blank Project).

W LabVIEW, każdy program i podprogram zapisywany jest w osobnym pliku o rozszerzeniu .vi (od Virtual Instrument). Mówiąc o programach LabVIEW, bardzo często używa się właśnie określenia „VI”.

Aby utworzyć nowy VI, z menu File projektu wybieramy opcję „New VI” lub klikamy prawym przyciskiem myszy na ikonę „My Computer” w drzewie projektu, a następnie z menu wybieramy opcje New -> VI.

Po wybraniu tej opcji LabVIEW wyświetli nam dwa okna. Na pierwszym z nich (Front Panel) zamieszczamy kontrolki odpowiadające za elementy interfejsu użytkownika. Drugie okno (Block Diagram) służy do opracowywania logiki działania programu w postaci diagramu blokowego.

Przydatne skróty klawiszowe:

  • CTRL + E – przełączanie się pomiędzy oknami,

  • CTRL + T – równomierne rozłożenie okien obok siebie na ekranie.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86r1nel10bs1d7e19c8s494.png
Rysunek 2 Panel Frontowy i Diagram Blokowy LabVIEW

Praca z LabVIEW polega na wybieraniu bloczków potrzebnych do zrealizowania programu z odpowiednich palet. Dostęp do palet uzyskujemy poprzez kliknięcie prawym przyciskiem myszy w pustym obszarze okna. Dla panelu frontowego będą to palety zawierające kontrolki interfejsu, dla diagramu blokowego – kontrolki funkcyjne.

Tworzenie nowego programu zaczniemy od umieszczenia dwóch kontrolek na panelu frontowym. Będzie to suwak, umożliwiający nastawienie określonej wartości oraz termometr, na którym ta wartość będzie wyświetlana.

W tym celu klikamy prawym przyciskiem myszy w puste pole panelu frontowego i umieszczamy na nim następujące kontrolki:

  • Silver -> Numeric -> Vertical Pointer Slide

  • Silver -> Numeric -> Thermometer

Po umieszczeniu kontrolek na panelu frontowym mamy możliwość dostosowania ich wielkości (wymaga to niekiedy bardzo precyzyjnego operowania myszą) oraz skali (w tym celu wystarczy kliknąć dwukrotnie w liczbę na skali, a następnie wpisać pożądaną wartość).

Wszystkie właściwości kontrolek możemy ponadto skonfigurować poprzez kliknięcie prawym przyciskiem myszy na kontrolce i wybranie opcji Properties.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86siev9ulac1a0ule45.png
Rysunek 3 Panel frontowy programu po dopasowaniu wielkości i skali kontrolek

Po umieszczeniu kontrolek na panelu frontowym zauważamy, że odpowiadające im bloczki pojawiły się automatycznie na diagramie blokowym.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86s1mvt114g4e31oo2med6.png
Rysunek 4 Diagram blokowy po umieszczeniu kontrolek na panelu frontowym

Kontrolka „Vertical Pointer Slide” jest kontrolką wyjściową (taką, z której dane wypływają), co można poznać poprzez trójkątny konektor wyjściowy z jej prawej strony, jak również zamalowaną ramkę. Kontrolka „Thermometer” jest kontrolką wyświetlającą dane, co można poznać poprzez wejście z lewej strony, jak również niezamalowaną ramkę. Kolor pomarańczowy obu kontrolek oznacza, że są one przeznaczone do przechowywania liczb rzeczywistych, co jest również sygnalizowane literkami DBL (od „double”).

Aby umożliwić wyświetlanie przez termometr wartości nastawionej na suwaku, należy połączyć wyjście suwaka z wejściem termometru.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uka5cvr1cfdvbq13kb86h1due3.png
Rysunek 5 Połączenie między kontrolkami diagramu blokowego

Następnie możemy ustawić określoną wartość na suwaku, a po kliknięciu strzałki uruchamiającej program (Run) zauważymy, że wartość ta pojawiła się również na kontrolce wyjściowej – termometrze.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86tt8p1lv4qgc1ejma158.png
Rysunek 6 Przycisk uruchamiający program w LabVIEW

Wykonanie programu w ten sposób jest jednak jednorazowe. Po przesłaniu wartości do termometru program kończy swoje działanie. Aby umożliwić działanie programu w sposób ciągły, należy skorzystać z dostępnych w LabVIEW pętli. Do sterowania działaniem programu do momentu aż użytkownik zechce zakończyć jego pracę służy pętla While. Petlę while umieszczamy na diagramie blokowym, wybierając ją z palety Programming -> Structures -> While Loop, a następnie otaczając nią te elementy diagramu blokowego, które chcemy zapętlić.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86tc0q1mjglr21reghv09.png
Rysunek 7 Diagram blokowy z pętlą while

Po wstawieniu pętli możemy zauważyć, że programu nie da się uruchomić. Przycisk uruchamiania programu przybiera postać złamanej strzałki, co sygnalizuje obecność błędów. Klikając ten przycisk otrzymujemy okno z informacjami o błędach. Z tego okna możemy odczytać zarówno na czym polegają poszczególne błędy, możemy również klikając dwukrotnie na poszczególne pozycje na liście, przejść do fragmentów diagramu blokowego, które są źródłem błędu.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86tthl1pgd58a1dsfomla.png
Rysunek 8 Lista błędów w LabVIEW

W tym przypadku błąd polega na tym, że pętla while nie ma ustalonego warunku stopu, nie wiadomo więc kiedy ma zakończyć swoje działanie. W celu ustalenia warunku stopu pętli while, wystarczy umieścić w programie przycisk STOP, a następnie podpiąć go do terminala warunkowego pętli, znajdującego się w jej prawym dolnym rogu. Taki efekt można uzyskać na różne sposoby, najprostszą metoda jest kliknięcie prawym przyciskiem myszy na wejściu terminala warunkowego na diagramie blokowym, a następnie wybranie opcji Create Control. Możemy również na panelu frontowym z palety Boolean wybrać kontrolkę Stop Button, a następnie na diagramie blokowym wciągnąć ją do pętli while i podpiąć jej wyjście do wejścia terminala warunkowego pętli.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86t1m29m5h1d4d1820c66b.png
Rysunek 9 Pętla while z warunkiem stopu

Po wykonaniu tej operacji i uruchomieniu programu widzimy, że działa on już w sposób ciągły. Wartości ustawiane na suwaku są wyświetlane na termometrze do momentu, aż użytkownik zakończy działanie programu przyciskiem STOP.
LabVIEW umożliwia również podejrzenie kolejności wykonania poszczególnych operacji programu. W tym celu należy kliknąć przycisk żarówki „Highlight Execution”, dostępny w oknie diagramu blokowego. Uruchomienie programu z załączoną opcją podglądu powoduje wykonanie go w zwolnionym tempie z animacją przepływu poszczególnych danych.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86t1esi1cmo10ums9j10o6c.png
Rysunek 10 Podgląd wykonania programu

Program z pętlą while działa tak szybko, na ile pozwala mu system operacyjny, co może powodować zbyt częste odczytywanie danych z kontrolek wejściowych niż jest to rzeczywiście potrzebne. Efekt ten możemy zaobserwować poprzez umieszczenie kontrolki wyświetlającej wartość aktualnej iteracji pętli while. W tym celu należy utworzyć kontrolkę wyświetlającą dane z konektora „i”, widocznego w lewym dolnym rogu pętli while. Najszybciej można to zrealizować poprzez kliknięcie prawym przyciskiem myszy na ten konektor i wybranie opcji „Create Indicator”.

Aby ograniczyć szybkość działania programu, korzystamy z bloczka „Wait until next ms multiple”, dostępnego w palecie „Timing” na diagramie blokowym. Bloczek ten należy umieścić wewnątrz pętli, której czas wykonania ma kontrolować, a na jego wejście należy podpiąć wartość numeryczną, odpowiadająca liczbie milisekund, z jaką ma wykonywać się każda iteracja pętli. Najszybszym sposobem na podłączenie stałej na wejście tej kontrolki jest kliknięcie na jej wejściu prawym przyciskiem myszy, wybranie opcji „Create Constant”, a następnie wpisanie pożądanej wartości. Do uzyskania płynności działania interfejsu użytkownika, wystarczająca jest wartość 25 milisekund.

Po umieszczeniu tych kontrolek na diagramie blokowym możemy zauważyć, że mają one kolor niebieski. Kolorem takim w LabVIEW oznaczane są liczby całkowite.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86u65m1rujkct8931rnkd.png
Rysunek 11 Program z kontrolą czasu wykonania pętli while

LabVIEW udostępnia nam wiele innych dodatkowych kontrolek i funkcji. Możemy na przykład umieścić na panelu frontowym diodę, która będzie sygnalizować nam sytuację, gdy odczytywana wartość przekroczy zadany próg alarmowy.
Aby zrealizować taką funkcję, na panelu frontowym umieszczamy kontrolkę LED. Kontrolce tej zmieniamy nazwę na „Alarm”, a we właściwościach możemy dopasować jej kolor (np. czerwony).

Następnie na diagramie blokowym umieszczamy kontrolkę „Greater” z palety „Comparison”. Do górnego z jej wejść podpinamy wartość odczytywaną z suwaka, dolne wejście natomiast podłączamy do kontrolki lub stałej ustalającej wartość progową. Wyjście kontrolki „Greater” podłączamy na wejście diody LED, umieszczonej wcześniej wewnątrz pętli while.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86u9jq1h7016495lu1dhse.png
Rysunek 12 Program z sygnalizacją przekroczenia progu alarmowego

LabVIEW pozwala nam również w prosty sposób wizualizować odczytywane dane na wykresach. W tym celu możemy na panelu frontowym umieścić kontrolkę „Waveform Chart”. Następnie na diagramie blokowym do wejścia tej kontrolki podpinamy wartość odczytywaną z suwaka.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86u1nes17pgac51t5517b8f.png
Rysunek 13 Program z wizualizacją danych na wykresie

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86u1lceeu98q13elbajg.png
Rysunek 14 Panel frontowy ostatecznej wersji programu

Na podstawie opracowanego w ten sposób programu, możemy utworzyć plik wykonywalny exe, którego uruchomienie będzie możliwe na komputerze nie wyposażonym w środowisko LabVIEW.

W tym celu w oknie projektu klikamy prawym przyciskiem myszy na „Build Specifications” i wybieramy opcję New -> Application (EXE). Kreator tworzenia pliku wykonywalnego posiada wiele opcji, jednak aby utworzyć taki plik wystarczy w grupie „Source Files” wskazać ten VI, który ma zostać wywołany przy uruchomieniu programu. W naszym przypadku taki VI jest jeden.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17uk9u86vdaf1o9gebff0n8v4h.png
Rysunek 15 Konfiguracja pliku wykonywalnego EXE

Po poprawnym skonfigurowaniu i kliknięciu przycisku „Build”, na dysku zostanie utworzony plik wykonywalny, który następnie możemy uruchomić tak, jak każdy inny program w systemie operacyjnym. Program taki również możemy uruchomić na innym komputerze. W zależności od tego, jak dużo komponentów LabVIEW używa program, do jego uruchomienia może okazać się konieczna instalacja bibliotek uruchomieniowych „LabVIEW Runtime Engine”, dostępnych na stronie producenta.

31

(0 odpowiedzi, napisanych Programowanie obiektowe (C++))

W literaturze i środowiskach programistycznych można spotkać się z różnymi określeniami metod, dających dostęp do pól prywatnych lub chronionych w klasach.

Sposób pierwszy:
- get accessor
- set accessor

Sposób drugi:
- getter
- setter

Sposób trzeci:
- akcesor (na metody typu get)
- modyfikator (na metody typu set)

Stąd może wynikać to, że czasem na oba mówimy "akcesor", a czasem ktoś wyróżnia "modyfikator".

Nie spotkałem się z żadną akademicką dyskusją na temat tego, który sposób jest poprawny, więc myślę, że wszystkie należy uznać za dopuszczalne.

Fusion Charts Free

Sencha Ext JS

Google Charts

Do Visual Studio dostępnych jest bardzo dużo przydatnych i łatwych w instalacji rozszerzeń.

Aby uzyskać do nich dostęp, wybieramy opcję Extension Manager z menu Tools.
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17e70d0niue51kra1bcmq2l7bj3.png

Następnie możemy kliknąć w zakładkę "Online Gallery", dzięki czemu uzyskamy możliwość przeglądania bazy dostępnych rozszerzeń.

Jeżeli narzędzie nie może się połączyć, możemy również przeglądać galerię, pobierać i instalować rozszerzenia bepzośrednio na stronie Visual Studio Gallery

Indent Guides
Rozszerzenie wyświetla w kodzie dodatkowe linie odpowiadające kolejnym wcięciom.
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17e70mse0606ud61lm11rmjdri5.png
(opcję można wyłączyć w menu Edit -> Advanced -> View Indent Guides)

Go To Definition
Umożliwia przejście do definicji funkcji lub zmiennej poprzez kliknięcie w jej nazwę z przytrzymanym klawiszem CTRL (zamiast wciskania F12). Baaardzo przydatne i umilające pracę (szczególnie dla osób, które znają tę opcję z innych IDE, np. Netbeans).

Editor Guidelines
Umożliwia wyświetlenie w edytorze dodatkowych pionowych linii nawigacyjnych (jeżeli na przykład chcemy łatwiej zapewnić, że nie przekroczymy określonej liczby znaków w linii).
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17e7o9voj17ii1sqn8419hi6543.png
W celu ustawienia takiej linii możemy wprowadzić odpowiedni klucz w rejestrze:
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor
Nazwa wartości: Guides, typ: wartość ciągu, przykładowa zawartość: RGB(255,0,0) 99
Możemy również skorzystać z rozszerzenia Editor Guidelines UI).

CodeMaid
Udostępnia dodatkowe narzędzia porządkowania kodu, w postaci usunięcia niepotrzebnych linii, spacji itp...

Productivity Power Tools
Znacznie zmienia zachowanie Visuala, dodając opcje typu automatyczne uzupełnianie nawiasów, zaznaczenie aktualnej linii, w której jest kursor, przyklejanie zakładek, dodawanie pionowych linii nawigacyjnych i wiele innych drobnych ułatwień (również CRTL + click).
Polecane raczej dla bardziej zaawansowanych użytkowników, ponieważ może wymagać dostosowania do indywidualnych preferencji (poprzez opcje w menu Tools).

Wiele z opcji dostępnych w tym rozszerzeniu, zostaje wdrożonych bezpośrednio w kolejnych wersjach Visual Studio (na przykład zaznaczanie całej linii, w której znajduje się kursor jest już domyślnie w Visual Studio 2012).

Local History
Automatycznie zapamiętuje historię dokonywanych zmian w plikach, ułatwiając powrót do kodu z przeszłości.

W przypadku Visual Studio 2012 zalecam zainstalowanie Productivity Power Tools, Indent Guides i Local History

34

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Znajomość bardziej zaawansowanych opcji, może dodatkowo zaoszczędzić nam czas podczas debugowania.

Run to cursor

Uruchomiliśmy w trybie debugowania prosty program i w danej chwili nasz kursor znajduje się na pierwszej pętli for:
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dis2r1fm0nvo21pdpbm0a63.png

Załóżmy, że nie chcemy przechodzić przez wszystkie iteracje tej pętli (czasem może ich być sporo), tylko chcielibyśmy od razu przejść do debugowania drugiej pętli.

Moglibyśmy to zrobić wstawiając obok drugiej pętli breakpoint i wciskając przycisk F5 (Continue):
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dis7hetiaj11ojf9e1jlmgdm5.png

Prostszym sposobem (dodatkowo nie wymagającym późniejszego czyszczenia tego breakpointa) jest kliknięcie prawym przyciskiem myszy w miejscu, do którego chcemy dotrzeć i wybranie opcji "Run To Cursor". Cały kod zostanie wykonany aż do tego miejsca.
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17disanjq1j3vp2cr9l4tt1amm7.png

Set Next Statement

Opcja ta pozwala nam zmienić kolejność wykonywania programu.

Załóżmy na przykład, że nasz program odczytuje dane z pliku.
Po długiej interakcji z programem (wybór opcji z menu, wpisanie nazwy pliku...) stwierdzamy podczas debugowania, że pliku nie udało się otworzyć - program wszedł od razu w warunek wyświetlający komunikat o błędzie:
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dispic31mtd1qbv1pvu10928lu3.png

Szybko zauważamy, że powód jest banalny - zapomnieliśmy umieścić plik we właściwym katalogu.

Naprawiamy ten błąd, wgrywając plik we właściwe miejsce.

Aby nie trzeba było zatrzymywać działania programu, uruchamiać go od nowa i raz jeszcze przechodzić całej procedury interakcji z programem (wybór z menu, wpisywanie nazwy), możemy kliknąć prawym przyciskiem myszy na linii kodu, do której chcielibyśmy wrócić i wybrać polecenie "Set Next Statement".
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17disve0tp7svu01kbl8gqn205.png

Kursor debuggera zostanie przestawiony w to miejsce i będziemy mieć możliwość ponownego przejścia przez ten fragment kodu.


Edycja kodu w trakcie działania programu

Załóżmy, że debugujemy prosty program:
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dit797u1ja41oqobrgdnsmt63.png

Podczas debugowania zdajemy sobie sprawę, że w pętli for, która właśnie ma zacząć działanie, chodziło nam tak naprawdę o zwiększenie elementów tablicy o 3, a nie o 2.

Aby to poprawić, nie musimy wyłączać programu, możemy zmienić kod od razu i kontynuować debugowanie - każdy element tablicy zostanie zwiększony o 3.
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ditbrso131l12nj1chc1ano1ui55.png

Edycja wartości zmiennej w trakcie działania programu

Jeżeli w trakcie debugowania programu zauważymy, że któraś z naszych zmiennych przyjmuje nieprawidłową wartość, a chcemy sprawdzić pozostałą część kodu, możemy zmienić wartość tej zmiennej klikając w nią dwukrotnie w oknie Locals, Watch lub w okienku, które pojawia się po najechaniu na jej nazwę.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17ditlnn61ula1vrfpc21hph1ngt3.png

http://www.student.mvlab.pl/wiedza/img/m/2/t/p17diu7a6g1go2c9t1sqo7sq1oau3.png

Aby otwierając plik do odczytu w programie, nie trzeba było podawać do niego pełnej ścieżki dostępu, najlepiej umieścić plik w tym samym folderze, w którym uruchamiany jest program.

Domyślnie folderem roboczym programu jest folder projektu. Jeżeli wiemy więc czym różnią się solucje od projektów, możemy taki folder na dysku łatwo odszukać.

Innym sposobem na szybkie dotarcie do folderu projektu jest kliknięcie prawym przyciskiem myszy na nazwie projektu w Solution Explorerze i wybranie opcji "Open Folder in Windows Explorer".
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dgg8f1ksbe1kuk14l71das1g8f3.png

Jeżeli chcemy, katalog roboczy projektu możemy zmienić w opcjach projektu. W tym celu klikamy prawym przyciskiem myszy na nazwę projektu, wybieramy opcję Properties, a następnie w zakładce Debugging ustawiamy pole Working Directory.
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dggdjr0nal14t71ecsbrf1g295.png

A jeżeli nie pamiętamy dokładnie nazw plików, które znajdują się w naszym folderze, na początku uruchomienia programu możemy wyświetlić informacje o bieżącym folderze wykorzystując polecenie systemowe "dir":

system("dir");

36

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

W Visual Studio w jednej solucji (rozwiązaniu) może znaleźć się klika projektów.

Na przykład jednym z takich projektów może być biblioteka dll, zawierająca funkcje odpowiadające za logikę programu, a drugim projektem może być aplikacja z interfejsem okienkowym, korzystająca z tych funkcji.

Na początku pracy z Visual Studio będziemy mieć z reguły do czynienia z jednym projektem w solucji. Podczas tworzenia projektu, Visual Studio domyślnie tworzy również solucję o takiej samej nazwie. Efektem tego jest pojawienie się wielu folderów o tych samych nazwach.

Jeżeli na przykład utworzymy nowy projekt i nazwiemy go "Program 1" to zobaczymy, że w miejscu, gdzie go utworzyliśmy, powstał folder "Program 1", a w nim kolejny folder "Program 1". Pierwszy z nich to właśnie folder dla solucji "Program 1", a drugi dla projektu "Program 1".

W folderze solucji umieszczane są pliku konfiguracyjne dla solucji (zawierające niewiele opcji, przede wszystkim to, który projekt jest projektem startowym).

W folderach projektów umieszczane są pliki konfiguracyjne projektu, kody źródłowe, tu również tworzone są foldery z pośrednimi plikami, jakie powstają podczas kompilacji.

Jeżeli nasz program wymaga podania dodatkowych argumentów podczas uruchomienia, możemy na potrzeby debugowania te argumenty ustawić w opcjach projektu, aby nie trzeba było za każdym razem wpisywać ich w okienku konsoli.

W tym celu wchodzimy w opcje projektu, klikając prawym przyciskiem myszy na jego nazwę w Solution Explorerze i wybierając "Properties".
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dgdsjqu14pj1iutkhdoau1tr63.png

W zakładce Configuration Properties -> Debugging, wpisujemy interesujące nas parametry w polu "Command Arguments".
http://www.student.mvlab.pl/wiedza/img/m/2/t/p17dgdt060j343131c8v1647152g5.png

Przykładowy program wyświetlający przekazane argumenty:

#include <stdio.h>

void main(int argc, char **argv) {
    int i;

    for(i = 0; i < argc; ++i) {
        printf("%s\n", argv[i]);
    }

}

38

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Podczas przetwarzania obrazów, możemy chcieć wykonać kilka operacji pod rząd na jednym obrazie, na przykład:

negatyw(&obraz);
progowanie(&obraz);
obrot(&obraz);
zapisz(&obraz, "wynik.pgm");

Jeżeli każdą z tych funkcji zadeklarujemy tak, że będzie zwracała wskaźnik na przetworzony obraz (lub NULL w przypadku błędu), możliwe będzie wywołanie ich w następujący sposób:

zapisz(obrot(progowanie(negatyw(&obraz))), "wynik.pgm");

(warto to przećwiczyć pod kątem przyszłej nauki łańcuchowego wywoływania metod w C++)

Podczas działania programu możemy chcieć wywołać jakieś polecenie systemowe. Polecenia są jednak różne w zależności od systemu operacyjnego.

Na przykład dla systemów unixowych, aby wyczyścić ekran i wyświetlić zawartość katalogu, używamy następujących poleceń:

system("clear");
system("ls -a");

W Windowsie natomiast:

system("cls");
system("dir");

Możemy jednak uzależnić kompilację fragmentów programu od systemu operacyjnego, na przykład:

void wyczysc_ekran() {
#ifdef _WIN32
    system("cls");
#else
    system("clear");
#endif
}
void wyswietl_folder() {
#ifdef _WIN32
    system("dir");
#else
    system("ls -a");
#endif
}

Wówczas w naszym programie nie używamy już poleceń system, tylko naszych funkcji, które zostaną poprawnie skompilowane niezależnie od systemu operacyjnego:

wyczysc_ekran(); //zamiast system("cls");
wyswietl_folder(); //zamiast system("dir");

40

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Jeżeli w systemie operacyjnym mamy do typu plików PGM przyporządkowany jakiś program (np. Irfan View), możemy przyspieszyć sprawdzanie efektów działania programu wykorzystując następujące polecenie:

system(nazwa_pliku);

Wywołanie to będzie analogiczne z dwukrotnym kliknięciem w plik.

Przykład użycia:

negatyw(&obraz);
zapisz(obraz, "tmp.pgm");
system("tmp.pgm");

41

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Często spotykane błędy i ostrzeżenia
warning C4996: This function or variable may be unsafe
error C2143: syntax error : missing ';' before 'type'
error C2065: 'cos_tam' : undeclared identifier
Stack around the variable 'x' was corrupted
Access Violation Reading / Writing Location

Środowisko Visual Studio
Solucje i Projekty
Jak otworzyć projekt z jednej wersji Visual Studio w innej
Kompilacja i uruchomienie programu
Visual Studio kompiluje tylko te pliki, które są w projekcie!
Gdzie umieścić plik, aby program go odczytał?
Jak debugować programy z argumentami w linii poleceń
Wykrywanie wycieków pamięci
Zaawansowane opcje debuggera
Visual Studio - przydatne rozszerzenia

Dobre praktyki programistyczne
Jak się obchodzić ze wskaźnikami
C - konwencje nazewnictwa i styl programowania
Ograniczanie czasu życia zmiennych
Dokumentowanie kodu
Uniezależnianie się od systemu operacyjnego
Jak podzielić program na funkcje
Redukowanie sprzężeń czasowych pomiędzy funkcjami
"Czar" czy "kar" - jak wymawiać słówko "char"?

Przetwarzanie sygnałów
Stałe matematyczne w Visual Studio
Dlaczego program losuje te same liczby?
Odczyt z plików

Przetwarzanie obrazów
Konwerter obrazów do formatu PGM
Dynamiczna alokacja tablicy wielowymiarowej
Podział projektu na wiele plików, strażnik nagłówka
Co to jest LUT?
Splot obrazu
Wyświetlanie obrazu
Łańcuchowe wywołanie funkcji
Jak utworzyć paletę pseudokolorów?

Różne
Narzędzia do generowania wykresów

42

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Splot obrazu (ang. image convolution) sprowadza się do przetworzenia obrazu w następujący sposób:

1. Przygotowujemy sobie tzw. jądro splotu (ang. kernel) o wielkości 3x3 piksele (stosowane są również większe, np. 5x5).
2. Poszczególne piksele obrazu przeliczamy w ten sposób, że dla każdego piksela przemnażamy każdą wartość z kernela przez każdą wartość odpowiadającego temu pikselowi fragmentu obrazu o wielkości takiej jak kernel.
3. Następnie sumujemy poszczególne wyniki, a jeżeli suma elementów jądra jest większa od 1, dzielimy sumę naszych mnożeń przez sumę elementów jądra i zaokrąglamy do wartości całkowitej.
4. Jeżeli otrzymany w ten sposób wynik przekracza dopuszczalną skalę szarości, przycinamy go odpowiednio do jej dolnej lub górnej wartości granicznej - czyli jak jest ujemny to do 0, jak za duży to do maksymalnego poziomu szarości obrazu (można również stosować bardziej złożone sposoby normalizacji wyników wyjściowych).
5. Ostateczny wynik umieszczamy w wynikowej tablicy pikseli (nie można wpisać go od razu do tablicy źródłowej, ponieważ w kolejnej iteracji zostałby wzięty do obliczeń, z uwagi na to, że po obrazie przemieszczamy się okienkiem wielkości 3x3).

Najlepiej taką operację przedstawić obrazowo:
https://developer.apple.com/library/mac/documentation/Performance/Conceptual/vImage/Art/kernel_convolution.jpg
(wynik otrzymany na powyższym obrazku powinien jeszcze zostać dopasowany do skali szarości)

Inny przykład (w wersji interaktywnej powinno być jeszcze dzielenie przez sumę z kernela):
http://www.olympusmicro.com/primer/java … index.html

Wynik tak przeprowadzonej filtracji obrazu zależy od tego, jakie wartości umieścimy w jądrze splotu.

W ten sposób możemy zrealizować np. rozmycie, wyostrzenie, wykrycie krawędzi, uwypuklenie itp.

Przy tego typu przetwarzaniu pojawia się pytanie: co zrobić na krawędzi obrazu?

Najlepszym rozwiązaniem jest tymczasowe powiększenie tablicy o 1 piksel w każdą stronę i przepisanie do tak powstałej krawędzi wartości sąsiednich pikseli. Rozmiar tego powiększenia zależy od tego, jakiej wielkości filtrami chcemy się przemieszczać po obrazie, przykład dla filtrów 5x5:
http://zone.ni.com/reference/en-XX/help … e_borders/

Ponieważ do splotu stosować możemy różne filtry, dobrym rozwiązaniem może być napisanie funkcji realizującej splot, która tablicę z jądrem splotu przyjmuje jako argument wywołania, np:

splot(struct Obraz* o, int[3][3] jadro);

43

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

LUT oznacza Lookup Table (tablica podglądu)
http://pl.wikipedia.org/wiki/Tablicowanie
http://en.wikipedia.org/wiki/Lookup_table

Jeżeli mamy za zadanie przetworzyć obraz w ten sposób, że wartość każdego z pikseli chcemy przeliczyć według określonego wzoru (podczas potęgowania, logarytmowania itp.), to przy rozmiarze na przykład 3000x3000 pikseli, wzór ten będzie musiał być obliczony 9 milionów razy i za każdym razem wynik będzie wstawiany do tablicy pikseli.

Wiemy jednak, że wejściowe wartości brane do obliczeń zawierają się w znanym nam zakresie - piksele mogą przyjmować wartości całkowite od 0 do skali szarości.

Dlatego w celu optymalizacji czasu wykonania przetwarzania, możemy sobie przygotować dodatkową tablicę, o liczbie elementów równej liczbie możliwych wartości na wejściu (na przykład dla skali szarości 255 będzie to 256 elementów).

W każdej komórce tablicy wpisujemy wynik przeliczania naszego wzoru dla poszczególnych wartości pikseli:

lut[i] = funkcja(i);

Następnie podczas przetwarzania obrazu już nic nie obliczamy, tylko wstawiamy do tablicy pikseli wartości z tablicy LUT.

Stosując to rozwiązanie, zajęliśmy dodatkowe miejsce w pamięci, ale dzięki temu nasz wzór przeliczany był tylko 256, a nie 9 milionów razy.

Aby funkcja przetwarzająca obraz w ten sposób była jak najbardziej uniwersalna i mogła działać dla różnych tablic LUT, warto ją napisać w ten sposób, aby tablica LUT była przekazywana jako argument wywołania funkcji, na przykład:

przetwarzaj_obraz_LUT(struct Obraz* o, int* lut);

44

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Kod wyjściowy, niepodzielony:

#include <stdio.h>
#include <stdlib.h>

struct Macierz {
    int wys;
    int szer;
    double** liczby;
};

void wyswietl(struct Macierz mac) {
    int i,j;
    for(i=0; i<mac.wys; i++) {
        for(j=0; j<mac.szer; j++) {
            printf("%lf ",mac.liczby[i][j]);
        }
        printf("\n");
    }
}

void wypelnij(struct Macierz* ma, double w) {
    int i,j;
    for(i=0; i<(*ma).wys; i++) {
        for(j=0; j<(*ma).szer; j++) {
            (*ma).liczby[i][j] = w;
        }
    }
}

void main() {

    struct Macierz m;
    int i;
        
    m.wys = 10;
    m.szer = 5;
    m.liczby = (double**)calloc(m.wys, sizeof(double*));
    for (i=0; i<m.wys; i++) {
        m.liczby[i] = (double*)calloc(m.szer, sizeof(double));
    }
    wypelnij(&m, 3.0);
    wyswietl(m);
}

Plik macierz.h - tworzymy w folderze "Header files", zawiera: deklarację struktury, deklaracje funkcji, tutaj również umieszczamy dokumentację struktur i funkcji, pamiętamy też o "strażniku nagłówka", chroniącym przed wielokrotnym includowaniem:

#ifndef MACIERZ_H
#define MACIERZ_H
/**
 * Struktura reprezentujaca macierz
 */
struct Macierz {
    int wys;
    int szer;
    double** liczby;
};

/* 
 * Wyswietla macierz
 * \param mac macierz wejsciowa
 */
void wyswietl(struct Macierz mac);
/* 
 * Wypelnia macierz
 * \param ma macierz wejsciowa
 * \param w wartosc, ktora macierz zostanie wypelniona
 */
void wypelnij(struct Macierz* ma, double w);

#endif

Plik macierz.c - tworzymy w folderze "Source files", załącza macierz.h i wszystkie biblioteki potrzebne do kompilacji, zawiera definicje funkcji

#include <stdio.h>
#include <stdlib.h>
#include "macierz.h"

void wyswietl(struct Macierz mac) {
    int i,j;
    for(i=0; i<mac.wys; i++) {
        for(j=0; j<mac.szer; j++) {
            printf("%lf ",mac.liczby[i][j]);
        }
        printf("\n");
    }
}

void wypelnij(struct Macierz* ma, double w) {
    int i,j;
    for(i=0; i<(*ma).wys; i++) {
        for(j=0; j<(*ma).szer; j++) {
            (*ma).liczby[i][j] = w;
        }
    }
}

Plik main.c - program główny. Jeżeli oprócz funkcji main() zawiera dodatkowe funkcje, to tutaj umieszczamy do nich dokumentację.

#include <stdio.h>
#include <stdlib.h>
#include "macierz.h"

void main() {

    struct Macierz m;
    int i;
        
    m.wys = 10;
    m.szer = 5;
    m.liczby = (double**)calloc(m.wys, sizeof(double*));
    for (i=0; i<m.wys; i++) {
        m.liczby[i] = (double*)calloc(m.szer, sizeof(double));
    }
    wypelnij(&m, 3.0);
    wyswietl(m);
}

W Visual Studio strażnik nagłówka można zrobić w inny sposób, w pierwszej linijce pliku wpisując:

#pragma once

Wówczas nie potrzeba już żadnych #ifndef itd... To rozwiązanie jest wspierane przez różne kompilatory, nie tylko VS.

Polecam bardzo dobre opracowanie na ten temat:
http://www.ritambhara.in/allocating-mem … y-on-heap/

Alokując tablicę pamiętamy również o jej zwalnianiu:
http://www.ritambhara.in/freeing-memory … sing-free/

Jeżeli podczas uruchomienia programu otrzymujemy tego typu błąd, to najczęściej oznacza on, że w programie wyszliśmy gdzieś za daleko w pamięć.

Sytuacja taka może nastąpić podczas nieprawidłowego indeksowania tablic.

Musimy pamiętać, że tablice w C indeksowane są od zera, czyli dla 10-elementowej tablicy zadeklarowanej jako:

int tab[10];

pierwszy element to tab[0], a ostatni tab[9].

Na przykład dla poniższego programu:

int main(void) {
    int i;
    int tab[10];
    for (i=0;i<=10;++i) {
        tab[i] = i + 1;
    }
    return EXIT_SUCCESS;
}

W trakcie działania pętli for, zmienna "i" osiągnie wartość 10, co spowoduje próbę odwołania do elementu tablicy tab[10], który jest już poza zarezerwowanym dla tablicy obszarem pamięci.

Jeżeli otrzymujemy tego typu błędy, należy dokładnie przejrzeć program w poszukiwaniu tego typu sytuacji, tym bardziej, że taki błąd może pojawić się dopiero na końcu działania programu, a nie w chwili kiedy rzeczywiście nastąpiło błędne odwołanie do pamięci.

W przypadku podanego wyżej przykładu, należy zmienić warunek stopu pętli for:

int main(void) {
    int i;
    int tab[10];
    for (i=0;i<10;++i) {
        tab[i] = i + 1;
    }
    return EXIT_SUCCESS;
}

47

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Na samym początku pliku z funkcją główną programu, umieszczamy blok komentarza według następującego wzoru:

/**
 * \file   nazwa_pliku.c
 * \author Imie Nazwisko
 * \date   miesiac, rok
 */

Jeżeli chcemy na podstawie naszego kodu wygenerować automatycznie dokumentację, przed deklaracją każdej funkcji zamieszczamy podobny blokowy komentarz, zawierający krótki opis działania funkcji, poszczególnych parametrów oraz zwracanej wartości

/**
 * Zlicza liczbę próbek w pliku
 * \param nazwa nazwa pliku
 * \param separator znak, którym rozdzielone są kolumny danych w pliku
 * \return liczba próbek lub wartość -1 w przypadku błędu
 */
int policz_probki(char* nazwa, char separator);

Dzięki takiemu przygotowaniu kodu źródłowego, możemy automatycznie wygenerować dokumentację przy pomocy programu Doxygen.

Jeżeli kod wymaga komentarza to znaczy, że nie jest wystarczająco czytelnie napisany. Zamiast komentarzy należy stosować jasne nazwy zmiennych i funkcji, komentować w ostateczności.

48

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Jeżeli po dokonaniu zmian w programie od razu próbujemy go uruchomić wciskając F5, to w pewnej sytuacji może okazać się, że nie zauważymy żadnej zmiany w działaniu programu.

W normalnej sytuacji Visual Studio pyta, czy przebudować program, jeżeli zauważa, że zostały w nim dokonane zmiany.

Jeżeli natomiast podczas budowania wystąpią błędy, pyta czy uruchomić ostatnią poprawną wersję pliku exe.

W pojawiających się oknach dialogowych można jednak zaznaczyć prośbę o nie wyświetlanie ich ponownie i bardzo prawdopodobne, że ktoś (lub my sami) już kiedyś taką opcję mniej lub bardziej świadomie zaznaczył.

Wówczas Visual nie zapyta o nic, tylko w najgorszej sytuacji po naciśnięciu F5 uruchomi od razu poprzednią wersję programu.

Dlatego zalecam (szczególnie na komputerach w laboratorium) zawsze najpierw kompilować program przez CTRL + F7, a dopiero potem jeżeli widzimy, że nie ma żadnych błędów, uruchamiać go klawiszem F5.

Ponadto jest istotna różnica pomiędzy uruchamianiem przy pomocy F5 a CTRL+F5. Pierwsza z opcji uruchamia program z debuggerem, czyli możemy śledzić jego wykonanie, wstawiać breakpointy i analizować instrukcje linijka po linijce. Skutkiem ubocznym jest jednak natychmiastowe zniknięcie okienka konsoli po zakończeniu działania programu.

Przy uruchamianiu przez CTRL+F5 okienko nie znika, jednak jednocześnie nie działa debugger, dlatego zalecam uruchamiać program klawiszem F5, a jeżeli chcemy zatrzymać okienko konsoli, możemy zrobić to bezpośrednio w programie.

Można to zrobić na przykład instrukcją system("pause"), która ma to do siebie, że działa tylko pod Windowsem i wyświetla standardowy komunikat z prośbą o naciśnięcie dowolnego klawisza, więc nie każdemu może się to rozwiązanie podobać, ale komunikaty z funkcji system można przekierować w nicość pisząc:

system("pause > null");

49

(0 odpowiedzi, napisanych Programowanie proceduralne (C))

Funkcje, z którymi warto się zapoznać, bo mogą okazać się bardzo przydatne:

- fopen
- fclose
- fscanf
- fgetc
- fgets
- fseek
- rewind
- feof

Poniżej zamieszczam wskazówki dotyczące stylu programowania w języku C

Nazwy zmiennych zaczynamy od rzeczownika pisanego małą literą.
Kolejne słowa w nazwach możemy pisać wielką literą lub rozdzielać podkreślnikiem:
(wybieramy jeden sposób i konsekwentnie się go trzymamy)

double amplituda_szumu;   //ten sposob jest czesciej spotykany w C

struct ObrazPgm obraz_wejsciowy;

double amplitudaSzumu;

Jednoliterowe nazwy zmiennych stosujemy w typowych przypadkach lub dla stałych fizycznych:

int i, j;  //zmienne do iterowania w petlach

char c; //zmienna do wczytywania znaku

double t, f; //czas i czestotliwosc

Nazwy funkcji zaczynamy od czasownika pisanego małą literą na zasadzie zrób_coś_z_czymś.
Kolejne słowa w nazwach możemy pisać wielką literą lub rozdzielać podkreślnikiem.

Nazwa funkcji powinna sugerować, co dana funkcja robi. Jeżeli nie jesteśmy w stanie jej tak sformułować, to może oznaczać, że trzeba ją ponownie przemyśleć.

int losuj_liczbe(void);

int losujLiczbe(void);

Nazwy makr piszemy WIELKIMI_LITERAMI z podkreślnikiem pomiędzy słowami:

#define LICZBA_PROBEK 100

Nazwy struktur zaczynamy od rzeczownika pisanego wielką literą:

struct ObrazPgm {
   ...
}

Stosujemy jednolity język (polski lub angielski), nie mieszamy (zarówno jeżeli chodzi o program jako całość, jak i pojedyncze nazwy zmiennych i funkcji).

W jednej linii piszemy maksymalnie 100 znaków (oprócz czytelności na ekranie ma to znaczenie jeżeli chcemy później wydrukować listing programu). Innym często spotykanym limitem jest 80 znaków.

Wskaźniki piszemy przy nazwie typu, a nie nazwie zmiennej (spację stawiamy po znaczku "*"):

int* utworz_tablice(int rozmiar_tablicy);

Na początku nauki programowania, w pętlach for stosujemy zawsze nawiasy klamrowe, nawet jeśli zapętlona jest jedna instrukcja:

for (i = 0; i < LICZBA_PROBEK; ++i) {
   printf("%f\n", sygnal[i]);
}

Wybieramy jeden z poniższych sposobów stawiania nawiasów klamrowych i konsekwentnie się go trzymamy:

while (a < 10) {
   (..)
}

while (a < 10)
{
   (..)
}

Po otwierającym i przed zamykającym nawiasem klamrowym stawiamy enter.

Poprawnie:

for (i = 0; i < 10; ++i) {
   tab[i] = i;
}

Niepoprawnie:

for (i=0; i<10; ++i)
{tab[i] = i;}

Stosujemy dodatkowe spacje oddzielające znaki + - = < > , % itd. czyli:

int tablica[] = {8, 2, 9, 3, 1, 30, 34};
scanf("%d,%d,%d", &a, &b, &c);
a = c + d++;

zamiast:

int tablica[]={8,2,9,3,1,30,34};
scanf("%d,%d,%d",&a,&b,&c);
a=c+d++;