Help us understand the problem. What is going on with this article?

InCaaaan:印鑑マシーンの作り方 上司ツール編

InCaaaanとは?

InCaaaan:印鑑マシーンの作り方 超概要編に、全体的な構成と動画を置いてますので、ご確認ください。
では、早速作り方を!!!!

InCaaaanの中核は、IBM CloudのNode-RED

主に以下の制御を行っている。

  • 部下用の申請Webコンテンツの表示(LINE ID、LINE名取得もする)
  • Kintoneに申請データを保存する
  • AWSからKintoneのレコードが無事に追加されたことを受ける
  • レコードが追加されたことをトリガーにTwillioに自動音声の電話を要求(今回は説明対象外)
  • 電子型押印にKintoneのレコード IDとLINE名と金額をMQTTで送る
  • 電子型押印からの承認要求を受けて、承認データを生成してKintoneに送信
  • Obnizのサーボを動作

スクリーンショット 2019-12-14 20.14.28.png

Kintoneの環境を準備する

帳票を作成する

Kintoneは非常に使い勝手の良いサービスだけではなく、ユーザガイドも充実してますので、ここであえて解説は必要ないと思います。
デベロッパーに登録すると、無料でかなり楽しめる・・・間違えたかなり試せるので、デベロッパー登録は忘れずに!
登録は、kintone 開発者ライセンスこちらからできます。

InCaaaanの申請・承認を保管する帳票の作成

帳票には以下の項目を配置した。括弧の中は変数名です。

  • レコード番号(recordId)
  • 承認キー(Approval)
  • LINE ID(LINE_ID)
  • 氏名(LINE_NAME)
  • LINE プロファイル(LINE_PROFILE)
  • 書類名(FileName)
  • 金額(currency)
  • 申請理由(Reason)

Kintone

APIトークンを設定する

設定にある「APIトークン」をクリックします。
スクリーンショット 2019-12-14 19.58.46.png
APIは、レコード閲覧、レコード追加、レコード編集にチェックをしておく。なんと!これだけで終わり!
スクリーンショット 2019-12-14 20.06.52.png

なぜレコードの追加を受けるか、というと、部下用のUIとしてLINE Front-end Frameworkを使って表示しているWebサイトを入力元にしたいと考えたからである。
当初、ブラウザからKintoneにログインして・・・という考えもあったが、若い人がいつも使い慣れているインターフェースを入り口にして、いつか認証の連携もLINEと行えば、LINE IDで様々なことができるのではないか?と考えたからです(ただし、ヒーローズリーグでは実装できてなかった)

PNGイメージ.png

部下からの申請をNode-REDで受信する

部下がLINE上で入力したデータを受信する仕組みです。
構造は至ってシンプルで、POSTされたデータをKintone APIを使ってKintoneにレコード追加するのみとなります。

スクリーンショット 2019-12-14 23.00.54.png

「Headerを追加」というファンクションでKintone APIを呼ぶ前の準備をしています。
構造は至ってシンプルで、APIキーをセットして、部下からPOSTされてきたデータをKintoneのお作法にならってJSON形式に整形してあげるだけです。

KintoneAPIを呼ぶ前の準備
var api_key = 'KintoneのAPIキー';
msg.headers = {
    'X-Cybozu-API-Token': api_key,
    'Content-Type': 'application/json',
    'Authorization': 'Basic ' + api_key   
    //"X-HTTP-Method-Override": "GET"
};
msg.payload = {
            app: 1,
            record: {
                'LINE_ID': {
                    value: msg.payload.LINE_ID
                },
                'LINE_NAME': {
                    value: msg.payload.LINE_NAME
                },
                'LINE_PROFILE': {
                    value: msg.payload.LINE_PROFILE
                },
                'FileName': {
                    value: msg.payload.FileName
                },
                'currency': {
                    value: msg.payload.currency
                },
                'Reason': {
                    value: msg.payload.Reason
                }
            }
}
return msg;

レコードが追加された場合に動作するWebhookを設定する

