Komunikacja MQTT w panelach Astraada HMI

Z tego artykułu dowiesz się:

  • czym jest MQTT i jakie ma zastosowanie,
  • jak przygotować brokera do konfiguracji komunikacji w protokole MQTT,
  • jak monitorować informacje pojawiające się na serwerze,

MQTT – Czym jest? Do czego służy?

Message Queue Telemetry Transport, w skrócie MQTT, to protokół komunikacyjny stworzony w oparciu o wzorzec publikacja/subskrypcja. Charakteryzuje się on dużą prostotą, niewielkim obciążeniem urządzeń uczestniczących w komunikacji, a także wysoką niezawodnością. Dodatkowo protokół ten świetnie wpisuje się w nowoczesną technologię IoT (Internet of Things).

Aby skorzystać z komunikacji po MQTT w panelach Astraada HMI należy skorzystać z najnowszej wersji oprogramowania Astraada HMI CFG 4.0, który jest do pobrania pod tym linkiem.

Komunikacja w protokole MQTT oparta jest o broker (serwer), a także podłączonych do niego klientów. Klienci mogą realizować w obrębie serwera dwie opcje: publikować wiadomość na dany temat (topic) lub odbierać wiadomość z subskrybowanych tematów. Jeden klient może jednocześnie realizować obie te opcje.

Całość komunikacji przebiega w następujących krokach:

1. Klienci wysyłają na serwer listę subskrybowanych tematów komunikacji.
2. Jeden z klientów publikuje nowe dane w konkretnym temacie.
3. Broker MQTT rozsyła informację o temacie, w którym pojawiły się nowe dane, do klientów subskrybujących dany temat
4. Klient subskrybujący temat pobiera nową informację.

Z powyższego schematu łatwo wywnioskować, że większość działań wykonywana jest przez broker MQTT. Nie ma potrzeby stałego odpytywania poszczególnych urządzeń o konkretne wartości, przez co protokół MQTT nie obciąża znacząco mocy obliczeniowej urządzenia. Rola klienta/subskrybenta jest tutaj znacząco bierna, serwer sam wyśle do niego informacje.

Tematy publikacji można bardzo intuicyjne grupować w kategorie i podkategorie. Odbywa się to na wzór schematu drzewka folderów w Microsoft Windows. Dla przykładu:

Maszyna1/ParametryPodstawowe/CzasPracy

W przypadku powyższej ścieżki sterownik będzie otrzymywał informację na temat zmian wartości czasu pracy w pierwszej maszynie.

Przy definiowaniu ścieżki do tematu, który ma być nasłuchiwany można skorzystać z dwóch operatorów: „+” i „#”.

  • + – dopasowuje się do dowolnej nazwy na danym poziomie. Przykładowo, w momencie użycia ścieżki: Urządzenie/ID1/StanAkumulatora, klient/subskrybent będzie otrzymywał informację o aktualnym stan naładowania akumulatora tylko w pierwszym urządzeniu, gdy natomiast skorzystamy z operatora „+” w następujący sposób: Urządzenie/+/StanAkumulatora, klient/subskrybent będzie otrzymywał informację o aktualnym stanie naładowania akumulatora na wszystkich urządzeniach podłączonych do wątku.
  • # – dopasowuje się do wszystkich zagłębień danego poziomu. Wykorzystując przykład powyższy, używając operatora „#” w następujący sposób: Urządzenie/ID1/# umożliwi klientowi/subskrybentowi otrzymywanie informacji na temat wszystkich dostępnych parametrów urządzenia o ID1.

Komunikacja w protokole MQTT cechuje się wysokim bezpieczeństwem. Informacje przesyłane do serwera kodowane są w oparciu o protokół SSL. Dodatkowo broker może zostać skonfigurowany w taki sposób, aby weryfikował każdego klienta przed podłączeniem do serwera, poprzez podanie loginu i hasła.

Przygotowanie brokera

Konfigurowanie komunikacji w protokole MQTT należy rozpocząć od przygotowania brokera.

Broker może być:

  • Wewnętrzny – niektóre urządzenia posiadają wbudowany serwer, który może pełnić taką funkcjonalność.
  • Zewnętrzny – specjalne moduły IoT podłączane do urządzenia.
  • Zdalny – usługodawcy, którzy za pośrednictwem stron internetowych udostępniają możliwość stworzenia własnego brokera.

