
//====================================================================================
// 作成者: RATOC Systems, Inc
//
// 本プログラムはRS-WF62と通信するためのサンプルプログラムです。
// 以下の機能を有します。
//
// デバイスの検索
//  5073ポートでUDPデータグラムを送信するデバイスを
//  RS-WF62としてデバイスのリストコントロールに列挙します。
//  RS-WF62はデフォルトの設定で8秒おきにポート5073でUDPデータグラムを送信しています。
//
// ソケットを使用した通信
//  「TCP/IPで接続」ボタンをクリックすると、
//  デバイスのリストコントロールで選択中のIPアドレスとポート5074でTCPで通信を開始します。
//  通信の開始後、"$$$"を送信するとコマンドモードに入り、
//  "exit"を送信するとコマンドモードを抜けます。
//  RS-WF62のデフォルトのTCPポートは5074です。
//
// COMポートを使用した通信
//  RS-WF62はCOMポートとして通信することはできません。
//  シリアル通信はソフトウェアまたはハードウェアでTCP-COMポート変換のうえ行ってください。
//  本サンプルでは存在するCOMポートをすべて列挙します。
//
// 履歴:
//  Ver.1.0 2025/08/08 新規作成
//====================================================================================

// RS-WF62Dlg.cpp : 実装ファイル
//

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

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#include "passwordDialog.h"
#include "scanDialog.h"
#include "ledDialog.h"
#include "terminalDialog.h"

// ダイアログ
scanDialog g_scanDlg;
ledDialog g_ledDlg;
CRSWF62Dlg* g_mainDlg = NULL;

// CRSWF62Dlg ダイアログ

char g_readData[RECEIVE_BUFF_SIZE];
char g_notDisplayedData[RECEIVE_BUFF_SIZE] = {0};

// ソケット
SOCKET g_tcpSock;
struct sockaddr_in g_server = {0};
SOCKET g_udpSock;

// PostMessage()したメッセージを処理中かどうか
BOOL g_isShowReadDataBusy = FALSE;

BOOL isSockTerminal = FALSE;

CString g_selectedIp;


CRSWF62Dlg::CRSWF62Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CRSWF62Dlg::IDD, pParent)
, m_devPassword(_T(""))
, m_ledCheckThread(NULL)
, m_deviceScanThread(NULL)
, m_selectedDev(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CRSWF62Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_COMBO_COM, m_comList);
	DDX_Control(pDX, IDC_LIST_DEVICE, m_deviceList);
	DDX_Text(pDX, IDC_EDIT_MAIN_SELECTED_DEVICE, m_selectedDev);
}

BEGIN_MESSAGE_MAP(CRSWF62Dlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_SEARCH_START_MESSAGE, showScanDialog)
	ON_MESSAGE(WM_BLINK_START_MESSAGE, showLedDialog)
	ON_MESSAGE(WM_SEARCH_FINISHED_MESSAGE, updateDeviceList)
	ON_MESSAGE(WM_BLINK_FINISHED_MESSAGE, hideLedDialog)
	ON_BN_CLICKED(IDC_BUTTON_RESCAN, &CRSWF62Dlg::OnBnClickedButtonRescan)
	ON_BN_CLICKED(IDC_BUTTON_LED, &CRSWF62Dlg::OnBnClickedButtonLed)
	ON_BN_CLICKED(IDC_BUTTON_CONNECT_TELNET, &CRSWF62Dlg::OnBnClickedButtonConnectTelnet)
	ON_BN_CLICKED(IDC_BUTTON_CONNECT_COM, &CRSWF62Dlg::OnBnClickedButtonConnectCom)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_DEVICE, &CRSWF62Dlg::OnLvnItemchangedListDevice)
END_MESSAGE_MAP()


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

