2012/07/29

Linux環境でのbin/neo4j installコマンドの挙動

グラフDBの一つであるNeo4Jをいろいろ試す中で、 サーバースクリプトの引数にinstallオプションがある事に気がつきました。

いろいろ勝手にやってくれるのは便利なのですが、 それはそれで動きが分からないと不便なので、調べたことをメモしておきます。

環境はUbuntu 12.04 LTS 64bit版、JVMはOracle版のJava 1.6.0_33です。

マニュアルの説明

セクション18.1 Server InstallationではWindows, Linux, MacOSX毎の説明が書かれています。 ここではLinux用の記述について説明しています。

まず、導入から起動までの基本的なステップとして次のように書かれています。

  • $ bin/neo4j install
  • $ service neo4j-service status
  • $ service neo4j-service start

そして、導入ステップの中で次のような情報を入力や作業が行なわれるとされています。

  • ユーザー名 (デフォルトは"neo4j") の入力
  • ユーザーが存在しない場合の作成をスクリプトが行なう
  • dataディレクトリ以下のファイル所有者をユーザー名に変更

さらにセクション 22.9 にあるLinux用のパフォーマンスチューニングを実施するよう勧めています。

スクリプトの挙動

サービスとして起動させるために、何をするのかの説明はないので、スクリプトを普通に眺めていきます。

bin/neo4jスクリプトの中では、installservice()シェル関数の中で処理が行なわれています。

1. OS、JVMの判別

bin/utilsで定義されているdetectos, findjavaシェル関数でそれぞれOSとJVMの判別が行なわれています。

detectosの中では$ uname -sコマンドの出力をベースにDIST_OS変数に"linux"(solaris,cygwinなどの場合には正規化した値)が入るようになっています。

findjava関数はいろいろ泥くさい処理をしていて、例えばgentoo linuxの場合には$ java-config --jre-homeの出力をJAVA_HOME変数に設定するなどの処理をしています。 最終的にはJAVACMD変数にjavaコマンドのパスを格納して、それを後続の処理で使うようになっています。

2. /etc/init.d/neo4j-serviceファイルの配置

/etc/init.dディレクトリがある場合には、/etc/init.d/neo4j-serviceファイルはbin/neo4jへのシンボリックリンクとして作成されます。

その後で、$ update-rc.d neo4j-service defaults$ chown -R neo4j: data confのコマンドが実行されています。ユーザー名neo4jは適宜入力された名前に変更されて、ドキュメントにある通りの設定がされています。

グループ名の指定がないのがポイントと、いくつかドキュメントにない暗黙の設定があります。

インストール処理の自動化について

falseに設定されているHEADLESS変数は質問があるところで参照されて、もしtrueだと全ての設問にy(yes)を選択した挙動になります。 これを使う場合、外部から変数にtrueを設定する仕組みはないので、スクリプト自体を上書きする必要があります。

またwrapper_user変数は、作成するユーザー名の指定があって、通常は$ id -unから実行時のUIDの値が入りますが、$ sudo env wrapper_user=neo4jsrvr bin/neo4j installのようにするとコマンドラインからデフォルトのユーザーIDが指定できます。

この他にenvコマンドで同様に操作できるパラメータには次の変数がありました。

  • org_neo4j_server_webserver_port - ポート番号 (既存プロセスのステータス確認に使用)
  • wrapper_user - サーバーを実行するユーザーID
  • wrapper_ntservice_name - /etc/init.d/直下に作成するシンボリックリンクのファイル名 (serviceコマンドの引数に指定する名前)

ユーザーIDと/etc/init.d以下に配置されるファイル名の変更であれば、手動でHEADLESS=falseをtrueに変更して、envコマンドの引数でパラメータを指定すれば、対話的な処理なしにインストール作業を進めることができます。

org_neo4j_server_webserver_port は導入時のプロセスチェックにのみ使われて、confファイルは変更されません。 conf/neo4j-server.propertiesファイルを直接変更する必要があります。

上書きされるファイルについて

変更されたユーザー名については、conf/neo4j-wrapper.propertiesファイルが変更されます。 これによって$ sudo /etc/init.d/neo4j-service startのように実行すると、自動的に指定したユーザーIDでneo4jプロセスが起動します。

これ以外のパラメータは、confファイルを変更する必要があります。

