Sensorデータをデータベースに蓄積・活用する
7月3日のオープンソースセミナー2010@愛媛でお見せした、センサーデータをWindows 7 Sensor & Location Platform APIで取出し、ADO.NET Entity Data Modelで作成したSQL Serverのデータベースに格納・格納したデータをグラフや3Dモデルで見せるデモの、Entity Data Modelの部分について詳細を紹介します。
これまで、いろんな機会で紹介してきたセンサーデモですが、今までのものは、Sensor APIから取得した情報を元にWPFの各種コントロールを制御するものでした。センサーデータを取得する方法は全く同じです。https://msdn.microsoft.com/ja-jp/windows/ff432707.aspx で紹介しているように、Windows 7 API Code Packを通じて取得します。
デモアプリは、“LCASensorCloud”という名前でWPFアプリケーション(C#)を一つ作っておき、Windows 7 API CodepackのCore、Shell、Sensorsを参照として追加しておきます。
※私の環境はVisual Studio 2010の英語版が入ってます。メニューやコマンドなど適宜日本語への読み替えをお願いします。
取得したデータを格納するデータベースが必要になる訳ですが、次の手順で作っていきます。
- SQL Server Management Studioで、“LocationContextSensorDB”という名前のデータベースを新たに作成。
- Visual Studio 2010で“LCASensorCloud”プロジェクトに、データのADO.NET Entity Data Modelテンプレートを使って、新規項目を追加する。名前は”LCADataModel.edmx”としておきましょう。
- Entity Data Model Wizardで、”Empty Model”を選択し、”Finish”ボタンをクリック。
すると、空のEntity Data Model Designer(いかにもDSL Toolkitで作りましたといった感じ)が開きます。蓄積したいSensorのデータ、及び、関連データを蓄積するためのEntity Modelを作成します。ツールボックスのEntityを配置、Entity間のAssociationを定義していきます。デモでは、下図のようなEntity Modelを作成しています。定義の要領は、Shlaer-Mellor法のクラスモデルの作り方と同じ。(関連クラスがないぐらいか)
一応説明しておくと、
エンティティ | 説明 |
Device | センサーが装着されているPCや機器 |
Sensor | Deviceの各種環境情報を計測するセンサーデバイス、Deviceには複数のセンサーが装着されてよい。 SensorIdは、センサーデバイスの一意のID |
SensorSpec | Windows 7 Sensor & Location Platformで規定されているセンサーのタイプ。このEntity Instanceは手入力。 SensorのEntityはこのエンティティを参照として一つ関連付けられ、そのSensorデバイスがどんな種類のSensorかがわかる。 ※Executable UMLのレシピパターン |
SensorPropertySpec | Windows 7 Sensor & Location Platformで規定されているセンサープロパティ。 EDMでは複合型が使えるので、PropertyIdTypeという型(Guid値とInt値)を定義し、PropertyIdの型としている。 |
MeasuredSession | あるDeviceのあるSensorによるひとまとまりの測定区間。 |
MeasurePoint | あるMeasuredSessionがターゲットとするSensorのMeasuredSessionに属する計測点。計測した時間を保持 |
MeasuredProperty | MeasurePointのSensor計測値。SensorPropertySpecと関連を持つことにより。どのSensorのどのPropertyなのかを示す。 一つのセンサーで一度に複数の計測値を上げてくるセンサーデバイス向けに、MeasurePointから見て多になっている。 |
といったところ。
モデルを理解したところで、次にSQL Server上のデータベースにテーブルを生成します。手順は以下の通り。
- Entity Data Model Designerビューの白地で右クリック→”Generate Database from Model..."を選択
- データコネクションを選択し”Next”をクリック。一覧に冒頭で作ったLocationContextSensorDBが表示されない場合は、New Connection…をクリックし、Server NameにSQL Serverのサーバー名を入力して、下の方のデータベースコンボボックスから”LocationContextSensorDB”を選択し、”OK”ボタンをクリック
- テーブルを定義するためのSQLのDDLスクリプトが生成され、一応内容をざっと見て、”Finish”をクリック。
- 生成されたSQLファイルを表示し、Visual Studioのメニューの下図のパートで、左からデータベースに接続(Click&Select)、ターゲットデータベースの選択(コンボボックスから選択)、SQLの実行(アイコンクリック)を順に実施
これで、SQL ServerのLocationContextSensorDB上にEDMモデルで定義されたEntityがテーブルとして生成されます。更に、このEDMモデルのセマンティクスのレベルでデータにLINQでアクセスするためのManagedライブラリが生成されます。アプリケーションでは、このライブラリを使用します。
デモアプリでは、予め、SensorSpecとSensorPropertySpecのEntityは、SQL Server Management Studioで手入力(当日は、Accelerometer3D、Ambient Light、Human Presence、Brainwaveの四種類)しておきました。センサーから取得したデータを、EDM層を通じて書き込めば、センサーデータを蓄積する側のコードの出来上がりです。
Ambient Lightの場合で、このコードを例示します。
まず計測開始時のコード:
AmbientLightSensor al = ...; // Windows 7 API Codepackのクラス using (var entityContainer = new LocationContextDataModelContainer()) // EDM Toolが生成したデータベースコンテナ { // 登録済みのSensorSpec、SensorPropertySpecを検索 var sspecs = from sspec in entityContainer.SensorSpec where sspec.TypeName.CompareTo("AmbientLight")==0 select sspec; SensorSpec alSpec = sspecs.First(); SensorPropertySpec alPropSpecLux = alSpec.SensorPropertySpec.First(); // Sensorインスタンスを作成 ※このクラスはEDM Toolが生成したクラス。 // ※Win 7 API Codepackのものではないことに注意 Sensor alSensor = new Sensor(); alSensor.SensorSpec = alSpec; alSensor.Device = targetDevice; // targetDeviceインスタンスはDeviceエンティティのインスタンス。予め作成しておく ... entityContainer.AddToSensor(alSensor); entityContainer.SaveChanges(); // ここでIdの値が確定 // 計測セッションを作成 MeasuredSession session = new MeasuredSession(); session.Device = targetDevice; session.Sensor = alSensor; entityContainer.AddToMeasuredSession(session); entityContainer.SaveChanges(); |
LocationContextDataModelContainerやSensorSpec、SensorPropertySpec、Sensor等は、先ほど図示したEntity Modelから生成されたクラス群です。
こんな感じで、SQLを知らなくても、データベース上のクエリや、レコード生成が可能です。
そして、Ambient Lightセンサーがデータを通知してくるたびに、以下のようなコードを実行します。
double lux = al.CurrentLuminousIntensity.Intensity; var mp = new MeasurePoint(); // EDM ToolがMesuarePointエンティティの定義から生成したクラス mp.MeasuredSession = session; // MeasuredSessionのインスタンスは、計測開始時に作っておく mp.MeasuredTime = al.DataReport.TimeStamp; entityContainer.AddToMeasurePointSet(mp); // コンテナに追加し entityContainer.SaveChanges(); // データベースに保存。Idはここで確定 var mprop = new MeasuredProperty(); mprop.MeasurePoint = mp; mprop.Value = lux.ToString(); mprop.SensorPropertySpec = alPropSpecLux; // alPropSpecLuxは、SensorPropertySpecの照度センサーの照度プロパティをあらかじめ検索しておく entityContainer.AddToMeasuredPropertySet(mprop); entityContainer.SaveChanges(); |
※実際にはエラーハンドリングのコードが必要だが、煩雑なので割愛。スコープの関係で、実際には変数の定義場所、スコープの維持等で工夫が必要なことにご注意。
私が良く使っているFreescaleのSensor Development Kitボードの場合、100msec毎にデータが通知されます。そのハンドラーにこのコードを書けば100msec毎にデータベースに値が蓄積されます。
さて、次は、蓄積されたセンサーデータを利用する側のコードを説明します。
// 一秒間隔でデータ取得コードを起動するコード var timer = new System.Window.Threading.DispatcherTimer timer(); timer.Interval = new TimeSpan(0,0,1); timer.Tick += new EventHandler(timer_Tick); timer.Start(); .... void timer_Tick(object sender, EventArgs e) { using (var entityContainer = new LocationContextDataModelContainer()) { // Ambient Lightの計測データで計測された最後の値をクエリ var mps = from mp in entityContainer.MeasurePoint mp.Sensor.SensorSpec.TypeName.CompareTo("AmbientLight")==0 && mp.MeasuredSession.Device.EqualTo(targetDevice) orderby mp.MeasuredTime descending select mp; var lastMProp = mps.First().MeasuredProperty.First(); // 取得したMeasuredPropertyに格納された値をdouble化 double lux = Double.Parse(lastMProp.Value); .... // 後は煮るなり、焼くなり勝手にしてね。 } } |
こんな風にコードを書いて、データを蓄積する側のコードの実行と並行して、このコードを実行すれば、一秒単位でその時点での最新データを取得できます。後はそのデータをグラフ化するなり、https://msdn.microsoft.com/ja-jp/windows/ff423757.aspx#02で公開している脳波で地球を回すアプリの、脳波センサーからデータを取得するところを置き換えたりと、活用することができます。
実はこのEDM、ETロボコンの競技計測システムのデータベースアクセスの部分で使用しています。SQLでゴリゴリ書いたりしなくて非常に便利です。EDMを使った部分の不具合も今のところ皆無です。
他にも、私が連載中の「デバイス+クラウド」のコラムでも、現実世界に無数のセンサーノードを設置して、センサー値を蓄積していくシナリオが紹介されていますが、そんなシーンでも活躍することでしょう。
このテクノロジーは、Windows 7の特殊用途向けライセンスでももちろんそのまま使えますし、Windows 7の組込み向けOS、Windows Embedded Standard 7でも利用可能です。ぜひご活用ください。
ちなみに、イベント当日にお見せしたアプリケーションは、こんな感じ。