Lambda環境を利用してPieceをつくる

目次

Lambda上で動作するコードさえ実装すれば、スマートフォン上のネイティブコードの動きやRiiiver SDKの動作について理解しなくても、S/A Pieceを作成することが可能です。実際の動作としてはRiiiver SDKPieceCoreが連携してLambda上の関数を呼ぶのですが、 下の図の様な動作を一切意識する必要はありません。

以下の図のようにRiiiver SDKやスマートフォン側の実装をブラックボックスとして扱い、Lambda上で稼働するプログラムの開発だけに集中できます。

今回は Node.js を使って開発をした場合を例に説明をします。
上の図のように開発を進めるには、まずPieceJSONのキーを以下のように定義します。

"blockType":"service", //← A Pieceを作りたい場合は"action"
"executor":"RBCCommonWebServiceExecutor", //← SDKにビルトインされているPieceCore。これを指定。
"serviceProxy":{
  "service":"citizenSample" //← 皆さまがつくるLambda上のJavascriptファイル名から.jsを抜いたもの
},

PieceJSONに上記キーを含めれば、あとはLambda上に皆さまがアップロードしたJavascriptファイルが呼ばれた後からの動作を考えるだけです。

RiiiverではPieceBuilderというWebツールを準備しています。PieceBuilderはファイルアップローダーのような役割を果たし、PieceBuilderを利用することで、皆さまが作成したPieceのプログラムファイルをLambda上に適切に設置することができます。RiiiverLambda環境は、他の開発者の皆さまと共有して利用することができます。

皆さまが開発したLambdaで動作するPieceを含んだiiideaをユーザーが実行した場合、PieceJSON"serviceProxy"内の"service"で定義したファイル名のスクリプトを実行します。

実際のJavascriptは、通常のLambdaのものと同じく以下のようなコードです。

'use strict';
exports.handler = async event => {

  var response = {
    status: 200,
    body: {
      resultOfThisPiece:0.0   // Initialization
    }
  };

  //....ここに処理....

  //....結果を返却....
  return response;
};

皆さまが作成したPieceの実行順になったら、このハンドラが呼ばれます。このハンドラから抜ける時、戻り値にresponse変数に定義しているようなJSONデータを格納して返却する必要があります。response内のbody:で定義している{...}で囲まれている内容が、次のPieceに渡されるデータとなります。例えば、上に示すコードをそのままPieceとしてアップロードした場合、このPieceの出力は{ 0.0 }です。


ローカル環境でデバッグする環境を整える

皆さまの作成したコードをRiiiverLambda環境にアップロードしていただく前に、ローカル環境でデバッグしながら開発を進める方法をご紹介します。

準備

デバッグには以下のツールを利用してご説明しますので、各自の開発環境にインストールをお願いします(現状、他の環境では動作確認できておりませんので、ここでは環境を揃えさせていただければ幸いです)。

サンプルコードの中では、天気を取得するREST-APIを提供しているOpenWeatherというサービスを利用します。APIを利用して天気情報を取得するために、APIの利用登録を行ってください。

1. OpenWeather Webページからアカウント作成画面に進む。

2. 必要な入力事項を登録してアカウント作成。

3. 登録したメールアドレスに以下のようなメールが届きます。

このメールで言うところのYour API Key is 98d2......cとなっている文字列がAPIを利用するときに必要なキーです。メモなどを取っておいてください。

4. 動作を確認します。

メール中のExample of API call: api.openweathermap.org/.....は実際に入手したキーでAPIをコールする例です。ここではロンドンの現在の天気情報を取得する例になっています。このURLをそのままクリックしてみましょう。以下のようなページが表示されれば問題ありません。

それでは実際にローカル環境で動作を確認してみましょう。サンプルコードをダウンロードして下さい。


サンプルコードを展開する

サンプルコードの構成

サンプルコードを解凍すると以下のような構成になっています。

Sample Code
├── .vscode
├── kicker_on_lambda.js
└── YourCode
    ├── JSON
    │   └── pieceJson_Sample.json ✏️
    ├── ProxyCore
    │   ├── pieceCore_Sample.js ✏️
    │   └── package.json
    └── event.json ✏️

