ラベル dev の投稿を表示しています。 すべての投稿を表示
ラベル dev の投稿を表示しています。 すべての投稿を表示

2015年9月27日日曜日

Joel on Software 読書のすすめ

皆さんは "Joel on Software"(日本語訳版『ジョエル・オン・ソフトウェア』オーム社) という書籍をご存知だろうか。この本は Joel Spolsky という人がソフトウェアに関わる様々なこと、例えば言語選択の方法、 Unicode についての基礎知識、良いプログラマの雇い方、非技術系マネージャの頭の中など、多岐に渡る話題に対して彼なりの考え方を書いたものだ。


この本について書評を書こうと思ったのは内容が非常に良かったからに他ならない。本の帯には「マネジメントの世界にようこそ!」とあるが、よくあるマネジメントに関する教科書のように(というか、マネジメントに限らず退屈な教科書によく見られることだが)無味乾燥な解説が並んでいるのではない。ユーモア溢れる文体で笑いながら読めるのである。技術書なのに、まるで落語家が喋っているかのように私は感じた。

例えば Unicode に関する章(*)には次のような段落がある。
私は宣言する。もしあなたが21世紀において仕事しているプログラマであり、キャラクタ、キャラクタセット、エンコーディング、Unicodeの基本について知らないのであれば、私はあなたをひっ捕まえて、潜水艦で6ヶ月間のたまねぎ剥きの刑に処する。絶対そうするから。
私はこれを読んだときゲラゲラと笑ってしまった。Unicodeに関する技術的な記事なのに、である。このように楽しい表現が本書の随所に見られ、Joel さんの文章力(人心掌握力)に感心するばっかりだ。私は日本語訳を読んでいるので、訳者の青木靖さんのユーモアセンスによる部分もあるかもしれないけど。

(*)「すべてのソフトウェア開発者が絶対確実に知っていなければならない Unicode とキャラクタセットに関する最低限のこと(良い訳なし!)」という章題からして面白い。

この本はLinuxのコマンドラインだとか、特定のプログラミング言語だとか、今流行の技術だとかについては解説しない。この本は、あなたがプログラムを作って売る会社で働く、またはそのような会社を経営するとして、どうすればビジネスとして成功するかを解説している。あなたがプログラマなら、古いクズコードを捨ててスクラッチから書き直したいと思ったことがあるだろう。ビジネスをする上で、それがいかに悪い戦略であるかを、プログラマなら誰でも納得できる理由とともに説明している。

私がお気に入りなのは「氷山の秘密、明らかに」という章だ。題名からは何の話だかまったく分からないが、つまりこういうことだ:プログラミング作業全体を氷山として見たとき、ユーザインターフェースに関する部分は水面に出ている10%程度でしかない。だから、UI以外の部分を作るのに全体のスケジュールの90%の時間がかかるのだ。そして「プログラマ以外の人々はこの事実を理解していない。(p.212)」

章題の面白さもさることながら、本文も筆者の体験を踏まえて非常にコミカルに書かれ、それでいて明日からさっそく役立ちそうな実践的な話になっている。顧客または非技術系マネージャがあなたのプログラムを見る時はUIしか見ないので、プロトタイプとして最初にほとんど完璧なUIを見せてしまうと、彼らはその時点でプログラムが完成していると思ってしまう。そして、残りの期間、「人々はあなたが何をしているのか分からず、何もしていないと思うのだ。(p.213)」

この本は次のような人におすすめする。

  • 趣味ではなく、仕事でプログラムを書く人
  • スケジュール管理をしたり、社長に進捗を報告する必要のある人
  • 会社を成功させるためにソフトウェアの開発・販売戦略を考える人

この本の主軸は、ビジネスとしてソフトウェア開発が成功する方法について、である。その世界では、高品質なソフトウェアをなるべく出荷することが正義であり、プログラマ生活を楽しく過ごすことが正義である。そのためにスケジュール管理や仕様書が大切であり、有能なプログラマを雇って集中できる環境を作ることが大切なのだ。

仕様書が大切だ、というと、あの堅苦しい、バインダー数冊分もある分厚い仕様書を書くなんて御免だ!と思うかもしれない。その通り。本書でいう良い仕様書とは、可笑しく、学術論文のように堅苦しくなく、箇条書き・挿絵・チャート・表・空白をたくさん使い、画一的なテンプレートには従わないで書かれた、プログラムの機能仕様書である。仕様書はその製品に関わるプログラマ、品質保証の人、マーケティングの人、マネージャなどが読むための文書であり、プログラマでない彼らが読もうと思える文書にしなければならない。本書には明日から実践できる具体的なルールが詰まっている。

ここでは仕様書に関する本書の内容を紹介したが、スケジュール管理の方法、有能なプログラマを雇う方法、彼らを集中できるようにする方法ももちろん書かれている。ソフトウェアを作って売ることに関係がある方・または将来関係を持つだろう方には、強くこの本をおすすめする。ああ、どうして、私がソフトウェア工学を学んでいた大学院生のときにこの本を読まなかったのだろう。

2015年8月14日金曜日

出版のお知らせ 『自作エミュレータで学ぶx86アーキテクチャ』

久しぶりのブログ更新となりました。
理由の一つは、ここ数ヶ月は暇さえあれば本の原稿を書いていたからです。

本のタイトルはちょっと長いですが
『自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解!』
ということになりました。
8月28日に出版予定でございます。



2015年8月23日更新
Kindle 版が 2,152円+税で予約開始したようです。
自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解!


共著の方(d-kamiさん)のブログでも紹介されています。
というわけで本を出版します - マイペースなプログラミング日記

タイトルの通り、この本はx86 CPUの機械語を実行するエミュレータソフトウェアを作りながらx86アーキテクチャを学んでいこうという本です。例えばinc [ebp-4]を機械語に変換するとff 45 fcになり、それぞれがオペコード、ModR/M、ディスプレースメントに対応することを学びます。ModR/Mの中も実はビット単位で意味があり、Mod、REG、R/Mに分かれているんだよという話もします。

2015年8月25日更新:
本書で作るエミュレータは x86 の中でも一部の機能を実装したとてもシンプルなものです。一言で表せば、オペランドサイズが 32 ビットのリアルモードエミュレータです。リアルモードの全命令を実装しているわけではありませんし、セグメント機構など高度な機能も実装しませんが、それでも C 言語で書いたプログラムを動かすことができるくらいの機能は持っています。人によっては物足りなく感じる方もいらっしゃると思いますが、本書は C 言語を学び終えたくらいの人が次に読む本を想定していますのでご了承ください。

CPUに閉じることなく、CPUとその周りとの関連も扱っています。x86 CPUはメインメモリにスタック構造を作ります。C言語入門した直後だとそもそもスタックを知らないかもしれませんので、一般的なスタックの話から始めて実際にメモリ上に作られるスタックフレームの話まで書いています。また、メモリより外の世界としてI/Oの話も扱います。CPUはI/Oを介して外界に繋がらなければ役立つ仕事はできません。エミュレータにin/out命令とキャラクタデバイスを実装し、実際にキーボード入力とディスプレイ出力を行えるようにしてみます。

最後の章では、実機でプログラムを実行する方法も説明しています。USBメモリのブートセクタに自作の機械語を書き込み、パソコンを起動させ、OSの力を借りずにプログラムを実行させます。BIOSの文字表示機能を使って、色付きの文字列を画面に表示させるのがゴールです。




この本はC言語の入門書を読み終わったくらいのレベルの人が読めることを目指して書いているため、C言語プログラミングで入門の次に知ると良さそうな事柄、例えばファイル分割の方法とか、printfで色付きの文字列を出力する方法なども扱っています。1章分のページをまるまる使って、アセンブリ言語の視点からポインタを学び直すので、C言語のポインタで躓いてしまった人にも何らかのひらめきを与えられる可能性があります。

