ミドルウェアの設計指針 〜 Akerunつくったエンジニアのそこそこ大規模な組み込みプログラミング 〜 

こんばんは。Photosynth の 組み込みエンジニア @ishturk  です。
[Akerun Adventカレンダー](http://qiita.com/advent-calendar/2016/akerun) 7日目 の記事です。
今日は Akerun ファームウェア　特に ミドルウェア の作り方についてお話ししたいと思います(雑)

Akerunはこんな製品です。まずは動画をご覧ください。

[![動画のリンク](http://img.youtube.com/vi/G1pF_YJX7WM/0.jpg)](http://www.youtube.com/watch?v=G1pF_YJX7WM)

スマホなどでサムターンを物理的に回転させて錠を操作。履歴が取れるというものです。

動画はスーパーかっこいいですが、組み込ミストの僕にはこんな風に見えます。

- LED（フルカラーイルミネーション）を30fpsくらいで制御しながら
- メモリの読み書きして
- 音声を再生し
- アクチュエータを制御しながら
- センサーをもりもり監視して
- BLEでセキュアに通信している
- (実はほかにもいろいろ。。。）

なかなかに心躍るリッチなデバイスです。設計を失敗すると一瞬で破綻する未来が見えます。

# ミドルウェア設計の話

用語の定義ですが以下のイメージ図をご覧ください
今日お話しするのは、この「ミドルウェア」の部分です。


![Akerun イメージ.png](https://qiita-image-store.s3.amazonaws.com/0/54261/d1b00c52-1080-1da8-df1e-cc36f41f6c31.png)

本記事ではミドルウェアを設計するのに重要な「ステートマシン」「モジュール構成」にフォーカスしてお話しします。

## 状態を管理する・状態で管理する : ステートマシンの話

そこそこの規模になると一つ以上のステートマシンで管理するのが設計の基本原則です。
たとえば、あける、しめる　という制御の場合でも

- あいてるときにしめる要求→しめる制御
- あいてるときにあける要求→無視
- しまっているときにあける要求→あける制御
- しまっているときにしめる要求→無視
- 起動中→全部無視

とこれだけのパターンがありますね。
ステートマシンのメリットは、

「〇〇状態でXX要求がきたら△△する」という制御を書くのはもちろんですが
「〇〇状態でXX要求がきても何もしない（必要に応じて後始末）」を明確にすることです。

たとえば、以下のような感じ。わかりやすいですね。
ステートマシンの約束事として、
 - 要求のことをイベント
 - イベントを受けて行う処理をアクション
 - アクションする条件をガード条件
と呼びます。
状態遷移が発生するイベントは→で遷移を示します。→につけるコメントは、 "イベント/[ガード条件]アクション"のような書き方をします。
状態遷移が発生しないけど何かしらの処理を行うイベントの場合は、状態のなかに "イベント/[ガード条件]アクション"のような書き方をします。
図に書かれていないイベント（起動中のあける要求　など）は全部無視です。

![](http://www.plantuml.com/plantuml/png/Aov9B4bLUB5r-zFkgKzshEKg1lCVDpOzRcnvtBZiSLCt5pB7AZMlfAXIGL_1nzNbSVFkb_Ct0yd7pMiVDss0Qvt-k77VuyR5SDqeV7m6S755QyKgwDeXEGVEKj1D0KeWEqN1IY4QcH_xT5tFarrjc5fGx8VeGX62QKGn7AjEa4XJ8uYjh_2qOzlJVGlHlQU0wKWmToAA9jGWHL6mPEEpgTDVDinwFdyfHj2XM8-a4000)

へぼい設計をしてしまうと、ステートマシンがスパゲッティになっていきます
そのときは、
- 状態を増やしてみる
- 階層構造にしてみる
- そもそも全体を見直す
などの対策をしましょう。

みんな大好きステートマシンのお話でした。
「状態遷移表」「状態遷移設計」というキーワードも合わせて検索してみてね。
ちなみに C言語でかくステートマシンの作り方も投稿しているので参考にどうぞ
[C言語でファンクタを使ってステートマシンを実装する](http://qiita.com/ishturk/items/671896a66a557ecc05ab)

##　モジュールをどう切るか : 抽象度と依存関係の話

もしモジュールを分けずに実装したらどうなるか？おそらく main.c が2000000行くらいになるでしょう。
では、どう界面を切ったらいいか。

僕が意識しているのは以下の部分です。

 - この人(モジュール)はどこまで知っているか
 - この人がどこまで細かく指示をだすか

各モジュールがもつ情報量を極力シンプルにするのがベストです。

たとえば、あける動作をするとき、
- LEDを赤色に2秒周期で点滅させる
- モータを90度回転
- あけました！と音をならす
- あけたログを残す
と仮定します。

たとえばこんな感じ。

![](http://www.plantuml.com/plantuml/png/XPJVRz9G5CVVxr_Hx7qF-kY36H6ZYn19UF35bmeDGrbhkavZ38dtLe49ovXJDw8PSqusHhQPOJGWo1zpB2rF-nSylTpEYwiiD4tvSdvyxZbVEhTagQQrl9WZZzNK8pMZAS0wO1V0xe5T0diRs0-msyfpOZnugAKi9McfbXPNKmjPNIEwOMcAcSqiMCerPLWfkdjTe3j0r_cz1Y_Ox_uxO6_yPrO6sW6w2XGVJjpw5s0hG0-1liGmaZ_lVyFGBTF8vJGJcmRYRUp11K4nVDyVLNQy_Szu_vimoUEiRNAIUW2s3_QkVousu7sRG8-7vLTEVmDeIyHmJkIvhtlDDj1Df7EA3RTQc8eSCvxASB6RKOb9YEUzB_uKMKvdZOjopCZyUxIP0BFiazXuDO7jWLt56JkbxywlJpEAkgJeLqWUFmc15pMpJsI-k64PvbWCf5k9-RkJIk9UV5AOlvgS5EBQec4-urg05yogvGm-0jq2saX6x_3zr_crnl4YaOWqF8n0j3mXihEKsVJizVzi6LSxbdoJiH89Z-8LGgOkfVDbEn-slP-xVDMDQQNahSf5x7MiplyjRNIOopekFRcycjXv6DJngDuUjRzngrqEPMH2eOw0VSLIG3T44NaGSWNP39W_h37le8Gp0VOQrtZMgvtrgawtGQJJYqp9CtxZbTEn0SCYkPT4e3ymkmMi8yAbGKXGahKm1oS2Td7CxnqU0HqunIuUB3oP6rLaEeLLzssTy35DRU4TR8qgfuH_5n5eJ3AdwMbyEVu1)

上位ほど抽象的な指示を出し、下位ほど限定的かつ具体的な制御をするモジュールになってます。チェックポイントとしては、
- 各モジュールが判断する情報を減らす。上から言われたことを下へ(理解出来る情報に変換して)伝えるのがミッション。
- 上下関係を明確に。下から上への指示は厳禁。 ※通知などはコールバックやイベントハンドラを使いましょ
- 依存関係は最小限に

実際にコードを書く前にモジュール関係図を書いてみると設計に失敗することがなくなりますね。

## まとめ

以上、ステートマシンとモジュール構成のお話しでした。
設計するときにどちらも設計の根幹になるものなので大事にしたいですね。
~~これらのドキュメントがないプロジェクトは地雷の可能性が高いので気を付けましょう。~~

明日も自分が担当です！