あーこれでようやくPerlとJavaScriptとDSLiteの世界にいけるな。

PhidgetsのRFIDキットをGroupLab.Phidgets.NET Libraryで動かしてみた。本家のPhidgets.comの方にも、PhidgetWebServiceが必要な.NET用ライブラリがあるが、GroupLabのは単独で動く。

あとDelegateとかInvokeとか、VisualStudio2005にしてから制限が厳しくなったThread関連についても下の方に書いた。

単純なRFIDリーダのステータス&読み込んだタグのIDを表示するソフトをVisualStudio2005+C#2.0の環境で作った。→ SourceCode & exe(実行には.NET Framework2.0が必要)

使ったクラスはGroupLab.Phidgets.RFIDのみ。

PhidgetRFIDTest

これがPhidgetRFIDキット

PhidgetRFIDキット

特徴は

・125KHz帯(長波帯)

・USBバスパワーのみで動作

・5Vの出力とLED出力のプログラマブルなポートが付いている

・タグを同時に1枚しか検知できない(2枚以上重ねても落ちることはない)

・常に連続読み取りしかできない

・タグが来た事はイベントで通知してくれるが、無くなった事はイベント通知してくれない

・プラグアンドプレイに対応していて、抜き差しをイベントで通知してくれる

・アンテナと基盤が一体化している

 (基盤のアンテナ中央にスルホールがあるので、自分でアンテナ作って接続出来るかも知れない)

複数同時読み取りできないのに気を付ければ、かなり色んな事に使える。



■ソフト作ってみてわかったこと

・ステータスビットがある

public void rfid_init(){

rfid.Outputs[0].State = false; // 5V output

rfid.Outputs[1].State = false; // external LED

rfid.Outputs[2].State = true; // on board LED

rfid.Outputs[3].State = true; // active Reader

}

こんな処理で、基盤上のLEDを光らせたり消したりできる。特にOutputs[3]は重要で、falseだとリーダそのもが動かない。デフォルトでfalseなので気付くまで時間がかかった。

・Attachイベントの後じゃないとnew された事になってない

上記の rfid_init() 関数だが、普通にFormのコンストラクタのInitializeComponent();の後に書いてもインスタンスが生成されていないというエラーがでる。見るとちゃんとrfidはnewされてるんだが、USBデバイスなので色々あるんだろう。

RFIDクラスにAttachイベントがあるので、その中でfid_init()を呼び出せばok

■Thread越しのUI操作について

上に書いた情報を頭に入れてGroupLabのライブラリの用意してるメソッドの通り書けば、10分ぐらいでRFIDリーダを使ったアプリが作れるだろう。

ただ、WindowsアプリケーションとしてVisualStudioを使う時、Thread関係で詰まった。

VisualStudio2005からはご丁寧にも、Formの動いているスレッド以外からFormの要素に書き込もうとすると

「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール ~~ がアクセスされました」とかエラーが出る。

これのお陰で今まではスレッドなんて全然気にしないでシリアルポートとか色々使っていたのに、全部動かなくなった。Timerとかで無理矢理誤魔化したりしてたが、さっきめでたく解決できたのでまとめておく。

・BackgroundWorker

メインスレッド(Form)から呼び出し、新しいスレッドを作りそこで重たい処理をさせる。進捗状況などもイベントを指定しておけばわかる。

スレッドを簡単に使うための物みたいだが、俺のやりたいのはサブからイベント通知→メインスレッドの要素を変更なので駄目。

参考:

Visual Studio 2005 による応答性の高いアプリケーション構築のためのスレッド使用

C# Note Control – C#のコントロール説明:backgroundWorker

・Invoke, Delegate

これが正解だった。

ようするに「スレッドのデッドロック」という奴が起きるのを防ぐ為に、同期してない2つのスレッドの間にDelegateという「処理を委譲するオブジェクト」を作って、デッドロックが起きないようにタイミングをみて書き込んでもらうみたい。

別スレッドで動いているPhidgetsRFIDの起こしたイベントから、メインスレッドで動いているtextBoxLogのTextプロパティに書き込む時の方法

まずこれを準備しておいて

/* デリゲートを用意 引数の型を合わせて */

delegate void StringDataDelegate(string msg);

/* ConsoleとForm要素両方にメッセージ出力 */

public void message_writeLine(string str){

Console.WriteLine(str);

StringDataDelegate mes_delegate = MessageWrite; // 関数名を指定してDelegate作成

try{

this.Invoke(mes_delegate, str); // Formと同じスレッドで実行

}

catch{ // 例外処理

Console.WriteLine(“thread error”);

}

}

/* delegate越しに呼ばれる関数 */

private void MessageWrite(string str){

textBoxLog.Text = str+”\r\n”+textBoxLog.Text;

}

Phidgetのイベント内に

message_writeLine(“読み込んだタグはhogehogeです”);

とか書くと良い。

参考:

++C++; //未確認飛行C++ デリゲート

連載:C#入門 第17回 処理を委譲するdelegate

delegateは引数をargs[]みたいにするとか、色々綺麗に書くのに工夫が要りそう。

今回俺が書いたプログラムではstring要素一つを委譲するStringDataDelegateを、Formのタイトルを書き直すのとかにも使い回してみたりした。

けどそうするとC#でサポートされてるアクセサ(関数じゃない奴)から呼び出したりとかするし、アクセサの側にprivateのメンバ変数書いておきたいから必然的にコードの可読性が落ちる。プラクティスが必要だな。