micro:bit用ステートマシンの見直し ver.0.8.3

# 従来 ver. 0.7.0

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

## 課題

ver. 0.7.0 では、次の課題があるので、改善したい。

1. **machine/state**
状態を管理する変数が２つある（machine, state）
1. **説明の記述方法**
状態名称（遷移を含む）と説明とが１つの引数で記述されている
1. **複数の遷移先(説明記述)**
遷移先が複数ある場合を想定し、その遷移先を配列で指定できるが、説明の記述も複数必要である
1. **イベント依存**
micro:bitのイベントに依存している
1. **ポーリング間隔**
時間待ちループで、タイムアウトをポーリングしている

<details><summary><b>ソースコード（ブロック定義）</b></summary>

```typescript: mstate.ts (ver.0.7.0)
num StateMachines {
    M0 = 0,
    M1,
    M2,
    M3,
    M4,
    M5
}

/**
 * mstate blocks
 * Defining blocks: https://makecode.com/defining-blocks
 * Playground: https://makecode.com/playground
 * icon: a Unicode identifier for an icon from the Font Awesome icon set.
 *       http://fontawesome.io/icons
 */
//% weight=100 color="#4C97FF" icon="\uf362"
//% groups="['Command', 'Declare', 'Transit']"
namespace mstate {

    /**
     * convert state/trigger name (string) to id (number): new id if undefined
     * @param name state name (string) or trigger name (string)
     * @returns state id or trigger id
     */
    //% block="id of $name"
    //% name.defl="a"
    //% weight=210
    //% advanced=true
    export function convId(name: string): number {
    }

    /**
     * Internal event settings
     * @param aStateMachine StateMachines
     * @param eventId Event ID (default: 32868 = 32768 + 100)
     * @param ms Event loop interval (default: 100ms)
     */
    //% block="settings [$aStateMachine] event ID: $eventId every: $ms ms"
    //% aStateMachine.defl=StateMachines.M0
    //% eventId.defl=32768
    //% ms.shadow="timePicker"
    //% ms.defl=100
    //% weight=190
    //% advanced=true
    export function settingsMachineEvent(aStateMachine: StateMachines, eventId: number,
        ms: number
    ) {
    }

    /**
     * define state
     * @param aStateMachine StateMachines
     * @param aStateName state name
     * @param body code to run, (machine: machine ID, state: state ID)
     */
    //% block="define [$machine,$state] to $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% draggableParameters="reporter"
    //% weight=180
    //% group="Declare"
    export function defineState(aStateMachine: StateMachines, aStateName: string,
        body: (machine: number, state: number) => void
    ) {
    }

    /**
     * declare ENTRY action.
     * @param aMachine machine ID
     * @param aState state ID
     * @param body code to run
     */
    //% block="on entry [$aMachine,$aState]"
    //% aMachine.defl=0
    //% aState.defl=0
    //% handlerStatement
    //% weight=170
    //% group="Declare"
    export function declareEntry(aMachine: number, aState: number,
        body: () => void
    ) {
    }

    /**
     * declare DO activity.
     * @param aMachine machine ID
     * @param aState state ID
     * @param aEvery interval time (milliseconds)
     * @param body code to run
     */
    //% block="on do [$aMachine,$aState] every $aEvery ms"
    //% aMachine.defl=0
    //% aState.defl=0
    //% aEvery.shadow="timePicker"
    //% handlerStatement
    //% weight=160
    //% group="Declare"
    export function declareDo(aMachine: number, aState: number, aEvery: number,
        body: () => void
    ) {
    }

    /**
     * declare EXIT action.
     * @param aMachine machine ID
     * @param aState state ID
     * @param body code to run
     */
    //% block="on exit [$aMachine,$aState]"
    //% aMachine.defl=0
    //% aState.defl=0
    //% handlerStatement
    //% weight=150
    //% group="Declare"
    export function declareExit(aMachine: number, aState: number,
        body: () => void
    ) {
    }

    /**
     * declare simple transition.
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTriggerName trigger name
     * @param aToName next state nam
     */
    //% block="trasition [$aMachine,$aState] when $aTriggerName to $aToName"
    //% aMachine.defl=0
    //% aState.defl=0
    //% aTriggerName.defl="e"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=140
    //% group="Transition"
    export function declareSimpleTransition(aMachine: number, aState: number,
        aTriggerName: string, aToName: string
    ) {
    }

    /**
     * declare timeouted transition.
     * @param aMachine machine ID
     * @param aState state ID
     * @param aMs timeout (ms)
     * @param aToName next state name
     */
    //% block="trasition [$aMachine,$aState] timeouted $aMs to $aToName"
    //% aMachine.defl=0
    //% aState.defl=0
    //% aMs.shadow="timePicker"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=130
    //% group="Transition"
    export function declareTimeoutedTransition(aMachine: number, aState: number,
        aMs: number, aToName: string
    ) {
    }

    /**
     * declare custom transition.
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTriggerName trigger name
     * @param aTransList array of next state name 
     * @param body code to run
     */
    //% block="trasition [$aMachine,$aState] when $aTriggerName $args to $aTransList"
    //% aMachine.defl=0
    //% aState.defl=0
    //% aTriggerName.defl="e"
    //% draggableParameters="reporter"
    //% handlerStatement
    //% weight=120
    //% group="Transition"
    export function declareCustomTransition(aMachine: number, aState: number,
        aTriggerName: string, aTransList: string[],
        body: () => void
    ) {
    }

    /**
     * is timeouted.
     * @param aMachine machine ID
     * @param aMs timeout (milliseconds)
     */
    //% block="timeouted [$aMachine] $aMs"
    //% aMachine.defl=0
    //% aMs.shadow="timePicker"
    //% weight=110
    //% group="Transition"
    export function isTimeouted(aMachine: number, aMs: number): boolean {
    }

    /**
     * trigger args.
     * @param aMachine machine ID
     * @returns trigger args
     */
    //% block="trigger args [$aMachine]"
    //% aMachine.defl=0
    //% weight=105
    //% group="Transition"
    //% advanced=true
    export function getTriggerArgs(aMachine: number): number[] {
    }

    /**
     * transit to.
     * @param aMachine machine ID
     * @param index states index]
     */
    //% block="transit [$aMachine] at $index"
    //% aMachine.defl=0
    //% index.defl=0
    //% weight=100
    //% group="Transition"
    export function transitTo(aMachine: number, index: number) {
    }

    /**
     * send trigger
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     */
    //% block="send $aStateMachine $aTriggerName"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=95
    //% group="Command"
    export function send(aStateMachine: StateMachines, aTriggerName: string) {
    }

    /**
     * send trigger with args
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     * @param aTriggerArgs args
     */
    //% block="send $aStateMachine $aTriggerName $aTriggerArgs"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=90
    //% group="Command"
    //% advanced=true
    export function sendWith(aStateMachine: StateMachines, aTriggerName: string,
        aTriggerArgs: number[]
    ) {
    }

    /**
     * start state machine
     * @param aStateMachine StateMachines
     * @param aStateName default state name
     */
    //% block="start $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=80
    //% group="Command"
    export function start(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * export UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aStateMachine
     * @param aStateName default state
     */
    //% block="UML $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=70
    //% group="Command"
    //% shim=mstate::simu_export_uml
    //% advanced=true
    export function exportUml(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * UML state
     * @param aMachine  machine ID
     * @param aStateName state name
     */
    //% block
    //% shim=mstate::simu_state_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuStateUml(aMachine: number, aStateName: string) {
    }

    /**
     * UML transition
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTransList array of next state name
     */
    //% block
    //% shim=mstate::simu_transition_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuTransitionUml(aMachine: number, aState: number,
        aTransList: string[]
    ) {
    }

    /**
     * convert id (number) to state/trigger name (string)
     * @param id state id or trigger id
     * @returns state name (string) or trigger name (string): "[<id>]" if undefined
     */
    //% block
    //% shim=mstate::simu_conv_name
    //% blockHidden=true
    //% advanced=true
    export function _simuConvName(id: number): string {
    }

}
```
</details>

