Google Homeとobnizで部屋の電気をON/OFFする

2018年12月29日 追記
obniz単体だとエンドポイントを作れないと思っていたので、RunKitでエンドポイントを作っていたのですが、実際はobniz単体でも、Event機能でWebhookを選択すればエンドポイントを作れました。失礼しました。訂正します。


この記事はobniz Advent Calendar 2018 10日目の記事です。

obnizとサーボモータで部屋の電気をON/OFFできるようにして、Google Homeから音声操作できるようにしました。 まずは動画をご覧ください。スマート電球を使わず物理的にスイッチを叩いて電気をON/OFFしている様子がお分りいただけると思います。(カメラのライトをつけているので明るさはあまり変わってませんが。)


Google Homeとobnizで部屋の電気をON/OFFする

私はこれまで電子工作もVUIアプリ開発もほとんど経験がなかったのですが、obnizのハンズオンに参加して「こりゃ簡単すぎて自分でも何か作らなきゃ嘘だぜ」と思ってこのアプリを作りました。
電子工作、VUIアプリ開発の双方でたくさん学びがあったので、この記事では実施した内容とそこで得たノウハウを順を追って書いていきます。この記事が、私のような初心者の方の参考になれば幸いです。
記事は「1. 準備編」「2. 実装編」「3. 運用編」の3部構成です。

1. 準備編

今回使ったものはGoogle Home mini, obniz, サーボモータ, スイッチカバーです。

1-1. obnizとサーボモータの接続

アプリ開発の第一歩。張り切ってobnizとサーボモータを接続しよう!としたところ、接続部分が受け口と受け口で付けられませんでした。

f:id:sonomirai:20181201221004j:plain:w300
受け口と受け口!

変換用の部品が存在することは知っていたのですが、名前が分からずネットで注文できなかったので、サーボモータ側のコードを切ってねじってobnizに差し込んで動かしました。その後、変換用部品は「ピンヘッダ」という名前であると知りました。

f:id:sonomirai:20181208104645p:plain
ピンヘッダ

1-2. スイッチカバーへのサーボモータの取り付け

元々のスイッチカバーは丸みを帯びていてサーボモータを固定しづらそう&部屋のもの傷つけるのが嫌だったので、サーボモータの取り付け方で迷いました。
ヒントを求めて向かったホームセンターで、サーボモータを固定しやすそうな角ばったスイッチカバーが80円で売っていたので、それを買ってサーボモータをアロンアルファで固定して、元のスイッチカバーと付け替えました。特に問題なく付け替えられて一安心。

f:id:sonomirai:20181208104708j:plain:w400
左が元々のスイッチカバーで右が今回のスイッチカバー

これで準備は終わりです。

2. 実装編

実装は以下のように段階的に進めました。【】内はざっくりとした作業カテゴリです。
1.【IoT化】obnizのWeb画面からサーボモータを動かす
2.【API化】RunKitからサーボモータを動かす
3.【VUI化】音声でRunKitを叩いてサーボモータを動かす
4.【UI改善】Google Home周りのユーザビリティを上げる

実装の流れをざっくりと概念図で示すと下記の通りです。

f:id:sonomirai:20181201223245p:plain
実装の流れ

2-1.【IoT化】obnizのWeb画面からサーボモータを動かす

ハンズオンやパーツライブラリのサンプルコードを参考に、obnizのWeb画面でこちらのプログラムを作成しました。実行画面は以下の通りで、ボタンをタップすると電気が点灯/消灯します。

f:id:sonomirai:20181201225140p:plain:w200
実行画面

2-2.【API化】RunKitからサーボモータを動かす