//--------------------------------------------------------
//内容
// ダイアログの初期化
// RS-WF62とCOMポートを検索し、リストコントロールに表示する
//引数
// なし
//戻り値
// TRUE
//--------------------------------------------------------
BOOL CRSWF62Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
	//  Framework は、この設定を自動的に行います。
	SetIcon(m_hIcon, TRUE);			// 大きいアイコンの設定
	SetIcon(m_hIcon, FALSE);		// 小さいアイコンの設定

	CString str;

	// リストコントロールの設定
	// 行ごとに選択可能に
	m_deviceList.SendMessage( LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT);

	// カラムの設定
	LVCOLUMN column;
	column.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	for(int i=0; i<DEV_COLUMN_COUNT; i++)
	{
		column.iSubItem = i;
		switch(i)
		{
		case 0:
			str.LoadString(IDS_STRING_LIST_DEV_NAME);
			column.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));  
			column.cx = 85;  
			break;
		case 1:
			str.LoadString(IDS_STRING_IP);
			column.pszText =  const_cast<LPTSTR>(static_cast<LPCTSTR>(str)); 
			column.cx = 90;  
			break;
		case 2:
			str.LoadString(IDS_STRING_LIST_CONNECT_MODE);
			column.pszText =  const_cast<LPTSTR>(static_cast<LPCTSTR>(str)); 
			column.cx = 100;    
			break;
		case 3:
			str.LoadString(IDS_STRING_VERSION);
			column.pszText =  const_cast<LPTSTR>(static_cast<LPCTSTR>(str)); 
			column.cx = 60;    
			break;
		}
		m_deviceList.InsertColumn(i, &column);
	}

	// 設定ダイアログからの参照用
	g_mainDlg = this;

	// RS-WF62を検索
	OnBnClickedButtonRescan();

	UpdateData(FALSE);

	return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

//--------------------------------------------------------
//内容
// RS-WF62の情報をリストコントロールに追加
//引数
// DeviceInfo deviceInfo[]: デバイス情報の構造体の配列
// UINT deviceCount: deviceInfoの数
//戻り値
// TRUE
//--------------------------------------------------------
void CRSWF62Dlg::setDeviceList(DeviceInfo deviceInfo[], UINT deviceCount)
{
	LVITEM item;
	for (int i=0; i<(int)deviceCount; i++)
	{
		item.mask = LVIF_TEXT;
		item.iItem = i;
		for(int j=0; j<DEV_COLUMN_COUNT; j++)
		{
			item.iSubItem = j;
			switch(j)
			{
			case 0:
				item.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(deviceInfo[i].name));
				m_deviceList.InsertItem(&item);
				break;
			case 1:
				item.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(deviceInfo[i].ipAddress));
				m_deviceList.SetItem(&item);
				break;
			case 2:
				item.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(deviceInfo[i].mode));
				m_deviceList.SetItem(&item);
				break;
			case 3:
				item.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(deviceInfo[i].version));
				m_deviceList.SetItem(&item);
				break;
			}
		}
	}
}