# 対策 ver. 0.8.0

:::note info
**ブロック定義を試す**
次のツールで、MakeCodeのブロック定義を試すことができます。
[MakeCode Bolocks Playground - https://makecode.com/playground](https://makecode.com/playground)
:::

## 課題1.　machine/state

従来は、状態を管理する変数や引数が2つあった( `machine` と `state` )。
それを、１つにした( `mstateId` )。

<table><tr><th>従来版</th><th>対策版</th></tr>
<tr><td>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/1311f291-a099-bb36-ede2-b4fd6f1cb271.png)

</td><td>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/a346ec34-e100-85f6-5155-0f1afac98360.png)

</td></tr></table>


<details><summary><b>ソースコード（ブロック定義）</b></summary>

```typescript: mstate.ts (machine/state)
enum StateMachines {
    M0 = 0,
    M1,
    M2,
    M3,
    M4,
    M5
}

/**
 * mstate blocks
 * Defining blocks: https://makecode.com/defining-blocks
 * Playground: https://makecode.com/playground
 * icon: a Unicode identifier for an icon from the Font Awesome icon set.
 *       http://fontawesome.io/icons
 */
//% weight=100 color="#4C97FF" icon="\uf362"
//% groups="['Command', 'Declare', 'Transit']"
namespace mstate {

    /**
     * convert state/trigger name (string) to id (number): new id if undefined
     * @param aStateMachine StateMachines
     * @param name state name (string) or trigger name (string)
     * @returns machine-state id or machine-trigger id
     */
    //% block="id of $aStateMachine $name"
    //% aStateMachine.defl=StateMachines.M0
    //% name.defl="a"
    //% weight=210
    //% advanced=true
    export function convId(aStateMachine: StateMachines, name: string): number {
    }

    /**
     * Internal event settings
     * @param aStateMachine StateMachines
     * @param eventId Event ID (default: 32868 = 32768 + 100)
     * @param ms Event loop interval (default: 100ms)
     */
    //% block="settings $aStateMachine event ID: $eventId every: $ms ms"
    //% aStateMachine.defl=StateMachines.M0
    //% eventId.defl=32768
    //% ms.shadow="timePicker"
    //% ms.defl=100
    //% weight=190
    //% advanced=true
    export function settingsMachineEvent(aStateMachine: StateMachines, eventId: number,
        ms: number
    ) {
    }

    /**
     * define state
     * @param aStateMachine StateMachines
     * @param aStateName state name
     * @param body code to run, (mstateId: machine-state ID)
     */
    //% block="define $aStateMachine $mstateId to $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% draggableParameters="reporter"
    //% weight=180
    //% group="Declare"
    export function defineState(aStateMachine: StateMachines, aStateName: string,
        body: (mstateId: number) => void
    ) {
    }

    /**
     * declare ENTRY action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on entry"
    //% $aMstateId.defl=0
    //% handlerStatement
    //% weight=170
    //% group="Declare"
    export function declareEntry(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare DO activity.
     * @param machine-state ID
     * @param aEvery interval time (milliseconds)
     * @param body code to run
     */
    //% block="mstate $aMstateId on do every $aEvery ms"
    //% aMstateId.defl=0
    //% aEvery.shadow="timePicker"
    //% handlerStatement
    //% weight=160
    //% group="Declare"
    export function declareDo(aMstateId: number, aEvery: number,
        body: () => void
    ) {
    }

    /**
     * declare EXIT action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on exit"
    //% aMstateId.defl=0
    //% handlerStatement
    //% weight=150
    //% group="Declare"
    export function declareExit(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare simple transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aToName next state nam
     */
    //% block="mstate $aMstateId trasition when $aTriggerName to $aToName"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=140
    //% group="Transition"
    export function declareSimpleTransition(aMstateId: number,
        aTriggerName: string, aToName: string
    ) {
    }

    /**
     * declare timeouted transition.
     * @param machine-state ID
     * @param aMs timeout (ms)
     * @param aToName next state name
     */
    //% block="mstate $aMstateId trasition timeouted $aMs to $aToName"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=130
    //% group="Transition"
    export function declareTimeoutedTransition(aMstateId: number,
        aMs: number, aToName: string
    ) {
    }

    /**
     * declare custom transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aTransList array of next state name 
     * @param body code to run
     */
    //% block="mstate $aMstateId trasition when $aTriggerName $args to $aTransList"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% draggableParameters="reporter"
    //% handlerStatement
    //% weight=120
    //% group="Transition"
    export function declareCustomTransition(aMstateId: number,
        aTriggerName: string, aTransList: string[],
        body: () => void
    ) {
    }

    /**
     * is timeouted.
     * @param machine-state ID, as machine ID
     * @param aMs timeout (milliseconds)
     */
    //% block="mstate $aMstateId timeouted $aMs"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% weight=110
    //% group="Transition"
    export function isTimeouted(aMstateId: number, aMs: number): boolean {
    }

    /**
     * trigger args.
     * @param machine-state ID, as machine ID
     * @returns trigger args
     */
    //% block="mstate $aMstateId trigger args"
    //% aMstateId.defl=0
    //% weight=105
    //% group="Transition"
    //% advanced=true
    export function getTriggerArgs(aMstateId: number): number[] {
    }

    /**
     * transit to.
     * @param machine-state ID, as machine ID
     * @param index states index]
     */
    //% block="mstate $aMstateId transit at $index"
    //% aMstateId.defl=0
    //% index.defl=0
    //% weight=100
    //% group="Transition"
    export function transitTo(aMstateId: number, index: number) {
    }

    /**
     * send trigger
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     */
    //% block="send $aStateMachine $aTriggerName"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=95
    //% group="Command"
    export function send(aStateMachine: StateMachines, aTriggerName: string) {
    }

    /**
     * send trigger with args
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     * @param aTriggerArgs args
     */
    //% block="send $aStateMachine $aTriggerName $aTriggerArgs"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=90
    //% group="Command"
    //% advanced=true
    export function sendWith(aStateMachine: StateMachines, aTriggerName: string,
        aTriggerArgs: number[]
    ) {
    }

    /**
     * start state machine
     * @param aStateMachine StateMachines
     * @param aStateName default state name
     */
    //% block="start $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=80
    //% group="Command"
    export function start(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * export UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aStateMachine
     * @param aStateName default state
     */
    //% block="UML $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=70
    //% group="Command"
    //% shim=mstate::simu_export_uml
    //% advanced=true
    export function exportUml(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * UML state
     * @param aMachine  machine ID
     * @param aStateName state name
     */
    //% block
    //% shim=mstate::simu_state_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuStateUml(aMachine: number, aStateName: string) {
    }

    /**
     * UML transition
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTransList array of next state name
     */
    //% block
    //% shim=mstate::simu_transition_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuTransitionUml(aMachine: number, aState: number,
        aTransList: string[]
    ) {
    }

    /**
     * convert id (number) to state/trigger name (string)
     * @param id machine-state id or machine-trigger id
     * @returns state name (string) or trigger name (string): "[<id>]" if undefined
     */
    //% block
    //% shim=mstate::simu_conv_name
    //% blockHidden=true
    //% advanced=true
    export function _simuConvName(id: number): string {
    }

}

```
</details>

