// terminalDialog.cpp : 実装ファイル
//

#include "stdafx.h"
#include "RS-WF62.h"
#include "terminalDialog.h"
#include "RS-WF62Dlg.h"


extern CRSWF62Dlg* g_mainDlg;
extern SOCKET g_tcpSock;
extern char g_readData[RECEIVE_BUFF_SIZE];
extern BOOL g_isShowReadDataBusy;
extern char g_notDisplayedData[RECEIVE_BUFF_SIZE];
extern BOOL isSockTerminal;

// COMポート
HANDLE g_hPort = NULL;
static OVERLAPPED g_ovlpdRead;
static OVERLAPPED g_ovlpdSend;
TCHAR g_sendData[SEND_BUFF_SIZE];

// terminalDialog ダイアログ

IMPLEMENT_DYNAMIC(terminalDialog, CDialog)

terminalDialog::terminalDialog(CWnd* pParent /*=NULL*/)
	: CDialog(terminalDialog::IDD, pParent)
	, m_strSend(_T(""))
	, m_strReceive(_T(""))
	, m_comReadThread(NULL)
	, m_comSendThread(NULL)
	, m_sockReadThread(NULL)
	, m_connectType(FALSE)
{

}

terminalDialog::~terminalDialog()
{
}

void terminalDialog::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_SEND, m_strSend);
	DDX_Text(pDX, IDC_EDIT_RECEIVE, m_strReceive);
}


BEGIN_MESSAGE_MAP(terminalDialog, CDialog)
	ON_MESSAGE(WM_RECEIVE_THREAD_MESSAGE, showReadData)
	ON_MESSAGE(WM_SEND_ERR_MESSAGE, showSendErrorDialog)
	ON_BN_CLICKED(IDC_SEND, &terminalDialog::OnBnClickedSend)
	ON_BN_CLICKED(IDC_BUTTON_CLEAR, &terminalDialog::OnBnClickedButtonClear)
	ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &terminalDialog::OnBnClickedButtonDisconnect)
	ON_WM_DESTROY()
END_MESSAGE_MAP()

//--------------------------------------------------------
//内容
// COMポートの受信用スレッド
//引数
// LPVOID pDlg: 呼び出し元ダイアログへのポインタ
//戻り値
// 0
//--------------------------------------------------------
UINT ComReadThread(LPVOID pDlg)
{
	DWORD dwEvent;
	DWORD dwRead;
	DWORD dwEndtime;
	DWORD err;
	int readDataLen = 0;
	char inBuff[RECEIVE_BUFF_SIZE] = {0};

	// 監視イベントをマスク。EV_RXCHARはデータ受信
	SetCommMask(g_hPort, EV_RXCHAR);
	while(((terminalDialog*)pDlg)->m_comReadThread != NULL)
	{
		// COMイベントを待つ
		WaitCommEvent(g_hPort, &dwEvent, NULL);
		if((dwEvent & EV_RXCHAR) == EV_RXCHAR)
		{
			while(g_isShowReadDataBusy == TRUE)
			{
				Sleep(MSEC_30);
			}
			dwRead = 0;
			// データ受信
			if(ReadFile(g_hPort, inBuff, RECEIVE_BUFF_SIZE, &dwRead, &g_ovlpdRead) == 0)
			{
				err = GetLastError();
				dwRead = 0;
				// ERROR_IO_PENDINGのときはReadFile()の結果がエラーでも可
				if(err == ERROR_IO_PENDING)
				{
					dwEndtime = GetTickCount() + WAIT_PENDING_TIME;
					// 完了したらTRUEになる
					while(!GetOverlappedResult(g_hPort, &g_ovlpdRead, &dwRead, FALSE))
					{
						if(GetTickCount() > dwEndtime)
						{
							break;
						}
					}
				}
			}
			if(err == 0 || err == ERROR_IO_PENDING)
			{
				if(dwRead)
				{
					// 受信データをコピーしてダイアログに受信メッセージを送る
					readDataLen = dwRead;
					memset(g_notDisplayedData, NULL, RECEIVE_BUFF_SIZE);
					memcpy(g_notDisplayedData, inBuff, readDataLen);

					((CDialog*)pDlg)->PostMessage(WM_RECEIVE_THREAD_MESSAGE, 0, 0);
				}
			}
		}
	}
	
	return 0;
}