また、本書を通してx86エミュレータというある程度の規模のソフトウェアを作りあげる経験を通して、読者のプログラミング能力を養おうという裏の目標もあったりします。手軽にx86の機械語を実行できる環境を作るという(ある意味)実用的なゴールを持ってプログラミングを行うことで、学習のための学習より効果の高いプログラミング練習になるでしょう。

お近くの書店で目についた際にチラ見していただければ幸いです。

最後に目次情報を載せておきます。

Chapter 1: C言語とアセンブリ言語

  • 1.1. C言語から機械語へ
  • 1.2. 機械語とアセンブリ言語
  • 1.3. 機械語に飛び込む
  • 1.4. アセンブリ言語を少し詳しく
  • 1.5. 基本のmov命令
  • 1.6. インクリメント専用のinc命令
  • 1.7. 16進数入門
  • 1.8. 2の補数入門

Chapter 2: ポインタとアセンブリ言語

  • 2.1. レジスタ
  • 2.2. メモリ
  • 2.3. 初めてのエミュレータ
  • 2.4. ポインタの復習
  • 2.5. ポインタに飛び込む
  • 2.6. 構造体とポインタ
  • 2.7. 不完全型とポインタ
  • 2.8. 関数ポインタ

Chapter 3: CPUがプログラムを実行する仕組み

  • 3.1. プログラムの配置
  • 3.2. エミュレータのorg対応
  • 3.3. プログラムの実行
  • 3.4. エミュレータのModR/M対応
  • 3.5. 無条件分岐命令
  • 3.6. call命令とスタック
  • 3.7. エミュレータのcall対応
  • 3.8. ローカル変数とスタック
  • 3.9. フラグレジスタと条件分岐命令
  • 3.10. エミュレータの条件分岐命令対応
  • 3.11. プログラムの繰り返し
  • 3.12. デバイスアクセス

Chapter 4: BIOSの仕組みと実機起動

  • 4.1. BIOS
  • 4.2. BIOSの実装
  • 4.3. 割り込み
  • 4.4. ブートセクタ
  • 4.5. PBRを見てみよう
  • 4.6. 実機で動かしてみよう

2015年5月31日日曜日

Qt Creator で Qt Quick アプリを作るときの落とし穴回避 & TIPS

接続した Android タブレットが「互換性のないデバイス」と表示されてしまう

その Android が armeabi-v7a アーキテクチャのときに Android for x86 などのキットを使うと互換性がない。

プロジェクトのプロパティ画面で、対象のアーキテクチャ用のキットを追加する。
目的のアーキテクチャ用のキットが見当たらない場合は maintenancetool.exe で追加インストールする。
maintenancetool.exe はスタートメニューの Qt の中にショートカットがあるはず。Qt のインストールディレクトリから直接起動してもよい。

QML ファイルを追加したけど、変更しても反映されないことがある

QML ファイルが Makefile の依存関係に取り込まれていない可能性があるので「ビルド」 >「qmake の実行」で Makefile を更新してみる。

2015年4月5日日曜日

読みやすいソースコードを書く方法

読みやすいソースを書くために気をつけていることを軽くまとめてみます。ソースコード例は Python ですが、特に言語に関係なく通用する話題です。業務などでコーディングする際の参考になれば幸いです。

良い名前を付ける

最優先で気をつけるのはこれです。「プログラミングは『名前』が9割」と言われることもあるくらい、名前付けには時間を割くべきだと思います。良い名前を付ければプログラムの見通しが良くなり全体像を把握しやすくなりますし、他人(または未来の自分)が保守しやすいソースコードになります。

では良い名前とはなんでしょうか?自分なりにまとめてみました。

名前と内容を一致させる

関数名なら処理内容を端的に表す名前にすべきですし、逆に名前から想像できない処理はしてはいけません。

私は実際に、副作用を持った operator +() の実装を見たことがあります。内部でメンバ変数を書き換えていたのです。 operator +() は C++ の機能で、自作クラスに関する加算(a + b)を独自に定義できるのですが、加算演算子ですからもちろん(外から見える)副作用を持つべきではありません。これは、関数が名前から想像できない処理をしてしまっている例です。

動的型付け言語ではもっと注意が必要なことがあります。例えば get_host() という関数を見たとき、これが Host クラスのインスタンスを返すのか、ホスト名を文字列として返すのか、新人さんは迷うはずです。ただ get_host() 自体は適切な関数名ですから、これを変えるのは難しいです。それより、ホスト名を扱う変数は常に host_name という名前にする、 get_host() の戻り値を host_obj という変数で受け取るなど、ホスト名と Host インスタンスを区別するように意識しましょう。

boolを返す関数はcheck…にしない

例えば hoge.check_available() だと hoge が有効なのが戻り値が真のときか偽のときか分かりません。 hoge.is_available() であれば紛れはありません。if 文の条件に書いたときに英文として自然になる名前を選ぶようにすると良いでしょう。

is, has など英語の三人称単数形を使うと適切なことが多いですが、敢えて動詞を使わないこともあります。例えば C++ のコンテナには、要素が空かどうかを調べる関数 empty() があります。これは形容詞だけの名前ですが、下手に check_empty() とするより分かりやすい関数名ですね。

コメントを付ける

これも基本中の基本でしょう。コメントはソースコードの理解を大幅に助けてくれますし、自分が後で読み返すときも、ソースコード断片を一つのかたまりとして思い出すのに大いに役立ちます。

ただ、闇雲にコメントを書けばよいかというとそうでもありません。多くの入門書にも書いてあることですが、コメントにも悪いコメントと良いコメントがあります。一般に、ソースコードから一瞬で分かることをコメントで書いてはいけません。関数コメントの例で言えば、get_size() に対して「サイズを返す」とコメントするのは単なるタイプ数の無駄というものです。関数名だけで十分な情報は伝わっていますから。一方で get_host() に対し「Host クラスのインスタンスを返す」とコメントするのは価値があります。この関数がホスト名でなく Host クラスのインスタンスを返すことが分かるからです。

一見して分かる情報はコメントしないという一般原則を頭に置きつつ、以下ではコメントのパワーが最大に発揮できる場面を考えていきましょう。

関数コメント

関数コメントは本当に役立ちますので、是非書いて下さい。関数コメントは普通、その関数の利用者に向けて書くものですが、関数の実装者にとってもその関数をより良いものにするために役立ちます。容易くコメントが書ける関数は、利用者にとっても直感的に使える関数になりやすく、長く複雑なコメントを書かねばならない関数は、利用も困難な関数になりがちです。関数の利用しやすさを図る尺度として、関数コメントが役立つのです。

関数コメントには最低限、その関数が何をするかという一言を書いて下さい。簡単な関数ならそれだけで十分です。少し大きな関数には、引数と戻り値の意味をコメントします。実践に則した関数の使用例があるとなお良いです。Python なら関数コメントに実行例を含めて python -m doctest hoge.py などと自動テストができますから、そのテストに合格するように書くと完璧です。

def fetch_hosts(conn, klass):
    '''class が `klass` であるホストオブジェクトすべてをテーブルから読み込んで返す。

    conn    - DB コネクションオブジェクト
    klass   - Host クラスのサブクラスオブジェクト

    >>> conn = MySQLdb.connect(host='127.0.0.1', db='hostdb')
    >>> hosts = fetch_hosts(conn, AppHost)
    >>> conn.close()
    '''

    # do something

ソースコードコメント

ソースコード断片に対してコメントを書くことも可読性の向上に役立ちます。ソースコードを意味のまとまりごとに空行を入れて分け、それぞれのソースコード断片に対して1行から数行のコメントを書くと調度良い感じです。コメントは、その断片が何をしようとしているのか、意図を書くようにします。例えば、注文履歴をもとに各商品が何個売れたかを計算するプログラムにコメントを書いてみます。

# 昨日の注文履歴をすべて取得する。
begin_dt = date.today() - timedelta(days=1)
end_dt = date.today()
sells = fetch_sells(conn, begin_dt, end_dt)