## 課題2.　説明の記述方法

従来は、状態名称に区切り文字で、区切ってUML状態遷移図の説明を記述していた。
その為、状態名称の可読性が失われたり、micro:bit本体での実行時に不具合が生じたりしていた。
それを、新たに追加したブロックで説明を記述するようにした。

説明記述ブロックに続けて、宣言ブロックを配置すると、その宣言ブロックの状態または遷移の説明となる。

:::note info
**対応する宣言ブロック**

説明記述ブロックは、次の宣言ブロックに対応します。

- `on entry`
- `on do`
- `on exit`
- `transition`

:::

<table><tr><th>UML</th><th>従来版</th><th>対策版</th></tr>
<tr><td>

```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : do something
a --> [*] : [guard] / action
}
@enduml
```

</td><td>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/8361e8ed-d8f1-8200-be75-69f425755e21.png)

</td><td>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/fa4b8981-ed87-871c-267e-199540c89c4c.png)

</td></tr></table>


<details><summary><b>ソースコード（ブロック定義）</b></summary>

```typescript: mstate.ts (説明の記述方法)
enum StateMachines {
    M0 = 0,
    M1,
    M2,
    M3,
    M4,
    M5
}

/**
 * mstate blocks
 * Defining blocks: https://makecode.com/defining-blocks
 * Playground: https://makecode.com/playground
 * icon: a Unicode identifier for an icon from the Font Awesome icon set.
 *       http://fontawesome.io/icons
 */
//% weight=100 color="#4C97FF" icon="\uf362"
//% groups="['Command', 'Declare', 'Transit']"
namespace mstate {

    /**
     * convert state/trigger name (string) to id (number): new id if undefined
     * @param aStateMachine StateMachines
     * @param name state name (string) or trigger name (string)
     * @returns machine-state id or machine-trigger id
     */
    //% block="id of $aStateMachine $name"
    //% aStateMachine.defl=StateMachines.M0
    //% name.defl="a"
    //% weight=210
    //% advanced=true
    export function convId(aStateMachine: StateMachines, name: string): number {
    }

    /**
     * Internal event settings
     * @param aStateMachine StateMachines
     * @param eventId Event ID (default: 32868 = 32768 + 100)
     * @param ms Event loop interval (default: 100ms)
     */
    //% block="settings $aStateMachine event ID: $eventId every: $ms ms"
    //% aStateMachine.defl=StateMachines.M0
    //% eventId.defl=32768
    //% ms.shadow="timePicker"
    //% ms.defl=100
    //% weight=190
    //% advanced=true
    export function settingsMachineEvent(aStateMachine: StateMachines, eventId: number,
        ms: number
    ) {
    }

    /**
     * define state
     * @param aStateMachine StateMachines
     * @param aStateName state name
     * @param body code to run, (mstateId: machine-state ID)
     */
    //% block="define $aStateMachine $mstateId to $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% draggableParameters="reporter"
    //% weight=180
    //% group="Declare"
    export function defineState(aStateMachine: StateMachines, aStateName: string,
        body: (mstateId: number) => void
    ) {
    }

    /**
     * declare ENTRY action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on entry"
    //% $aMstateId.defl=0
    //% handlerStatement
    //% weight=170
    //% group="Declare"
    export function declareEntry(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare DO activity.
     * @param machine-state ID
     * @param aEvery interval time (milliseconds)
     * @param body code to run
     */
    //% block="mstate $aMstateId on do every $aEvery ms"
    //% aMstateId.defl=0
    //% aEvery.shadow="timePicker"
    //% handlerStatement
    //% weight=160
    //% group="Declare"
    export function declareDo(aMstateId: number, aEvery: number,
        body: () => void
    ) {
    }

    /**
     * declare EXIT action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on exit"
    //% aMstateId.defl=0
    //% handlerStatement
    //% weight=150
    //% group="Declare"
    export function declareExit(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare simple transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aToName next state nam
     */
    //% block="mstate $aMstateId trasition when $aTriggerName to $aToName"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=140
    //% group="Transition"
    export function declareSimpleTransition(aMstateId: number,
        aTriggerName: string, aToName: string
    ) {
    }

    /**
     * declare timeouted transition.
     * @param machine-state ID
     * @param aMs timeout (ms)
     * @param aToName next state name
     */
    //% block="mstate $aMstateId trasition timeouted $aMs to $aToName"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=130
    //% group="Transition"
    export function declareTimeoutedTransition(aMstateId: number,
        aMs: number, aToName: string
    ) {
    }

    /**
     * declare custom transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aTransList array of next state name 
     * @param body code to run
     */
    //% block="mstate $aMstateId trasition when $aTriggerName $args to $aTransList"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% draggableParameters="reporter"
    //% handlerStatement
    //% weight=120
    //% group="Transition"
    export function declareCustomTransition(aMstateId: number,
        aTriggerName: string, aTransList: string[],
        body: () => void
    ) {
    }

    /**
     * is timeouted.
     * @param machine-state ID, as machine ID
     * @param aMs timeout (milliseconds)
     */
    //% block="mstate $aMstateId timeouted $aMs"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% weight=110
    //% group="Transition"
    export function isTimeouted(aMstateId: number, aMs: number): boolean {
    }

    /**
     * trigger args.
     * @param machine-state ID, as machine ID
     * @returns trigger args
     */
    //% block="mstate $aMstateId trigger args"
    //% aMstateId.defl=0
    //% weight=105
    //% group="Transition"
    //% advanced=true
    export function getTriggerArgs(aMstateId: number): number[] {
    }

    /**
     * transit to.
     * @param machine-state ID, as machine ID
     * @param index states index]
     */
    //% block="mstate $aMstateId transit at $index"
    //% aMstateId.defl=0
    //% index.defl=0
    //% weight=100
    //% group="Transition"
    export function transitTo(aMstateId: number, index: number) {
    }

    /**
     * send trigger
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     */
    //% block="send $aStateMachine $aTriggerName"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=95
    //% group="Command"
    export function send(aStateMachine: StateMachines, aTriggerName: string) {
    }

    /**
     * send trigger with args
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     * @param aTriggerArgs args
     */
    //% block="send $aStateMachine $aTriggerName $aTriggerArgs"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=90
    //% group="Command"
    //% advanced=true
    export function sendWith(aStateMachine: StateMachines, aTriggerName: string,
        aTriggerArgs: number[]
    ) {
    }

    /**
     * start state machine
     * @param aStateMachine StateMachines
     * @param aStateName default state name
     */
    //% block="start $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=80
    //% group="Command"
    export function start(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * export UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aStateMachine
     * @param aStateName default state
     */
    //% block="UML $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=70
    //% group="Command"
    //% shim=mstate::simu_export_uml
    //% advanced=true
    export function exportUml(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * description UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aDescription
     */
    //% block="description $aDescription"
    //% aDescription.defl="(description)"
    //% weight=60
    //% group="Command"
    //% shim=mstate::simu_description_uml
    //% advanced=true
    export function descriptionUml(aDescription: string) {
    }
    
    /**
     * UML state
     * @param aMachine  machine ID
     * @param aStateName state name
     */
    //% block
    //% shim=mstate::simu_state_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuStateUml(aMachine: number, aStateName: string) {
    }

    /**
     * UML transition
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTransList array of next state name
     */
    //% block
    //% shim=mstate::simu_transition_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuTransitionUml(aMachine: number, aState: number,
        aTransList: string[]
    ) {
    }

    /**
     * convert id (number) to state/trigger name (string)
     * @param id machine-state id or machine-trigger id
     * @returns state name (string) or trigger name (string): "[<id>]" if undefined
     */
    //% block
    //% shim=mstate::simu_conv_name
    //% blockHidden=true
    //% advanced=true
    export function _simuConvName(id: number): string {
    }

}

```