さいごに

installオプションを実行すると、ファイルが展開されているディレクトリがそのまま使われます。 マニュアルにもありますが、/opt以下などにtar.gzを展開する必要があるでしょう。

その反面 data, conf ディレクトリは展開されたディレクトリに含まれているので、本番で運用するのであれば、これらデータ本体の配置は考慮する必要があります。

アップグレード時は、初回起動時にデータベース構造の更新処理が走る事になっているので、tar.gzを展開して、conf, dataを入れ替えるか、シンボリックリンクで適当なディレクトリを参照するようにしてあげるぐらいで良いようです。

その他に本番運用にはクラスタリングやオンラインバックアップなど、まだ考慮しなければいけない要素があるので、これから検証していく予定です。

2012/07/25

Ubuntu 12.04 LTSでmrubyを試してみた

組み込み用途に特化した軽量版Rubyの実装として、mrubyがリリースされています。

これの使いどころを考えてみたのですが、Tech-Onのインタビュー記事では、一例としてC言語で記述が難しい処理をmrubyにオフロードするとありました。

個人的には静的なコードはC言語で書くだろうと考えていて、動的なランタイムの変更をmrubyでユーザーに開放するんだろうなと思っています。 具体的には設定ファイルの記述やプラグインをmrubyスクリプトで作成することになるでしょう。

この使い方だけならluaなんかでも良いんですが、操作可能な要素をクラス単位でまとめる事ができるのは、実際のところ名前空間があるかどうかの違いぐらいしかありませんが、実用上の使い勝手は表面的な違い以上のものがあると感じています。

まぁ、ここら辺はいまのところ信念の問題なのですが、今回はmrubyを使って設定要素をクラスにまとめる場合を想定して、ちょっとしたサンプルを作ってみる事にしました。

テストした環境とmrubyのバージョンは次のようになっています。 特定のmrubyのバージョンには依存していないと思いますが、Webで調べたコードの中の関数の引数の取り方が違うものもあったので、念のため書いておきます。

  • OS: Ubuntu 12.04 LTS x86_64版
  • コンパイラ: gcc 4.6.3
  • mruby git commit: 8b6f6faf1e3771c04a1e2a58b1bbc84fe7d5c1e2

ちなみに、commit:のハッシュを使って、このテストしたmrubyと同じソースコードをbranch名"20120723.083824"で入手するには次のようにします。

$ git clone https://github.com/mruby/mruby.git
$ git checkout -b 20120723.083824 8b6f6faf1e3771c04a1e2a58b1bbc84fe7d5c1e2

mrubyのバージョンや日付によってメソッドの呼び出しシグネチャが違う事がありますが、だいたいそのまま読めると思います。

参考資料

参考にしたのは、主に手探りでおぼえるmruby その1:クラスを定義する、メソッドを定義するで、RICOHの方がまとめた記事もありましたがコードがGPLだったりするので、考え方などを参考にするに留めています。

とりあえずmrubyについては使い方よりも、生み出された背景や、中間コンパイラとしての挙動について、mrbcコマンドの動きを抑えておくのがお勧めです。

実証実験に参加した各種企業が公開しているドキュメントが参考になるでしょう。

mrubyのサイズ

x86_64環境でライブラリをenv COMPILE_MODE=release makeで作成すると、libmruby.a, libmruby_core.aの各ファイルはそれぞれ790KB前後のサイズになります。(strip後は470KB前後)

ruby-1.9.3-p194のコードをビルドすると、librucy-static.aのサイズはstrip後も2.1MBほどになります。

ライブラリの全てのシンボルとリンクする分けでもないので、静的にリンクした実行ファイルのサイズはもっと小さくなるはずですですが、CRubyのライブラリサイズの大きさは少し大き過ぎるなぁと感じるところです。

mrubyに求める機能

既に説明していますが、アプリケーションが提供するクラスを主体として、そのオブジェクトを組み込みメソッドを組み合せて操作できるところがメリットだろうと思っています。

普通のアプリケーションでは設定ファイルに固定文字列しか書けないのが普通ですが、これはセキュアだとは思うものの、記述できる語彙が少な過ぎるとも感じています。

luaはちょっとローレベルに過ぎる印象があってオブジェクト的に構造体の中に値と操作用関数をまとめる事はできますが、その環境をセットアップするのは少し面倒な印象です。

