micro:bitでステートマシンを使ったコーディングを体験しよう

# micro:bitでステートマシン

micro:bitでステートマシンを使ったコーディングを体験することができます。
**※ UMLブロックでステートマシン図のテキスト構文を出力しています**

https://makecode.microbit.org/_F5vamaejWYxa

## 設計

ステートマシン図を使って、カウントアップする機能を次のように設計しました。

**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] -r-> a
state b : entry/\n - "b"表示\n - カウント=0\ndo/\n - カウント表示\n - カウントアップ(0-9)\nexit/\n - "B"表示\n - カウント表示
b -r-> [*] : e
state a : entry/\n - "a"表示\n - カウント=0\ndo/\n - カウント表示\n - カウントアップ\nexit/\n - "A"表示\n - カウント表示
a -r-> b
}
@enduml
```

**ステートマシンの説明**
1. ステートマシンM0をデフォルトステート a から開始します
1. ステート a に遷移すると、entryアクションが実行されます
1. 続けて、doアクティビティが実行されます
1. ステート a からステート b へは、トリガーのないトランジションであるため、doアクテビティの実行後、遷移します
1. 遷移する前に、ステート a のexitアクションが実行されます
1. その後、ステート b に遷移すると、entryアクションが実行されます
1. 続けて、doアクティビティが実行され、トリガー e により遷移するまで、doアクテビティが繰り返し実行されます
1. トリガー e により、終了状態へ遷移します
1. 遷移する前に、ステート b のexitアクションが実行され、その後、終了状態へと遷移します

## コーディング

micro:bitで次のようにコーディングできます。
ボタンAでステートマシンM0を開始し、ボタンBでトリガー e を発生させるようにしています。

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/1d2f599e-aec9-b276-1949-de228133ac6f.png)

**JavaScript**
```javascript
let カウント = 0
// b:entry/\n - "b"表示\n - カウント=0\ndo/\n - カウント表示\n - カウントアップ(0-9)\nexit/\n - "B"表示\n - カウント表示
mstate.defineState(StateMachines.M0, "b", function (machine, state) {
    mstate.declareEntry(machine, state, function () {
        basic.showString("b")
        カウント = 0
    })
    mstate.declareDo(machine, state, 500, function () {
        basic.showNumber(カウント)
        カウント = (カウント + 1) % 10
    })
    mstate.declareExit(machine, state, function () {
        basic.showString("B")
        basic.showNumber(カウント)
    })
    mstate.declareSimpleTransition(machine, state, "e", "")
})
input.onButtonPressed(Button.A, function () {
    mstate.start(StateMachines.M0, "a")
})
input.onButtonPressed(Button.B, function () {
    mstate.fire(StateMachines.M0, "e", [])
})
// a:entry/\n - "a"表示\n - カウント=0\ndo/\n - カウント表示\n - カウントアップ\nexit/\n - "A"表示\n - カウント表示
mstate.defineState(StateMachines.M0, "a", function (machine, state) {
    mstate.declareEntry(machine, state, function () {
        basic.showString("a")
        カウント = 0
    })
    mstate.declareDo(machine, state, 500, function () {
        basic.showNumber(カウント)
        カウント += 1
    })
    mstate.declareExit(machine, state, function () {
        basic.showString("A")
        basic.showNumber(カウント)
    })
    mstate.declareSimpleTransition(machine, state, "", "b")
})
```

# インストール方法

micro:bitでステートマシンを使ったコーディングをするため、[Microsoft MakeCode for micro:bit](https://makecode.microbit.org/)プロジェクト内で、[pxt-mstate拡張機能](https://github.com/jp-rad/pxt-mstate)をインストールしてください。

https://github.com/jp-rad/pxt-mstate

２つのインストール方法がありますが、どちらの方法でも構いません。

## 【方法1】コンパイル済みのHEXファイルからのインストール

1. **Microsoft MakeCode for micro:bit** [(https://makecode.microbit.org/)](https://makecode.microbit.org/)を開きます
1. **新しいプロジェクト** をクリックし、プロジェクトに名前をつけて **作成** をクリックします
1. Mstate拡張機能の**リリースページ**[(https://github.com/jp-rad/pxt-mstate/releases)](https://github.com/jp-rad/pxt-mstate/releases)を開きます
1. **Assets** の中から **pxt-mstate.*X.Y.Z*.hex** ファイルをクリックし、ダウンロードします(*X.Y.Z*はリリースバージョン)
1. 作成したmicro:bitのプロジェクトに戻り、ブロックのグループにある **(＋)拡張機能** をクリックします
1. 拡張機能のページが開くので、 **Import File** をクリックします
1. **ファイル選択** で、ダウンロードした **pxt-mstate.*X.Y.Z*.hex** ファイルを選択し、 **つづける** をクリックします

## 【方法2】最新のソースコードからのインストール

1. **Microsoft MakeCode for micro:bit** [(https://makecode.microbit.org/)](https://makecode.microbit.org/)を開きます
1. **新しいプロジェクト** をクリックし、プロジェクトに名前をつけて **作成** をクリックします
1. ブロックのグループにある **(＋)拡張機能** をクリックします
1. 拡張機能のページが開くので、 検索欄に `https://github.com/jp-rad/pxt-mstate` と入力します（コピー＆ペースト）
1. Enterキーで検索を開始し、表示された **mstate** タイルをクリックします