//--------------------------------------------------------
//内容
// COMポートの送信用スレッド
//引数
// LPVOID pDlg: 呼び出し元ダイアログへのポインタ
//戻り値
// 0
//--------------------------------------------------------
UINT ComSendThread(LPVOID pDlg)
{
	BOOL ret;
	DWORD dwEndtime;
	CString sendStr;

	sendStr.Format(L"%s", g_sendData);
	memset(g_sendData, 0, sizeof(g_sendData));
	while(((terminalDialog*)pDlg)->m_comSendThread != NULL)
	{
		DWORD dwSendSize;	// 送信データサイズ

		ret = WriteFile(g_hPort, sendStr, sendStr.GetLength()*2, &dwSendSize, &g_ovlpdSend);
		if(ret == FALSE)
		{
			if(GetLastError() == ERROR_IO_PENDING)
			{
				dwEndtime = GetTickCount() + WAIT_PENDING_TIME;
				// 完了したらTRUEになる
				while(GetOverlappedResult(g_hPort, &g_ovlpdSend, &dwSendSize, FALSE) == FALSE)
				{
					if(GetTickCount() > dwEndtime)
					{
						break;
					}
				}
			}
			else
			{
				CloseHandle(g_hPort);
				g_hPort = NULL;
				((CDialog*)pDlg)->PostMessage(WM_SEND_ERR_MESSAGE, 0, 0);
				return 0;
			}
		}
		sendStr.Empty();
		((terminalDialog*)pDlg)->m_comSendThread = NULL;
		return 0;
	}

	return 0;
}

//--------------------------------------------------------
//内容
// socketの受信用スレッド
//引数
// LPVOID pDlg: 呼び出し元ダイアログへのポインタ
//戻り値
// 0
//--------------------------------------------------------
UINT socketReadThread(LPVOID pDlg)
{
	UINT ret;

	memset(g_readData, 0, sizeof(g_readData));
	memset(g_notDisplayedData, 0, sizeof(g_notDisplayedData));

	while(((terminalDialog*)pDlg)->m_sockReadThread != NULL)
    {
		while(g_isShowReadDataBusy == TRUE)
		{
			Sleep(MSEC_30);
		}
		// データ受信
		ret = g_mainDlg->receiveSockData(FALSE);
		if(ret == ERR_SUCCESS)
		{
			// ダイアログに受信メッセージを送る
			((CDialog*)pDlg)->PostMessage(WM_RECEIVE_THREAD_MESSAGE, 0, 0);
		}
    }
	
	return 0;
}

// terminalDialog メッセージ ハンドラ


//--------------------------------------------------------
//内容
// RS-WF62からの受信データをテキストボックスに描画
//引数:
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT terminalDialog::showReadData(WPARAM wParam, LPARAM lParam)
{
	g_isShowReadDataBusy = TRUE;
	// 受信文字列をエディットボックスの表示
	m_strReceive += g_notDisplayedData;

	UpdateData(FALSE);

	// 受信エディットボックスのカーソルを最後に
	int pos = m_strReceive.GetLength();
	CEdit* pEdit = (CEdit*)GetDlgItem(IDC_EDIT_RECEIVE);
	pEdit->SetSel(pos, pos);

	memset(g_notDisplayedData, 0, sizeof(g_notDisplayedData));

	g_isShowReadDataBusy = FALSE;
	return 0;
}

//--------------------------------------------------------
//内容
// 送信スレッドのエラーをメッセージボックスで表示
//引数
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT terminalDialog::showSendErrorDialog(WPARAM wParam, LPARAM lParam)
{
	AfxMessageBox(IDS_STRING_SEND_ERR);
	return 0;
}

