.. include:: ../extlink.txt 設計モデリング ============== 設計モデリングのアウトプットとなる設計モデルは、MDA [#f1]_ にて定義される PSM [#f2]_ と等価である。 すなわち設計モデルの作成を進めるには、まず、設計モデルと実装を紐づけるルールを定義する必要がある。 ルール策定の際には、以下を考慮しなくてはならない。 * プログラミング言語 * RTOS * マルチタスク制御 * 動的メモリ確保 なお、本ドキュメントで説明するマッピングルールは、以下の前提で定義する。 * プログラミング言語には C言語を使用 * 実装対象のプラットフォームは RTOS を搭載 * マルチタスク制御あり * 動的メモリ確保あり .. _section-label-oop-by-c: C言語によるオブジェクト指向プログラミング ----------------------------------------- C言語はオブジェクト指向をサポートする言語ではないため、クラスなど、UMLで扱われるモデルをそのまま実装することができない。 このため、まず `[Gre00]`_ に従い、以下のモデルを導入する。 シングルインスタンスモジュール (Single-instance module) システム稼働中に必要となるインスタンスは、ひとつだけであるモジュール。 一般的に C言語で実装されるファイルモジュールと等価と考えて差支えない。 マルチインスタンスモジュール (Multiple-instance module) システム稼働中、複数のインスタンスが必要となるモジュール。 Java、C#、C++ などオブジェクト指向をサポートする言語のクラスインスタンスとほぼ等価である。 型による動的インターフェイス (Per-type dynamic interface) 同じインターフェイスを持つモジュールの型が複数あり、それぞれ独自のインターフェイスを持てるようにするためのモデル。 オブジェクト指向の継承を C言語で実現するためのモデル。 以下、各モデルごとに UML での表記ルールと、C言語での実装例を示す。 .. note:: `[Gre00]`_ では、動的インターフェイス (Dynamic Interface) というモデルも定義されているが、 型による動的インターフェイスのみで十分と判断し、使用しないこととする。 .. _section-label-single-instance-module: シングルインスタンスモジュール ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 以下にシングルインスタンスモジュールの UML 表記を示す。 当該モジュールのクラスには、ステレオタイプ <> を付与する。 .. figure:: ../img/Single-instance_module.png :width: 60% :align: center シングルインスタンスモジュール - クラス図 以下にヘッダファイル (LightScheduler.h) の記述内容を示す。 プロトタイプ宣言は、基本的にクラスの public 操作をそのまま落としこめばよい。 .. literalinclude:: ../src/jgade-code/t0/include/HomeAutomation/LightScheduler.h :lines: 28-42 次にソースコード (LightScheduler.c) の記述内容を示す。まずヘッダファイルインクルード、定数、属性の定義、private メソッドのプロトタイプ宣言を行う。 シングルインスタンスモジュールの場合、属性は static 変数として定義する。 .. literalinclude:: ../src/jgade-code/t0/src/HomeAutomation/LightScheduler.c :lines: 28-56 続いて Create/Destroy メソッドを定義する。Create メソッドでは属性の初期化や、資源の獲得等を行う。 Destroy メソッドでは必要に応じて資源の解放等を行う。(コード例は属性の初期化のみ) .. code-block:: c void LightScheduler_Create(void) { int i; for (i = 0; i < MAX_EVENTS; i++) { eventList[i].id = UNUSED; } } void LightScheduler_Destroy(void) { } 以降は public/private メソッドを定義する。実装の詳細は省略する。 .. code-block:: c int LightScheduler_ScheduleTurnOn(int id, Day day, int minuteOfDay) { ... } int LightScheduler_ScheduleTurnOff(int id, Day day, int minuteOfDay) { ... } void LightScheduler_Randomize(int id, Day day, int minuteOfDay) { ... } void LightScheduler_ScheduleRemove(int id, Day day, int minuteOfDay) { ... } void LightScheduler_WakeUp(void) { ... } マルチインスタンスモジュール ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 以下にマルチインスタンスモジュールの UML 表記を示す。 当該モジュールは一般的なクラスとして扱うため、特別な表記は付与しない。 .. figure:: ../img/Multi-instance_module.png :width: 32% :align: center マルチインスタンスモジュール - クラス図 以下にヘッダファイル (CircularBuffer.h) の記述内容を示す。 まず、クラスデータ構造 (CircularBufferStruct) へのポインタ型 (CircularBuffer) を 定義する。次に、クラスの public 操作をプロトタイプ宣言に落としこむが、モデルで 定義したパラメタに加え、第1パラメタにクラスデータ構造へのポインタを定義しなければ ならないことに注意する。 .. literalinclude:: ../src/jgade-code/include/util/CircularBuffer.h :lines: 29-43 このように、クラスデータ構造へのポインタ型を前方宣言することにより、当該モジュールの 詳細を隠ぺいすることができる。 .. note:: 動的メモリ確保が使用できないプラットフォームでは、クラスデータ構造を公開する必要がある。 この場合、クラスデータ構造は Private ヘッダ (CircularBufferPrivate.h 等) に定義し、 当該モジュールの利用者が意識する必要がないものであることを明示する。 次にソースコード (CircularBuffer.c) の記述内容を示す。 まずヘッダファイルインクルード、定数、属性の定義、private メソッドのプロトタイプ宣言を行う。 マルチインスタンスモジュールの場合、属性はクラスデータ構造のフィールドとして定義する。 .. literalinclude:: ../src/jgade-code/src/util/CircularBuffer.c :lines: 27-41 続いて Create/Destroy メソッドを定義する。マルチインスタンスモジュールの場合、属性を保持する 領域は、malloc()等、標準ライブラリや RTOS が提供する動的メモリ確保 API を用いて獲得する。 Destroy メソッドでは、属性保持用の領域を解放する。 .. literalinclude:: ../src/jgade-code/src/util/CircularBuffer.c :lines: 43-56 以降は public/private メソッドを定義する。属性は、第1パラメタ経由で変更、参照する。 .. literalinclude:: ../src/jgade-code/src/util/CircularBuffer.c :tab-width: 4 :lines: 58- .. note:: 各メソッドの第1パラメタは、C++ の this ポインタと同じ役割を果たす。 型による動的インターフェイス ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 以下に型による動的インターフェイスの UML 表記を示す。 他モジュールへのインターフェイスとなるモジュール (スーパークラス) は抽象クラスで定義し、 固有の処理・データは抽象クラスのサブクラスにまとめる。 .. figure:: ../img/Per-type_dynamic_interface.png :width: 50% :align: center 型による動的インターフェイス - クラス図 スーパークラスの実装 """""""""""""""""""" 以下にヘッダファイル (LightDriver.h) の記述内容を示す。 マルチインスタンスモジュール同様、クラスデータ構造 (LightDriverStruct) へのポインタ型 (LightDriver) を定義し、 public 操作をプロトタイプ宣言に落としこむ。 .. literalinclude:: ../src/jgade-code/t3/include/devices/LightDriver.h :tab-width: 4 :lines: 27- 型による動的インターフェイスを使用する場合は、複数のファイルが LightDriverStruct のレイアウトを知る必要があるため、 これを Private ヘッダに定義する。 .. literalinclude:: ../src/jgade-code/t3/include/devices/LightDriverPrivate.h :tab-width: 4 :lines: 27- 最後に、ソースコード (LightDriver.c) の記述内容を示す。 サブクラスメソッドは、vtable フィールドを通して呼び出す。 .. literalinclude:: ../src/jgade-code/t3/src/devices/LightDriver.c :tab-width: 4 :lines: 27-56 サブクラスの実装 """""""""""""""" 以下に X10LightDriver サブクラスのヘッダファイル (X10LightDriver.h) の記述内容を示す。 クラスデータ構造 (X10LightDriverStruct) へのポインタ型 (X10LightDriver) 、および公開する列挙型 (X10_HouseCode)を 定義する。プロトタイプ宣言は、Create メソッドのみ行う。 .. literalinclude:: ../src/jgade-code/t3/include/devices/X10LightDriver.h :tab-width: 4 :lines: 27- 次にソースコード (X10LightDriver.c) の記述内容を示す。 まず、ヘッダファイルをインクルードした後、クラスデータ (X10LightDriverStruct) 構造を定義する。 X10LightDriverStruct の先頭に、スーパークラスのフィールド (base) を定義している点に注意する。 .. literalinclude:: ../src/jgade-code/t3/src/devices/X10LightDriver.c :tab-width: 4 :lines: 27-38 続いて vtable に登録する関数を、static 関数として定義する。 .. literalinclude:: ../src/jgade-code/t3/src/devices/X10LightDriver.c :tab-width: 4 :lines: 55-74 vtable に登録する関数を interface としてまとめ、 最後に Create メソッドを定義する。 .. literalinclude:: ../src/jgade-code/t3/src/devices/X10LightDriver.c :tab-width: 4 :lines: 76-92 UML-C マッピングルール ---------------------- 本節では、UMLとC言語概念のマッピングルールを定義する。 .. _section-label-uml-to-c: UML to C ^^^^^^^^ クラス図 """""""" クラス :ref:`section-label-oop-by-c` で導入したモデルにもとづいて定義する。 構造体など、C言語が提供する型の表現は、 :ref:`section-label-c-to-uml` を参照のこと。 属性 (attribute) クラスが、基本型 (char、short、int など) のインスタンス、もしくはポインタ [#f3]_ を保持する場合は、属性で表現する。 .. figure:: ../img/uml-attribute.png :width: 32% :align: center .. note:: 基本型には、int32_t など、基本型を typedef した型も含む。 関連 (association) クラスが、ポインタを通してクラスインスタンスを参照する場合は、関連で表現する。 集約 (aggregation) 集約は、厳密には関連以上の意味を持たない `[Mar00]`_ ため、使用しないこと。 使用する場合は、ノート等で意図を明記すること。 コンポジション (composition) クラス間の関係が以下のいずれかに当てはまる場合は、コンポジションで表現する。 #. あるクラスが、もう一方のクラスのインスタンスを保持する場合 (:ref:`section-label-single-instance-module` の例を参照) #. あるクラスが、ポインタを通してもう一方のクラスインスタンスを参照するが、 もう一方のクラスインスタンスは共有不可 (所有者数は 1 以下) である場合 .. _section-label-c-to-uml: C to UML ^^^^^^^^ 型 "" 構造体 * ステレオタイプ <> を付加したクラスとする * メンバ変数は public 属性で定義する * 以下は非表示とする * 可視性 * 操作区画 .. figure:: ../img/c-struct.png :width: 18% :align: center 列挙型 * ステレオタイプ <> を付加したクラスとする * 列挙定数は int 型の public 属性として定義する * 以下は非表示とする * 可視性 * 属性の型 * 操作区画 .. figure:: ../img/c-enum.png :width: 18% :align: center 共用体 * ステレオタイプ <> を付加したクラスとする * メンバ変数は public 属性で定義する * 以下は非表示とする * 可視性 * 操作区画 .. figure:: ../img/c-union.png :width: 18% :align: center プリプロセッサ """""""""""""" マクロ定数 * 定義されるクラスの属性として定義する * 属性にはステレオタイプ <> を付加する .. figure:: ../img/c-macro.png :width: 28% :align: center マクロ関数 特殊な場合を除いて、マクロ関数はインライン関数で代替可能であるため、本ドキュメントはインライン関数の使用を推奨する。 従って、マクロ関数の表記法は定義しない。 組込みソフトウェア特有概念の表現ルール -------------------------------------- 本節では、 `[Ogs00]`_ を参考に、割り込みなど組込みソフトウェア特有の概念を UML で表現するルールを定義する。 構造面 ^^^^^^ 割り込み * 割り込みコントローラをアクターで表現する * 割り込みハンドラとなる操作に、ステレオタイプ <> を付加する .. figure:: ../img/embedded-interrupt.png :width: 45% :align: center タスク * アクティブクラスとする * タスクのエントリポイントとなる操作には、ステレオタイプ <> を付加する * タスクを管理する RTOS は、アクターで表現する .. figure:: ../img/embedded-os-task.png :width: 35% :align: center 振る舞い面 ^^^^^^^^^^ 排他 * シーケンス図上では複合フラグメント critical を使用し、排他に使用する手段 (割り込み禁止、セマフォ等) を名前で表現する .. figure:: ../img/interrupt-handering.png :width: 45% :align: center マルチタスクによる並行処理 * シーケンス図上では複合フラグメント par を使用する * RTOS からのタスク起動メッセージは非同期とする [#f4]_ .. figure:: ../img/multi-task.png :width: 60% :align: center .. rubric:: 脚注 .. [#f1] Model-Driven Architecture (モデル駆動型アーキテクチャ)、Object Management Group (OMG) が2001年に 公式に発表したソフトウェア設計手法のこと。 .. [#f2] platform-specific model (プラットフォーム特化モデル) .. [#f3] astah* では、ポインタをタイプ修飾子で表現することができる。その他のツールでは、ポインタ型のクラスを別途定義すること。 .. [#f4] タスクの実行完了を RTOS が待つわけではないため。