//<Source File>

#include "stdafx.h"
#include "CSerialPort.h"
#include <assert.h>
#include "ZLG600_DLL.h"
//Constructor
CSerialPort::CSerialPort()
{
m_hComm=NULL;
m_Thread = NULL;
m_ov.Offset=0;
m_ov.OffsetHigh=0;
m_ov.hEvent=NULL;
m_hWriteEvent=NULL;
m_hShutdownEvent=NULL;
m_szWriteBuffer=NULL;
m_bThreadAlive=FALSE;
m_nWriteSize =0;
 //handles
m_hShutdownEvent=NULL;
m_hComm=NULL;
m_hWriteEvent=NULL;
m_nPortNr=0;
m_szWriteBuffer=NULL;
//m_dwCommEvents=0xffffffff;
//m_nWriteBufferSize=0xffffffff;
}
CSerialPort::~CSerialPort()
{
 do                                //彻底释放串口资源
 {
 SetEvent(m_hShutdownEvent);
 }while(m_bThreadAlive);
 if(m_hComm!=NULL)
 {
  CloseHandle(m_hComm);
  m_hComm=NULL;
 }
 if(m_hShutdownEvent!=NULL)
  CloseHandle(m_hShutdownEvent);
 if(m_ov.hEvent=!NULL)
  CloseHandle(m_ov.hEvent);
 if(m_hWriteEvent!=NULL)
  CloseHandle(m_hWriteEvent);
 TRACE("Thread ended\n");
 delete [] m_szWriteBuffer;
}
HANDLE CSerialPort::OpenUartPort(short PortNum,LPCTSTR Settings)
{

 if(m_bThreadAlive)
 {
  do
  {
   SetEvent(m_hShutdownEvent);
  }while(m_bThreadAlive);
  //TRACE("Thread ended\n");
 }
 //create events
 if(m_ov.hEvent!=NULL)				//为解决类存泄露而添加的代码
  ResetEvent(m_ov.hEvent);
 else
  m_ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 if(m_hWriteEvent!=NULL)
  ResetEvent(m_hWriteEvent);
 else
  m_hWriteEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 if(m_hShutdownEvent!=NULL)
  ResetEvent(m_hShutdownEvent);
 else
  m_hShutdownEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 m_hEventArray[0]=m_hShutdownEvent;
 m_hEventArray[1]=m_ov.hEvent;
 m_hEventArray[2]=m_hWriteEvent;
 //initialize critical section
 //InitializeCriticalSection(&m_csCommunicationSync);
 //set buffersize for writing and save the owner

 if(m_szWriteBuffer!=NULL)
  delete [] m_szWriteBuffer;



 BOOL bResult=FALSE;
 char *szPort=new char[50];
 //now it critical!
 //EnterCriticalSection(&m_csCommunicationSync);
 //if the port is already opened:close it
 if(m_hComm!=NULL)
 {
 CloseHandle(m_hComm);
 m_hComm=NULL;
 }
 //prepare port strings
 sprintf(szPort,"COM%d",PortNum);

 //get handle to the port
 m_hComm=CreateFile(szPort,
     GENERIC_READ|GENERIC_WRITE,
     0,
     NULL,
     OPEN_EXISTING,
     FILE_FLAG_OVERLAPPED,
     0);
 if(m_hComm==INVALID_HANDLE_VALUE)
 {
 delete [] szPort;
 return INVALID_HANDLE_VALUE;
 }
 SetupComm(m_hComm,100,100);
 //set the timeout values
 m_CommTimeOuts.ReadIntervalTimeout=0;//字符之间的超时时间，ms为单位，0表示不设置超时
 m_CommTimeOuts.ReadTotalTimeoutConstant=100+20;
 //总时间=ReadTotalTimeoutMultiplier*要读得字节数+ReadTotalTimeoutConstant;
 //总时间=100*1+100=200ms
 m_CommTimeOuts.ReadTotalTimeoutMultiplier=1;
 m_CommTimeOuts.WriteTotalTimeoutConstant=200+20;
 m_CommTimeOuts.WriteTotalTimeoutMultiplier=5;
 //configure
 /* SetCommTimeouts(m_hComm,&m_CommTimeOuts);
 SetCommMask(m_hComm,dwCommEvents);
 GetCommState(m_hComm,&m_dcb);
 m_dcb.fRtsControl=RTS_CONTROL_ENABLE;
 BuildCommDCB(szBaud,&m_dcb);
 SetCommState(m_hComm,&m_dcb);
*/
 if(SetCommTimeouts(m_hComm,&m_CommTimeOuts))
 {
 
   if(GetCommState(m_hComm,&m_dcb))
   {
	  /* m_dcb.fOutxCtsFlow = FALSE;
	   m_dcb.fOutxDsrFlow = FALSE;
	   m_dcb.fOutX = TRUE;
	   m_dcb.fInX = TRUE;
	   m_dcb.XonChar = 0x11;
	   m_dcb.XoffChar = 0x13;
	   m_dcb.XoffLim = 100;
      m_dcb.XonLim = 100;*/
    m_dcb.fRtsControl=RTS_CONTROL_ENABLE;//set RTS bit high
    if(BuildCommDCB(Settings,&m_dcb))
    {
     if(SetCommState(m_hComm,&m_dcb))
      ;
     else
      ProcessErrorMessage("SetCommState()");
    }
    else
     ProcessErrorMessage("BuildCommDCB()");
   }
   else
    ProcessErrorMessage("GetCommState()");
 }
 else
  ProcessErrorMessage("SetCommTimeouts()");
 delete [] szPort;
 //flush the port
 PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
 //release critical section
 //LeaveCriticalSection(&m_csCommunicationSync);
 TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n",PortNum);
  return m_hComm;
}
BOOL CSerialPort::ClosePort(HANDLE hCtrl)
{
	CloseHandle(m_hWriteEvent);
	CloseHandle(hCtrl);
	return TRUE;
}
UINT CSerialPort::CommThread(LPVOID pParam)
{
 CSerialPort *port=(CSerialPort*)pParam;
 port->m_bThreadAlive=TRUE;
 DWORD BytesTransfered=0;
 DWORD Event=0;
 DWORD CommEvent=0;
 DWORD dwError=0;
 COMSTAT comstat;
 BOOL bResult=TRUE;
 if(port->m_hComm)
  PurgeComm(port->m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
 //begin forever loop.
 for(;;)
 {
 bResult=WaitCommEvent(port->m_hComm,&Event,&port->m_ov);
 if(!bResult)
  {
   switch(dwError=GetLastError())
   {
   case ERROR_IO_PENDING:
    {
    break;
    }
   case 87:
    {
    break;
    }
   default:
    {
    port->ProcessErrorMessage("WaitCommEvent()");
    break;
    }
   }
  }
 else
  {
  bResult=ClearCommError(port->m_hComm,&dwError,&comstat);
  if(comstat.cbInQue==0)
   continue;
  }
 Event=WaitForMultipleObjects(3,port->m_hEventArray,FALSE,INFINITE);
 switch(Event)
 {
 case 0:
  {
  port->m_bThreadAlive=FALSE;
  AfxEndThread(100);
  break;
  }
 case 1:   //read Event
  {
  GetCommMask(port->m_hComm,&CommEvent);
  if(CommEvent&EV_CTS)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_CTS_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  if(CommEvent&EV_RXFLAG)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_RXFLAG_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  if(CommEvent&EV_BREAK)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_BREAK_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  if(CommEvent&EV_ERR)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_ERR_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  if(CommEvent&EV_RING)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_RING_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  
  if(CommEvent&EV_RXFLAG)
   ::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_RXFLAG_DETECTED,(WPARAM)0,(LPARAM)port->m_nPortNr);
  if(CommEvent&EV_RXCHAR)
   ReceiveChar(port,comstat);    //实际接受串口数据是在这里处理的
  break;
  }
 case 2:  //Write Event
  {
  WriteChar(port);
  break;
  }
 }
}
return 0;
}
BOOL CSerialPort::StartMonitoring()
{
if(!(m_Thread=AfxBeginThread(CommThread,this)))   //把CommThread函数送进监视进程
 return FALSE;
 m_Thread->SetThreadPriority(THREAD_PRIORITY_TIME_CRITICAL);
 TRACE("Thread started\n");
return TRUE;
}
BOOL CSerialPort::RestartMonitoring()
{
TRACE("Thread resumed\n");
m_Thread->ResumeThread();
return TRUE;
}
BOOL CSerialPort::StopMonitoring()
{
TRACE("Thread suspended\n");
m_Thread->SuspendThread();
return TRUE;
}
void CSerialPort::ProcessErrorMessage(char* ErrorText)
{
char *temp=new char[200];
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
     NULL,
     GetLastError(),
     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
     (LPTSTR)& lpMsgBuf,
     0,
     NULL
     );