✏️これは開発時に皆さまで編集が必要なファイルになりますので、移動はしないでください。

  • SampleCode サンプルプロジェクトのルートフォルダ。
    • .vscode VS Codeの設定ファイル。
    • kicker_on_lambda.js RiiiverLambda環境のトリガーを模擬するキッカー。
    • YourCode 皆さまのコードを格納するフォルダ。
  • JSON 皆さまが作成するPieceJSONを格納するフォルダ。
    • pieceJS_Sample.json 皆さまが記述するPieceJSON。ファイル名は自由。拡張子は.json
  • ProxyCore 皆さまのPieceCoreを格納するフォルダ。
    • pieceCore_Sample.js PieceCoreの本体。ファイル名は自由ですが、PieceJSONで指定したファイル名と一致する必要があります。
    • package.json - pieceCore_Sample.jsで使用するモジュールの依存関係がまとめられたファイルです。
  • event.json PieceCoreに渡すデータ。前のPieceからの出力と、ユーザーの設定情報などをこのファイルで模擬します。
Local環境での動作を理解する

サンプルコードはそのまま実行することができます。実際にローカル環境で実行して動きを確認してみましょう。Pieceが動作する際、Lambda上で実行されるタイミングを理解するのが重要です。ローカル環境でも本番のLambda環境と近い動作をするように構成しています。Lambdaの代わりに、皆さまのコードを実行してくれるのが、kicker_on_lambda.js です。中身まで読む必要はありませんが、このJavascriptをコールすることで、前のPieceから皆さまのPieceの順番になったときの動作をエミュレートします。

SampleCode
├── .vscode
├── kicker_on_lambda.js  ← これがLambdaをスタートさせたときと同じ動作をエミュレートします。 
└── YourCode
    ├── JSON
    ....

一度、実際に動作させてみましょう。

API Keyの設定

実行するためには、皆さまが取得したAPI Keyを設定する必要があります。サンプルフォルダをVS Codeで開いてみましょう。

SampleCode」フォルダを開いてください。

フォルダの階層を間違わないようにお気をつけください。
開くフォルダを間違うと、デバッグできません。

SampleCode/YourCode/ProxyCore/pieceCore_sample.js をクリックして開きます。apikey変数に、先ほど取得した皆さまのOpenWeatherAPI Keyを入力してください。Keyの文字列を""で囲み、書き換えたら上書き保存をします。

必要なnode modulesのインストール

このサンプルではHTTP通信でWeb上のAPIに接続します。HTTPの通信部分の実装を簡単にするために、このサンプルではaxiosというモジュールを利用します。以下のコマンドを実行してaxiosのモジュールをインストールしましょう。

1. サンプルコード(Javascript)がある位置まで移動します。

# For Windows
cd (サンプルコードを解凍した場所)\SampleCode\YourCode\ProxyCore
# For Linux/Mac
cd (サンプルコードを解凍した場所)/SampleCode/YourCode/ProxyCore

2. npmコマンドを実行して必要なモジュールをインストールします。

npm install

3. package.jsonに従って、「node_modules」フォルダ内に必要なライブラリがインストールされます

これで通信用モジュールを使うための準備が整いました。

ターミナルで実行する

では実行してみましょう。Linux/Macの場合はターミナル、Windowの場合はコマンドプロンプトを開いて「SampleCode」ディレクトリまで移動します。

 # Windowsの場合
 cd (サンプルコードを解凍した場所)\SapmleCode
 # Linux/Macの場合
 cd (サンプルコードを解凍した場所)/SapmleCode

そして以下のコマンドを入力して実行します。

# Windowsの場合
node kicker_on_lambda.js
# Linux/Macの場合 
node kicker_on_lambda.js

次のような結果が出るはずです。

Proxy経由で接続する環境で実行する場合は注意してください。
正しく動作しない可能性があります。
$ node ./kicker_on_lambda.js 
start: event = {"serviceProxy":"citizenSample","properties":{"preferences":{"cityName":"Tokyo"},"parameters":{},"input":{}}}
https://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp&APPID=98d2xxxxxxxxxxxxxxxxxxxxxxxxc
getWeatherInfo: start
getWeatherInfo: response.data = {"coord":{"lon":139.76,"lat":35.68},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":297.19,"pressure":1006,"humidity":69,"temp_min":293.71,"temp_max":299.26},"visibility":10000,"wind":{"speed":7.2,"deg":240},"clouds":{"all":75},"dt":1570525660,"sys":{"type":1,"id":8074,"message":0.0078,"country":"JP","sunrise":1570480841,"sunset":1570522589},"timezone":32400,"id":1850147,"name":"Tokyo","cod":200}
output:  {"status":200,"body":{"celsiusTemperature":24.04000000000002}}
Your Function Output ::  {"status":200,"body":{"celsiusTemperature":24.04000000000002}}

いろいろなログが出ていますが、このサンプルPieceが次のPieceに出力するデータは最終行の"body"キー内の

{"celsiusTemperature":24.04000000000002}}

