msc-generatorでシーケンス図を作成する
複数機器間の通信の手順を可視化したり、仕様として規定する際、「シーケンス図」というものを作成することがあります。
英語では、Message Sequence Chartというそうで、graphvizのようにテキストファイルを入力として、きれいな図を生成してくれるソフトが何種類かあるようです。
今回、SpaceWire関連の仕様書を記述するタスクのために、MSC-Generatorというソフトを利用しました。ドキュメントもしっかり用意されていたり、日本語も扱えたりしてひじょうに心強いです。
Macでの使用に当たってはソースコードからビルドする必要があり、手順が少し難しいところがあったのでまとめておきます。
[tmkm-amazon]9784774143958[/tmkm-amazon]
サンプル
たとえば以下のようなコードからは次のような図が生成されます。
hscale=auto, numbering=yes; numbering.format = "roman"; a->b: Lowercase roman; numbering.format = "ABC)"; a->b: Uppercase letters; numbering.format = "123"; a->b: Arabic numbers; |
|
インストール手順
- SorceForgeのサイトからソースコードをダウンロードして展開。msc-generator/というフォルダが生成される。
今回は3.5.18というバージョンを利用しました。 - Homebrewで最新のbisonとflexをインストール。
インストール後、
brew link bison --force
としてバージョン2.7のbisonを/usr/local/binにリンクする。
which bison
として、/usr/local/bin/bisonが見えている事を確認。
(システムのデフォルトである/usr/bin/bisonが見えてしまっている場合、.zshrc等にexport PATH=/usr/local/bin:$PATHを追加して、/usr/binよりも先に/usr/local/binが見えるようにする) - Homebrewでgcc4.7をインストール。30分くらいかかるので、コーヒーブレイク。
- cd msc-generator
- 以下を実行して、ylwrapというラッパースクリプトをmsc-generator/直下にコピー(src/内ではないので注意)。
cp /usr/local/share/automake-1.13/ylwrap . - 以下の一連のコマンドでconfigureスクリプトを生成する。
aclocal
automake --add-missing
autoconf - ./configure CXX=g++-4.7 CC=gcc-4.7 CXXFLAGS=-I/usr/local/include/cairo
として、gccのバージョンを指定しつつMakefileを生成。
(ユーザのローカルな場所にインストールしたい場合は--prefix=$HOME/work/install等を添加) - nano Makefile としてテキストエディタで開き、SUBDIRの行でdocを削除(ドキュメントのコンパイルはしない)。
- make -j8
- 途中、src/commandline.cppの591行目でstd::max()の引数がおかしいというエラーが出るので、
std::vectorscale(std::max((unsigned int)1, (unsigned int)oScale.size()), XY(1., 1.));
のようにキャストを追加して修正する。</li>- 再度make
- 今度はsrc/language.hの229行目で、yyparseの重複定義というエラーが出るので、第一引数の方をMscに変更して、
int yyparse (Msc &RESULT, void *yyscanner);
として保存。- 再度makeすると通るはず。
- make install
- インストールディレクトリ($HOME/work/install/bin等)にパスを通せば使えるようになる。
</ol>さらに詳しい使い方は、ドキュメントを参照してください(ひじょうに詳しく説明されています)。
msc-gen (ソースファイル名)
とすると実行され、Successと表示されるとpngが生成されます。
現実的なサンプルある通信トランザクションを可視化したサンプルを下に示します。ソースコードは「続きを読む」に記載しておきます。
```shell hscale=2; text.format="f(Hiragino Kaku Gothic Pro)"; Master: マスタ機器 [shadow.offset=4, line.radius=5, line.type=solid] { App: 上位nアプリケーション [line.radius=5]; I: RMAPnイニシエータ [pos=-0.25,line.radius=5]; }; Middle0: middle0 [show=no, pos=-0.25]; Middle: middle [show=no, pos=-0.9]; User: ユーザ機器 [shadow.offset=4, line.radius=5, line.type=solid] { T: RMAPnターゲット [line.radius=5]; Uapp: ユーザnアプリケーション [pos=-0.25,line.radius=5]; }; []; App--App: "コマンド配信n処理開始"; App->I: "RMAP Write要求"; I=>T: "RMAP Write CommandnTransaction ID = 0xABCDnAddress = 0xFF80_3800"; mark top; {T<->Uapp: "内部バスアクセスnコマンド実行";}; comment: 書き込まれたコマンドを実行(上位の アプリケーションへ通知)する。再送 発生時の重複実行を避けるため、RMAP WriteコマンドヘッダーのTransaction IDを記憶する。; parallel Middle<<T: "RMAP Write ReplynStatus = Success"; Middle0--Middle0: "喪失"; []; []; []; []; vertical top-> at I- [makeroom=yes]: "タイムアウト時間"; []; I--I: "タイムアウト"; App<-I: "タイムアウト通知"; App--App: "再送の判断"; App->I: "コマンド配信要求"; I=>T: "RMAP Write CommandnTransaction ID = 0xABCDnAddress = 0xFF80_3800"; {T<->Uapp: "内部バスアクセスnコマンド再度実行しない";}; comment: ここでは、RMAP Writeコマンドが前回 実行したコマンドと同じTIDを持っている ので、コマンドは再実行しない。; I<=T: "RMAP Write ReplynStatus = Success"; App<-I: "RMAP Writen完了通知"; App--App: "コマンド送信n処理完了"; ``` 参考: Javascriptによるデータ可視化(d3.js) [tmkm-amazon]9784797368864[/tmkm-amazon] [tmkm-amazon]9784774163017[/tmkm-amazon]