- ベストアンサー
ReadFile(GPSとの通信)Win7での挙動
- GPS(Garmmin GPSmap 60Cx)との通信において、Windows 7では問題が発生しています。ReadFile関数を実行してデータを取得する際に、最初の3つのウエイポイントしか取得できず、残りの6つは取得できません。しかし、Sleep(1)の処理を追加すると、全てのデータを正常に取得することができます。
- 通信方法を非同期で行うために、CreateFile関数でFILE_FLAG_OVERLAPPEDを指定し、DeviceIoControlとReadFile関数にOverlapped構造体を指定した場合でも、問題が解決せず、Sleep(1)が必要となります。
- Windows 7のマシンスペックがWinXPと比べて高速なため、ReadFile関数が完了して次の処理を行う前に一定の待機時間が必要となります。Win7のPCはCore i5-2500 3.3GHz、WinXPのPCはPentium4 3GHz程度のスペックです。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
それは、たんに、XP のマシンより、7 のマシンの方が「速い」というだけのことかもしれません。 シリアルポートの読み込みで、「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になります。 なので、基本は、 ・読み込まれるべきデータが全部そろうまで読み込みを行い、 ・途中でデータがなくなる可能性に対応するために、タイムアウトの処理を行う ということになります。 実際には、USB を使っていたとしても、GPS側の処理スピードで、各データブロックの間には、そこそこの時間が空くことはよくあります。 処理時間が間に合うのなら、 ・データが全部そろった → 終了 ・読み込みバッファが空 → Sleep(1) で再試行 ・Sleep(1) で再試行が、何回も続く → abort というのも、処理方法としては、間違ってはいないと思います。
その他の回答 (2)
- hidebun
- ベストアンサー率50% (92/181)
> theDevDetailData->DevicePath このパスは間違えていないとは思いますが、正しいものが入っていますか? > SetupComm()、GetCommTimeouts 、SetCommTimeouts のいずれの関数も失敗してしまい(GetLastErrorの戻り値:ファンクションが間違っています。) これはそれぞれの関数の戻り値が0なのでしょうか? その上での「GetLastError戻り値:ファクションが間違っています」なのでしょうか? GerminのGPSを使ったプログラムサンプルなどは、検索したら色々見つかりそうなものですが、 RS232Cのプログラミングは地味に難しいので、1つずつ調べていくしかないです。
- hidebun
- ベストアンサー率50% (92/181)
初期設定はちゃんと行っておられますか? 以下、MSDNより抜粋 >通信デバイスからデータを読み取っているとき、ReadFile 関数の動作は、現在の通信タイムアウト値の影響を受けます。SetCommTimeouts と GetCommTimeouts の各関数は、この通信タイムアウト値の設定と取得を行います。タイムアウト値の設定を怠ると、予測できない結果が生じることがあります。 初期設定として ・ReadFileのタイムアウト値はどの程度になっているのでしょう。 ・SetupComm()関数で送受信バッファーの容量を設定します。十分な値が設定されてますか?ご確認下さい。 ここからは個人的な感想なんですが、Sleep(1)で動くなら、それで良いと思います。 Sleepがないと、for(;;)ではCPUコア1つを100%使ってしまうことになるので。 GPSの通信を受信する程度では、ビジーループにする必要はないでしょう。
補足
ご回答ありがとうございます。 送受信バッファーの容量設定、タイムアウト値の設定は、しておりませんでした。(ちなみに、元にしたGarmin Device Interface SDK[https://www8.garmin.com/support/commProtocol.html]にない処理でした。) 設定したところ、SetupComm()、GetCommTimeouts 、SetCommTimeouts のいずれの関数も失敗してしまい(GetLastErrorの戻り値:ファンクションが間違っています。)、つたない知識のため、原因がわかりません。 また、ビジーループのお話から、ReadFile繰り返す処理を再帰処理に書き換えてみましたが、現象は、変わりませんでした。しかし、再帰処理の関数内の最後でSleep(1)をすると正常に動作しました。 以下は、バッファー容量、タイムアウト値を設定したソースの抜粋です。 bool GarComm::GComm::Initialize() { // Make all the necessary Windows calls to get a handle to our USB device DWORD theBytesReturned = 0; PSP_INTERFACE_DEVICE_DETAIL_DATA theDevDetailData = 0; SP_DEVINFO_DATA theDevInfoData = { sizeof( SP_DEVINFO_DATA ) }; //-------------------------------- //デバイス情報セットを取得する HDEVINFO theDevInfo = SetupDiGetClassDevs( (GUID*) &GUID_DEVINTERFACE_GRMNUSB, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE ); //----------------------------------------------------- //GarmminGPSのデバイスインターフェース情報を取得する。 SP_DEVICE_INTERFACE_DATA theInterfaceData; theInterfaceData.cbSize = sizeof( theInterfaceData ); if( !SetupDiEnumDeviceInterfaces( theDevInfo, NULL, (GUID*) &GUID_DEVINTERFACE_GRMNUSB, 0, &theInterfaceData ) && GetLastError() == ERROR_NO_MORE_ITEMS ) { gHandle = 0; return false; } //----------------------------------------------------- //デイバイスインターフェスの詳細情報のサイズを取得する。 SetupDiGetDeviceInterfaceDetail( theDevInfo, &theInterfaceData, NULL, 0, &theBytesReturned, NULL ); //----------------------------------------------------- //デイバイスインターフェスの詳細情報を取得する。 theDevDetailData = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc( theBytesReturned ); theDevDetailData->cbSize = sizeof( SP_INTERFACE_DEVICE_DETAIL_DATA ); SetupDiGetDeviceInterfaceDetail( theDevInfo, &theInterfaceData, theDevDetailData, theBytesReturned, NULL, &theDevInfoData ); gHandle = CreateFile( theDevDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); free( theDevDetailData ); //送受信バッファーの容量を設定 int a = SetupComm(gHandle,MAX_BUFFER_SIZE+256,MAX_BUFFER_SIZE+256); DWORD dwGle3 = GetLastError(); //タイムアウトの設定 COMMTIMEOUTS ctOutSet; ctOutSet.ReadIntervalTimeout =10; ctOutSet.ReadTotalTimeoutMultiplier=10; ctOutSet.ReadTotalTimeoutConstant=100; ctOutSet.WriteTotalTimeoutMultiplier=10; ctOutSet.WriteTotalTimeoutConstant=100; int b =SetCommTimeouts(gHandle,&ctOutSet); DWORD dwGle = GetLastError(); COMMTIMEOUTS ctOut; int c =GetCommTimeouts(gHandle,&ctOut); DWORD dwGle2 = GetLastError(); // Get the USB packet size, which we need for sending packets //パケット送付のために必要なUSBパケット・サイズを得ます。 DeviceIoControl( gHandle, IOCTL_USB_PACKET_SIZE, 0, 0, &gUSBPacketSize, sizeof( gUSBPacketSize ), &theBytesReturned, NULL ); //------------------------------------------------------------------ // Tell the device that we are starting a session. //セッションを始めようとしていると装置に伝える。 //------------------------------------------------------------------ Packet_t theStartSessionPacket = { 0, 0, 0, Pid_Start_Session, 0 , 0 }; Packet_t* thePacket = 0; SendPacket( theStartSessionPacket ); // Wait until the device is ready to the start the session // デバイスが開始セッションの準備が整うまで待つ for( ; ; ) { thePacket = GetPacket(); if( thePacket->mPacketType == 0 && thePacket->mPacketId == 6 ) { break; } free( thePacket ); } free( thePacket ); return true; }
お礼
ご回答ありがとうございます。 >「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になりま>す。 確かに、バッファの中身がなしになっていました。7のマシンが「速い」ためのようです。 そもそも、returnbyteが0で終了条件としていましたが、送信データに終了IDが送信されてくることがわかりました。 終了IDまでデータを読み続け、returnbyte0は、読み飛ばすことでデータをすべて取得することができました。 どうもありがとうございました。