Niezależnie jaki rodzaj brokera posiadamy, koniecznie należy znać jego adres IP (lub html jeżeli jest taka możliwość), a także port, na którym serwer nasłuchuje informacji. Opcjonalnie: login i hasło dostępu.

Na potrzeby niniejszej instrukcji wykorzystany zostanie broker firmy ASTOR o następujących parametrach:

  • Adres: mqtt.astor.com.pl
  • Port: 1883

Po aktywowaniu brokera i odczytaniu niezbędnych parametrów można przystąpić do konfiguracji panelu Astraada HMI.

Konfigurowanie połączenia z brokerem

Aby skonfigurować połączenie pomiędzy brokerem, a panelem Astraada HMI, należy rozwinąć opcję Links z głównego drzewka projektowego i wybrać opcję MQTT Client.

Po wybraniu powyższej opcji, uruchomi się okno konfiguracji komunikacji MQTT, w której zaznaczamy opcję Enable i uzupełniamy informacje dotyczące brokera.

W przypadku, gdy posiadamy jedynie adres IP brokera, a nie HTML, należy zaznaczyć okienko przy pozycji IP Address. Konfigurator umożliwi nam wtedy wprowadzenie adresu IP.

Jeżeli broker jest zabezpieczony loginem i hasłem, należy tutaj wpisać te dane odpowiednio: Login w pole User Name, a hasło w pole Password. W pole Client ID opcjonalnie, możemy wpisać dowolny identyfikator aktualnie programowanego urządzenia.

Konfigurowanie trybu publikacji panelu Astraada HMI w protokole MQTT

Konfigurowanie trybu należy rozpocząć od przygotowania tematu publikacji, a także jej treści. W przypadku panelu Astraada HMI i oprogramowania Astraada HMI CFG, dokonuje się tego w pozycji Text Format Table znajdującej się w głównym drzewku projektowym.

Po wybraniu powyższej opcji pojawi się tabela z trzema kolumnami: ID, Name oraz Language 1. Jeżeli ta ostatnia kolumna nie jest widoczna, należy kliknąć w dowolnym miejscu w obszarze tabeli, prawym przyciskiem myszy i zaznaczyć opcję Language 1.

Kolumna ID informuje o unikalnym identyfikatorze do przygotowanego tekstu – informacje z tej kolumny wykorzystywane są przy tworzeniu funkcji w makrach. Kolumna Name będzie zawierać dowolną, zdefiniowaną przez nas nazwę, dzięki której będziemy mogli wyszukać odpowiedni tekst w momencie potrzeby jego wyboru. Właściwy tekst, którym będzie operował program Astraada HMI CFG, jak i sam panel, znajduje się w kolumnie Language 1.

Zacznijmy zatem od przygotowaniu tekstu pod temat – klikamy ikonkę NEW, a w tabeli powinien pokazać się pusty wiersz o ID 00001. W kolumnie Name wpisujemy dowolną nazwę tekstu, najlepiej w taki sposób, aby łatwo można było go odróżnić od innych, np.: Temat1. W polu Language 1 wpisujemy właściwą formę tematu, w którym chcemy publikować. W naszym przykładzie, będziemy publikować pod tematem „Publish”, więc właśnie to słowo zostaje tutaj wpisane.

Poprawnie uzupełniona tabela powinna wyglądać w następujący sposób:

Przygotowany tekst w chwili obecnej nie posiada żadnych cech. Środowisko Astraada HMI CFG wymaga, aby powyższym tekstom nadać w pierwszej kolejności odpowiednie cechy, aby można było ich używać do odpowiednich celów.

W celu nadania cech danemu tekstowi, zaznaczamy żądany tekst i klikamy przycisk Property ( piąty od prawej w panelu Text Format Table).

Po kliknięciu powinno pojawić się powyższe okno. Możliwe cechy tekstu zawarte są w Specific Purposes. Tekst może pełnić rolę: tematu publikacji (Composing MQTT Publish Topic), tematu subskrypcji (Composing Subscription Message), treści wiadomości publikacji (Composing MQTT Publish Message), a także wysyłać i odczytywać zmienne (Sending/Reciving Data Values). W aktualnym przypadku interesuje nas opcja pierwsza, dlatego zaznaczamy ją i potwierdzamy przyciskiem OK.