//--------------------------------------------------------
//内容
// 「送信」ボタンのクリックイベント
// 「送信データ」エディットボックス内の文字列をRS-WF62に送信
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void terminalDialog::OnBnClickedSend()
{
	UpdateData(TRUE);

	if(m_strSend == L"")
	{
		return;
	}

	// 送信データ生成
	CString sendStr = m_strSend;
	char strTmp[SEND_BUFF_SIZE*2];
	int dataLength = 0;
	int i, j;

	// データの送信
	// COM
	if(m_connectType == CONNECT_COM)
	{
		// エラーのクリア
		COMSTAT Comstat;	//  通信デバイス情報構造体
		DWORD dwErrorMask;	//  エラーコードを受け取る変数
		ClearCommError(g_hPort, &dwErrorMask, &Comstat); 

		// スレッドで使用するためchar[]に変換
		memset(g_sendData, 0, sizeof(g_sendData));
		memset(strTmp, 0, sizeof(strTmp));
		dataLength = sendStr.GetLength();
		memcpy(strTmp, sendStr, dataLength*2);
		// 1バイトおきに0が入るので取り除く
		for(i=0, j=0; i<dataLength*2; i+=2, j++)
		{
			g_sendData[j] = strTmp[i];
		}
		// スレッドを設定
		if(m_comSendThread == NULL)
		{
			m_comSendThread = AfxBeginThread(ComSendThread, this);
		}
	}
	// Socket
	else
	{
		// 文字列の送信
		if(g_mainDlg->sendSockData(sendStr) == SOCKET_ERROR)
		{
			AfxMessageBox(IDS_STRING_RECEIVE_DATA_FAILED);
		}
	}
	// テキストエリアのクリア
	m_strSend.Empty();

	UpdateData(FALSE);
}

//--------------------------------------------------------
//内容
// 「受信データのクリア」ボタンのクリックイベント
// 「受信データ」テキストエリア内の文字列をクリアする
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void terminalDialog::OnBnClickedButtonClear()
{
	m_strReceive.Empty();
	UpdateData(FALSE);
}

//--------------------------------------------------------
//内容
// 「切断」ボタンのクリックイベント
// リソースを開放し、ダイアログを閉じる
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void terminalDialog::OnBnClickedButtonDisconnect()
{
	// リソースの開放
	releaseResources();
	OnOK();
}

