SORACOM Inventory Agent のカスタムオブジェクトを実装してみる

 先日の記事では SORACOM Inventory が Public Beta になったということでデバイス管理と SORACOM Harvest との連携を試してみました。

blog.akanumahiroaki.com

 Public Beta になったタイミングで SORACOM Inventory では LwM2M のカスタムオブジェクトが定義できるようになったので、今回は簡単に実装してみました。 SORACOM Inventory の機能概要や LwM2M のリソースモデル等については SORACOM の公式ドキュメントを参照ください。

dev.soracom.io

コアライブラリとサンプル実装のダウンロード

 SORACOM Inventory では下記リポジトリで Java版エージェントのコアライブラリとサンプル実装が提供されています。

github.com

 まずはこちらを clone します。

$ git clone https://github.com/soracom/soracom-inventory-agent-for-java.git
$ cd soracom-inventory-agent-for-java/

環境構築

 プロジェクトのビルドには Gradle が使用されています。私は IDE に Eclipse を使用しているので、下記コマンドで Eclipse 用の設定ファイルを生成します。

$ ./gradlew eclipse

 また、後でコアライブラリの jar ファイルを使用するので、下記コマンドでビルドしておきます。

$ ./gradlew build

 そして Eclipse のプロジェクトとしてインポートします。File メニュー の Import から General > Existing Projects into Workspace と辿って、root directory に soracom-inventory-agent-for-java ディレクトリを選択します。

 次に今回作成するエージェントのプロジェクト用のディレクトリを作成します。

$ mkdir restroom-monitor-agent
$ cd restroom-monitor-agent/

 下記コマンドでプロジェクトを初期化します。

$ gradle init --type java-application

 すると下記のようなファイルが生成されます。

$ ls -l
total 40
-rw-r--r--  1 akanuma  staff   992 May 12 23:52 build.gradle
drwxr-xr-x  3 akanuma  staff    96 May 12 23:52 gradle
-rwxr-xr-x  1 akanuma  staff  5296 May 12 23:52 gradlew
-rw-r--r--  1 akanuma  staff  2260 May 12 23:52 gradlew.bat
-rw-r--r--  1 akanuma  staff   369 May 12 23:52 settings.gradle
drwxr-xr-x  4 akanuma  staff   128 May 12 23:52 src

 build.gradle の内容は下記のように変更します。

$ cat build.gradle 
plugins {
    id 'java'
    id 'eclipse'
    id 'idea'
    id 'application'
}

repositories {
    jcenter()
    maven { url 'https://soracom.github.io/maven-repository/' }
}

def INVENTORY_AGENT_VERSION="0.0.5"

dependencies {
    compile "io.soracom:soracom-inventory-agent-for-java-core:$INVENTORY_AGENT_VERSION"
    testCompile 'junit:junit:4.12'
}

mainClassName = 'com.akanumahiroaki.restroommonitor.agent.RestroomMonitorAgent'

 Eclipse 用の設定ファイルを生成します。

$ ./gradlew eclipse

 そして Eclipse にインポートします。初期化時に生成されたメインクラスは下記のように App.java になっています。

f:id:akanuma-hiroaki:20180513223817p:plain

 これを build.gradle の mainClassName で指定したパッケージとクラス名に合うように変更します。

f:id:akanuma-hiroaki:20180513223853p:plain

 パッケージとクラス名変更後の初期の実装は下記のようになっています。

package com.akanumahiroaki.restroommonitor.agent;
/*
 * This Java source file was generated by the Gradle 'init' task.
 */
public class RestroomMonitorAgent {
    public String getGreeting() {
        return "Hello world.";
    }

    public static void main(String[] args) {
        System.out.println(new RestroomMonitorAgent().getGreeting());
    }
}

 次に必要なライブラリにパスを通します。 LeshanClient を使用するためのライブラリは別途取得する必要があるので、私は下記サイトから依存する jar と共に取得してきました。

jar-download.com

 また、先ほどビルドしたコアライブラリの jar が下記パスに生成されています。

soracom-inventory-agent-for-java/build/libs/soracom-inventory-agent-for-java-0.0.5.jar

 これらの jar ファイルを外部ライブラリとしてビルドパスに追加しておきます。

f:id:akanuma-hiroaki:20180513223933p:plain

カスタムオブジェクト定義

 カスタムオブジェクトを定義するには XML ファイルを作成する必要があります。今回はシンプルに読み取り用のリソースを一つだけ定義してみます。トイレセンサーを作っている想定で、その空室状況を表すステータスです。カスタムオブジェクトの使い方としてこういうステータスを扱うのが正しいのか微妙な気がしましたが、今回は実装を試すのが主目的ということでやってみます。下記のような XML ファイルを作成し、サンプルプロジェクトと同様に src/main/resources 配下に 30000.xml というファイル名で配置します。

<?xml version="1.0" encoding="UTF-8"?>
<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd">
    <Object ObjectType="MODefinition">
        <Name>Restroom</Name>
        <Description1>Restroom Monitor</Description1>
        <ObjectID>30000</ObjectID>
        <ObjectURN>urn:oma:lwm2m:oma:30000</ObjectURN>
        <MultipleInstances>Single</MultipleInstances>
        <Mandatory>Optional</Mandatory>
        <Resources>
            <Item ID="0">
                <Name>Status</Name>
                <Operations>R</Operations>
                <MultipleInstances>Single</MultipleInstances>
                <Mandatory>Mandatory</Mandatory>
                <Type>String</Type>
                <RangeEnumeration />
                <Units/>
                <Description>Status of a restroom.</Description>
            </Item>
        </Resources>
        <Description2 />
    </Object>