です。実際のファイルを見ながら動作の流れとPieceCoreの記述方法についてこの後に確認していきます。

Debugしてみる

先ほどはターミナル/コマンドプロンプトで実行しましたが、VS Codeを利用すると、ブレークポイントなどを仕掛けて動作をデバッグすることができます。デバッグできると実装効率があがりますので、ぜひ利用しましょう。

Debug環境設定

API Keyの設定のときと同じく、プロジェクトフォルダ「SampleCode」をVS Codeで開いてください。VS Codeの左側のアイコンが並ぶペインでデバッグのボタン(虫のアイコン)をクリックします。

これでデバッグ画面になります。次にデバッグ時に実行するファイルなどを設定します。↓の図の歯車のボタンをクリックして下さい。

すると、上記のように launch.jsonVS Codeのデバッグ環境設定ファイル)の編集画面になります。ここで"program":で記述されている内容を変更します。上図のように kicker_on_lambda.js を実行するように設定してください。

"program": "${workspaceFolder}/kicker_on_lambda.js"

Node.js のデバッグ機能はVS Codeに標準で組み込まれていますので、特に拡張機能のインストールなども必要ありません。kicker_on_lambda.js は名前の通り、AWSLambdaの代わりに皆さまが実装したコードをPieceJSONに記述している通りに呼び出すものです。

以上でデバッグの為の準備は整いました。実際にデバッグしてみましょう。

VS Code上でブレークポイントなども仕掛けられます。最初に正しくデバッグ環境が設定されて、kicker_on_lambda.jsが無事に実行されているか確認するために、このファイル内にブレークポイントを設定してみましょう。

  1. 画面左のアイコンでファイルをクリックして、プロジェクトフォルダ内を表示します。
  2. kicker_on_lambda.jsファイルをクリックして、ソースを表示します。
  3. kicker_on_lambda.jsのタブを選択します。(選択されていない場合)
  4. 9行目の行番号の左側の部分をクリックして、赤丸(ブレークポイント)をつけます。

これで、正しく動作すれば、kicker_on_lambda.jsが実行され、9行目に到達したときに一時停止するはずです。

Debug動作確認

実際にデバッグ実行してみましょう。

  1. もう一度虫のアイコンをクリックしてデバッグモードにします。
  2. ↓の再生マーク実行すると、デバックが開始されます。

正しく動作したら、↓のように9行目に設定したブレークポイントで一時停止されるはずです。

ブレーク時のローカル変数、グローバル変数は「VARIABLES」のペインで確認できます。またマウスカーソルをソースコード上の変数などでマウスオーバーすると、ブレイク時の値や状態を確認することができます。

そのまま実行する場合には、下のボタンをクリックしましょう。

サンプルコードが正しく動作すれば、VS Codeの「DEBUG CONSOLE」に以下のようにconsole.log()の出力が確認できると思います。

上の画面のように、ターミナルで実行した時と同じ様に結果を出力していることがわかるかと思います。

{"status":200,"body":{"celsiusTemperature":23.52000000000004}}

PieceJSONを準備する

Pieceの仕様書となるPieceJSONを準備します。すでに動作するものがサンプルプロジェクトに含まれています。YourCode/JSON/pieceJson_Sample.json がそうです(以下にも示しますが、今まで説明に利用してきたものと同じものです)
PieceJSONのファイル名は自由に変更できます(拡張子は.jsonとしてください)。
必要な記述事項について説明します。