# 商品ごとの注文数をカウントする。 { 商品名 : 注文数 }
item_sells = defaultdict(int)
for sell in sells:
    item_sells[sell.item_name] += sell.count

このように、それぞれのソースコード断片が何を目的としているかを簡単に書いておくと、他人がソースコードを理解するときにも、自分が後で処理の全体像を読み返すときにも役立ちます。プログラムを単に日本語に変換しただけのような、ソースコードを読めば分かるコメントにならないように注意してください。

グローバル変数を使わない

グローバル変数やシングルトンなど、どこからでも参照できる変数があるとソースコードが読みにくく、保守しにくくなる場合が多いです。あるソースコード断片が上手く動くかどうかがその変数の状態に依存する場合、分かりにくいバグの原因となります。例えば、DBに接続してユーザ一覧を取得するプログラムを考えます。

setup_db()
user_db = UserDB.instance()
users = user_db.get_users()

setup_db() は UserDB.instance() を初期化してデータベースに接続する関数です。User.instance() はシングルトンの実装になっていますので setup_db() はあるプロセスの中で1回だけ呼び出せば良く、他の場所でユーザ一覧が欲しいと思えば以下のように書くことになります。

# setup_db() ?
user_db = UserDB.instance()
users = user_db.get_users()

ここで、プログラマは setup_db() を呼び出すべきかどうかを考えます。もし、この部分より前で setup_db() が呼び出されるなら、基本的にはここで呼ぶ必要はありません。しかし、もしこの2行が直前の setup_db() の呼び出しと別プロセスで動くなら、再度 setup_db() を呼ぶ必要があります。別プロセスで動くかどうかはこの2行からは判断できず、プログラム全体のアーキテクチャを理解する必要があります。この例では、グローバル変数の使用が保守性を著しく低下させています。

2015年1月31日土曜日

bossac で Arduino Due に書き込む

Mac OS X Mavericks のターミナルで Arduino Due にプログラムを書き込む方法です。
getting-started_flash.bin をフラッシュメモリに書き込んでみます。

stty -f /dev/cu.usbmodem1411 1200
/Applications/Arduino.app/Contents/Java/hardware/tools/bossac -U false -p cu.usbmodem1411 -e -w -v -b getting-started_flash.bin -R

bossac で Arduino Due に書き込む前に stty コマンドで通信速度を 1200 bps に設定するのがミソです。これをやらないと "No device found on cu.usbmodem1411" というエラーが出てしまいます。

サンプルプログラムの準備

ちなみに、書き込むための bin ファイルは ASF (Atmel Software Framework) に付属のサンプルプログラムを GCC でビルドしました。ダウンロードページに "ASF standalone archive for GCC (makefile-based) ..." というリンクがありますので、そこからダウンロードして展開します(執筆時点での最新版が 3.20.1 でした。以下 ~/Downloads/xdk-asf-3.20.1 に展開したとします)。

$ export PATH=$PATH:/Applications/Arduino.app/Contents/Java/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/
$ cd ~/Downloads/xdk-asf-3.20.1/sam/applications/getting-started/sam3x8e_arduino_due_x/gcc
$ make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all PROJECT_TYPE=flash
CC      common/services/clock/sam3x/sysclk.o
MKDIR   common/services/serial/
CC      common/services/serial/usart_serial.o
MKDIR   common/utils/interrupt/
...
$ ls *.bin
getting-started_flash.bin getting-started_sram.bin

まず Arduino IDE 付属の GCC コマンドへのパスを通します。
パスを通したら展開したソースコードの sam/applications/getting-started/sam3x8e_arduino_due_x/gcc に移動します。
最後に make を実行すると、カレントディレクトリに getting-started_flash.bin が出力されるはずです。

ASF には Arduino Due で使えるサンプルプログラムが幾つか入っています。以下のように探せます。

$ cd ~/Downloads/xdk-asf-3.20.1/sam
$ find . -name Makefile | grep arduino
./applications/getting-started/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_temp_sensor_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_threshold_wakeup_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/chipid/chipid_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/dacc/sinewave_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/gpbr/unit_tests/sam3x8e_arduino_due_x/gcc/Makefile
...

2015年1月19日月曜日

同じval++;でもaddになったりincになったり。

最近GCCのビルドをたくさんやってるなかで気づきました。
int val = 0;
val++;
という単純なC言語プログラムでさえ、i386向けのGCCとi686向けのGCCで出力されるバイナリが違うことを。

違うのは変数のインクリメントでした。i386向けGCCは
inc dword [ebp-4]
を出力しました。一方でi686向けGCCは
add dword [ebp-4], 1
を出力したのです。

addの方がバイナリが大きくなるのですが、なぜi686向けはそちらを選択したのか。僕はその理由を知りません。不思議です。

2015/01/20追記
Twitterで良い記事を紹介していただきました。
この記事によれば、inc/dec命令でストールが発生するアーキテクチャがあるそうな。そこで、そういうアーキテクチャではincの代わりにaddを使うんだそうです。
x86アーキテクチャの闇は深い。

2015年1月16日金曜日

Python の exit(), sys.exit(), os._exit() の違い

Python には 3つの似たようなプログラム終了用の関数があります。
exit(), sys.exit(), os._exit()です。
これらの違いを簡単に調べてまとめてみました。

ものぐさな人へ


  • インタラクティブシェルを終了するには exit()
  • スクリプトの中でメインプロセスを終了するには sys.exit()
  • fork() した子プロセスを終了するには os._exit()


exit([code=None])

site モジュールにより、プログラムの起動時に自動的に追加される定数。
exit 自体が表示されると画面に "Use quit() or Ctrl-D (i.e. EOF) to exit" のようなメッセージを表示し、
exit() と呼び出すと、終了コードを伴って SystemExit 例外を投げる不思議なオブジェクト。

sys.exit() にも言えるが、例外を投げるだけなので、その外側で例外を捕捉してシステムを終了させないようにすることも可能。

sys.exit([arg])

終了コードを伴って SystemExit 例外を投げる関数。
つまり、上で紹介した exit() と呼び出し時の挙動は同じ。

os._exit(n)

終了ステータス n でプロセスを終了する。
これは exit(), sys.exit() と違って、例外を投げるのではなくプロセスを終了させる。
os._exit() は fork() された後の子プロセスを終了させるときに使うのが標準的。

2014年7月21日月曜日

PCで音声認識してmbedを制御する(解説編)

この記事はPCで音声認識してmbedを制御するに対する解説記事です。



全体構成

システム全体の構成を以下に示します。大きく4つの部分から構成しています。

mbed

パソコンのシリアルポートと接続し、パソコンからJSON形式の命令を受け取って動作します。
実際にはmbedとパソコンをUSBケーブルで繋ぐと、仮想的なRS-232ポートとして認識されます。

Webサーバー

Webブラウザとmbedの橋渡しをします。Webブラウザから受け取ったGET要求を解釈し、シリアルポートにJSON形式の命令を送ります。
JavaScriptからシリアルポートを直接扱えませんので、このサーバーを作成して橋渡しさせます。

index.html

マイクの音声をWeb Speech APIで文字列に変換し、その文字列を元にWebサーバーに命令を送ります。
XMLHttpRequestを使い、http://localhost:8080/mbed?cmd=right
のようなURLにGET要求を送ります。

音声認識サーバー

Web Speech APIが内部的に呼び出すサーバーです。いや、もしかしたらWeb Speech APIは外部サーバーを使わないで実装されてるかもしれませんが、まあほぼ、Googleのサーバーを使っていると思うわけです。なんせこの記事の執筆時点では、Google ChromeでしかWeb Speech APIが使えませんので。
Web Speech APIの利用側からは音声認識サーバーを意識する必要はありません。

動作

