[OCI]Project GreenThumb Part 5 - フロントエンド、Build Pipeline、Push Notifications、全体の進捗状況 (2021/03/31)
Project GreenThumb Part 5 - フロントエンド、Build Pipeline、Push Notifications、全体の進捗状況 (2021/03/31)
https://blogs.oracle.com/developers/project-greenthumb-part-5-the-front-end%2c-build-pipeline%2c-push-notifications-and-overall-progress
投稿者:Todd Sharp
この短いブログシリーズでは、
私がハードウェア、ソフトウェア、クラウドを使って苗の育成プロセスを自動化・監視するために作成したプロジェクト「Project GreenThumb」をご紹介しました。
このシリーズの他の記事を読んでいない方は、ぜひ読んでみてください。
- Project GreenThumb Part 1 - Automating & Monitoring Seedling Growth With Microcontrollers & The Cloud
- Project GreenThumb Part 2 - The Data Collection
- Project GreenThumb Part 3 - Consuming and Persisting the Sensor Data in the Cloud
- Project GreenThumb Part 4 - Reporting Queries and WebSockets
今回の記事では、フロントエンド、クラウドにデプロイするためにアプリケーションのビルドをどのように自動化したか、
プッシュ通知のサポートをどのように追加したかを見ていき、最後に目標に対するプロジェクトの現在の進捗状況を見ていきます。
Micronautアプリケーションへのシンプルなビューの追加
Micronautは "データファースト "なクラウドネイティブ・マイクロサービス・プラットフォームであることは明らかですが、
サーバーサイド・ビューレンダリングのサポートや統合も含まれていることはあまり知られていません。
Nettyのイベントループをブロックしないように、MicronautはサーバーサイドのビューレンダリングをI/Oスレッドプールで処理します。
多くのビューレンダリングエンジンがサポートされていますが(Handlebars、Velocity、Freemarkerなど)、
私は他の選択肢よりもThymeleafを選んだ理由は、少し慣れているからです。
ビューをレンダリングするには、コントローラは、
レンダリングするビューテンプレートの名前とモデルとして使用するオブジェクトを含むModelAndViewオブジェクトを返さなければなりません。
@Get()
ModelAndView home() {
return new ModelAndView("home", CollectionUtils.mapOf("currentView", "home"));
}
ビューは、おなじみの${variable}構文で、任意のモデル変数にアクセスできます。
フロントエンド
フロントエンドを複雑にする必要はありませんでした。
私が必要としていたのは、現在のセンサーデータの全体像を素早く把握できるような方法でデータを表示することであり、
シンプルでありながら反応の良いレイアウトでそれを達成できたと思っています。
ホームビューは、先に設定したWebSocketサーバーのエンドポイントに接続し、新しいメッセージを受信すると、
メモリ内の読み取り値のリストを更新します(最新の50個の読み取り値に限定されます)。
const connect = () => { console.log('Connecting to WebSocket...') const ws = new WebSocket("ws://" + location.hostname + ":" + location.port + "/data/greenthumb"); ws.onopen = (msg) => { console.log('Connected!') }; ws.onmessage = (msg) => { const reading = new Reading(JSON.parse(msg.data)); soilTempReadings.push({y: reading.soilTemp, x: reading.readAt}); // keep the latest <code class="code-inline">maxPoints</code> if( soilTempReadings.length > maxPoints ) soilTempReadings.shift(); if (chartsInit) soilTempChart.update(); // update the table that displays the latest values document.querySelector('#currentOutletState').innerHTML = reading.outletState; document.querySelector('#currentAirTemp').innerHTML = reading.airTemp; document.querySelector('#currentSoilTemp').innerHTML = reading.soilTemp; document.querySelector('#currentHumidity').innerHTML = reading.humidity; document.querySelector('#currentMoisture').innerHTML = reading.moisture; document.querySelector('#currentLight').innerHTML = reading.light; }; ws.onclose = (e) => { console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); setTimeout(function() { connect(); }, 1000); }; ws.onerror = function(err) { console.error('Socket encountered error: ', err.message, 'Closing socket'); ws.close(); }; }; |
レポートでは、集約されたセンサーデータの様々な表示を1つのページで出力します。
例えば、今日の時間帯別の平均値を見ることができるので、いつもと違う状況になったときに必要な調整を行うことができます。
また、すべての時間帯の時間別平均を見ることで、長期的な成功を測ることができますね。
あるいは、日ごとの平均値を見ることでも。
もちろん、"昼 "と "夜 "では異なる目標を設定しているので、それらの指標に対する進捗状況を示すレポートも必要です。
そして最後に、センサーデータの全体的な平均値です。
ビルド (GitHubアクション)
もちろん、ビルドプロセスを自動化しなければ、プロジェクトは完成しません。
そのために、GitHub Actions ワークフローを追加しました。
このワークフローでは、コードをチェックアウトしてから JAR ファイルをビルドします。
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: '11.0.3'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assemble
その後、Oracle Cloud Infrastructure RegistryにログインしてDockerイメージを構築し
(Micronautが提供するアウトオブボックスのDockerfileを使用)、そのDockerイメージをOCIRにプッシュします。
- name: 'Login To OCIR'
uses: actions-hub/docker/login@master
env:
DOCKER_USERNAME: ${{ secrets.OCIR_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.OCIR_PASSWORD }}
DOCKER_REGISTRY_URL: phx.ocir.io
- name: 'Docker Build'
run: docker build -t phx.ocir.io/[redacted]/[redacted]/greenthumb-client:latest .
- name: 'Docker Push'
uses: actions-hub/docker@master
with:
args: push phx.ocir.io/[redacted]/[redacted]/greenthumb-client:latest
最後に、私のVMにログインし、既存のDockerイメージを停止し、最新のイメージをVM上に引き出して実行します。
- name: 'Deploy Container' uses: appleboy/ssh-action@master with: host: [redacted] username: opc key: ${{ secrets.VM_PRIV_KEY }} script: | echo ${{ secrets.OCIR_PASSWORD }} | docker login phx.ocir.io --username [redacted] --password-stdin docker stop project-greenthumb docker rm project-greenthumb docker pull phx.ocir.io/[redacted]/[redacted]/greenthumb-client:latest docker run -d --name project-greenthumb --restart=always --env MICRONAUT_ENVIRONMENTS=oraclecloud --env MQTT_CLIENT_USER_NAME=${{ secrets.MQTT_CLIENT_USER_NAME }} --env DATASOURCES_DEFAULT_USERNAME=${{ secrets.DATASOURCES_DEFAULT_USERNAME }} --env MQTT_CLIENT_PASSWORD=${{ secrets.MQTT_CLIENT_PASSWORD }} --env DATASOURCES_DEFAULT_OCID=${{ secrets.DATASOURCES_DEFAULT_OCID }} --env MQTT_CLIENT_CLIENT_ID=${{ secrets.MQTT_CLIENT_CLIENT_ID }} --env DATASOURCES_DEFAULT_PASSWORD=${{ secrets.DATASOURCES_DEFAULT_PASSWORD }} --env MQTT_CLIENT_SERVER_URI=${{ secrets.MQTT_CLIENT_SERVER_URI }} --env DATASOURCES_DEFAULT_WALLET_PASSWORD=${{ secrets.DATASOURCES_DEFAULT_WALLET_PASSWORD }} -p 8080:8080 -p 80:80 phx.ocir.io/[redacted]/[redacted]/greenthumb-client:latest |
プッシュ通知アラート
大量のデータを収集しても、そのデータが必要と判断したときに自動で行動を起こさなければ意味がありません。
このプロジェクトに自動給水機能を追加することは簡単でしたが、私はこのような栽培に慣れていないので、
慣れるまでは手動でコントロールしたいと考えていました。
そこで、Pushoverを使ってプッシュ通知を追加し、土壌の水分量から苗に水をやるべきだと判断したときに通知を受け取れるようにすると、とても便利だと思いました。
Pushoverアカウントと統合するために、pushover4jライブラリ(サイドノート:"4j "プロジェクトはもうたくさん!)を追加することもできましたが、
APIへのPOSTリクエストだけなので、別の依存関係を追加することは避け、Micronautを使った宣言型httpクライアントを使用することにしました。
まず、Pushoverの設定を行います。
codes:
recursive:
pushover:
userKey: ${CODES_RECURSIVE_PUSHOVER_USER_KEY}
apiKey: ${CODES_RECURSIVE_PUSHOVER_API_KEY}
次に、APIレスポンスを格納するPOJOを作成しました。
@Introspected public class PushNotificationResponse { private int status; private String request; public PushNotificationResponse(int status, String request) { this.status = status; this.request = request; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getRequest() { return request; } public void setRequest(String request) { this.request = request; } } |
最後に、クライアント・インターフェースを作成しました。
Micronautはコンパイル時に必要な配管をすべて処理し、クライアントはアプリケーションで使用する準備ができています。
@Client("https://api.pushover.net")\\\\public interface PushoverClient { @Post(value = "/1/messages.json", produces = MediaType.APPLICATION_FORM_URLENCODED, consumes = MediaType.APPLICATION_JSON) Flowable<PushNotificationResponse> pushMessage(String token, String user, String message, String url); } |
そして、クライアントをMQTTコンシューマーに注入し、メッセージを受信したときに測定値をチェックし、
土壌の水分量が50%以下になった場合にはプッシュ通知(20分に1回にスロットル)を送信しました。
もちろん、メッセージの引数を適切に変更するだけで、必要に応じて他の測定基準や閾値に拡張することができます。
int soilMoisture = (int) reading.getReadingAsMap().get("moisture"); if( (System.currentTimeMillis() - lastAlert > interval) && soilMoisture < 50) { PushNotificationResponse response = pushoverClient.pushMessage( apiKey, userKey, "Soil Moisture Alert! Current moisture: " + soilMoisture, "http://[redacted url]:8080/page" ).blockingFirst(); lastAlert = System.currentTimeMillis(); } |
私のPixel3 XL端末での通知の様子です。
通知をクリックすると、ダッシュボードに直接アクセスできるリンクが表示されます。
進捗状況(結果)について
苗を植えてから3週間が経過しました。
システムに最適な方法を学びながら、ハードウェアとソフトウェアの両方に若干の調整を加えていますが、
これまでのところ、結果はほぼ範囲内(あるいは確実に目標に極めて近い)に収まっています。
夜/日や日/時間の内訳を無視して)「全体」から見た場合。
しかし、データは物語の半分しか語っていません。
本当に重要なのは、苗が発芽して元気な姿を見せているかどうかという結果です。そのためには
まとめ
この話は今日で終わりではなく、今年の後半に結論が出たときには、その成果を数値化するのはもっと難しくなるでしょう。
私にとってこのプロジェクトの成功は、最終的に出来上がるホットソースの味と辛さにかかっていますが、
味は本当に主観的なもので、製品を判断する人の好みに左右されます。
しかし、このプロジェクトの成功を測るには、もうひとつの方法があると思います。
それは、体験そのものの価値を見ることです。そう考えると、このプロジェクトはすでに大きな成功を収めていると言えるでしょう。
なぜなら、私が毎日一緒に仕事をしている開発者のコミュニティで、何かを計画し、構築し、そこから学び、共有することができたからです。
このブログシリーズで使用したコードをご覧になりたい方は、GitHubの適切なリポジトリを参照してください。
コメント
コメントを投稿