Intel GalileoでErlang/OTPを動かす


はじめに

昨年末からRaspberryPiやBeableBoard-XMでErlang/OTPアプリを動かしているのですが、Intelから出たGalileo上で動いたら素敵だなーと思ったので、さっそくGalileoを秋月で購入。Erlang/OTPをビルド・インストールしてみました。
ちなみに、Galileoで動いたら何が素敵かというと、ボード一枚でアナログ6量が取れるからです。

Galileoファームウエアのアップデート

とりあえずGalileoのファームウエアをアップデートします。GalileoIDEを起動して、【ヘルプ】-> 【Firmware update】を実行。

開発環境を準備

makeなど、Erlang/OTPのビルドに必要なあれこれが入ったYocto LinuxSDKバージョンをダウンロードしてSDカードに保存します。

マイクロSDカードの準備

  • SDカードをFAT32でフォーマット(Macなら、SDカードを差してFinderのメニューから「移動」→「ユーティリティ」→「ディスクユーティリティ」を起動
  • SDカードを選んで、「消去」タブをクリック
  • フォーマットに「MS-DOS(FAT32)」を選択して、名前を「GALILEO」に設定
  • 消去を実行
  • 下記のリンクから「Clanton_Full_SDK.7z」をダウンロード、解凍。

http://ccc.ntu.edu.tw/index.php/news/40

  • 7zが無い場合は brewからインストール
$ brew install 7z

解凍

$ 7z x Clanton_Full_SDK.7z

できたClanton_Full_SDK/ ディレクトリと、さきほどフォーマットしたGALILEO( /Volumes/GALILEO/ )をrsyncで同期

 $ rsync -avz Clanton_Full_SDK/ /Volumes/GALILEO/

一旦、Galileoの電源を落として、さきほどのマイクロSDカードを差し込んで電源ON。
起動はけっこう時間がかかる。SDカード横のLEDが光ったり消えたりしてる間は起動中です。

起動したらSSHでログインする(ユーザーはroot, パスワードはなし)。

$ ssh root@192.168.3.3 #IPアドレスは環境による

Erlang/OTPのビルド済みバイナリ

次の章でソースからビルドする方法を書いてますが、ビルドしたパッケージを下記においておきます。
/usr/local/erlang/R16B03-1 にインストールした者を単にtarで固めたものなので解凍してパスを通せば動くと思います。

Erlang/OTP R16B03-1
https://googledrive.com/host/0BxkTrmjB9NZ1dkVxX3ViLUlORk0/R16B03-1.tar.bz2

あとrebarを使う場合はgitとcurlが必要です。curlは前章のLinuxイメージ入れてれば入っているのですが、どうもまともに動かなかったのでソースからコンパイルしました。
これらもgalileo上でコンパイルしたものを置いておきます。展開すればそのまま使えるはず。

cURL 7.35
https://googledrive.com/host/0BxkTrmjB9NZ1dkVxX3ViLUlORk0/curl-7.35.tar.bz2

  1. /usr/bin/curl* を全て/usr/local/curl/7.35/bin 以下からシンボリンックリンクで置き換え。
  2. /usr/include/curl に /usr/local/curl/7.35/include/curl からシンボリンックリンクで置き換え。
  3. /usr/lib/libcurl* を /usr/local/curl/7.35/lib/libcurl* からのシンボリックリンクで置き換え。
  4. /etc/pki に /usr/local/curl/7.35/etc/pki からのシンボリックリンクを貼る。

最後に ~/.gitconfigに以下を追加

[http]
sslCAinfo = /etc/pki/tls/certs/ca-bundle.crt

Git 1.9.0
https://googledrive.com/host/0BxkTrmjB9NZ1dkVxX3ViLUlORk0/git-1.9.0.tar.bz2

Erlang/OTPをソースからビルド・インストール

ここでは最新安定板のR16B03-1を入れる。

# mkdir src
# cd src
# wget http://download.basho.co.jp.cs-ap-e2.ycloud.jp/otp/download/otp_src_R16B03-1.tar.gz

解凍

# tar xvzf otp_src_R16B03-1.tar.gz

configure実行

# ./configure --prefix=/usr/local/erlang/R16B03-1 \
             --disable-threads --enable-smp-support --enable-kernel-poll \
             --disable-sctp --disable-hipe /
             --enable-m32-build --without-javac --with-ssl

ビルド、インストール

# make
# make install

めちゃくちゃ時間がかかります。数時間はかかるので寝る前にビルド開始して、起きた頃に結果を見た方がいいかも。

上記cnofigureオプションだと /usr/local/erlang/R16B03-1 に入ります。
従って

export PATH=/usr/local/erlang/R16B03-1/bin:${PATH}

すれば使えます。

$ erl
Erlang R16B03-1 (erts-5.10.4) [source] [async-threads:10] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
1> 1 + 1.
2
2> spawn(fun() -> io:format("spawn!") end).
spawn!<0.36.0>
3> 

とりあえずは普通に使えそうです。

RaspberryPi+Arduino、オムロンPLC、Intel Galileo でMQTTしてみた話

2014/3/21 オムロンPLC, Intel Galileoを追加した図を追加。

やりたかったこと

  1. クラウド(IaaS)上のサーバー(MQTTブローカー)ごしにセンサーの値を送受信して遠隔地の機器を制御した場合にどの程度リニアに制御できるか知りたかった。
  2. 同じくクラウド(IaaS)上のサーバー(MQTTブローカー)ごしにセンサーの値を送受信してブラウザ上でグラフ描画した場合にどの程度リニアにセンサーの値がグラフに反映されるか知りたかった。
  3. Erlang/OTPで組込み側のソフトウエアを組んだ場合の感触を知りたかった。

特に、ある端末のセンサー値をMQTTプロトコルを介して別の端末に配信した場合の機器制御がどの程度のリニアさなのか、がもっとも知りたかった事です。
この後にある動画で、僕が手で回してるダイヤルと、それによって回転するサーボは間隔が15cmほどですが、実はこれらは電気的には繋がっておらず、はるか10,800km彼方のニューヨークのサーバーを通して繋がっています。そのような構成で、インターネットを通じて離れた場所にある機器を制御した場合にどの程度のリニアさが得られるのかを今回試してみました。

MQTTプロトコルについて

MQTTプロトコルについては、下記サイトを参照させて頂きました。

「MQ Telemetry Transport (MQTT) V3.1 プロトコル仕様」
https://www.ibm.com/developerworks/jp/websphere/library/wmq/mqtt31_spec/

「MQTT コトハジメ
https://gist.github.com/voluntas/8238751

「MQTTについてのまとめ」そこはかとなく書くよん。
http://tdoc.info/blog/2014/01/27/mqtt.html

「HTTPからMQTTへ - IBMが提唱するモノとモノがつながる時代に最適化したプロトコル&アプライアンス
http://it.impressbm.co.jp/special/2013/12/09/5313

用意するもの

  • RaspberryPi(2個)
  • Arduino uno(2個)
  • 可変抵抗(1個)
  • サーボ(1個)
  • それらを繋ぐジャンパワイヤー

です。RaspberryPiを無線LANでネットワークに接続する場合は無線LAN子機も必要です。
今回、Arduinoは単にアナログ、デジタルデータの入出力のために使用します。Arduino内で制御プログラムは組まず、arduino IDEに入っている「Standard Firmata」というスケッチを入れただけのものです。

あ、それともちろんMQTTブローカー(サーバー)が必要です。今回は@voluntasさん実装の時雨堂MQTTブローカーを使わせて頂きました。ありがとうございます。
それと、このページを読んでいる人は大概知っておられると思いますがRaspberryPiとArduinoについては以下の通りです。

RaspberryPi
RaspberryPiは手のひらサイズの安価なコンピュータ基板です。CPUは700MHz、メモリは512MBと十年前のノートパソコンと同等近い性能を持ちLinuxが稼働可能(raspbianというDebian GNU/Linuxを元にした専用ディストリビューションあり)、ボード単体では4,000円程度で購入する事ができます。

Wikipedia:RaspberryPi

Arduino uno
Arduino unoはアナログ入力6点、デジタル入出力14点を備えた基板。単体で使用するだけでなく、Firmata(ファルマータ)プロトコルを通じてPCなどから制御することでもできる。ボード単体では3,000円以下で購入可能。

Wikipedia:Arduino


構成

構成は上図のとおりになります。
基本的にpi-001がアナログ、デジタルデータをブローカー(サーバー)に送信、pi-002はそのデータを受け取って自身に繋がった機器を制御します。

なお、TCP/IPセッションは常に端末側からサーバーに対してオープンし且つそのセッションを維持する為、端末側に固定IPアドレスは不要ですし、会社や家庭のLAN内にあっても特に問題はありません。このあたりは地味ではありますがMQTT良いなと思いました。

  1. 各RaspberryPiとarduinoはUSBケーブルを通じてアナログ・デジタルデータを送受信します。
  2. Arduinoからデータを受け取ったRaspberryPi(pi-001)はそれをインターネットを通じてサーバー(MQTTブローカー)へ送信(publish)します。
  3. データを受け取ったサーバー(MQTTブローカー)はトピックの内容を見て、そのトピックを受信待機(subscribe)しているRaspberryPi(pi-002)へ、インターネットを通じてデータを送信します。
  4. データを受け取ったRaspberryPi(pi-002)は受け取った値(0〜1024)を角度の0〜180度に変換し、USBケーブルを通じてArduinoにサーボ制御要求を出し、サーボが回転します。
  5. 同様のデータをWebSocketを通じて受け取ったブラウザでは、最新のデータでグラフを更新します。

なお、ブラウザでのグラフ描画にはフリーのグラフ描画ライブラリ、ccchartを使わせて頂きました。WebSocketをサポートしている上に見た目も綺麗で見やすいので気に入っています。

デモ動画
で、実際に動かしてみた様子はこんな感じです。

サーバーはニューヨークにあるとの事ですが、太平洋を渡って一往復してるにしては結構いい反応速度です。
これ以外にpi-001に繋がったタクトスイッチをON、OFFしてpi-002のLEDを光らせてみましたが、遅延はどんなに長くても1秒以内、だいたい平均して0.5秒くらいでした。

感想
MQTTの興味深いところは、従来のセンサーデータ収集ネットワークと異り、最初から機器間の相互制御を念頭に置いた設計になっているところだと思います。
そのため、TCP/IPセッションは常にオープンしっぱなしであり、従ってソケットオープンの為の時間が0となるため、かなりリニアにデータの送受信ができます。実際今回の実験を見ても、サーバーは10,800km彼方のニューヨークにあるわけですが、電気信号と同等とはいかないまでも待ち時間は長く見積もっても1秒以内でした。サーバーが国内に有れば反応速度は更に早くなる事が予想できます。

課題としては、MQTTの特徴でもあるTCP/IPセッション張りっぱなしの部分で、サーバー側で多数のクライアントの通信をどう捌くか、という所かと思います。さらにそれをインフラとして運用する場合、サーバーがダウンすると端末制御が止まるだけでなく、サーバー復活時には一気に端末からの再接続が始まる訳で、クラスタを組むなどして(クラスタ単体でのダウンはあるとしても)絶対にシステム全体がダウンしない運用を念頭においてシステム構成しないといけないなーと思います(とはいえ絶対は無いので、その場合にどうするか、も考慮が必要と思います)。

なお、動画の時点では0.3秒おきにアナログデータをpublishしてますが、それだと変化が無い場合には頻繁すぎるかなーと思いましたので、現在は通常時は3秒おきにしておき、ダイヤルが操作されるなどして一定以上の変化が有ると、その間は0.3秒間隔で送るように変えました。TCP/IPの再送なども考慮すると常時1秒以下の間隔で送るのはちょっと辛いかなと思ってそうしたのですが、どれくらいの間隔で送るのがいいのか、はケースバイケースで考慮が必要と思います。


追記: 2013年3月21日

追記1:オムロンPLC, Intel Galileoボードに対応
上記に続き、オムロンPLCのFinsプロトコルに対応しました。またGalileo上でもほぼそのまま動作しましたので追加。
ただしGalileoのGPIO特有の部分は現在実装してるところです。GalileoをサポートしたいのはGalileoはこれ一枚でアナログもデジタルも取れるからです。

なお、開発中の端末側アプリのレポジトリはこちらです(サーバーサイドは前述の通り時雨堂のvoluntasさん実装のブローカーとの通信となります)。
https://github.com/hiroeorz/marionet-device

Erlang/OTPで組んでいます。速度やメモリ使用量ではCに比べて不利ですが、M2Mアプリで必須となる並列処理やプロセス停止時の自動再起動などで楽ができると思ったので。
それと端末側でセンサーなど各種機器とやりとりする際のプロトコルはバイナリフォーマットが多いので、型やサイズを指定してのバイナリ生成や、バイナリパターンマッチを持っているErlang/OTPは、その変でもらくちんです。
あ、それと僕のアプリではあんまり関係ありませんが、Erlang/OTPのGCはプロセス毎に張り付いているのでGCの為に全体が停止する事が無いってエロい偉い人がいってました!

あと、オムロンのPLCと通信できるようになりました。まあ1年くらい前に興味本位でfinsプロトコルを喋るErlangアプリ書いてみたので折角なので使ってみたくらいの事ですが。オムロンのPLC(プログラマブル コントローラ)はUDPのfinsプロトコルを介してDMエリアにアクセスできます。

* FinsプロトコルオムロンPLCがシリアルまたはネットワークを介して通信するための公開プロトコルです。

制御プログラムはこのDMエリアに値を書き込んだり読み込んだりできるので、例えばある場所にある端末でセンサーの値がDMエリアに常に書き込まれてるとして、そこから数kmあるいは地球の反対側にある端末に繋がったPLCのDMエリアにほぼリアルタイムに同期する事が可能です。地球の反対側のDMエリアと同期して何に使うのかはわかりませんが同じ国内程度なら色々用途が有りそうです。


追記2:設定用のWebインターフェイスを追加
今までボードにSSHでログインしてErlang形式の設定ファイルをvimなどで編集する必要が有りましたが、簡単なWebインターフェイスを追加しました。
こちらはcowboyでWebサーバーをアプリに組込み、クライアントインターフェイスAngularJS、あとデザインで楽する為にTwitter Bootstrapを使っています。

MQTTの通信設定

MQTTのSubscribe設定

GPIOの設定

シリアル接続したArduinoの設定

オムロンPLCの通信設定

オムロンPLCの監視対象DM(データメモリ)の設定

「Erlang/OTPトレーニング2013.10」に参加してきました

概要

並列処理に強く、耐障害性を持つプログラミング言語およびその実行環境であるErlang/OTPの本格的なトレーニング。今回のトレーニングでの学習範囲はErlang/OTPの基礎からOTP(Open Telecom Platform)の入り口までくらいまで。

主催は「株式会社 時雨堂」
参加費は1万円(招待枠ということで免除いただきました。助かります。ありがとうございます...m(_ _)m )
場所はスポンサーのVOYAGE GROUP会議室を貸していただきました。

講師は、軽量 高速なHTTPサーバ「cowboy」の開発者であり、Erlang/OTPのトレーニングをプロとして実施しておられる Loïc Hoguinさん。

スポンサーにはVOYAGE GROUP以外に「Basho Japan」も名を連ねておられました。懇親会のピザとお寿司はBasho Japanさんから出していただきました。ピザおいしかったです(なんかみんなお腹空いてたらしくピザがみるみるなくなっていってたw)。

日時: 2013年10月13日(日) 10:00 - 20:00
場所: 株式会社VOYAGE GROUP セミナールーム「パンゲア

目的

今回参加の目的は3つあって、

  1. Erlang/OTPについて、基礎からきっちりとしたトレーニングを受ける
  2. Erlang/OTPを使っておられる方々と交流していろいろお話聞きたい
  3. 以前からTwitterでは色々アドバイスなど頂いており、今回のトレーニングの主催者でもある時雨堂の@voluntasさんにお会いしたい

です。
@voluntasさんには以前からアドバイスや情報を頂いたり、自身のトレーニング目的で書いたErlang/OTPアプリにgithub上でコードレビューや提案を頂いておりながらも、まだお会いした事が無く、今回はじめての対面となりました。

出発-到着

開催地は東京、僕が住むのは島根。というわけで前日からの出発となりました。島根 - 東京間にはサンライズ出雲っていう夜行列車があるので、これで行くことにしました。

余談ですが、今が神在月のせいなのか、島根は観光で行き来される方がかなり多く、サンライズ出雲も一週間前に切符買いにいきましたが禁煙席は既に満席でした。サンライズに乗りたい人は早めに切符買った方がよさそうです。当日とか前日では多分厳しいと思います。

あと、渋谷ははじめてで、駅周辺でわりと迷子になりました(笑

受付を済ませると参加者にbashoステッカーや扇子が無料で配布されました、のでありがたく頂きました。MacBook Proに即張りです。

それとCowboy Tシャツを売ってました、ので買いました。えらく人気があったようで最後の1枚でした。

レーニン

予定通り、10時ごろからLoïcによるトレーニングが開始されました。トレーニングが英語ということもあって、だいたい1時間に一回休憩をいれながら行われました。

ちなみに僕はこの日のために2週間前からNHKの「おとなの基礎英語」と「リトルチャロ」で英語を勉強した訳ですが、残念ながらその程度の付け焼き刃で聞き取れるほど甘くはなかったですw
(でもせっかくなので英語の勉強はこのまま続けていこうと思います)

とはいえ、プレゼンテーション的に前に画面を映して、それに伴ってLoïcが喋る感じで進んだのと、ある程度予備知識があったのでわりと内容はわかりました。

ちなみに当日のトレーニングで使われたプレゼンテーションはこちら
http://ninenines.eu/talks/thinking_in_erlang/thinking_in_erlang.html

当日実際に書いたソースコードはこちら
https://gist.github.com/essen/6970232

内容は

  1. Erlang/OTPの概念
  2. hello world」から始まる基礎的な構文、四則演算など
  3. 関数、型、ガード、case節、パターンマッチなど...
  4. 再帰によるループ、Accumulator...
  5. spawnによる並列実行、spawnとリスト内包表記の合わせ技など...
  6. メッセージパッシング、receiveによるメッセージの取り出し
  7. プロセス間でのcastやcallを実装
  8. プロセスのクラッシュとmonitor,linkなどなど
  9. gen.erlのソースコードを読む(ここ重要!)
  10. 質疑応答

という感じでした。OTPのソースコードというのは実は見た事無くて、この部分が今回のトレーニングで一番印象的 & 勉強になりました。

懇親会

懇親会は終始なごやかな雰囲気の中でお寿司など頂きながらお話しさせていただきました。

ちょっとびっくりしつつもうれしかったのは、以前僕がgithubにあげたeredis_poolというeredisとpoolboyを組み合わせただけのアプリ(といえるのか?)をお仕事のアプリで使って頂いている方とお話できたことです。
まあほんとに組み合わせただけで、自分で書いたところはちょっとなんですが... しかも数ヶ月前に、その方の同僚の方からプルリクエスト送って頂いていたのに全く気づいていませんでした...(翌日の帰りの新幹線の中でプルさせていただきました。すみません...)
でも自分の公開しているものが誰かのお仕事に役立っていると思うと嬉しいです。

そんな中はたと見ると、Basho Japanの @kuenishiさんがおられたので、初対面でしたが少しお話しさせて頂きました。最初@kuenishiさんと二人で話させて頂いていましたが、いつの間にか周りに人が増えてRiakやErlang/OTP, Elixirやなんやで話が色々と盛り上がりました。

@voluntasさん、@kuenishiさんとは、その後の食事もご一緒させて頂き、とても楽しい時間を過ごさせて頂きました。お二人とも実力、知名度ともに高い方ですがフレンドリーに接して頂き嬉しかったです。それから色々と技術的に濃いお話も聞けて興味深かったし大変勉強になりました。

振り返り

レーニングの内容はいわゆる飛行機本を読んでいれば知っている内容が大部分でしたが、前述のようにOTPのソースコードの一部を読む機会を得たのと、実際にそれっぽいものをLoïcに続いて自分で手を動かして書いて学べたのは収穫でした。

また、トレーニング全体を通して、要点をポンポンとあげつつ重要な部分は実際にコードを書きながら概念を説明して頂けて効果的なトレーニングだと感じました。このへんはさすがに普段からトレーニングを仕事としてやってるプロです。
この進め方は会社で僕が他の社員にErlang/OTP教える際の参考にさせて頂きます。その前にもっと勉強が必要ですが....

それと、懇親会、食事と色々とお話できたのは本当に楽しかったし勉強になりました。

島根からだと交通費もばかにはなりませんが、それ以上にすばらしい時間が過ごせました。
主催の時雨堂さん、講師のLoïcさん、場所を貸して頂いたVOYAGE GROUPさん、懇親会のピザとお寿司をごちそうして頂いたBasho Japanさんには心からお礼申し上げます。

もし続編等あったら是非また参加したいと思います。ありがとうございました!

configureファイルの書き方

C言語のプロジェクトでautoconfやらautomakeやら使ってconfigureスクリプトつくる手順を勉強したのでメモっときます。
なお、MacOSX "Mountain Lion"では、たとえXcodeを入れてあっても下記で使用するautoconfやautomakeがないようです。以下のサイトに従ってこれらをインストールする必要があります。
Autoconf and Automake on Mac OS X Mountain Lion

MacOSX Mountain Lion での下準備

一応説明抜きで実行する内容を抜粋して記録
MacOSX Mountain Linon用。Linux等の場合はそれぞれのパッケージ管理ツールから入れるのがよいと思います。

$ curl -OL http://ftpmirror.gnu.org/autoconf/autoconf-2.68.tar.gz
$ tar xzf autoconf-2.68.tar.gz
$ cd autoconf-2.68
$ ./configure --prefix=/usr/local/my_build_tools
$ make
$ make install
$ export PATH=/usr/local/my_build_tools/bin:${PATH}

$ cd ..
$ curl -OL http://ftpmirror.gnu.org/automake/automake-1.11.tar.gz
$ tar xzf automake-1.11.tar.gz
$ cd automake-1.11
$ ./configure --prefix=/usr/local/my_build_tools
$ make
$ make install

$ cd ..
$ curl -OL http://ftpmirror.gnu.org/libtool/libtool-2.4.tar.gz
$ tar xzf libtool-2.4.tar.gz
$ cd libtool-2.4
$ ./configure --prefix=/usr/local/my_build_tools
$ make
$ make install

これにより"/usr/local/my_build_tools/bin"以下に各種ツールがインストールされます。
必要に応じてパスを通して使用します。

ソースファイルを生成

とりあえず、test.cにmain()をおいて、print.hとprint.c に print(char *msg) という関数をおくことにします。
ソースファイルの置き場所はトップディレクトリ以下の src 以下。

src/print.h

void print(char *msg);

src/print.c

#include <stdio.h>

void print(char *msg)
{
  printf("say: %s\n", msg);
}

src/test.c

#include <stdio.h>
#include "print.h"

int main()
{
  print("hello configure!");
  return 0;
}

Makefile.amを作る

ここでMakefile.amを作りますが、これはディレクトリごとに必要となります。
このサンプルプログラムの場合は現時点でトップディレクトリと"src"ディレクトリにひとつづつ必要ということになります。

まずはプロジェクトのトップディレクトリに以下のようなMakefile.amというファイルを作ります。
ここでは、トップディレクトリでは特にやることはないのでソースファイルのある"src"ディレクトリをサブディレクトリとして指定しています。

Makefile.am

SUBDIRS = src

続いて以下のようにsrcディレクトリ以下にMakefile.amというファイルを作ります。
src/Makefile.am

bin_PROGRAMS = test
test_SOURCES =  test.c print.h print.c

configure.inの生成、編集

プロジェクトのトップディレクトリでautoscanを実行してconfigure.inのひな形を作ります。

$ autoscan

上記コマンドを実行すると、カレントディレクトリに"configure.scan"というファイルが作られます。ので、これをconfigure.inというファイル名に変更して編集します。

$ mv configure.scan configure.in
$ emacs configure.in &

編集後のconfigure.inは以下の通り。
編集箇所は

  1. AC_INITの引数をプロジェクトにあわせて修正する
  2. AM_INIT_AUTOMAKEを追加
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.68])
AC_INIT([test], [0.1], [hoge_tara@mail.com]) # 各種プロジェクトの情報に修正
AM_INIT_AUTOMAKE                             # AM_INIT_AUTOMAKEを追加
AC_CONFIG_SRCDIR([src/print.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile
                 src/Makefile])