次にこれをGoogle Homeから実行しようとしたところで、obnizのクラウドシステムには外部からプログラムを叩くためのエンドポイントを作成する機能がないので、別のサービスを使う必要があることに気付きました。
[訂正]上記は間違いで、obnizのEvent機能でWebhookを選択すればエンドポイントを作成することができました。
AWSのLambdaなどのサービスを使う必要がある?と思いつつググっていたところ、RunKitというサービスでエンドポイントを作っている資料があったので、とりあえずRunKitを使ってみることにしました。
RunKitはとても便利で、作成したコードを叩くためのエンドポイントを勝手に作成してくれます。
点灯用のコードと消灯用のコードを分けて作成しました。点灯用のコードはこちらです。

curlでRunKitのエンドポイントを叩くと、電気が点灯/消灯されるところまで確認しました。

2-3.【VUI化】音声でRunKitを叩いてサーボモータを動かす

点灯/消灯のエンドポイントを準備できたので、次は音声操作でエンドポイントを叩けるようGoogle Homeの設定をします。
Google Homeでのアプリ開発ではGoogleアシスタント, Action on Google, Dialogflowといった複数のツールが出てきますが、詳細は割愛します。WEB+DB PRESS Vol.105のスマートスピーカの記事が分かりやすかったです。

今回は以下の通り設定しました。

  1. Action on Googleのプロジェクト名を設定する
  2. DialogflowのIntentsでFulfillmentを有効化する
  3. IntentsとFulfillmentの関数を紐付ける

2-3-1. Action on Googleのプロジェクト名を設定する

VUIアプリ開発にあたり、まず最初にAction on Googleのプロジェクトを以下のどちらで作るか迷いました。

  1. 点灯用と消灯用でプロジェクトを分けてそれぞれのDefault Welcome Intentで処理をする
  2. プロジェクトは1つにして、Intentによって点灯と消灯の処理を分ける

前者だとプロジェクト名のみの発話でいいので、「OK Google, 電気つけて」「OK Google, 電気消して」という短い発話で操作ができます。
ただ発話毎にプロジェクトを分けるのは流石にイケてなさそうと言う気がしました。
いっぽう後者は、例えばプロジェクト名を「ルームライト」とした場合、「OK Google, ルームライト」と発話して、Google Homeの応答を待ってから、「電気消して」と発話することになるので、かなり使いづらそうだと悩みました。
そこでヒントを求めて読んだWEB+DB PRESS Vol.105Google Homeの記事に「プロジェクト名を使ってフレーズ名」という発話ができると書いてあって、コレだ!と思いました。これなら少し発話は長くなりますが、「OK Google, ルームライトを使って電気つけて」と1フレーズで操作ができるのでユーザビリティとしては許容範囲だと判断だし、後者で実装することにしました。

2-3-2. DialogflowのIntentsでFulfillmentを有効化する

Action on GoogleのActionsからDialogflowの編集画面を開いて、インテントを設定します。点灯用のインテントの設定は以下の通りです。点灯用インテントから先ほど設定したRunKitの点灯用コードを実行するため、最後の「Enable webhook call for this intent」を有効にします。

f:id:sonomirai:20181202235940p:plain:w400
点灯用インテントの設定

2-3-3. IntentsとFulfillmentの関数を紐付ける

DialogflowのFulfillmentに移動して、RunKitのエンドポイントを実行するための設定をしようとしたのですが、ここでつまづきました。感覚的に、Intentsとそれに対応するFulfillmentを1:1で紐付けするためのGUIがあるはずだと思ったのですが、Intentsの設定画面にもFulfillmentの設定画面にもそのようなGUIがなかったのです。
そこで有識者の方に伺って、IntentsとFulfillmentを紐付けするGUIはないので、そこは自分でIntentsとFulfillmentを紐付けするコードと振分けするコードを書く必要がある、と教えていただきました。(よく見るとInline Editorのサンプルコードにこの辺りのことが書いてありましたが、気づきませんでした。。。)

f:id:sonomirai:20181207215445p:plain:w500
インテントとフルフィルメントのマッピング

