W architekturze sterowanej zdarzeniami każdy komponent działa tylko wtedy, gdy zdarzenie jest wysyłane przez system.
Wyobraź sobie hipotetyczny samochód z pedałem hamulca i światłem hamowania.
- Światło stop włącza się, gdy odbiera zdarzenie brak hamulca , i gaśnie, gdy odbiera zdarzenie brak hamulca .
- Pedał hamulca wysyła zdarzenie brake_on , gdy jest wciśnięty, i zdarzenie hamulca_off , gdy jest zwolnione.
Wszystko dobrze i dobrze, dopóki nie pojawi się sytuacja, w której samochód zostanie włączony, gdy pedał hamulca jest już wciśnięty . Ponieważ światło hamowania nigdy nie otrzymało zdarzenia brak_wyłączenia , pozostanie wyłączone - wyraźnie niepożądana sytuacja. Domyślne włączenie światła stopu tylko odwraca sytuację.
Co można zrobić, aby rozwiązać ten „problem stanu początkowego”?
EDYCJA: Dziękuję za wszystkie odpowiedzi. Moje pytanie nie dotyczyło prawdziwego samochodu. W samochodach rozwiązali ten problem poprzez ciągłe wysyłanie stanu - dlatego nie ma problemu z uruchomieniem w tej domenie. W mojej domenie oprogramowania to rozwiązanie wymagałoby wielu niepotrzebnych cykli procesora.
EDYCJA 2: Oprócz odpowiedzi @ gbjbaanb idę na system, w którym:
- hipotetyczny pedał hamulca, po inicjalizacji, wysyła zdarzenie ze swoim stanem, oraz
- hipotetyczne światło stopu po inicjalizacji wysyła zdarzenie żądające zdarzenia stanu z pedału hamulca.
Dzięki temu rozwiązaniu nie ma zależności między komponentami, żadnych warunków wyścigu, żadnych kolejek komunikatów, które mogłyby się zestarzeć, ani żadnych komponentów „wzorcowych”.
źródło
initialize
), które zawiera potrzebne dane z czujnika.Odpowiedzi:
Można to zrobić na wiele sposobów, ale wolę, aby system oparty na wiadomościach był możliwie jak najbardziej rozdzielony. Oznacza to, że cały system nie może odczytać stanu żadnego składnika, ani żaden składnik nie odczytał stanu żadnego innego (ponieważ w ten sposób spoczywają zależności zależności).
Tak więc, podczas gdy działający system będzie dbał o siebie, potrzebujemy sposobu, aby każdy komponent sam się uruchomił, a my już mamy coś takiego w rejestracji komponentu, tj. Przy uruchomieniu system podstawowy musi informować każdy komponent, że jest teraz zarejestrowany (lub poprosi każdy komponent o zwrócenie jego danych, aby można go było zarejestrować). Jest to etap, na którym komponent może wykonywać swoje zadania uruchamiania i może wysyłać wiadomości tak, jak w normalnej pracy.
Tak więc pedał hamulca, gdy zapłon zostanie uruchomiony, otrzyma komunikat rejestracyjny / kontrolny od kierownictwa samochodu i zwróci nie tylko komunikat „Jestem tutaj i pracuję”, ale następnie sprawdzi swój stan i wyśle komunikaty dla tego stanu (np. komunikat z wciśniętym pedałem).
Problem staje się wtedy zależny od uruchomienia, tak jakby światło stopu nie zostało jeszcze zarejestrowane, wówczas nie otrzyma komunikatu, ale można to łatwo rozwiązać, ustawiając w kolejce wszystkie te komunikaty, aż system podstawowy zakończy procedurę uruchamiania, rejestracji i sprawdzania .
Największą zaletą jest to, że nie jest wymagany specjalny kod do obsługi inicjalizacji, z wyjątkiem tego, że musisz już napisać (ok, jeśli wysyłanie wiadomości o zdarzeniach dotyczących pedału hamulca odbywa się w module obsługi pedału hamulca, będziesz musiał to również wywołać podczas inicjalizacji , ale zwykle nie stanowi to problemu, chyba że napisałeś ten kod silnie powiązany z logiką modułu obsługi) i nie ma interakcji między komponentami oprócz tych, które już do siebie wysyłają w normalny sposób. Z tego powodu architektury przekazywania wiadomości są bardzo dobre!
źródło
Możesz mieć zdarzenie inicjalizacji, które odpowiednio ustawia stany podczas ładowania / uruchamiania. Może to być pożądane w przypadku prostych systemów lub programów niezawierających wielu elementów sprzętowych, jednak w przypadku bardziej skomplikowanych systemów z wieloma fizycznymi komponentami, ponieważ narażasz się na takie samo ryzyko, jak w ogóle nie inicjowanie - jeśli zdarzenie „hamowania” zostanie pominięte lub utracone podczas komunikacji system (na przykład system oparty na CAN), możesz przypadkowo ustawić swój system do tyłu, tak jakbyś uruchomił go z wciśniętym hamulcem. Im więcej kontrolerów możesz mieć, na przykład z samochodem, tym większe prawdopodobieństwo, że coś zostanie pominięte.
Aby to uwzględnić, logika „hamulec włączony” może wielokrotnie wysyłać zdarzenia „hamulec włączony”. Może co 1/100 sekundy lub coś takiego. Twój kod zawierający mózg może nasłuchiwać i wywoływać „hamowanie” podczas ich odbierania. Po 1/10 sek. Nieotrzymania sygnałów „hamowanie włączone” wyzwala wewnętrzne zdarzenie „brak hamulca”.
Różne zdarzenia będą miały znacznie różne wymagania dotyczące czasu. W samochodzie twoje światło stopu musi być znacznie szybsze niż powiedz, że paliwo kontrolne paliwo (gdzie opóźnienie wielosekundowe jest prawdopodobnie dopuszczalne) lub inne mniej ważne układy.
Złożoność twojego systemu fizycznego decyduje o tym, które z tych podejść jest bardziej odpowiednie. Biorąc pod uwagę, że twoim przykładem jest pojazd, prawdopodobnie chciałbyś czegoś podobnego do tego drugiego.
Tak czy inaczej, w systemie fizycznym NIE chcesz polegać na tym, że pojedyncze zdarzenie zostanie poprawnie odebrane / przetworzone. Z tego powodu połączone mikrokontrolery w systemie sieciowym często mają limit czasu „Jestem żywy”.
źródło
W tym przypadku nie modelowałbym hamulca jako prostego włączania / wyłączania. Zamiast tego wysyłałbym zdarzenia „ciśnienia hamowania”. Na przykład ciśnienie 0 oznaczałoby wyłączenie, a ciśnienie 100 byłoby całkowicie obniżone. System (węzeł) stale wysyła zdarzenia ciśnienia przerwania (w określonych odstępach czasu) do kontrolera (-ów) w razie potrzeby.
Gdy system został uruchomiony, zaczął odbierać zdarzenia ciśnienia, dopóki nie został wyłączony.
źródło
Jeśli jedynym sposobem przekazywania informacji o stanie są zdarzenia, masz kłopoty. Zamiast tego musisz być w stanie:
Światło stop może być postrzegane jako obserwator pedału hamulca. Innymi słowy, pedał hamulca nie wie nic o świetle hamowania i może działać bez niego. (Oznacza to, że każde pojęcie pedału hamulca proaktywnie wysyłającego zdarzenie „stanu początkowego” do światła hamowania jest niewłaściwe).
Po uruchomieniu systemu światło hamowania rejestruje się w pedale hamulca, aby otrzymywać powiadomienia o hamowaniu, a także odczytuje aktualny stan pedału hamulca i włącza się lub wyłącza.
Następnie powiadomienia o hamowaniu można wdrożyć na jeden z trzech sposobów:
Wolę pierwsze podejście, co oznacza, że po otrzymaniu powiadomienia światło stopu po prostu zrobi to, co już wie, jak to zrobić: odczytuje aktualny stan pedału hamulca i sam się włącza lub wyłącza.
źródło
W systemie opartym na zdarzeniach (z którego obecnie korzystam i które uwielbiam) uważam, że ważne jest, aby zachować możliwie jak największą niezależność. Mając to na uwadze, zajrzyjmy od razu.
Ważne jest, aby mieć stan domyślny. Światło hamowania przyjmie domyślny stan „wyłączony”, a pedał hamulca przyjmie domyślny stan „w górę”. Wszelkie późniejsze zmiany byłyby wydarzeniem.
Teraz, aby odpowiedzieć na twoje pytanie. Wyobraź sobie, że twój pedał hamulca został zainicjowany i wciśnięty, zdarzenie zapala się, ale jeszcze nie ma świateł stop, które je przyjmą. Odkryłem, że najłatwiej jest oddzielić tworzenie obiektów (w których inicjowane byłyby detektory zdarzeń) jako osobny krok przed zainicjowaniem jakiejkolwiek logiki. Zapobiegnie to warunkom wyścigowym, które opisałeś.
Uważam również, że niewygodne jest używanie dwóch różnych zdarzeń w celu uzyskania tego samego efektu .
brake_off
ibrake_on
można je uprościće_brake
za pomocą parametrubool on
. W ten sposób możesz uprościć swoje wydarzenia, dodając dane pomocnicze.źródło
To, czego potrzebujesz, to zdarzenie transmisji i skrzynki odbiorcze wiadomości. Transmisja to komunikat publikowany nieokreślonej liczbie słuchaczy. Składnik może zasubskrybować wydarzenia rozgłoszeniowe, aby odbierać tylko zdarzenia, które są nim zainteresowane. Zapewnia to oddzielenie, ponieważ nadawca nie musi wiedzieć, kim są odbiorcy. Tabela subskrypcji musi zostać skonfigurowana statycznie podczas instalacji komponentu (zamiast podczas inicjowania). Skrzynka odbiorcza jest częścią routera wiadomości, który działa jako bufor do przechowywania wiadomości, gdy docelowy komponent jest w trybie offline.
Korzystanie z faktur wiąże się z jednym problemem, jakim jest rozmiar skrzynki odbiorczej. Nie chcesz, aby system przechowywał rosnącą liczbę komunikatów dla komponentów, które nigdy nie będą już w trybie online. Jest to ważne zwłaszcza w przypadku systemu osadzonego o ścisłych ograniczeniach pamięci. Aby pokonać limit wielkości skrzynki odbiorczej, wszystkie rozgłaszane wiadomości muszą przestrzegać kilku zasad. Reguły są następujące:
Nazwa emisji musi zostać zadeklarowana podczas instalacji komponentu. Jeśli komponent wysyła drugą transmisję o tej samej nazwie, zanim odbiornik przetworzy poprzednią, nowa transmisja zastępuje poprzednią. Teraz możesz mieć limit wielkości statycznej skrzynki odbiorczej, który nigdy nie przekroczy określonego rozmiaru i może być wstępnie obliczony na podstawie tabel subskrypcji.
Wreszcie potrzebujesz również archiwum emisji. Archiwum transmisji to tabela zawierająca ostatnie zdarzenie z każdej nazwy emisji. Nowo zainstalowane komponenty będą miały wstępnie wypełnioną skrzynkę odbiorczą z wiadomościami z archiwum emisji. Podobnie jak skrzynka odbiorcza wiadomości, archiwum emisji może mieć również rozmiar statyczny.
Dodatkowo, aby poradzić sobie z sytuacją, w której sam router wiadomości jest w trybie offline, potrzebne są również skrzynki nadawcze wiadomości. Skrzynka nadawcza wiadomości jest częścią komponentu, który tymczasowo przechowuje wiadomość wychodzącą.
źródło