{
  "title": {
    "en": "Current Temperature (celsius)",
    "ja": "現在の気温 (摂氏)"
  },
  "version": "1.0.0",
  "sdkVersion": "1.0.0",
  "deviceId": "none",
  "vendorId": "none",
  "description": {
    "en": "~~What is this Piece?~~\nGet the current temperature in degrees Celsius (°C).\n\n~~How do I use it?~~\nInclude this Piece in an iiidea to output a simple number representing the current temperature in °C.\nYou can choose in the iiidea settings for where to get information.\n\n~~Notes~~\nThe output value will be -50 if this Piece cannot get information due to network or a separate error.\n\nAbout Trademarks ( https://riiiver.com/en/trademark/ )",
    "ja": "【どんなPiece?】\n現在の気温を摂氏で次のPieceに渡します。\n\n【どうやって使うの?】\n「地域」のドラムロールから、気温を知りたい場所を選択してください。\n\n【ご注意!】\n気温をうまく取得できなかった場合は値が -50 となります。\n\n商標について ( https://riiiver.com/trademark/ )"
  },
  "blockType": "service",
  "executor": "RBCCommonWebServiceExecutor",
  "serviceProxy" : {
    "service": "pieceCore_Sample"
  },
  "pieceCapability": "notUse",
  "preferences": {
    "type": "object",
    "properties": {
      "cityName": {
        "type": "string",
        "enum": [
          "Sapporo-shi",
          "Tokyo",
          "Osaka",
          "Fukuoka-ken",
          "Okinawa-ken"
        ],
        "default": "Tokyo",
        "x-input-type": "drumroll",
        "x-title": {
          "ja": "地域",
          "en": "Area setting"
        },
        "x-description": {
          "en": "set the area from which to get the temperature.",
          "ja": "気温を取得する地域を指定します。"
        },
        "x-enum-titles": {
          "Sapporo-shi": {
            "ja": "札幌市",
            "en": "Sapporo"
          },
          "Tokyo": {
            "ja": "東京都",
            "en": "Tokyo"
          },
          "Osaka": {
            "ja": "大阪府",
            "en": "Osaka"
          },
          "Fukuoka-ken": {
            "ja": "福岡県",
            "en": "Fukuoka"
          },
          "Okinawa-ken": {
            "ja": "沖縄県",
            "en": "Okinawa"
          }
        }
      }
    }
  },
  "output": {
    "type": "object",
    "properties": {
      "celsiusTemperature": {
        "type": "number",
        "minimum": -50,
        "maximum": 50
      }
    }
  },
  "osType":"none",
  "categoryIds": ["cat_0007"]
}

繰り返しますが、ここで重要なのが"executor""serviceProxy"です。

"executor": "RBCCommonWebServiceExecutor",
"serviceProxy" : {
    "service": "pieceCore_Sample"
},

"executor"の値は、Riiiverが準備したLambda環境で動作するプログラムを作るときには必ず"RBCCommonWebServiceExecutor"になります。"serviceProxy"内の"service"で指定する値は、皆さまが記述するロジック部分に当たるJavascriptのファイル名から.jsを除いたもの指定してください。
上の例では pieceCore_Sample.js ファイルのhandlerLambdaからコールされます(.jsファイルについては、この後触れます)


descriptionに関するガイドライン

PieceJSONdescriptionについて、ガイドラインがございますので、こちらをご確認ください。

descriptionに記載いただきたい内容
{
  "title": {
    "en": "Current Temperature (celsius)",
    "ja": "現在の気温 (摂氏)"
  },
  "description": {
    "en": "
    ~~What is this Piece?~~\n
    Summary regarding your Piece is here.\n
    \n
    ~~How do I use it?~~\n
    Describe how to use.\n
    ~~Notes~~\n
    Precautions for use. This part is optional.\n
    \n
    ~~Compatible with~~\n
    Clarify the compatible products if your Piece cannot be used among all Riiiver devices without causing trouble",
    "ja": "
    【どんなPiece?】\n
    Pieceの概要を記載します。\n
    \n
    【どうやって使うの?】\n
    Preferenceの設定項目など、Pieceの使い方を説明します。\n
    \n
    【ご注意!】\n
    エラー時の処理など、ユーザーに知らせておきたい使用上の注意を記載します。特にない場合は記載不要です。\n
    \n
    【対象製品】\n
    使用できるデバイスが限られている場合、ここに記載してください。全てのRiiiverデバイスで利用可能な場合は記載不要です。"
  },
・
・
・
}
改行コードについて
実際にはjsonファイル内での改行はできませんが、ここでは見易さのために改行して記載しています。
使用非推奨ワードとその置き換え

非推奨ワード置き換えワード
iiideaの実行、動作iiideaを開始する、動かす、動き出す
アクティブ使用可能なとき、準備ができている状態
フォアグランドアプリの画面が表示されて操作可能なとき(フォアグランド)
バックグラウンドアプリが起動されているが画面に表示されていないとき(バックグラウンド)
トグル交互に切り替える
コントロール操作する、動かす
制御操作する、動かす
アウトプット、出力次のPieceに渡す
インプット、入力前のPieceから受け取った~
文字列、テキスト文字、文章
デバイス機器/商品/製品