別のインターフェースを用いたことにより、KintoneにAPIの追加が完了して、次の承認要求のプロセスを動作させる為に、Webhookを利用した。
アプリの設定のWebhookを選択する。
スクリーンショット 2019-12-14 20.29.08.png
Webhookの画面の+ボタンを選択します。
スクリーンショット 2019-12-14 20.30.35.png

ここでは、URLと通知を送信する条件として「レコードの追加」を選択して保存するだけとなる。
スクリーンショット 2019-12-14 20.31.35.png

追加が完了すると、以下のように追加されている。緑のチェックアイコンは正常に接続された場合なので、ここが赤いXマークだとWebhookが動作してないのでご注意を。
スクリーンショット 2019-12-14 20.33.37.png

AWSの環境(KintoneからWebhookを受ける)を整える

KintoneからWebhookの通知を送る方法を確認したところ、以下公式ページから拝借した図では、Zapier、Microsoft Flow、IFTTTなどの紹介があった。しかし、これらは何か追加された、削除された、というような簡単なリクエストを受ける場合には有益なサービスであるが、部下からの入力データを使い回したいのニーズに適用できなかった。
スクリーンショット 2019-12-14 20.38.37.png

その為、AWSで自作することにした。というか、AWSで作る方が早いと思った。

Lambdaファンクションを作る

AWSと言っても沢山のサービスがある。AWSマネジメントコンソールの「サービスを検索する」の部分に「Lambda」と入力する

スクリーンショット 2019-12-14 20.37.54.png

AWS Lambdaの画面に遷移するので、早速「関数の作成」を選択する

スクリーンショット 2019-12-14 21.39.56.png

「一から作成」を選択し、ランタイムは「Python 3.8」を選択して作成する

スクリーンショット 2019-12-14 21.42.33.png

「関数コード」の部分に以下のコードを入力する

スクリーンショット 2019-12-14 21.45.11.png

プログラムはものすごく単純で、KintoneのWebhookで通知されてきたリクエストに格納されているデータを抽出し、IBM CloudにPOSTしている

lambda_function.py
import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(event['record'])
    logger.info(u'レコード番号 : ' + event['record']['recordId']['value'])
    logger.info(u'金額 : ' + event['record']['currency']['value'])
    logger.info(u'作成日時 : ' + event['record'][u'作成日時']['value'])

    import urllib.request
    url = 'IBM CloudのNode-REDのポスト先:電子型押印に通知'
    params = {
        'recordId': event['record']['recordId']['value'],
        'LINE_ID': event['record']['LINE_ID']['value'],
        'LINE_NAME': event['record']['LINE_NAME']['value'],
        'LINE_PROFILE': event['record']['LINE_PROFILE']['value'],
        'FileName': event['record']['FileName']['value'],
        'currency': event['record']['currency']['value'],
        'Reason': event['record']['Reason']['value'],
        'date':event['record'][u'作成日時']['value']
    }

    req = urllib.request.Request('{}?{}'.format(url, urllib.parse.urlencode(params)))
    with urllib.request.urlopen(req) as res:
        body = res.read()

API Gatewayを設定する

AWSマネジメントコンソールの「サービスを検索する」の部分に「API Gateway」と入力する

スクリーンショット 2019-12-14 20.37.54.png

Amazon API Gatewayの画面が表示されたら、さっそく「APIを作成」をクリックする

スクリーンショット 2019-12-14 21.57.44.png

REST APIの「構築」を選択します。
スクリーンショット 2019-12-14 21.58.58.png

API名を入力して、「APIの作成」を選択しましょう

スクリーンショット 2019-12-14 22.00.30.png

「アクション」というタブの中に、「メソッドの作成」があるので選択してください。

スクリーンショット 2019-12-14 22.01.31.png

HTTPのコマンドが表示されるので「POST」を選択します。

スクリーンショット 2019-12-14 22.02.37.png

POSTのセットアップ画面が表示されるので、作成したLambda関数を入力しましょう

スクリーンショット 2019-12-14 22.03.44.png

