狛ログ

2016年10月31日月曜日

Raspberry Pi 3 で 3G 通信をしてみる。

10月 31, 2016
オフィス狛 技術部です。

Raspberry Pi で 3G 通信をしてみよう! という事で、
4ヶ月ほど前に色々検証したのですが、ブログに載せるのが遅れてしまいました。

検証に使用したのは、「3GPi」という通信モジュールですが、
検証内容をブログに載せるのが遅れている間に、「3GPi」の販売元の会社から、
「anyPi(エニーパイ)」というスターターキットが出てしまいました。

約3分でセットアップできる! ラズパイIoT向けスターターキット 「anyPi」

ホント、IT業界ってのは時の流れが早いです。
しかし、ブログに載せるのを遅れた自分への戒めの為、今このタイミングで敢えて、
上記スターターキットを使わない方法を載せたいと思います。

大まかな手順は以下の通りです。

1)SORACOM アカウント登録から SIM 登録まで

2)Raspberry Pi 3 への 3GPI 取付と設定

3)通信設定&通信速度を測ってみる


今回、実験するのに用意したのは、以下の機器です。

  • SORACOM Air SIM カード(データ通信のみ) (ナノ)
  • Raspberry Pi 3 Model B (Element14)
  • REMAX SIM 変換 アダプター 4 点セット ナノシム
  • 3GPI(Raspberry Pi 向け 3G 通信モジュール)

そして、またもや言い訳になってしまいますが、載せる情報は、少し古い可能性がありますので、
最新の情報は、各メーカーのホームページ等を参照して頂くようお願いします。

では早速、最初の手順を進めて行きます。

1)SORACOM アカウント登録から SIM 登録まで
http://blog.officekoma.co.jp/2016/10/raspberry-pi-3-3g-soracom-sim.html


2016年10月30日日曜日

SwiftにおけるUIImageView・contentMode 別の画像表示

10月 30, 2016


オフィス狛 技術部です。

実装をしていると「あれ?これどうやるんだっけ?」
ということが人並み以上に多いため、
よく忘れるあれなものをどんどんメモします。

今回は、SwiftでUIImageViewmにImageを設定するときのcontentModeの表示方法
※画像は320 x 600 の縦長の画像を使い、300 x 300のImageViewの中に表示するようにしています。


1) サイズ指定無し(アスペクト比維持)の画像



2) ScaleToFill = 縦横の比率を変えて全体を表示する
imageView.contentMode = UIViewContentMode.ScaleToFill




3) Aspect Fit = 縦横の比率はそのままで長い辺を基準に全体を表示する
imageView.contentMode = UIViewContentMode.ScaleAspectFit




4) Aspect Fill = 縦横の比率はそのままで短い辺を基準に全体を表示する
imageView.contentMode = UIViewContentMode.ScaleAspectFill




5) Redraw = Aspect Fillとの違いはUIImageViewのサイズ変更に追随してリサイズしてくれる。
imageView.contentMode = UIViewContentMode.Redraw




6) Center = 画像サイズを変えずに真ん中を基準にして表示する
imageView.contentMode = UIViewContentMode.Center




7) Top = 画像サイズを変えずに上辺を基準にして表示する
imageView.contentMode = UIViewContentMode.Center




8) Bottom = 画像サイズを変えずに下辺を基準にして表示する
imageView.contentMode = UIViewContentMode.Bottom




9) Left = 画像サイズを変えずに左辺を基準にして表示する
imageView.contentMode = UIViewContentMode.Left




10) Right = 画像サイズを変えずに右辺を基準にして表示する
imageView.contentMode = UIViewContentMode.Right




11) Top Left = 画像サイズを変えずに左上を基準にして表示する
imageView.contentMode = UIViewContentMode.TopLeft




12) Top Right = 画像サイズを変えずに右上を基準にして表示する
imageView.contentMode = UIViewContentMode.TopRight




13) Bottom Left = 画像サイズを変えずに左下を基準にして表示する
imageView.contentMode = UIViewContentMode.BottomLeft




14) Bottom Right = 画像サイズを変えずに右下を基準にして表示する
imageView.contentMode = UIViewContentMode.BottomRight




以上、ほとんど自分の為の備忘録です。はい。


2016年9月11日日曜日

知らねばほっとけとはいかない Codeigniter3 独自 バリデーション チェック に 引数 を渡す

9月 11, 2016


オフィス狛 技術部です。

第1回(小石につまずくとけっこう痛い Codeigniter3 ファイル命名規約)、
第2回(小石につまずくとけっこう痛い Codeigniter3 複数ファイル同時アップロード)、
第3回(小石につまずくとけっこう痛い Codeigniter3 csrf_token の 有効期限切れ エラー をキャッチ)に引き続き、