Preferenceについて理解する

この例で重要になるのが"preferences":以下で囲まれている情報です。

Preferenceは皆さまが作ったPieceiiideaの一部として実行される前に、エンドユーザーにセットしてほしい設定項目を定義します。ユーザーはiPhone/Android端末から設定することになります。この pieceJson_Sample.json preferenceはユーザーには以下のようなUIが表示されます。

preference内のJSONを読み込んでみましょう。まず、"type":ですが、これは常に"Object"とします。"properties"で指定するキーが、ユーザーに設定してもらう値の名前になります。この例だと、"cityName"です。続いて"cityName"について定義が続きます。"cityName"の方は"string"です。取り得る値はenumで定義していますので、下記値のどれかが"string"として設定されます。

[
  "Sapporo-shi",
  "Tokyo",
  "Osaka",
  "Fukuoka-ken",
  "Okinawa-ken"
],

ユーザーがこの値を設定しない場合は、"default"の値が設定された状態でPieceが動作します。この例では"default":"Tokyo",ですので"Tokyo"が設定されます。
他の"x-"から始まるキーはUIの指定に関するものです。次に主要なUIに関するキーの説明をします。

x-input-type

ユーザーに表示するUIの種類をここで指定することができます。以下のUIをユーザーに表示して、必要な情報を設定してもらうことができます。

  • time
  • switch
  • map
  • radio
  • date
  • slider
  • textarea
  • text
  • drumroll

今回の例では「drumroll(ドラムロール)」を利用しています。

"x-input-type": "drumroll",
x-title

ユーザーに設定してもらう内容を簡単に表すようなタイトルを付けましょう。上のUI画面でいう「地域」が相当します。多言語対応です。

"x-title": {
  "ja": "地域",
  "en": "Area setting"
},
x-description

簡単な説明書きです。上のUI画面の「気温を取得する地域を指定します。」の部分が相当します。多言語対応です。

"x-description": {
    "en": "Set the area from which to get the temperature.",
    "ja": "気温を取得する地域を指定します。"
},
x-enum-titles
"x-enum-titles": {
  "Sapporo-shi": {
    "ja": "札幌市",
    "en": "Sapporo"
  },
  "Tokyo": {
    "ja": "東京都",
    "en": "Tokyo"
  },
  "Osaka": {
    "ja": "大阪府",
    "en": "Osaka"
  },
  "Fukuoka-ken": {
    "ja": "福岡県",
    "en": "Fukuoka"
  },
  "Okinawa-ken": {
    "ja": "沖縄県",
    "en": "Okinawa"
  }
}

実際に設定する値と、ユーザーに出したい情報が別の場合は、このようにユーザーに見せる文字列と設定値を別々にすることができます(ユーザーが「札幌市」を選択すると、"Sapporo-shi"が設定値となります)。
各値がenum値に対応していることがわかると思います。今回はSapporo-shiなどの都市名(ID)OpenWeather側の仕様で決まっています。これをユーザーにわかりやすく設定してもらうために、ドラムロールでは実際の地域名を漢字で表示させています。


実際にPreference画面を表示させて値を設定する

このようにPieceJSON内に記述することでユーザーに情報を設定してもらう画面を表示することができます。実際に記述しているJSONで問題なく表示できるのか、所望の設定値が設定されているのかを確認するため、オンラインツールのPieceJSON Generatorを準備しています。
PieceJSON Generatorを利用してPieceJSONのフォーマットが正しいか、Preferenceとして表示したいUIは期待通りか、実際に表示して確認することができます。

PieceJSON Generatorを利用する

実際に自分で記述したJSONファイルで正しくUIが生成できるか確認してみましょう。
https://piecejson-generator.riiiver.com/を訪れてください。以下のような画面が表示されると思います。

サンプルコードに含まれている先ほどのPieceJSONファイルの中身をコピーアンドペースト、もしくはFile > Openからファイルを指定して開いてください。以下の様な画面になるはずです。

PieceJSONのフォーマットをチェックする

現在記載されているPieceJSONのフォーマットが正しいか、必須となっているキーがは全て記述されているかを確認をするには、「Debug」メニューの「Check Piece Format」をクリックします。

すると、画面下のペインに結果が表示されます。

ここでエラーが出た場合には、エラーメッセージを確認して修正作業に入りましょう。

Preference UIを確認する

