//---------------------------------------------------------------------------
// For SELMA200, 20180503, kim, 변경사항 activemode = 2 // 1-usb all 2-hcb-usb/tcp-spb  3-tcp all
//---------------------------------------------------------------------------
#include "TcpComm.h"
#include "Environment.h"
#include <vcl.h>
#pragma hdrstop

#include "common.h"
#include <stdio.h>
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------
#pragma package(smart_init)

#define COMMAND_TYPE_NORMAL		0
#define COMMAND_TYPE_EXTRA		1

#define COMMAND_STATUS_NONE				0
#define COMMAND_STATUS_PENDING			1
#define COMMAND_STATUS_READY			2
#define COMMAND_STATUS_SENDING			3
#define COMMAND_STATUS_WAITING			4
#define COMMAND_STATUS_POLLING			5
#define COMMAND_STATUS_RECEIVE_READY	6
#define COMMAND_STATUS_RECEVING			7
#define COMMAND_STATUS_COMPLETE			8

#define OVERLAP_STATE_NONE				0
#define OVERLAP_STATE_PENDING			1
#define OVERLAP_STATE_READY				2
#define OVERLAP_STATE_POLLING			3
#define OVERLAP_STATE_COMPLETE			4

#define DF_STATUS_IDLE				0
#define DF_STATUS_SENDING			1
#define DF_STATUS_RECEVING			2

#define DEFAULT_WAITING_TIME           15000 //300000		// 3000ms
#define DEVICE_RESET_WAIT_TIME		100000 
//#define DEVICE_RESET_WAIT_TIME		10000

#define BULKUSB_IOCTL_INDEX             0x0000
#define IOCTL_SETUP_IO 				CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_BULKUSB_RESET_PIPE 	CTL_CODE(FILE_DEVICE_UNKNOWN, BULKUSB_IOCTL_INDEX + 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_BULKUSB_RESET_DEVICE  CTL_CODE(FILE_DEVICE_UNKNOWN, BULKUSB_IOCTL_INDEX + 1, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define min(x, y)	(x < y ? x : y)
//---------------------------------------------------------------------------
//TCommandUnit
//---------------------------------------------------------------------------

