FIWAREと画像処理の連携

昨今様々な自治体で、オープンデータやスマートシティへの取り組みが進められています。その際に重要となるパーツが**データ連携基盤**であり、特にそのコアとなるのはデータを仲介する**ブローカー機能**だと言われています。[^1]
![各地域のデータ連携基盤の整備.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37648/d8093b6b-fd93-d8f5-c18d-0b13d4d61148.png)
[^1]: 出典：デジタル庁, デジタル田園都市国家構想実現会議（第４回）, 資料8 データ連携基盤の整備について, 2022/2/24, https://www.cas.go.jp/jp/seisaku/digital_denen/dai4/siryou8.pdf

このデータ連携基盤を実現する際には、EUの第7次研究枠組計画で開発されオープンソースとして維持運用されている[FIWARE](https://www.fiware.org/)を利用することが多く、FIWAREのブローカー機能である[orion](https://github.com/telefonicaid/fiware-orion)を中心にFIWAREの様々なコンポーネントを組み合わせてインフラを組み立てることになります。[^2]

![FIWARE Generic Enablers](https://camo.githubusercontent.com/c0dead7b7b49db290579b8f2bb14b11970dbc36a6181d303aa955145fd05c7e4/68747470733a2f2f6669776172652e6769746875622e696f2f636174616c6f6775652f696d672f636174616c6f6775652e706e67)
[^2]: 出典：FIWARE Foundation, FIWARE Catalogue, https://github.com/FIWARE/catalogue

しかしFIWARE orionはテキストデータのブローカー機能に特化しているため、画像や映像を直接的に取り扱うことはできません。FIWAREのコンポーネントには[Kurento](https://kurento.readthedocs.io/en/stable/)や[OpenVidu](https://openvidu.io/)といった映像ストリームや音声ストリームを扱うものがありますが、FIWARE orionと容易に連携できるものではありません。[^3]
[^3]: KurentoやOpenViduは、カメラやマイク（映像や音声の送信元）とブラウザ（再生デバイス）をWebRTCで直結してリアルタイムに映像や音声を配信するコンポーネントです。映像を伝送する途中で画像解析処理と通信処理を割り込ませ、例えばカメラが撮影した自動車のナンバープレートを読み取り、そのテキストデータをFIWARE orionに登録する、といったことをさせることも可能です。ただしリアルタイムに映像が伝送できるインフラの準備と、リアルタイムな画像解析・通信処理の実装は、かなりハードルが高くなってしまいます。

そこで今回は、USBカメラを接続したRaspberry Piを監視カメラに見立て、監視カメラが撮影した画像をクラウドストレージに保存し、同時に「いつ・どこで画像を撮影したか」「その画像が保存されているクラウドストレージのパス」をFIWAREに登録するという仕組みを紹介します。

# 今回のアーキテクチャ
今回は、Microsoft Azureが提供するManaged Kubernetes Serviceである[Azure AKS](https://azure.microsoft.com/ja-jp/services/kubernetes-service/#overview)上にFIWAREのデータ連携基盤を構築し、Managed Object Storageである[Azure Blob Storage](https://azure.microsoft.com/ja-jp/services/storage/blobs/)と連携させます。
![architecture](https://github.com/nmatsui/qiita_fiware_camera/blob/main/qiita_fiware_camera.png?raw=true)
KubernetesやAPI Gatewayを活用することである程度の可用性とセキュリティは担保していますが、バックアップを考慮していないなど、あくまでも検証用のインフラであることにはご留意ください。

# 環境構築
それではまず、クラウド側のインフラを構築します。スクリプト類はgithubで公開（[qiita_fiware_camera](https://github.com/nmatsui/qiita_fiware_camera)）していますので、リポジトリをcloneしてください。
またMicrosoft Azureを操作するために[azure-cli](https://docs.microsoft.com/ja-jp/cli/azure/)を、Kubernetesを操作するために、[kubectl](https://kubernetes.io/ja/docs/reference/kubectl/overview/)と[helm](https://helm.sh/ja/)、及び[envsubst](https://www.gnu.org/software/gettext/manual/html_node/envsubst-Invocation.html)をインストールしておいてください。

## 変数設定
まずは[env.temp](https://github.com/nmatsui/qiita_fiware_camera/blob/main/variables/env.temp)をもとに、変数の値を設定したenvファイルを作成します。
<details><summary>envファイルを作成</summary><div>

```bash
nmatsui@:qiita_fiware_camera (main =)$ cp variables/env.temp variables/env
nmatsui@:qiita_fiware_camera (main =)$ vi variables/env
```
</div></details>

Microsoft Azureのテナント名やAzure DNSのドメイン名など、" << "と" >> "で囲まれた変数は環境ごとに異なるため、自らの環境にあわせて適切に設定してください（この記事では、**nmatsui.work**というドメインを利用したという形で解説しています）。

## Azure AKSとAzure Blob Storageの準備
`installation`ディレクトリに移動し、Microsoft Azure上にAzure AKSとAzure Blob Storageを起動します。

<details><summary>installationディレクトリに移動</summary><div>

```bash
nmatsui@:qiita_fiware_camera (main =)$ cd installation/
```
</div></details>

[01_azure_login.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/01_azure_login.sh)を実行するとブラウザが立ち上がるので、Microsoft Azureへログインしてください。
<details><summary>Microsoft Azureにログイン</summary><div>

```bash
nmatsui@:installation (main =)$ ./01_azure_login.sh
```
</div></details>

[02_azure_create_resourcegroup.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/02_azure_create_resourcegroup.sh)を実行して今回作成するクラウドリーソースを格納するResource Groupを作成します。
<details><summary>リソースグループを作成</summary><div>

```bash
nmatsui@:installation (main =)$ ./02_azure_create_resourcegroup.sh
```
</div></details>

[03_azure_start_aks.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/03_azure_start_aks.sh)を実行するとAzure AKSが起動します。5分程度かかりますので、コマンドが終了するまで待ってください。

<details><summary>Azure AKSを起動</summary><div>

```bash
nmatsui@:installation (main =)$ ./03_azure_start_aks.sh
```
```bash
{
  "aadProfile": null,
  "addonProfiles": null,
  "agentPoolProfiles": [
    {
      "availabilityZones": null,
      "count": 3,
      "creationData": null,
      "currentOrchestratorVersion": "1.24.0",
      "enableAutoScaling": false,
      "enableEncryptionAt

...

  "type": "Microsoft.ContainerService/ManagedClusters",
  "windowsProfile": null
}
Merged "qfcaks" as current context in /Users/nmatsui/.kube/config
```
```bash
nmatsui@:installation (main =)$ kubectl get nodes
NAME                                STATUS   ROLES   AGE     VERSION
aks-nodepool1-14606101-vmss000000   Ready    agent   7m12s   v1.24.0
aks-nodepool1-14606101-vmss000001   Ready    agent   7m14s   v1.24.0
aks-nodepool1-14606101-vmss000002   Ready    agent   7m17s   v1.24.0
```
</div></details>

[04_azure_blobstorage.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/04_azure_blobstorage.sh)を実行して、Azure Blob Storageを利用可能にします。また監視カメラから画像をアップロードするためのコンテナも作成しておきます。

<details><summary>Azure Blob Storageを設定</summary><div>

```bash
nmatsui@:installation (main =)$ ./04_azure_blobstorage.sh
```
```bash
{
  "accessTier": "Hot",
  "allowBlobPublicAccess": true,
  "allowCrossTenantReplication": null,
  "allowSharedKeyAccess": null,

...

  "storageAccountSkuConversionStatus": null,
  "tags": {},
  "type": "Microsoft.Storage/storageAccounts"
}
{
  "created": true
}
```
</div></details>

## FIWAREの準備
次にFIWARE orionとcygnus、及びそれらが依存するMongoDBをKubernetes上に起動します。

[05_fiware_start_mongodb.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/05_fiware_start_mongodb.sh)を実行すると、MongoDBのPRIMARY Nodeを1つ、SECONDARY Nodeを2つ起動します。立ち上がるまで数分待ちましょう。
<details><summary>MongoDBのStatefulSetを起動</summary><div>

```bash
nmatsui@:installation (main =)$ ./05_fiware_start_mongodb.sh
```
```bash
...

NAME: mongodb
LAST DEPLOYED: Sun Jun 26 21:26:58 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mongodb
CHART VERSION: 12.1.20
APP VERSION: 5.0.9

...

Then, run the following command:
    mongosh admin --host "mongodb-0.mongodb-headless.default.svc.cluster.local:27017,mongodb-1.mongodb-headless.default.svc.cluster.local:27017,mongodb-2.mongodb-headless.default.svc.cluster.local:27017"
```
```bash
nmatsui@:installation (main =)$ kubectl get statefulsets
NAME      READY   AGE
mongodb   3/3     2m49s
```
```bash
nmatsui@:installation (main =)$ kubectl get pods -l app.kubernetes.io/name=mongodb
NAME        READY   STATUS    RESTARTS   AGE
mongodb-0   1/1     Running   0          2m55s
mongodb-1   1/1     Running   0          2m
mongodb-2   1/1     Running   0          65s
```
</div></details>

MongoDBの3つのPodが立ち上がった後、[06_fiware_setup_mongodb.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/06_fiware_setup_mongodb.sh)を実行してクラスタ化します。
<details><summary>MongoDBをクラスタ化</summary><div>

```bash
nmatsui@:installation (main =)$ ./06_fiware_setup_mongodb.sh
```
```bash
...

If you don't see a command prompt, try pressing enter.
[
  {
    name: 'mongodb-0.mongodb-headless.default.svc.cluster.local:27017',
    stateStr: 'PRIMARY'
  },
  {
    name: 'mongodb-1.mongodb-headless.default.svc.cluster.local:27017',
    stateStr: 'SECONDARY'
  },
  {
    name: 'mongodb-2.mongodb-headless.default.svc.cluster.local:27017',
    stateStr: 'SECONDARY'
  }
]
pod "mongodb-client" deleted
```
</div></details>

[07_fiware_start_orion.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/07_fiware_start_orion.sh)を実行して[FIWARE orion](https://github.com/telefonicaid/fiware-orion)を立ち上げます。
FIWARE orionは監視カメラから送られてきたデータを受け取り、条件に従って適切なコンポーネントを呼び出してそのデータの処理を依頼するといった、**ブローカー機能**を実現するコンポーネントです。

<details><summary>orionを起動</summary><div>

```bash
nmatsui@:installation (main =)$ ./07_fiware_start_orion.sh
```
```bash
service/orion created
deployment.apps/orion created
```
```bash
nmatsui@:installation (main =)$ kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
orion   3/3     3            3           4m33s
```
```bash
nmatsui@:installation (main =)$ kubectl get pods -l app=orion
NAME                     READY   STATUS    RESTARTS   AGE
orion-54c98d69c7-4tjmh   1/1     Running   0          4m48s
orion-54c98d69c7-964st   1/1     Running   0          4m48s
orion-54c98d69c7-kgxrl   1/1     Running   0          4m48s
```
</div></details>

[08_fiware_start_cygnus.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/08_fiware_start_cygnus.sh)を実行して[FIWARE cygnus](https://github.com/telefonicaid/fiware-cygnus)を立ち上げます。
FIWARE cygnusはorionにデータが到着するごとにorionから呼び出され、到着したデータを時系列でデータベースに記録します。cygnusは様々なデータベースが利用できますが、今回は構築済みのMongoDBにデータを記録するように設定しました。

<details><summary>cygnusを起動</summary><div>

```bash
nmatsui@:installation (main =)$ ./08_fiware_start_cygnus.sh
```
```bash
service/cygnus created
deployment.apps/cygnus created
```
```bash
nmatsui@:installation (main =)$ kubectl get deployments
NAME     READY   UP-TO-DATE   AVAILABLE   AGE
cygnus   3/3     3            3           89s
orion    3/3     3            3           9m55s
```
```bash
nmatsui@:installation (main =)$ kubectl get pods -l app=cygnus
NAME                      READY   STATUS    RESTARTS   AGE
cygnus-6dc69bdd9f-l6kql   1/1     Running   0          105s
cygnus-6dc69bdd9f-pzplw   1/1     Running   0          105s
cygnus-6dc69bdd9f-w8dss   1/1     Running   0          105s
```
</div></details>

## API Gatewayの準備
FIWAREが立ち上がりましたので、API Gatewayとして[kong](https://konghq.com/)を起動しFIWARE orionと連携させます。FIWARE orionを直接インターネットと接続せず、API Gateway経由でインターネットに公開することで、通信経路の暗号化や利用者の認証認可、また必要に応じてレートリミットやIP制限などを実現できるようになります。
ただし今回は検証なので、簡易的な固定Tokenによる認証でお茶を濁しています。実際に運用する場合は、[OpenID Connect](https://openid.net/connect/)等を用いて適切な認証・認可を設定すべきでしょう。

[09_kong_start_kong.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/09_kong_start_kong.sh)を実行してkongのカスタムリソースやIngress Controller等を立ち上げます。同時に、Azure Load BalancerとAzure AKSが連携し、インターネットからkongへリクエストができるようになります。ただしDNSもTLSもルーティングも設定していないため、この時点ではあまり意味がありません。

<details><summary>kongを起動</summary><div>

```bash
nmatsui@:installation (main =)$ ./09_kong_start_kong.sh
```
```bash
...

service/kong-proxy created
service/kong-validation-webhook created
deployment.apps/ingress-kong created
ingressclass.networking.k8s.io/kong created
deployment.apps/ingress-kong scaled
```
```bash
nmatsui@:installation (main =)$ kubectl -n kong get deployments
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
ingress-kong   3/3     3            3           4m37s
```
```bash
nmatsui@:installation (main =)$ kubectl -n kong get pods
NAME                            READY   STATUS    RESTARTS   AGE
ingress-kong-6454f9b96b-nbfsp   2/2     Running   0          4m52s
ingress-kong-6454f9b96b-xz6mh   2/2     Running   0          4m52s
ingress-kong-6454f9b96b-zx9qw   2/2     Running   0          4m52s
```
```bash
nmatsui@:installation (main =)$ kubectl -n kong get services
NAME                      TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
kong-proxy                LoadBalancer   10.0.104.182   20.194.150.71   80:31629/TCP,443:30182/TCP   3m31s
kong-validation-webhook   ClusterIP      10.0.37.181    <none>          443/TCP                      3m31s
```
```bash
nmatsui@:installation (main =)$ curl -i http://20.194.150.71/
HTTP/1.1 404 Not Found
Date: Sun, 26 Jun 2022 13:08:35 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 48
X-Kong-Response-Latency: 0
Server: kong/2.8.1

{"message":"no Route matched with those values"}
```
</div></details>

[10_kong_start_cert-manager.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/10_kong_start_cert-manager.sh)を実行し、Azure DNSにAレコードを登録した後にLet's EncryptからTLS証明書を取得します。

<details><summary>DNSの登録とTLS証明書の取得</summary><div>

```bash
nmatsui@:installation (main =)$ ./10_kong_start_cert-manager.sh
```
```bash
namespace/cert-manager created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created

...

  "provisioningState": "Succeeded",
  "resourceGroup": "dns-zone",
  "targetResource": {
    "id": null
  },
  "ttl": 3600,
  "type": "Microsoft.Network/dnszones/A"
}
clusterissuer.cert-manager.io/letsencrypt-prod created
```
</div></details>

[11_kong_setup_kong.sh](https://github.com/nmatsui/qiita_fiware_camera/blob/main/installation/11_kong_setup_kong.sh)を実行し、kongにFIWARE orionへのルーティングを設定します。その際、TLS証明書の適用と固定Tokenによる認証、httpのhttpsへのリダイレクトも同時に設定することで、インターネットへ公開するorion APIのセキュリティを向上させます。

<details><summary>FIWARE orionへのルーティング定義とAPIセキュリティの設定</summary><div>

```bash
nmatsui@:installation (main =)$ ./11_kong_setup_kong.sh
```
```bash
kongingress.configuration.konghq.com/https-only created
secret/kong-keyauth created
kongconsumer.configuration.konghq.com/kong-keyauth created
kongplugin.configuration.konghq.com/key-auth created
ingress.networking.k8s.io/orion-ingress created
```
```bash
nmatsui@:installation (main =)$ curl -i http://fiware-camera.nmatsui.work/
HTTP/1.1 404 Not Found
Date: Sun, 26 Jun 2022 13:25:25 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 48
X-Kong-Response-Latency: 0
Server: kong/2.8.1

{"message":"no Route matched with those values"}
```
```bash
nmatsui@:installation (main =)$ curl -i http://fiware-camera.nmatsui.work/version
HTTP/1.1 301 Moved Permanently
Date: Sun, 26 Jun 2022 13:26:10 GMT
Content-Type: text/html
Content-Length: 134
Connection: keep-alive
Location: https://fiware-camera.nmatsui.work/version
X-Kong-Response-Latency: 0
Server: kong/2.8.1

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
</body>
</html>
```
```bash
nmatsui@:installation (main =)$ curl -i https://fiware-camera.nmatsui.work/version
HTTP/2 401
date: Sun, 26 Jun 2022 13:26:37 GMT
content-type: application/json; charset=utf-8
www-authenticate: Key realm="kong"
content-length: 45
x-kong-response-latency: 0
server: kong/2.8.1

{
  "message":"No API key found in request"
}
```
```bash
nmatsui@:installation (main =)$ curl -i -H "Authorization: ${API_TOKEN}" https://fiware-camera.nmatsui.work/version
HTTP/2 200
content-type: application/json
content-length: 656
fiware-correlator: f3fe3f82-f553-11ec-82c3-4a55b25ab823
date: Sun, 26 Jun 2022 13:29:06 GMT
x-kong-upstream-latency: 4
x-kong-proxy-latency: 2
via: kong/2.8.1

{
"orion" : {
  "version" : "3.4.0",
  "uptime" : "0 d, 0 h, 48 m, 20 s",
  "git_hash" : "e8ed9c5f363ab503ac406a9e62a213640d9c2864",
  "compile_time" : "Tue Dec 14 09:34:09 UTC 2021",
  "compiled_by" : "root",
  "compiled_in" : "5221bc0800ef",
  "release_date" : "Tue Dec 14 09:34:09 UTC 2021",
  "machine" : "x86_64",
  "doc" : "https://fiware-orion.rtfd.io/en/3.4.0/",
  "libversions": {
     "boost": "1_66",
     "libcurl": "libcurl/7.61.1 OpenSSL/1.1.1k zlib/1.2.11 nghttp2/1.33.0",
     "libmosquitto": "2.0.12",
     "libmicrohttpd": "0.9.70",
     "openssl": "1.1",
     "rapidjson": "1.1.0",
     "mongoc": "1.17.4",
     "bson": "1.17.4"
  }
}
}
```
</div></details>

## FIWAREのEntityとSubscriptionの定義
クラウド上のFIWARE orionのAPIへアクセスできるようになりましたので、監視カメラに相当するEntityを登録し、そのEntityが更新されるとcygnusがデータを記録するようにSubscriptionを定義します。

<details><summary>setupディレクトリに移動</summary><div>

```bash
nmatsui@:installation (main =)$ cd ../setup/
```
</div></details>

<details><summary>監視カメラのEntityを登録</summary><div>

```bash
nmatsui@:setup (main =)$ ./01_create_camera_entity.sh
```
```bash
HTTP/2 201
content-length: 0
location: /v2/entities/camera01?type=camera
fiware-correlator: 3e0f4e94-f555-11ec-b8fc-4a55b25ab823
date: Sun, 26 Jun 2022 13:38:20 GMT
x-kong-upstream-latency: 101
x-kong-proxy-latency: 1
via: kong/2.8.1
```
</div></details>
<details><summary>cygnusを呼び出すSubscriptionを定義</summary><div>

```bash
nmatsui@:setup (main =)$ ./02_subscribe_camera_entity.sh
```
```bash
HTTP/2 201
content-length: 0
location: /v2/subscriptions/62b86186fa263b47d742d76b
fiware-correlator: 60a34aa0-f555-11ec-94c9-eebfce5fe69a
date: Sun, 26 Jun 2022 13:39:18 GMT
x-kong-upstream-latency: 44
x-kong-proxy-latency: 1
via: kong/2.8.1
```
</div></details>

これによりFIWARE orionへ以下のEntityが登録され、このEntityが更新されるとcygnus経由でMongoDBにデータが時系列で記録されるようになります。

```bash
nmatsui@:setup (main =)$ source ../variables/env
nmatsui@:setup (main =)$ curl -sS -H "Authorization: ${API_TOKEN}" -H "Fiware-Service: ${FIWARE_SERVICE}" https://fiware-camera.nmatsui.work/v2/entities/ | jq .
[
  {
    "id": "camera01",
    "type": "camera",
    "created_at": {
      "type": "ISO8601",
      "value": "2022-06-26T13:38:19.000Z",
      "metadata": {}
    },
    "location": {
      "type": "geo:json",
      "value": {
        "type": "Point",
        "coordinates": [
          0,
          0
        ]
      },
      "metadata": {}
    },
    "path": {
      "type": "string",
      "value": "/",
      "metadata": {}
    }
  }
]
```

## 監視カメラの準備
最後にRaspberry Pi 3にUSBカメラを接続して監視カメラにします。今回はpython 3.10上でOpenCV 4を動作させてUSBカメラを扱います。[pyenv](https://github.com/pyenv/pyenv)等を用いてRaspberry Pi 3にpython 3.10を導入し、[poetry](https://cocoatomo.github.io/poetry-ja/)をインストールしておいてください。
なおazure-storage-blobが依存する[cryptography](https://pypi.org/project/cryptography/)は、ビルド時に[rust](https://www.rust-lang.org/)が必要となります。公式手順を参考に、rustもインストールしておいてください。

まずは`camera_client`ディレクトリをRaspberry Pi 3へSCPします。

<details><summary>camera_clientディレクトリをscp</summary><div>

```bash
nmatsui@:setup (main =)$ cd ../
nmatsui@:qiita_fiware_camera (main =)$ scp -r camera_client pi@raspberrypi.local:/home/pi/
```
</div></details>

また監視カメラの実行時に必要となるため、Azure Blob Storageへの接続文字列を取得しておき、同時に編集済みのenvファイルもSCPしておいてください。

<details><summary>Azure Blob Storageへの接続文字列を取得</summary><div>

```bash
nmatsui@:qiita_fiware_camera (main =)$ cd variables/
nmatsui@:variables (main =)$ ./01_azure_storage_connectionstring.sh
```
```bash
...................<<接続文字列>>....................
```
</div></details>

<details><summary>envファイルをscp</summary><div>

```bash
nmatsui@:variables (main =)$ scp ./env pi@raspberrypi.local:/home/pi/
```
</div></details>

では新しいターミナルを起動してraspberry pi 3にSSHし、[opencv-python](https://pypi.org/project/opencv-python/)やAzure Blob Storageのpythonライブラリ[azure-storage-blob](https://pypi.org/project/azure-storage-blob/)をインストールしましょう。
ただしopencv-pythonをビルドするためには様々なライブラリが必要となるため、[このページ](https://raku-tech.com/rasberrypi/opencv-install/)などを参考にエラーメッセージを見ながら地道に足りないライブラリを追加してください。ライブラリが全部揃っている状態でも、raspberry pi 3でopencvをビルドするには4~5時間はかかります。。。

<details><summary>raspberry pi 3にssh</summary><div>

```bash
nmatsui@:~$ ssh pi@raspberrypi.local
```
</div></details>
<details><summary>ライブラリをインストール</summary><div>

```bash
pi@raspberrypi:~ $ cd camera_client/
pi@raspberrypi:~/camera_client $ poetry install
```
</div></details>

poetryのshellに入り、opencv等がインストール済みのpython仮想環境をactivateします。またenvファイルの変数を環境変数として読み込み、Azure Blob Storageへの接続文字列も環境変数としてセットします。

<details><summary>仮想環境をactivate</summary><div>

```bash
pi@raspberrypi:~/camera_client $ poetry shell
```
</div></details>
<details><summary>envファイルの変数を環境変数として読み込み</summary><div>

```bash
(camera-client-4j-8aR1T-py3.10) pi@raspberrypi:~/camera_client $ set -a;source ~/env; set +a
```
</div></details>
<details><summary>Azure Blob Storageへの接続文字列を環境変数としてセット</summary><div>

```bash
(camera-client-4j-8aR1T-py3.10) pi@raspberrypi:~/camera_client $ export AZURE_STORAGE_CONNECTION_STRING="...................<<接続文字列>>...................."
```
</div></details>

# 監視カメラとFIWARE&クラウドストレージの連携
諸々準備できましたので、監視カメラを動作させFIWARE及びクラウドストレージと連携させてみましょう。今回は `main.py` を実行するだけです。想定通りに環境が準備できていれば、`finish script`と表示されるはずです。

<details><summary>監視カメラを動作</summary><div>

```bash
(camera-client-4j-8aR1T-py3.10) pi@raspberrypi:~/camera_client $ ./main.py
```
```bash
start script
finish script
```
</div></details>

無事に実行できましたら、FIware orionのEntityを確認してください。今撮影した監視カメラの映像の情報（撮影日時や場所）と、Azure Blob Storageのパスが記録されているはずです。[^4]
[^4]: 今回はGNSSモジュールが手に入らなかったため、[位置情報を取得する実装](https://github.com/nmatsui/qiita_fiware_camera/blob/main/camera_client/camera_client/gnss.py)はダミーです。

```bash
nmatsui@:variables (main =)$ curl -sS -H "Authorization: ${API_TOKEN}" -H "Fiware-Service: ${FIWARE_SERVICE}" https://fiware-camera.nmatsui.work/v2/entities/ | jq .
[
  {
    "id": "camera01",
    "type": "camera",
    "created_at": {
      "type": "ISO8601",
      "value": "2022-06-26T14:27:22.170Z",
      "metadata": {}
    },
    "location": {
      "type": "geo:json",
      "value": {
        "type": "Point",
        "coordinates": [
          139.932983435,
          37.487650993
        ]
      },
      "metadata": {}
    },
    "path": {
      "type": "string",
      "value": "photos/2022-06-26T23_27_22.170270.jpg",
      "metadata": {}
    }
  }
]
```

何度か撮影を繰り返してみましょう。FIWARE orionは最新の情報だけを保持するため、常に最後の撮影の情報がEntityに格納されています。過去の撮影記録を含め時系列に情報を知りたい場合は、cygnusが記録したデータベースを参照すると良いでしょう。

```bash
nmatsui@:variables (main =)$ kubectl run mongodb-client --rm --tty -i --restart='Never' --image marketplace.azurecr.io/bitnami/mongodb:4.4.15-debian-10-r2 --command -- \
>   mongosh admin --host 'mongodb-0.mongodb-headless.default.svc.cluster.local:27017,mongodb-1.mongodb-headless.default.svc.cluster.local:27017,mongodb-2.mongodb-headless.default.svc.cluster.local:27017'

...

rs0 [primary] admin> use sth_fiware_camera
rs0 [primary] sth_fiware_camera> db.sth_x002f.find()
[
  {
    _id: ObjectId("62b86cce266c7040087246bd"),
    entityId: 'camera01',
    entityType: 'camera',
    path: 'photos/2022-06-26T23_27_22.170270.jpg',
    created_at: ISODate("2022-06-26T14:27:22.170Z"),
    location: { type: 'Point', coordinates: [ 139.932983435, 37.487650993 ] },
    recvTime: ISODate("2022-06-26T14:27:24.735Z")
  },
  {
    _id: ObjectId("62b86ebc370e400b96d1a443"),
    entityId: 'camera01',
    entityType: 'camera',
    path: 'photos/2022-06-26T23_35_37.381996.jpg',
    created_at: ISODate("2022-06-26T14:35:37.381Z"),
    location: { type: 'Point', coordinates: [ 139.932983435, 37.487650993 ] },
    recvTime: ISODate("2022-06-26T14:35:39.858Z")
  },
  {
    _id: ObjectId("62b86eeb266c7040087246be"),
    entityId: 'camera01',
    entityType: 'camera',
    path: 'photos/2022-06-26T23_36_21.931998.jpg',
    created_at: ISODate("2022-06-26T14:36:21.931Z"),
    location: { type: 'Point', coordinates: [ 139.932983435, 37.487650993 ] },
    recvTime: ISODate("2022-06-26T14:36:24.368Z")
  }
]
```

上記の`path`で記録されている画像が、Azure Blob Storageに保存されていることも確認できます。
![スクリーンショット 2022-06-26 23.43.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37648/eb818303-28a9-96dc-2400-05e2132e145b.png)

ちなみにこんな画像がAzure Blob Storageに保存されていました。この監視カメラはどこを監視したいのでしょうか？
![2022-06-26T23_36_21.931998.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37648/d728bf16-5cd4-3e97-f264-650096d874f2.jpeg)

# まとめ
ということで、Azure AKS上のFIWAREとAzure Blob Storageを連携させ、監視カメラが取得した画像とその撮影記録を保管する仕組みを検証してみました。今回の検証はただ単に記録するだけでしたが、FIWARE orionがcygnusだけでなく画像解析プログラムも呼び出すように設定すれば、監視カメラに何か不審なものが映っていた場合に通報するような仕組みを作ることも可能でしょう。
FIWAREの得意不得意を踏まえて上手く活用し、皆様も良きFIWAREライフを送りましょう！

# 参考）検証した環境の情報
<table>
  <thead>
    <tr>
      <th>種類</th><th>コンポーネント</th><th>バージョン</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td rowspan="5">開発環境</td><td>macOS Big Sur</td><td>11.6.7</td>
    </tr>
    <tr>
      <td>azure-cli</td><td>2.37.0</td>
    </tr>
    <tr>
      <td>kubectl</td><td>1.24.2</td>
    </tr>
    <tr>
      <td>helm</td><td>3.9.0</td>
    </tr>
    <tr>
      <td>envsubst</td><td>0.21</td>
    </tr>
    <tr>
      <td rowspan="7">クラウド</td><td>Azure AKS</td><td>1.24.0</td>
    </tr>
    <tr>
      <td>Azure BLOB Storage</td><td></td>
    </tr>
    <tr>
      <td>Azure DNS</td><td></td>
    </tr>
    <tr>
      <td>FIWARE Orion</td><td>2.4.0</td>
    </tr>
    <tr>
      <td>FIWARE Cygnus</td><td>2.15.0</td>
    </tr>
    <tr>
      <td>MongoDB</td><td>4.2.7</td>
    </tr>
    <tr>
      <td>Kong</td><td>2.8.1</td>
    </tr>
    <tr>
      <td rowspan="7">監視カメラ</td><td>Raspberry Pi 3</td><td>Model B Plus Rev 1.3</td>
    </tr>
    <tr>
      <td>USBカメラ</td><td>LOGICOOL C270n</td>
    </tr>
    <tr>
      <td>Raspbian GNU/Linux 11 (bullseye)</td><td>5.15.32-v7+ #1538</td>
    </tr>
    <tr>
      <td>python</td><td>3.10.5</td>
    </tr>
    <tr>
      <td>opencv-python</td><td>4.6.0</td>
    </tr>
    <tr>
      <td>azure-storage-blob</td><td>12.12.0</td>
    </tr>
    <tr>
      <td>requests</td><td>2.28.0</td>
    </tr>
  </tbody>
</table>