Następnie przechodzimy do przygotowania tekstu dla wiadomości, która będzie publikowana. Ponownie wciskamy przycisk NEW. W nowopowstałym wierszu, w kolumnie Name wpisujemy dowolną nazwę np.: Wiadomosc1. W kolumnie Language 1 wpisujemy treść wiadomości jaką chcemy przesłać. Treść może być dowolna. Protokół MQTT nie narzuca żadnej formy pisania wiadomości, programista może zatem w bardzo czytelny sposób publikować informacje na brokerze.

Na potrzeby niniejszego przykładu, prześlemy informacje o jednej zmiennej w postaci: „Aktualna wartość zmiennej:”. W związku z tym wpisujemy podany tekst w kolumnę Language 1. Tabela powinna aktualnie posiadać następującą formę:

Następnie, tak jak w przypadku tematu publikacji, należy nadać nowemu tekstowi odpowiednie cechy. Dodatkowo, z racji, że tekst będzie przesyłał zmienną, powinna zostać ona odpowiednio zdefiniowana. Dlatego też zaznaczamy nowy tekst i klikamy przycisk Property.

W pierwszej kolejności przystępujemy do definicji zmiennej. Klikamy przycisk NEW znajdujący się w lewym górnym rogu. W tabeli Embedded Variables powinna pojawić się nowa pozycja: %1. Po utworzeniu nowej zmiennej, należy określić jej parametry. Najważniejsze z nich to:

  • Data Type – typ zmiennej, w naszym przykładzie będzie to 16-Bit Unsigned Integer
  • Display Type – sposób wyświetlania zmiennej, w naszym przykładzie: 16-Bit Unsigned Decimal
  • Address – adres w pamięci panelu, w którym będzie przechowywana wartość zmiennej, w naszym przykładzie: $U100.
  • Total Digits – liczba znaków, które ma przechowywać zmienna – dowolna wartość
  • Fractional Digits – liczba znaków po przecinku, które ma przechowywać zmienna – dowolna wartość

Gdy cechy zmiennej zostały zdefiniowane, należy przydzielić odpowiednie cechy dla całego tekstu. Nasz tekst ma być wiadomością publikacji, a także przekazywać informacje o zmiennej, dlatego też należy zaznaczyć opcje Composing MQTT Publish Message, a także Sending/Receiving Data Values. Odpowiednie wypełnione okno Property powinno mieć następującą postać:

Zatwierdzamy przyciskiem OK. Ostatnim krokiem w definiowaniu tekstu wiadomości, jest wpisanie zdefiniowanej zmiennej do kolumny Language 1. Nasza zmienna została oznaczona znakiem %1, dlatego też ten znak należy wpisać w treść wiadomości, która finalnie powinna mieć postać: „Aktualna wartosc zmiennej: %1”.

Powracamy następnie do MQTT Client. W oknie tym, należy przeprowadzić ostatnie elementy konfiguracji. Pierwszym z nich jest nadanie naszemu tematowi odpowiedniego ID. Zaznaczamy w Publish Topics (gdyż przeprowadzamy procedurę konfiguracji publikacji) okienko przy Topic ID 1. Czynność ta powinna odblokować listę wyboru, z której wybieramy nasz zdefiniowany temat – Temat1.

Komunikacja MQTT odbywa się za pośrednictwem specjalnych makr. Dlatego też w lewym dolnym roku zaznaczamy opcję MQTT Message Receiving Macro. Co powinno odblokować nową zakładkę o tej samej nazwie. Poprawnie skonfigurowane okno MQTT Client powinno wyglądać następująco:

Ostatnim krokiem jest napisanie makra, które umożliwi nam publikacje naszej wiadomości. W związku z tym przechodzimy do zakładki MQTT Message Receiving Macro. Klikamy przycisk New… i nadajemy dowolną nazwę, w naszym przykładzie, będzie to: Macro1.