今回のゴール

mrubyを通してユーザーは日時情報や外部ファイルに記述されている内容などの情報に応じて振舞いを変化させる事ができるようになるといいなぁというわけですが、サンプルなので今回の範囲は一組のsetter/getterをラップしてみようと思います。

具体的に何をするのか、図にしてみた

図にしてみると次のようなプログラムを作成してみる事になります。

C言語レベルではConfigクラスに相当する設定可能な要素を構造体で定義します。

アプリからは直接構造体を操作せず、操作用のsetter/getterメソッドをConfigControllerクラスとしてまとめています。mrubyからはWrapperクラスを通してConfigControllerファイルでまとめているメソッドにアクセスしています。

実際のコード

いろいろ大風呂敷を広げましたが、ここから先はしょぼいコードが並びます。

ただGoogleで検索にヒットしたページは、やはりmrubyにコードをオフロードする仕組みやmrbcを使って中間コードを実行する方法に特化していたりしたので、もうちょっと泥くさい使い方を考えてみました。

conf.h, conf.c

実際にはConfとConfControllerに相当する機能はconf.c, conf.hにまとめました。 でも何かするわけではなくて、デバッグモードかどうかを動的に切り替えられるというだけです。

内容にはまったく意味がないんですが、気にしないでください。

conf.hファイル

#ifndef YA_CONF_H
#define YA_CONF_H 1

#include <mruby.h>

struct _conf {
  int is_debug;
} conf;

int is_debug(void);
void set_is_debug(int d);

#endif

conf.cファイル

#include "conf.h"
int is_debug(void) {
  return conf.is_debug;
}
void set_is_debug(int d) {
  conf.is_debug = d;
}
wrapper.h, wrapper.c

wrapper.hファイル

#ifndef YA_WRAP_H
#define YA_WRAP_H 1

#include <mruby.h>

struct RClass* yamrb_class;

mrb_state* yamrb_init(void);
mrb_value yamrb_is_debug(mrb_state* mrb, mrb_value self);
mrb_value yamrb_is_debug_equal(mrb_state* mrb, mrb_value self);

#endif

wrapper.cファイル

#include "conf.h"
#include "wrapper.h"

#include <mruby.h>
#include <mruby/numeric.h>

mrb_state* yamrb_init() {
  mrb_state* mrb = mrb_open();
  yamrb_class = mrb_define_class(mrb, "YaConf", mrb->object_class);
  mrb_define_method(mrb, yamrb_class, "is_debug", yamrb_is_debug, ARGS_NONE());
  mrb_define_method(mrb, yamrb_class, "is_debug=", yamrb_is_debug_equal, ARGS_REQ(1));

  return mrb;
}

mrb_value yamrb_is_debug(mrb_state* mrb, mrb_value self) {
  mrb_value ret = mrb_fixnum_value(is_debug());
  return ret;
}

mrb_value yamrb_is_debug_equal(mrb_state* mrb, mrb_value self) {
  mrb_int arg_debug;
  int argc = mrb_get_args(mrb, "i", &arg_debug);
  if(argc == 1) {
    set_is_debug(arg_debug);
  }
  return self;
}
main.c
#include <stdio.h>
#include "conf.h"
#include "wrapper.h"
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/compile.h>

mrb_state *mrb;
int gen_code_num;
mrbc_context *mrbc_ctx;

void init() {
  mrb = yamrb_init();
  FILE *fp = fopen("main.rb","r");
  mrbc_ctx = mrbc_context_new(mrb);
  struct mrb_parser_state* st = mrb_parse_file(mrb, fp, mrbc_ctx);
  fclose(fp);
  gen_code_num = mrb_generate_code(mrb, st->tree);
  mrb_pool_close(st->pool);
}

int main(int argc, char** argv) {
  init();
  
  // first run
  printf("current is_debug: %d\n", is_debug());
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[gen_code_num]), mrb_nil_value());

  printf("current is_debug: %d\n", is_debug());
  set_is_debug(1);
  printf("new is_debug: %d\n", is_debug());

  // second run
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[gen_code_num]), mrb_nil_value());

  // close
  mrbc_context_free(mrb, mrbc_ctx);
  mrb_close(mrb);
}

