AWS GreengrassにComponentをデプロイする

## まとめ
* RaspberryPi4をGreengrassコアデバイスとしてセットアップし、コンポーネントのデプロイまで一度実施済み。(参考: [AWS IoT Greengrass 2.0 入門ハンズオン](https://catalog.us-east-1.prod.workshops.aws/v2/workshops/5ecc2416-f956-4273-b729-d0d30556013f/ja-JP/))
* コンポーネントの作成は、ローカルにRecipeとArtifactを置いた状態からCLIコマンドで作成と、S3にArtifactを置いてから、コンソールから作成するパターンの両方を試していた。
* 数週間経った後で、改めてコンポーネントの作成とデプロイを実施しようとしたら、何箇所かつまづいたので、つまづいた箇所の記録をこの記事に残す。

## つまづいた箇所
#### コンポーネント作成の際に、S3にアクセスできずFailedが返ってくる。
* **原因:** デフォルトのRaspberry PiのIAM RoleにはS3アクセス権限が与えられていない。
* **解決:** S3へのアクセスのIAM Policyをロール GreengrassV2TokenExchangeRoleに追加。
* **参考資料:** [Allow access to S3 buckets for component artifacts](https://docs.aws.amazon.com/greengrass/v2/developerguide/device-service-role.html#device-service-role-access-s3-bucket)

#### IoT CoreにMQTT信号を送るPublisherをデプロイしたのは良いが、どうやって起動するんだっけ？
* **解決1:** ローカルデバッグコンソールも一緒にデプロイ。ローカルからPublisherをスタートしてコンソールから信号を受け取っていることを確認。
* **解決2:** Raspberry PiにSSHで接続してから、`sudo /greengrass/v2/bin/greengrass-cli component restart —names "COMPONENT_NAME"`で走らせられることも確認。
* **参考資料:** [[AWS IoT Greengrass V2] ローカルデバッグコンソール(aws.greengrass.LocalDebugConsole)を使用してみました](https://dev.classmethod.jp/articles/greengrass-v2-aws-greengrass-localdebugconsole/)


## 次のステップ
1. Subscribeした信号をトリガーにMQTT信号を送り返すコンポーネントの作成及びデプロイ。これは単純にpythonでコンポーネントを書くことに慣れるのが目的
1. Subscribeした信号をトリガーにAWSサービスにアクセスするコンポーネントの作成及びデプロイ。目的はGreengrassとIoT Core以外のサービスとのやりとりについて学ぶ 
1. Subscribeした信号をトリガーにRaspberry Piのアプリケーションを起動させ、その結果をAWSサービスに共有するコンポーネントの作成及びデプロイ。目的はGreengrassとローカルリソースとのやりとりについて学ぶ

## デプロイしたコンポーネント
#### Publisher
以下にコードを残す。これはコンポーネントをDeployする段階で、"S3にアクセスができません"とエラーが来て、なるほど、コンポーネント作成とは全ての情報があることを確認するだけで、実際の組み立てはデプロイの段階なのね、と自分なりに理解。

```Python:publisher.py 
import time
import json
import awsiot.greengrasscoreipc
from awsiot.greengrasscoreipc.model import (
    QOS,
    PublishToIoTCoreRequest
)

TIMEOUT = 10
topic = "topic"
qos = QOS.AT_LEAST_ONCE

ipc_client = awsiot.greengrasscoreipc.connect()

print("publisher start!")

for i in range(5):
    message = {
        "counter": i
    }

    request = PublishToIoTCoreRequest()
    request.topic_name = topic
    request.payload = json.dumps(message).encode('utf-8')
    request.qos = qos
    
    operation = ipc_client.new_publish_to_iot_core()
    operation.activate(request)
    future = operation.get_response()
    future.result(TIMEOUT)

    print("publis :{}".format(message))
    time.sleep(1)

print("publish completed!")
```
#### Subscriber
こっちの方が手こずった。`StreamHandler`は、まだまだ呪文にしか聞こえないけど、今はとりあえず呪文として扱って先に進む。どこかでちゃんと学ぼう。レシピの作成でいくつかReviseしてなんとかデプロイに漕ぎ着ける。

```python:Subscriber
import time
import awsiot.greengrasscoreipc
import awsiot.greengrasscoreipc.client as client
from awsiot.greengrasscoreipc.model import (
    IoTCoreMessage,
    QOS,
    SubscribeToIoTCoreRequest
)

TIMEOUT = 10
topic = "topic"
qos = QOS.AT_MOST_ONCE

ipc_client = awsiot.greengrasscoreipc.connect()

print("start the subscriber")

class StreamHandler(client.SubscribeToIoTCoreStreamHandler):
    def __init__(self):
        super().__init__()

    def on_stream_event(self, event: IoTCoreMessage) -> None:
        message = str(event.message.payload, "utf-8")
        print("payload:{}".format(message))

    def on_stream_error(self, error: Exception) -> bool:
        return True
    
    def on_stream_closed(self) -> None:
        pass

request = SubscribeToIoTCoreRequest()
request.topic_name = topic
request.qos = qos
handler = StreamHandler()
operation = ipc_client.new_subscribe_to_iot_core(handler)
future = operation.activate(request)
future.result(TIMEOUT)

while True:
    time.sleep(1)

operation.close()
print("Stop the subscription.")
```

## 参考資料
https://catalog.us-east-1.prod.workshops.aws/v2/workshops/5ecc2416-f956-4273-b729-d0d30556013f/ja-JP/

https://docs.aws.amazon.com/greengrass/v2/developerguide/device-service-role.html#device-service-role-access-s3-bucket

https://dev.classmethod.jp/articles/greengrass-v2-aws-greengrass-localdebugconsole/

https://docs.aws.amazon.com/ja_jp/greengrass/v2/developerguide/ipc-iot-core-mqtt.html#ipc-operation-publishtoiotcore