# コーディングのパターン

|#  |タイトル|説明|
|:-:|--------|----|
|  1|ステートマシンの開始|startでステートマシンを開始します|
|  2|UMLの出力|UMLブロックでステートマシン図のテキスト構文を出力します(Plant UML)|
|  3|Doアクティビティ|その状態のDoアクティビティが繰り返し呼び出されます|
|  4|トリガーとトランジション|複数の状態を宣言し、状態遷移を行います|
|  5|タイムアウトとトランジション|タイムアウトによって、状態遷移を行います|

## ステートマシンの開始

`ステート"a"`を宣言し、`start`ブロックでデフォルトステート"a"からステートマシンを開始します。
ステートマシンのマシンIDは、`M0`を使います。
ステートマシンは、`開始状態`から`ステート"a"`へと遷移し、`entry`ブロックで宣言したコードが実行されます。

**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a
}
@enduml
```

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/3398d967-6cbf-ea78-d516-891e4b632090.png)

## UMLの出力

`UML`ブロックを使うと、シミュレーターのコンソールに、`Plant UML`のテキスト構文を出力でき、ステートマシン図として[変換表示](
http://www.plantuml.com/plantuml/)することができます。

http://www.plantuml.com/plantuml/

`define` ブロックのステート名称に`":"`で区切って、文字列を記述すると、ステートの説明として、UML出力されます。また、`"\n"`で改行されます。

```
"a:ハート表示\n小ハート表示"
```

**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : ハート表示\n小ハート表示
}
@enduml
```

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/4edd3218-a2d9-5ab4-362c-f94db87f3d74.png)

**シミュレーター画面**
`Show data シミュレーター`ボタンでコンソール出力の画面が表示されます。
出力された内容は、右上の `Save raw text` ボタンでダウンロードできます（テキストファイル形式）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/776e28c3-3ce3-be21-18c7-adeb6038e938.png)

## Doアクティビティ

`do`ブロックは、その状態にあると繰り返し実行されるDoアクテビティです。
次の例では、500ms毎にLEDの反転処理が行われます。

**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : 反転
}
@enduml
```

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/64fe8ba4-360b-bd79-37e7-f7158742e1de.png)

## トリガーとトランジション

複数の状態を宣言し、トリガーとトランジションを使って、状態遷移を行います。
`トリガー"e"`により、`ステート"a"`から`ステート"b"`へ、`ステート"b"`から`ステート"a"`へと遷移するように`transition`ブロックで宣言します。
また、`トリガー"stop"`により、`ステート"a"`から`ステート""`へ、`ステート"b"`から`ステート""`へと遷移するように`transition`ブロックで宣言します。尚、`ステート""`は特殊な状態であり、`終了状態`を意味します。
`トリガー"e"`はボタンBを押したときに、`トリガー"stop"`はボタンA+Bを押したときに、それぞれ、`fire`ブロックで通知しています。


**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : 反転(500ms)
a --> b : e
a --> [*] : stop
state b : 反転(200ms)
b --> a : e
b --> [*] : stop
}
@enduml
```

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/6a6c9d55-46a1-9676-f01c-c14fd558fbc9.png)

## タイムアウトとトランジション

複数の状態を宣言し、タイムアウトとトランジションを使って、状態遷移を行います。
タイムアウトにより、`ステート"a"`から`ステート"b"`へ、`ステート"b"`から`ステート"a"`へと遷移するように`transition`ブロックで宣言します。

**ステートマシン図**
```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : 反転(500ms)
a --> b : [>5s]
state b : 反転(200ms)
b --> a : [>2s]
}
@enduml
```

**ブロック**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/d09f1a40-76ad-cde5-4cb9-049814058920.png)

# おわりに

1. ステートマシンによる設計とコーディングを体験しました
1. ステートマシン拡張機能であるMstateをインストールしました
1. Mstateのコーディングパターンを示しました

コーディングだけでなく、UMLブロックでステートマシン図のテキスト構文を出力することも可能ですので、設計とコーディングとを相互に検討することができます。

尚、トランジションのガードやエフェクトについては、また、別の機会にご紹介します。

**ヒント**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/1c2d41ca-da5d-fd88-a7e9-30909de70ac6.png)


https://github.com/jp-rad/pxt-mstate