AC_OUTPUT

Makefile.in を生成

あとはautoheader, aclocal, automakeを実行して本命のconfigureスクリプトを生成します。

$ autoheader
$ aclocal

続いてautomake

$ automake --add-missing --copy

Useless use of /d modifier in transliteration operator at /usr/local/my_build_tools/share/automake-1.11/Automake/Wrap.pm line 58.
configure.in:6: installing `./install-sh'
configure.in:6: installing `./missing'
src/Makefile.am: installing `./depcomp'
Makefile.am: installing `./INSTALL'
Makefile.am: required file `./NEWS' not found
Makefile.am: required file `./README' not found
Makefile.am: required file `./AUTHORS' not found
Makefile.am: required file `./ChangeLog' not found
Makefile.am: installing `./COPYING' using GNU General Public License v3 file
Makefile.am:     Consider adding the COPYING file to the version control system
Makefile.am:     for your code, to avoid questions about which license your project uses.

おっと、NEWS,README,AUTHORS,ChangeLogが無いぞってしかられてしまいました。
あとでちゃんと作るとして、とりあえず空のファイルを作っておきます。

$ touch NEWS README AUTHORS ChangeLog

再度automakeします

$ automake --add-missing --copy

今度はうまくいきました。カレントディレクトリにMakefile.inが作られたことを確認します。

configure スクリプトを生成

automakeを実行してconfigureスクリプトを生成します。

$ autoconf

カレントディレクトリ以下に"configure"という実行可能ファイルが生成されたことを確認します。

試しにビルドしてみる

早速、生成された./configureスクリプトを使ってMakefileを生成、ビルド、インストールまで一気にやってみます。

$ ./configure

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... ./install-sh -c -d
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
....

configureスクリプトによるシステムチェックが走ります。
これが終わるとMakefileがカレントディレクトリにできているはずですので確認します。

makeを実行します。

$ make

make  all-recursive
Making all in src
gcc -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT test.o -MD -MP -MF .deps/test.Tpo -c -o test.o test.c
mv -f .deps/test.Tpo .deps/test.Po
gcc -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT print.o -MD -MP -MF .deps/print.Tpo -c -o print.o print.c
mv -f .deps/print.Tpo .deps/print.Po
gcc  -g -O2   -o test test.o print.o  
make[2]: Nothing to be done for `all-am'.