Aby opublikować wiadomość, należy skorzystać z komendy: A = MqttP_IF(B,C,D), gdzie:

  • A – adres, pod którym program będzie zapisywał kod operacji (informował o jej poprawności)
  • B – ID tematu, w którym ma zostać opublikowana wiadomość
  • C – ID tekstu, który ma zostać użyty do publikacji wiadomości
  • D – Poziom zabezpieczeń. Może on przybrać trzy wartości: 0 – wiadomość zostaje wysłana bez żadnych potwierdzeń dostarczenia. 1 – pierwsza wiadomość zostaje wysłana z potrzebą potwierdzenia dostarczenia, jeżeli potwierdzenie zostanie odesłane przez serwer, komunikacja będzie kontynuowana. 2 – wszystkie wiadomości zostają wysłane z potrzebą potwierdzenia dostarczenia.

W związku z powyższym Macro1, w przypadku naszego przykładu, będzie mieć postać:

$U1= MqttP_IF(1, 2, 0)

Ponieważ: nasz temat Publish został zaznaczony przy Topic ID 1, więc posiada ID 1, tekst naszej wiadomości jest zapisany w Text Format Table z ID równym 00002, a także nie potrzebujemy żadnych potwierdzeń dostarczenia.

W ten sposób przygotowana została komunikacja w protokole MQTT. W związku z tym, opublikujemy w najprostszy sposób wiadomość na serwerze. Do tego będziemy potrzebować elementu Numeric Entry, który należy przypisać pod adres naszej zmiennej, a więc $U100, a także przycisku. Bit Button, w którym należy zawrzeć stworzone wcześniej makro.

Ustawienie powyższych elementów:

Tak przygotowany program wgrywamy na panel HMI. Na panelu należy wprowadzić liczbę w elemencie Numeric Entry, a następnie kliknąć przycisk Bit Button. W momencie kliknięcia przycisku wiadomość zostanie opublikowana na serwerze.

UWAGA!

Należy pamiętać, aby urządzenie było podłączone do brokera, jeżeli korzystamy z zewnętrznego, lub do sieci internetowej – jeżeli korzystamy z brokera zdalnego (funkcja DHCP musi być aktywna)!

Konfigurowanie trybu subskrypcji panelu Astraada HMI w protokole MQTT

W związku z tym, że urządzenie może jednocześnie pełnić funkcję publikacji, jak i subskrypcji, wykorzystamy aplikację trybu publikacji i zmodyfikujemy ją w taki sposób, aby panel HMI nasłuchiwał również wybranego tematu. Naszym celem będzie odczytanie z serwera wiadomości opublikowanej pod tematem Publish o treści: „Aktualna wartość zmiennej: %1”

Rozpoczniemy od modyfikacji tekstów w Text Format Table.

Temat1 aktualnie skonfigurowany jest jedynie jako temat publikacji. Należy zmodyfikować go tak, aby pełnił również funkcjonalność tematu subskrypcji, gdyż w tym przykładzie chcemy subskrybować ten sam temat, na który aktualnie publikujemy – nie ma więc potrzeby tworzenia nowego tekstu.

Wybieramy Temat1 i klikamy opcję Property

Zaznaczamy opcję Composing MQTT Subscription Topic (tak jak na powyższym rysunku), gdyż chcemy, aby ten sam tekst mógł tworzyć równocześnie temat subskrypcji. Zatwierdzamy przyciskiem OK.

W trybie subskrypcji nie udostępniamy żadnych wiadomości, mimo to tekst widomości jest niezbędny, gdyż używany jest on do innych celów. Stanowi swego rodzaju klucz, którym panel będzie się posługiwał, aby wyodrębnić odpowiednie zmienne z przesyłanej na serwer informacji. Tak jak wspomniane było w przypadku konfiguracji trybu publikacji – MQTT nie narzuca żadnej formy wysyłania informacji na serwer, jednak należy pamiętać, aby urządzenie, które ma odczytać daną wiadomość posiadała dokładną informację jaką formę będzie mieć przesyłana na serwer wiadomość. W związku z tym klucz musi być idealnie taki sam jak treść publikacji. W szczególności, jeżeli chodzi o odstępy i duże litery.

W niniejszym przykładzie będziemy odczytywać dokładnie taką samą wiadomość, jaką wysyłamy na serwer, dlatego też nie trzeba definiować nowego tekstu, dodatkowo konfiguracja tekstu publikowanego, jak i odczytywanego jest dokładnie taka sama, więc nie ma potrzeby ingerencji w panel Property.