main.rb

print "-- begin --\n"
y = YaConf.new()
p y.is_debug()
y.is_debug = 2
p y.is_debug()
print "----\n"

Makefileファイル

INC = ./src/include
LIB = ./src/lib

main: conf.o wrapper.o main.c
	gcc -std=gnu99 -I. -I$(INC) -o main main.c conf.o wrapper.o -L$(LIB) -lmruby -lm

conf.o:	conf.c conf.h
	gcc -std=gnu99 -I. -I$(INC) -c conf.c 

wrapper.o: wrapper.c wrapper.h
	gcc -std=gnu99 -I. -I$(INC) -c wrapper.c 

気になったこと

Rubyの拡張ライブラリの知識は、いろんな意味で役に立ちますが、README.EXT.jaに対応するまとまったドキュメントがないので、いろいろ混乱するかもしれません。

FIX2INTなどの型変換マクロがない

Rubyの拡張ライブラリでは標準的なC言語の型との変換はマクロで実現していましたが、 mruby.hでは静的な関数が準備されています。

  • static mrb_value mrb_fixnum_value(mrb_int)
  • static mrb_value mrb_float_value(mrb_float)
  • などなど

mrb_intは標準のint型とtypedefされているだけで、直接に代入できます。 mrb_floatはmrbconf.hで定義されていますが、手元では8byteで通常はdouble型に紐付くようです。 文字列はmruby/string.hに定義されているような、組み込みString型用の関数を使う事になります。

mruby.hに定義されていなければ、操作対象の型に応じてmruby/以下のヘッダーファイルを眺める事になります。

mrb_generate_code()が返すint型の番号を管理する方法が欲しい

これはmrubyが準備する話しではないのですが、今回は処理が単純なのでmrbも全体で共有するような作りにしました。少し複雑になってきても、必要な都度、必要なコードをmrb_run()で呼びますが、この場合には対象のコードをmrb_generate_code()の戻り値で指定する必要がでてきます。

この管理方法が、まぁ対象のアプリに依りますが、どうなるかなぁと思っているところです。

まとめ

rubyはいろいろ機能が増えすぎて特定のバージョンとコードを密接に管理する必要があるので、mrubyはシンプルに保って欲しいなぁと思っています。まぁコンパイル時のオプションで調整することもできる部分もありますが。

C言語でアプリを組む時に機能の一部をオフロードする目的だと内蔵クラスや機能が多い方が楽になるわけですが、アプリケーションのプラグイン的にユーザーに開放する場合には、むしろ機能や組み込みクラスはないぐらいの方がセキュアになります。

mrubyは現状ミニマムで、これからスタンダード、フルといった主にクラスが追加される形のバージョンが出てくる事になっています。 とはいえ、ミニマムしかない現状でアプリの拡張をmrubyで行なおうとすると物足りない印象はあるので、前倒しでミニマウなmrubyが拡張されるような可能もあるのかなぁと思っています。 クラスセットについては、決まっているようですけれど、環境としてはまだまだ機能の追加が続いていますしね。

どうなるか、まだよくわかりませんが、アプリのコアエンジンとして安定してくれるといいなぁと思います。

2012/07/11

Google Chrome拡張を{ "manifest_version": 2 }に対応させてみる

気がついたらGoogleから「マニフェストのバージョンが2になったから対応よろしくね」、という旨のメールが届いていた。 まぁ8月中旬から新規の登録受付を中止して、年内には更新の受け付けができなくなるスケジュールが引かれていたので、 使うだけのユーザーなら影響がでるのは来年以降ですね。

今回はmanifest.jsonファイルを変更して気になった点をまとめていきます。

ちなみに変更は動作確認が終り次第、gitoriousにあるコード(Japan Postal Code Search, Open PinnedTab Link, etc...)に反映させます。

参考にしたページ

Googleのガイドはとてもまとまっているので、あまり他のサイトを調べる必要性を感じないのですが、 さすがに今回はStackOverflowなどのサイトにお世話になりました。

とはいえ、まず抑えておかなければいけないのはGoogleのmanifest.jsonの"manifest_version"の説明ページです。

ここで、全体のスケジュールと全体の変更の概要が書かれています。 しかし、ここだけでは具体的にアプリケーションにどんな変更が必要か把握するのは難しかったです。