</LWM2M>

実装クラス

 上記で定義したカスタムオブジェクトの実装クラスを下記のように作成します。今回はテキストファイルに現在のステータスが記録されているという前提で、簡単にその内容を読み込んで返すというだけにしてあります。

 AnnotatedLwM2mInstanceEnabler を継承したクラスに @LWM2MObject アノテーションでオブジェクトIDとオブジェクト名を指定します。また、値を返すメソッドには @Resource アノテーションでリソースIDとオペレーション種別を指定します。今回は読み取り専用のリソースなので、オペレーション種別は Operation.Read を指定しています。

package com.akanumahiroaki.restroommonitor.agent.object;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import io.soracom.inventory.agent.core.lwm2m.*;

@LWM2MObject(objectId = 30000, name = "Restroom")
public class RestroomObject extends AnnotatedLwM2mInstanceEnabler {
    @Resource(resourceId = 0, operation = Operation.Read)
    public String readStatus() {
        try {
            List<String> lines = Files.readAllLines(Paths.get("/home/pi/work/RestroomMonitor/status.txt"));
            return String.join("", lines);
        } catch (IOException e) {
            e.printStackTrace();
            return "unknown";
        }
    }
}

エージェントクラスの実装

 ここまでで用意したカスタムオブジェクト定義と実装を利用するエージェントクラスを下記のように実装します。

 lwM2mModelBuilder.addPresetObjectModels() でデフォルトのオブジェクト定義を読み込んだ後で、 lwM2mModelBuilder.addObjectModelFromClassPath("/30000.xml") で今回定義したオブジェクト定義を読み込んでいます。また、実装したオブジェクトクラスを initializer.addInstancesForObject(new RestroomObject()) で設定しています。

package com.akanumahiroaki.restroommonitor.agent;

import org.eclipse.leshan.client.californium.LeshanClient;

import com.akanumahiroaki.restroommonitor.agent.object.RestroomObject;

import io.soracom.inventory.agent.core.initialize.InventoryAgentInitializer;
import io.soracom.inventory.agent.core.initialize.LwM2mModelBuilder;
import io.soracom.inventory.agent.core.lwm2m.typed_object.impl.MockDeviceObjectImpl;

public class RestroomMonitorAgent {
    public static void main(String[] args) {
        InventoryAgentInitializer initializer = new InventoryAgentInitializer();
        
        LwM2mModelBuilder lwM2mModelBuilder = new LwM2mModelBuilder();
        lwM2mModelBuilder.addPresetObjectModels();
        lwM2mModelBuilder.addObjectModelFromClassPath("/30000.xml");
        initializer.setLwM2mModel(lwM2mModelBuilder.build());
        
        initializer.addInstancesForObject(new MockDeviceObjectImpl());
        initializer.addInstancesForObject(new RestroomObject());
        LeshanClient client = initializer.buildClient();
        client.start();
    }
}

ユーザコンソールからオブジェクト定義を登録

 ユーザコンソールにもカスタムオブジェクトの定義が反映されるように、オブジェクト定義のXMLをユーザコンソールから登録します。ユーザコンソールの左メニューの SORACOM Inventory の オブジェクトモデル を選択します。

f:id:akanuma-hiroaki:20180513224301p:plain

 オブジェクトモデルの追加 ボタンをクリックします。

f:id:akanuma-hiroaki:20180513224322p:plain

 表示されたフォームにオブジェクトモデル定義XMLの内容を貼り付けて、 追加 ボタンをクリックします。

f:id:akanuma-hiroaki:20180513224344p:plain

 すると下記のようにオブジェクトモデルが追加されます。

f:id:akanuma-hiroaki:20180513224404p:plain

エージェントの実行

 ここまでで一通り実装は完了なので、最後にデバイスへの配布用のアーカイブを下記のコマンドで生成します。

$ ./gradlew distZip

 下記のようにアーカイブが生成されますので、これをエージェントを実行するデバイスへ配布します。

$ ls -ltr build/distributions/
total 2272
-rw-r--r--  1 akanuma  staff  1159199 May 13 01:44 restroom-monitor-agent.zip

 配布先デバイスで下記のようにアーカイブを展開してエージェントを実行します。

$ unzip restroom-monitor-agent.zip 
$ cd restroom-monitor-agent/bin
$ ./restroom-monitor-agent

 すると下記のようにコンソールからカスタムオブジェクトが確認できるようになります。 Observe することも可能です。

f:id:akanuma-hiroaki:20180513224426p:plain

まとめ

 今回試してみたのはシンプルな内容だったので、簡単にカスタムオブジェクトを実装することができました。実際のサービスで使用する場合にはカスタムオブジェクトの前に標準で提供されているリソースモデルに対して正しい値を返すようにエージェントを実装する必要がありますので、取得したい情報全てが取得できるようにするには結構なボリュームの実装が必要そうではあります。ただ、エージェントを実装すればプラットフォームには手をかける必要がないので、うまく使えればとても便利なのではないかと思います。