Po zmodyfikowaniu tekstów, przechodzimy do MQTT Client, aby nanieść tam odpowiednie modyfikacje. Przede wszystkim należy nadać odpowiednie ID naszemu tematowi subskrypcji. Zaznaczamy w Subsctiption Topics (gdyż przeprowadzamy procedurę konfiguracji subskrypcji) okienko przy Topic ID 1. Czynność ta powinna odblokować listę wyboru, z której wybieramy nasz zdefiniowany temat – Temat1.

Okno konfiguracji powinno wyglądać tak jak na zdjęciu powyżej.

Ostatnim elementem konfiguracji trybu subskrypcji jest napisanie odpowiedniego makro. Które będzie wyczekiwać na informacje o nowej informacji na serwerze i weryfikować czy informacja ta jest dla nas istotna, czy też nie.

W tym celu w MQTT Client przechodzimy do zakładki MQTT Message Receiving Macro i klikamy New… Nadajemy dowolną nazwę, w naszym przypadku będzie to Macro2 i przystępujemy do edycji.

Rozpoczynamy od funkcji, która będzie odbierać informację od serwera o najnowszej wiadomości. Funkcja ta ma następującą postać:

A = MqttGet(B, C, D)

Gdzie:

  • A – Adres, pod którym będzie przechowywana długość najnowszej na serwerze wiadomości
  • B – Adres, pod którym będzie przechowywany temat najnowszej na serwerze wiadomości
  • C – Adres, pod którym będzie przechowywana treść wiadomości.
  • D –  Adres, pod którym będzie przechowywana długość tematu

Przy pomocy tej funkcji odczytujemy wszelkie informacje o najnowszej wiadomości, które będą nam potrzebne w dalszej procedurze odczytu. Przede wszystkim, trzeba zweryfikować, czy temat wiadomości pokrywa się z tematem, który aktualnie subskrybujemy. Do tego celu, należy wyszukać czy temat znajduje się w bazie danych panelu Astraada HMI, czyli czy ma ID i jeżeli tak, to jakie? A także, przy pomocy pętli if napisać odpowiednią procedurę.

W celu weryfikacji tematu publikacji należy użyć następującej funkcji:

A = MqttChkTopic(B, C, D)

Gdzie:

  • A – Adres, pod którym będzie przechowywany adres ID weryfikowanego tematu ( jeżeli temat nie jest zdefiniowany w bazie danych urządzenia wartość ta będzie równa 0).
  • B – Adres, w którym aktualnie przechowywana jest informacja o temacie, który należy zweryfikować ( w naszym przypadku będzie to parametr B z funkcji MqttGet).
  • C – Adres, w którym aktualnie przechowywana jest informacja o długości tematu, który należy zweryfikować ( w naszym przypadku będzie to parametr D z funkcji MqttGet)
  • D – Wpisujemy 0 (parametr chwilowo nieobsługiwany przez oprogramowanie).

Po użyciu powyższych funkcji, należy zapisać procedurę przy pomocy funkcji if, którą panel HMI ma przeprowadzić z informacją o temacie publikacji. Przede wszystkim należy zweryfikować, czy ID tematu jest niezerowe i czy jest równe 1 (gdyż temat o ID 1 jest tematem, który chcemy subskrybować). Jeżeli ID odczytanego tematu pokrywa się z ID subskrybowanego tematu, to należy skorzystać z funkcji, która rozszyfruje wiadomość i wstawi odczytane parametry pod odpowiednie zmienne:

A = MqttProcMsg(B, C, D)

Gdzie:

  • A – Adres, pod którym będzie przechowywana informacja o poprawności przeprowadzenia odczytania wiadomości. Jeżeli odczytanie przebiegło poprawnie wartość tego adresu powinna być równa 0, jeżeli nie – równa 1.
  • B – W tym miejscu należy wprowadzić ID tekstu z Text Format Table, który ma zostać użyty jako klucz do odczytania wiadomości.
  • C – Adres, pod którym przechowywana wiadomość, którą chcemy odczytać ( W naszym przypadku będzie to parametr C z funkcji MqttGet).
  • D – Adres, pod którym przechowywana jest informacja o długości wiadomości, którą chcemy odczytać ( w naszym przypadku będzie to parametr A z funkcji MqttGet).

Biorąc pod uwagę wszystkie powyższe wiadomości nasze makro powinno mieć następującą postać:

Warto zwrócić uwagę, że adres pod którym przechowywana jest treść wiadomości jest znacząco odsunięty od pozostałych ($U200). Dzieje się to dlatego, iż w treści wiadomości przesyłany jest pełny kod, który może zająć dużo miejsca w pamięci. Wszystko zależy od długości odebranej wiadomości. Dlatego też ważne jest, aby mieć pewność, że rejestry nie będą nachodzić na siebie i nie będą powodować zakłóceń. Jeżeli podczas komunikacji urządzenie odczytuje wiadomość, jednak nie jest w stanie jej rozszyfrować, mimo iż klucz jak i treść jest identyczna – warto przesunąć rejestr o kilka pozycji dalej, gdyż być może występuje konflikt adresu.

Powyższego makra nie trzeba wywoływać. Funkcja zostanie automatycznie wywołana w momencie przysłania przez serwer informacji o nowej zmiennej, dlatego też nie trzeba przypisywać żadnego przycisku pod makro.

Aby zwizualizować przebieg komunikacji w najprostszy sposób należy wykorzystać trzy okienka: dwa Character Display i jedno Numeric Display. Okienka Character Display będą wyświetlać (jeżeli temat wiadomości zgadza się z tematem subskrypcji): temat, na który została wiadomość nadana i treść wiadomości. Numeric Display wyświetli natomiast aktualną wartość przypisanej zmiennej.

Poprawna konfiguracja powyższych elementów, w przypadku niniejszego przykładu powinna wyglądać następująco:

W przypadku wyświetlenia treści wiadomości:

W przypadku wyświetlenia tematu:

W przypadku wyświetlenia wartości nadpisanej zmiennej:

Jak monitorować informacje pojawiające się na serwerze?

W najprostszy sposób można to zrobić przy pomocy przeglądarki Google Chrome i aplikacji MQTTLens, która pełni rolę publishera/subskrybenta. Otwieramy przeglądarkę i w wyszukiwanie Google wpisujemy hasło: MQTTLens.

Klikamy w powyższy wynik, a następnie wybieramy opcje „zainstaluj aplikację” – jeżeli jeszcze jej nie posiadamy, lub „uruchom aplikację” – jeżeli znajduje się ona już na naszym komputerze.

Po uruchomieniu aplikacji, w lewej części okna można dodać broker, do którego będzie się łączyć MQTTLens. W obszarze CONNECTIONS klikamy znak „+”, otwierając okno Add a new Connection. Dane brokera, w przypadku naszego przykładu będzie to:

  • Hostname: mqtt.astor.com.pl
  • Port: 1883
  • Connection name: można wpisać dowolną nazwę
  • Username oraz Password: pozostawiamy puste

Po uzupełnieniu tych informacji klikamy w dolnej części okna CREATE CONNECTION.

Po uruchomieniu brokera, należy zasubskrybować temat, który został wcześniej skonfigurowany (w przypadku naszego przykładu temat nosił nazwę: „Publish”). Aby to zrobić, należy w polu Subscribe wpisać nazwę tematu i należy kliknąć przycisk SUBSCRIBE. Następnie na panelu należy upublicznić żądaną wiadomość klikając przycisk Bit Button. Po tej operacji, informacja zostanie przesłana do serwera, co będzie widoczne w MQTTLens w następujący sposób:

W przypadku, gdy chcemy wysłać wiadomość na serwer, która ma zostać odczytana przez panel korzystamy z segmentu Publish. W polu topic wpisujemy temat, który jest subskrybowany przez panel HMI. W przypadku naszego przykładu temat ten to ”Publish”. Następnie w polu Message wpisujemy treść wiadomości. Aby panel poprawnie odczytał naszą informację, należy wpisać w tym miejscu dokładną treść klucza użytego podczas konfiguracji. Najlepiej przekopiować go z Text Format Table i zamiast wartości %1 wpisać żądaną wartości. W naszym przypadku będzie to: „Aktualna wartosc zmiennej: 70”. Po wypełnieniu wspomnianych pól, klikamy przycisk PUBLISH. Okno powinno wyglądać tak, jak na poniższym zdjęciu.

Czy ten artykuł był dla Ciebie przydatny?

Średnia ocena artykułu: 4.3 / 5. Ilość ocen: 3

Ten artykuł nie był jeszcze oceniony.

Opublikuj

Twój adres email nie zostanie opublikowany.

Czytaj więcej