うまくいったようです。"src"ディレクトリに"test"という実行可能ファイルができていることを確認します。
試しに実行してみます。

./src/test 
say: hello configure!

ちゃんと動作しました。

続いてインストールしてみます。

$ sudo make install

configureにprefixオプションを渡していない場合は/usr/local/bin/test にインストールされます。

$ /usr/local/bin/test
say: hello configure!

ちゃんとインストールされました (ワーイ

おまけ:bootstrapファイル

ファイル追加した場合などはautoheaderから実行する必要があるっぽいので下記のようにスクリプトにしておけば楽かなって。

emacs bootstrap &

./bootstrap

#!/bin/sh

autoheader
aclocal
automake --add-missing --copy

実行権限を与えて実行してみる

$ chmod 755 bootstrap
$ ./bootstrap

ファイルを追加したりしてプロジェクトの構成が変化した場合はMakefile.amにファイル名を追加して./bootstrapを実行すればMakefile.inやconfigureを作り直してくれます。たぶん。

chef-solo + knife 覚え書き

イントロダクション

Twitterのタイムラインで見かけた「入門 Chef Solo」という、chef soloの入門書を試しに買ってみたら、案外わかりやすくてサクッと入門できました。
chef入門したいけど、どこから手をつけていいか解らない場合はこの本を読んでみることをおすすめします。
890円とお手頃価格ですし、vagrantの使い方なども書いてあって入門書としてわかりやすいです。



インストール

インストールはサクッと

 # gem install chef

としてもいいですが、僕はあまりシステムのgemに色々入れたくないので、下記のようにGemfileを用意すればbundlerで個別にパッケージ管理できます。

ource "http://rubygems.org"

gem "chef",                "~> 11.6.0"
gem "knife-solo",          "~> 0.3.0"

あとはbundle install

 $ bundle install --path=.path

書きかけ...

Erlang + ranchでTCPサーバー

Erlang製サーバーcowboyから汎用サーバー部分を分離したranchを使ってTCPサーバー実装する方法のメモ

アプリケーション作成
rebarを落としてranch_sampleて名前のアプリケーションを作る
すでにあるアプリケーションに組み込む場合は不要ですが。

$ mkdir ranch_sample
$ cd ranch_sample
$ wget https://github.com/rebar/rebar/wiki/rebar
$ chmod 755 rebar
$ ./rebar create-app appid=ranch_sample

rebar.configを作成してdepsにranchを追加。こんな感じで

%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et

{erl_opts, [warnings_as_errors,
            warn_export_all,
            warn_unused_import,
            warn_untyped_record
            ]}.
{xref_checks, [undefined_function_calls]}.
{sub_dirs, ["rel"]}.

{edoc_opts, [{doclet, edown_doclet}, 
             {dialyzer_specs, all}, 
             {report_missing_type, true},
             {report_type_mismatch, true}, 
             {pretty_print, erl_pp},
             {preprocess, true}]}.

{validate_app_modules, true}.

{deps, [
	{ranch, "0.8.*", 
         {git, "git://github.com/extend/ranch.git", {branch, "master"}}}
       ]}.

とりあえずここまでのところで、ranchの取得、コンパイルが正常に行えることを確認。

$ ./rebar get-deps
$ ./rebar compile

サーバーの実装
まずはアプリケーション起動時にranch起動するようにする。

src/ranch_sample_app.erl

-module(ranch_sample_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
    NbAcceptors = 100,
    {ok, _} = ranch:start_listener(sample, NbAcceptors, ranch_tcp, 
				   [{port, 5555}], sample_protocol, []),
    rebar_sample_sup:start_link().

stop(_State) ->
    ok.

start/2の中でranchを起動しています。
NbAcceptorsは、クライアントからのコネクションを待機するプロセスの数。
それとsample_protocolは各コネクションに対する処理を移譲するモジュール名。

こんな感じ。ほぼexampleのecho_serverのままw

-module(sample_protocol).

%% API
-export([start_link/4, init/4]).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% @end
%%--------------------------------------------------------------------
start_link(Ref, Socket, Transport, Opts) ->
    Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
    {ok, Pid}.

init(Ref, Socket, Transport, _Opts = []) ->
    ok = ranch:accept_ack(Ref),
    loop(Socket, Transport).

%%%===================================================================
%%% Internal functions
%%%===================================================================

loop(Socket, Transport) ->
    case Transport:recv(Socket, 0, 5000) of
	{ok, Data} ->
	    Response = list_to_binary([<<"You say ">>,  Data]),
	    Transport:send(Socket, Response),
	    loop(Socket, Transport);
	_ ->
	    ok = Transport:close(Socket)
    end.

動作確認
起動してみる

$ erl -pa ebin deps/*/ebin

ranchとrench_sampleを起動

> application:start(ranch).
ok
> application:start(ranch_sample).
ok

この状態でtelnetから接続してみる

$ telnet localhost 5555
hello #<- 入力
you say hello #<- サーバーからのレスポンス

あとはloopのところで実際の処理を書き足していく感じなのかな?

Erlang製Webツールキットwebmachine 触ってみた

イントロダクション
bashoがオープンソースとして公開しているwebmachine触ってみました。

webmachineはErlang製のHTTPアプリケーション用フレームワーク...じゃなくてツールキットだそうです。
といってもRuby On Railsみたいにアレもコレもな感じではなく、あくまでサーバーにHTTPもっといえばRESTful APIを被せることを目的としたアプリケーションのようです。触りはじめたばかりでよくわかりませんが。Ruby界隈だとSinatraに近い感じでしょうか。

RubyでコーディングするSinatraに比べて、webmachineではErlang/OTPを使うことになるので、単純に言語やフレームワークの学習コストはwebmachineが高くつきますが、webmachine+Erlang/OTPの場合は軽量プロセス、OTPが

インストール
とりあえず、Quick Startにしたがってインストールしてみます。

githubからコードを取得

$ git clone git://github.com/basho/webmachine

以上w

Webアプリケーション雛形の生成
生成されたディレクトリに移動して、Webアプリケーションの雛形を作る

$ mkdir ~/tmp
$ ./scripts/new_webmachine.sh demo ~/tmp

できたアプリケーションの雛形のディレクトリに移動してビルドする

$ cd ~/tmp/demo
$ make

起動
エラーなくコンパイルが終了したら、HTTPサーバーを起動します。

$ ./start.sh

つらつら〜〜っとメッセージが表示されて起動完了したら、ブラウザから http://localhost:8000 にアクセスします。

Hello, new world

と表示されたら無事起動しています。

リソースの追加
では作ったWebアプリケーションにリソースを追加してみます。
emacsなどのエディタで"priv/dispatch.conf" を開きます。
初期状態では以下の用になっているはず。

{[], demo_resource, []}.

この設定だと、"/"へのリクエストはdemo_resourceというモジュールに振られ、それ以外はNotFoundが返ります。

ではひとつ設定を追加してみます。

%%-*- mode: erlang -*-
{[], demo_resource, []}.
{["hello", '*'], webmachine_hello_resource, []}.

追加した設定により、http://localhost:8000/hello へのリクエストはwebmachine_hello_resourceというモジュールへ振られることになります。
ちなみに最初の"hello"は文字列、そのあとの'*'はアトムですので気をつけてください。"*"では期待通りに動作しません。

今はまだ、そのモジュールがないので作ります。エディタで"src/webmachine_hello_resource.erl"という名前のファイルを新規で作ります。

-module(webmachine_hello_resource).
-export([init/1, to_html/2]).

-include_lib("webmachine/include/webmachine.hrl").

init([]) -> {ok, undefined}.

to_html(ReqData, State) ->
    io:format("~p", [ReqData]),
    {"<html><body>hello</body></html>", ReqData, State}.

再びビルドして起動します。

$ make
$ ./start.sh

ブラウザから http://localhost:8000/hello にアクセスしてみます。

hello

とアクセスされたらおkですね。

先ほどのルート指定では

["hello", '*']

としていました。

ここを何種類か変えてみます。

再び"/priv/dispatch.conf"を開いて、いくつかリソースを追加します。

{[], demo_resource, []}.
{["hello", "china", '*'], webmachine_hello_china_resource, []}.
{["hello", "japan", '*'], webmachine_hello_japan_resource, []}.
{["hello", '*'], webmachine_hello_resource, []}.

以前と同様に、webmachine_hello_china_resource.erlとwebmachine_hello_japan_resource.erlを作ります。

src/webmachine_hello_china.erl

-module(webmachine_hello_china_resource).
-export([init/1, to_html/2]).

-include_lib("webmachine/include/webmachine.hrl").

init([]) -> {ok, undefined}.

to_html(ReqData, State) ->
    io:format("~p", [ReqData]),
    {"<html><body>に〜はお</body></html>", ReqData, State}.

src/webmachine_hello_japan.erl

-module(webmachine_hello_china_resource).
-export([init/1, to_html/2]).

-include_lib("webmachine/include/webmachine.hrl").

init([]) -> {ok, undefined}.

to_html(ReqData, State) ->
    io:format("~p", [ReqData]),
    {"<html><body>こんにちは</body></html>", ReqData, State}.

一旦サーバを止めてビルドしてサーバを起動します。

$ make
& ./start.sh

"http://localhost:8000/hello/china" へのリクエストでは "に〜はお"、http://localhost/8000/hello/japanへのリクエストでは "こんにちは"と表示されたと思いますがどうでしょうか。

RESTfulアプリケーション
で、最後にRESTです。とりあえずこんな感じなんだろうかと書いてみました。
"http://localhost:8000/hello/china"に対して、GET,POST,PUT,DELETEそれぞれのメソッドごとに呼び出される関数を分けています。
こういう使い方が一般的なのかどうかわかりませんが...

-module(webmachine_hello_china_resource).
-export([init/1,
	 allowed_methods/2, 
	 content_types_accepted/2,
	 post_is_create/2,
	 create_path/2,
	 to_html/2, 
	 from_json/2,
	 delete_resource/2]).

-include_lib("webmachine/include/webmachine.hrl").

init([]) -> {ok, undefined}.

%%%===================================================================
%%% Allow Methods and Accepted Content Types
%%%===================================================================

%% 許可するHTTPメソッド
allowed_methods(Req, State) ->
    {['GET', 'POST', 'PUT', 'DELETE'], Req, State}.

%% 許可するcontent type
content_types_accepted(Req, State) ->
    {[{"text/html", to_html},
      {"application/json", from_json}], Req, State}.

%%%===================================================================
%%% Post setting
%%%===================================================================

%% POSTリクエストは新規作成として扱うかどうか
post_is_create(Req, State) ->
    {true, Req, State}.

%% post_is_create/2がtrueを返したときに呼ばれる
%% 新しいリソースへのパスを返す(このパスをLocationヘッダに埋め込んでかえす)
create_path(Req, State) ->
    {"/new/path", Req,State}.

%%%===================================================================
%%% Handler Functions
%%%===================================================================

%% GET
to_html(#wm_reqdata{method = 'GET'} = Req, State) ->
    io:format("http request(method:~p).~n", [Req#wm_reqdata.method]),
    {"<html><body>に〜はお</body></html>", Req, State}.

%% POST
from_json(#wm_reqdata{method = 'POST'} = Req, State) ->
    %% なにか新規作成処理する...
    io:format("json post request(method:~p).~n", [Req#wm_reqdata.method]),
    {true, Req, State};

%% PUT
from_json(#wm_reqdata{method = 'PUT'} = Req, State) ->
    %% なにか更新処理する...
    io:format("json put request(method:~p).~n", [Req#wm_reqdata.method]),
    {true, Req, State}.

%% DELETE
delete_resource(Req, State) ->
    %% 削除処理する...
    io:format("delete request(method:~p).~n", [Req#wm_reqdata.method]),
    {true, Req, State}.

で、ここまできて今更ですが、わかりやすいスライドありました。

それからVoluntas氏によるGistが 
https://gist.github.com/voluntas/4363064