</details>

## 課題3.　複数の遷移先（説明の記述）

複数の遷移先がある場合、それぞれの遷移に説明を記述できるようにする為、説明記述ブロックの引数を配列にする（遷移先の配列と順序を合わせる）。
また、説明記述ブロックの引数配列化に伴い、状態の宣言ブロックに対しては、UML出力時に、その要素毎に改行した説明文字列を出力する(改行コード `\n` )。

<table><tr><th>UML</th><th>対策版</th></tr>
<tr><td>

```plantuml
@startuml
' http://www.plantuml.com/plantuml/
state __M0__ {
[*] --> a
state a : Do/\n - do something\n
a --> [*] : [guard] / action
a --> [*] : [guard2]
a --> a : [else]
}
@enduml
```

</td><td>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/bfb134e2-58a3-1e78-f5dd-b004085807bc.png)

</td></tr></table>


<details><summary><b>ソースコード（ブロック定義）</b></summary>

```ts:mstate.ts (複数の遷移先)
enum StateMachines {
    M0 = 0,
    M1,
    M2,
    M3,
    M4,
    M5
}

/**
 * mstate blocks
 * Defining blocks: https://makecode.com/defining-blocks
 * Playground: https://makecode.com/playground
 * icon: a Unicode identifier for an icon from the Font Awesome icon set.
 *       http://fontawesome.io/icons
 */
//% weight=100 color="#4C97FF" icon="\uf362"
//% groups="['Command', 'Declare', 'Transit']"
namespace mstate {

    /**
     * convert state/trigger name (string) to id (number): new id if undefined
     * @param aStateMachine StateMachines
     * @param name state name (string) or trigger name (string)
     * @returns machine-state id or machine-trigger id
     */
    //% block="id of $aStateMachine $name"
    //% aStateMachine.defl=StateMachines.M0
    //% name.defl="a"
    //% weight=210
    //% advanced=true
    export function convId(aStateMachine: StateMachines, name: string): number {
    }

    /**
     * Internal event settings
     * @param aStateMachine StateMachines
     * @param eventId Event ID (default: 32868 = 32768 + 100)
     * @param ms Event loop interval (default: 100ms)
     */
    //% block="settings $aStateMachine event ID: $eventId every: $ms ms"
    //% aStateMachine.defl=StateMachines.M0
    //% eventId.defl=32768
    //% ms.shadow="timePicker"
    //% ms.defl=100
    //% weight=190
    //% advanced=true
    export function settingsMachineEvent(aStateMachine: StateMachines, eventId: number,
        ms: number
    ) {
    }

    /**
     * define state
     * @param aStateMachine StateMachines
     * @param aStateName state name
     * @param body code to run, (mstateId: machine-state ID)
     */
    //% block="define $aStateMachine $mstateId to $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% draggableParameters="reporter"
    //% weight=180
    //% group="Declare"
    export function defineState(aStateMachine: StateMachines, aStateName: string,
        body: (mstateId: number) => void
    ) {
    }

    /**
     * declare ENTRY action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on entry"
    //% $aMstateId.defl=0
    //% handlerStatement
    //% weight=170
    //% group="Declare"
    export function declareEntry(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare DO activity.
     * @param machine-state ID
     * @param aEvery interval time (milliseconds)
     * @param body code to run
     */
    //% block="mstate $aMstateId on do every $aEvery ms"
    //% aMstateId.defl=0
    //% aEvery.shadow="timePicker"
    //% handlerStatement
    //% weight=160
    //% group="Declare"
    export function declareDo(aMstateId: number, aEvery: number,
        body: () => void
    ) {
    }

    /**
     * declare EXIT action.
     * @param machine-state ID
     * @param body code to run
     */
    //% block="mstate $aMstateId on exit"
    //% aMstateId.defl=0
    //% handlerStatement
    //% weight=150
    //% group="Declare"
    export function declareExit(aMstateId: number,
        body: () => void
    ) {
    }

    /**
     * declare simple transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aToName next state nam
     */
    //% block="mstate $aMstateId trasition when $aTriggerName to $aToName"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=140
    //% group="Transition"
    export function declareSimpleTransition(aMstateId: number,
        aTriggerName: string, aToName: string
    ) {
    }

    /**
     * declare timeouted transition.
     * @param machine-state ID
     * @param aMs timeout (ms)
     * @param aToName next state name
     */
    //% block="mstate $aMstateId trasition timeouted $aMs to $aToName"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% aToName.defl="a"
    //% inlineInputMode=inline
    //% weight=130
    //% group="Transition"
    export function declareTimeoutedTransition(aMstateId: number,
        aMs: number, aToName: string
    ) {
    }

    /**
     * declare custom transition.
     * @param machine-state ID
     * @param aTriggerName trigger name
     * @param aTransList array of next state name 
     * @param body code to run
     */
    //% block="mstate $aMstateId trasition when $aTriggerName $args to $aTransList"
    //% aMstateId.defl=0
    //% aTriggerName.defl="e"
    //% draggableParameters="reporter"
    //% handlerStatement
    //% weight=120
    //% group="Transition"
    export function declareCustomTransition(aMstateId: number,
        aTriggerName: string, aTransList: string[],
        body: () => void
    ) {
    }

    /**
     * is timeouted.
     * @param machine-state ID, as machine ID
     * @param aMs timeout (milliseconds)
     */
    //% block="mstate $aMstateId timeouted $aMs"
    //% aMstateId.defl=0
    //% aMs.shadow="timePicker"
    //% weight=110
    //% group="Transition"
    export function isTimeouted(aMstateId: number, aMs: number): boolean {
    }

    /**
     * trigger args.
     * @param machine-state ID, as machine ID
     * @returns trigger args
     */
    //% block="mstate $aMstateId trigger args"
    //% aMstateId.defl=0
    //% weight=105
    //% group="Transition"
    //% advanced=true
    export function getTriggerArgs(aMstateId: number): number[] {
    }

    /**
     * transit to.
     * @param machine-state ID, as machine ID
     * @param index states index]
     */
    //% block="mstate $aMstateId transit at $index"
    //% aMstateId.defl=0
    //% index.defl=0
    //% weight=100
    //% group="Transition"
    export function transitTo(aMstateId: number, index: number) {
    }

    /**
     * send trigger
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     */
    //% block="send $aStateMachine $aTriggerName"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=95
    //% group="Command"
    export function send(aStateMachine: StateMachines, aTriggerName: string) {
    }

    /**
     * send trigger with args
     * @param aStateMachine StateMachines
     * @param aTriggerName trigger name
     * @param aTriggerArgs args
     */
    //% block="send $aStateMachine $aTriggerName $aTriggerArgs"
    //% aStateMachine.defl=StateMachines.M0
    //% aTriggerName.defl="e"
    //% weight=90
    //% group="Command"
    //% advanced=true
    export function sendWith(aStateMachine: StateMachines, aTriggerName: string,
        aTriggerArgs: number[]
    ) {
    }

    /**
     * start state machine
     * @param aStateMachine StateMachines
     * @param aStateName default state name
     */
    //% block="start $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=80
    //% group="Command"
    export function start(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * export UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aStateMachine
     * @param aStateName default state
     */
    //% block="UML $aStateMachine $aStateName"
    //% aStateMachine.defl=StateMachines.M0
    //% aStateName.defl="a"
    //% weight=70
    //% group="Command"
    //% shim=mstate::simu_export_uml
    //% advanced=true
    export function exportUml(aStateMachine: StateMachines, aStateName: string) {
    }

    /**
     * description UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aDescription
     */
    //% block="description $aDescription"
    //% aDescription.defl="(description)"
    //% weight=60
    //% group="Command"
    //% shim=mstate::simu_description_uml
    //% advanced=true
    export function descriptionUml(aDescription: string) {
    }
    
    /**
     * description UML, PlantUML
     * PlantUML Web server: http://www.plantuml.com/plantuml/
     * @param aDescription
     */
    //% block="description $aDescriptionList"
    //% weight=50
    //% group="Command"
    //% shim=mstate::simu_descriptions_uml
    //% advanced=true
    export function descriptionsUml(aDescriptionList: string[]) {
    }
    
    /**
     * UML state
     * @param aMachine  machine ID
     * @param aStateName state name
     */
    //% block
    //% shim=mstate::simu_state_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuStateUml(aMachine: number, aStateName: string) {
    }

    /**
     * UML transition
     * @param aMachine machine ID
     * @param aState state ID
     * @param aTransList array of next state name
     */
    //% block
    //% shim=mstate::simu_transition_uml
    //% blockHidden=true
    //% advanced=true
    export function _simuTransitionUml(aMachine: number, aState: number,
        aTransList: string[]
    ) {
    }

    /**
     * convert id (number) to state/trigger name (string)
     * @param id machine-state id or machine-trigger id
     * @returns state name (string) or trigger name (string): "[<id>]" if undefined
     */
    //% block
    //% shim=mstate::simu_conv_name
    //% blockHidden=true
    //% advanced=true
    export function _simuConvName(id: number): string {
    }

}

```