//--------------------------------------------------------
//内容
// RS-WF62をリストコントロールに表示する
// RS-WF62の検索方法
//  1. UDPポート5073で受信し、送信側のIPアドレスを特定
//  2. 1のデバイスをRS-WF62とみなし、TCPポート2000で接続し、
//     MACアドレス確認コマンドを発行する
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::searchDevice(void)
{
	int i;	//一時用

	// 現在の内容をクリア
	m_comList.ResetContent();
	m_deviceList.DeleteAllItems();
	m_selectedDev.Empty();
	m_devPassword.Empty();

	// UDPの受信を行う
	WSADATA udpWsa;
	struct sockaddr_in udpAddr = {0};
	struct sockaddr_in fromAddr = {0};
	char receiveData[RECEIVE_BUFF_SIZE] = {0};
	struct protoent* pEnt;
	int inAddrSize = sizeof(struct sockaddr_in);
	// ソケットの初期化
	if(WSAStartup(MAKEWORD(2,2), &udpWsa) != 0)
	{
		AfxMessageBox(IDS_STRING_SOCK_ERR);
		return;
	}
	pEnt = getprotobyname("udp");
	if(pEnt == NULL)
	{
		WSACleanup();
		return;
	}
	// ソケットの作成
	g_udpSock = socket(AF_INET, SOCK_DGRAM, pEnt->p_proto);
	if(g_udpSock == INVALID_SOCKET)
	{
		AfxMessageBox(IDS_STRING_SOCK_OPEN_ERR);
		WSACleanup();
		return;
	}
	// バインド
	udpAddr.sin_family = AF_INET;
	udpAddr.sin_port = htons(PORT_UDP);	// デフォルトのUDPポートは5073
	udpAddr.sin_addr.S_un.S_addr = INADDR_ANY;
	if(bind(g_udpSock, (struct sockaddr *)&udpAddr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
	{
		AfxMessageBox(IDS_STRING_BIND_ERR);
		endSocket(g_udpSock);
		return;
	}

	// UDPパケットの送信元IPアドレスを取得
	// note: 初回接続時にread可能状態をOSに通知しないNICに対応するため、
	// select()ではなくノンブロッキングのrecvfrom()で受信を確認

	CString strTmp, strTmp2;
	DeviceInfo devList[MAX_DEVICE] = { 0 };
	int foundDevice = 0;
	BOOL isReceived;
	u_long arg = SOCK_ARG_NON_BLOCKING;
	ioctlsocket(g_udpSock, FIONBIO, &arg);
	int timeCount = 0;
	while(timeCount < UDP_WAIT_TIME*2)
	{
		isReceived = FALSE;
		// 受信データがある
		if(recvfrom(g_udpSock, receiveData, sizeof(receiveData), 0, (struct sockaddr *)&fromAddr, &inAddrSize) > 0)
		{
			strTmp = inet_ntoa(fromAddr.sin_addr);
			if(foundDevice != 0)
			{
				for(i=0; i<foundDevice; i++)
				{
					// 同じIPアドレスから受信
					if(strTmp == devList[i].ipAddress)
					{
						isReceived = TRUE;
						break;
					}
				}
			}
			if(isReceived == FALSE)
			{
				devList[foundDevice].ipAddress = strTmp;

				CStringA strSSID;
				strSSID.Format("%s", (PCHAR)(receiveData + 0x6A));
				devList[foundDevice].name = strSSID;


				// 文字列終端とみなさないよう受信データの\0をスペースに変換
				for(i=0; i<RECEIVE_BUFF_SIZE; i++)
				{
					if(receiveData[i] == '\0')
					{
						receiveData[i] = ' ';
					}
				}

				// データ先頭にある文字にできないバイト列を削除
				strTmp = strstr(receiveData, "Ver");

				int currentPos = 0;
				int endPos;
				strTmp2 = strTmp.Tokenize(L" ", currentPos);
				i = 0;
				while(strTmp2 != L"")
				{
					if(i == 1)
					{
						endPos = strTmp2.Find(L",");
						if(endPos != -1)
						{
							//パラメータ名と"="以降の文字列を抽出
							devList[foundDevice].version = strTmp2.Left(endPos);
							break;
						}
					}
					strTmp2 = strTmp.Tokenize(L" ", currentPos);
					i++;
				}

				CString strMac; 
				strMac.Format(L"%02X:%02X:%02X:%02X:%02X:%02X", receiveData[0] & 0xFF, receiveData[1] & 0xFF, receiveData[2] & 0xFF, receiveData[3] & 0xFF, receiveData[4] & 0xFF, receiveData[5] & 0xFF);

				// 0x00を0x20に変換してるため
				if (receiveData[6] == 0x20)
				{
					devList[foundDevice].mode = "インフラストラクチャ";
				}
				else if (receiveData[6] == 0x01)
				{
					devList[foundDevice].mode = "アクセスポイント";
				}
				else
				{
					devList[foundDevice].mode = "Provisioning";
				}


				foundDevice++;
			}
		}
		// 受信データがなければ1秒待つ
		else
		{
			timeCount++;
			Sleep(MSEC_500);
		}
		if(foundDevice > MAX_DEVICE)
		{
			break;
		}
	}
	endSocket(g_udpSock);
	if(foundDevice == 0)
	{
		return;
	}

	setDeviceList(devList, foundDevice);
}


// ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
//  下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
//  これは、Framework によって自動的に設定されます。
void CRSWF62Dlg::OnPaint()
{
	if(IsIconic())
	{
		CPaintDC dc(this); // 描画のデバイス コンテキスト

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// クライアントの四角形領域内の中央
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// アイコンの描画
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
//  システムがこの関数を呼び出します。
HCURSOR CRSWF62Dlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

//--------------------------------------------------------
//内容
// 選択中のRS-WF62の情報を取得
// 選択中のデバイスリストから指定したカラムの文字列を取得する
//引数
// 文字列を取得するカラム
// DEV_LIST_NAME: デバイス名
// DEV_LIST_ADDRESS: IPアドレス
// DEV_LIST_MODE: 接続モード
//戻り値
// 選択中のデバイスの情報
// 選択されていない場合空文字
//--------------------------------------------------------
CString CRSWF62Dlg::getSelectedDevice(UINT targetItem)
{
	CString dev = L"";
	UpdateData(TRUE);
	int lineIndex;
	POSITION selectedPos;
	selectedPos = m_deviceList.GetFirstSelectedItemPosition();
	if(selectedPos != NULL)
	{
		lineIndex = m_deviceList.GetNextSelectedItem(selectedPos);
		dev = m_deviceList.GetItemText(lineIndex, targetItem);
	}

	return dev;
}

//--------------------------------------------------------
//内容
// ソケット通信の開始
// 指定したIPアドレスとソケット通信を開始する
//引数
// CString address: 通信を行うデバイスのIPアドレス(書式: xxx.xxx.xxx.xxx)
//戻り値
// エラーコード
//  ERR_SUCCESS: 成功
//  それ以外: エラー
//--------------------------------------------------------
int CRSWF62Dlg::openSocket(CString address)
{
	WSADATA wsaData;
	int ret;
	char cIP[IP_LENGTH];

	// ソケットの初期化
	ret = WSAStartup(MAKEWORD(2,2), &wsaData);
	if(ret != 0)
	{
		return ret;
	}

	// ソケットの作成
	g_tcpSock = socket(AF_INET, SOCK_STREAM, 0);

	// 接続先指定用構造体の準備
	g_server.sin_family = AF_INET;
	g_server.sin_port = htons(PORT_TCP);	// ポートは5074固定
	strcpy_s(cIP, CT2CA(address));
	g_server.sin_addr.S_un.S_addr = inet_addr(cIP);

	// サーバに接続
	ret = connect(g_tcpSock, (struct sockaddr *)&g_server, sizeof(g_server));
	// note: 接続先が見つからないとき、関数が帰ってくるまで20秒かかる
	if(ret == SOCKET_ERROR)
	{
		endSocket(g_tcpSock);
		return ret;
	}
	return ERR_SUCCESS;
}

//--------------------------------------------------------
//内容
// ソケットの終了
//引数
// SOCKET socket: 終了するソケット
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::endSocket(SOCKET socket)
{
	shutdown(socket, SD_BOTH);
	closesocket(socket);
	WSACleanup();
}

//--------------------------------------------------------
//内容
// RS-WF62検索スレッド
// 検索中にダイアログを表示し、操作を行えないようにする
//引数
// LPVOID pDlg: 呼び出し元ダイアログへのポインタ
//戻り値
// 0
//--------------------------------------------------------
UINT deviceScanThread(LPVOID pDlg)
{
	((CDialog*)pDlg)->PostMessage(WM_SEARCH_START_MESSAGE, 0, 0);
	((CRSWF62Dlg*)pDlg)->searchDevice();
	((CRSWF62Dlg*)pDlg)->searchCom();
	((CDialog*)pDlg)->PostMessage(WM_SEARCH_FINISHED_MESSAGE, 0, 0);

	return 0;
}

//--------------------------------------------------------
//内容
// LED確認スレッド
// 緑のLEDを消灯→点灯→消灯→点灯→消灯→点灯と1秒ずつ繰り返す
//引数
// LPVOID pDlg: 呼び出し元ダイアログへのポインタ
//戻り値
// 0
//--------------------------------------------------------
UINT ledCheckThread(LPVOID pDlg)
{
	((CDialog*)pDlg)->PostMessage(WM_BLINK_START_MESSAGE, 0, 0);
	((CRSWF62Dlg*)pDlg)->blinkLed();
	((CDialog*)pDlg)->PostMessage(WM_BLINK_FINISHED_MESSAGE, 0, 0);

	return 0;
}

//--------------------------------------------------------
//内容
// 検索中のダイアログを表示
//引数
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT CRSWF62Dlg::showScanDialog(WPARAM wParam, LPARAM lParam)
{
	g_scanDlg.DoModal();
	return 0;
}

//--------------------------------------------------------
//内容
// LED点滅中のダイアログを表示
//引数
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT CRSWF62Dlg::showLedDialog(WPARAM wParam, LPARAM lParam)
{
	g_ledDlg.DoModal();
	return 0;
}
//--------------------------------------------------------
//内容
// デバイスリストの表示を更新
//引数
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT CRSWF62Dlg::updateDeviceList(WPARAM wParam, LPARAM lParam)
{
	m_deviceScanThread = NULL;

	// 検索中に表示していたダイアログを消す
	g_scanDlg.EndDialog(0);

	switchDisable();

	UpdateData(FALSE);
	Invalidate(FALSE);

	return 0;
}

//--------------------------------------------------------
//内容
// LED点滅中のダイアログを終了
//引数
// WPARAM wParam: メッセージ情報
// LPARAM lParam: メッセージ情報
//戻り値
// 0
//--------------------------------------------------------
LRESULT CRSWF62Dlg::hideLedDialog(WPARAM wParam, LPARAM lParam)
{
	g_ledDlg.EndDialog(0);
	return 0;
}

//--------------------------------------------------------
//内容
// ソケット通信でのデータ受信
//引数
// BOOL isBlocking: 使用するソケットがブロッキングかどうか
//					TRUEのときselect()で20秒間待つ
//戻り値
// エラーコード
//  ERR_SUCCESS: 成功
//  それ以外: エラー
//--------------------------------------------------------
UINT CRSWF62Dlg::receiveSockData(BOOL isBlocking)
{
	int recvSize;
	if(isSockTerminal != NULL)
	{
		strcat_s(g_notDisplayedData, sizeof(g_notDisplayedData), g_readData);
	}

	if(isBlocking == TRUE)
	{
		struct timeval timeout;
		fd_set readfds;
		// fd_setの初期化
		FD_ZERO(&readfds);
		// selectで待つ読み込みソケットとしてg_udpSockを登録
		FD_SET(g_tcpSock, &readfds);
		// タイムアウトを20秒に設定
		timeout.tv_sec = 20;
		timeout.tv_usec = 0;
		// fdsに設定されたソケットが読み込み可能になるまで待つ
		if(select(0, &readfds, NULL, NULL, &timeout) == 0)
		{
			endSocket(g_tcpSock);
			return ERR_SELECT_TIME_OUT;
		}
	}

	// データ受信
	memset(g_readData, 0, sizeof(g_readData));
	recvSize = recv(g_tcpSock, g_readData, RECEIVE_BUFF_SIZE, 0);

	// 受信データなし
	if(recvSize == 0)
	{
		endSocket(g_tcpSock);
		return ERR_SOCK_NO_REC_DATA;
	}
	// 受信に失敗
	else if(recvSize == SOCKET_ERROR)
	{
		// ソケットを閉じて終了
		endSocket(g_tcpSock);
		isSockTerminal = FALSE;
		return SOCKET_ERROR;
	}

	return ERR_SUCCESS;
}

//--------------------------------------------------------
//内容
// RS-WF62とソケット通信を開始し、コマンドモードに突入する
//引数
// CString address: 通信を行うデバイスのIPアドレス(書式: xxx.xxx.xxx.xxx)
// BOOL isFromSearch: 検索時の呼び出しかどうか
//					  TRUEのときパスワードがかかっているRS-WF62には
//					  コマンドモードへの移行は行わない
//戻り値
// エラーコード
//  ERR_SUCCESS: 成功
//  それ以外: エラー
//--------------------------------------------------------
int CRSWF62Dlg::enterCmdMode(CString address, BOOL isFromSearch)
{
	int err;
	passwordDialog passwordDlg;

	// パスワード設定時クローズ処理が遅れて失敗することがあるので、
	// 閉じるきっかけに1回ソケットの開け閉めをする
	openSocket(address);
	endSocket(g_tcpSock);

	// ソケットオープン
	err = openSocket(address);
	if(err != 0)
	{
		return err;
	}

	// "comm remote"の値が空の場合receiveSockData()で20秒の待ちが発生するので、
	// 初回の受信のみノンブロッキングで行う
	memset(g_readData, 0, sizeof(g_readData));
	u_long arg = SOCK_ARG_NON_BLOCKING;
	ioctlsocket(g_tcpSock, FIONBIO, &arg);
	Sleep(1000);
	err = receiveSockData(FALSE);
	// ブロッキングに戻す
	arg = SOCK_ARG_BLOCKING;
	ioctlsocket(g_tcpSock, FIONBIO, &arg);
	if(err != 0)
	{
		// "comm remote"の値が空の場合ここで失敗して以降の関数も失敗するため
		// ソケットを開きなおす
		endSocket(g_tcpSock);
		err = openSocket(address);
		if(err != 0)
		{
			return err;
		}
	}
	// パスワードが設定されていればバスワード入力ダイアログを表示
	if(strstr(g_readData, "PASS?") != NULL)
	{
		if(isFromSearch == TRUE)
		{
			endSocket(g_tcpSock);
			return ERR_LOCKED_DEVICE;
		}
		else
		{
			if(m_devPassword == L"")
			{
				passwordDlg.DoModal();
			}
			if(sendSockData(m_devPassword) == SOCKET_ERROR)
			{
				AfxMessageBox(IDS_STRING_WRONG_PASSWORD);
				m_devPassword.Empty();
				return SOCKET_ERROR;
			}
			err = receiveSockData();
			if(err != 0)
			{
				endSocket(g_tcpSock);
				AfxMessageBox(IDS_STRING_WRONG_PASSWORD);
				m_devPassword.Empty();
				return err;
			}
			// パスワード間違い
			if(g_readData[0] == 0)
			{
				endSocket(g_tcpSock);
				AfxMessageBox(IDS_STRING_WRONG_PASSWORD);
				m_devPassword.Empty();
				return ERR_WRONG_PASSWD;
			}
		}
	}

	// コマンドモードの前後には250msec待つ必要がある
	Sleep(MSEC_250);
	// コマンドモードに突入
	if(sendSockData(L"$$$") == SOCKET_ERROR)
	{
		endSocket(g_tcpSock);
		return SOCKET_ERROR;
	}
	Sleep(MSEC_250);

	// コマンドモードに突入した際の返答を捨てる
	err = receiveSockData();
	if(err != 0)
	{
		endSocket(g_tcpSock);
		return err;
	}

	return ERR_SUCCESS;
}

//--------------------------------------------------------
//内容
// RS-WF62にソケットで文字列を送信
// 送信対象とソケット通信を確立した状態で使用のこと
//引数
// CString g_sendData: 送信文字列
//戻り値
// エラーコード
//  ERR_SUCCESS: 成功
//  それ以外: エラー
//--------------------------------------------------------
int CRSWF62Dlg::sendSockData(CString sendData)
{
	// 受信スレッドがあるときは文字列をそのまま、
	// ないときは末尾に改行を追加する
	if(isSockTerminal == FALSE)
	{
		sendData += "\r\n";
	}

	char cSendStr[SEND_STR_SIZE] = {0};
	strcpy_s(cSendStr, CT2CA(sendData));
	if(sendto(g_tcpSock, cSendStr, strlen(cSendStr), 0, (struct sockaddr *)&g_server, sizeof(g_server)) == SOCKET_ERROR)
	{
		return SOCKET_ERROR;
	}

	return ERR_SUCCESS;
}

//--------------------------------------------------------
//内容
// 文字列から目的のパラメータの値を切り出す
//引数
// CString orgString: 該当パラメータを含む文字列
//                    (e.g. "get xx"コマンドで取得したパラメータ一覧)
// CString targetPara: 値を取得するパラメータ名
//戻り値
// パラメータの値
//--------------------------------------------------------
CString CRSWF62Dlg::getParaValue(CString orgString, CString targetPara)
{
	CString retStr;
	CString tmpStr;
	int currentPos = 0;
	// 1行ずつ読み込み
	tmpStr = orgString.Tokenize(L"\r\n", currentPos);
	while(tmpStr != L"")
	{
		if((tmpStr.Find(targetPara)) != -1)
		{
			// パラメータ名と"="以降の文字列を抽出
			retStr = tmpStr.Mid(targetPara.GetLength()+1);
			break;
		}
		tmpStr = orgString.Tokenize(L"\r\n", currentPos);
	}
	return retStr;
}

//--------------------------------------------------------
//内容
// コントロールの有効/無効を切り替え
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::switchDisable(void)
{
	UpdateData(TRUE);
	BOOL isEnabledTcp = FALSE;
	BOOL isEnabledCom = FALSE;

	// TCP通信が可能なRS-WF62がある
	if(m_deviceList.GetItemCount() != 0)
	{
		isEnabledTcp = TRUE;
	}
	GetDlgItem(IDC_LIST_DEVICE)->EnableWindow(isEnabledTcp);
	GetDlgItem(IDC_BUTTON_LED)->EnableWindow(isEnabledTcp);
	GetDlgItem(IDC_BUTTON_CONNECT_TELNET)->EnableWindow(isEnabledTcp);

	// COMポートがある
	if(m_comList.GetCount() != 0)
	{
		isEnabledCom = TRUE;
	}
	GetDlgItem(IDC_BUTTON_CONNECT_COM)->EnableWindow(isEnabledCom);
	GetDlgItem(IDC_COMBO_COM)->EnableWindow(isEnabledCom);
	GetDlgItem(IDC_STATIC_PORT)->EnableWindow(isEnabledCom);

	UpdateData(FALSE);
	Invalidate(FALSE);
}

//--------------------------------------------------------
//内容
// 「再スキャン」ボタンのクリックイベント
// RS-WF62の再検索を行う
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::OnBnClickedButtonRescan()
{
	m_selectedDev.Empty();
	UpdateData(FALSE);
	Invalidate(FALSE);

	switchDisable();
	if(m_deviceScanThread == NULL)
	{
		m_deviceScanThread = AfxBeginThread(deviceScanThread, this);
	}
}

//--------------------------------------------------------
//内容
// 「LED確認」ボタンのクリックイベント
// 5秒間RS-WF62の緑のLEDを消灯させる
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::OnBnClickedButtonLed()
{
	if(checkSelection() == FALSE)
	{
		return;
	}

	switchDisable();

	m_ledCheckThread = AfxBeginThread(ledCheckThread, this);
}


//--------------------------------------------------------
//内容
// 「TCP/IPで接続」ボタンのクリックイベント
//  選択中のIPアドレスとソケットで接続し、受信スレッドを開始する
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::OnBnClickedButtonConnectTelnet()
{
	if(checkSelection() == FALSE)
	{
		return;
	}

	terminalDialog terminalDlg;
	terminalDlg.m_connectType = CONNECT_SOCK;
	terminalDlg.DoModal();
}

//--------------------------------------------------------
//内容
// 「COM」ボタンのクリックイベント
//  選択中のCOMポートを開く
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::OnBnClickedButtonConnectCom()
{
	UpdateData(TRUE);
	terminalDialog terminalDlg;
	terminalDlg.m_connectType = CONNECT_COM;
	terminalDlg.DoModal();
}

//--------------------------------------------------------
//内容
// デバイスリストの選択イベント
//  選択中のデバイスの情報を格納
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::OnLvnItemchangedListDevice(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

	UpdateData(TRUE);

	m_devPassword.Empty();
	g_selectedIp = getSelectedDevice(DEV_LIST_ADDRESS);
	m_selectedDev = getSelectedDevice(DEV_LIST_NAME);

	UpdateData(FALSE);
	Invalidate(FALSE);

	*pResult = 0;
}

//--------------------------------------------------------
//内容
// COMポートを検索し、コンボボックスに表示する
// (すべてのCOMポートを列挙し、RS-WF62かどうかの判定は行わない)
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::searchCom(void)
{
	// COMポートの一覧取得
	int i, j;
	HKEY hKey = NULL;
	DWORD dwCount = 0;
	TCHAR cNameBuff[COM_NAME_SIZE];
	BYTE byValueBuff[COM_NAME_SIZE];
	DWORD dwNameBuffSize = 0;
	DWORD dwValueBuffSize = 0;
	DWORD dwType = 0;
	LONG regErr;
	CString strTmp;

	// レジストリのオープン
	regErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hKey);
	if(regErr == ERROR_SUCCESS)
	{
		// 要素数を取得
		RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwCount, NULL, NULL, NULL, NULL);
		if(regErr == ERROR_SUCCESS)
		{
			for(i=0; i<(int)dwCount; i++)
			{
				dwNameBuffSize = sizeof(cNameBuff);
				dwValueBuffSize = sizeof(byValueBuff);

				// 値を取得
				RegEnumValue(hKey, i, &cNameBuff[0], &dwNameBuffSize, NULL, &dwType, &byValueBuff[0], &dwValueBuffSize);
				if(regErr == ERROR_SUCCESS)
				{
					strTmp.Empty();
					// 1バイトおきに0が入るので取り除く
					for(j=0; j<(int)dwValueBuffSize; j+=2)
					{
						strTmp += byValueBuff[j];
					}
					m_comList.InsertString(-1, strTmp);
				}
			}
		}
	}
	RegCloseKey(hKey);
	if(m_comList.GetCount() != 0)
	{
		m_comList.SetCurSel(0);
	}
}

//--------------------------------------------------------
//内容
// リストコントロールの選択をチェック
//引数
// なし
//戻り値
// 未選択、ファームVer.2.38.3のデバイス選択時FALSE
//--------------------------------------------------------
BOOL CRSWF62Dlg::checkSelection(void)
{
	if(m_deviceList.GetFirstSelectedItemPosition() == NULL)
	{
		AfxMessageBox(IDS_STRING_NOT_SELECTED);
		return FALSE;
	}
	if(getSelectedDevice(DEV_LIST_VERSION) == OLD_VER)
	{
		AfxMessageBox(IDS_STRING_INVALID_VERSION);
		return FALSE;
	}
	return TRUE;
}


//--------------------------------------------------------
//内容
// 選択中のRS-WF62の緑のLEDの状態を
// 消灯→点灯→消灯→点灯→消灯→点灯と1秒ずつ変更する
//引数
// なし
//戻り値
// なし
//--------------------------------------------------------
void CRSWF62Dlg::blinkLed(void)
{
	if(enterCmdMode(g_selectedIp) == ERR_SUCCESS)
	{
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_2);
		Sleep(MSEC_1000);
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_3);
		Sleep(MSEC_1000);
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_2);
		Sleep(MSEC_1000);
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_3);
		Sleep(MSEC_1000);
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_2);
		Sleep(MSEC_1000);
		sendSockData(CMD_SET_SYS_LED + (CString)CMD_PARA_IOFUNC_3);
		Sleep(WAIT_CMD_TIME);
		endSocket(g_tcpSock);
	}
}