sprintf(temp,"WARNING:%s Failed with the following error:\n%s\nPort:%d\n",
  (char*)ErrorText,lpMsgBuf,m_nPortNr);
MessageBox(NULL,temp,"Application Error",MB_ICONSTOP);
LocalFree(lpMsgBuf);
if(temp)
 delete [] temp;
return;
}
void CSerialPort::WriteChar(CSerialPort* port)
{
BOOL bWrite =TRUE;
BOOL bResult=TRUE;
DWORD BytesSent=0;
ResetEvent(port->m_hWriteEvent);
EnterCriticalSection(&port->m_csCommunicationSync);
if(bWrite)
{
 port->m_ov.Offset=0;
 port->m_ov.OffsetHigh=0;
 PurgeComm(port->m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
 bResult=WriteFile(port->m_hComm,
	 port->m_szWriteBuffer,
 // strlen((char*)port->m_szWriteBuffer),
  port->m_nWriteSize,
  &BytesSent,
  &port->m_ov);
 if(!bResult)
 {
  DWORD dwError=GetLastError();
  switch(dwError)
  {
  case ERROR_IO_PENDING:
   {
    BytesSent=0;
    bWrite=FALSE;
    break;
   }
  default:
   {
    port->ProcessErrorMessage("WriteFile()");
   }
  }
 
 }
 else
 {
  LeaveCriticalSection(&port->m_csCommunicationSync);
 }
}
if(!bWrite)
{
 bWrite=TRUE;
 bResult=GetOverlappedResult(port->m_hComm,
  &port->m_ov,
  &BytesSent,
  TRUE);
 if(!bResult)
 {
  port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
 }
}
//if(BytesSent!=strlen((char*)port->m_szWriteBuffer))
if(BytesSent!=port->m_nWriteSize)
{
 TRACE("WARNING:WriteFile() error..Bytes Sent:%d; Message Length: %d\n",BytesSent,strlen((char*)port->m_szWriteBuffer));
}
return;
}

void CSerialPort::ReceiveChar(CSerialPort* port,COMSTAT comstat)
{
 BOOL bRead=TRUE;
 BOOL bResult=TRUE;
 DWORD dwError=0;
 DWORD BytesRead=0;
 unsigned char RXBuff;
 for(;;)
 {
  EnterCriticalSection(&port->m_csCommunicationSync);
  bResult=ClearCommError(port->m_hComm,&dwError,&comstat);
  LeaveCriticalSection(&port->m_csCommunicationSync); 
   if(comstat.cbInQue==0)
  {
   break;
  }
  EnterCriticalSection(&port->m_csCommunicationSync);
  if(bRead)
  {
   bResult=ReadFile(port->m_hComm,
    &RXBuff,
    1,
    &BytesRead,
    &port->m_ov);
   if(!bResult)
   {
    switch(dwError=GetLastError())
    {
    case ERROR_IO_PENDING:
     {
      bRead=FALSE;
      break;
     }
    default:
     {
      port->ProcessErrorMessage("ReadFile()");
      break;
     }
    }
   }
   else
   {
    bRead=TRUE;
   }
  }
  if(!bRead)
  {
   bRead=TRUE;
   bResult=GetOverlappedResult(port->m_hComm,
    &port->m_ov,
    &BytesRead,
    TRUE);
  /* if(!bResult)
   {
    port->ProcessErrorMessage("GetOverlappedResult() in ReadFile()");
   }*/
  }
  LeaveCriticalSection(&port->m_csCommunicationSync);

  ::SendMessage((port->m_pOwner)->m_hWnd,WM_COMM_RXCHAR,(WPARAM)RXBuff,(LPARAM)port->m_nPortNr);

 }
 return;
}
/***************************************************************
**串口发送数据函数
**输入参数：Data--需发送数据的首地址   length---要发送的数据长度
**返回值：实际发送的数据长度
**函数编号：4
***************************************************************/
DWORD CSerialPort::WriteChars(unsigned char *Data,unsigned char length)
{
	BOOL bWrite =TRUE;
	BOOL bResult=TRUE;
	DWORD BytesSent=0;
	//ResetEvent(port->m_hWriteEvent);                //改为直接发送后，不需要清除写数据事件了
	m_ov.Offset=0;
	m_ov.OffsetHigh=0;
	PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
	bResult=WriteFile( m_hComm,				//输数据的实际入口
		Data,
		length,
		&BytesSent,
		&m_ov);               //返回值是实际发送的数据长度
	if(!bResult)
	{
		DWORD dwError=GetLastError();
		switch(dwError)
		{
		case ERROR_IO_PENDING:
			{
				BytesSent=0;
				bWrite=FALSE;
				break;
			}
		default:
			{
				ProcessErrorMessage("WriteFile()");
			}
		}
		
	}
	do{
		DWORD dwError;
		ClearCommError(m_hComm,&dwError,&m_state);
	}while(m_state.cbOutQue!=0); //等待发送缓冲区发送完毕
	if(BytesSent!=length)
	{
		TRACE("WARNING:WriteFile() error..Bytes Sent:%d; Message Length: %d\n",BytesSent,length);
	}
	return m_ov.InternalHigh;//BytesSent;
}
/***********************************************************
**串口接收数据函数
**输入参数：RxBuff--接收数组   length---一次最大接收数据长度
**返回值：实际接收的数据长度
**函数编号：5
************************************************************/
DWORD CSerialPort::ReceiveChars(unsigned char *RxBuff,DWORD length)
{
	char bRead=3;
	BOOL bResult=TRUE;
	DWORD dwError=0;
	DWORD BytesRead=0;
	do{
		DWORD dwError;
		ClearCommError(m_hComm,&dwError,&m_state);
	}while(m_state.cbOutQue!=0); //等待发送缓冲区发送完毕
	do{
		bResult=ReadFile(m_hComm,
			RxBuff,
			length,
			&BytesRead,
			&m_ov/*&BytesRead*/
			);    //异步时利用ReadFile比较稳定
		bResult = GetOverlappedResult(m_hComm,&m_ov,&BytesRead,TRUE);	 //等待异步操作完成再返回  非0表示执行成功，0表示执行失败（或者是同步时挂起）
		/*不可少，否则byteread返回不正确*/
		if(!bResult)
		{
			switch(dwError=GetLastError())
			{
			case ERROR_IO_PENDING:              //表示数据未读取完，需要继续读取
				{
					//	bRead=FALSE;
					break;
				}
			default:
				{
					ProcessErrorMessage("ReadFile()");
					break;
				}
			}
		}
		else  //读取成功
		{
			break;	
		}
	}while(bRead--);
//	FlushFileBuffers(m_hComm);
	PurgeComm(m_hComm,PURGE_RXCLEAR);
	return BytesRead;
}
DCB CSerialPort::GetDCB()
{
 return m_dcb;
}
DWORD CSerialPort::GetCommEvents()
{
 return m_dwCommEvents;
}
DWORD CSerialPort::GetWriteBufferSize()
{
 return m_nWriteBufferSize;
}


