シリアルポート(COMポート)の操作方法
その3 非同期通信の巻
UINT SimReadThread(
LPVOID pParam // [IN] 未使用
)
{
BOOL Bool_Ret; // 関数戻り値
ULONG Read_Size; // 読み込みバイト数
static char ReadBuff[10]; // 読み込みバッファ
OVERLAPPED osReader = {0}; // 非同期読み込み用構造体
BOOL fWaitingOnRead = FALSE; // 非同期中メッセージ待ち状態
BOOL Loop_End = FALSE; // 処理終了状態
DWORD dwRes; // 非同期読み込み完了待ち関数戻り値
//-----------------------------------------------------------------------------
/// 端末からの電文受信処理
//-----------------------------------------------------------------------------
// 電文受信待ちループ
while( !Loop_End )
{
// 非同期処理の割り込み用イベントの生成
// ?よく分からないがこのまま記述すること
osReader.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
// イベント生成失敗
if( osReader.hEvent == NULL )
{
// エラーメッセージ表示
SimErrMsg( "%s[%d] 受信用イベント生成失敗(%d)",
0, __file__, __line__, GetLastError() );
// ループ終了フラグON
Loop_End = TRUE;
break;
}
// 読み込みバッファ初期化
memset( ReadBuff,NULL,sizeof(ReadBuff) );
// 1バイトずつ読み込み
// ?ここでは1バイト読み込めたかどうかに関わらず、ReadFile()関数はすぐに
// ?リターンします。
Bool_Ret = ReadFile( E_ComPort, ReadBuff, 1, NULL, &osReader );
// 読み込み結果チェック
// ?1バイト読み込めればTRUEが、読み込み完了待ちまたはエラーの時は
// ?FALSEが返ります。
if( Bool_Ret == FALSE )
{
// 読み込みエラー
// ?読み込み完了待ちの時はGetLastError()関数の戻りにERROR_IO_PENDINGが
// ?返されます。それ以外の場合は本当にエラーです。
if( GetLastError() != ERROR_IO_PENDING )
{
// エラーメッセージ表示
SimErrMsg( "%s[%d] 受信失敗(%d)",
0, __file__, __line__, GetLastError() );
// ループ終了フラグON
Loop_End = TRUE;
break;
}
else
{
// 非同期中メッセージ待ち状態をONにする
fWaitingOnRead = TRUE;
}
}
else
{
// 読み込みOK
// バッファリング処理
Bool_Ret = SimReadSuccess( (char)ReadBuff[0] );
// バッファリング処理失敗
if( Bool_Ret != TRUE )
{
// エラーメッセージ表示
SimErrMsg( "%s[%d] バッファリング処理失敗(%d)",
0, __file__, __line__, GetLastError() );
}
}
// 非同期読み込み完了までループ
while( fWaitingOnRead )
{
// ReadFile関数の非同期読み込み完了待ち
// タイムアウトは500ミリ秒
// ?ここでようやく、非同期モードの本領発揮です。WaitForSingleObject()関数は
// ?読み込み完了、読み込みエラー発生、タイムアウトのどれかが発生すると
// ?リターンします。
dwRes = WaitForSingleObject( osReader.hEvent, 500 );
switch( dwRes ) // 戻り値で分岐
{
case WAIT_OBJECT_0: // イベント発生
// イベントが正常がチェックする
// ?読み込み完了と読み込みエラーのリターンコードが同じなので
// ?正常終了したかどうかチェックします。
if( !GetOverlappedResult( E_ComPort, &osReader, &Read_Size, FALSE ))
{
// 読み込み中に回線断が発生した場合はここを通ります
// エラーメッセージ表示
SimErrMsg( "%s[%d] 受信失敗(%d)",
0, __file__, __line__, GetLastError() );
Loop_End = TRUE;
}
else
{
// バッファリング処理
Bool_Ret = SimReadSuccess( (char)ReadBuff[0] );
// バッファリング処理失敗
if( Bool_Ret != TRUE )
{
// エラーメッセージ表示
SimErrMsg( "%s[%d] バッファリング処理失敗(%d)",
0, __file__, __line__, GetLastError() );
}
}
// 非同期中メッセージ待ち状態をOFFにする
fWaitingOnRead = FALSE;
break;
case WAIT_TIMEOUT: // タイムアウト
// ?無通信チェックもできると思います。
break;
default:
break;
}
}
}
// スレッド終了
return(0);
}
BOOL COMMwrite(
char *WriteMsg, // [IN] 送信電文
UINT WriteLength // [IN] 送信電文長
)
{
OVERLAPPED osWrite = {0}; // 非同期通信用OVERLAPPED構造体
DWORD dwWritten; // 書き込みバイト数
BOOL fRes; // 内部リターンコード
BOOL Bool_Ret; // 関数戻り値
long Ret; // 関数戻り値
//-----------------------------------------------------------------------------
/// 非同期通信用の処理完了イベントを設定
//-----------------------------------------------------------------------------
// 非同期通信用の処理完了イベントを設定
// ?読み込みと同じでそのまま記述して下さい。
osWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
// イベント設定に失敗
if( osWrite.hEvent == NULL )
{
// 内部ログの書き込み
(long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0,
PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() );
// エラーでリターンする
return( FALSE );
}
//-----------------------------------------------------------------------------
/// 送出TCSに電文を送信する
//-----------------------------------------------------------------------------
// TCSに送信電文を送信する
// ?WriteFile()関数は書き込みが完了したかどうかに関わらず、すぐにリターンします。
if( !WriteFile( Com2Port, WriteMsg, WriteLength, &dwWritten, &osWrite))
{
// エラーチェック
// ?書き込み完了待ちならば、GetLastError()の戻りがERROR_IO_PENDINGになります。
// ?それ以外は書き込みエラーです
if( GetLastError() != ERROR_IO_PENDING )
{
// ライト完了待ち以外のエラー
// 内部ログの書き込み
(long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0,
PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() );
// 内部リターンコードをエラーにする
fRes = FALSE;
}
else
{
// ?読み込み時と同じようにイベントの発生待ちを行います
// ?タイマ秒数は外部変数です
fRes = WaitForSingleObject( osWrite.hEvent, E_Wri_TimMax );
switch( fRes )
{
case WAIT_OBJECT_0:
// ?書き込み完了と読み込みエラーのリターンコードが同じなので
// ?正常終了したかどうかチェックします。
if( !GetOverlappedResult( Com2Port, &osWrite, &dwWritten, FALSE ) )
{
fRes = FALSE;
}
else
{
fRes = TRUE;
}
break;
default:
// ?ここを通る場合はまず、書き込みタイムアウトの場合でしょう
// ?COM通信は相手がいなくても、書き込みでエラーにならないし、
// ?回線が切れていようが、分からないので、ここのタイムアウトにより、
// ?エラーと判断しています。
fRes = FALSE;
}
// ライト完了待ち
// ?昔の処理で、タイムアウト値が無限大になっています。
// ?このバージョンだと、正常に回線断を検出できませんし、
// ?結局、GetOverlappedResult()関数でWAITになってしまい
// ?非同期にした意味がありません。
// if( !GetOverlappedResult( Com2Port, &osWrite, &dwWritten, INFINITE ) )
if( fRes == FALSE )
{
// ライト異常終了
// 内部ログの書き込み
(long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0,
PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "GetOverlappedResult", GetLastError() );
// 内部リターンコードをエラーにする
fRes = FALSE;
}
else
{
// ライト正常終了
// 内部リターンコードを正常にする
fRes = TRUE;
}
}
}
else
{
// Writeが正常に完了した
// 内部リターンコードを正常にする
fRes = TRUE;
}
//-----------------------------------------------------------------------------
/// 電文送信エラー処理
//-----------------------------------------------------------------------------
// ライト失敗なら回線を切断する
if( fRes == FALSE )
{
// 回線を切断する
Bool_Ret = COMMcommclose( );
if( Bool_Ret != TRUE )
{
// 内部ログの書き込み
(long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0,
PRCNAM_COM, __file__, __line__, D_LOG_MSG09, "COMMcommclose", GetLastError() );
}
// 再オープン用タイマを設定する
Ret = SetTimer( E_WinHdl_MF, D_TIMER_OPEN, E_Opn_TimMax, NULL );
if( Ret == 0 )
{
// 内部ログの書き込み
(long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0,
PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() );
}
}
// リターンする
return( fRes );
}