といっても、なんのこっちゃ、という話になるので、Lambda関数の部分には、Lambda関数を作成していた画面の右上に表示されている「ARN」の部分(以下の図)の文字の羅列をコピーして貼り付けると良いです。そして保存しておしまいです。

スクリーンショット 2019-12-14 22.05.06.png

次に、「ステージ」を作成します。と、いいつつ、「APIのデプロイ」を行います。

スクリーンショット 2019-12-14 22.16.28.png

APIのデプロイを選択すると、新しいステージが作れるので、新しいステージを選択して、ステージ名を入力後、「デプロイ」を選択する。

スクリーンショット 2019-12-14 22.17.46.png

なんと、これで呼び出し可能なURLが発行されました。
本来は、APIキーの発行だ、とか、VPCリンクだ、とかセキュリティを担保する設定も必要だが、今回はプロトタイプということで設定を省略しています。

スクリーンショット 2019-12-14 22.19.08.png

このURLをKintoneのWebhookの追加に出てきたURLに設定するだけで、Kintoneにレコードが追加されるたびに、AWSに送信される仕組みとなる

スクリーンショット 2019-12-14 20.31.35.png

AWSからPOSTを受けるIBM Cloud Node-REDで受信後、MQTTのメッセージをパブリッシュする

ここは、HTTPのPOSTを受信し、MQTTでメッセージをパブリッシュするというシンプルな説明となる。

スクリーンショット 2019-12-14 22.26.14.png

ここでは、「MQTTで送信」というファンクションが肝になる。
記載しているプログラムはシンプルで、AWSからPOSTされたデータ(KintoneレコードID、LINE名、金額)を、アンダーバーで連結して、新しいmsg2.payloadという変数に格納しなおしているだけとなる。

MQTTで送信
var msg2 = {};
msg2.topic='TEST'
msg2.payload=msg.payload.recordId + "_" +msg.payload.LINE_NAME + "_" + msg.payload.currency;
return msg2;

以下にNode-REDでインポート可能なものを置いておきます。
MQTTはCloudMQTTを使用しています(フリーの範囲で十分使える)

関連するNode-RED
[{"id":"f50b99c1.829b18","type":"http in","z":"38dd45d0.ae9242","name":"申請レコードが追加された","url":"/kintoneData","method":"get","upload":false,"swaggerDoc":"","x":130,"y":800,"wires":[["a7540857.d977d","4c688ee7.9e7a08","11a5ece2.3aa2fb"]]},{"id":"a7540857.d977d","type":"debug","z":"38dd45d0.ae9242","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":470,"y":760,"wires":[]},{"id":"15081bde.4256d4","type":"mqtt out","z":"38dd45d0.ae9242","name":"","topic":"TEST","qos":"2","retain":"false","broker":"33535a31.0e74ee","x":710,"y":800,"wires":[]},{"id":"4c688ee7.9e7a08","type":"function","z":"38dd45d0.ae9242","name":"MQTTで送信","func":"var msg2 = {};\nmsg2.topic='TEST'\nmsg2.payload=msg.payload.recordId + \"_\" +msg.payload.LINE_NAME + \"_\" + msg.payload.currency;\nreturn msg2;","outputs":1,"noerr":0,"x":410,"y":820,"wires":[["15081bde.4256d4"]]},{"id":"33535a31.0e74ee","type":"mqtt-broker","z":"","name":"","broker":"xxx.cloudmqtt.com","port":"14263","clientid":"","usetls":false,"compatmode":true,"keepalive":"15000","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","willTopic":"","willQos":"0","willRetain":"false","willPayload":""}]

電子型押印(M5StickC)の準備

電子型押印は、MQTTでメッセージを受ける仕組みしています。
受けたメッセージを、LCDに表示し、LCDにメッセージが表示された状態で、Y軸方向に1.5Gの重力を加えれば承認された、解釈してMQTTメッセージをパブリッシュします。

ほとんど、ヒーローズリーグ直前で作ったので、スピード重視にUI Flowを使いました。

スクリーンショット 2019-12-14 22.40.15.png

