SObjectizerのルーツは、自動プロセス制御システム(APCS)のトピックに根ざしています。 ただし、プロセス制御システムから遠く離れた領域でSObjectizerを使用しました。 そのため、「ドラフトを長い間取り上げていません...」というカテゴリからノスタルジアを感じることがあります 。このため、SObjectizerで最もボリュームのある例の1つであるmachine_controlが登場しました。 それから、私は「昔を揺さぶる」ことを望み、現代のSObjectizerで機器を制御するタスクをシミュレートしました。 さて、バケツから、配信フィルター、テンプレートエージェント、優先度をサポートするディスパッチャーなど、SObjectizerのさまざまなおいしい機能の例を詰め込みます。 今日は、すべてがどのように機能するかを説明してみます。
モデリングとは何ですか?
エンジンを内蔵した生産施設で機械または工作機械を扱っているとします。 さて、コンベアベルト駆動としましょう。 または、水を汲み上げるポンプです。 ポイントではありません。 重要なことは、エンジンが動作しているとき、エンジンが熱くなることです。 そして、熱くなったら、冷却する必要があります。 そのため、エンジンの近くに冷却ファンが取り付けられています。 エンジンが70度以上暖まった場合、このファンをオンにする必要があります。 冷却の結果、エンジンが50度に冷却された場合、ファンをオフにする必要があります。 冷却にもかかわらずエンジンが加熱し続け、温度が95度に達した場合、エンジンを停止し、50度に冷めるまで待つ必要があります。
当然、これらすべてがダイナミクスでどのように発生するかを確認したいと思います。 エンジンが現在動作しているかどうか、エンジンの温度、冷却ファンがオンになっているかどうかを確認する必要があります。 これを行うには、machine_controlの実装で、このすべての情報をコンソールに簡単に定期的に印刷します。
さて、私たちにとってより面白くするために、1つのエンジンではなく、複数のエンジンで作業します。 すべて同じように機能しますが、プロパティは異なります。 それらのいくつかはより速く熱し、いくつかはより遅くなります。 したがって、たとえば4つのエンジンの作業を観察すると、それぞれが独自の生活を送っているように見えます。
行こう!
選択したソリューションに関するいくつかの一般的な言葉
問題はいくつかの方法で解決できることは明らかです。 以下で説明する方法は、可能な方法の1つにすぎません。 単純さと実用性の理由からではなく、SObjectizerのさまざまな機能を実証する機会のために選ばれました。 したがって、後続のテキストをデモとして扱うことをお勧めします。 さらに、本番環境ではすべてがより深刻で悪化します;)
この例はどのように機能しますか?
いくつかのマシンエージェントがあり、特別な共通メールボックスがあります。 マシンエージェントは、現在のステータスに関するメッセージをこの共有メールボックスに時々送信します。
一般的なメールボックスから、マシンエージェントのステータスに関するメッセージは、完全に異なる2つのエージェントに届きます。 最初のtotal_status_dashboardは、マシンエージェントのステータスに関する情報を収集し、標準出力ストリームで発生していることに関する情報を定期的に表示します。 例の実行により、おおよそ次の図が表示されます。
2番目のエージェントはstatuses_analyserです。 彼は、マシンで何が起こっているかを制御し、マシンの1つが外部の影響を必要とする瞬間を判断するために、ステータスに関する情報を受け取ります。 アクションが必要な場合、statuses_analyserエージェントは同じパブリックメールボックスに別のメッセージを送信します。 Machine_controllerエージェントはこのメッセージに応答します。 特定のマシンにどの制御アクションを適用するかをすでに決定しており、対応するメッセージを対応するエージェントマシンに直接送信します。
実際、それがすべてです。 残りの点については、以下で詳細に検討します。
エージェントa_machine_t
「ストーブから」の例の実装の分析を開始します。 エンジンと冷却ファンを備えたマシン自体から。 実際には、いくつかのセンサーとコントローラーが接続される実際のハードウェアがあります。 そして、このすべてのキッチンでは、プログラムから1つまたは別のインターフェイスを介して、または多分いくつかの異なるインターフェイスを介して作業する必要があります。
しかし、架空の例があるため、ポートをポーリングしたり、実際の機器からデータを読み取ったりする必要はありません。 稼働中のマシンのようなものが必要です。 これを行うために、エージェントa_machine_tがあります。
- エンジンの状態、ファンの動作、エンジンの現在の温度など、その状態に関する情報を定期的に外部に発行します。
- エンジンをオン/オフするコマンドを受け入れて実行します。
- ファンをオン/オフするコマンドを受け入れて実行します。
このエージェントが模倣に従事しているという事実にもかかわらず、かつてはプロセス制御システムのタスクで、同様のロジックを使用することが可能でした。 タイマーイベントのエージェントは、何らかのインターフェイスを介して機器にアクセスし、センサーから情報を収集しました。 その後、彼はキャプチャした情報を目的の形式に変換し、この情報を適切に処理できる人に送信しました。
このa_machine_tエージェントの外観を見てみましょうが、最初にa_machine_tの実装に必要な定義をいくつか紹介します。
// . enum class engine_state_t { on, off }; // . enum class cooler_state_t { on, off }; // - . struct turn_engine_on : public so_5::signal_t {}; struct turn_engine_off : public so_5::signal_t {}; // - . struct turn_cooler_on : public so_5::signal_t {}; struct turn_cooler_off : public so_5::signal_t {}; // . struct machine_status { // - . // , . const std::string m_id; // . const engine_state_t m_engine_status; // . const cooler_state_t m_cooler_status; // . const float m_engine_temperature; };
したがって、エージェントa_machine_tは、turn_engine_on / turn_engine_offおよびturn_cooler_on / turn_cooler_offシグナルメッセージの形式で制御コマンドを受信し、machine_statusメッセージを送信してそのステータスを通知します。
これで、エージェントa_machine_t自体の検討に進むことができます。 ジブルから始めましょう:
class a_machine_t : public so_5::agent_t { // // . struct update_status : public so_5::signal_t {}; // - : // , , const state_t st_engine_on{ this, "on" }; // , . const state_t st_engine_off{ this, "off" }; // - . const std::string m_id; // machine_status. const so_5::mbox_t m_status_distrib_mbox; // : // , const float m_initial_temperature; // , const float m_engine_heating_step; // . const float m_cooler_impact_step; // . float m_engine_temperature; // . engine_state_t m_engine_status = engine_state_t::off; cooler_state_t m_cooler_status = cooler_state_t::off; // ID update_status. // SO-5 ID // , // . so_5::timer_id_t m_update_status_timer;
エージェントa_machine_tは、「エンジンがオン」と「エンジンがオフ」の2つの状態を持つ非常に単純な状態マシンです。 それらのそれぞれで、彼はいくつかのメッセージに異なって反応します。 SObjectizerでエージェントを状態マシンとして提示するために、st_engine_onとst_engine_offの2つの個別の属性が必要でした。
開始すると、エージェントは定期的なupdate_statusメッセージを開始します。 このメッセージを受信するたびに、エージェントはエンジンとファンが作動しているかどうかを考慮して、m_engine_temperature値を再カウントします。 次に、現在の測定値を含むmachine_statusメッセージがm_status_distrib_mboxメールボックスに送信されます。
エージェントのコンストラクターは、初期パラメーターが豊富なために膨大に見えますが、実際には些細なことなので、考慮しません。
しかし、それはもっと面白いです。 まず、これは特別なso_define_agent()メソッドであり、エージェントがSObjectizer内で動作するように自身を構成できるようにするために使用されます。 a_machine_tは初期状態に移行し、必要なメッセージをサブスクライブする必要があります。 これは次のようなものです。
virtual void so_define_agent() override { this >>= st_engine_off; st_engine_on .event< turn_engine_off >( &a_machine_t::evt_turn_engine_off ) .event< turn_cooler_on >( &a_machine_t::evt_turn_cooler_on ) .event< turn_cooler_off >( &a_machine_t::evt_turn_cooler_off ) .event< update_status >( &a_machine_t::evt_update_status_when_engine_on ); st_engine_off .event< turn_engine_on >( &a_machine_t::evt_turn_engine_on ) .event< turn_cooler_on >( &a_machine_t::evt_turn_cooler_on ) .event< turn_cooler_off >( &a_machine_t::evt_turn_cooler_off ) .event< update_status >( &a_machine_t::evt_update_status_when_engine_off ); }
異なる状態のエージェントは、異なるハンドラーを使用してupdate_status信号に応答することに注意してください。 また、st_engine_on状態では、すでに実行中のエンジンをオンにする意味がないため、turn_engine_on信号が無視されることもわかります。 同様に、状態st_engine_offのturn_engine_offを使用します。
so_define_agent()メソッドに関しては、少し余談するしかありません。一見、このメソッドは冗長に見えますが、それなしでも実行できるようです。 結局のところ、コンストラクターですべてのエージェント設定を直接実行できます。
確かに、それは可能です。 簡単な場合、これは行われます。 ただし、エージェントの継承が使用される場合、so_define_agent()でエージェントを設定する方がコンストラクターよりも便利です。 クラスの相続人は、基本クラスの設定に干渉する簡単で便利な方法を取得します(たとえば、基本クラスでso_define_agent()を単に呼び出すことはできず、すべての設定は派生クラスによって行われます)。
さらに、経験上、別のso_define_agent()メソッドは、さまざまな人々によって書かれた大規模なプロジェクトで正当化され始めます。他の人のコードを開き、すぐに別の人のエージェントのso_define_agent()を見ると、すべてが1か所に表示されます 多くの時間とエネルギーを節約します。
次に重要なメソッドはso_evt_start()です。 SObjectizerは、SObjectizer内で動作を開始するすべてのエージェントで自動的に呼び出します。 a_machine_tはso_evt_start()を使用して、定期的なupdate_statusメッセージの送信を開始します。
virtual void so_evt_start() override { // update_status // ID , , // . m_update_status_timer = so_5::send_periodic< update_status >( // . *this, // . std::chrono::milliseconds(0), // 200ms. std::chrono::milliseconds(200) ); }
次は、a_machine_tエージェントイベントハンドラーです。 イベントハンドラは、エージェントが対応するインシデントメッセージを受信したときにSObjectizerが呼び出すメソッドです。 インシデントメッセージとハンドラーの対応は、メッセージをサブスクライブするときに設定されます。 したがって、次の形式のサブスクリプション:
st_engine_on .event< turn_engine_off >( &a_machine_t::evt_turn_engine_off )
エージェントがturn_engine_offタイプのメッセージを受信した場合、エージェントはevt_turn_engine_off()メソッドを呼び出す必要があることをSObjectizerに伝えます。
a_machine_tエージェントには4つの単純なハンドラーがありますが、それらがどのように機能するかを説明する意味はありません。
void evt_turn_engine_off() { // . this >>= st_engine_off; // . m_engine_status = engine_state_t::off; } void evt_turn_engine_on() { this >>= st_engine_on; m_engine_status = engine_state_t::on; } void evt_turn_cooler_off() { // . // . m_cooler_status = cooler_state_t::off; } void evt_turn_cooler_on() { m_cooler_status = cooler_state_t::on; }
しかし、定期的なメッセージupdate_statusに対する反応については、いくつかの説明をする必要があります。 最初に、エンジンの実行中のupdate_statusに対する反応を見てみましょう。
void evt_update_status_when_engine_on() { m_engine_temperature += m_engine_heating_step; if( cooler_state_t::on == m_cooler_status ) m_engine_temperature -= m_cooler_impact_step; distribute_status(); }
エンジンは私たちと一緒に働いており、それゆえに熱くなっているので、現在の温度を上げる必要があります。 ただし、冷却ファンがオンになっている場合は、冷却効果を考慮して温度を調整する必要があります。
update_status信号がst_engine_off状態で処理されるとき、つまり エンジンがオフになったときに、冷却の効果を考慮する必要があるのは、現在オンになっている場合のみです。
void evt_update_status_when_engine_off() { if( cooler_state_t::on == m_cooler_status ) { m_engine_temperature -= m_cooler_impact_step; // // . if( m_engine_temperature < m_initial_temperature ) m_engine_temperature = m_initial_temperature; } distribute_status(); }
さて、補助メソッドdistribute_status()は完全に簡単な実装です。 その唯一のタスクは、この目的のために特別に設計されたメールボックスにmachine_statusメッセージを送信することです。
void distribute_status() { // send- machine_status, // send. // // m_status_distrib_mbox. so_5::send< machine_status >( // . m_status_distrib_mbox, // // machine_status. m_id, m_engine_status, m_cooler_status, m_engine_temperature ); }
machine_statusメッセージはどこに行きますか?
おそらく読者の1人が疑問を抱いていました。なぜa_machine_t自体がファンとエンジンのオン/オフを決定しないのですか? 何が起こっているのかを個別に分析して適切なアクションをとるのではなく、a_machine_tは現在の状態に関するメッセージを定期的にどこかに送信するだけなのですか?
これは、分解のためです:)
オブジェクト指向のアプローチに基づいてシステムを設計するとき、各オブジェクトがそのタスクを担当し、必要な効果がそれらを組み合わせることによって達成されるように努めています。 アクター(エージェント)に基づいてシステムを設計するときにも同じことが起こります。各アクターが自分のタスクを担当し、それらの組み合わせを通じてソリューションを策定します。
したがって、a_machine_tエージェントは1つのタスクのみを解決します。機器とのインターフェースを実装します(この例では、このインターフェースを模倣します)。 実際のタスクでは、a_machine_tは、ビットの設定/リセット、通信ポートでのバイトの読み取りと書き込み、I / O操作の成功の確認、データ転送速度、タイムアウトなどの低レベルの制御を行います。
したがって、a_machine_tのタスクは、さらなる処理に適したデバイスから意味のある情報を取得し、この情報を2階の誰かに与え、上からデバイスのコマンドを受け取り、このコマンドをデバイスが理解する一連のアクションに変換することです。 特定のシミュレーションの制限の範囲で、a_machine_tが行うこと。
エージェントa_total_status_dashboard_t
a_total_status_dashboard_tエージェントはmachine_statusメッセージを収集し、これらのメッセージから情報を集約し、集約された情報を定期的に標準出力にマップする必要があります。
仕事を完了するために、a_total_status_dashboard_tは2つのメッセージをサブスクライブします。
virtual void so_define_agent() override { so_subscribe( m_status_distrib_mbox ) .event( &a_total_status_dashboard_t::evt_machine_status ); so_subscribe_self().event< show_dashboard >( &a_total_status_dashboard_t::evt_show_dashboard ); }
エージェントは、a_machine_tエージェントがmachine_statusメッセージを送信する特別なメールボックスからの最初のメッセージmachine_statusを予期します。 そして、2番目のメッセージ、show_dashboard、エージェントa_total_status_dashboard_tは、定期的なメッセージの形式で自分自身に送信します。
virtual void so_evt_start() override { // // . const auto period = std::chrono::milliseconds( 1500 ); m_show_timer = so_5::send_periodic< show_dashboard >( *this, period, period ); }
ここで、a_total_status_dashboard_tはa_machine_tエージェントと同じアプローチを使用します— so_evt_start()メソッドで定期的なメッセージを開始します。SObjectizerは最初にa_total_status_dashboard_tを自動的に呼び出します。
machine_statusの処理は非常に簡単です。次のバッチの情報を連想コンテナに保存するだけです。
void evt_machine_status( const machine_status & status ) { m_machine_statuses[ status.m_id ] = one_machine_status_t{ status.m_engine_status, status.m_cooler_status, status.m_engine_temperature }; }
また、show_dashboardハンドラーには複雑なものは何も含まれていません。標準出力ストリームへの出力を使用して、連想コンテナーの内容を繰り返し処理するだけです。
void evt_show_dashboard() { auto old_precision = std::cout.precision( 5 ); std::cout << "=== The current status ===" << std::endl; for( const auto & m : m_machine_statuses ) { show_one_status( m ); } std::cout << "==========================" << std::endl; std::cout.precision( old_precision ); }
エージェントa_total_status_dashboard_tはあまり関心がなく、その実装は非常に単純なので、これ以上実装に進みません。 気にする人は、a_total_status_dashboard_tの完全なソースコードをここで見ることができます 。
エージェントa_statuses_analyser_tおよびa_machine_controller_t
machine_controlの例のこの実装では、a_machine_tエージェントからの情報の分析とマシンエージェントへの制御コマンドの発行にいくつかのエージェントの束が関与しています。
まず、エージェントa_statuses_analyser_tは、machine_statusメッセージを受信して分析し、特定のa_machine_tに何らかの影響が必要であることを判断します。
次に、a_machine_controller_tタイプのテンプレートエージェントのグループがあります。これは、a_statuses_analyser_tからの信号に応答し、特定のマシンに特定の効果をもたらします。 したがって、1つのエージェントa_machine_controlle_tは、冷却ファンをオンにする必要がある状況に反応し、対応するエージェントa_machine_tにturn_cooler_onメッセージを送信します。 別のa_machine_controller_tエージェントは、エンジンをオフにする必要がある状況に応答し、turn_engine_offメッセージを送信します。 等
一般的に、このようなa_statuses_analyser_tとa_machine_controller_tの分割は、この例の明らかな複雑さです。 1つのエージェントa_statuses_analyser_tだけで完全に管理できます。エージェントa_statuses_analyser_t自体は、情報を分析して制御コマンドを送信できます。 ほとんどの場合、エージェントa_statuses_analyser_tのボリュームは同時に大幅に増加します。
当初、machaine_controlはさまざまなSObjectizer機能、特にテンプレートエージェントの使用とエージェントの優先度を表示したかったため、a_statuses_analyser_tとa_machine_controller_tの間でロジックを分離することにしました。
したがって、a_machine_t、a_statuses_analyser_t、a_machine_controller_tの相互作用の本質は次のとおりです。
- a_machine_tは、メッセージmachine_statusの形式で自身に関する情報を定期的に送信します。
- a_statuses_analyser_tはmachine_statusから情報を収集し、特定のa_machine_tへの影響が必要かどうかを判断します。 影響が必要な場合、a_status_analyser_tは、どのマシンにどの影響が必要かに関する情報を含むメッセージをmachine_needs_attentionに送信します。
- a_machine_controller_tエージェントはmachine_needs_attentionメッセージに応答し、特定のa_machine_tエージェントのturn_engine_on / turn_engine_offおよび/またはturn_cooler_on / turn_cooler_offメッセージを生成します。
machine_needs_attentionメッセージは次のようになります。
// , . enum class attention_t { none, engine_cooling_done, engine_cooling_needed, engine_overheat_detected }; // , - . struct machine_needs_attention { // - . const std::string m_id; // . const attention_t m_attention; // (/). const engine_state_t m_engine_status; // (/). const cooler_state_t m_cooler_status; };
a_statuses_analyser_tエージェントは、各マシンに関する過去の情報を保存し、machine_statusメッセージからの新しい情報と比較します。 エンジンの冷却が必要であることが検出された場合、エンジンが過熱した場合、またはエンジンが安全な温度に達した場合、a_statuses_analyser_tはmachine_needs_attentionメッセージを生成します。 このメッセージは、対応するa_machine_controller_tによって取得され、必要なコマンドが必要なa_machine_tエージェントに送信されます。
エージェントの詳細a_statuses_analyser_t
エージェントa_statuses_analyser_tの内臓は非常に膨大です。 ただし、それらのほとんどは、マシンエージェントの現在の状態の保存と分析に関連しています。 この部分を詳細に分析することはしません(質問があれば、コメントで回答します)。簡単に説明します。
- agent a_statuses_analyser_tは、各マシンエージェントの最後の既知のステータスに関する情報を含む連想コンテナを格納します。 このコンテナの鍵は、マシンの一意の識別子名です。
- 次のmachine_statusメッセージを受信すると、エージェントは以前に保存した情報で最新の情報を確認します。 マシンエージェントへの露出が必要な状況が見つかった場合、machine_needs_attentionメッセージが生成されます。
- その後、a_statuses_analyser_tエージェントは、関連するコンテナー内のデータを更新します。
コードでは、これにはかなりの量の行が必要ですが、そこには複雑なことは何もありません。
ただし、SObjectizerとの対話に関連するa_statuses_analyser_tエージェントの部分は通常最小限です。so_define_agent()の唯一のメッセージとこのメッセージの唯一のイベントにサブスクライブするだけです。
virtual void so_define_agent() override { so_subscribe( m_status_distrib_mbox ).event( &a_statuses_analyzer_t::evt_machine_status ); } void evt_machine_status( const machine_status & status ) { auto it = m_last_infos.find( status.m_id ); if( it == m_last_infos.end() ) // . // . it = m_last_infos.insert( last_info_map_t::value_type { status.m_id, last_machine_info_t { attention_t::none, status.m_engine_temperature } } ).first; handle_new_status( status, it->second ); }
ここで、evt_machine_status()内で呼び出されるhandle_new_status()メソッドは、既に簡単に説明した、適用されたエージェントマシンステータス制御ロジックの一部です。
a_machine_controller_tエージェントの詳細
一般的にa_machine_controller_tの意味は何ですか?
a_machine_controller_tエージェントのインスタンスをいくつか持つことは、次の意味があります。たとえば、a_statuses_analyser_tエージェントが、車のエンジンが安全な温度まで冷却されたと判断した場合、いくつかの異なることを行う必要があります。 まず、冷却ファンをオフにします。 第二に、エンジンが以前にシャットオフされたことが判明する場合があり、再度オンにする必要があります。
このすべてのロジックをa_statuses_analyser_t内に配置すると、コードが大きくなり、それがより困難になることがわかります。 代わりに、a_statuses_analyser_tにマシンエージェントの操作で注意を払うべき内容を宣言するように強制しました。 A_machine_controller_tエージェントはすでにこのアナウンスに応答しています。 そして、各a_machine_controller_t自体が応答する必要があるかどうかを決定します。 必要な場合、a_machine_contoller_tはメッセージmachine_needs_attentionを受信し、エージェントマシンに対して対応するコマンドを開始します。 必要でない場合、a_machine_controller_t machine_needs_attentionは単にメッセージを無視します。
エージェントa_machine_controller_t
テンプレートエージェントa_machine_controller_tのコードは小さいため、操作しやすいように、このエージェントのコード全体を完全に示します。
template< class LOGIC > class a_machine_controller_t : public so_5::agent_t { public : a_machine_controller_t( context_t ctx, so_5::priority_t priority, so_5::mbox_t status_distrib_mbox, const machine_dictionary_t & machines ) : so_5::agent_t( ctx + priority ) , m_status_distrib_mbox( std::move( status_distrib_mbox ) ) , m_machines( machines ) , m_logic() {} virtual void so_define_agent() override { so_set_delivery_filter( m_status_distrib_mbox, [this]( const machine_needs_attention & msg ) { return m_logic.filter( msg ); } ); so_subscribe( m_status_distrib_mbox ) .event( [this]( const machine_needs_attention & evt ) { m_logic.action( m_machines, evt ); } ); } private : const so_5::mbox_t m_status_distrib_mbox; const machine_dictionary_t & m_machines; const LOGIC m_logic; };
したがって、これは、1つのパラメーター(特定のa_machine_controller_tが持つべきアプリケーションロジックのタイプ)によってパラメーター化されるテンプレートクラスです。 このタイプのLOGICは、次の形式の2つのメソッドを持つタイプでなければなりません。
struct LOGIC { bool filter( const machine_needs_attention & msg ) const; void action( const machine_dictionary_t & machines, const machine_needs_attention & evt ) const; };
C ++ 11に概念があった場合、a_machine_controller_tテンプレートのパラメーターになりうるタイプとそうでないタイプを判別しやすくするために、適切な概念を宣言することができます。 しかし、なぜなら C ++ 11は概念をもたらさなかったため、アヒルのタイピングに頼らなければなりません。
a_machine_controller_tエージェントは、内部にLOGICタイプのインスタンスを作成し、そのすべてのアクションをこのインスタンスに委任します。 そして、このインスタンスは、エージェントマシンに制御アクションを発行するコントローラーにすぎません。
コントローラーには2つのアクションのみがあります。
まず、特定のコントローラーにとって関心のないメッセージをフィルターで除外する必要があります。これを行うために、a_machine_controller_tはメッセージ配信フィルターを割り当てます。
so_set_delivery_filter( m_status_distrib_mbox, [this]( const machine_needs_attention & msg ) { return m_logic.filter( msg ); } );
配信フィルターは、SObjectizerの特別なメカニズムです。エージェントがメールボックスMからタイプTの特定のメッセージをサブスクライブし、タイプTのすべてのメッセージを受信するのではなく、エージェントにとって興味深い情報を含むメッセージのみを受信する場合に必要です。
so_set_delivery_filter() , -, M , T. true, . false, , , .
. Subscriber-1 mbox-. Subscriber-2 , .
LOGIC::filter() , machine_needs_attention, .
-, - machine_needs_attention, , .
LOGIC::action(). a_machine_controller_t machine_needs_attention:
so_subscribe( m_status_distrib_mbox ) .event( [this]( const machine_needs_attention & evt ) { m_logic.action( m_machines, evt ); } );
controller-
, , a_machine_controller_t, . , turn_engine_off, :
struct engine_stopper_t { bool filter( const machine_needs_attention & msg ) const { return msg.m_attention == attention_t::engine_overheat_detected; } void action( const machine_dictionary_t & machines, const machine_needs_attention & evt ) const { so_5::send< turn_engine_off >( machines.find_mbox( evt.m_id ) ); } };
すなわち machine_needs_attention, . , - turn_engine_off.
:
struct cooler_starter_t { bool filter( const machine_needs_attention & msg ) const { return (msg.m_attention == attention_t::engine_overheat_detected || msg.m_attention == attention_t::engine_cooling_needed) && msg.m_cooler_status == cooler_state_t::off; } void action( const machine_dictionary_t & machines, const machine_needs_attention & evt ) const { so_5::send< turn_cooler_on >( machines.find_mbox( evt.m_id ) ); } };
. , , .
:
- engine_stopper_t ;
- engine_starter_t ;
- cooler_starter_t ;
- cooler_stopper_t .
, a_machine_controller_t — .
a_machine_controller_t
machine_control , machine_needs_attention . , machine_needs_attention attention_t::engine_overheat_detected cooler_status_t::off, : engine_stopper_t, cooler_starter_t.
, . , engine_overheat_detected turn_engine_off, turn_cooler_on. - , . , . , .
SObjectizer- ?
. , . , . . , , .
SObjectizer : , . so_5::disp::prio_one_thread::strictly_ordered. a_machine_controller_t .
- :
- engine_stopper_t, .. ;
- cooler_starter_t, — ;
- engine_starter_t;
- cooler_stopper_t.
, a_machine_controller_t :
a_machine_controller_t( ..., so_5::priority_t priority, ... ) : so_5::agent_t( ctx + priority ) , ... {}
:
coop.make_agent_with_binder< a_machine_controller_t< engine_stopper_t > >( disp->binder(), so_5::prio::p4, status_distrib_mbox, machines ); coop.make_agent_with_binder< a_machine_controller_t< cooler_starter_t > >( disp->binder(), so_5::prio::p3, status_distrib_mbox, machines );
so_5::prio::p4 so_5::prio::p3 — .
-
, a_machine_t st_engine_off, .. . , . , a_machine_t, a_machine_controller_t ?
, - -, turn_engine_on -.
, C++ , a_machine_t, a_total_status_dashboard_t, a_statuses_analyser_t a_machine_controller_t. , ad-hoc . すなわち , -. machine_control :
coop.define_agent().on_start( [&dict] { dict.for_each( []( const std::string &, const so_5::mbox_t & mbox ) { so_5::send< turn_engine_on >( mbox ); } ); } );
define_agent() ad-hoc , . ad-hoc SObjectizer-: turn_engine_on.
SObjectizer-, , .
. - . SObjectizer . , .
machine_control , :
- - , one_thread;
- a_total_status_dashboard_t ( one_thread);
- a_statuses_analyser_t ( one_thread);
- a_machine_controller_t prio_one_thread::strictly_ordered, ;
- - .
« »?
, , , : machine_dictionary . , - - .
, - ( machine_status machine_needs_attention) . SObjectizer , , , turn_engine_on, . machine_dictionary.
?
, , , .
, , , , a_machine_t a_machine_controller_t, .
SObjectizer-, ad-hoc — . , , - SObjectizer, , , , « ». -, , ping-pong , , . , SObjectizer.
, , . , , Actor Model Publish/Subscribe .
machine_control , (.. ) .
, a_machine_t machine_status , , . a_machine_t ( ) . , .
a_machine_t ( turn_engine_on), , . a_machine_t , . , — a_machine_t .
, - turn_engine_on a_machine_t, Actor Model, 1:1.
machine_status machine_needs_attention Actor Model, .. 1:N. Publish/Subscribe. , machine_status machine_needs_attention, . — - Publish. Subscribe, , , a_total_status_dashboard_t, a_statuses_analyser_t a_machine_controller_t so_define_agent().
, SObjectizer: . , SObjectizer multi-producer/multi-consumer , 1:N.
, machine_status, , , . a_total_status_dashboard_t machine_status . a_statuses_analyser_t machine_status a_machine_t.
. , . a_total_status_dashboard_t a_statuses_analyser_t. a_total_status_dashboard_t - a_gui_status_dashboard_t, std::cout, . , , , machine_status.
SObjectizer — , , . SObjectizer , ( CSP- ).
SObjectizer , , . (.. ), .
, . — . , . -, - , so_5::signal_t, . , - , - . , , - - .
結論の代わりに
, , .
, SObjectizer- so_5_extra , SObjectizer. , SObjectizer , , . so_5_extra , . - SObjectizer, . SObjectizer, so_5_extra.