//--------------------------------------------------------
//内容
// メインダイアログで選択した方法でRS-WF62との通信を開始する
//   「TCP/IPで接続」クリック時:
//     「デバイスの選択」リストコントロールで選択中のIPアドレスと
//     ソケットで接続し、受信スレッドを開始する
//   「COMポートに接続」クリック時:
//     選択中のCOMポートを開く
//引数
// なし
//戻り値 TRUE
//--------------------------------------------------------
BOOL terminalDialog::OnInitDialog()
{
	CDialog::OnInitDialog();

	// IMEの無効化
	ImmAssociateContext(GetDlgItem(IDC_EDIT_SEND)->GetSafeHwnd(), NULL);

	CString str;

	// TCP/IP
	if(m_connectType == CONNECT_SOCK)
	{
		if(g_mainDlg->m_deviceList.GetFirstSelectedItemPosition() == NULL)
		{
			AfxMessageBox(IDS_STRING_NOT_SELECTED);
			EndDialog(IDCANCEL);
			return TRUE;
		}

		str.LoadString(IDS_STRING_TITLE_TCP);
		this->SetWindowTextW(str);

		int ret;
		// ソケットのオープン
		ret = g_mainDlg->openSocket(g_mainDlg->getSelectedDevice(DEV_LIST_ADDRESS));
		// ソケットのオープンに失敗
		if(ret != 0)
		{
			AfxMessageBox(IDS_STRING_SOCK_OPEN_ERR);
		}
		else
		{
			// 受信スレッドを開始
			if(m_sockReadThread == NULL)
			{
				m_sockReadThread = AfxBeginThread(socketReadThread, this);
			}
		}
	}
	// COMポート
	else
	{
		str.LoadString(IDS_STRING_TITLE_COM);
		this->SetWindowTextW(str);

		BOOL ret;
		CString comNum;		// COMポート番号
		CString comName;	// \\.\をつけたCOMポート名
		g_mainDlg->m_comList.GetLBText(g_mainDlg->m_comList.GetCurSel(), comNum);
		comName = L"\\\\.\\" + comNum;

		// COMポートのオープン
		g_hPort = CreateFile(comName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
		if(g_hPort == INVALID_HANDLE_VALUE)
		{
			AfxMessageBox(IDS_STRING_COM_OPEN_FAILED);
			EndDialog(IDCANCEL);
			return TRUE;
		}

		// 送受信バッファ初期化
		// COMの設定
		ret = SetupComm(g_hPort, RECEIVE_BUFF_SIZE, SEND_BUFF_SIZE);
		if(ret == FALSE)
		{
			AfxMessageBox(IDS_STRING_COM_SET_FAILED);
			CloseHandle(g_hPort);
			g_hPort = NULL;
			EndDialog(IDCANCEL);
			return TRUE;
		}
		// 初期化
		ret = PurgeComm(g_hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
		if(ret == FALSE)
		{
			AfxMessageBox(IDS_STRING_COM_PURGE_FAILED);
			CloseHandle(g_hPort);
			g_hPort = NULL;
			EndDialog(IDCANCEL);
			return TRUE;
		}

		// 通信条件の設定
		DCB dcb;
		ret = GetCommState(g_hPort, &dcb);
		dcb.DCBlength = sizeof(DCB);

		dcb.BaudRate = 115200;		// ボーレート115200
		dcb.ByteSize = 8;			// データサイズ8bit
		dcb.StopBits = ONESTOPBIT;	// ストップビット1bit		
		dcb.fParity = NOPARITY;		// パリティチェックなし

		dcb.fOutxCtsFlow = FALSE;	// ハードウェアフロー制御なし
		dcb.fRtsControl = RTS_CONTROL_DISABLE;		

		dcb.fOutX = FALSE;			// ソフトウェアフロー制御なし
		dcb.fInX = FALSE;

		dcb.fNull = TRUE;			// エラー関連
		dcb.fAbortOnError = TRUE;
		dcb.fErrorChar = FALSE;
		dcb.ErrorChar = 0x00;

		// dcbの値を反映
		ret = SetCommState(g_hPort, &dcb);
		if(ret == FALSE)
		{
			AfxMessageBox(IDS_STRING_COM_SET_FAILED);
			CloseHandle(g_hPort);
			g_hPort = NULL;
			EndDialog(IDCANCEL);
			return TRUE;
		}

		// タイムアウト時間の設定
		COMMTIMEOUTS timeout;
		timeout.ReadIntervalTimeout = COM_WAIT_TIME;
		timeout.ReadTotalTimeoutMultiplier = 0;
		timeout.ReadTotalTimeoutConstant = COM_WAIT_TIME;
		timeout.WriteTotalTimeoutMultiplier = 0;
		timeout.WriteTotalTimeoutConstant = COM_WAIT_TIME;
		SetCommTimeouts(g_hPort, &timeout);

		// スレッドを設定
		if(m_comReadThread == NULL)
		{
			m_comReadThread = AfxBeginThread(ComReadThread, this);
		}
	}
	isSockTerminal = TRUE;
	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

//--------------------------------------------------------
//内容
// スレッドを終了し、リソースを開放する
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void terminalDialog::releaseResources(void)
{
	// 通信中であればリソースを開放
	if(m_comReadThread != NULL)
	{
		if(!((g_hPort == NULL) || (g_hPort == INVALID_HANDLE_VALUE)))
		{
			CloseHandle(g_hPort);
			g_hPort = NULL;
		}
		m_comReadThread = NULL;
	}
	if(m_comSendThread != NULL)
	{
		if(!((g_hPort == NULL) || (g_hPort == INVALID_HANDLE_VALUE)))
		{
			CloseHandle(g_hPort);
			g_hPort = NULL;
		}
		m_comSendThread = NULL;
	}
	if(m_sockReadThread != NULL)
	{
		g_mainDlg->endSocket(g_tcpSock);
		m_sockReadThread = NULL;
	}
	isSockTerminal = FALSE;
}

//--------------------------------------------------------
//内容
// ダイアログ終了イベント
// 終了時にRS-WF62に接続中の場合は切断する
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void terminalDialog::OnDestroy()
{
	CDialog::OnDestroy();

	// リソースの開放
	releaseResources();
}