すごくシンプルで、MQTTブローカーと接続した後、TESTというトピックをサブスクライブした時に、

  • 受信データをLCDに表示する
  • Y軸の加速度1.5Gが来るまでループする
  • Y軸の加速度1.5Gがきたら、トピック"InCan"にメッセージにTESTトピックで受信したデータをパブリッシュ
  • Y軸の加速度1.5Gがきたら、LCDに_表示を設定
  • ループを抜け出す(また、TESTトピックが来るまで待機)

という流れである。めっちゃシンプル!

電子型押印の承認モーションをNode-REDで受信する

M5StickCからパブリッシュされたInCanというメッセージを受信して処理を開始します。

スクリーンショット 2019-12-14 22.47.38.png

やることは、2つで

  • 承認した対象のレコードを更新する為、Kintone APIを起動する
  • ObnizのWebhookを呼び出す

のみです。

サーボを動かすだけ
<!-- HTML Example -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@2.5.0/obniz.js"></script>
</head>
<body>

<script>
var obniz = new Obniz("40997538");
obniz.onconnect = async function () {
  var servo = obniz.wired("ServoMotor", {gnd:0 , vcc:1 , signal:2 });
  servo.angle(0);
  await obniz.wait(2000);
  servo.angle(55);
  await obniz.wait(2000);
   servo.angle(0);
  await obniz.wait(2000);
}
</script>
</body>
</html>

MQTTで制御しようかと思いましたが、

【これはボツなので参考にしないで】MQTTのメッセージで制御
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/starter-sample.css">
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/obniz@2.5.0/obniz.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
  </head>

  <body>

    <div id="obniz-debug"></div>
    <div id="print">----</div>

    <script>

      var obniz = new Obniz("Obniz");

      // MQTTクライアント生成
      var client = new Paho.MQTT.Client("MQTTブローカ接続先", ポート,"aaa");

      // コールバックの定義
      client.onConnectionLost = onConnectionLost; // 接続が切れたとき
      client.onMessageArrived = onMessageArrived; // メッセージ受信したとき

      // 接続
      var options = {
        useSSL: true,
        userName: "ユーザID",
        password: "パスワード",
        onSuccess:onConnect, // 接続したときのコールバック
        onFailure:doFail // 失敗したときのコールバック
      }
      client.connect(options);

      // 接続したとき
      function onConnect() {
        console.log("onConnect");

        // トピックを購読する
        client.subscribe("メッセージ");

        // トピックにメッセージを発行してみる
        //message = new Paho.MQTT.Message("");
        //message.destinationName = "メッセージ";
        //client.send(message);
      }

      // 失敗したとき
      function doFail(e){
        console.log(e);
      }

      // 接続が切れたとき
      function onConnectionLost(responseObject) {
        if (responseObject.errorCode !== 0) {
          console.log("onConnectionLost:"+responseObject.errorMessage);
        }
        client.connect(options);
      }

      // メッセージを受信したとき
      function onMessageArrived(message) {
        console.log("onMessageArrived:"+message.payloadString);
        // テキスト表示
        $('#print').text(message.payloadString);
      }
    </script>
  </body>
</html>

サーバレスイベントに、サーボを動かすプログラムを設定するのみとなります。
サーボでキレイに押印するのは、割と苦労したので、ここでは伏せておきます。

スクリーンショット 2019-12-14 22.56.41.png

今回、ヒーローズリーグに向かう道中に輸送している最中にサーボが死ぬ(電源入れてもトルクが全くない状態)、会場で大手術をするというハプニングがありました。

スクリーンショット 2019-12-14 23.08.26.png

まあ、本番前には無事に治ったんですが、
正直なところ今回は物理押印をキレイに押す、という難しさを克服する為、

  • 木を加工してサーボとObnizを取り付ける
  • 取り付けた場所の距離を考えて、スポンジ、印鑑台を設定
  • スポンジとサーボを動かす角度を調整する

というのが、一番作るのが苦労した部分だった・・・
手が痛くなるくらい、木を削り、角度を測り、何度も何度もテストしてレッドハッカソンよりも進化させたという・・・・

スクリーンショット 2019-12-14 23.08.40.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした