//----------------------------------------------------------------------------------------
//  SIserialComm.cpp: CSIserialComm クラスのインプリメンテーション
//
//----------------------------------------------------------------------------------------

#include "SIserialComm.h"
#include <stdio.h>
#include <locale.h>


//----------------------------------------------------------------------------------------
//  構築
//----------------------------------------------------------------------------------------
CSIserialComm::CSIserialComm()
{
    m_hComm = INVALID_HANDLE_VALUE;
    m_nCommStatus = 0;
}

//----------------------------------------------------------------------------------------
//  消滅
//----------------------------------------------------------------------------------------
CSIserialComm::~CSIserialComm()
{
}


//----------------------------------------------------------------------------------------
// COMポートをオープン
//    RETURN  オープン結果   0:失敗、1:成功
//----------------------------------------------------------------------------------------
int  CSIserialComm::open( const char *pcComName, int baud )
{
    DWORD   lpErrors ;  /* エラーコードを受け取る変数へのポインタ */
    COMSTAT lpStat ;    /* 通信状態バッファへのポインタ */

#if 1
    WCHAR  temp[30] ;
    char   temp2[20] ;

    sprintf( temp2, "\\\\.\\%s", pcComName ) ;
    mbstowcs( temp, temp2, 20 ) ;
#else
    WCHAR  *temp = L"\\\\.\\COM12" ;
#endif
    m_nCommStatus = 0;

    m_hComm = CreateFile(
            temp,                           //  シリアルポート名
            GENERIC_READ|GENERIC_WRITE,     //  アクセスモード
            0,                              //  共有モード
            NULL,                           //  セキュリティ属性
            OPEN_EXISTING,                  //  作成フラグ
            FILE_ATTRIBUTE_NORMAL,          //  属性
            NULL                            //  テンプレートのハンドル
    );

    if( m_hComm != INVALID_HANDLE_VALUE ){
        //  通信設定
        if( ::GetCommState( m_hComm, &m_DCB ) == 1 ){       // DCB を取得
            m_DCB.DCBlength = sizeof(DCB);
            m_DCB.fBinary = 1;
            m_DCB.BaudRate = baud;
            m_DCB.ByteSize = 8;
            m_DCB.Parity = NOPARITY;
            m_DCB.StopBits = ONESTOPBIT;
            m_DCB.fParity = 0;
            m_DCB.fRtsControl = RTS_CONTROL_DISABLE;
            m_DCB.fDtrControl = DTR_CONTROL_DISABLE;
            m_DCB.fOutxCtsFlow = 0;
            m_DCB.fTXContinueOnXoff = 0;
            m_DCB.fInX = m_DCB.fOutX = 0;
            m_DCB.XonChar = 0x11;
            m_DCB.XoffChar = 0x13;
            m_DCB.XonLim = 100;
            m_DCB.XoffLim = 1500;
        } else {
            close();
        }

        ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;

        if( ::SetCommState( m_hComm, &m_DCB ) == 1 ){   // DCB を設定
            if( ::SetupComm( m_hComm, 1600 , 1600 ) == 1 ){
                //  タイムアウトの設定を変更
                ::GetCommTimeouts( m_hComm, &m_CTO );
                m_CTO.ReadIntervalTimeout = 50;
                m_CTO.ReadTotalTimeoutMultiplier = 30;
                m_CTO.ReadTotalTimeoutConstant = 50;
                m_CTO.WriteTotalTimeoutMultiplier = 30;
                m_CTO.WriteTotalTimeoutConstant = 50;

                if( ::SetCommTimeouts( m_hComm, &m_CTO ) != 1 ) {
                    close();
                }

                if( ::PurgeComm( m_hComm, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR) != 1 ) {
                    close();
                }
            } else {
                close();
            }
        } else {
            close();
        }
    }

    if( m_hComm != INVALID_HANDLE_VALUE ) {
        m_nCommStatus = 1;
    }

    return( m_nCommStatus ) ;
}