今回も PHP ( Codeigniter3 )で地味につまずいたことと書こうと思っております。
今回は、
『 独自のバリデーションチェック に引数を渡す 』
です。

独自バリデーションチェックの作成方法は本家サイトにお任せします。
Form Validation — CodeIgniter 3.1.0 documentation callbacks-your-own-validation-methods

ちなみに
 $this->form_validation->set_rules() に設定する際に
メソッドの先頭に「 callback_ 」を付けなければいけないというルールを知らずに
ちょっとつまってしまった経験があるので、一応記載しておきます。
チェックメソッドが
username_check()
ならば、
$this->form_validation->set_rules('username', 'Username', 'callback_username_check');
と書く必要があるということですね。

では、このチェックメソッドにどうやって引数を渡すのか?

調べた結果、2つ方法があるようです。
1.  $this->input->post() で取得する
$this->form_validation->set_rules('uri', 'URI', 'callback_check_uri');
function check_uri(){
 $this->input->post('url');
 $this->input->post('id');
}

2. $this->form_validation->set_rules()の中に書く
$this->form_validation->set_rules('uri', 'URI', 'callback_check_uri['.$this->input->post('id').']');
function check_uri($field, $id){
 // $fieldにはバリデーション対象のPOSTデータが自動で入ります。
}
※CI2.1以上であることが条件だそうです。

個人的には
文字列結合して引数を渡しているのが
どうしても無理やりな感じがしているので
1の方がいいなと思うのですが
公式ドキュメントでは2の方法が書かれています。

1の場合ですと、引数が増えても楽に対応出来そうですよね。

ちなみに2のような形式で行う場合は、

$this->form_validation->set_rules('uri','URI','callback_check_uri[id,method]');
function check_picture($image,$param){
 $param = preg_split('/,/', $param);
 $id = $param[0];
 $method = $param[1];
}
と書けば上手くいくようです。
※公式ではないようですが。。。

チェック自体は単純にPOSTデータを取得してチェックが可能ですが、
その後のエラーメッセージの表示などを考えると他のバリデーションチェックと
同じようにできるのは便利ですし、可読性・保守性が向上するのではないかと思います。

知らねばほっとけではいけませんね。

2016年9月2日金曜日

小石につまずくとけっこう痛い Codeigniter3 csrf_token の 有効期限切れ エラー をキャッチ

9月 02, 2016
オフィス狛 技術部です。

前々回(小石につまずくとけっこう痛い Codeigniter3 ファイル命名規約)、
前回(小石につまずくとけっこう痛い Codeigniter3 複数ファイル同時アップロード)に引き続き、

今回も PHP ( Codeigniter3 )で地味につまずいたことと書こうと思っております。

何度も言いますが
 Codeigniter は日本語の情報が少ない!!!
ということでよくつまずきます。

なので、自分の調べた備忘録も兼ねて、
ここにつまずきろく(記録)をためていきたいと思います。

今回のつまずきは、
『 csrf_token の有効期限切れ エラーをキャッチする 』
でした。

デフォルトの設定ですと「 error_general.html 」が表示されるようになっていますが、
弊社では、「操作に失敗しました。」といったようなその名の通り全般的なエラーの内容を表示するようにしています。

画面を放置していて動かしてみたらいきなり「操作に失敗しました。」と出るのは違和感があるので、そのままページを再読み込するようにしてほしいとの要望がありました。

どうやるのかパッと思いつかず、つまずき発生。
で、色々調べてみた結果、

「 CI_Security 」を継承して「 csrf_show_error() 」メソッドをオーバーライドすればよかったようです。

class MY_Security extends CI_Security {
    public function __construct()
    {
        parent::__construct();
    }
    public function csrf_show_error()
    {
        // エラー画面を表示したり
        // show_error('The action you have requested is not allowed.');

        // ページをリフレッシュしたり
        // header('Location: ' . htmlspecialchars($_SERVER['REQUEST_URI']));
    }
} 
参考にしたサイト:
http://stackoverflow.com/questions/14406922/prevent-from-displaying-the-default-csrf-error-page-in-codeigniter

そういえば、独自のエラーハンドリングを行う際も、
「 CI_Exceptions 」を継承してエラーメソッドをオーバーライドするのと同じだなと後々気が付きました。

パッと出てこないということはまだまだ習熟が足りてない証拠です。
改めてドキュメントを見返してみるなど勉強が必要だなと思う今日この頃でした。

足元の確認は大事ですね。

2016年8月17日水曜日