PieceJSONPreference設定項目を記述している場合には、PieceDebuggerを利用してユーザーに設定してもらいたい画面を実際に表示して確認することができます。
メニューの「Preview」をクリックし、英語で画面を確認するか、日本語で画面を確認するか選択します。

上の例では英語でのUI表示を確認しています。Englishを設定した場合、PieceDebuggerの右のペインに結果が表示されます。

今回の例ではServcie Pieceを作成しているので、S Serviceのセクションに皆さまが記述したUIが表示されます(Piece単体の確認をするためにPieceDebuggerを利用しますが、実際にユーザーがPreference画面を見る場合はiiideaの詳細画面になります。そのため、右のペインでもiiideaの設定画面を表示しています。ただし、PieceDebuggerでは現在確認しているPiece以外の表示はなく、iiideaのタイトルもダミーです)。

右のペインはユーザーが操作する様に、実際に値を設定することができます。上記の例の様に、地域のドラムロールUIをクリックして「Osaka」を選択してみましょう。選択が終わったらメニューの「Debug」ボタンをクリックし、続いて「Check Preference」をクリックしてください。

画面下のペインのデバッグコンソール部分に表示された下記の

preferences: {"cityName":"Osaka"}

が実際に皆さまのPieceの順番になった時に渡ってくるユーザーが設定した値になります。
この値をユーザが設定した値とみた立てて、デバッグすることができます。


Input/Outputについて理解する

PieceJSONではPieceが受け取ることができるデータの型と、次のPieceに出力するデータの型を定義します。
ユーザーがiiideaを作成する時、一つPieceを選択するとその前後に接続できるPieceかどうかはこのPieceJSON "input""output"の値で判断します。型が異なるPiece通しは接続することができないので、ユーザーには表示しない様な仕組みになっています。

input

サンプルのpieceJson_Sample.jsonファイルにはinputキーがありません。これはPieceが入力を受けないこと意味します。入力を受け付ける場合は以下の様に記述します。(↓の例では文字列を受け付ける)

"input": {
  "type": "object",
  "properties": {
    "inputstring": {
    "type": "string",
      "format" : "text",
      "x-title": {
        "en": "Inpurt Text",
        "ja": "入力されるテキスト"  
      },
      "x-description": {
        "en": "This Piece can receive the text such as ......",
        "ja": "このPieceは次の様なテキストを受け取れます: ○○○○○…"
      },
    }
  }
},
output

サンプルのpieceJson_Sample.jsonでは以下の様に定義されています。

"output": {
    "type": "object",
    "properties": {
        "celsiusTemperature": {
            "type": "number",
            "x-title" : {
                "en" : "Temperature (celsius)",
                "ja" : "気温(摂氏)"
            },
            "x-description" : {
                "en" : "Output the setting area's temperature in celsius.",
                "ja" : "指定した都市の温度を摂氏で出力します。"
            },
            "minimum" : -50,
            "maximum" : 50 
        }
    }
},

このPiece"celsiusTemperature"というキーで数字を出力することがわかります。


実行時のデータの流れを理解する

PieceJSONについては大まかにご説明しました。PieceJSONの詳細な情報はこちらのページを参照下さい。次に、実際にPieceが実行される時にどのようなデータが流れるのかを見ていきましょう。

Pieceに渡るデータ event.json

Pieceに渡るデータには上で説明したPreference設定を含め、以下のような値があります。

  • Preference
  • Input
  • Parameter
  • userData
Preference

上の例で示したpreferences: {"cityName":"Tokyo"}に相当する設定データです。ユーザーがさまざまなUIを介して設定することができます。他にどのようなUIがあるかはこちらのページをご参照ください。

Input

前のPieceの出力値です。

Parameters

前のPieceの出力値とは別に、スマートフォンの情報を取得することができます。現時点ではスマートフォンの現在位置情報の座標を取得できます。

userData

ユーザーのスマートフォン上での設定項目がuserDataとして渡されます。
userDataの中に以下の項目が含まれています。

  • date - ユーザーのスマートフォンのローカルタイムを取得できます。
  • languageCode - ユーザーがスマートフォンで利用している言語が取得できます。
  • countryCode - ユーザーがスマートフォンに設定している国を取得できます。

これらの情報を利用して、ユーザーの環境を考慮した動作をすることができます。
Pieceに入力される情報=Lambda実行時に渡ってくる情報」は以下のようなまとまったJSON形式のデータになります。