//----------------------------------------------------------------------------------------
//  COMポートをクローズ
//----------------------------------------------------------------------------------------
int  CSIserialComm::close()
{
    BOOL  bResult = TRUE ;
    DWORD   lpErrors ;  /* エラーコードを受け取る変数へのポインタ */
    COMSTAT lpStat ;    /* 通信状態バッファへのポインタ */

    if( m_hComm != INVALID_HANDLE_VALUE ){
        /* clear ERROR */
        ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;

        bResult = ::CloseHandle( m_hComm );
    }

    m_hComm = INVALID_HANDLE_VALUE;
    m_nCommStatus = 0;

    return  (bResult==TRUE)?1:0;
}


//----------------------------------------------------------------------------------------
//  受信データバッファにあるデータをクリア
//      RETURN  void
//----------------------------------------------------------------------------------------
void    CSIserialComm::clearRecvQueue()
{
    unsigned char   ucData;

    if( m_nCommStatus == 1 ){
        while( checkRecvQueue() != 0 )  recv( &ucData, 1 );
    }
}


//----------------------------------------------------------------------------------------
//  受信データバッファにデータがあるかチェック
//      RETURN  int         データ長
//----------------------------------------------------------------------------------------
int     CSIserialComm::checkRecvQueue()
{
    DWORD   dwCount = 0;                    //  受信データのバイト数
    COMSTAT ComStat;                        //  デバイスの状態
    DWORD   dwErrors;                       //  エラー情報

    if( m_nCommStatus == 1 ){
        ::ClearCommError( m_hComm, &dwErrors, &ComStat );
        dwCount = ComStat.cbInQue;
    }

    return  (int)dwCount;
}


//----------------------------------------------------------------------------------------
//  データの受信
//      データを受信する。ただし、指定されたバイトするに達しない場合、50msecでタイムアウト
//      する。
//      RETURN  int         受信したデータ数
//      OUT     *char       受信データ格納ポインタ
//      IN      int         受信したいデータ長
//----------------------------------------------------------------------------------------
int     CSIserialComm::recv( unsigned char *pcData, int nLength )
{
    DWORD   dwRdLength = 0;                 //  ポートから読み出したバイト数
    DWORD   lpErrors ;  /* エラーコードを受け取る変数へのポインタ */
    COMSTAT lpStat ;    /* 通信状態バッファへのポインタ */

    if( m_nCommStatus == 1 ){
        if( m_hComm != INVALID_HANDLE_VALUE ){
            /* check ERROR */
            ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;
            if( lpErrors == 0 ) {

                ::ReadFile( m_hComm, pcData, nLength, &dwRdLength, NULL );
                ::WaitForSingleObject( m_hComm, 50 ) ;

                /* clear ERROR */
                ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;
            }
        }
    }


    return (int)dwRdLength;
}


//----------------------------------------------------------------------------------------
//  データの送信
//      データを送信する。
//      RETURN  int     送信したデータ数
//      IN      uchar*  送信データ格納ポインタ
//      OUT     int     送信するデータ長
//----------------------------------------------------------------------------------------
int     CSIserialComm::send( const unsigned char *pcData, int nLength )
{
    DWORD dwWrLength = 0;                       // ポートへ書き込んだバイト数
    DWORD   lpErrors ;  /* エラーコードを受け取る変数へのポインタ */
    COMSTAT lpStat ;    /* 通信状態バッファへのポインタ */

    if( m_nCommStatus == 1 ){
        if( m_hComm != INVALID_HANDLE_VALUE ){
            /* check ERROR */
            ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;
            if( lpErrors == 0 ) {

                ::WriteFile( m_hComm, pcData, nLength, &dwWrLength, NULL );
                ::WaitForSingleObject( m_hComm, 50 ) ;

                /* clear ERROR */
                ::ClearCommError( m_hComm, &lpErrors, &lpStat ) ;
            }
        }
    }


    return  (int)dwWrLength;
}
