※ 2015年1月に執筆したものを転載
はじめに
前回に引き続き、scilab/xcosの話になります。
CBlockの特殊な使用方法の一端に属するものです。
で、タイトルにも出てきている、boost::statechartですが、
こいつはシミュレータでも何でもないです。
C++のboostというテンプレートライブラリ群に含まれる、有限状態機を割りと簡単に作成するためのライブラリです。
組み込み系だと有限状態機をサクっとつくれちゃうライブラリって重宝するんですよね。
「まだ設計Fixできないけど、大筋あってそうかちょっとだけ動きを見てみたいなぁ」
って時に大活躍してくれます。
というわけで、先にboost:statechartの紹介を簡単にします。
boost:statechart
UML:ステートマシン図の構成を維持したまま状態機を作成できます。
「構成を維持」できるというのが、結構ポイントが高く、
大概、ステートマシン図をソフトウェアとして実装すると、
ソフトウェアの構造とステートマシン図の構造が一致しない状況になりやすいです。
それでもなんとか振る舞いを一致させるのが状態機実装の頑張りどころなのですが。
ここで問題となるのが、「構造が一致しない状況」です。
これのために発生する問題としては以下があります。
- 設計を変更したが、該当するコード、影響範囲がよくわからない。
- コード上で不具合を潰して設計側に反映したいが、そもそも設計のどこに該当するかわからない。
- 設計上で一個状態を追加しただけなのに遷移爆発が起こった。
「構成を維持」、つまり、構造が一致している状況にできれば上記問題は解決。
そのためのライブラリってことになります。
状態機というと、GOF:ステートパターンってのもあって、
そのイメージが強い方も多いかもしれません。
というわけでGOF:ステートパターンと比較した差を書き出します。
階層構造が作れる
これは比較的大きな差かもしれません。
GOF:ステートパターンは階層構造は想定していません。
固定的にステートの中にサブステートという別の状態機を埋め込んで実現することはできますが、
これは状態機の多重構造であり、ステートマシン図の階層構造とは異なる振る舞いとなります。
(実装側で頑張って同期性/再同期性を保証して同一の振る舞いにすることは当然可能ですが。)
イベント(またはメッセージ)の受付が可変
各状態にすべてのイベント受付の定義は不要で、対象の状態に必要なイベントに対しての振る舞い規定のみ実装するだけでOKです。
GOF側は基本、全イベントを受け付けるインターフェースが基底にいるため、
不要なイベントに対しても、「何もしない」という受付ハンドラを規定する必要があります。
ただし、Commandパターンみたいなのを一枚挟むとか、
空の振る舞いセットを抱え込んだAbstructをインターフェースにかぶせる事で、同様のことができたりはしますが、そこは実装側の工夫の話ですね。
遷移規定は定義のみ
遷移だけであれば、処理用のコードは不要です。
これが最大の差となります。
当然、実装側の工夫で、GOFでも似たようなことはできますが、
仕組みとして仕込む必要があるので、もはやステートパターンの範疇から外れた話になります。
ヒストリ/ディープヒストリ機能あり
そもそも階層構造がないと必要無いのですが、boost:statechartでは実装されています。
私はあまり使わないですが。
defer機能あり
UML:ステートマシン図ならではの機能です。
これもシステムを自滅させる直前など、タイミングの入れ替えが必要みたいな状況に陥らない限り使いません。
ざっくり言ってしまうと
GOF:ステートパターン → 状態遷移「表」ベース
boost:statechart → UML::ステートマシン図ベース
という差があります。
※状態遷移表にも拡張階層型というものがありますが、ステートマシン図の階層とは別物です。どちらかというと見やすくするための表現のグルーピングで、本質的には非階層です。
boost:statechartの特殊な利用方法
boost:statechartの実際の使い方とかはググってください。
そこそこ情報は出てきます。
本当に利用したい人はboost.orgでざっと見して土地勘を養っておいた方が良いです。
さて、なぜこのboost::statechartをscilab/xcosと連携せねばならぬのか?
xcosは前回説明したようにブロック線図を簡単に記述することができます。
逆にいうとそれだけなのです。
つまり、分岐や状態遷移に相当する機能にはめっぽう弱いということになります。
制御対象の振る舞いとソフトウェアの振る舞いを連携させるための大きな壁の一つです。
単純な分岐であれば、if-elseプロックというものがあるので、
それを使用して分岐表現はできますが、
分岐のたびにそのブロックを設置してたら、何をしている処理か追うに追えなくなります。
そういう分岐物は、いっそのことCBlockにおしこめてしまった方が良いです。
そして、分岐はCBlockお任せで良いのですが、
問題は状態遷移。
C言語も状態遷移が得意とは言えません。というかむしろ弱いです。
そもそも状態遷移が得意な言語ってのもあまり無いのかもしれません。
というわけで、状態遷移が得意というかそれしかできないboost:statechartに任せてしまおうってのが今回の話の中心となります。
Scilab CBlock
そしてまたまた出てくる新たな問題。
CBlockって・・・
その名の通りC言語を記載するブロックなんですよね。
そして、boost::statechartって・・・
C++なんですよね。
いや、でも使ってるコンパイラ自体はC/C++両方行けるんだから、大丈夫かも?
結果を言うと「ダメ」です。
Cblock内に記載されたコードをscilab内でライブラリ(dllまたはso)としてビルドしているのですが、
そのためのMakefileの生成はscilab内で完全に閉じてしまっています。
記述コードの拡張子が強制的に.cにされるため、C++コードを書いても文法エラーになってしまいます。
また、ライブラリのリンク指示もMakefile内に記載されるため、利用者側からコントロールできない領域が多すぎるのです。
そもそもはC言語で数式モデルのシミュレーションをしていた時代の資産流用を目的としたブロックであるため、C++の利用は全く想定されていません。Fortranとかはいけますが。これも資産流用目的ですね。
ならばと、CBlockとは別にライブラリを作って動的リンクさせてはどうかと思い立つ。
Makefileは弄れないので、所謂、明示的リンクでいけるかもしれない。
WindowsいうとLoadlibrary、GetProcAddress
Unix系OSであればdlopen、dlsym
というAPIを使用するパターン。
APIコールができるのか不安ではあったが、これはWin/Linux共に難なくクリア。
なんとなく手段が見えてきたところで青図を描くと↓
きっとなんとかなるはず。
中編に続く。
コメント