SORACOMのあのボタンでIoTシステムのテストができるようにしてしまおう!


本記事は SORACOM LTE-M Button powered by AWS Advent Calendar 2018の23日目の記事です。

この記事を読んでいるということは、かなりの確率でIoTをやりたいな、やらなくちゃ、絶対やってやる!系の人ではないかと思いますが、IoTシステム(特にバックエンド側)のテストって、テスト用のデータを送るの大変じゃないですか?

動作確認をするたびにデバイスをあれこれ操作しないといけなくって、やってられんわ!ってなったり、
負荷試験まで考え出すと、そんなにデバイス用意できるか!できたとしても操作できるか!とかなったり。
デバイスシミュレーターを自作しようにも、そんな時間なかったり。

そんなお悩みを、あのボタンとmockmockというサービスを連携させて解決してみます。

mockmockとは

その名のとおり、IoTのデバイスmockを作るサービスです。
作ったmockでテスト用のデータを生成し、自分が開発しているIoTのバックエンドにデータを送信します。

  • 送信するデータ(json)のフォーマットは思いのまま
  • 温度などの時系列に沿って変化する値、指定した経路を移動したときの緯度経度など、様々なデータが生成できる
  • mockごとにシリアル番号などの固有の値も持たせることができる
  • デバイスの状態遷移もシミュレートできる
  • 大量のmockを一瞬で起動できる(5,000台まで。それ以上は要問合せ)
  • データの送信先として、独自サーバー(HTTP(S), MQTT(S))のほかに、AWS IoT Core, Amazon Kinesis Data Streams, GCP Cloud IoT Core, Azure IoT Hubといった、主要クラウドのIoT系サービスにも対応している

といった特長があります。
そして先日、APIがリリースされ、プログラマブルにmockの操作ができるようになりました!
そこで、あのボタンでmockの起動、停止、再起動ができるようにして、\ポチィ/とするだけでIoTのバックエンドにテストデータが送れるようにしてやろうと思います。

mockmockの設定

今回はウエアラブルデバイス的な感じで作ってみましょう。
送るデータはこんな感じです。

{
  "id": [デバイス固有ID],
  "pos": {
    "lon": [緯度],
    "lat": [経度]
  },
  "hrt": [心拍数],
  "time": [タイムスタンプ]
}

ざっと設定を見ていきます。

プロジェクト設定

今回はチュートリアル用のエンドポイントに送ります。

バリュージェネレーター

心拍数を生成するためのグラフバリュージェネレーター。
このグラフに沿って値を変化させて送ります。周期は一時間、±5の誤差を設定しています。

緯度経度のジェネレーター。
適当に福岡の街中をウロウロさせてみます。

データテンプレート

データのフォーマットや生成方法を定義するデータテンプレート。
この定義に沿ってjsonが生成されます。

mockグループ

mockの最大稼働時間やmockに持たせる固有値のフィールド名を設定します。

mockステータス

今回は状態遷移をさせないので1つだけです。
HTTPのパスやメソッド、データの送信間隔、使用するデータテンプレートを指定します。

mock作成

mockを一つ用意したところ。

念のためテスト送信してちゃんとデータが送れるか確認してみます。

API鍵

API鍵も作成しておきましょう。

これでmockmock側の準備は完了です。

SORACOM LTE-M Button

基本的な設定はGetting Startedあたりをご覧ください。

Placement Attributesは以下のように設定します。

mock-groupで指定したmockグループに所属するmock全てに対して操作を行います。
1,000台用意していれば、1,000台が一気に起動したりするような感じです。

ボタンから呼ぶLambda関数は以下のとおり(python3.6)。

import json
import logging
import os
import urllib.request

API_BASE = 'https://console.mock-mock.com/api/v2'

def lambda_handler(event, context):

    mock_group   = event['placementInfo']['attributes']['mock-group']
    token_id     = event['placementInfo']['attributes']['token_id']
    secret_token = event['placementInfo']['attributes']['secret_token']

    # リクエストヘッダ
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Token token={token_id};secret={secret_token}'
    }

    # mockのID一覧を取得
    req = urllib.request.Request(
        f'{API_BASE}/mock_groups/{mock_group}',
        headers=headers
    )
    res = urllib.request.urlopen(req)
    if res.getcode() != 200:
        return {'statusCode': res.getcode()}

    res_body = json.loads(res.read())
    targets = [{'code': mock_info['code']} for mock_info in res_body['result']['mocks']]

    # 操作内容
    operation = None
    if event['deviceEvent']['buttonClicked']['clickType'] == "SINGLE":
        operation = 'launch'
    if event['deviceEvent']['buttonClicked']['clickType'] == "DOUBLE":
        operation = 'terminate'
    if event['deviceEvent']['buttonClicked']['clickType'] == "LONG":
        operation = 'reboot'

    # mock操作
    data = {
      'operation': operation,
      'options': {
        'ignore_inoperable': True
      },
      'targets': targets
    }
    req = urllib.request.Request(
        f'{API_BASE}/mock_groups/{mock_group}/operate',
        data=json.dumps(data).encode("utf-8"),
        headers=headers,
        method='POST'
    )
    res = urllib.request.urlopen(req)
    if res.getcode() != 200:
        return {'statusCode': res.getcode()}

    return {'statusCode': 200}

結果

  • シングルクリック: mock起動
  • ダブルクリック : mock停止
  • 長押し : mock再起動

という動作が実現しました。

ちなみに送られてきたデータはこんな感じです。

$ curl https://tutorial-api.mock-mock.com/data/xxxxxxxxxxxx
2018-12-22 16:50:28.162544 {"id":"mk00001","pos":{"lon":33.58325137601978,"lat":33.58325137601978},"hrt":134,"time":1545497427}
2018-12-22 16:50:37.075294 {"id":"mk00001","pos":{"lon":33.58347080996428,"lat":33.58347080996428},"hrt":132,"time":1545497437}
2018-12-22 16:50:47.080990 {"id":"mk00001","pos":{"lon":33.58371461329075,"lat":33.58371461329075},"hrt":126,"time":1545497447}
2018-12-22 16:50:57.079951 {"id":"mk00001","pos":{"lon":33.58390608076425,"lat":33.58390608076425},"hrt":129,"time":1545497457}
2018-12-22 16:51:07.077066 {"id":"mk00001","pos":{"lon":33.58366228749682,"lat":33.58366228749682},"hrt":130,"time":1545497467}
2018-12-22 16:51:17.082268 {"id":"mk00001","pos":{"lon":33.583418481421425,"lat":33.583418481421425},"hrt":131,"time":1545497477}
2018-12-22 16:51:27.094228 {"id":"mk00001","pos":{"lon":33.58314226914939,"lat":33.58314226914939},"hrt":126,"time":1545497487}
2018-12-22 16:51:37.089284 {"id":"mk00001","pos":{"lon":33.58261584160005,"lat":33.58261584160005},"hrt":123,"time":1545497497}
2018-12-22 16:51:47.081681 {"id":"mk00001","pos":{"lon":33.58263765026497,"lat":33.58263765026497},"hrt":132,"time":1545497507}
2018-12-22 16:51:57.079058 {"id":"mk00001","pos":{"lon":33.583164077388226,"lat":33.583164077388226},"hrt":127,"time":1545497517}

まとめ

なかなかテストが大変なIoTのバックエンドですが、ボタン\ポチィ/で簡単にテストができるようになりました。開発が楽になりますね!

mockmockには無料枠もありますので、ぜひお試しください!
https://mock-mock.com/ja/