{
    "serviceProxy": "pieceCore_Sample",
    "properties": {
        "preferences": {
            "cityName": "Tokyo"
        },
        "parameters": {},
        "input": {}
    },
    "userData": {
        "date": "2020-03-27T13:09:18+0900",
        "languageCode": "ja",
        "countryCode": "JP"
    }
}

上記Pieceに渡るデータをLocal環境ではYourCode/event.jsonとして記述し、実際に動作させてデバッグします。

SampleCode
├── .vscode
├── kicker_on_lambda.js
└── YourCode
    ├── JSON
    │   └── pieceJson_Sample.json ✏️
    ├── ProxyCore
    │   ├── pieceCore_Sample.js ✏️
    │   └── package.json
    └── event.json ✏️   ←これ!!!

PreferenceエミュレータでUIで設定した値は画面右上のデバッグボタンを押すと、上記YourCode/event.jsonファイル内のpreferencesキーが書き換えられます。
ターミナルやコマンドプロンプトで実行したkicker_on_lambda.jsも上記event.jsonファイルで定義されたデータを入力ファイルとして読み込む仕組みになっています。

ブレークポイントを仕掛けてデバッグしてみる

それでは、実際のPieceevent.jsonファイルで設定したファイルが渡ってきている内容を確認してみましょう。またVS Codeを利用して、好きなところにブレークポイントを仕掛けます。

Lambdaが実行された直後動作

実際にAWS上のLambdaで皆さまのコードが実行される時を想定してexports.handlerで示す関数が呼ばれた直後にブレークポイントを仕掛けてみましょう。サンプルコードではpieceCore_Sample.jsファイルがexports.handlerを定義しています。

