Як обробити початковий стан в керованій подіями архітектурі?


33

У архітектурі, керованій подією, кожен компонент діє лише тоді, коли подія надсилається через систему.

Уявіть собі гіпотетичний автомобіль з педаллю гальма та гальмівним світлом.

  • Гальмівні легкі повороти на , коли він отримує brake_on подія, і геть , коли він отримує brake_off подія.
  • Педаль гальма надсилає подія гальма при натисканні на неї, а подія гальма - при відпуску .

Це все добре і добре, поки у вас не складеться ситуація, коли автомобіль увімкнено, коли вже натиснута педаль гальма . Оскільки гальмівний світло ніколи не отримував події гальма , він залишатиметься вимкненим - явно небажана ситуація. Увімкнення індикатора гальма за замовчуванням лише обертає ситуацію.

Що можна зробити, щоб вирішити цю "початкову проблему стану"?

EDIT: Дякую за всі відповіді. Моє запитання не стосувалося фактичного автомобіля. У автомобілях вони вирішували цю проблему, безперервно надсилаючи державу - тому в цій галузі не виникає проблем із запуском. У моєму програмному домені це рішення використовувало б багато зайвих циклів процесора.

EDIT 2: Окрім відповіді @ gbjbaanb , я збираюся створити систему, в якій:

  • після ініціалізації гіпотетична педаль гальма надсилає подію зі своїм станом та
  • після ініціалізації гіпотетичний гальмівний індикатор посилає подію, що вимагає події стану, від педалі гальма.

У цьому рішенні немає ніяких залежностей між компонентами, немає перегонових умов, немає черг на повідомлення, які стають несвіжими, і немає "головних" компонентів.


2
Перше, що спадає на думку, - це генерувати "синтетичну" подію (називати її initialize), яка містить необхідні дані датчика.
msw

Чи не повинна педаль надсилати подію brake_pedal_on, а власне гальмо посилати подію brake_on? Я б не хотів, щоб моє гальмівне світло загорілося, якщо гальмо не працювало.
bdsl

3
Я вже згадував, що це був гіпотетичний приклад? :-) Це дуже спрощено, щоб питання було коротким і чітким.
Френк Кустерс

Відповіді:


32

Існує багато способів зробити це, але я вважаю за краще підтримувати систему на основі повідомлень якомога більше роз'єднаною. Це означає, що загальна система не може прочитати стан будь-якого компонента, ані будь-який інший компонент не зможе прочитати стан будь-якого іншого (оскільки таким чином лежать спагетті залежностей).

Отже, поки запущена система буде доглядати за собою, нам потрібен спосіб сказати кожному компоненту, щоб він запустився, і ми вже маємо таке при реєстрації компонента, тобто при запуску основна система повинна повідомити кожен компонент, що це тепер зареєстрований (або попросить кожен компонент повернути свої дані, щоб він міг бути зареєстрований). Це етап, на якому компонент може виконувати свої завдання із запуску і може надсилати повідомлення так, як це робиться у звичайній роботі.

Тож педаль гальма при запуску запалювання отримає повідомлення про реєстрацію / перевірку від керівництва автомобіля, і воно поверне не лише повідомлення "Я тут і працюю", але потім перевірить власний стан і надішле повідомлення для цього стану (наприклад, натиснуте на педаль повідомлення).

Потім ця проблема стає однією із залежностей при запуску, так як якщо гальмівний індикатор ще не зареєстрований, він не отримає повідомлення, але це легко вирішується чергою всіх цих повідомлень, поки основна система не завершить процедуру запуску, реєстрації та перевірки. .