それではIntentsとFulfillmentを紐付けます。Fulfillmentから外部サービスを呼び出すこともできますが、今回は素直にInline Editorに紐付けのコードを書くことにしました。コードはこちらです。(Inline Editorの裏ではCloud Functions for Firebaseが動くので、いくらか課金されます。)
ここまで設定してActions on GoogleのSimulatorを実行すると、「ルームライトを使って電気つけて」で電気をつけられるようになり、実運用できるようになります。

f:id:sonomirai:20181207224547p:plain:w300
Simulator実行画面

2-4.【UI改善】Google Home周りのユーザビリティを上げる

2-3までの実装で、音声で電気のON/OFFが可能になったので、実際に使い始めたのですが、使って見ると以下のような問題が発生しました。(Google Homeが認識した発話の内容はGoogle Homeアプリのマイアクティビティから確認します。)

  1. 「ルームライトを使って消灯」が「ショート」に誤認識される
  2. ルームライトを使って消灯」が「ウムラウト」に誤認識される
  3. 「ルームライトを使って消灯」をついつい「ルームライト消灯」と言ってしまう

問題3は実装の初期段階から懸念していて、気をつければ大丈夫だろうと思っていたのですが、たまに間違った発話をしてしまうことがありました。
結論から言うと問題1はDialogflowのEntitiesでフレーズ名にエイリアスをつけることで解決できました。また問題2, 3はGoogle Homeアプリのルーティンで発話にエイリアスをつけることで解決できました。それぞれ述べます。

2-4-1. DialogflowのEntitiesでフレーズ名にエイリアスをつける

問題1を解決するため、ルームライト内で「ショート」と認識したら「消灯」と捉えるようにエイリアスをつけます。この設定はDialogflowのEntitiesから以下のように設定します。

f:id:sonomirai:20181208114041p:plain:w400
Entitiesの設定

Simulatorで期待する動作をすることを確認します。

f:id:sonomirai:20181208114200p:plain:w300
Simulator実行画面

2-4-2. Google Homeアプリのルーティンで発話にエイリアスをつける

次に「ルームライト」が「ウムラウト」と誤認識される問題と「ルームライト」と言ってしまい、期待する動作を得られない問題に対処します。前述の通り、今回は「プロジェクト名を使ってフレーズ名」という発話をする想定でいます。問題1はフレーズ名の誤認識に対する対処で、これはDialogflowの設定で解決できたのですが、問題2, 3はDialogflowの世界の外の話なので、Dialogflowの設定では解決できません。
そこで有識者の方に伺ったところ、Google Homeアプリのルーティンでエイリアスを設定できると教えてもらいました。(ルーティンはエイリアスをつけるだけではなく色々なことに使える機能なのですが、説明は割愛します。)この機能はとても強力で「ルームライトを使って電気つけて」に対して「電気つけて」というエイリアスをつけることができるので、「ルームライトを使って」の発話がいらなくなり、問題2, 3が発生しないようにできるのです!(これに気付いた時はめちゃめちゃうれしかった。)

ルーティンの設定は「Google Homeアプリ>GOOGLEアシスタント>その他の設定>アシスタント>ルーティン」から行います。

f:id:sonomirai:20181208160152p:plain:w500
ルーティンの設定

これでプロジェクトの保守性を保ちながら、短い発話で操作することが可能になりました!

3. 運用編

今回作成したアプリを2週間くらい使っての感想です。

  • 就寝時に電気を消しにスイッチまで歩く必要がなくなり、便利になった。(特に最近寒いので、ベッドから出ないで済むのが有難い。)
  • 発話からサーボモータが動くまでのタイムラグ、およびGoogle Homeがしゃべり出すまでのタイムラグが気になるといえば気になりなるので、そこは今後改善したいです。
  • この記事で紹介した内容に加えて、obnizのイベント機能を使って毎朝自動で電気をつける設定もし他のですが、これが思った以上にいい感じでした。今までは目覚まし時計に叩き起こされていたので朝起きるのが辛かったのですが、今は自動で電気がついた5〜10分後に目がパッと覚める感覚になり、気持ちよく起きれるようになりました。

