畑の監視その後
前にIoTで畑を監視するという内容の記事を別のブログで書いたのですが、バージョンアップしたのでその辺りを個人ブログで紹介しようかと。 tech.recruit-mp.co.jp
全体像
新しくやったこと
- 無停電キットの導入
- カメラによる監視の追加
- アプリのオートアップデート
- 静止画からタイムプラス動画を生成する
最新版のソースはこちら
https://github.com/sparkgene/iot_farm_monitoring
無停電キットの導入
東京デバイセズが出しているソーラーUSB電源システムキット
がソーラーで発電しながら蓄電でき、USBで5vを簡単に取り出せるすぐれものです。
これを使えば、日中に発電して夜間も動作させることが可能です。
カメラによる監視の追加
センサーだけの監視ではなく、肉眼で畑の様子を見れたほうが便利だなと思ったので、カメラをセットアップしました。 ライブ配信だとsoracomの利用料がとんでもないことになりそうなので、静止画を撮ってアップロードする方式にしました。 使ったカメラモジュールはこれ。
公式のカメラモジュールで、静止画、動画の撮影が出来、専用のコマンドも用意されているのでものすごく簡単に使うことが出来ます。
raspistill -o image.jpg
たったこれだけで、静止画が撮れます。
さすがに、標準の解像度だとファイルサイズが大きくなりすぎるので、-w 1280 -h 720
とオプションを指定してサイズを抑えます。
撮影した静止画は、S3にアップロードして保存するようにしています。 AWS IoTを使って画像ファイルをbase64化して送ることも考えたのですが、あまりメリットが無さそうなので、aws cliで普通にアップするようにしています。 この場合は、awsのクレデンシャル情報が必要となるので、最低限の権限を付与したACCESS KEYを使っています。
アプリのオートアップデート
実際に畑を監視するときは遠隔地にデバイスを置くことも考えられるので、バグを修正したい時とか、新しい機能を追加したい時に気軽に触ることが出来ません。 また電源節約のため、Raspberry Piは常時ネットに接続していないので、sshで入ったり直接操作することが出来ません。 そこで、非同期でRaspberry Pi側からソースのアップデートができるようにする必要があります。
AWS IoTのDevice Shadowを利用することで、Raspberry Piがオフラインであっても次にオンラインとなった時に、設定した値を読み取ることができ、それをトリガーとしてgithubから最新のソースを取得して反映させることができるようになります。
https://github.com/sparkgene/iot_farm_monitoring/blob/master/iot_shadow.js#L31-L38
Device Shadowのソースはこんな感じでチェックしており、ローカルにあるソースのバージョンとDevice Shadowに設定されているバージョンを比較して、違う場合はgit cloneしてソースを入れ替える様になっています。
静止画からタイムプラス動画を生成する
静止画で畑の様子を見れるようにしたのですが、動画で一日の様子を見れるようにしたほうが面白いなと思い、S3にアップロードされてくる画像を元に動画を生成するようにしました。
qiita.com タイムプラス動画を作る方法はQiitaの方に上げてあるので、詳しいことはそちらを参考にしてもらえればと。
畑に置いた
さすがに畑の土真ん中に置くのは邪魔になるので道具小屋の所に置きました。
収集されるデータ
soracom → AWS IoT → Lambda → CloudWatchと流れてくるセンサーのデータ あれ、、水分の値が全然拾えてない(と言うか、小屋の脇だとカラッカラに乾燥してるのが原因)
タイムプラス動画
Raspberry Piからアップロードされてくる画像を元に、Lambdaが動画を生成してくれました。 設置した時はカメラが逆さまだったので、最初の方は逆さまですw しかも設置中の自分も写ってしまってる。。
今回の反省
24時間稼働してくれない
無停電キットで24時間運用できるようになったはずでしたが、想定よりも消費電力が大きすぎて発電が追いつかず、バッテリーが空になって2日目には動かなくなった。。。 もっと大きなソーラーパネルに置き換えれば解決しそう。
カメラモジュールの防水対策
オフィシャルなカメラはフラットケーブルでRaspberry Piと繋げるのですが、カメラ基盤がむき出しだったりと、屋外での利用に向いていない。仮で、基盤自体をバスボンドで防水加工してみたけど、ちゃんとしたケースが欲しい。あと、カメラの向きを簡単に調整できるようなマウントも欲しい。 リモートから角度調整とかもできると最高。
AWSの進化の速さ
AWS IoTも日々進化しており、当初これを作った時にはなかったのですが、現在はAWS IoTのRuleのActionとしてCloudWatchのメトリックスが追加されたので、このブログのサンプルのようにLambdaを経由する必要がなくなりました。 ただし、現状csvで送っている形式を見なおす必要はありそうです。
さいごに
家で実験している時に気になってたことが、そのまま実際の畑に持って行ったら、やっぱりダメだった。 そのまま置いてくるつもりでいたけど、見なおして出直す必要がありますね。
JAWS DAYS 2016
今年も開催されました! jawsdays2016.jaws-ug.jp
前回の参加記事はこちら sparkgene.hatenablog.com
昨年と同様、今年もHackDayのスタッフとしてお手伝いしました。 ただし、今年は当日スタッフとしてではなく、事前準備からHackDayの開催に向けた準備をお手伝いしました。 事前申し込みが1500名、当日参加が1100名を超えるような、巨大なイベントが出来上がっていくのを見れたのは非常に楽しかったです。 大きく7トラックのセッションがあり、それ以外にもスポンサーのブースが有ったり、SAに相談できたり、カルタ、麻雀など、盛りだくさんのコンテンツでした。 今年はイアホンの貸出もあり、スピーカーの声がよく聞こえて評判はかなり良かったようです。 中には、参加中のセッション以外のチャンネルを聞くという2度美味しい楽しみ方をしている人もいたようです。
キーノートの様子。 アンケートを取ったら初参加の人が6〜7割と、幅広いユーザーが参加したのも今年の特徴なのかなと。
それにしても、これほどのイベントとなると、殆どシステムの開発と同じような感じですね。 しかも、100%リモートワーク。
さまざまな会話がSlack上で行われ、やらなければならないことはチケットで管理され、たまにオフラインで集まって調整といった形は、普段の業務と変わらないです。 そして当日は、参加者が快適にイベントに参加できるように、スタッフそれぞれがその場で判断したり、相談したりして運営していくのは、リリースしたサービスの障害対応や、エンハンスに近いとも思います。
HackDay
初級編
HackDayは初級編と中級編の2つのコンテンツを用意しました。
初級編は満席で、OpenBlocks Iot + SORACOM + AWS IoT + Elasticsearch Serviceを使った、センサー情報の収集〜ビジュアライズまでを体験できるセッションでした。 ビジュアライズまでやってるのに、全部マネージドサービス使っているので、サーバレスという最強の環境です。
中級編
中級編ではMaBeeeをAWS IoT〜OpenBlocks経由で操作するというセッションでした。 ネットワークの調子が悪く、結局完走出来たのは一人だけと非常に申し訳ない結果となりましたが、一人でも完走者が出て良かったです。 MaBeeeは6月頃に発売されるようですが、すごく面白そうなので手にれたいと思います。
恒例のLT大会
一通りセッションが終わると、LT大会が始まります。とてもLTとは思えないような凝ったものもあり、ビールを飲みながら聞くのが楽しいです。 一日中チューターとして立っていたので、このタイミングでやっと椅子に座れましたw 案の定、翌日は筋肉痛です。。。
打ち上げ
去年とちがって公式な打ち上げはなかったので、有志でふらふらっと飲みに行きました。 ギネスビールとか飲みまくって、楽しく飲みました。(さすがにHubへいく元気はなかった)
11時月頃から手伝い始めてあっという間の4ヶ月でした。 次回のJAWS DAYSはどんなイベントになるか楽しみです!
sphero + leap motion + gobot = フォースの覚醒
BB-8欲しいですね!ただ、結構高い…
けど、これってspheroがベースなんですよね。 mashable.com
色々ググってると。すでに先人がいました makezine.com
そんなわけで、たまたま手元にあったspheroを改造して、手の届かないBB-8を自作してみることにしました。
まずは、つなぎ目があるのでそれに沿って切ります。 そして、外殻のギリギリにネオジム磁石が来るように、ガチャポンの入れ物を切って基盤の上に載せます。 ガチャポンを基盤に固定する必要があるので、グルーガンで固定しちゃいました。
磁石が固定できたら、半分に割ったspheroの殻を内側に段差ができないように組み合わせて、接着剤でくっつけます。
頭の部分はユザワヤで買ってきた、発泡スチロール球を半分に切って使います。
頭の底の部分には、磁石にくっつけるために金属製のクリップをノリ付けします。
色は白のままですが、とりあえず組みあがりました。BB-8の色は難しいから、 うーむ、実物と比べるとちょっと頭でっかちになってしまった… しかも、目玉の部分とか、アンテナが無いとただの雪だるまみたいだな。
Gobotでleap motionとspheroBB-8を制御
Gobotを使ってleapmotionでBB-8を操作してみようと思います。
package main import ( "fmt" "github.com/hybridgroup/gobot" "github.com/hybridgroup/gobot/platforms/leap" "github.com/hybridgroup/gobot/platforms/sphero" ) func main() { gbot := gobot.NewGobot() leapAdaptor := leap.NewLeapMotionAdaptor("leap", "127.0.0.1:6437") spheroAdaptor := sphero.NewSpheroAdaptor("Sphero", "/dev/tty.Sphero-OBG-AMP-SPP") leapDriver := leap.NewLeapMotionDriver(leapAdaptor, "leap") spheroDriver := sphero.NewSpheroDriver(spheroAdaptor, "sphero") work := func() { cnt := 0 gobot.On(leapDriver.Event("message"), func(data interface{}) { hands := data.(leap.Frame).Hands heading := 0.0 if len(hands) > 0 { if cnt > 20 { x := hands[0].Direction[0] if x > 0 { // This means 0 degree. sphero move to forward heading = 0 } else { // This means 180 degree. sphero move to backward heading = 180 } fmt.Println("heading:", heading) spheroDriver.Roll(60, uint16(heading)) cnt = 0 } else { cnt++ } } }) } robot := gobot.NewRobot("leapBot", []gobot.Connection{leapAdaptor, spheroAdaptor}, []gobot.Device{leapDriver, spheroDriver}, work, ) gbot.AddRobot(robot) gbot.Start() }
アダプターとドライバーにそれぞれleap motionとspheroを指定して、leapmotionから手の位置を取得してそれを元に動かすスクリプトです。
spheroは0〜360の数値を渡してあげるとその方向(0=前、90=右、180=後ろ、270=左)に動くので、本来であればleap motionから2軸分のデータを使って操作したほうがうまく動かせると思うんですが、座標と数値がちょっとめんどくさそうだったので手抜きしました。。 leap motionの座標
実際に動かしたムービーはこちら。
本物と比べると頭の位置が安定しないです。しかもすぐ取れちゃう。頭側も磁石にしたほうが安定するかな。 そんな訳で、スター・ウォーズの新作公開日に間に合って良かったw
Raspberry Piに RASPBIAN JESSIEをインストールする
久しぶりにRaspberry Piを使おうと思い、ついでに最新のOSを入れてやれってことで、作業メモ。
ダウンロード
ここからダウンロードする
Download Raspbian for Raspberry Pi
普通にZip形式のほうをダウンロードすると残り1時間とか出るので、TorrentファイルをダウンロードしてMac用のBitTorrentクライアントを使ってダウンロードした。 これだと、数分でダウンロードが終わる。
SDカードの初期化
ディスクユーティリティーを使ってFat形式で初期化する。
一旦ボリューム名を JESSIE
で作りました。
JESSIEをSDカードに書き込む
$ df Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on /dev/disk0s2 488555536 477290304 10753232 98% 59725286 1344154 98% / devfs 372 372 0 100% 647 0 100% /dev map -hosts 0 0 0 100% 0 0 100% /net map auto_home 0 0 0 100% 0 0 100% /home localhost:/umin04NXvaENoWAQ_HarOu 488555536 488555536 0 100% 0 0 100% /Volumes/MobileBackups /dev/disk1s1 15542528 5056 15537472 1% 0 0 100% /Volumes/JESSIE
関係ないが、メインのストレージのinodeが98%消費してるじゃないか。。
一度アンマウント
$ sudo diskutil umount /Volumes/JESSIE/
ダウンロードして解答した 2015-09-24-raspbian-jessie.img
があるディレクトリに移動して、以下のコマンドを実行する
/dev/disk1s1
はdfコマンドで調べた時の値の disk1s1
の頭に r
を付け、s1
を消した /dev/rdisk1
を指定する
$ sudo dd bs=1m if="./2015-09-24-raspbian-jessie.img" of="/dev/rdisk1" 4125+0 records in 4125+0 records out 4325376000 bytes transferred in 410.162326 secs (10545522 bytes/sec)
これでOSの準備ができたので、HDMI、LAN、USBキーボード、USBマウス、micro USB(電源)を繋いで起動します。
初期設定
初回はGUIのデスクトップが表示されるので、リモート接続できるようにSSHを有効にします。
メニューの Preferences > Raspberry Pi Configuration から設定出来ます。 (コマンドラインからも設定できるけど、せっかくマウス繋いだので)
次に、ターミナルを起動してIPアドレスを調べます。 IPアドレスが分かったら、リモートからSSHで入って、以降の作業はリモートで行います。
ユーザーは pi
パスワードは raspberry
で入ります。
ssh pi@192.168.0.13 pi@192.168.0.13's password: The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Sun Nov 8 10:31:30 2015
sudo raspi-config
と入力して設定モードを起動します
ファイルシステムの拡張
初期状態だとSDカードのサイズが大きくても2Gでパーテションが作られているようなので、Expand Filesystem
のメニューを選んで、拡張します
ブート後はCUIモードで起動
リモートで繋いで使うのでGUIは使わないから、起動後はCUIモードになるように設定します。
Boot Options
を選んでCUIを指定します。
以上で、初期設定が終わりました。
最近はEdisonを触ることのほうが多いので、久しぶりにイジってみたら、OSのインストールやら、初期設定画終わるところまでは、結構大変(USBキーボード、USBマウスが必要だったりと)ですね。。。
Amazon EchoとAmazon Alexaに感じる可能性
re:Invent 2015AWS re:Invent 2015に参加してきました。
(参加エントリーは会社ブログにいっぱい上げたけど、こっちにはまだ書いてないので、そのうち書く)
以前から気になっていてqiitaにも書いたのですが、re:InventのセッションでもAmazon EchoとAmazon Alexa系のものに参加してきました。
何に興味があったかというと、Lambdaファンクションを使うことで、自由にAlexa Skill(機能)をAmazon Echoに足せるということです。 しかも、このSkilは公開することができ、他の人も自分のAmazon Echoで使うことが出来ます。
つまり、自分が作ったサービスを音声で利用することが可能になるということですね。
ちなみに、Amazon Echo 自体は大きめの水筒ぐらいのサイズで、机の上にもちょこんと置けるような感じです。
参加したセッション
- WRK308 - AWS + ASK: Teaching Amazon Echo New Skills
- MBL308 - Extending Alexa’s Built-in Skills – See How Capital One Did It
AWS + ASK: Teaching Amazon Echo New Skills
このセッションはワークショップ形式のもので、冒頭から「通常は5時間使って行うものを2時間でやります!頑張ってついてこいよ!」と言われました(苦笑)
ワークショップではサンプルを使って、Alexa Skillの作り方、Amazon Echoへのひも付けを駆け足で説明した後、自分たちでSkillを作ってみましょうとなりました。
サンプルをAmazon Echoで試してみるとこんな感じで、自分の好きな色を質問してもらい、それを答えると覚えてくれて、好きな色を聞くと答えてくれるものです。
Lambdaのファンクションの作成から登録を一通り学べるワークショップでしたので、これで自作のSkillを作ることが可能になります。
Extending Alexa’s Built-in Skills – See How Capital One Did It
Capital One社が実際にAmazon Echoを使って自社のアプリケーションと連携して、カードの利用情報を確認するといったSkillを作った際の話を聞きました。
www.slideshare.net
アプリのアカウントとAmazon Echoのひも付け方法など、実際に自社サービスを連携させる際のワークフローの参考になる話も聞けました。
セッションの中での興味深かったのは、Webやアプリと違い音声認識向けのUI設計が必要だという話でした。
Webであればたくさんの選択肢をラジオボタンやドロップダウンで1つだけ選択させ、次のアクションに進めることが出来ます。 しかし、音声の場合はすべての選択肢を列挙しても、ユーザーはすべてを聴き終えた頃には最初の話を忘れてしまうこともあるので、何を選んだら良いか分からなくなるとのこと。 最も選ばれる3つぐらいの候補を最初の選択肢として挙げておき、それ以外はヘルプを呼び出させることで、全部列挙すると言ったUI設計が必要になるということです。
また、Webであれば読込中をインジケーター等で、視覚的に状態を知ることが出来ますが、音声の場合は処理中はずっと無音となるので、ユーザーからすると何が起きているかが分かりにくく、使いづらいと思われてしまいます。 その為にはすぐレスポンスを返せるように作らなければなりません。
今後の可能性
Alexa SkillはLambdaでも自分のサーバでも提供することが出来ます。 自分のサーバで提供する場合はhttpsが必須となりますが、すでにnode moduleとして作っている人がいたりするので、既存のシステムに連携させることも比較的簡単に出来ると思います。
DevOpsであれば、音声でサーバを増やしたり、サービスのステータスを教えてもらうことも出来ます。 今日の献立に悩んだ時に、おすすめの料理を教えてもらうことも可能です。
Amazon Fire TVではすでに、Amazon Alexaを使って音声でビデオを選んだりすることが可能になっています。
ただし、Amazon Echoはまだ英語にしか対応していません。 シアトルでのスペシャルセッションでは、ドイツ語、イギリス英語への対応は進めているとの話はありました。 ワークショップでは是非日本語も対応してとお願いしておきましたw
速く国内で発売して欲しいですね。
最後に
Amazon Echoの可能性はこの動画からも感じ取れると思います。
nginx-rtmp-moduleでお試しLive配信環境を作る
USTREAMやYouTube Liveの様に、リアルタイムで動画を配信しながら視聴できる一連の環境づくり&設定についてまとめてみた。
(お試し環境なので、同時接続が辛いとか色いろあります)
配信サーバの準備
Real Time Messaging Protocol (RTMP) を扱うために、オープンソースで公開されているnginx-rtmp-moduleを使います。
今回は映像を受信して、配信するサーバは同じサーバとして、EC2(Amazon Linux)上に用意して試します。
sudo su yum update yum groupinstall "Development Tools" yum -y install pcre-devel zlib-devel openssl-devel cd /usr/local/src/ wget http://nginx.org/download/nginx-1.8.0.tar.gz sudo wget http://nginx.org/download/nginx-1.8.0.tar.gz tar -zxvf nginx-1.8.0.tar.gz wget https://github.com/arut/nginx-rtmp-module/archive/master.zip unzip master.zip
build
cd nginx-1.8.0 ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-master make make install
nginxの設定ファイルにrtmpの設定を追加
/usr/local/nginx/conf/nginx.confの一番最後に以下を追加
rtmp { server { listen 1935; chunk_size 4096; application live { live on; record off; } } }
applicationで指定する名称がURLの一部になります。
上記の設定だとURLは、rtmp://
設定ができたら、nginxを起動します。
/usr/local/nginx/sbin/nginx
この状態でブラウザからhttp://
配信サーバへ映像を送るクライアントを準備
本格的に配信をする人ならLiveShell Proとか使って配信するかもしれませんが、今回はお試しなのでPC上でキャプチャ出来て、配信サーバにアップロードできるオープンソースのソフトを使います。
Mac版をダウンロードして起動します。
起動するとこんな感じの画面が表示されると思います(デフォルトではデスクトップをキャプチャする)
配信サーバの指定
右下のボタンの設定をクリックして設定画面を開きます。
次に設定画面の左側の配信を選択します。
配信種別でカスタムストリーミングサーバを選択して、URLに準備したサーバのURLを指定します。
ストリームキーには何らかの文字列を指定します(適当な文字列でもOK)
ここで指定したストリームキーは後で映像を試聴する際のURLで使われます。
映像キャプチャの設定
OBSの最初の画面でソースとなっているコントロールで「+」をクリックすると、追加するソースが表示されるので、映像キャプチャデバイスを選択します。
開かれたダイアログで、新規作成のままOKをクリックすると、Mac Book Proの場合はFaceTime HD Cameraが選べるので、それを選択すると、カメラからの映像が表示されます。
OKをクリックして設定を終了します。
この状態だとソースが画面のキャプチャと映像デバイスの2箇所となっているので、ソースで不要な方を選んで「ー」をクリックして削除します。
プレイヤーの準備
配信の準備はできたので、最後にプレイヤーを準備します。
video.js v4.2.0からrtmpに対応(beta)したので、こちらを使います。
jsファイルはCDNにホストされたものを使うので、プレイヤーと言っても以下の様なHTMLファイルを準備するだけです。
<!DOCTYPE html> <html lang="en" class=""> <head> <link href="http://vjs.zencdn.net/4.2.0/video-js.css" rel="stylesheet"> <script src="http://vjs.zencdn.net/4.2.0/video.js"></script> </head> <body> <video id="rtmp live test" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264" data-setup='{}'> <source src="rtmp://<ip address>/live/test" type='rtmp/mp4'> <p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a> </p> </video> </body> </html>
srcに指定したURLのtestは、先ほどOBSのストリームキーに設定した文字列を指定します。
このファイルをS3など、どこかのサーバに置いて、ブラウザで開きます。
配信
OBSで配信開始ボタンをクリックすると、配信が始まります。
プレイヤー側のブラウザで再生のアイコンをクリックして少し待つと、Live配信されている動画が表示されます。
(AU光のネットワークで、配信とプレーヤーのPCを分けてテストしたところ、遅延は3secぐらい)
あとがき
以上で、お試しのリアルタイムの動画配信環境の構築とテストが出来ました。
実際にnginxのサーバがどれくらいの同時視聴に耐えられるかはわかりませんが、ブラウザ3つぐらい開いて試聴するぶんには、負荷の変化もさほどなく遅延も変わらない感じでした。
Grafana + GraphiteでAnnotationsを使ってイベントを可視化する
GrafanaにはAnnotationsという、イベントをグラフ上へ表示する機能があります。
http://grafana.org/docs/features/annotations/
Annotationsの取得元はいくつか利用可能ですが、データソースとしてGraphiteのEvent
を使う方法について解説します。
Event
GraphiteにはEventと言う情報を保存する仕組みがあります。
しかし探してみても、データを格納する方法については公式ドキュメントで見つからない。
ただ、データの取り出し方については書かれています。
http://graphite.readthedocs.org/en/stable/functions.html?highlight=event#graphite.render.functions.events
こちらのブログで登録方法が書かれているので、そちらを参考に進めます。
Eventの登録
最低限のデータで登録する場合は、以下の様にデータをPostしてあげればいいです。
curl -X POST "http://graphitehost.com/events/" -d '{"what": "fugafuga", "tags": "hoge"}'
Eventの情報が格納されるテーブルはevents_eventで、このテーブルの構造を見ると以下のように定義されてます。
CREATE TABLE "events_event" ( "id" integer NOT NULL PRIMARY KEY, "when" datetime NOT NULL, "what" varchar(255) NOT NULL, "data" text NOT NULL, "tags" varchar(255) NOT NULL );
テーブル構造から分かるように、さらにwhenとdataを指定することが可能です。
- whenは、デフォルトでシステム日付が設定されます
- dataは、デフォルトでからが設定されます
例えばこんな感じで
curl -X POST "http://graphitehost.com/events/" -d '{"what": "Deploy apps 5", "tags": "deploy", "data": "ここには長い文章が指定できます。"}'
Grafanaの設定
Graphiteに登録されているEventをグラフ上に表示するには、Dashboardの設定から登録します。
Dashboardで設定するように、グラフ単位では指定できないみたいです。
- Name: Annotationの名称
- Datasource: Graphiteのデータソース
- Graphite event tags: イベントを登録した時のtag名
先ほどのdataに指定した場合は、以下のように表示されます。
Dashboardには複数のAnnotationが登録でき、チェックボックスのON/OFFで表示非表示の切り替えができるので、デプロイ、サーバ追加と言った複数のイベントを登録して表示することで、どのような出来事がきっかけでリソースに変化があったかが一目瞭然となります。