Найбільша перевага полягає в тому, що не існує спеціального коду, необхідного для обробки ініціалізації, за винятком того, що ви вже повинні писати (гаразд, якщо ваше повідомлення для подій педалі гальма знаходиться в обробці педалі гальма, вам доведеться зателефонувати і цьому під час ініціалізації. , але це зазвичай не є проблемою, якщо ви не написали, що цей код сильно прив'язаний до логіки обробника) і немає взаємодії між компонентами, крім тих, які вони вже надсилають один одному як звичайні. Через це архітектури передачі повідомлень дуже хороші!


1
Мені подобається ваша відповідь, оскільки вона тримає всі компоненти нерозділеними - це було найважливішою причиною вибору цієї архітектури. Однак на даний момент не існує справжнього «головного» компонента, який би вирішив, що система перебуває у «ініціалізованому» стані - все просто починає працювати. З проблемою в моєму питанні як результат. Після того як ведучий вирішить, що система працює, він може надіслати подію "ініціалізована система" всім компонентам, після чого кожен компонент починає трансляцію свого стану. Проблема вирішена. Дякую! (Тепер я просто залишився з проблемою, як вирішити, чи буде ініціалізована система ...)
Френк Кустерс

Як щодо того, щоб диспетчер оновлень статусу відслідковував останнє оновлення, отримане від кожного об'єкта, і щоразу, коли надходить новий запит на підписку, чи надсилає він новому абоненту останні оновлення, отримані з зареєстрованих джерел подій?
supercat

У такому випадку ви також повинні стежити, коли закінчуються події. Не всі події підлягають назавжди за будь-якими новими компонентами, які можуть реєструватися.
Френк Кустерс

@spaceknarf добре, у випадку, коли "все тільки починає працювати", ви не зможете побудувати залежність в компонентах, тому педаль запускається після світла, вам доведеться просто запустити їх у такому порядку, хоча я уявляю, що щось починає їх працювати, тому запускайте їх у "правильному" порядку (наприклад, запуску Linux init-скриптів перед systemd, де послуга для запуску спочатку називається 1.xxx, а друга - 2.xxx тощо).
gbjbaanb

Сценарії з таким порядком неміцні. Він містить безліч неявних залежностей. Натомість я думав, що якщо у вас є "головний" компонент, у якого є статично налаштований список компонентів, які слід запускати (як згадував @Lie Ryan), то він може транслювати "готовий" захід, коли всі ці компоненти завантажуються. У відповідь на це всі компоненти транслюють свій початковий стан.
Френк Кустерс

4

Ви можете мати ініціалізацію події, яка встановлює стани відповідним чином при завантаженні / запуску. Це може бути бажано для простих систем або програм, що не містять декількох апаратних частин, однак для складніших систем з декількома фізичними компонентами, оскільки ви ризикуєте зовсім не ініціалізувати - якщо подія "гальмування" пропущена або втрачена під час спілкування система (наприклад, система на базі CAN), ви можете ненавмисно встановити систему назад, як якщо б ви її запустили при натиснутому гальмі. Чим більше у вас контролерів, наприклад, з автомобілем, тим більша ймовірність того, що щось буде пропущено.

Для того, щоб врахувати це, ви можете мати логіку "гальмування" кілька разів надсилати події "гальмування". Можливо, кожні 1/100 секунди чи щось. Ваш код, що містить мозок, може прослуховувати ці події і викликати "гальмування", поки він їх отримує. Після того, як 1 / 10сек не отримує сигналів "гальма увімкнено", він запускає внутрішню подію "гальма".

Різні події матимуть значно різні вимоги до термінів. У автомобілі ваш гальмівний світло повинен бути набагато швидшим, ніж скажімо, ваш контрольний паливний індикатор (де певна затримка, можливо, прийнятна) або інші менш важливі системи.

Складність вашої фізичної системи буде диктувати, який із цих підходів є більш підходящим. Враховуючи, що ваш приклад - це транспортний засіб, ви, мабуть, хотіли б чогось подібного до останнього.

Так чи інакше, за допомогою фізичної системи НЕ хочете покладатися на те, що одна подія буде отримана / оброблена правильно. З цієї причини підключені мікроконтролери в мережевій системі часто мають час очікування "я живий".


у фізичній системі ви будете запускати провід і використовувати двійкову логіку: ВИСОК пригнічене гальмо, а НИЗКО гальмо не пригнічене
храповик виродка

@ratchetfreak Є дуже багато можливостей для подібних речей. Можливо, комутатор впорається з цим. Є багато інших системних подій, які не обробляються просто.
Ендерленд

1

У цьому випадку я б не моделював гальмо як просте вмикання / вимикання. Швидше, я б посилав події "тиску гальм". Наприклад, тиск 0 вказуватиме на виключення, а тиск 100 буде повністю знижений. Система (вузол) постійно надсилатиме події тиску перерви (через певний проміжок часу) до контролера (ів) за потребою.

Коли система була запущена, вона почне приймати події тиску, поки не буде вимкнено.


1

Якщо ваш єдиний засіб передачі інформації про стан - це події, значить, ви переживаєте проблеми. Натомість вам потрібно вміти обидва:

  1. запитувати поточний стан педалі гальма та
  2. зареєструйте для подій, що змінилися в штаті, з педалі гальма.

Світло гальма можна побачити як спостерігач педалі гальма. Іншими словами, педаль гальма нічого не знає про світло гальма, і може працювати без нього. (Це означає, що будь-яке поняття педалі гальма проактивно спрямовує подію "початкового стану" на гальмівне світло нераціонально.)

Після встановлення системи гальмівний індикатор реєструється за допомогою педалі гальма, щоб отримувати сповіщення про гальмування, а також зчитує поточний стан педалі гальма та вмикається або вимикається.

Потім сповіщення про гальмування можна реалізувати одним із трьох способів:

  1. як параметри "події педалі гальмування змінилися" події
  2. як пара "педаль гальмування тепер пригнічена" і "гальмівна педаль тепер відпущена" події
  3. як подія "нового стану педалі розбиття" з параметром "пригнічений" або "відпущений".

Я віддаю перевагу першому підходу, а це означає, що після отримання повідомлення, гальмівний індикатор просто зробить те, що вже знає, як робити: прочитати поточний стан педалі гальма і включити або вимкнути.


0

У системі, керованій подіями (яку я зараз використовую і люблю), я вважаю важливим підтримувати речі якомога більше нерозділеними. Тож маючи на увазі цю ідею, давайте поглибимося прямо.

Важливо мати деякий стан за замовчуванням. Ваш гальмівний індикатор прийняв стан за замовчуванням "вимкнено", а ваша гальмівна педаль прийме стан за замовчуванням "вгору". Будь-які зміни після цього були б подією.

Тепер для вирішення вашого питання. Уявіть, що ваша педаль гальма ініціалізується та натискається, подія спрацьовує, але гальмівних вогнів ще немає, щоб прийняти подію. Мені було найпростіше відокремити створення об’єктів (де слухачі подій ініціалізуються) як окремий крок перед ініціалізацією будь-якої логіки. Це запобіжить будь-яким умовам гонки, як ви описали.

Я також вважаю незручним використовувати два різні події для того, що фактично одна і та ж річ . brake_offі brake_onможе бути спрощений в e_brakeпараметр bool on. Ви можете спростити свої події таким чином, додавши допоміжні дані.


0

Вам потрібні вхідні повідомлення про події та повідомлення. Трансляція - це повідомлення, яке публікується невизначеній кількості слухачів. Компонент може передплачувати трансляцію подій, тому він отримує лише події, які його цікавлять. Це забезпечує роз'єднання, оскільки відправник не повинен знати, хто такі приймачі. Таблицю передплати потрібно статично налаштувати під час встановлення компонента (замість того, коли ініціалізовано). Вхідні - це частина маршрутизатора повідомлень, яка виконує функцію буфера для утримання повідомлень, коли цільовий компонент в автономному режимі.

Використання рахунків-фактур приносить одну проблему - розмір папки "Вхідні". Ви не хочете, щоб система мала зберігати все більшу кількість повідомлень для компонентів, які більше ніколи не будуть в Інтернеті. Це важливо особливо при вбудованій системі із суворими обмеженнями в пам'яті. Щоб подолати обмеження розміру вхідних повідомлень, всі трансляційні повідомлення повинні дотримуватися кількох правил. Правила такі:

  1. кожна трансляційна подія вимагає імені
  2. у будь-який момент часу відправник трансляції події може мати лише одну активну трансляцію із вказаним іменем
  3. ефект, викликаний подією, повинен бути безсильним

Ім’я широкомовної програми необхідно оголосити під час встановлення компонента. Якщо компонент надсилає другу трансляцію з тим самим іменем до того, як приймач обробить попередній, новий трансляція замінить попередній. Тепер ви можете мати статичний ліміт розміру вхідних повідомлень, за якого можна гарантувати, що він ніколи не перевищує певний розмір, і його можна здійснити попередній розрахунок на основі таблиць підписки.

Нарешті, вам також потрібен архів трансляції. Архів трансляції - це таблиця, яка містить останню подію від кожного імені трансляції. Щойно встановлені нові компоненти матимуть свою папку "Вхідні", за допомогою якої надсилаються повідомлення з архіву широкомовної програми. Як і вхідні повідомлення, архів трансляції також може мати статичний розмір.

Крім того, для вирішення ситуації, коли сам маршрутизатор повідомлень перебуває в автономному режимі, вам також потрібні вихідні повідомлення. Вихідні повідомлення є частиною компонента, який тимчасово утримує вихідне повідомлення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.