f:id:sonomirai:20181208161915p:plain:w300
イベント設定(時刻指定はUTC

画像の中で設定しているprivate_LightOn.htmlはこちらです。

まとめ

obnizとサーボモータで部屋の電気をON/OFFできるようにして、Google Homeから音声操作できるようにしました。またobnizのイベント機能を使って毎朝自動で電気をつける設定もしました。両方とも2週間くらいいい感じで使えているので、今後も継続して使おうと思っています。

SORACOM IoTもくもく会参加レポート

SORACOM主催のIoTもくもく会に参加してきた。
手順はGitHubにあるので詳細省略するが、ざっくり言うと「Raspberry Piに接続したセンサデータ(*)をインターネット上にSoracom Airで送って、送ったデータをSoracom Beamでセキュアに連携したり、Soracom Harvestで可視化する」というハンズオンだった。
Rasberry Piと超音波センサーとSORACOM Airがあれば、家で一人ででもできそう。

けっこう時間がカツカツでHarvestでの可視化まではたどり着けなかったのが悔しいが、IoTの手触りとSORACOMがIoTやる時に担う役割について体験できてよかった。

書き残しておきたいが2つあるので、記しておく。

1つめ。SORACOMのサービスの中心にはAirがいて、Airを使ってIoT始めようとした時にユーザが困るところを他のサービスでケアする構成っぽいと理解した。 AirはモバイルWifi用途でずっと使っていたが、Beamはじめとしたその他のサービスはいまいち用途がわからなくて真面目に追っていなかった。 今日のハンズオンで、Raspberry Piなどのデバイス側でセキュアな通信を実装するのは手間なので、Beamでクラウド側に処理をオフロードすると言う話を聞き、そこで初めてBeamはじめとしたSORACOMのサービス群の用途のイメージが湧いた。

2つめは、IoT始めるにはモノ(センサ)、インターネット、クラウドの3つが必要という話。
IoTに必要なのはモノ、インターネット、サーバだと思っていたので、白目を向いてしまった(笑) なんというか、時代の波に乗れるのは、その前の波にちゃんと乗れていた人だなぁと思った。
基盤自動化の波に乗れるのは、それ以前にソフト開発のプラクティスを実践していた人だけ。
IoTの波を乗れこなせるのは、それ以前にクラウド上でシステム開発していた人だけ。
そこを意識せず流行りに飛びついても溺れるだけなので、そこは要注意だと思った。

深夜のノリでポエムになってしまったが、IoTもくもく会は勉強になった。参加してよかった。
別のテーマの時にまた参加したい。

サクッとBCryptで文字列を暗号化する

コマンドライン引数で文字列をBCryptで暗号化するコード書いたのでメモしておく。
Mavenなどのパッケージ管理ツール使わず、とにかくサクッとやる想定。

パッケージダウンロード

利用するライブラリをサイトから手動ダウンロードする。

spring-security-coreだけだと実行時にcommons-loggingがないというjava.lang.NoClassDefFoundErrorが出力されるので、とりあえず追加。

実装

これらのライブラリをlib配下に格納し、以下の2クラスを実装する。
引数をBCryptで暗号化するCLBCryptPasswordEncoder.java

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.apache.commons.logging.LogFactory;

public class CLBCryptPasswordEncoder{
  public static void main(String args[]){

    String textPassword = args[0];

    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    System.out.println(encoder.encode(textPassword));
  }
}

平文パスワードと暗号化済パスワードが一致するか確認するCLBCryptPasswordMatcher.java

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.apache.commons.logging.LogFactory;

public class CLBCryptPasswordMatcher{
  public static void main(String args[]){
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    String textPassword = args[0];
    String encryptedPassword = args[1];

    if (encoder.matches(textPassword, encryptedPassword)) {
      System.out.println("matched");
    } else {
      System.out.println("mismatched");
    }
  }
}

ディレクトリ構成

ディレクトリ構造は以下のとおり。

  • CLBCryptPasswordEncoder.java
  • CLBCryptPasswordMatcher.java
  • lib/
    • commons-logging-1.2.jar
    • spring-security-core-5.0.2.RELEASE.jar

コンパイル

$ javac -cp "lib/*" CLBCryptPasswordEncoder.java
$ javac -cp "lib/*" CLBCryptPasswordMatcher.java

カレントディレクトリにクラスが生成される。

実行

暗号化と検証の実行。classpathにカレントディレクトリを追加。

$ java -cp "lib/*:." CLBCryptPasswordEncoder sonomirai
$2a$10$ALcXPgrpOQKXoIyrgS90huCbgR906LtWrH1dOsZmHtBZdSB19n9Bi
$ java -cp "lib/*:." CLBCryptPasswordMatcher sonomirai '$2a$10$ALcXPgrpOQKXoIyrgS90huCbgR906LtWrH1dOsZmHtBZdSB19n9Bi'
matched
$ java -cp "lib/*:." CLBCryptPasswordMatcher dummypswd '$2a$10$ALcXPgrpOQKXoIyrgS90huCbgR906LtWrH1dOsZmHtBZdSB19n9Bi'
mismatched

こんな感じでやりたかったことはやれた。

Java本格入門13章をdocker上で写経する

はじめに

Java本格入門の13章「周辺ツールで品質を上げる」を写経した。
Maven使った操作はdocker上で写経したので、その時の手順を記録しておく。
誰かの役に立てば幸い。なお写経したコードはこのリポジトリにある。

準備

HTML形式のレポートを見るため、centos/httpdイメージからコンテナを起動する。

コンテナ起動

$ docker run --name javabook -h javabook -d -p 8080:80 centos/httpd

http状態確認

ブラウザでhttp://localhost:8080にアクセスして「It works!」と表示されることを確認する。

コンテナ内に入る

$ docker exec -it javabook /bin/bash

パッケージインストール

[root@javabook /]# yum install -y vim git java maven

Javamavenバージョン確認

[root@javabook /]# java -version
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
[root@javabook /]# mvn -version
Apache Maven 3.0.5 (Red Hat 3.0.5-17)
Maven home: /usr/share/maven
Java version: 1.8.0_151, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el7_4.x86_64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.9.49-moby", arch: "amd64", family: "unix"

Git準備

[root@javabook /]# git config --global user.email <メールアドレス>
[root@javabook /]# git config --global user.name <ユーザ名>
[root@javabook /]# git clone https://github.com/acroquest/javabook-maven-example.git
Cloning into 'javabook-maven-example'...
remote: Counting objects: 42, done.
remote: Total 42 (delta 0), reused 0 (delta 0), pack-reused 42
Unpacking objects: 100% (42/42), done.
[root@javabook /]# cd javabook-maven-example/

これで準備終わり。書籍の内容に入る。(書籍にはEclipseの手順とMavenの手順の両方が
掲載されているが、この記事ではMavenの手順のみ記載するので、章番号はとびとび。)

13-1-2 Mavenの基本的な利用方法

[root@javabook javabook-maven-example]# mvn package
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building javabook-maven-example 1.0-SNAPSHOT
[INFO] ---------------------------------------------------
〜中略〜
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.java.book.app.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.191 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
〜中略〜
[INFO] Building jar: /javabook-maven-example/target/javabook-maven-example-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:13.426s
[INFO] Finished at: Tue Nov 28 14:13:10 UTC 2017
[INFO] Final Memory: 18M/157M
[INFO] ------------------------------------------------------------------------

man packageを実行するとビルドが始まって、問題なければBUILD SUCCESSが表示される。
ログを見ていて、とくに設定した覚えはないのにテストが実行されていることに気づいた。
そこでちょっと検証してみたところ、pom.xmldependencyJUnitの記載があるかつsrc/test配下にテストファイルがあると、mvn packageでテストが実行されるように見えた。
pluginではJUnitを指定しておらず、dependencyを指定しただけなのに、テストが実行されて驚いたが、そんなものなんだろうか。
mvn installを実行するとローカルリポジトリにビルド成果物が登録される。ログには以下の通り出力される。

[INFO] Installing /javabook-maven-example/target/javabook-maven-example-1.0-SNAPSHOT.jar to /root/.m2/repository/com/java/book/app/javabook-maven-example/1.0-SNAPSHOT/javabook-maven-example-1.0-SNAPSHOT.jar
[INFO] Installing /javabook-maven-example/pom.xml to /root/.m2/repository/com/java/book/app/javabook-maven-example/1.0-SNAPSHOT/javabook-maven-example-1.0-SNAPSHOT.pom

13-2-4 APIドキュメントを作成する

APIドキュメント確認

pom.xmlmaven-javadoc-pluginの設定を記述してmvn siteを実行する。
生成されたHTMLをhttpdのドキュメントルートに配置する。

[root@javabook javabook-maven-example]# cp -rp target/site/apidocs/* /var/www/html/
[root@javabook javabook-maven-example]# 

ブラウザでhttp://localhost:8080にアクセスしてAPIドキュメントを確認する。
f:id:sonomirai:20171128234402p:plain

13-3-3 Mavenによるフォーマットチェック

pom.xmlmaven-checkstype-pluginの設定を記述してmvn clean package siteを実行する。
生成されたHTMLをhttpdのドキュメントルートに配置する。

[root@javabook javabook-maven-example]# rm -rf /var/www/html/*
[root@javabook javabook-maven-example]# cp -rp target/site/* /var/www/html
[root@javabook javabook-maven-example]# 

ブラウザでhttp://localhost:8080にアクセスする。 f:id:sonomirai:20171202214806p:plain

Project ReportsのCheckstyleをクリックしてレポートを見る。けっこうErrorがある。。。 f:id:sonomirai:20171202214811p:plain

とりあえずErrorを0にするよう修正したが、あまり意味のある変更とは思えなかった。
Checkstyleの使い方は今度の課題かな。。。 f:id:sonomirai:20171202215438p:plain

13-4-4 Mavenによるバグチェック

pom.xmlにfixbugs-maven-pluginの設定を記述してmvn clean package siteを実行する。
生成されたHTMLをhttpdのドキュメントルートに配置する。

[root@javabook javabook-maven-example]# rm -rf /var/www/html/*
[root@javabook javabook-maven-example]# cp -rp target/site/* /var/www/html
[root@javabook javabook-maven-example]# 

ブラウザでhttp://localhost:8080にアクセスし、FindBugsのレポートを見る。 f:id:sonomirai:20171202220406p:plain

13-5-3 テストを実行する

書籍だとEclipseで実行しているところを、Mavenで実行してみる。
先ほどまでのリポジトリ内に、書籍に掲載されているテスト用プログラム(わざとバグを入れてある)とテストコードを作成する。
mvn clean package siteを実行するとテストがコケてビルドに失敗する。
日本語が正しく出力できていない。。。そこは今後の課題。。。

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.java.book.app.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.101 sec
Running acroquest.java.junit.GreetingTest
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.019 sec <<< FAILURE!

Results :

Failed tests:   getMessage_\u591C\u958B\u59CB(acroquest.java.junit.GreetingTest): 
  getMessage_\u663C\u958B\u59CB(acroquest.java.junit.GreetingTest): 

Tests run: 4, Failures: 2, Errors: 0, Skipped: 0

コード修正して再実行するとテストに成功する。
JUnitの結果もCheckstyle, FindBugsと同様にレポート出力されるかと思ったが、されなかった。
実際はJUnitの結果はJenkinsで出力させることになるので、気にせず次に進む。
次の章に向けて、Gitのローカルリポジトリをリモートリポジトリにコミットしておく。

13-6-2 Jenkinsの環境を準備する

書籍だとWindowsにJenkinsをインストールしているところを、DockerのJenkinsコンテナを立ち上げて利用する。

Jenkinsコンテナ準備

Jenkinsコンテナを立ち上げて、Mavenをインストールする。

$ docker run --name javabook_jenkins -h javabook -u 0 -d -p 8081:8080 -p 50000:50000 jenkins
$ docker exec -it javabook_jenkins /bin/bash
root@javabook:/# apt-get update; apt-get install -y maven

Jenkins状態確認

ブラウザでhttp://localhost:8081にアクセスして以下の画面が表示されることを確認する。 f:id:sonomirai:20171202224921p:plain

Jenkins設定作業

書籍に掲載された方法で設定していく。(「Create First Admin User」画面は、どうもユーザが
作成できなかったので、「Continue as admin」を選択して先に進んだ。)

13-6-3 Jenkinsでビルドを実行する

書籍の手順通り設定する。ソースコードの取得元の設定では、先ほどコミットしたリモートリポジトリを設定する。 f:id:sonomirai:20171202231531p:plain

とくに問題なくビルド成功するはず。 f:id:sonomirai:20171202231754p:plain

13-6-4 Jenkinsでレポートを作成する

書籍の手順通り設定すると、Checkstyle, FindBugs, JUnit, カバレッジのレポートが出力される。
おお、こんな感じの画面を見てみたかった。よかった。 f:id:sonomirai:20171202233008p:plain

JUnitの結果もこんな感じで表示される。

f:id:sonomirai:20171202233048p:plain

さいごに

Java本格入門の13章「周辺ツールで品質を上げる」をdocker環境で写経する手順について書いた。
JavaアプリってWindows上のEclipseで開発してLinuxで動作させることが結構あると思うが、
これまで環境ごとの周辺ツールの使い方やツール同士の連携のさせ方がわかっていなかった。
13章を写経することで、その辺りの使い方がはっきり理解できてよかった。

ゼロから作るDeep Learning 5.7.2項のTwoLayerNetを理解するために書いた図

ゼロから作るDeep Learning 5.7.2項のTwoLayerNetの処理が、初見では理解できなかった。
そこで書籍の内容を復習して図示してからTwoLayerNetを読んだところ、かなり理解が進んだので、その時書いた図を残しておく。f:id:sonomirai:20170923205219p:plain

TwoLayerNet理解ためには、ReLU、Sigmoid、Softmaxは各層の活性化関数で、Affineは層の間の重み(重みは行列で表される)の内積ということがイメージできている必要があると思うが、自分は最初そこがイメージできていなくて苦戦したので、そこをサポートするような図を描いたつもり。

Selenium3をMacで動かす

Selenium使うことになったので、Selenium実践入門読みながらインストールしようと思ったら、Seleniumのバージョンが3に上がってて、パッケージ構成から違ってて涙目になった。
ググったところ、下記の記事が参考になったが、一部つまづくところがあったので、自分のためにメモを残す。
なお対象のブラウザはFirefoxChrome

Selenium入門その6[Selenium3でWebDriver(Java/Junit4)の環境を作成しEdge,Chrome,Firefoxで確認してみる]

環境情報

ソフト バージョン
Mac OS X El Capitan 10.11.6
Selenium 3.4.0
mozilla Firefox 54.0.1
geckodriver 0.18.0
Google Chrome 59.0.3071.115
chromedriver 2.30

前提

  • Eclipseがインストール済みであること
  • Firefoxがインストール済みであること
  • Chromeがインストール済みであること FirefoxChromeがインストール済みであることの確認は、LaunchpadでFirefox, Chromeで検索して、プログラムが見えればOKだと思う。

環境構築

基本は参考ページ通りでいいが、chromedriverはPATHの通ったディレクトリに配置しておかないと、実行時にエラーになるっぽい。
自分は/usr/local/bin配下に配置した。

実行

参考ページのソースを以下のように微修正して実行した。

package sample.selenium3;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class Selenium3Sample {

    @Test
    public void firefoxTest() {
        System.setProperty("webdriver.gecko.driver", "./exe/geckodriver");
        DesiredCapabilities cap = DesiredCapabilities.firefox();
        cap.setCapability("marionette", true);
        WebDriver driver = new FirefoxDriver(cap);
        driver.navigate().to("http://www.google.com");
        driver.findElement(By.id("lst-ib")).sendKeys("Selenium3");
        driver.findElement(By.name("btnK")).click();
        if(driver!=null) {
            driver.close();
        }
    }
    
    @Test
    public void chromeTest() {
        System.setProperty("webdriver.chromedriver.driver", "");  //chromedriverはPATHの通っている/usr/local/binに配置
        WebDriver driver = new ChromeDriver();
        driver.navigate().to("http://www.google.com"); 
        driver.findElement(By.id("lst-ib")).sendKeys("Selenium3");
//     driver.findElement(By.name("btnK")).click();              //動かなかった
        driver.findElement(By.id("lst-ib")).sendKeys(Keys.ENTER);    //動いた
        if(driver!=null) {
            driver.close();
        }
    }
}

参考ページからの変更点は3点。

  1. DesiredCapabilities周りの設定の削除
    なしでも動いたので削除した。Seleniumのバージョンが上がって、なしで動くようになった?
  2. chromedriverのPATH指定
    exe配下にchromedriver置いてもちゃんと読んでもらえず、エラーになってしまう。
    PATHの通ったところにchromedriverを配置したら読んでもらえたので、setPropertyの第二引数は明示的に空にしておいた。
  3. chromedriverでの検索実行
    「driver.findElement(By.name(“btnK”)).click();」だと動かなかったので、とりあえず「driver.findElement(By.id(“lst-ib”)).sendKeys(Keys.ENTER);」にした。

まとめ

参考ページをベースにちょっと変更をしたら、Selenium3を動かすことができた。参考ページに感謝。

STSをMacにインストールして日本語化する

同じような手順がネットに載っているのは重々承知しているが、
その通りやっても動かなかったので、成功した手順をメモで残す。

バージョン情報

Mac: OS X El Capitan 10.11.6
STS: 3.8.4
pleiades:1.7.27

手順

  1. STSサイトからダウンロードする
  2. 解凍する
    tar -zxvf spring-tool-suite-3.8.4.RELEASE-e4.6.3-macosx-cocoa-x86_64.tar.gz
  3. sts-bundleディレクトリ配下のSTSをダブルクリックして起動する。
    日本語化前に一度STSを起動するこの手順を抜かすと、日本語化した後STSが起動しない気がするので、やっておく。
    ワークスペースを開いてIDEの画面が見えるところまで確認したら閉じる。
  4. pleiadesサイトからダウンロードする。
    Pleiades All in One」ではなく、最新版ダウンロードからダウンロードすることに注意。
  5. 解凍する
    unzip pleiades.zip -d pleiades
  6. pleiadesディレクトリ内のfeaturesとpluginsをコピーする。ここはGUIの想定で説明する。
    手順2で作成されたsts-bundleディレクトリ内のSTSファイルを右クリックして「パッケージの内容を表示」をクリックする。
    Contents/Eclipse配下にfeaturesとpluginsディレクトリがあるので、pleiades内の同名のディレクトリ配下のファイル「jp.sourceforge.mergedoc.pleiades」をコピーする。
  7. Contents/Eclipse配下のSTS.iniのバックアップを念のため取っておく。
    cp STS.ini STS.ini.org
  8. STS.iniの末尾にオプションを追記する。
    echo '-javaagent:../Eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar' >> STS.ini
  9. sts-bundleディレクトリ配下のSTSをダブルクリックして起動する。

自分はこの手順で動いた。サンプルのプロジェクトを用いた動作確認はこちらの手順で実施した。