Analog系(AMP含)
Analogコンテンツまとめトップ
個人メモ,オーディオネタのwiki contentsの親用.随時更新予定...
個人メモ,オーディオネタのwiki contentsの親用.随時更新予定...
ググレカス→delegate C#
注意:Delegateクラスもある.delegate型とは別ぽい.
しかし,"デリゲートの使用 (C# プログラミング ガイド)"では,以下の記述がある.
デリゲート型は System.Delegate から派生するため、 このクラスで定義されているメソッドとプロパティをデリゲートで呼び出すことができます。
強制クローズを禁止する.(システムメニューの削除,ALT+F4の終了排除.)
→参照 http://www.atmarkit.co.jp/fdotnet/dotnettips/142closebtn/closebtn.html
宇宙仮面のC#プログラミング-非同期プログラミングと, 例のサンプルから推測交じりで消化.delegate,Thread class等の比較による紹介.
リード処理をdlegate宣言したクラス(変数?)にセットする.読み出し要求が発生したときに,BeginInvokeする.このとき,非同期callbackを行うため,AsyncCallbackオブジェクトも渡す.処理終了時に,AsyncCallbackオブジェクトを作るときのmethodが呼ばれる.引数として,(IAsyncResult)arを受け取る.キャストして,自分の定義したdeletgate型へ修正する.
ReadInputReportDelegate deleg = ( ( ReadInputReportDelegate )( ar.AsyncState ) );
ar.EndInvoke()でスレッド終了.この引数には,delegate宣言したmethodのうち,参照型'ref'をつけたものが順に渡せる.+ar自身.
deleg.EndInvoke( ref myDeviceDetected, ref inputReportBuffer, ref success, ar );
System.Object System.Delegate System.MulticastDelegate
この型のすべてのパブリック staticメンバは、スレッド セーフです。
class methodをThreadStartクラスでインスタンス化したとき,そのクラスから半ば独立したthreadで起動する模様. 親windowが死んでも走り続けていたので,なぜか不思議であった.上記終了処理で子スレッド終了を待たせてみたが,スマートではないと思えたので検索↓.
k.kinukawa(momo_dev)の日記より,デフォルトはフォアグランドスレッド?として起動していた模様.
ハンドル類はcloseされることなく,そのスレッドが従属するクラスのデストラクタも呼び出されなかったため,裏でHID Read fileを無限に繰り返す状態になっていたようだ.
(Rxthread).IsBackground = true;
とセットしてから,
(RxThread).Start();
とすることで,バックグランドで走るようになった.親スレッド(Form)をcloseした際に,デストラクタが呼び出された.Threadは放置すると良くないと思ったので,thread.abort()で殺した.
この処理を実装しなくても,Thread objectのデストラクタかどこかで閉じられた模様.ファイルハンドルがどうなったのか….safefilehandle objectで触っていたから,これもデストラクタが綺麗にしてくれたのだろうか.不安が募るが,何という適当な処理言語だろう….runtimeでデバッグコードが走るようなものか?安全といえば安全だが.とりあえず明示的に殺すことにする.
C++と同じ.クラス名の先頭にチルダ'~'をつける.引数はナシ(空白).objectが破棄されるときに呼ばれるハズ.Formのクラスですが,ALT+F4では呼ばれなかった@デバッガ.breakがうまく効かなかっただけなのかなぁ...
class Foo { ... ~Foo() { }
C# .Net SendMessageで他のアプリに文言を送信したいを参照.
キーワード:C# Alloc message lparam wparamで,探索.
主にC#ベースで作成します.速度を求めるならnativeコードで実装すべきでしょう.とりあえずC#で作って,速度を稼ぎたいところでunsafe codeを書いていきます.
その時点でWindows APIの使用となり,Import宣言等を自前で書く必要が出てきます.
それで我慢できなくなったら,VC++等への移行も検討しましょう.
デバイス設定:断りの無い限り,下記のとおりとする.
item | value | remark |
---|---|---|
Poling cycle | 10 mSec | Interface Descriptorで指定 |
EndPointBuffer size | 64 bytes | Interrupt transfer |
Report Descriptorで定義したサイズは固定長である.
Report Descriptorでは,Variable....という記載があったので,デバイス→ホスト間の転送サイズは任意であると考えていた.実際に動かして見ると調子が悪かったので,HIDは 原則固定長であると認識した.
実際にDevice/Host間で送受信データサイズを変えて確認した.ただし,いずれもデバイスのReport Descriptorでは 8bit 64count(64octet)とし,Interface Descriptorでは,EP sizeを64octetとしている.
Transfer direction | from | to | Remark |
---|---|---|---|
IN transaction | DEV: 64byte送信 | HOST: 64Bytes受信 | OK |
OUT transaction | HOST: 64byte送信 | DEV:64byte受信 | OK |
IN transaction | DEV: 1byte送信 | HOST: 64Bytes受信 | NG |
IN transaction | DEV: 1byte送信 | HOST: 1Bytes受信 | NG |
IN transaction | DEV: 64byte送信 | HOST: 1Bytes受信 | NG |
Full Speed時は64byte(max)のユーザデータを送受信できるが,固定長となる.UARTの代替として使用する場合,もしくは64octetを超えるパケット通信の土管として使用する場合には,さらに一段ラッパーをかます必要がある.Low Speedもサポートするのであれば,パケットサイズが8octetになる.
struct { Byte SizeOfData ; BYte Data[63] ; // Low=63, High/Full=63 }
PC側は,Report Descriptorを受け取って,capabilityとしてサイズを認識するので,データサイズは固定としない.デバイス側で,最大長となるように設定する.デバイスのメモリの都合・データ流量の都合により,削減する場合は,descriptor・interrupt transferの転送サイズをReport通りに修正するだけでよい.
Interrupt転送は,デバイス-ホスト間でのデータ取りこぼしが起こりえないという話をどこかで見かけた.本当にそうなのかを検証してみた.*1
Poling Timing(Report) | PO cycle実測 | 結果 | Remark |
---|---|---|---|
10mSec | 8mSec | 取りこぼしなし | 周期が早いのは..? |
1mSec | 2mSec | かなりこぼす | 複合デバイスにしたので,最速では回らないか? スレッドで0秒寝かせているのが悪いのかもしれない.回りっぱなしでもトライする?. |
2mSec | 2mSec | かなりこぼします | 10/Sep.の追試実験用.条件よければOK. |
※試験結果を随時追加していく.★疑問:GetInputReportBufferSize は なに?EPサイズでもreportサイズでもない.. reportの記憶回数でもない..
同時期に,C++ Builderでホストプログラムを作成されている方より,取りこぼしはおきない旨,伺いました.Visual C#が癌なのかという疑いが出てきます.
前回の測定時のフローを示す.
1. 受信を Threadクラスを使ってぶん回す.ひたすらRead()を呼び出し続ける.
2. 受信したデータは,下記のクラスを用いてデータを積み上げる.
System.Collection.Generic.Queue<Byte>
このとき,スレッド内では,追加時にlock(Que)する.
3. Formスレッドで,上記クラスからenqueしてデータを取り出し,表示用のListControlに追加する.
このとき,lockなし,連続して吸い出している.
Formスレッドで,Queから読み出すたびに,Thread.sleep(0)を入れた.体感もっさりした気がする*3.取りこぼしは消えない.
MSVSのmanualを見ると,動的に記憶領域を延ばすと書かれていた.追加時のオーバヘッドでもこぼしている可能性があるのだろう.
また,ロックの仕方に問題がありそう.本質的にスレッドセーフではないとあるので,記載どおりのlockを行う.
・・・ために,generic Queueではなく,Collection.Queueクラスを用いる.
ヘルプに記載のとおり,下記のlock()を適用する.
lock(myCollection.SyncRoot)
System.Collection.Generic.Queue<>ではなく,System.Collection.Queueを使ってみた.読み書き両方でlock()を使ったが,やはり駄目だ.
Byte[]の動的確保にも負荷がかかっているのだろう.開放処理もGCが走るだろうし.
表示させずにデータ取得させてから,表示してみる.
取りこぼしナシ.CPU負荷がそれなりにかかる模様.*4
アプリケーション走行速度・データの扱い方による問題と思われる.
また,受信バッファは512byteまで指定可能のようだ.でかい値を放り込んでも拒絶される(32byte:report値に戻る?).
ただし,これによる救済措置が図れるのかは疑問である・・・.
実は,チェキ1が終わった段階で,USB Snoopy Proを使用してPC側にデータが来ているかどうかを確認していた.
案の定,データが来ていることは見て取れたので,取りこぼしているのはドライバ~アプリのアタリであるとアタリがついていたのです.
そもそもHIDと言っている以上,ヒトの応答速度程度の情報を送受信するのが目的であるし,仕様なのかもしれない.
また,Report Descriptorを見ても判るように,固定長・ビット単位で意味を持たせたデータを送るのが目的であり,今回のようにストリームデータを送るのは想定外使用なのだろう.もし,確実に双方向のデータ通信を行いたいのであれば,ホスト~デバイス間でハンドシェイクを行う必要があるでしょう.
そこまでするならBulk転送で~とも思うのだけれど,それはそれでホスト側のドライバが面倒.汎用USBドライバやWinUSBといったものがあるようなので,それらの使用も視野に入れたい.
ひとまずHIDポート制御部分を切り出してコンポーネント化しておきたいところですね.
★本気時は書きかけのものです.頓挫する前に公開★
氏のsample"generichid_cs"から,必要なコードを流用する.C#では,Win32APIの呼び出しを直接行うことはできない.unsafe codeとして,DLLからAPIをimport宣言してやる必要がある.VBでWin32APIを用いるために宣言していたのと同様である.*1
console applicationでもよさそうだが,今後のことを考えてGUIで作っていく.Formに配置するリソースを列挙する.
下記のファイルをコピーする.ただし,VBのnamespaceも使ってるようなので,少し綺麗にしたいと思た.ちうことで,VBのlength/CRLFだけを使っていたので,これを排除しただけなんですがね.
Include all functions of HostTest_01.
WM_DEVICECHANGEメッセージが通知される模様.標準でフックできないようなので,WindowProcedureをoverrideしてsniffする.
登録: RegisterForDeviceNotifications() 停止:StopReceivingDeviceNotifications()
WM_DEVICECHANGEメッセージを受けて,pathnameとマッチするかチェックする.マッチする場合はtrueを返す.
Uses SetupDi API functions to retrieve the device path name of an attached device that belongs to an interface class.
SetupDi APIを使って,interface classに属する接続されたdevice path nameを検索する.
★MSDNより,C#で Num Text boxの作成 - 派生させてHex Edit boxでも作れ..~ 入力: hexのみ, 出力:':'区切りで表示, nibbleでfocus失うなら捨てる. 入力中は文字色変えるとか.
http://msdn.microsoft.com/ja-jp/library/ms229644(VS.80).aspx http://msdn.microsoft.com/en-us/library/ms790920.aspx http://msdn.microsoft.com/ja-jp/library/cc429201.aspx