自作Webサーバーに置いてあるindex.htmlをGoogle Chromeで開き、パソコンのマイクに向かってしゃべります。「みぎ!」とか「ひだり!」とか。
次にWeb Speech APIを使い、その音声をテキストに変換します。「右」とか「左」という文字列が帰ってきます。
その結果を基にして、自作WebサーバーにGETリクエストを送ります。http://localhost:8000/mbed?cmd=right
という感じのURLに対してXMLHttpRequestを使ってGETすればOKです。

GETリクエストを受け取ったWebサーバーは、pySerialライブラリを使ってmbedにJSON形式の命令を送ります。
コマンドは{"cmd"="right"}みたいな文字列を送ります。rightの部分には、GETリクエストのcmd引数の値を埋め込みます。

mbedは常にシリアルポートを監視しており、1文字来る度にpicojsonライブラリで解析します。JSON形式の数値、文字列、オブジェクトのいずれか一つ受信するとpicojsonから処理が帰ってきますので、picojsonから得られたJSONオブジェクトを利用し、mbedに付いてるLEDを制御します。例えば以下のように。
if (obj["cmd"] == "right") {
    led1 = 1;
}

詳細説明

picojsonとmbed

picojsonは色々な使い方ができます。std::istreamの>>演算子を使う、文字列のバッファを与える、入力イテレータを与える、という3つの方法がありますが、今回は一番最後の入力イテレータの方法を使います。
シリアルポートからは標準入力とか文字列のバッファと違い、データが1文字ずつ送られて来ますし、JSONオブジェクトの区切りもpicojsonに自動でやって欲しいからです。