CocoaPods で undefined method inherit が出た場合の対処方法

8月 17, 2016
オフィス狛 技術部です。

今回は iOS のライブラリ管理ツールである CocoaPods で発生したエラーについて書いてみます。

※CocoaPods とは、Androidでいうところの Gradle みたいなもので、
ライブラリ管理においては、非常に有用なツールです。


弊社のGit管理上、Pods 以下は gitignoreに入れています。
その為、Gitからプロジェクトをクローンして、最初にやるのが、ターミナルでの
pod update
になります。

今回も、意気揚々とコマンド入力したら・・・

[!] Invalid `Podfile` file: undefined method `inherit!' for #. Updating CocoaPods might fix the issue.

 #  from /Users/officekoma/testproj/Podfile:18
 #  -------------------------------------------
 #    target 'SampleTests' do
 >      inherit! :search_paths
 #      # Pods for testing
 #  -------------------------------------------

[!] Your Podfile has had smart quotes sanitised. To avoid issues in the future, you should not use TextEdit for editing it. If you are not using TextEdit, you should turn off smart quotes in your editor of choice.

あらら。「inherit なんてメソッド知らない!」と怒られてしまいました。
調べてみると、inherit は「cocoapods-1.1.0.beta.1」より追加されたメソッドのようです。
という訳で、ターミナルで
pod --version
と打ってみます。
結果は・・・1.0.1

どうやら、このプロジェクトを作成した端末の CocoaPods と、
私の端末の CocoaPods はバージョン違っているようです。

では、バージョンアップしようという事で、今度はターミナルで、
sudo gem install cocoapods --pre
と打ってみます。(「--pre」はbeta版をインストールする為に必要となります。)

ERROR:  While executing gem ... (Errno::EPERM)
    Operation not permitted - /usr/bin/xcodeproj

またエラー・・・

ただ、このエラーは割と有名です。
mac の OS が El Capitan 以降の場合、セキュリティ上の仕様で、
「/usr/bin」にはroot権限でもアクセス制限が掛かってしまいます。

インストールディレクトリを変える必要があるので、ターミナルで、
sudo gem install -n /usr/local/bin cocoapods --pre
と入力し、今度こそ 1.1.0.beta.1 のインストールに成功。

「pod setup」で、セットアップを行い、バージョンを確認すると・・・1.0.1
・・・あれ?確かにインストールは成功したのに・・・。
デフォルトで適用されるものを最新バージョンで差し替えてくれる訳では無いのですね。
(まあ、そりゃそうですよね。)

という訳で、ターミナルで
sudo gem uninstall cocoapods
と入力し、

Select gem to uninstall:
 1. cocoapods-0.39.0
 2. cocoapods-1.0.1
 3. cocoapods-1.1.0.beta.1

1.1.0.beta.1 以外のバージョンを削除します。
これで無事に「pod update」まで出来ました。

後書き

上記はあまり良い方法とは言えませんのでご注意下さい。
複数メンバーで開発していると、 CocoaPods のバージョンが異なる事はよくあります。
(今回は、undefined method でそれが発覚しました)

通常は、それを避ける為に bundler を導入します。

・・・が、そもそも、
CocoaPodsの開発元は、Pods 以下は gitignoreに入れない事を推奨しています。

CocoaPods Guides -Using CocoaPods

確かに、Gitからクローンしただけで、ビルド出来るのが最適ですからね。

2016年8月11日木曜日

小石につまずくとけっこう痛い Codeigniter3 複数ファイル同時アップロード

8月 11, 2016
オフィス狛 技術部です。

前回(小石につまずくとけっこう痛い Codeigniter3 ファイル命名規約)に引き続き、
今回も PHP ( Codeigniter3 )で地味につまずいたことを書こうと思います。

何度も言いますが
 Codeigniter は日本語の情報が少ない!!!
ということでよくつまずきます。

なので、自分の調べた備忘録も兼ねて、
ここにつまずきろく(記録)をためていきたいと思います。

今回は、
『 複数のファイルをまとめてアップロードする 』
です。

1つのファイルをアップロードする例は本家サイトにも載っているので割愛。
https://www.codeigniter.com/userguide3/libraries/file_uploading.html

何が困ったかというと、

1. そもそも複数のファイルデータってどうやってとるの?

2. アップロードメソッドに値ってどうやって設定するの?

という2点。


結論から言うと、どちらも単純な PHP の話。

1. そもそも複数のファイルデータってどうやってとるの?

<input name="images[]" type="file" />

であれば、

$_FILES['images']でファイルデータの配列が取れます。

2. アップロードメソッドに値ってどうやって設定するの?

アップロードメソッド( $this->upload->do_upload() )
は1つずつのアップロードしか対応していないようなので、
ファイルデータの配列から1つずつループしてアップロードしていく必要があります。

$config = array(
   'upload_path' => './uploads/',
   'allowed_types' => 'jpg|gif|png',
   'overwrite' => true,
);

foreach ($files['name'] as $key => $image) {
   $_FILES['images']['name']= $files['name'][$key];
   $_FILES['images']['type']= $files['type'][$key];
   $_FILES['images']['tmp_name']= $files['tmp_name'][$key];
   $_FILES['images']['error']= $files['error'][$key];
   $_FILES['images']['size']= $files['size'][$key];
   $config['file_name'] = $image;
   $this->upload->initialize($config);
   if ($this->upload->do_upload('images')) {
    $this->upload->data();
   } else {
    return false;
   }
}

1つ1つ値を設定し直してアップロードしていくだけの話でした。


フレームワークを使っているとデータを勝手に設定してくれたりするので、
『枠内』だけで考えてしまいがちになってしまう今日この頃。


足元の確認は大事ですね。

2016年8月2日火曜日

URLConnection(HttpURLConnection)と向き合おう~POSTメソッドでJSONを送信する~

8月 02, 2016
オフィス狛 技術部です。

URLConnection(HttpURLConnection)と向き合うために、今回はPOSTメソッドを使います。
POSTはPOSTでも特にHTMLのフォームタグでPOSTを行ったときと同様のデータを送信します。

今回の目標

  • HttpURLConnectionを使ってPOSTメソッドを行う。
  • ContentTypeはapplication/jsonの形式にする。
  • POSTメソッドのレスポンスデータを取得する。

HttpURLConnectionを使ったPOSTのやり方

HttpURLConnection はGET/POSTに関係なくメソッドの呼び出し順序は大体決まっています。
順序は下記のとおりです。
  • ステップ1 :接続URLを決める。
  • ステップ2 :URLへのコネクションを取得する。
  • ステップ3 :接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。
  • ステップ4 :コネクションを開く
  • ステップ5 :リクエストボディの書き込みを行う。(POSTのみ行う)
  • ステップ6 :レスポンスの読み出しを行う。
  • ステップ7 :コネクションを閉じる。

ステップ1:接続URLを決める。

接続先URLを決めるには java.net.URL を使います。
URL url = new URL("[接続先のURLを設定してください。]");

ステップ2:URLへのコネクションを取得する。

接続URLのコネクションを取得することができます。
URL url = [ステップ1:接続URLを決めるを参照してください];
HttpURLConnection urlConnection = null;
try {
    urlConnection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
    e.printStackTrace();
}

ステップ3:接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。

今回はPOSTを使ったデータの送受信を行うので特に下記の設定を行う必要があります。
  • setRequestMethod を使いPOSTを使う設定をする
  • リクエストヘッダーの Content-Type を application/json に設定する。
  • リクエストボディにデータを書き込むため、setDoOutput を使い許可をする。
  • レスポンスボディのデータを読み出しするため、setDoInput を使い許可をする。
//接続タイムアウトを設定する。
urlConnection.setConnectTimeout(100000);
//レスポンスデータ読み取りタイムアウトを設定する。
urlConnection.setReadTimeout(100000);
//ヘッダーにUser-Agentを設定する。
urlConnection.setRequestProperty("User-Agent", "Android");
//ヘッダーにAccept-Languageを設定する。
urlConnection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
//ヘッダーにContent-Typeを設定する
urlConnection.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
//HTTPのメソッドをPOSTに設定する。
urlConnection.setRequestMethod("POST");
//リクエストのボディ送信を許可する
urlConnection.setDoOutput(true);
//レスポンスのボディ受信を許可する
urlConnection.setDoInput(true);

ステップ4:コネクションを開く

コネクションを設定した内容で接続し、実際にデータの送受信ができる状態にします。
このメソッド以降は「接続先にデータを送信する OutputStream 」と「接続先からデータを受信する InputStream 」を使うことができるようになります。
//ステップ4.コネクションを開く
urlConnection.connect();

ステップ5:リクエストボディの書き込みを行う。

「application/json」の送信データはRFC7159に準拠した文字列を送信する必要があります。
今回はorg.json.JSONObjectクラスを使いHashMapをJSON文字列に変換します。
//ステップ5:リクエストボディの書き出しを行う。
OutputStream outputStream = urlConnection.getOutputStream();
HashMap<String, Object> jsonMap = new HashMap<>();
jsonMap.put("text" , "value");
ArrayList<String> array = new ArrayList<>();
array.add("array001");
array.add("array002");
array.add("array003");
jsonMap.put("array" , array);
if (jsonMap.size() > 0) {
    //JSON形式の文字列に変換する。
    JSONObject responseJsonObject = new JSONObject(jsonMap);
    String jsonText = responseJsonObject.toString();
    PrintStream ps = new PrintStream(urlConnection.getOutputStream());
    ps.print(jsonText);
    ps.close();
}
outputStream.close();

ステップ6:レスポンスボディの読み出しを行う。

主なレスポンスデータは下記のメソッドを使って取得します。
  • getResponseCode を使うことでHTTPステータスコードを取得できます。
  • getInputStream を使うことレスポンスボディのデータストリームを取得できます。

//ステップ6:レスポンスボディの読み出しを行う。
int statusCode = urlConnection.getResponseCode();

String responseData = "";
InputStream stream = urlConnection.getInputStream();
StringBuffer sb = new StringBuffer();
String line = "";
BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
while ((line = br.readLine()) != null) {
    sb.append(line);
}
try {
    stream.close();
} catch (Exception e) {
    e.printStackTrace();
}
responseData = sb.toString();
今回はレスポンスボディのデータストリームを文字列に変換しています。

ステップ7:コネクションを閉じる。

使ったコネクションはしっかりと閉じましょう。
//ステップ7:コネクションを閉じる。
urlConnection.disconnect();

サンプルコード

今回のサンプルコードの仕様は下記のとおりです。

  • URLとハッシュマップを引数で渡す。
  • 指定したURLにPOSTメソッドで接続する。
  • Content-Typeはapplication/jsonを使用する。
  • org.json.JSONObjectを使用してハッシュマップをJSON文字列に変換する。
  • リクエストボディにapplication/jsonに適合する文字列を設定する。
  • レスポンスボディデータは文字列として扱う。
  • 非同期処理から呼び出されることを想定している。
  • 呼び出し側には文字列を返却する。
    public String execute(String argStrApiUrl, HashMap<String,Object> jsonMap) {
        String ret = "";
        HttpURLConnection urlConnection = null;
        try {
            //ステップ1.接続URLを決める。
            URL url = new URL(argStrApiUrl);

            //ステップ2.URLへのコネクションを取得する。
            urlConnection = (HttpURLConnection) url.openConnection();

            //ステップ3.接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。
            //接続タイムアウトを設定する。
            urlConnection.setConnectTimeout(100000);
            //レスポンスデータ読み取りタイムアウトを設定する。
            urlConnection.setReadTimeout(100000);
            //ヘッダーにUser-Agentを設定する。
            urlConnection.setRequestProperty("User-Agent", "Android");
            //ヘッダーにAccept-Languageを設定する。
            urlConnection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
            //ヘッダーにContent-Typeを設定する
            urlConnection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            //HTTPのメソッドをPOSTに設定する。
            urlConnection.setRequestMethod("POST");
            //リクエストのボディ送信を許可する
            urlConnection.setDoOutput(true);
            //レスポンスのボディ受信を許可する
            urlConnection.setDoInput(true);

            //ステップ4.コネクションを開く
            urlConnection.connect();

            //ステップ5:リクエストボディの書き出しを行う。
            OutputStream outputStream = urlConnection.getOutputStream();
            if (jsonMap.size() > 0) {
                //JSON形式の文字列に変換する。
                JSONObject responseJsonObject = new JSONObject(jsonMap);
                String jsonText = responseJsonObject.toString();
                PrintStream ps = new PrintStream(urlConnection.getOutputStream());
                ps.print(jsonText);
                ps.close();
            }
            outputStream.close();

            //ステップ6.レスポンスボディの読み出しを行う。
            int responseCode = urlConnection.getResponseCode();
            ret = convertToString(urlConnection.getInputStream());
            Log.d("execute", "URL:" + argStrApiUrl);
            Log.d("execute", "HttpStatusCode:" + responseCode);
            Log.d("execute", "ResponseData:" + ret);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                //7.コネクションを閉じる。
                urlConnection.disconnect();
            }
        }
        return ret;
    }

    public String convertToString(InputStream stream) throws IOException {
        StringBuffer sb = new StringBuffer();
        String line = "";
        BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        try {
            stream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

今回のまとめ

  • POSTメソッドではリクエストボディを使ったデータ送信を許可するために setDoOutput にtrueを渡すこと。
  • application/json に適合した送信文字列を作成するときはorg.json.JSONObjectクラスなどのJSON文字列を取り扱うクラスを使用すること。
  • テキストとファイルを同時に送信する Content-Type「multipart/form-data」とは異なるので注意すること。