イベント駆動型アーキテクチャ では、各コンポーネントは、イベントがシステムを通じて送信されたときにのみ機能します。
ブレーキペダルとブレーキライトを備えた架空の車を想像してください。
これは、ブレーキペダルがすでに押された状態で車がオンになる状況になるまで、すべて順調です。ブレーキライトはbrake_onイベントを受け取らなかったため、オフのままになります-明らかに望ましくない状況です。デフォルトでブレーキライトをオンにしても、状況が逆になるだけです。
この「初期状態の問題」を解決するために何ができるでしょうか?
編集:すべての回答に感謝します。私の質問は実車についてではありませんでした。車では、状態を継続的に送信することでこの問題を解決しました。そのため、そのドメインには起動の問題はありません。私のソフトウェアドメインでは、そのソリューションは多くの不要なCPUサイクルを使用します。
EDIT 2:@ gbjbaanbの回答 に加えて、次のようなシステムを使用します。
このソリューションでは、コンポーネント間の依存関係、競合状態、メッセージキューの古さ、「マスター」コンポーネントはありません。
これを行う方法はたくさんありますが、私はメッセージベースのシステムを可能な限り切り離しておくことを好みます。これは、システム全体がコンポーネントの状態を読み取ることも、コンポーネントが他のコンポーネントの状態を読み取ることもできないことを意味します(そのようにして、依存関係のスパゲッティタイがあります)。
したがって、実行中のシステムはそれ自体を管理しますが、各コンポーネントにそれ自体を起動するように指示する方法が必要であり、コンポーネントの登録にそのようなことはすでにあります。つまり、起動時にコアシステムは各コンポーネントにそれがこれで登録されました(または各コンポーネントに詳細を返して登録できるようにするよう依頼します)。これは、コンポーネントが起動タスクを実行し、通常の操作と同じようにメッセージを送信できる段階です。
そのため、ブレーキペダルは、イグニッションが開始されると、車の管理から登録/チェックメッセージを受け取り、「私はここにいます」というメッセージを返すだけでなく、自分の状態をチェックして送信します。その状態のメッセージ(ペダルを踏んだメッセージなど)。
その後、問題は起動依存性の1つになります。ブレーキライトがまだ登録されていない場合、メッセージは受信されませんが、コアシステムが起動、登録、チェックルーチンを完了するまでこれらのメッセージをすべてキューに入れることで簡単に解決できます。 。
最大の利点は、既に記述する必要があることを除いて、初期化を処理するために必要な特別なコードがないことです(OK、ブレーキペダルイベントのメッセージ送信がブレーキペダルハンドラーにある場合は、初期化でもそれを呼び出す必要があります)ですが、ハンドラロジックに強く関連付けられたコードを記述し、コンポーネント間でやり取りを行わない限り、通常は問題ありません。このため、メッセージパッシングアーキテクチャは非常に優れています。
ロード/起動時に状態を適切に設定する初期化イベントを持つことができます。これは、複数のハードウェア部品を含まない単純なシステムまたはプログラムにとって望ましい場合がありますが、初期化をまったく行わないのと同じリスクを実行するために複数の物理コンポーネントを持つより複雑なシステムの場合-通信で「ブレーキ」イベントが失われたり失われたりした場合システム(たとえば、CANベースのシステム)は、ブレーキを踏んだままシステムを開始した場合と同じように、誤ってシステムを逆方向に設定する可能性があります。車の場合など、コントローラーの数が多いほど、何かが見落とされる可能性が高くなります。
これを説明するために、「ブレーキ」ロジックで「ブレーキ」イベントを繰り返し送信することができます。たぶん1/100秒ごとか何か。脳を含むコードは、これらのイベントをリッスンし、それらを受信している間に「ブレーキ」をトリガーできます。 「ブレーキオン」信号を1/10秒受信しないと、内部の「ブレーキ_オフ」イベントがトリガーされます。
イベントによって、タイミング要件はかなり異なります。車では、ブレーキライトは、チェックフューエルライト(数秒の遅延はおそらく許容範囲です)またはその他の重要度の低いシステムよりもはるかに高速である必要があります。
物理システムの複雑さにより、これらのアプローチのどちらがより適切であるかが決まります。あなたの例が車両であるとすると、あなたはおそらくが後者に似た何かを望んでいるでしょう。
どちらの方法でも、物理システムでは、単一のイベントが正しく受信/処理されることに依存したくありません。このため、ネットワークシステムに接続されたマイクロコントローラーには、「I'm alive」タイムアウトが発生することがよくあります。
この場合、ブレーキを単純なオン/オフとしてモデル化しません。むしろ、「ブレーキ圧力」イベントを送信します。たとえば、圧力0はオフを示し、圧力100は完全に押し下げられます。システム(ノード)は、必要に応じて(一定の間隔で)破壊圧力イベントをコントローラーに常に送信します。
システムが起動すると、オフになるまで圧力イベントの受信を開始します。
状態情報を渡す唯一の手段がイベントである場合、問題が発生します。代わりに、両方を実行できる必要があります。
ブレーキライトは、ブレーキペダルのオブザーバーと見なすことができます。つまり、ブレーキペダルはブレーキライトについて何も認識せず、ブレーキライトがなくても操作できます。 (これは、「初期状態」イベントを積極的にブレーキライトに送信するブレーキペダルの概念は、すべて誤解されていることを意味します。)
システムがインスタンス化されると、ブレーキライトがブレーキペダルに登録され、ブレーキ通知を受け取りますまた、ブレーキペダルの現在の状態も読み取りますで、オンまたはオフになります。
次に、ブレーキ通知を次の3つの方法のいずれかで実装できます。
私は最初のアプローチを好んでいます。つまり、通知を受け取ると、ブレーキライトは、それがすでに実行している方法を単純に実行します。つまり、ブレーキペダルの現在の状態を読み取り、オンまたはオフにします。
イベント駆動型のシステム(私が現在使用していて気に入っている)では、物事を可能な限り分離することが重要です。その考えを念頭に置いて、すぐに掘り下げましょう。
デフォルトの状態にすることが重要です。ブレーキライトは「オフ」のデフォルト状態になり、ブレーキペダルは「アップ」のデフォルト状態になります。その後の変更はすべてイベントになります。
今あなたの質問に対処します。ブレーキペダルが初期化されて押し下げられたとすると、イベントが発生しますが、イベントを受け取るためのブレーキライトはまだありません。オブジェクトの作成(イベントリスナーが初期化される場所)を個別のステップbeforeでロジックを初期化することで分離するのが最も簡単であることがわかりました。あなたが説明したように、それはいかなる競合状態も防ぎます。
また、事実上同じものに2つの異なるイベントを使用するのは不便です。 brake_off
およびbrake_on
は、パラメータe_brake
を使用してbool on
に簡略化できます。サポートデータを追加することで、この方法でイベントを簡略化できます。
必要なのは、ブロードキャストイベントとメッセージの受信トレイです。ブロードキャストは、不特定多数のリスナーに公開されるメッセージです。コンポーネントは、ブロードキャストイベントをサブスクライブして、関心のあるイベントのみを受信できます。これにより、送信者は受信者が誰であるかを知る必要がないため、分離が可能になります。サブスクリプションテーブルは、コンポーネントのインストール時に(初期化時ではなく)静的に構成する必要があります。受信ボックスは、宛先コンポーネントがオフラインのときにメッセージを保持するためのバッファーとして機能するメッセージルーターの一部です。
請求書を使用すると、受信トレイのサイズという1つの問題が発生します。システムが、オンラインにならないコンポーネントのメッセージをますます多く保持する必要はありません。これは、特にメモリの制約が厳しい組み込みシステムでは重要です。受信ボックスのサイズ制限を克服するには、すべてのブロードキャストメッセージがいくつかのルールに従う必要があります。ルールは次のとおりです。
ブロードキャスト名は、コンポーネントのインストール時に宣言する必要があります。レシーバーが前のブロードキャストを処理する前に、コンポーネントが同じ名前の2番目のブロードキャストを送信すると、新しいブロードキャストが前のブロードキャストを上書きします。これで、静的な受信ボックスのサイズ制限を設定できます。これは、特定のサイズを超えないことが保証され、サブスクリプションテーブルに基づいて事前計算できます。
最後に、放送アーカイブも必要です。ブロードキャストアーカイブは、各ブロードキャスト名の最後のイベントを保持するテーブルです。インストールされたばかりの新しいコンポーネントには、ブロードキャストアーカイブからのメッセージが事前に入力されています。メッセージの受信トレイと同様に、ブロードキャストアーカイブのサイズも静的にすることができます。
さらに、メッセージルーター自体がオフラインである状況に対処するには、メッセージ送信トレイも必要です。メッセージ送信ボックスは、送信メッセージを一時的に保持するコンポーネントの一部です。