【AWS】ポイントをおさえてAWS IoT Jobsを理解する #2/2【IoT】

※本稿は下記記事の続きです。

[【AWS】ポイントをおさえてAWS IoT Jobsを理解する #1/2【IoT】 - Qiita](https://qiita.com/tmiki/items/f6b9a7201fc449304bbb)
[【AWS】ポイントをおさえてAWS IoT Jobsを理解する #2/2【IoT】 - Qiita](https://qiita.com/tmiki/items/f4bd5d8eb86011b3d890)　**←いまココ**

# AWS IoT Jobsのフロー
## 2. Jobの実行
デバイスはJobDocumentに記載の内容をもとに必要な処理を実行していきます。
![2.PerformAJob.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/51c71282-80b7-dfe5-6f96-36ba8d02a713.png)

基本的には、デバイスから当該Jobのstatusを都度都度更新していく、という流れになります。
「UpdateJobExecution」というAPIを利用してJobの情報を更新していきます。

Using the AWS IoT Jobs APIs - AWS IoT -> UpdateJobExecution
https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-updatejobexecution

基本的な流れ・ポイント下記のとおりです。

- デバイスは当該Jobの状態（status/statusDetails）をこまめに更新していく
- 処理ステップを細かく分け、それぞれの状況をstatusDetailsに更新することで、進捗状況を細かく確認することができる
- statusは既定の文字列のみ許容される (IN_PROGRESS, FAILED, SUCCEEDED, or REJECTED)
- statusDetailsは任意の内容を設定できる

Jobの状態を更新する際には、その可否を知る必要がありますので、デバイスは処理の実行前に、当該Jobの更新結果を受け取るためのTopicを最初にSubscribeしておく必要があります。
具体的には以下のTopicとなります。
多くの場合、Jobの実行は並列で行わないと思いますので、「{{Job Id}}」の部分はワイルドカードである「+」にした方が便利です。

- $aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted
- $aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/rejected


再度、おさらいとして、Jobに含まれている情報を再確認してみましょう。
「execution.status」という項目が存在し「QUEUED」であることが分かります。作成直後の状態は「QUEUED」になります。
「statusDetails」はある意味Optionalな項目であり、デフォルトでは含まれません。
「versionNumber」は、以降で行う更新操作のたびにインクリメントされていきます。
「execution.jobDocument」の内容がJobDocumentになります。
このJSONドキュメントはAWS利用者が任意に定義できます。デバイスはこの内容に従い、必要な処理を行うようあらかじめ設計・実装されている必要があります。

```json:Job(iot-job01)
{
  "timestamp": 1555262670,
  "execution": {
    "jobId": "iot-job01",
    "status": "QUEUED",
    "queuedAt": 1555262670,
    "lastUpdatedAt": 1555262670,
    "versionNumber": 1,
    "executionNumber": 1,
    "jobDocument": {
      "jobType": "fetchFile",
      "fileUrl": "https://investigate-iot-jobs.s3.ap-northeast-1.amazonaws.com/iot-job01-files/distfile.bin?X-Amz-Security-Token=AgoGb3JpZ2luEGUaDmFwLW5vcnRoZWFzdC0xIoACFZBtrnE5T6QTP7%2FElU9iW06MYGK%2FmRVNuOZWGSV8WjY63nnm37IYbREWtVNkqSZ7gMf%2BSIf7o9fFW4r6IIWA7YDdfpYUCSBJWSt8zcr2hQTlXwtMc4FJlZztw2D%2F%2F5XBK32%2FJH22LuH0Tleac6a1f6hdGqxPPh8S5pysWz8XOzG4Qsa9qtaet4rM8iPMVH3fXfttMvYPYSHn7hNUsJyWnAyIBXiF920wspUDA%2BxFdV%2B%2FoQNkzusn%2F36mOgsToee9pgko3YgrDVm%2B3pica2E5vArQN1aW5SLFSAyE%2BPjNOF6PKBVjfPVa8UehzUrQ7CiVmEwPYe5M2YAVFRgpp6MWoiqPAgjb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDQ5NzMzOTA2MzAwOCIM28PN%2F0N3kY5OS2XdKuMBKnMV7KWUxHpw5CuLGWCgi9rce6A6%2Fyq3Y%2Fmiz6AfqWECjivGPzYiaN77u7u7z3kOKdQJJ7Jd8OQU%2B%2Fm9vF09JuVJe6II5IDvSorw%2FXUDafZj78bayCIApioHbjVDt8jrMgEeBiW%2Bxj8OcwDA1rYVhElA8qIdT6snzQRnKXAjhBt3nqNTQKU2N6PqLLiLRuZT51ENF1kfiY0FOLb6Hqyimmt1za2Pk9vrNo9poQeVCIAIZnKuhqzQzdp%2B1KRrYhTzhGEA3r%2FSvMiTDwcY%2FexRYWxIVOZGo418jLakBuUi6z%2B6PkMwztnN5QU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20190414T172430Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIAXHS5X3LQDCAJRFN4%2F20190414%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=636cc1afc7156948b49d6c8eff86af4f4d055117d9a0a7dda9fc4e394a4b8218"
    }
  }
}
```

### Jobのstatusの更新（※データ定義2-1-1, 2-1-2）
必要なMQTT TopicのSubscribeが完了し、デバイス側でJobを実行できる状態になったら、その状況をJobに更新します。
下記のようなJSONドキュメントを、下記MQTT TopicにPublishします。

- $aws/things/{{Thing Name}}/jobs/{{Job Id}}/update

```json:$aws/things/device01/jobs/iot-job01/update
{
    "status": "IN_PROGRESS",
    "statusDetails": { 
        "downloading": "in-progress",
        "extracting": "awaiting",
        "updating": "awaiting"
    },
    "expectedVersion": "1",
    "stepTimeoutInMinutes": 300,
    "clientToken": "arbitrary-client-token-string-123456789"
}
```

更新に成功すると、「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted」にその旨のメッセージがPublishされます。

```json:$aws/things/device01/jobs/+/update/accepted
{
  "clientToken": "arbitrary-client-token-string-123456789",
  "timestamp": 1555350329
}
```

ちなみに「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update」にPublishする際に指定する「clientToken」ですが、これは任意の文字列を指定します。
デバイスからの更新リクエストに対するレスポンスが「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted」にPublishされますが、この際にPublishされるメッセージに同一の「clientToken」を含みます。

「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted」にPublishされたメッセージが、どの更新リクエストに対応するものなのかを識別するために利用することができます。
これは「$aws/things/{{Thing Name}}/jobs/+/update/accepted」のように、ワイルドカードを利用してTopicをSubscribeしている場合に活用できます。


この状態で、AWS Management Consoleから「iot-job01」の状況を見てみましょう。device01のStatusが「In progress」となり、全体の台数も「In progress」のものが1台増えていることが確認できます。。
![2019-04-16_02h53_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/6d4cd06f-65f5-91c5-694d-3ac8ad0f3b32.png)


### Jobのstatusの更新（※データ定義2-1-3, 2-1-4）
配信ファイルのダウンロードが完了したら、「statusDetails」を更新していきましょう。
先ほどとほとんど同一の操作となりますが、幾つか違いがあります。

まず、「statusDetails」には詳細な進捗状況を記載します。
「versionNumber」は先ほどの操作でインクリメントされていますので、「expectedVersion」には「2」を指定します。
「clientToken」は、また新たに生成した任意の文字列を指定します。

```json:$aws/things/device01/jobs/iot-job01/update
{
    "status": "IN_PROGRESS",
    "statusDetails": { 
        "downloading": "done",
        "extracting": "awaiting",
        "updating": "awaiting"
    },
    "expectedVersion": "2",
    "stepTimeoutInMinutes": 300,
    "clientToken": "arbitrary-client-token-string-987654321"
}
```

更新に成功すると、「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted」にその旨のメッセージがPublishされます。

```json:$aws/things/device01/jobs/+/update/accepted
{
  "clientToken": "arbitrary-client-token-string-987654321",
  "timestamp": 1555351386
}
```

### Jobの完了とstatusの更新（※データ定義2-1-5, 2-1-6）
デバイス側での処理が完了したら、結果が成功であるにせよ失敗であるにせよ、「status」を更新します。
成功した場合は「SUCCEEDED」を、失敗した場合は「FAILED」を指定します。

「versionNumber」は先ほどの操作でインクリメントされていますので、「expectedVersion」には「3」を指定します。
「clientToken」は、また新たに生成した任意の文字列を指定します。

```json:$aws/things/device01/jobs/iot-job01/update
{
    "status": "SUCCEEDED",
    "statusDetails": { 
        "downloading": "done",
        "extracting": "done",
        "updating": "done"
    },
    "expectedVersion": "3",
    "stepTimeoutInMinutes": 300,
    "clientToken": "arbitrary-client-token-string-999999999"
}
```

更新に成功すると、「$aws/things/{{Thing Name}}/jobs/{{Job Id}}/update/accepted」にその旨のメッセージがPublishされます。

```json:$aws/things/device01/jobs/+/update/accepted
{
  "clientToken": "arbitrary-client-token-string-999999999",
  "timestamp": 1555351778
}
```

この状態で、AWS Management Consoleから「iot-job01」の状況を見てみましょう。device01のStatusが「Succeeded」となり、全体の台数も「Succeeded」のものが1台増えていることが確認できます。。

![2019-04-16_03h10_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/e42526d5-54bd-05f9-72c7-0480c75366d8.png)

### 次のJob情報のPublish
もしデバイスが下記の2つのMQTT TopicをSubscribeしている場合は、実行待ちのJobの一覧・次に実行するべきJob情報がPublishされます。
iot-job02の情報がPublishされていることが分かります。これは

- $aws/things/{{Thing Name}}/jobs/notify
- $aws/things/{{Thing Name}}/jobs/notify-next

```json:$aws/things/device01/jobs/notify
{
  "timestamp": 1555351780,
  "jobs": {
    "QUEUED": [
      {
        "jobId": "iot-job02",
        "queuedAt": 1555263139,
        "lastUpdatedAt": 1555263139,
        "executionNumber": 1,
        "versionNumber": 1
      }
    ]
  }
}
```

```json:$aws/things/device01/jobs/notify-next
{
  "timestamp": 1555351780,
  "execution": {
    "jobId": "iot-job02",
    "status": "QUEUED",
    "queuedAt": 1555263139,
    "lastUpdatedAt": 1555263139,
    "versionNumber": 1,
    "executionNumber": 1,
    "jobDocument": {
      "jobType": "fetchFile",
      "fileUrl": "https://investigate-iot-jobs.s3.ap-northeast-1.amazonaws.com/iot-job02-files/distfile.bin?X-Amz-Security-Token=AgoGb3JpZ2luEH4aDmFwLW5vcnRoZWFzdC0xIoACpHnYrYRllufirgV5pK3cGYwkSObGn8UZ0KZU1ZFw563mFqfExtfKRbXGm7rpLwslIcwupNHr5ac9bNlBM5FoiBNFMtJHrHSuYpE3D1TW8FVQFtIA42zxZwzY3wVLIbvvNWsRezYxsWtsn90fxGS%2F%2Fy%2BhRcosAcwEYN0UdsE4ZhoL5kT8KFbCnOVckT0yToz3bU%2FkydjcOGkIZgBVkSHXtsfpWgKIYPw0myjfcXToiVtiioT1QGuq9c1l8h5yuJmyBZerA6SurV9yJ8r3Msh%2FFXk0FZUI4yBwW0J2zSxTSQ8tq%2B0U7MCE4VXNRfxg2kdu%2BsYgOSTyUbu5uwZLOhFtuSqPAgjz%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDQ5NzMzOTA2MzAwOCIMDhqUkz2kSTvJU%2Ft4KuMBCSUCPWhE8cPHFCHXW6Qy8PBcX0Cm3DIlTEE8%2BN6CSS6Nd%2Fx2B88UCik8T%2FhFoYzxpWVwWDQtSKSvtthapMKSuZYq2wUA41LXUAmEIHUFMFB4K94tnlLUj0lo30UjvM%2BNZZv%2BGbhw7GuTdaQNR10fSYCgZBITNrPrTqhzfi6axKrpJI4gbA%2Bg14HiIG2meXB1SpQYimPkLAVwg8qVNHiokofrXTgaZU%2BtejX8XdST9hava06a%2FG3tQimSUUIL6b0OOnvl01zA8DQvO5%2FWMLARMHIobvWRTZsL4v0gSSxU4Fp8pwYw5JHT5QU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20190415T180940Z&X-Amz-SignedHeaders=host&X-Amz-Expires=299&X-Amz-Credential=ASIAXHS5X3LQJB466J4F%2F20190415%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=e5dbb4167d4a5ae71146f424dc3837dfcf5eda8b7ae819253ea764ed07a042a4"
    }
  }
}
```

### 実行するべきJobがなくなった場合
実行待ちのJobがすべて完了すると、下記それぞれのMQTT Topicにほぼ空のJSONドキュメントがPublishされます。

- $aws/things/{{Thing Name}}/jobs/notify
- $aws/things/{{Thing Name}}/jobs/notify-next

```json:$aws/things/device01/jobs/notify
{
  "timestamp": 1555352455,
  "jobs": {}
}
```

```json:$aws/things/device01/jobs/notify-next
{
  "timestamp": 1555352455
}
```

# おわりに
これまで見てきたように、AWS IoT Jobsとは、クラウド側とデバイス側でJSONドキュメントを相互に更新・参照する仕組みであると言えます。また、通信の途絶／メッセージのロストがあっても確実にリカバリできるようにうまく設計されています。

IoTデバイスに何らかファイルを配信したりプログラムを実行させたい場合は、AWS IoT Jobsが非常に有用です。
是非とも活用していきたいと思います。