__fastcall TCommandUnit::TCommandUnit()
{
	Complete = false;
	Result = true;
	Type = COMMAND_TYPE_NORMAL;
	Overlapped = false;
	CompletionRoutine = NULL;
	DFStatus = DF_STATUS_IDLE;
	TransferTimeOut = DEFAULT_WAITING_TIME;
}
//---------------------------------------------------------------------------
__fastcall TTcpComm::TTcpComm()
{
	activemode = ApplicationStartOption.SelectedCommType; // 1-usb all 2-hcb-usb/tcp-spb  3-tcp all
	DeviceHandle = NULL;
	FConnected = false;
	LastErrorMessage = "";
	FDeviceName = "";

	FAbort = false;
	AutoRecover = false;
	FMaxWaitingTime = DEFAULT_WAITING_TIME;
	PacketID = 0;
	FIgnoreState = false;
	FVirtualState = false;

	FBusy = false;
	ProtocolVer = NEW_USB_PROTOCOL_VER;

	ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
//---------------------------------------------------------------------------
__fastcall TTcpComm::~TTcpComm()
{
	if (FConnected)
		USBDisconnect();
	CloseHandle(ol.hEvent);

	TCPDisconnect();


	if (m_bSocketInit)
		WSACleanup();
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::TCPConnect(String sIpAddr, int nPort)
{
	int 	    nRet;
	BYTE	    byIpAddr[4];
	WSADATA     wsaData;
	WORD        wVersionRequested;
	struct      timeval tval;
	BOOL        optVal = 1;//SO_KEEPALIVE;
	int         option = 1;
	struct      linger solinger = { 1, 0 };
	SOCKADDR_IN sockAddr;


	FIgnoreState = false;
	m_sIpAddr = sIpAddr;
	m_nPort = nPort;
	if (FConnected) TCPDisconnect();

	// WSA Init
	if (!m_bSocketInit)
	{
		wVersionRequested = MAKEWORD(2, 2);
		nRet = WSAStartup(wVersionRequested, &wsaData);
		if (nRet != 0) return false;

		if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
		{
			WSACleanup();
			return false;
		}
		m_bSocketInit = true;
	}

	// 소켓이 있다면
	if (m_hSocket != INVALID_SOCKET) TCPDisconnect();

	// 소켓 생성==========================
	if (m_hSocket == INVALID_SOCKET)
	{
		m_hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

		// 소켓 생성에 실패하면
		if (m_hSocket == INVALID_SOCKET) return false;

		// 소켓에 기본 정보 bind
		memset(&sockAddr, 0, sizeof(SOCKADDR_IN));
		sockAddr.sin_family = AF_INET;
		sockAddr.sin_port = htons(nPort);
		sockAddr.sin_addr.s_addr = inet_addr(LPSTR(sIpAddr.c_str()));
	}

	// 연결제한 시간설정=================
	tval.tv_sec = 0;
	tval.tv_usec = 500000;
	nRet = connect_nonb(tval, sockAddr);
	if (nRet != 0) return false;

	// Socket 환경설정==================
	// Linger Option Setup
	nRet = setsockopt(m_hSocket, SOL_SOCKET, SO_LINGER, (char *)&solinger, sizeof(struct linger));
	if (nRet == SOCKET_ERROR) return false;

	// 소켓 재사용 옵션
	nRet = setsockopt(m_hSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option));
	if (nRet == SOCKET_ERROR) return false;

	// Nagle 알고리즘 off
	nRet = setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&optVal, sizeof(BOOL));
	if (nRet == SOCKET_ERROR) return false;


	// Event 생성===================
	if (m_hEvent == NULL)
	{
		m_hEvent = WSACreateEvent();
		if (m_hEvent == NULL) return false;
	}

	// Client 소켓에 Event 매칭
	nRet = WSAEventSelect(m_hSocket, m_hEvent, FD_READ | FD_CLOSE);
	if (nRet == SOCKET_ERROR) return false;

	FConnected = true;
	return true;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::USBConnect(AnsiString deviceName)
{
	bool returnValue = true;
	if (FConnected) USBDisconnect();
	//DeviceHandle = CreateFile(deviceName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	DeviceHandle = CreateFileA(deviceName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	if (DeviceHandle == INVALID_HANDLE_VALUE)
	{
		LastErrorMessage = SysErrorMessage(GetLastError());
		returnValue = false;
	}

	if (returnValue == true)
	{
		FDeviceName = deviceName;
		FConnected = true;
	}
	else
	{
		FDeviceName = "";
		FConnected = false;
	}

	FIgnoreState = false;
	return returnValue;
}
//---------------------------------------------------------------------------
int __fastcall TTcpComm::connect_nonb(struct timeval tval, SOCKADDR_IN  sockAddr)
{
	fd_set          rset, wset;
	int             n, error;
	int       		len;
	int 			nonblocking = 1;

	ioctlsocket(m_hSocket, FIONBIO, (unsigned long*)&nonblocking);

	n = connect(m_hSocket, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
	if (n < 0)
	{

		errno = WSAGetLastError();
		if (errno != WSAEINPROGRESS && errno != WSAEWOULDBLOCK) return(-1);
	}

	/* Do whatever we want while the connect is taking place. */

	if (n == 0)
	{
		goto done;    /* connect completed immediately */
	}
	FD_ZERO(&rset);
	FD_SET(m_hSocket, &rset);
	wset = rset;
	n = select((int)(m_hSocket + 1), &rset, &wset, NULL, ((tval.tv_sec > 0) || (tval.tv_usec > 0)) ? &tval : NULL);

	if (n == 0)
	{

		FD_CLR(m_hSocket, &rset);
		return(-1);
	}
	if (FD_ISSET(m_hSocket, &rset) || FD_ISSET(m_hSocket, &wset))
	{
		len = sizeof(error);
		if (getsockopt(m_hSocket, SOL_SOCKET, SO_ERROR, (char *)(&error), &len) < 0) return(-1);         /* Solaris pending error */
	}
	else
	{
		return(-1);
	}
done:
	nonblocking = 0;
	ioctlsocket(m_hSocket, FIONBIO, (unsigned long*)&nonblocking);

	FD_CLR(m_hSocket, &rset);

	if (error)
	{
		//Close();        /* just in case */
		errno = error;
		return(-1);
	}
	return(0);
}
//-----------------------------------------------------------------------------------------
bool __fastcall TTcpComm::TCPDisconnect(void)
{
	// Event close
	if (m_hEvent != NULL) WSACloseEvent(m_hEvent);
	m_hEvent = NULL;

	// 소켓 닫기
	if (m_hSocket != INVALID_SOCKET)
	{
		if (FConnected) shutdown(m_hSocket, SD_BOTH);
		closesocket(m_hSocket);
	}
	m_hSocket = INVALID_SOCKET;
	FConnected = false;
	FDeviceName = "";

	return true;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::USBDisconnect(void)
{
	if (DeviceHandle)
	{
		CloseHandle(DeviceHandle);
		DeviceHandle = NULL;
	}
	FConnected = false;
	FDeviceName = "";

	return true;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::RequestCommand(int commIndex, unsigned short command, void *data, unsigned int dataLength,
	void *receiveBuffer, unsigned int receiveBufferMaxSize)
{
	if (FBusy)
	{
		LastErrorMessage = "Communication is in progress.";
		return false;
	}
	FBusy = true;
	if (!Connected)
	{
		LastErrorMessage = "The device is not connected.";
		FBusy = false;
		return false;
	}
	else if (FIgnoreState)
	{
		FBusy = false;
		return false;
	}

	// make command unit
	TCommandUnit *aCommandUnit = new TCommandUnit;
	aCommandUnit->Type = COMMAND_TYPE_NORMAL;
	aCommandUnit->Command = command;
	aCommandUnit->ShortData = data;
	aCommandUnit->ShortDataLength = dataLength;
	aCommandUnit->ReceiveBuffer = receiveBuffer;
	aCommandUnit->ReceiveBufferMaxSize = receiveBufferMaxSize;
	aCommandUnit->TransferTimeOut = FMaxWaitingTime;
	aCommandUnit->Status = COMMAND_STATUS_PENDING;


	ProcessCommand(commIndex, aCommandUnit);


	bool result = aCommandUnit->Result;
	delete aCommandUnit;
	if (result == false)
	{
		//		RecoverDevice();
	}
	FBusy = false;
	return result;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::RequestCommandEx(int commIndex, unsigned short command, void *shortData, unsigned int shortDataLength,
	void *longData, unsigned int longDataLength, void *receiveBuffer, unsigned int receiveBufferMaxSize,
	TUSBCompletionRoutine CompletionRoutine, TOverlapIOStr *OverlapIOStr)
{
	if (FBusy)
	{
		LastErrorMessage = "Communication is in progress.";
		return false;
	}
	FBusy = true;

	if (!Connected)
	{
		LastErrorMessage = "The device is not connected.";
		FBusy = false;
		return false;
	}
	else if (FIgnoreState)
	{
		FBusy = false;
		return false;
	}

	// make command unit
	TCommandUnit *aCommandUnit = new TCommandUnit;
	aCommandUnit->Type = COMMAND_TYPE_EXTRA;
	aCommandUnit->Command = command;
	aCommandUnit->ShortData = shortData;
	aCommandUnit->ShortDataLength = shortDataLength;
	aCommandUnit->LongData = longData;
	aCommandUnit->LongDataLength = longDataLength;
	aCommandUnit->ReceiveBuffer = receiveBuffer;
	aCommandUnit->ReceiveBufferMaxSize = receiveBufferMaxSize;
	aCommandUnit->TransferTimeOut = FMaxWaitingTime;
	aCommandUnit->Status = COMMAND_STATUS_PENDING;
	if (CompletionRoutine)
	{
		aCommandUnit->Overlapped = true;
		aCommandUnit->OverlapState = OVERLAP_STATE_PENDING;
		aCommandUnit->CompletionRoutine = CompletionRoutine;
		aCommandUnit->OverlapIOStr = OverlapIOStr;
	}
	else
	{
		aCommandUnit->Overlapped = false;
	}

	// wait until command queue is empty
	unsigned long pollingStartTime = GetTickCount();

	pollingStartTime = GetTickCount();

	ProcessCommand(commIndex, aCommandUnit);

	bool result = aCommandUnit->Result;
	delete aCommandUnit;
	FBusy = false;
	return result;
}
//---------------------------------------------------------------------------
AnsiString __fastcall TTcpComm::GetLastErrorMessage(void)
{
	return LastErrorMessage;
}
//---------------------------------------------------------------------------
void __fastcall TTcpComm::SetDefaultMaxWaitingTime(void)
{
	FMaxWaitingTime = DEFAULT_WAITING_TIME;
}
//---------------------------------------------------------------------------
void __fastcall TTcpComm::ProcessCommand(int commIndex, TCommandUnit *aCommandUnit)
{
	unsigned int responseData[1];
	unsigned int receivedLength;
	unsigned int lastStatusTime;
	lastStatusTime = GetTickCount();

	while (!FAbort)
	{
		bool bSuccess = true;

		if (aCommandUnit->Status == COMMAND_STATUS_PENDING)
		{
			aCommandUnit->Status = COMMAND_STATUS_READY;
		}

		if (aCommandUnit->Status == COMMAND_STATUS_READY || aCommandUnit->Status == COMMAND_STATUS_WAITING)
		{
			aCommandUnit->Status = COMMAND_STATUS_SENDING;
			if (aCommandUnit->Type == COMMAND_TYPE_NORMAL)
			{
				aCommandUnit->DFStatus = DF_STATUS_SENDING;
				if (!SendCommand(commIndex, aCommandUnit->Command, aCommandUnit->ShortData, aCommandUnit->ShortDataLength, CMD_TYPE_APP_REQUEST, aCommandUnit->TransferTimeOut))
				{
					bSuccess = false;
				}
				if (bSuccess)
				{
					aCommandUnit->DFStatus = DF_STATUS_RECEVING;
					if (!ReceiveCommand(commIndex, responseData, 4, &receivedLength, aCommandUnit->TransferTimeOut))
					{
						bSuccess = false;
					}
				}
				aCommandUnit->DFStatus = DF_STATUS_IDLE;
				aCommandUnit->Result = bSuccess;
			}
			else		// ex type
			{
				aCommandUnit->DFStatus = DF_STATUS_SENDING;
				if (!SendCommandEx(commIndex, aCommandUnit->Command, aCommandUnit->ShortData, aCommandUnit->ShortDataLength,
					aCommandUnit->LongData, aCommandUnit->LongDataLength, CMD_TYPE_APP_REQUEST, aCommandUnit->TransferTimeOut))
				{
					bSuccess = false;
				}
				if (bSuccess)
				{
					aCommandUnit->DFStatus = DF_STATUS_RECEVING;
					if (!ReceiveCommand(commIndex, responseData, 4, &receivedLength, aCommandUnit->TransferTimeOut))
					{
						bSuccess = false;
					}
				}
				aCommandUnit->DFStatus = DF_STATUS_IDLE;
				aCommandUnit->Result = bSuccess;
			}

			if (bSuccess)
			{
				if (responseData[0])
				{
					aCommandUnit->Status = COMMAND_STATUS_POLLING;
				}
				else
				{
					aCommandUnit->Status = COMMAND_STATUS_WAITING;
				}
			}
			else
			{
				Comm_Connect(commIndex);
				aCommandUnit->Status = COMMAND_STATUS_COMPLETE;
			}
		}
		else if (aCommandUnit->Status == COMMAND_STATUS_POLLING)
		{
			aCommandUnit->DFStatus = DF_STATUS_SENDING;
			if (!SendCommand(commIndex, aCommandUnit->Command, NULL, 0, CMD_TYPE_APP_POLLING_RESULT, aCommandUnit->TransferTimeOut))
			{
				bSuccess = false;
			}
			if (bSuccess)
			{
				aCommandUnit->DFStatus = DF_STATUS_RECEVING;
				if (!ReceiveCommand(commIndex, responseData, 4, &receivedLength, aCommandUnit->TransferTimeOut))
				{
					bSuccess = false;
				}
			}
			aCommandUnit->DFStatus = DF_STATUS_IDLE;
			if (bSuccess)
			{
				if (responseData[0])
				{
					aCommandUnit->Status = COMMAND_STATUS_RECEIVE_READY;
				}
			}
			else
			{
				Comm_Connect(commIndex);
				aCommandUnit->Status = COMMAND_STATUS_COMPLETE;
			}
		}
		else if (aCommandUnit->Status == COMMAND_STATUS_RECEIVE_READY)
		{
			aCommandUnit->Status = COMMAND_STATUS_RECEVING;
			aCommandUnit->DFStatus = DF_STATUS_SENDING;
			if (!SendCommand(commIndex, aCommandUnit->Command, NULL, 0, CMD_TYPE_APP_RECEIVE_RESULT, aCommandUnit->TransferTimeOut))
			{
				bSuccess = false;
			}
			if (bSuccess)
			{
				if (aCommandUnit->ReceiveBufferMaxSize % 4 == 0)
				{
					aCommandUnit->DFStatus = DF_STATUS_RECEVING;
					if (!ReceiveCommand(commIndex, aCommandUnit->ReceiveBuffer, aCommandUnit->ReceiveBufferMaxSize, &receivedLength, aCommandUnit->TransferTimeOut))
					{
						bSuccess = false;
					}
				}
				else
				{
					unsigned char *tempBuffer = new unsigned char[(aCommandUnit->ReceiveBufferMaxSize + 3) / 4 * 4];
					aCommandUnit->DFStatus = DF_STATUS_RECEVING;
					if (!ReceiveCommand(commIndex, tempBuffer, (aCommandUnit->ReceiveBufferMaxSize + 3) / 4 * 4, &receivedLength, aCommandUnit->TransferTimeOut))
					{
						bSuccess = false;
					}
					else
					{
						if (receivedLength > aCommandUnit->ReceiveBufferMaxSize)
						{
							receivedLength = aCommandUnit->ReceiveBufferMaxSize;
						}
						memcpy(aCommandUnit->ReceiveBuffer, tempBuffer, receivedLength);
					}
					delete[] tempBuffer;
				}
			}
			aCommandUnit->DFStatus = DF_STATUS_IDLE;

			if (bSuccess)
			{
				aCommandUnit->Result = true;
				aCommandUnit->Complete = true;
				aCommandUnit->Status = COMMAND_STATUS_COMPLETE;
			}
			else
			{
				Comm_Connect(commIndex);
				aCommandUnit->Status = COMMAND_STATUS_COMPLETE;
			}
		}
		else if (aCommandUnit->Status == COMMAND_STATUS_COMPLETE)
		{
			break;
		}
		//Sleep(100);
	}
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::SendData(int commIndex, void *data, int dataLength, unsigned int writeStartTime, int timeout)
{
	//Application->ProcessMessages();

	if (activemode == 1 || (activemode == 2 && commIndex == 0))
	{
		DWORD dwWriteSize, error;
		bool returnValue = true;
		if (!WriteFile(DeviceHandle, data, dataLength, &dwWriteSize, &ol))
		{
			do
			{
				error = GetLastError();
				if (error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE)
				{
					if (GetTickCount() - writeStartTime > timeout)
					{
						LastErrorMessage = "USB Write timeout occurs";
						returnValue = false;
						break;
					}
				}
				else
				{
					LastErrorMessage = "USB Write Failed";
					returnValue = false;
					break;
				}
			}
			while (GetOverlappedResult(DeviceHandle, &ol, &dwWriteSize, FALSE) == FALSE);
		}

		if (returnValue == true && dwWriteSize != dataLength)
		{
			LastErrorMessage = "USB read timeout occurs (S)";
			returnValue = false;
		}
		return returnValue;
	}
	else
	{
		int     nLen;
		int     nerror;

		if (m_hSocket == INVALID_SOCKET)
		{
			LastErrorMessage = "TCP invalid socket";
			return false;
		}

		nLen = send(m_hSocket, (char*)data, dataLength, 0);//MSG_DONTROUTE|MSG_OOB);
		if (nLen == SOCKET_ERROR)
		{
			nerror = WSAGetLastError();

			// 연결에 관련된 에러만 다시 Reconnect시도
			if (nerror == WSAENETDOWN || nerror == WSAENETRESET ||
				nerror == WSAENOTCONN || nerror == WSAECONNRESET || nerror == WSAEWOULDBLOCK)
			{

				LastErrorMessage = "TCP diconnected..";
				return false;
			}
		}
		return (nLen == dataLength);
	}
}
//---------------------------------------------------------------------------

bool __fastcall TTcpComm::SendCommand(int commIndex, unsigned short command, void *data, unsigned int dataLength,
	unsigned short type, int timeout)
{
	bool        returnValue = true;
	int         transferDataLength = (dataLength + 3) / 4 * 4;

	TXDataHeader.Header = USB_PACKET_HEADER;
	TXDataHeader.Command = command;
	TXDataHeader.SmallDataLength = transferDataLength;
	TXDataHeader.LargeDataLength = 0;
	TXDataHeader.RequestKind = type;

	unsigned int writeStartTime = GetTickCount();

	try
	{
		// send header
		if (!SendData(commIndex, &TXDataHeader, sizeof(TUSBDataHeader), writeStartTime, timeout))
		{
 			throw Exception("Can not Send Header");
		}

		int remainDataSize = transferDataLength;
		unsigned char *pTransferData = (unsigned char *)data;

		while (remainDataSize > 0)
		{
			int sendDataLength = min(remainDataSize, USB_MAX_TRANSFER_SIZE);
			if (!SendData(commIndex, pTransferData, sendDataLength, writeStartTime, timeout))
			{
				throw Exception("Can not Send Data");
			}
			pTransferData += sendDataLength;
			remainDataSize -= sendDataLength;
		}
	}
	catch (...)
	{
		returnValue = false;
	}
	return returnValue;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::SendCommandEx(int commIndex, unsigned short command, void *shortData, unsigned int shortDataLength,
	void *longData, unsigned int longDataLength, unsigned short type, int timeout)
{
	bool returnValue = true;
	int transferShortDataLength = (shortDataLength + 3) / 4 * 4;
	int transferLongDataLength = (longDataLength + 3) / 4 * 4;

	TXDataHeader.Header = USB_PACKET_HEADER;
	TXDataHeader.Command = command;
	TXDataHeader.SmallDataLength = transferShortDataLength;
	TXDataHeader.LargeDataLength = transferLongDataLength;
	TXDataHeader.RequestKind = type;


	unsigned char *pTransferData;
	unsigned int writeStartTime = GetTickCount();

	try
	{
		// send header
		if (!SendData(commIndex, &TXDataHeader, sizeof(TUSBDataHeader), writeStartTime, timeout))
		{
			throw Exception("Can not Send Header");
		}

		int remainShortDataSize = transferShortDataLength;
		pTransferData = (unsigned char *)shortData;
		while (remainShortDataSize > 0)
		{
			int sendDataLength = min(remainShortDataSize, USB_MAX_TRANSFER_SIZE);
			if (!SendData(commIndex, pTransferData, sendDataLength, writeStartTime, timeout))
			{
				throw Exception("Can not Send Small Data");
			}

      Sleep(500);
			pTransferData += sendDataLength;
			remainShortDataSize -= sendDataLength;
		}

		int remainLongDataSize = transferLongDataLength;
		pTransferData = (unsigned char *)longData;
		while (remainLongDataSize > 0)
		{
			int sendDataLength = min(remainLongDataSize, USB_MAX_TRANSFER_SIZE);
			if (!SendData(commIndex, pTransferData, sendDataLength, writeStartTime, timeout))
			{
				throw Exception("Can not Send Large Data");
			}

      Sleep(500);
      pTransferData += sendDataLength;
			remainLongDataSize -= sendDataLength;
		}
	}
	catch (...)
	{
		returnValue = false;
	}

	return returnValue;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::ReceiveData(int commIndex, void *data, int dataLength, int &readCount, unsigned int readStartTime, int timeout)
{
	//Application->ProcessMessages();
	if (activemode == 1 || (activemode == 2 && commIndex == 0))
	{
		DWORD dwReadSize, error;
		bool returnValue = true;

		if (!ReadFile(DeviceHandle, data, dataLength, &dwReadSize, &ol))
		{
			do
			{
				error = GetLastError();
				if (error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE)
				{
					if (GetTickCount() - readStartTime > timeout)
					{
		        LastErrorMessage = "USB read timeout occurs";
						returnValue = false;
						break;
					}
				}
				else
				{
		      LastErrorMessage = "USB reading failure";
					returnValue = false;
					break;
				}
			}
			while (GetOverlappedResult(DeviceHandle, &ol, &dwReadSize, FALSE) == FALSE);
		}

		if (returnValue == true && dataLength != dwReadSize) // dwReadSize 0 헤더 사이즈 16
		{
			LastErrorMessage = "USB read size error";
			returnValue = false;
		}
		readCount = dwReadSize;
    //*readCount = dwReadSize;
		return returnValue;
	}
	else
	{
		  int 	            nRet;
			ULONG 	            dwLen = 0;
			int                 index;
			bool                bResult;
      bool                bRecevDone = false;
      int                 iRecevSize = 0;
			WSANETWORKEVENTS    NetworkEvent;

			if (m_hSocket == INVALID_SOCKET) return false;

			//index = WSAWaitForMultipleEvents(1,&m_hEvent,FALSE,WSA_INFINITE,FALSE);
			// 응답 대기시간 동안 대기
			index = WSAWaitForMultipleEvents(1, &m_hEvent, FALSE, timeout, FALSE);

			// Reset the signaled event
			bResult = WSAResetEvent(m_hEvent);
			if (!bResult)
			{
        //cout<< "E12"<< endl;
				//if( m_bTerminateCheck ) break;
				//WSAGetLastError();
				LastErrorMessage = "TCP  Error. Configsocket";
				TCPConnect(m_sIpAddr, m_nPort);
				return false;
			}

			switch (index)
			{
			case WSA_WAIT_FAILED:
			case WSA_WAIT_TIMEOUT:
				LastErrorMessage = "TCP read timeout occurs";
				// Error Occured
				return false;

			case WAIT_IO_COMPLETION:
				break;
			}

			nRet = WSAEnumNetworkEvents(m_hSocket, m_hEvent, &NetworkEvent);

			if (nRet == SOCKET_ERROR)
			{
				LastErrorMessage = "TCP Read Error.";
				return false;
			}

			nRet = 0;
			// Data Input

			if (NetworkEvent.lNetworkEvents & FD_CLOSE)
			{
        //cout<< "E15"<< endl;
				LastErrorMessage = "TCP disconnected..";
				return false;
			}

			if (NetworkEvent.lNetworkEvents & FD_READ)
			{
				nRet = ioctlsocket(m_hSocket, FIONREAD, &dwLen);

				if (nRet == SOCKET_ERROR || dwLen == 0)
				{
					LastErrorMessage = "TCP Read Error.";
					return false;
				}

				// 읽은 갯수가 더 많으면
				if (dwLen > dataLength) dwLen = dataLength;

        int k = dwLen;

				nRet = recv(m_hSocket, (char*)data, dwLen, 0);

        int u = dwLen;

				if (nRet == SOCKET_ERROR)
				{
					LastErrorMessage = "TCP Read Error.";
					return false;
				}

        if(dwLen == 0)
        {
          int j = 0;
        }
			}
			readCount = dwLen;

      if(readCount == 0)
      {
        //cout<< "ZERO2" << endl;
        //cout<< dwLen << endl;
        //cout<< iRecevSize << endl;
        //cout<< dataLength << endl;
      }
			return true;

	}
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::ReceiveCommand(int commIndex, void *receiveBuffer, unsigned int receiveBufferMaxSize,
	unsigned int *receivedLength, int timeout)
{
	bool            returnValue = true;
	unsigned int    rxCount;
	DWORD           dwReadSize;
	unsigned int    dataSize;
	int             readCount;
	int             dataPos = 0;
	unsigned char   *buffer;
	unsigned int    bufferMaxSize;

	if (receiveBuffer == NULL)
	{
		//buffer = (unsigned char *)RXPacketBuf + USB_PACKET_HEADER_SIZE * sizeof(int);
		//bufferMaxSize = (PACKET_MAX_SIZE - USB_PACKET_HEADER_SIZE) * sizeof(int);
		buffer = NULL;
		bufferMaxSize = 0;
	}
	else
	{
		buffer = (unsigned char *)receiveBuffer;
		bufferMaxSize = receiveBufferMaxSize;
	}

	DWORD           error;
	BOOL            ret;
	bool            bError = false;
	unsigned int    readStartTime = GetTickCount();


	try
	{
		// read Header
		int rr = sizeof(TUSBDataHeader);
    int readHeaderSize;
    readHeaderSize = sizeof(TUSBDataHeader);

    while(readHeaderSize)
    {
      if (!ReceiveData(commIndex, &RXDataHeader, readHeaderSize, readCount, readStartTime, timeout))
      {
      		return false;      
        //cout<< "E25"<<endl;
        //LastErrorMessage = "read size err Header";
        //throw Exception("Can not Receive Header");
      }

      readHeaderSize -= readCount;
    }

    if(readCount == 0)
    {
      //cout << "??"<<endl;
    }

		if (readCount != sizeof(TUSBDataHeader))
		{

      //cout<< "E27"<<endl;
      //cout<< readCount<<endl;
      //cout<< sizeof(TUSBDataHeader)<<endl;
			LastErrorMessage = "read size err Header";
			throw Exception("read size err Header");
		}

		if (RXDataHeader.Header != USB_PACKET_HEADER)
		{
			LastErrorMessage = "Wrong Header";
			throw Exception("Wrong Header");
		}

		if (RXDataHeader.SmallDataLength > 0)
		{
			if (dataPos + RXDataHeader.SmallDataLength <= bufferMaxSize)
			{
				if (!ReceiveData(commIndex, &buffer[dataPos], RXDataHeader.SmallDataLength, readCount, readStartTime, timeout))
				{
                                        LastErrorMessage = "Wrong Header";
					throw Exception("Can not Receive Small Data ");
				}
				dataPos += RXDataHeader.SmallDataLength;
			}
			else
			{
				LastErrorMessage = "To be received data are too large.";
				throw Exception("To be received data are too large.");
			}
		}

		if (RXDataHeader.LargeDataLength > 0)
		{
			if (dataPos + RXDataHeader.LargeDataLength <= bufferMaxSize)
			{
				int remainDataSize = RXDataHeader.LargeDataLength;
				int readLength;
				while (remainDataSize)
				{
					if (activemode == 1 || (activemode == 2 && commIndex == 0))
					{
						if (remainDataSize < USB_MAX_TRANSFER_SIZE)
						{
							readLength = remainDataSize;
						}
						else
						{
							readLength = USB_MAX_TRANSFER_SIZE;
						}
					}
					else {
						if (remainDataSize < TCP_MAX_TRANSFER_SIZE)
						{
							readLength = remainDataSize;
						}
						else
						{
							readLength = TCP_MAX_TRANSFER_SIZE;
						}
					}

					if (!ReceiveData(commIndex, &buffer[dataPos], readLength, readCount, readStartTime, timeout))
					{
						throw Exception("Can not Receive Large Data");
					}
					dataPos += readCount;
					remainDataSize -= readCount;

          if(readCount == 0)
          {
            //cout<< "E99"<<endl;
            //cout<< remainDataSize<<endl;
          }

				}
			}
			else
			{
				LastErrorMessage = "To be received data are too large(L).";
				throw Exception("To be received data are too large(L).");
			}
		}
	}
	catch (...)
	{
		returnValue = false;
	}

	*receivedLength = dataPos;
	return returnValue;
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::RecoverDevice(int commIndex)
{
	/*
	if (activemode == 1 || (activemode == 2 && commIndex == 0))
	{
		bool returnValue;
		DWORD readByte;
		FIgnoreState = false;

		try
		{
			TTransferInfo transferInfo;
			transferInfo.Size = 0;
			transferInfo.Index = 0;
			transferInfo.Type = TRANSFER_INFO_TYPE_RESET;

			if (DeviceHandle)
			{
				if (!CancelIo(DeviceHandle))
				{
  				  throw(Exception(SysErrorMessage(GetLastError())));
				}

				if (!DeviceIoControl(DeviceHandle, IOCTL_SETUP_IO, &transferInfo, sizeof(TTransferInfo), NULL, 0, &readByte, &ol))
				{
					throw(Exception("<IoControl failure> " + SysErrorMessage(GetLastError())));
				}
				if (!CloseHandle(DeviceHandle))
				{
					throw(Exception(SysErrorMessage(GetLastError())));
				}
				DeviceHandle = NULL;
				Sleep(DEVICE_RESET_WAIT_TIME);
			}
			Sleep(DEVICE_RESET_WAIT_TIME);
			DeviceHandle = CreateFileA(FDeviceName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
			if (DeviceHandle == INVALID_HANDLE_VALUE)
			{
				FConnected = false;
				returnValue = false;
				throw(Exception(SysErrorMessage(GetLastError())));
			}
			else
			{
				FConnected = true;
				returnValue = true;
			}
		}
		catch (Exception &ec)
		{
 			LastErrorMessage = ec.Message;
			if (DeviceHandle != INVALID_HANDLE_VALUE && DeviceHandle != NULL)
			{
				CloseHandle(DeviceHandle);
				DeviceHandle = NULL;
				Sleep(DEVICE_RESET_WAIT_TIME);
			}
			DeviceHandle = CreateFileA(FDeviceName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
			if (DeviceHandle == INVALID_HANDLE_VALUE)
			{
				FConnected = false;
				returnValue = false;
			}
			else
			{
				FConnected = true;
				returnValue = true;
			}
		}
		return returnValue;
	}*/
}
//---------------------------------------------------------------------------
bool __fastcall TTcpComm::RequestStallTest(int stallEvent)
{
/*
	bool returnValue = true;
	unsigned char TXPacketBuf[PACKET_MAX_SIZE], RXPacketBuf[PACKET_MAX_SIZE];
	TUSBPacket *TXPacket = (TUSBPacket *)TXPacketBuf;
	TUSBPacket *RXPacket = (TUSBPacket *)RXPacketBuf;
	DWORD dwWriteSize, dwReadSize;
	int stallMode[1];
	if (stallEvent == 0)
	{
		stallMode[0] = stallEvent;
		int dataLength = sizeof(int);
		int readLength = 10;
		TXPacket->header      = 0xA5A5;
		TXPacket->mode        = 1;
		TXPacket->shortLength = SizeOfWord(dataLength) + USB_PACKET_HEADER_SIZE;
	    TXPacket->longLength  = 0;
	    TXPacket->type        = CMD_TYPE_STALL_TEST;
	    TXPacket->command     = CMD_ECHO;
	    TXPacket->source      = BOARD_ID_PC;
	    TXPacket->destination = BOARD_ID_SPB(1);
	    TXPacket->packetID    = 0;

        memcpy(TXPacket->data, stallMode, dataLength);

		if (WriteFile(DeviceHandle, TXPacket, TXPacket->shortLength * sizeof(int), &dwWriteSize, NULL))
		{

	    }
	    else
	    {
	        returnValue = false;
	        LastErrorMessage = "USB 쓰기 실패(H) : " + SysErrorMessage(GetLastError());
	    }
		if (!returnValue) return returnValue;
		if (ReadFile(DeviceHandle, RXPacket, USB_PACKET_HEADER_SIZE * sizeof(int), &dwReadSize, NULL))
		{
        	if (ReadFile(DeviceHandle, RXPacket->data, readLength, &dwReadSize, NULL))
            {
                returnValue = true;
			}
            else
            {
            	returnValue = false;
                LastErrorMessage = "USB 읽기 실패(S) : " + SysErrorMessage(GetLastError());
//                    error = true;
			}
	    }
	    else
	    {
	    	returnValue = false;
	        LastErrorMessage = "USB 읽기 실패(H) : " + SysErrorMessage(GetLastError());
		}
    }
    return returnValue;
*/    
}
//---------------------------------------------------------------------------
void __fastcall TTcpComm::ProtocolCheck(int commIndex)
{
	unsigned int echoTX, echoRX;

	// new protocol 을 이용해서 2회 전송 시도
	ProtocolVer = NEW_USB_PROTOCOL_VER;
	if (RequestCommand(commIndex, CMD_ECHO, &echoTX, 4, &echoRX, 4))
	{
		return;
	}
	RecoverDevice(commIndex);
	if (RequestCommand(commIndex, CMD_ECHO, &echoTX, 4, &echoRX, 4))
	{
		return;
	}
	RecoverDevice(commIndex);
	// old protocol 을 이용해서 2회 전송 시도
	ProtocolVer = OLD_USB_PROTOCOL_VER;
	if (RequestCommand(commIndex, CMD_ECHO, &echoTX, 4, &echoRX, 4))
	{
		return;
	}
	if (RequestCommand(commIndex, CMD_ECHO, &echoTX, 4, &echoRX, 4))
	{
		return;
	}
	// 실패시 New Protocol로 간주한다.
	ProtocolVer = NEW_USB_PROTOCOL_VER;
}