ここでちょっと問題がありました。std::istream_iteratorを使うと、picojsonの実装の関係で文字が1文字遅れてしまうのです。この問題を詳しく書くのは別の機会に譲るとして、次のようなデータをシリアルポートに送ると、最後の{が受信された時点で、初めて1つのJSONオブジェクトがpicojsonから返されます。
{"cmd"="right"}{
これはまずいです。mbedを制御しようとしてPythonから命令を送ってもすぐには反応せず、次の命令を送ったときに初めて前の命令が実行される、という動作になってしまうのです。
これを解決するのに、自作の入力イテレータを実装しました。それが次のクラス。


template <class T, class CharT = char,
          class Traits = std::char_traits<CharT>,
          class Distance = std::ptrdiff_t>
class NoDelayIstreamIterator
{
    mutable std::istream_iterator<T, CharT, Traits, Distance> iter_;
    mutable bool next_;
public:
    typedef typename std::istream_iterator<T, CharT, Traits, Distance>::istream_type istream_type;

    NoDelayIstreamIterator()
        : iter_(), next_(false)
    {}
    NoDelayIstreamIterator(istream_type& s)
        : iter_(s), next_(false)
    {}
    NoDelayIstreamIterator(const NoDelayIstreamIterator& x)
        : iter_(x.iter_), next_(x.next_)
    {}

    const T& operator*() const
    {
        if (next_)
        {
            ++iter_;
            next_ = false;
        }
        return *iter_;
    }

    NoDelayIstreamIterator<T, CharT, Traits, Distance>& operator++()
    {
        next_ = true;
        return *this;
    }

    NoDelayIstreamIterator<T, CharT, Traits, Distance> operator++(int)
    {
        NoDelayIstreamIterator<T, CharT, Traits, Distance> temp(*this);
        temp.next_ = true;
        return temp;
    }

    bool operator==(const NoDelayIstreamIterator& y) const
    {
        return iter_ == y.iter_ && next_ == y.next_;
    }

    bool operator!=(const NoDelayIstreamIterator& y) const
    {
        return ! (*this == y);
    }
};

operator++では実際にはデータを読み込まず、続くoperator*の実行でデータを読み取るのがポイントです。picojsonでは++の後は必ず*が呼ばれますので、ちょっと実装をサボっています。連続で++を呼び出してはいけません。

利用側は次のようなコードになります。

int main() {
    picojson::value json;
    picojson::default_parse_context ctx(&json);
    NoDelayIstreamIterator<char> input(cin);
    string err;
    
    for (;;)
    {
        input = picojson::_parse(ctx, input, NoDelayIstreamIterator<char>(), &err);
        if (!err.empty()) {
            cerr << err << endl;
        }
        
        picojson::value::object& o = json.get<picojson::value::object>();
        std::string cmd = o["cmd"].get<std::string>();

後はmbedのLEDをDigitalOut - ピン出力で紹介されているような方法を使うなどして、LEDをピカピカさせましょう!(筆者は4つのLEDを左右へ順番に光らせるプログラムを書いたのですが、ソースコードを消してしまったので掲載できませんでした。ごめんなさい)

PythonとpySerial

mbed側でJSON形式の文字列をシリアルポート経由で受け取り、LEDを制御するまで解説しました。ここではパソコン側の実装を説明します。パソコン側は、Pythonを用いてWebサーバーを作り、その上でindex.htmlを動かし、またシリアルポートへJSON文字列を送信します。

まずWebサーバーです。PythonでWebサーバーというと幾つかライブラリがあります。今回はTornadoを使いました。

その前にWebサーバーの解説

Webサーバーと言われてピンと来ない方のために少し解説します。Webサーバーとは超大雑把に言えば、ファイルを配信するコンピュータのことです。皆さんは普段、インターネットで情報を見るのにInternet ExplorerとかFirefoxとかGoogle ChromeとかのWebブラウザーを使っていると思います。現にこのブログを見てくださっているということは、ほぼ確実に何らかのWebブラウザーを使っているはずです。そして、皆さんが見ている情報は、Webサーバーが配信してくれています。
このブログの記事も、例にもれずBloggerさんが保持するWebサーバー(以降、Bloggerサーバー)上に保管されています。Webブラウザーを使ってBloggerサーバーへ「uchanoteブログの2014年5月『PCで音声認識してmbedを制御する』の記事をくれ!」と要求すると、Bloggerサーバーが該当の記事の内容を送り返してくれるのです。

その要求を具体的に表すのがURLです。「uchanoteブログの2014年5月『PCで音声認識してmbedを制御する』の記事」を要求する場合、次のようなURLになります。WebブラウザーにこのURLを打ち込むと該当の記事を読めます。

http://uchanote.blogspot.jp/2014/05/pcmbed.html

URLには構造があります。大きく以下の3つの部分に別れています。

  • "http://":通信方式を表します。以降の話には関係ないので、詳しくは書きません。
  • "uchanote.blogspot.jp":Webサーバーを識別する名前です。他のサーバーと重複しない名前になっています。
  • "/2014/05/pcmbed.html":実際に内容を要求するファイルの名前です。2014年の5月のpcmbed.htmlという記事を要求しています。

という感じです。このURLをWebブラウザーのURL欄に貼り付けますと、内部的に
GET /2014/05/pcmbed.html HTTP/1.1
という命令に変換され、Webサーバーへと送信されます。Webサーバー側はこの文字列を読み込んで解析し、「ふむふむ。Webブラウザー君はuchanote.blogspot.jp上の/2014/05/pcmbed.htmlというファイルをGETしたいのだな」と理解します。

Webサーバーが送り返すのはファイルでなくてもいい

で、ここが大切なのですが、/2014/05/pcmbed.htmlの部分は実際のファイル名である必要がありません。Webサーバーが理解できる何かの名前であればいいのです。そこにファイル名を書けば、Webブラウザーへ送るべきデータをファイルとして保管しておけて楽だ、というだけなのです。
例えば
GET /happy_birthday HTTP/1.1
という命令を受けて、実際には/2014/05/pcmbed.htmlのファイルを送り返してもいいですし、そもそも実在するファイルの中身を送り返す必要もありません。Webブラウザーは、Webサーバー側に本当に/happy_birthdayという名のファイルが有るのか、それともWebサーバーがその場限りの適当なデータをでっち上げているのかは判断できません。

WebサーバーはWebブラウザーから文字列を受け取ると

  1. 文字列を解釈してどんな命令かを理解し
  2. 命令に基いて何か作業をし(普通は、送り返すためのデータを準備する)
  3. Webブラウザーにデータを送り返す
という動作をします。Webサーバーは本来、3番目が最終的な目的なのですが、今回は2番目が主役となります。Webブラウザーからの命令に基いてmbedを制御する、これこそが今回やりたかったことです。Webブラウザーに送り返すデータなんて、「Success」のような固定の文字列で十分です。(何も送り返さなくてもいいくらいです)

PythonでWebサーバー

ということで、今回作るWebサーバーは
GET /なんちゃら HTTP/1.1
の「/なんちゃら」の部分をmbed制御用指令だと解釈し、mbedに信号を送るものにします。こうすることで、WebブラウザーのURL欄に「http://localhost/なんちゃら」と入力すると、mbedが「なんちゃら」の通りの動きをしてくれます。ということで作ったWebサーバーのソースコードを以下に示します。全体でこれだけです。

import argparse
import serial
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        f = open('index.html')
        self.write(f.read())

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        cmd = self.get_argument('cmd', default=None)
        if cmd is not None:
            s = '{{"cmd":"{0}"}}'.format(cmd)
            serial_port.write(s.encode('ascii'))
            self.write('Sent command to serial port: ' + s)
            print('Sent command to serial port: ' + s)
        else:
            self.write('cmd parameter must be specified')

app = tornado.web.Application([
    (r'/', IndexHandler),
    (r'/mbed', MainHandler),
])

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Fujimin Web Server')
    parser.add_argument('--serial-device', default='/dev/tty.usbmodem1422')
    args = parser.parse_args()

    global serial_port
    serial_port = serial.Serial(args.serial_device, 9600)
    app.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

「/なんちゃら」の部分は
app = tornado.web.Application([
    (r'/', IndexHandler),
    (r'/mbed', MainHandler),
])
に対応します。「/」をGETするとIndexHandlerが、「/mbed」をGETするとMainHandlerが起動します。詳しく言うと、
GET / HTTP/1.1
というGET要求についてWebブラウザーに送るデータを準備するのがIndexHandler、
GET /mbed HTTP/1.1
というGET要求についてWebブラウザーに送るデータを準備するのがMainHandlerということです。

IndexHandlerクラスの中のgetメソッドを見てみると、index.htmlファイルを開き(open)、そのファイルの中身を全部読み込んで(read)、それをWebブラウザーに送ります(self.write)。簡単ですね。普通のWebサーバーの動作です。
ちなみに、index.htmlファイルにはWeb Speech APIを利用した音声認識の仕組みと、音声認識の結果を使って/mbedにGET要求を発行する仕組みが入っています。詳しくは後で説明します。

MainHandlerクラスの中のgetメソッドが大切な部分です。この中の
serial_port.write(s.encode('ascii'))
という行が、実際にmbedに指令を送っている行です。mbedはパソコンのシリアルポートにつながっているので、そのシリアルポートに対して送りたい文字列をwriteします。

cmd = self.get_argument('cmd', default=None)
という行で、GET要求の「cmd引数の値」を取得してきます。上では解説しませんでしたが、GET要求の「/なんちゃら」は詳しく見ると構造を持っているのです。

GET /mbed?cmd=right HTTP/1.1
などと書くと、/mbedというファイル(本当はファイルではありませんが、Webブラウザーからはファイルに見えています)をGETするときに、追加の情報をWebサーバーに送ることができます。それが引数です。この例では「?cmd=right」の部分が引数ですね。

で、その引数を取得するのがself.get_argumentです。
cmd = self.get_argument('cmd', default=None)
と書けば、「cmdという引数名」に設定された値を取ってきます。今回の例では「right」が取得されます。もし、GET要求にcmd引数がなかったり、cmd引数に値が設定されていない場合、Noneとなります。
要するに
GET /mbed?cmd=hoge HTTP/1.1
というGET要求がくれば、変数cmdには"hoge"という文字列が入ります。
WebブラウザーのURL欄に「http://localhost/mbed?cmd=hoge」と入力することでこのGET要求を発行することができます。

後は取得した引数の値を使ってmbedマイコンが解釈できる指令を作成し、mbedに送るだけです。今回作成したmbedマイコンのプログラムはJSON形式の指令を受け付けるので、
'{{"cmd":"{0}"}}'.format(cmd)
と書いてJSON形式の文字列を生成しています。.format(cmd)は{0}にcmdを埋め込む命令なので、結果として「{"cmd":"hoge"}」という文字列になります。({{が{に、}}が}になってるのは.formatの機能です。誤植ではありません)

pySerial

Webサーバーで残るはシリアルポートの説明です。

PythonでRS-232を使うにはpySerialを使います。Windows、Mac、Linuxで同じようにシリアルポートの制御プログラムを作れます。

mbedマイコンは実際にはUSBでパソコンと繋がりますが、mbed側にUSBとシリアル通信の変換チップが載っているため、プログラムからはあたかもシリアルポートで接続されているように見えます。ということで、pySerialで簡単に制御できます。

実際にpySerialを使っている部分は、Webサーバーのプログラムに組み込まれています。まずシリアルポートを開きます。
serial_port = serial.Serial(args.serial_device, 9600)
args.serial_device という名前のシリアルポートを、通信速度 9600bps で開きます。args.serial_device にはWebサーバーの起動時に指定したシリアルポート名、例えば "/dev/tty.usbmodem1422"が入ります。Windowsだと"COM3"とかになるでしょう。

シリアルポートを開いたら、後はシリアルポートに対してデータを送ったり、今回はやっていませんがシリアルポートからデータを受け取ったりできます。今回のWebサーバーのプログラムでデータを送っているのが以下の行。
s = '{{"cmd":"{0}"}}'.format(cmd)
serial_port.write(s.encode('ascii'))
先ほど開いたシリアルポート「serial_port」に対して、変数sの中身を送ります。変数sには「{"cmd":"right"}」のような文字列が格納されていますので、それをそのままmbedに送ればいい…と思いきや、pySerialのwriteメソッドは、引数に「文字列」を渡せません。文字列を「バイト列」に変換してから渡しましょう。

この辺りの話を詳しく書こうとすると本が一冊書けてしまいますので、表面的な説明のみに留めます。Python(特にバージョン3)では、文字列とバイト列を明確に区別します。文字列とは、そのままの意味で0個以上の文字の列です。文字列「cmd」は「c」と「m」と「d」の3文字の列です。
で、この文字列をシリアルポートに送るには、文字の列ではなくバイトの列に変換する必要があります。文字列に対して「文字列.encode('文字コード')」と書けば、文字列をバイト列に変換できます。ある文字をどんなバイト列に変換するか(例えば文字「c」を16進数で「0x63」に変換する)というルールが文字コードです。今回はASCII文字コードを使います。「"cmd".encode('ascii')」と書けば「0x63」「0x6d」「0x64」の3バイトの列になります。

ということで、sに「{"cmd":"right"}」が入っているときにs.encode('ascii')と書けば、「7b, 22, 63, 6d, 64, 22, 3a, 22, 72, 69, 67, 68, 74, 22, 7d」という15バイトの列になります。それをserial_portにwriteすることでmbedに送信します。

index.html

パソコンのマイクから音声を受け取り、Web Speech APIで文字列に変換し、自作WebサーバーにGET要求を発行するためのHTMLファイルです。ファイル全体を以下に示します。
<!DOCTYPE HTML>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Web Speech API Test</title>
  </head>
  <body onload="recognition.start();">
    <form>
      <input type="text" id="speech_result" value="認識結果"></input>
    </form>
    <script>
      if(typeof(String.prototype.trim) === "undefined")
      {
        String.prototype.trim = function() 
        {
          return String(this).replace(/^\s+|\s+$/g, '');
        };
      }
      var recognition = new webkitSpeechRecognition();
      recognition.continuous = true;
      recognition.lang = 'ja-JP';
      recognition.onsoundstart = function(event) {
        console.log(event);
      }
      recognition.onresult = function(event) {
        console.log(event);
        var word = event.results[event.results.length - 1][0].transcript;
        word = word.trim();
        document.getElementById('speech_result').value = word;
        var cmd = null;
        if (word === '右') {
          cmd = 'right';
          } else if (word === '左') {
          cmd = 'left';
          } else if (word === '止まれ') {
          cmd = 'off';
        }
        if (cmd != null) {
          var request = new XMLHttpRequest();
          request.open('GET', '/mbed?cmd=' + cmd);
          request.send();
          console.log(cmd)
        }
      }
    </script>
  </body>
</html>

Web Speech APIを使う準備が以下の部分です。
      var recognition = new webkitSpeechRecognition();
      recognition.continuous = true;
      recognition.lang = 'ja-JP';
      recognition.onsoundstart = function(event) {
        console.log(event);
      }
      recognition.onresult = function(event) {
        ...
      }

Web Speech APIについて詳しくはWebアプリに高機能な音声認識を追加するWeb Speech API - Kesin's diaryなどを参考にしてください。
var recognition = new webkitSpeechRecognition();
でWeb Speech APIインスタンスを作り、各種設定をします。

  • continuous = true : 連続変換モードを有効にする。マイクに向かって喋るたびに、自動で音声認識が行われます。
  • lang = 'ja-JP' : マイクに入る音声が日本語であることを示します。
  • onsoundstart = function(event) : 音声認識が始まったときに呼ばれる関数を設定します。今回はデバッグ用にJavaScriptコンソールにeventを表示しています。
  • onresult = function(event) : 音声認識が完了したときに呼ばれる関数を設定します。この関数の中身が、index.htmlファイルの中で最も重要な処理です。以下で説明していきます。

Web Speech APIで変換した結果を、デバッグ用に画面に表示する処理が以下です。Web Speech APIの変換結果はevent.resultsに書き込まれていますので、そこから希望の文字列を取得し、speech_resultテキストボックスに書き込みます。画面へ表示せずともmbedは制御できますが、Web Speech APIがどのように変換してくれたかを確認したいので表示しています。
        var word = event.results[event.results.length - 1][0].transcript;
        word = word.trim();
        document.getElementById('speech_result').value = word;
文字列を表示する場所として、inputタグを用いて1行テキストボックスを作っています。document.getElementByIdで探せるように、id="speech_result"としておきます。
    <form>
      <input type="text" id="speech_result" value="認識結果"></input>
    </form> 

自作WebサーバーにGET要求を送るのが以下です。
          var request = new XMLHttpRequest();
          request.open('GET', '/mbed?cmd=' + cmd);
          request.send();
XMLHttpRequestを使うと、同じWebサーバー内に置いてある別のページに対しGET要求を送れます。ここでは/mbedページに対し、?cmd=rightのような引数を付けたGET要求を生成し、発行します。すると、自作WebサーバーのMainHandlerが起動する、という仕組みです。

まとめ

以上で今回作ったシステムの全体を解説しました。index.htmlがユーザーとの接点となり、Web Speech APIでマイクからの音声をテキストに直し、そのテキストを元にWebサーバーへGET要求を発行し、Webサーバーがmbedにシリアル通信で指令を出します。
長文となってしまいましたが、この記事が何かのお役にたてれば幸いです。

2014年5月6日火曜日

PCで音声認識してmbedを制御する

PCのマイクで拾った音声の認識結果を用い、USB接続のmbedを制御しました。
今は詳しく書く気力がないので、作ったものを軽く紹介するだけにします。

使った技術


  • Web Speech API
    • HTML5で策定された仕様。まだGoogle Chromeでしか使えないけど、マイクからの音声を認識して、テキストとして取得できます。
  • Tornado
    •  HTTPサーバを構築するためのPython用ライブラリ。Web Speech APIを動かす基盤として、またmbedの操作窓口として使っています。
  • pySerial
    • Pythonでシリアルポートを使うためのライブラリ。シリアルポート経由でmbedと通信するために使います。
  • mbed
    • お手軽マイコン。PCからの命令を受け取ってLEDをチカチカさせます。

結果


「右」「左」「止まれ」の3つの声を認識し、mbedのLEDを制御できました。
最終的にはmbedにロボットを接続して、声で動かす予定です。

2013年10月15日火曜日

OS X Mountain Lion で i686-elf 向け GCC をビルドする

Xcode の Command Line Tools でインストールされる gcc コマンドは llvm-gcc なので、本当の GNU C Compiler ではありません。 Mac でクロスコンパイル用の GCC をビルドするためには、本物の gcc コマンドが必要です。
Mountain Lionユーザのためのクロスコンパイラビルド - Handwriting を読んでこの事実を知りました(それまで5回ぐらいクロスコンパイル用 GCC をビルドしようとして失敗していました)。

以下、ビルド手順メモ

ネイティブ用 GCC ビルド

クロスコンパイル用 GCC をビルドするための GCC をビルドします。A bare-metal x86-cross-compiler on Mountain Lion | The Intobooks や、そこからリンクされている Compiling gcc-4.7.1 on Mac OSX Lion が参考になりました。これらの記事の手順は僕の Mountain Lion でも使えました。

各種ソースコードのダウンロードと展開

$ cd ~
$ mkdir gcc-4.8.1
$ cd gcc-4.8.1
$ curl -O http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-4.8.1/gcc-4.8.1.tar.gz
$ curl -O http://ftp.tsukuba.wide.ad.jp/software/gmp/gmp-5.1.3.tar.gz
$ curl -O http://ftp.tsukuba.wide.ad.jp/software/mpfr/mpfr-3.1.2.tar.gz
$ curl -O http://ftp.tsukuba.wide.ad.jp/software/mpc/mpc-1.0.1.tar.gz
$ tar zxvf gcc-4.8.1.tar.gz
$ tar xzvf gmp-5.1.3.tar.gz 
$ tar xzvf mpfr-3.1.2.tar.gz
$ tar xzvf mpc-1.0.1.tar.gz

ビルドとインストール

ビルドするまえに環境変数 PATH の値をなるべくシンプルに調整するとミスがありません。PATHを調整し終えたらいざビルド。

GMP
$ mkdir gmp-build
$ cd gmp-build
$ ../gmp-5.1.3/configure --prefix=/Users/uchan/gcc-4.8.1/ --enable-cxx
$ make -j4
$ make install
$ cd ..

MPFR
$ mkdir mpfr-build
$ cd mpfr-build
$ ../mpfr-3.1.2/configure --prefix=/Users/uchan/gcc-4.8.1 --with-gmp=/Users/uchan/gcc-4.8.1
$ make -j4
$ make install
$ cd ..

MPC
$ mkdir mpc-build
$ cd mpc-build
$ ../mpc-1.0.1/configure --prefix=/Users/uchan/gcc-4.8.1 --with-gmp=/Users/uchan/gcc-4.8.1 --with-mpfr=/Users/uchan/gcc-4.8.1
$ make -j4
$ make install
$ cd ..

GCC
$ mkdir gcc-build
$ cd gcc-build
$ ../gcc-4.8.1/configure --prefix=/Users/uchan/gcc-4.8.1 --enable-checking=release --with-gmp=/Users/uchan/gcc-4.8.1 --with-mpfr=/Users/uchan/gcc-4.8.1 --with-mpc=/Users/uchan/gcc-4.8.1 --enable-languages=c,c++
$ make -j4
$ make install
$ cd ..

以上でクロスコンパイル用 GCC をビルドするためのネイティブ GCC が出来上がりました!(ややこしい(^^;

さて、やっとクロスコンパイル用 GCC のビルドです。まず PATH の調整など。先ほどビルドした GCC のパスと、クロスコンパイル用 GCC のパスも忘れず通します。GCC, GMP, MPFR, MPC のソースは先ほど展開したものを再利用します。
$ export PATH=$HOME/i686-elf-gcc-4.8.1/bin:$HOME/gcc-4.8.1/bin:$PATH
$ cd ~
$ mkdir i686-elf-gcc-4.8.1
$ cd i686-elf-gcc-4.8.1
$ mkdir src
$ cd src
$ ln -s ~/gcc-4.8.1/src/gcc-4.8.1 ./
$ ln -s ~/gcc-4.8.1/src/gmp-5.1.3 ./
$ ln -s ~/gcc-4.8.1/src/mpfr-3.1.2 ./
$ ln -s ~/gcc-4.8.1/src/mpc-1.0.1 ./

binutils
$ curl -O http://ftp.tsukuba.wide.ad.jp/software/binutils/releases/binutils-2.23.2.tar.bz2
$ tar xjvf binutils-2.23.2.tar.bz2
$ mkdir binutils-build
$ cd binutils-build
$ ../binutils-2.23.2/configure --prefix=/Users/uchan/i686-elf-gcc-4.8.1 --target=i686-elf
$ make -j4
$ make install
$ cd ..

GCC
$ mkdir gcc-build
$ cd gcc-build
$ ../gcc-4.8.1/configure --prefix=/Users/uchan/i686-elf-gcc-4.8.1 --target=i686-elf --enable-languages=c,c++ --without-headers
$ make -j4 all-gcc
$ make install-gcc

必要な configure オプションについては 開発環境(自作OS) - こうじのがく まとめ(自作OS、ホームページ作成) を参考にしました。
  • --without-headers : 自作 OS 開発には標準ヘッダファイルや標準ライブラリは要りませんので、これを指定しておきましょう。
make に all-gcc を指定するのが重要です。これを指定しないと make の途中でエラーが発生するかもしれません。(実際、 libstdc++ のビルドに失敗している様でした)

以上でクロスコンパイル用 GCC のインストールは完了です。やったね!

2013年10月11日金曜日

【自作OS】USBメモリからブートする

昨日あるチャットで自作OSの話題になったとき、最近のパソコンにはフロッピーが無い、USBメモリから起動できたらいいよね、という話が出ました。

気になったのでやってみました。


ブートセクタ - Wikipedia によれば、どんな起動デバイスでも BIOS は先頭 512 バイトを 0x7c00 番地に配置して実行してくれるらしい。

Hello World を表示する 512 バイトのバイナリを用意して、それを USB メモリの MBR に書き込んでしまえばいいはず。ちょうど良いことに「30日でできる! OS自作入門」の2日目3節で Hello World を表示するだけの 512 バイトのバイナリを作っていますので、これを利用することにしました。

MBR への書き込みには 【取り扱い注意】USB(メモリ)ブート関連ツール で紹介されている BOOTICE というツールを使いました。BOOTICE の画面で ProcessMBR → Restore MBR を選択し、上記の 512 バイトバイナリを選択すれば書き込めます。楽ちんだね!

壊れてもいい USB メモリを使ってくださいね!

で MBR への書き込みが完了した USB メモリをパソコンに刺して起動させれば、ほら! Hello World が出ました!

2013年9月6日金曜日

【Java】cannot be resolved to a type エラーと CLASSPATH の順序

Java の クラスパス の設定でちょっと躓いたので記録。

javac や java コマンドの実行時にクラスパスを指定するには2通りのやり方がある。 -classpath オプションを使う方法と CLASSPATH 環境変数を使う方法だ。今回検証したのは環境変数による設定のみである。

発生した問題を説明する。依存ライブラリの jar ファイルを全て CLASSPATH に含めて java コマンドを実行しても、コンパイルした bin ディレクトリ外では以下の様なエラーが発生した。

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
 Options cannot be resolved to a type
 Options cannot be resolved to a type
 CommandLineParser cannot be resolved to a type
 PosixParser cannot be resolved to a type
 CommandLine cannot be resolved to a type
 Option cannot be resolved to a type
 ParseException cannot be resolved to a type

 at com.github.uchan_nos.c_helper.Launcher.main(Launcher.java:32)

コンパイル時はエラーがなく、さらに bin ディレクトリで java コマンドを実行すれば正常に実行されるのに、 java コマンドを実行するときのカレントディレクトリが異なるとダメになる。

この時のクラスパスの設定は次。
export CLASSPATH=`python ~/gitrepos/c-helper/tools/classpath.py /Applications/eclipse/plugins`:~/java/lib/commons-cli-1.2/commons-cli-1.2.jar:~/gitrepos/c-helper/bin

`と`で囲まれた python コマンドは、依存している jar ファイルの名前をコロン区切りにするためのもの。その後ろに追加で依存する commons-cli-1.2.jar ファイルを指定した。

このようなときは、クラスパスの順序を変えれば解決するかもしれない。今回の場合は以下のように修正したら解決した。
export CLASSPATH=~/java/lib/commons-cli-1.2/commons-cli-1.2.jar:~/gitrepos/c-helper/bin:`python ~/gitrepos/c-helper/tools/classpath.py /Applications/eclipse/plugins`

私自身クラスパスの仕組みをはっきりと把握できていないため、今回の対処法が全ての場合で有効かは分からないが、今回の場合はこれが特効薬だった。

2013年8月16日金曜日

OS X Mountain Lion + Python 3 + Beautiful Soup 4

OS X Mountain Lion 上で動く Python 3 に Beautiful Soup 4 をインストールしようとしたら嵌ったのでメモ。

Python 3.3.2 (HomeBrew でインストールしたもの)
Beautiful Soup 4.3.0

上手く行ったインストール方法


$ sudo pip3 install beautifulsoup4
$ cd /usr/local/lib/python3.3/site-packages/
$ sudo 2to3-3.3 -w bs4

問題

pip3 でインストールするだけだと Beautiful Soup をインポートするときに SyntaxError 例外が発生する。

uchan@uchan-mba:site-packages$ python3
Python 3.3.2 (default, Jul 11 2013, 15:15:27) 
[GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from bs4 import BeautifulSoup4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./bs4/__init__.py", line 392
    print soup.prettify()
             ^
SyntaxError: invalid syntax

2to3-3.3 コマンドを手動で実行することで、ライブラリのソースコードを Python 3.3 向けに修正することができる。(インストーラが自動実行しないのはライブラリのバグかもしれない)

2013年7月29日月曜日

Qt Creator で Qt Quick アプリケーションを作ると QML ファイルが読み込めない

OS X Mountain Lion + Qt 5.1.0 + Qt Creator 2.8.0 という環境で開発しています。

Qt Creator で ファイル > ファイル/プロジェクトの新規作成 > Qt Quick 2 Application (Built-in Types) からプロジェクトを作ると main.qml, main.cpp, qtquick2applicationviewer.cpp が含まれたプロジェクトが生成されます。各ファイルに関する説明は QtQuick での C++ × QML バインディングについてまとめてみた を読むといいです。僕もこれを参考に勉強しています。

しかし、初期状態で作成したプロジェクトを実行しようとしても main.qml ファイルの読込エラーが発生してしまいます。原因を調査したところ、どうやらビルド成果物のパスに日本語が含まれると QML ファイルのコピーに失敗してしまうようです。

プロジェクトを作るときのウィザードで日本語を含むパスを指定する(画像参照)と QML ファイルのコピーに失敗しますので、設定を変えて(「デスクトップ」を「Desktop」にでも変更しましょう)再度プロジェクトを生成すると上手く行きます。エラーに悩まされている人は参考にしてください。

成果物へのパスに日本語が含まれている 
「デスクトップ」を「Desktop」に変えた
プロジェクトを再度作成したら英語になった!

ちなみに、元々コピー対象ではないリソースなどをバンドル(hoge.appのこと)に含めるには How to add resource files to an OS X application bundle という方法があるようです。この方法を使っても main.qml が見つからないエラーを解決できます。ただ、この方法は上手くやらないとプロジェクトファイルが OS X 専用になってしまうと思います。なるべく先述の設定変更で対処するのがいいでしょう。

2013年7月23日火曜日

Qt 5.1 + QML + qmlscene

QML でプログラミングを始めよう を参考に QML の勉強を始めました。
ボタンの表示からテキストエディタの作成、C++との連携など一連の内容が書いてあって参考になりそうな感じです。

Qt 5から QML ファイルのプレビュー方法が変わったらしく、ちょっとはまったのでメモしておきます。

上記サイトでは QMLViewer を用いてプレビューを行なっていますが、どうやら Qt 5 からは qmlscene コマンドを使うようです。BeagleBoard 向け Qt 5

公式の First Steps with QML | QtDoc 5.1 を見たら qmlscene を使うんだよとちゃんと書いてありました。お恥ずかしい (^^;

ということで、以下の様な QML ファイルを作って qmlscene しましょう。

-- win.qml --
import QtQuick 2.0

Rectangle {
  id: simplebutton
  color: "gray"
  width: 150
  height: 75

  Text {
    id: buttonlabel
    anchors.centerIn: parent
    text: "hello!!"
  }
}

$ qmlscene win.qml

2013年7月22日月曜日

Qt 5.1 on Mac OS X Mountain Lion

GUIアプリを作ることになりまして、久しぶりに Qt を使おうかなと思ったのでした。
調べたら Qt 5.1 がリリースされていましたので Mountain Lion への入れ方などメモします。

インストール

既に Homebrew で Qt 4.8.4 を入れていたので干渉を避けるため、ソースからビルドすることにしました。
Qt Project から qt-everywhere-opensource-src-5.1.0.tar.gz をダウンロードしビルドします。
ビルド方法は Installation | QtDoc 5.1 が参考になります。

$ tar xzvf qt-everywhere-opensource-src-5.1.0.tar.gz
$ cd qt-everywhere-opensource-src-5.1.0
$ ./configure
$ make
$ sudo make -j1 install

途中でライセンスの選択を聞かれたので LPGL 版を選択しました。最後に PATH の調整をして終わりです。~/.bashrc に次を書き加えればいいでしょう。

export PATH=/usr/local/Qt-5.1.0/bin:$PATH

最後に希望のバージョンが入っていることを確認します。

$ qmake -v

QMake version 3.0
Using Qt version 5.1.0 in /usr/local/Qt-5.1.0/lib

Hello, World

Qt で画像をフルスクリーン表示する を参考にテストプログラムを作りました。

-- main.cpp --
#include <QApplication>
#include <QPixmap>
#include <QLabel>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    QPixmap pixmap("/path/to/picture.jpg");
    QLabel screen;
    screen.setPixmap(pixmap);
    screen.show();

    return app.exec();
}
-- main.cpp --

ソースコードを準備したらプロジェクトファイルを生成します。そのプロジェクトのビルドに必要なライブラリや追加のインクルードパス、使用する Qt のモジュールなどを記述するためのファイルです。

$ qmake -project

Qt 5.1 から、どうやら GUI アプリを作る(QApplication クラスなどを使う)にはプロジェクトファイルの修正が必要なようです。生成されたプロジェクトファイルに次の1行を書き加えます。
この修正をしないとヘッダファイルが見つからないと言われたり、なんとか解決しても Mac: Undefined symbols for architecture x86_64: “QApplication::palette()”, referenced from: という感じのエラーが出てリンクできません。

QT += widgets

そうしたら Makefile を生成してビルドします。

$ qmake
$ make

アプリの起動は .app ファイルを起動しましょう。

$ open hoge.app

指定した画像が表示されたら成功です。

2013年6月19日水曜日

The project description file (.project) for ‘c-helper’ is missing

古い Mac から workspace をコピーしてきて、それを新しい Mac にインストールした Eclipse のワークスペースとして指定しました。
そうしたら、あるプロジェクトで "The project description file (.project) for ‘c-helper’ is missing" というエラーが出てしまいました。

同じようなエラーで悩んでいる人は結構居るみたいでした。
The project description file (.project) for my project is missing
Relocating Eclipse Projects: The project description file (.project) for XXX is missing
The project description file (.project) for ‘MyFlexProject’ is missing

プロジェクトをインポートし直すと治るよ(1つめの記事)とか、 .location ファイルを削除したら治るよ(2つめの記事)、という情報がありましたが、僕のケースではどちらもダメでした。

で、3つめの記事に書いてあった情報が役立ちました。
この記事は Flex Builder 3 についての記事ですが、 Eclipse にも当てはまるでしょう。

古いパソコンからワークスペースをコピーしてきて、 Eclipse がそのワークスペースを指すようにするのは間違った方法で、正しくはコピーしてきたワークスペースとは別のワークスペースを作って、プロジェクトをインポートするという方法だそうです。

とうことで、ワークスペースを新しく作って問題のプロジェクトをインポートしたら解決しました!

Xcode で gitk がインストールされない OS X Mountain Lion

新しい MacBook Air (2013 mid version)を買ったので、早速 Xcode 4.6.3 をインストールし、 Preferences > Download から Command Line Tools をインストールした。

しかし、どうやら gitk がインストールされていない。 git コマンドはインストールされたが、もしかしたら最新版の Xcode には gitk が含まれないのかもしれない。

ということで GitX http://gitx.frim.nl なるアプリをダウンロードしてみた。
最初に起動するとフォルダ選択画面になる。そこで .git が置いてあるフォルダ(または .git フォルダそのもの)を選択すれば、きれいなコミットグラフが表示される。

ターミナルから gitk と同じように呼び出して使いたいときは、 GitX を起動した状態で GitX > Enable Terminal Usage... を選択しよう。 /usr/bin/gitx がインストールされる。

2013年2月18日月曜日

【Eclipseプラグイン開発】 配布時に他プラグインへの依存をなくす

Eclipseプラグインの開発時と配布時の、他プラグインへの依存関係を変えたいことがあります。
例えば、開発時にJUnitを使ってテストをするような場合。
配布先ではテストをしないでしょうから、末端ユーザーの環境にJUnitプラグインがなくても大丈夫なようにしたい。
このように、開発時と配布時の依存関係を変えたいという要望があるでしょう。

まず基礎知識として、開発しているEclipseプラグインの依存設定はplugin.xmlで設定します。
マニフェストエディタでplugin.xmlを開けばDependenciesタブがあります(下図)。


Plug-in Dependencies - Help Eclipse Platform
を読むと、依存設定の"Optional"オプションを有効化すると、その依存プラグインが配布先環境に存在してもしなくても、配布したプラグインが実行できるそうです。

ということで、実際に試してみたところ、JUnitプラグインが存在しない環境でもC-Helperがインストール&実行できるようになりました。

2012年11月26日月曜日

【Git】タグを付け替える

Gitでのタグの付け方を勉強してました。
あるコミットに対してタグを付けたけど、やっぱり付け替えるやり方を見つけたのでメモ。

appling // gitタグの付け替えから引用します。
まずは既存のタグを削除
$ git tag -d <TAG>
$ git push origin :refs/tags/<TAG>
新たにタグを作成して公開
$ git tag <TAG>
$ git push --tags origin

この中で git push origin :refs/tags/<TAG> が良く分からなかったので、git help pushで調べてみました。
まずpushの構文は
 git push <repository> <refspec>
でした(かなり簡略化してあります)。

<refspec>には <src>:<dst> を指定できます。
<src>を空にした場合、<dst>を削除するという意味になるようです。
:<dst>を省略すると、<src>と同じrefが更新されるようです。

したがって、 git push origin :refs/tags/<TAG> はリモートの refs/tags/<TAG> を削除する命令ということですね。

そして、 push 時に --tags を指定すると、明示的に指定したrefに加え、 refs/tags 以下にあるすべての ref がアップロードされるようです。
したがって、 git push --tags origin は、<repository>=origin に、ローカルにあるすべてのタグをアップロードする命令ですね。