</details>

## 課題4.　イベント依存

pxt-mstateでは、状態遷移の判定とその実行を[micro:bitのイベント(uBit.messageBus)](https://lancaster-university.github.io/microbit-docs/ubit/messageBus/)に依存している。
このイベント処理部分を `mcontroller` 名前空間の関数群として抜きだし、他の実行環境でも容易に実装できるよう、差し替え可能にする（将来的には抽象クラスとして実現したい）。

```js: TypeScript

    /**
     * controller - micro:bit
     */
    export namespace mcontroller {

        /**
         * idle update
         * @param machineId machine ID
         */
        function _idleUpdate(machineId: number) {
            control.raiseEvent(MSTATE_BUS_ID.MSTATE_ID_UPDATE, machineId)
        }


        /**
         * create StateMachine
         * @param machineId machine ID
         * @returns instance of StateMachine
         */
        function _createNewStateMacnhe(machineId: number) {
            return new mmachine.StateMachine(machineId, function () {
                _idleUpdate(machineId)
            })
        }

    }
    
```

## 課題5.　ポーリング間隔

**（保留）**
時間待ちループの経過時間の取得もmicro:bitに依存しているため、外部タイマーとして外出しとし、差し替え可能にしたい。それに合わせて、タイムアウトによるトランジション方式も見直したい。


# まとめ

５つの課題のうち、４つの課題を解決し、[ver.0.8.3以降](https://github.com/jp-rad/pxt-mstate/tree/0.8.3) で対応した。
残課題は、次の機会に対応する。

| # | 課題 | 結果 | 残課題 |
|:-:|-----|:----:|----|
| 1 | machine/state | 解決 | |
| 2 | 説明の記述方法 | 解決 | |
| 3 | 複数の遷移先(説明記述) | 解決 | |
| 4 | イベント依存 | 解決 | **抽象クラス化** |
| 5 | ポーリング間隔 | 保留 | **外部タイマー化** |

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