exports.handler = async event => {
  /* 
     Code for "warm start"
    The Riiiver system might call your Lambda function constantly to make it Warm Start.
    When the system called your function, the heavy modules are cached on the Lamba environment.
    The following code makes the (heavy) external module cached.
     ピースの実行速度高速化処理
    Lambda関数をウォームスタート状態にするために、このLambda関数は
    定期的にRiiiver System側からコールされることがあります。その際の処理をここで記述します。
    外部モジュールなどの重いインスタンスをキャッシュさせ、高速化します。*/
  if (event.call === 'lambda') {
    console.log('CALLED:LAMBDA');

    /* If you use the external modules, please code the following: - 外部モジュールを使う場合に記入してくだい。
       e.g.  if ( xxxx !== null ){} // xxxx: instance for the module.  -  xxxx : 生成したインスタンス */
    if (axios !== null) { }

    return;
  }
  ...
11行目(サンプルコードでは17行目) if は絶対に消さずにそのままご使用ください。
通常、ここは通りませんが、別の役割がありますので、消さないでください。(詳細は後で説明します。)

ちょうど、29行目にコンソールにログを吐く様に仕掛けていますので、ここでブレークポイントを貼ってみましょう。

ここでもう一度デバッグ・実行ボタンを押して実行してみましょう。この時、Pieceに渡されている情報はevent変数として渡っています。eventの中を確認してみましょう。画面左のペインに「WATCH」というセクションがあります。ここの右側の「+」ボタンを押してeventと入力してみましょう。

するとevent変数の中身を表示することができます(実際はeventの中身は event.json で記入しているJSONオブジェクトです)。

preferencesとして{cityName: "Tokyo"}が渡っているのが分かると思います。
ブレークポイントを31行目に設定して、29行目のconsole.logを出力すると実際のeventの内容の出力が確認できます。以下のようになるはずです。

start: event = {"serviceProxy":"pieceCore_Sample","properties":{"preferences":{"cityName":"Tokyo"},"parameters":{},"input":{}},"userData":{"date":"2020-03-27T13:09:18+0900","languageCode":"ja","countryCode":"JP"}}

Pieceに入力・設定されたデータはeventとして渡されます。この変数をうまく利用して、処理していきましょう。


実行して実行結果を次のPieceに送る

実行時の処理

サンプルコードの処理を再度確認してみましょう。Lambda上で処理を行う対象のコードはPieceJSONで指定している通り、pieceCore_Sample.js です。要点となる部分をご説明します。

exports.handler = async event => {
  /* 
     Code for "warm start"
    The Riiiver system might call your Lambda function constantly to make it Warm Start.
    When the system called your function, the heavy modules are cached on the Lamba environment.
    The following code makes the (heavy) external module cached.
     ピースの実行速度高速化処理
    Lambda関数をウォームスタート状態にするために、このLambda関数は
    定期的にRiiiver System側からコールされることがあります。その際の処理をここで記述します。
    外部モジュールなどの重いインスタンスをキャッシュさせ、高速化します。*/
  if (event.call === 'lambda') {
    console.log('CALLED:LAMBDA');

    /* If you use the external modules, please code the following: - 外部モジュールを使う場合に記入してくだい。
       e.g.  if ( xxxx !== null ){} // xxxx: instance for the module.  -  xxxx : 生成したインスタンス */
    if (axios !== null) { }

    return;
  }
最初のif文の処理について

if (event.call === 'lambda') { で始まるブロックは、皆さまのLambda関数を素早く実行するためにRiiiver側で準備している仕掛けです。このコードは 絶対に消さずに、 このまま残しておいてください。
Lambda関数をウォームスタートするために必要なコードがここに記載されています。Riiiverのシステムは、皆さまの関数を定期的に呼び出す処理を行います。定期的な呼び出しの処理はユーザーが実行した呼び出しではなく、Lambda関数の応答速度を上げるためのものです。この例で使っているような外部モジュールは、初期化するだけでも時間がかかるものです。外部モジュールを利用する処理をこのif文ブロックに入れておくことで初期化が完了するまで待つことができます。
皆さまのLambda関数が呼ばれて実行されるこの関数はasyncキーワードがついています。つまりこの関数は非同期に別スレッドで動作することになります。if (axios !== null)の条件式を判断するタイミングでは、グローバル変数axiosと定義したaxiosモジュールはまだ初期化されていないかもしれません。このif文を実行しようとすることでプログラムはaxiosモジュールの初期化の完了を待ってから条件式の評価に移ります。実際はその後に何もしていないのですが、ここで大きなモジュールの初期化が完了することに注目してください。
この関数をRiiiverシステムが定期的に呼び出したときに外部モジュールのインスタンスを初期化された状態でメモリにキャッシュさせるのが目的です。
外部モジュールを利用する際には'use strict;の下にグローバル変数として宣言し、if (event.call === 'lambda') {ブロック内で宣言した変数を使用する処理を入れてください。皆さまの関数の応答速度が上がります。

ユーザーが入力した地域情報を取得

現在気温を取得する地域情報を、ユーザーに設定してもらう想定で作成しています。cityNameに入る値はOpenWeatherの仕様に準拠した値が入る様にPieceJSONを作成しています。event.json ファイルの情報がPieceの入力データとして処理されevent変数に入ります。ユーザーの設定情報を下記のようにアクセスして抽出しています。

const cityName = event.properties.preferences.cityName;     // Get the location information entered by the user - 自分のpieceのプリファレンスの値
天気情報を取得

天気情報を取得しているのは以下の処理です。

// Get weather information - 天気情報を取得
let weatherData = await getWeatherInfo(urlString);

http通信用に追加したnodeモジュールaxiosを使用して通信を実現しています。アクセス先のURLOpenWeatherの仕様に準拠しています。
https://api.openweathermap.org/data/2.5/weather」にクエリとしてid=cityNameの値を追加して通信すると、cityNameで指定した地域の天気情報が取得できます。

function getWeatherInfo(urlString) {
  console.log('getWeatherInfo: start');
  
  const options ={
    url : urlString,
    method : 'get',
  }

  return axios.request(options)
  .then(function (response) {
    console.log('getWeatherInfo: response.data = ',JSON.stringify(response.data));
    return response.data;
  })
  .catch(function(error){
    console.error('getWeatherInfo: response = ', JSON.stringify(error));
    return null;
  });
}
次のPieceへ送るoutput情報を作る

取得した現在の気温を次のPieceへ送るためにoutput情報を作成します。

// 天気情報から今日の情報を取得 
if (weatherData) { 
  const tempK = weatherData.main.temp; // 得られたJSON内の温度を保存。単位はケルビン。
  response.body.celsiusTemperature = tempK - 273.15; // 次のブロックに返す値は摂氏で数値(Number) 
}

Lambdaの関数から戻り値を渡すときのフォーマットは常に下のようにHTTPの結果と次のPieceへのoutputのデータとなります。

{
  status:200,
  body:{
    celsiusTemperature:-1.0
  }
};

以上でサンプルコードに関する説明は終了です。

その他にもLambda上で実現できる機能がありますので、「Pieceの機能を拡張する」をご覧ください。また作成してデバッグしたPieceをアップロードする場合は「Pieceをアップロードする」をご覧ください。


この記事は役に立ちましたか?