具体的な変更作業

とりあえず作成しているOpen PinnedTab LinkJapan Postal Code Searchの2つは、比較的簡単なアプリケーションです。

このアプリケーションを修正した際に必要な修正は以下のようになりました。 まだ稼働確認が終っていないので、追加で必要なものもありそうですが、とりあえずまとめます。

HTML中にonclick, onload属性は書けなくなった

scriptタグを使ったjQueryの$(function() {...});表記などは、そのまま使えますが、(x)HTML上のタグにあるonclick="..."やonload="..."の表記を埋め込む事ができません。

基本的なコードはGoogle ChromeガイドのContent Security Policy (CSP)ページに記載されています。

onloadについは書かれていませんが、onclickと同様にdocument.addEventListener('DOMContentLoaded', function () { ... };の...部分にonloadで行なっている関数呼び出しなどをコピーすれば動きます。

ここで問題になったのはJavaScriptのコード中で、明示的に文字列としてonclick属性を追加している場合でした。

変更前のコード:文字列としてonclick属性を追加している例

...
td4_span.setAttribute("onclick","showMapImg('" + center + "')");
...

変更後のコード:addEventListenerに書き換えた例

...
td4_span.addEventListener('click', function(){showMapImg(center);});
...

元々変更後のように組むべきだったとは思います。 とはいえ文字列で解決するのは単純で簡単だったので、使っている方もいそうです。

動的なアクションはすべからくaddEventListener関数で指定する事になると思われます。

background処理をhtmlからjsファイルへ移動

manifestのbackgroundで指定する対象がhtml("page":文字列)とjavascript("scripts":文字列配列)の2つが準備されています。

これまではhtmlで記述していたので、manifest.jsonを書き換えて、そのまま流用すれば良いと思っていたのですが、どういう分けか、うまく動きませんでした。

変更前:version 1のmanifest.jsonから抜粋

...
  "background_page": "background.html",
...

元々background.htmlは本文のない、scriptタグの中に処理が記述されているだけだったので、その中身をjsスクリプトファイルに分割して、manifest.jsonの表記を書き換えました。

変更後:version 2のmanifest.jsonから抜粋

...
  "background": { 
     "scripts": ["scripts/background.js"]
  },
...

jsファイルを指定した場合でも、内部では空のhtmlファイルが生成されて組み込まれているだけのようなので、今回の挙動はおかしいと思います。 おそらくhtmlファイルのまま移行できるように作られているはずなので、早晩このワークアラウンドは不要になるでしょう。

まとめ

いろいろ書こうと思ったのですが、うまく動かない処理を発見したりして別の記事にまとめる事にして、とりあえず今回はここまでで終りです。

今回取り上げなかったのですが、外部サイトからのJavaScriptファイルのロードなどもデフォルトではできなくなっています。 オプションページのためにjQueryのコードをCDNからダウンロードしたいような場合にも対応が必要そうです。

単純なmanifest.jsonの書き換えで対応できるアプリケーションは少ないのではないでしょうか。

とはいえ、変更内容を確認する限り、機能が制限されているものはないので、既存の"manifest_verison":1アプリは全て、ちゃんと書き換えれば動くはずです。

この「ちゃんと」という部分が曲者ですが、今回の経験からは、新ルールに矯正された事によってアプリケーションのコード全体は良くなっていると思います。

Chrome拡張のAPIについてはいろいろ疑問に思うところもあったので、これから始める方々には、面倒は増えるでしょうが、よりよいコードになっているとは思います。

これがどれくらい面倒かは…、なんともいえませんが、少なくとも既存の開発者は、どこに問題があるか調べるだけで面倒そうです。

2012/07/04

SK17iをICSにして撮影した画像のファイルサイズが増えている件について

ExifPMというAndroidアプリを作成している事もあって、Xperia Mini Pro (SK17i) で撮影した写真データのExifデータをチェックしていると、ICSへのアップデート前後でファイルサイズが違っている事に気がつきました。

Exifデータを比較してみると、画素データのサンプリング方法がYCbCr420からYCbCr422へと変更されていた事が原因でした。

元々500万画素のカメラなのに、生成される写真データは700[KB]から900[KB]程度のファイルサイズだったので、 カメラアプリの不具合かなぁと思っていたのですが、これはデータをYCbCr420で処理していた事が原因でした。

色データの扱いは対象の特性によって異っていて、ディスプレイへの出力はRGBで考えて、印刷をする時にはマゼンダなどの中間色を使ったCMYKが使われます。JPEGの場合にはYCbCrやYCbCrと呼ばれるような、輝度と色差のデータを使う事になります。

基本的にはYCbCr420は縦横4ピクセルで色差情報を共有するので、横並びの2ピクセルで色差情報を共有するYCbCr422と比較してデータ量が落ちるため、画質が落ちるといわれています。

データ量が少ない分だけ劣化するのは事実ですが、まぁそんなに単純な話しでもないので、 サンプリング方法の詳しい説明はGoogleでいくつかのサイトの記述を確認されるのがお勧めです。

問題はICSにアップデートしたSK17iではカメラの画質が上がっているのだろうか、という疑問に対する答えがあるのかどうかです。

結論からいえば情報不足でよく分からないという事になるのですが、とりあえずまとめたところをメモにしておきます。

撮影した画像の見た目の違い

残念ながら同一条件で、同一被写体を撮影した画像がないので、正確な比較はできません。

撮影する時間帯はずれているので照明の条件は同じになりませんが、似たようなアングルで撮影した画像をみてみると、古い画像は絹のような質感が感じられて何か靄のようなもので覆われている印象があります。それと比較するとICS以降に撮影した画像は鮮明に感じられます。

画像をみてICSで撮影された画像かどうか区別できるものもあれば、難しいと感じるものもあって、単純に画質が向上したというのは難しいかなと感じています。

レンズの汚れや撮影環境の条件をちゃんと揃えないと、見た目の判断だけでは何ともいえないところです。

画像ファイルサイズの違い

対象によってファイルサイズは変化しますが、室内・屋外で撮影した手元の画像を眺めると、だいたいファイルサイズは次のようになっています。

  • Android 2.3: 500[KB]〜900[KB]
  • Android 4.0: 1.2[MB]〜1.8[MB]

画質の違いは別にして、ファイルサイズは確実に増えています。

APIからみた画質の変化について

自作アプリを作った時にカメラAPIから渡されたデータは、YUV420SPという形式でした。 ちょっと調べたところ、このAPIに変化はなさそうです。

Androidのカメラアプリを作って気になったのは、カメラからのRAWデータにアクセスする方法がないところでもありました。APIとしては準備されているんですが、あれが有効なデータを返すデバイスを寡聞にして聞いた事がありません。

使えたとして、大抵のデバイスでは、ほぼ確実にヒープ領域が確保できないでしょうから、処理を始める前にアプリが落ちるんでしょうけれど。

カメラ固有のAPIからデータを取得してYCbCr422データを作らない限りは、YCbCr422のデータをYUV420SPをYCbCr422に変換してもデータサイズが大きいだけで画質の向上は見込めない事になります。

現状では満足していますが、画質が向上したのかという疑問にははっきりとした答えが出せないでいます。

さいごに

SK17iを入手して比較的すぐにICSにアップデートしてしまったので、カメラアプリの挙動の違いが本当に正しいのか、設定の違いなんじゃないのか、という点について良くわかっていません。

体験として嘘は書いてませんが、十分に調査したとはいえないので、ひょっとすると勘違いが含まれているかもしれません。その際はご指摘頂ければ幸いです。

ただICSにアップデートした事自体は公開していません。Xperiaのドコモ端末の中でRAM 512MB未満はICSへのアップデートが不適とされて行なわれない事になりました。

理由がRAMサイズにあるという事ですが、それで何が問題なのかという点はよく分かっていません。

ただしばらくSK17iを使っていて、落ち着いて使っている時は良いのですが、ソフトウェアキーボードを急いで操作する時には、ひっかかるように感じる時があります。

その反面、ハードウェアキーボードを使ってメールやtwitterを使う場面で、何か問題が起こった事はないので、細かい違いを気にするのであれば、ICSへのアップデートは避けた方がいいのかもしれません。

それを除けば、いまのところICSだから問題だという現象には遭遇していないところです。 カメラの件は裏の仕組みがどうなっているか分からないので、良いのかどうか分かりませんが、基本的に新しいものが好きなのでICSにして満足しています。