#include "stdafx.h" #include #include "CMMIO_SERIAL.H " ////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// #define LONG_TIMEOUT 5000 /* ///////////////////////////////////////////////////////////////////////////// // Code - Text for debug window static char Codes[][6] = { "","","","","","","","", "","","","","","","","","", "","","","","","","","", "","","","","","","" }; */ ///////////////////////////////////////////////////////////////////////////// // CPSerial() : Constructor ///////////////////////////////////////////////////////////////////////////// // CSerialTask() : Function to run ReceiveTask() method as a task // unsigned int WINAPI CSerialTask(LPVOID CSerialPtr) { TRACE( TEXT("Serial task has started \n") ); // Call the ControlTask function in the specified plugin ((CPSerial *)CSerialPtr)->ReceiveTask(); TRACE( TEXT("Serial task has completed \n") ); _endthreadex(0); return(0); } CPSerial::CPSerial() { // Serial port is not open m_PortHandle = INVALID_HANDLE_VALUE; // Default port settings m_Port = 1; m_Baud = CBR_115200; m_Parity ='N'; m_Bits = 8; m_StopBits = 1; m_HandShake =CS_HANDSHAKE_FOR_SO7; m_RXTimeout = CS_DEFAULT_RX_TIMEOUT; m_TXTimeout = CS_DEFAULT_TX_TIMEOUT; m_iRecvState=FALSE; m_csRecv=_T(""); m_iRecvByte=0; m_iReadDataNum=255; // Everything else set to NULL m_ThreadHandle = NULL; //m_TXHead = NULL; //m_TXTail = NULL; //m_RXHead = NULL; //m_RXTail =NULL; memset( &m_ReceiveOLap, 0, sizeof( OVERLAPPED )); memset( &m_TransmitOLap, 0, sizeof( OVERLAPPED )); memset( &m_ReadOLap, 0, sizeof( OVERLAPPED )); memset( &m_WriteOLap, 0, sizeof( OVERLAPPED )); m_hWaitCMMResponse = CreateEvent( NULL, TRUE, FALSE, NULL ); m_hNewRx = CreateEvent( NULL, TRUE, FALSE, NULL ); // to trigger OnRx //m_RXTempPtr = NULL; m_DebugInPtr = 0; m_DebugCount = 0; m_Item = 0; m_MaxTXRetries = 5; /* // CriticalSection for locking lists InitializeCriticalSection( &m_QueueLock ); InitializeCriticalSection( &m_WriteLock ); InitializeCriticalSection( &m_ReadLock ); */ // IsValidBuffer = FALSE; CurrentPointer = 0; m_hWaitCMMResponse = CreateEvent( NULL, TRUE, FALSE, NULL ); m_hNewRx = CreateEvent( NULL, TRUE, FALSE, NULL ); // to trigger OnRx // pParent = NULL; } ///////////////////////////////////////////////////////////////////////////// // ~CPSerial() : Destructor - Close the port and free up the CriticalSection CPSerial::~CPSerial() { if( IsOpen( ) ) { TRACE(TEXT("Warning : closing serial port in destructor\n")); Close(); } while(GetNextReceived()) ; // mp 3/3/99 prevents leaks /* DeleteCriticalSection( &m_QueueLock ); DeleteCriticalSection( &m_ReadLock ); DeleteCriticalSection( &m_WriteLock ); */ // close the overlapped io event CloseHandle( m_ReadOLap.hEvent ); CloseHandle( m_WriteOLap.hEvent ); // CloseHandle( m_hWaitCMMResponse ); CloseHandle( m_hNewRx); } ///////////////////////////////////////////////////////////////////////////// // OpenPort() : Opens the serial port using the parameters set by default // or a call to SetPort DWORD CPSerial::Open() { CString PortName; COMMTIMEOUTS CommTimeOut; int Ok; unsigned int ThreadID; // Ensure the debug window is registered // RegisterDebugWindow( ); // Close the port incase it is already open Close( ); // Start of assuming the worst Ok = FALSE; // Format the file name and open it // COM opens ports from 1..9 for two-difit ports it's becessary to use \\\\.\\COM #if 0 PortName.Format( TEXT("\\\\.\\COM%d"), m_Port ); #else PortName.Format( TEXT("COM%d"), m_Port ); #endif m_PortHandle = CreateFile( PortName, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if( IsOpen( ) ) { // Setup the port according to the stored parameters if( ProgramPort( m_Port, m_Baud, m_Parity, m_Bits, m_StopBits, m_HandShake ) ) { // Setup the timeouts CommTimeOut.ReadIntervalTimeout = 25; CommTimeOut.ReadTotalTimeoutMultiplier = 1; CommTimeOut.ReadTotalTimeoutConstant = 0; CommTimeOut.WriteTotalTimeoutMultiplier = 0; CommTimeOut.WriteTotalTimeoutConstant = m_TXTimeout; if( SetCommTimeouts( m_PortHandle, &CommTimeOut ) ) { // Setup the buffer sizes if( SetupComm( m_PortHandle, 2048, 2048 ) ) { // Setup the event masks for the monitoring task if( SetCommMask( m_PortHandle, EV_RXCHAR | EV_TXEMPTY | EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RLSD ) ) { // Initialise the Overlapping structures and start the // monitoring task m_ReceiveOLap.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); m_TransmitOLap.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); m_ThreadHandle = (HANDLE)_beginthreadex( NULL, 0, CSerialTask, this, 0, &ThreadID ); //Sleep(1000); Ok = TRUE; } } } // clear msg waiting m_iNbMsgWaiting = 0; } // Things have gone wrong so close the port if( Ok == FALSE ) { TRACE(_T("CmmIO> Port OPEN issue -> CLOSED\n"),Ok); Close( ); } } TRACE(_T("CmmIO> Port OPEN = %d \n"),Ok); // Return the state return( IsOpen( ) ); } ///////////////////////////////////////////////////////////////////////////// // IsOpen() : returns true if the serial port is open // int CPSerial::IsOpen( ) { return( m_PortHandle != INVALID_HANDLE_VALUE ); } ///////////////////////////////////////////////////////////////////////////// // SetPort() : Store the serial settings. If the port is open then these // settings are applied now int CPSerial::SetPort(int Port,int Baud,char Parity,int Bits,int StopBits,int HandShake) { // Use the current settings if the value has the default of 0 m_Port = Port; if(Baud == 0) Baud = m_Baud; if(Parity == 0) Parity = m_Parity; if(Bits == 0) Bits = m_Bits; if(StopBits == 0) StopBits = m_StopBits; // If the values are ok then store them if( ProgramPort( Port, Baud, Parity, Bits, StopBits, HandShake ) ) { m_Port = Port; m_Baud = Baud; m_Parity = Parity; m_Bits = Bits; m_StopBits = StopBits; m_HandShake = HandShake; return(TRUE); } return(FALSE); } ///////////////////////////////////////////////////////////////////////////// // GetPortData() : return the current settings // void CPSerial::GetPortData(int *Port,int *Baud,char *Parity,int *Bits,int *StopBits,int *HandShake) { // return the requested settings if( Port ) *Port = m_Port; if( m_Baud ) *Baud = m_Baud; if( Parity ) *Parity = m_Parity; if( Bits ) *Bits = m_Bits; if( StopBits ) *StopBits = m_StopBits; if( HandShake ) *HandShake = m_HandShake; } ///////////////////////////////////////////////////////////////////////////// // ClosePort() : Close the port and shut down the monitoring thread // DWORD CPSerial::Close() { //struct SerialList *Free; HANDLE Port; // If the port is open then close it if( IsOpen( ) ) { Port = m_PortHandle; m_PortHandle = INVALID_HANDLE_VALUE; CloseHandle( Port ); if( WaitForSingleObject( m_ThreadHandle, (5 * LONG_TIMEOUT) ) != WAIT_OBJECT_0 ) TRACE( TEXT("ERR:Serial port thread failed to terminate\n") ); m_ThreadHandle = NULL; CloseHandle( m_ReceiveOLap.hEvent ); CloseHandle( m_TransmitOLap.hEvent ); } CMMIO::Close(); //ZH /* // Delete the contents of the temp rx pointer if any delete[] m_RXTempPtr; m_RXTempPtr = NULL; // Clear down all internal lists EnterCriticalSection( &m_QueueLock ); while( m_RXHead ) { Free = m_RXHead; m_RXHead = m_RXHead->Next; delete[] Free->Buffer; delete Free; } m_RXHead = NULL; while( m_TXHead ) { Free = m_TXHead; m_TXHead = m_TXHead->Next; delete[] Free->Buffer; delete Free; } m_TXHead = NULL; LeaveCriticalSection( &m_QueueLock ); */ return(TRUE); } ///////////////////////////////////////////////////////////////////////////// // Send functions // [8/11/2004] // DWORD CPSerial::Send(LPCSTR buffer, int l, BOOL /*needsResponse=FALSE*/) { return ( WritePort ((const char*) buffer, (DWORD) l)); } /* DWORD CPSerial::Send(CString buffer) { char LocBuffer[MAX_OUTPUT_BUFFER_SIZE]; int length = buffer.GetLength (); if (length >MAX_OUTPUT_BUFFER_SIZE) { length = MAX_OUTPUT_BUFFER_SIZE; } unsigned short* ptr = (unsigned short*)buffer.GetBuffer (MAX_OUTPUT_BUFFER_SIZE); for (int i=0;iBuffer, BytesWritten, 2 ); OnTransmit( 0, BytesWritten ); SendBuffer(TRUE); } } */ if(Events & EV_BREAK) TRACE(_T("Break detected\n")); if(Events & EV_CTS) TRACE(_T("CTS Changed State\n")); if(Events & EV_DSR) TRACE(_T("DSR Changed State\n")); if(Events & EV_ERR) TRACE(_T("Line error\n")); if(Events & EV_RLSD) TRACE(_T("EV_RLSD error\n")); } // Go round while the port is open } while( IsOpen( ) ); } ///////////////////////////////////////////////////////////////////////////// // OnReceive() : Default OnReceive() // V114 void CPSerial::OnReceive() { // Dummy OnReceieve if not used char s[255]={0}; CStringA csTemp; s[1]='\0'; CurrentPointer = 0; int num=0; num = ReadPort(s, m_iReadDataNum); //for (int i=0;i Port was OPEN, ProgramPort done \n")); } else { TRACE(_T("CmmIO> Port was not OPEN ProgramPort not done \n")); Ok = TRUE; } } return(Ok); } ///////////////////////////////////////////////////////////////////////////// // OnTransmit() : Default OnTransmit() // void CPSerial::OnTransmit(int /*Item*/, DWORD /*Error*/) { // Dummy OnTransmit if not used } ///////////////////////////////////////////////////////////////////////////// // SetTimeouts() : Sets the rx and tx timeouts // void CPSerial::SetTimeouts( int RXTimeout, int TXTimeout ) { COMMTIMEOUTS CommTimeOut; // Store the timeouts m_RXTimeout = RXTimeout; m_TXTimeout = TXTimeout; // If the port is open then configure the port also // Currently we only use the Fixed timeouts if( IsOpen( ) ) { CommTimeOut.ReadIntervalTimeout = 25; CommTimeOut.ReadTotalTimeoutMultiplier = 1; CommTimeOut.ReadTotalTimeoutConstant = 0; CommTimeOut.WriteTotalTimeoutMultiplier = 0; CommTimeOut.WriteTotalTimeoutConstant = m_TXTimeout; SetCommTimeouts( m_PortHandle, &CommTimeOut ); } } ///////////////////////////////////////////////////////////////////////////// // AddToDebug() : Add the data to the debug output. State is 1 = rx 2 = tx // 3 = user void CPSerial::AddToDebug( const char * /*Ptr*/, DWORD /*BytesToCopy*/, int /*State*/ ) { //ZH #if 0 // We are messing with pointers so use the CriticalSection EnterCriticalSection(&m_QueueLock); // If ptr = NULL the change to string containing if( Ptr == NULL ) { BytesToCopy = 0; //Ptr = _T(""); Ptr = ""; } // If zero length then use length of string instead if( BytesToCopy == 0) BytesToCopy = strlen( Ptr ); // If length of data is greater then the debug buffer then just // use the end of the data if(BytesToCopy > CS_DEBUG_SIZE) { Ptr += BytesToCopy - CS_DEBUG_SIZE; BytesToCopy = CS_DEBUG_SIZE; } // If the data will wrap around then just copy the first block if( ( m_DebugInPtr + BytesToCopy ) > CS_DEBUG_SIZE) { // Copy data to the end of the debug buffer memcpy(m_DebugData + m_DebugInPtr, Ptr, CS_DEBUG_SIZE - m_DebugInPtr); memset(m_DebugState + m_DebugInPtr, State, CS_DEBUG_SIZE - m_DebugInPtr); m_DebugWnd.AddData( m_DebugData + m_DebugInPtr, m_DebugState + m_DebugInPtr, CS_DEBUG_SIZE - m_DebugInPtr ); // Move on by the amount copied Ptr += CS_DEBUG_SIZE - m_DebugInPtr; BytesToCopy -= CS_DEBUG_SIZE - m_DebugInPtr; // Point at the begining of the buffer m_DebugInPtr = 0; } // Copy the rest into the buffer memcpy(m_DebugData + m_DebugInPtr, Ptr, BytesToCopy); memset(m_DebugState + m_DebugInPtr, State, BytesToCopy); m_DebugWnd.AddData( m_DebugData + m_DebugInPtr, m_DebugState + m_DebugInPtr, BytesToCopy ); m_DebugInPtr += BytesToCopy; m_DebugCount += BytesToCopy; // Keep a count of howmany bytes are in the buffer if( m_DebugCount > CS_DEBUG_SIZE ) m_DebugCount = CS_DEBUG_SIZE; // All done so out of the CriticalSection LeaveCriticalSection(&m_QueueLock); #endif } ///////////////////////////////////////////////////////////////////////////// // FlushPort() : Removes all characters in the serial buffer // /* int CPSerial::FlushPort(void) { int TXTimeout,RXTimeout; char FlushBuffer[256]; DWORD BytesRead,BytesTotal; struct SerialList *Free; if( IsOpen( ) ) { // We are messing with pointers so use the CriticalSection EnterCriticalSection(&m_QueueLock); // Make a copy of the timeouts and set back to the defaults RXTimeout = m_RXTimeout; TXTimeout = m_TXTimeout; SetTimeouts( 10, 10 ); // Read all data from the port BytesTotal=0; while( ( BytesRead = ReadPort( FlushBuffer, 256 ) ) == 256 ) BytesTotal+=BytesRead; BytesTotal+=BytesRead; // Clear the Received list while(m_RXHead) { Free=m_RXHead; m_RXHead=m_RXHead->Next; delete[] Free->Buffer; delete Free; } // put the timeouts back SetTimeouts( RXTimeout, TXTimeout ); m_iNbMsgWaiting = 0; // All done so out of the CriticalSection LeaveCriticalSection(&m_QueueLock); } return(BytesTotal); } */ ///////////////////////////////////////////////////////////////////////////// // MaxPort() : // int CPSerial::MaxPort() { // return the max port, :-) return(8); } ///////////////////////////////////////////////////////////////////////////// // Transmit() : Adds data into a list to be transmitted when possible. // the function OnTransmit() will be called for each block of // data sent. // int CPSerial::Transmit(const char * /*Buffer*/,DWORD /*Bytes*/) { /* struct SerialList *Ptr; int Start; // Is the port open if( IsOpen( ) ) { TRACE(_T("CmmIo> Port OPEN ... transmitting %d bytes \n"),Bytes); // Yes so CriticalSection again EnterCriticalSection(&m_QueueLock); // Create a new list entry structure for the block of data Ptr = new struct SerialList; Ptr->Buffer = new char[Bytes]; Ptr->Bytes = Bytes; Ptr->Next = NULL; Ptr->Item = m_Item++; if(Ptr->Item == 0 ) Ptr->Item = m_Item++; memcpy( Ptr->Buffer, Buffer, Bytes ); // Add it into the list if( m_TXTail ) { Start = FALSE; m_TXTail->Next = Ptr; } else { Start = TRUE; m_TXHead = Ptr; } m_TXTail = Ptr; // If the list was empty then start sending the data, otherwise // it will be sentout when the previous data has been sent if(Start) SendBuffer(FALSE); // All done so out of the CriticalSection LeaveCriticalSection(&m_QueueLock); //return the ID for this block return( Ptr->Item ); } else{ TRACE(_T("CmmIo> Port NOT OPEN ... FAILED TO TRANSMIT %d bytes \n"),Bytes); } */ return(0); } ///////////////////////////////////////////////////////////////////////////// // AddReceived() : Helper function, this adds a block of data to a list which // read back by GetNextReceived() // /* int CPSerial::AddReceived( const char *Buffer,DWORD Bytes) { DWORD index = 0; //primary buffer index struct SerialList *Ptr; static char Buffer2[1000]; // result buffer static char* pBuffer2 = &Buffer2[0]; unsigned char c; bool bArmed; int count; static int escape = 0; static int tilde = 0; static int tildeseqcount = 0; bool bDone = false; bool bEventRequest = false; // ATLTRACE("AddReceived> pBuffer2 = %x\n",pBuffer2); // ATLTRACE("Content %s\n",Buffer); if (Bytes==0){ // TRACE("CMMIO> Exiting , no real input"); return TRUE; } do { bArmed = false; for (;index0x80 ){ ++ escape; // max will be 1 // ATLTRACE(" ESCAPE ___>> %d\n",escape); } if( c=='~' ){ ++ tilde; // max = 1 // ATLTRACE(" TILDE ESC___>> %d\n",tilde); } } if (index == Bytes) bDone=true; // else TRACE("\nCMMIO> (Serial)Splitting received stream....\n"); // We are messing with pointers so use the CriticalSection if (bArmed ){ EnterCriticalSection(&m_QueueLock); //Allocate a new list and add it in count = pBuffer2-(&Buffer2[0]); Ptr = new struct SerialList; Ptr->Buffer = new char[count + 1 ]; Ptr->Bytes = count; Ptr->Next = NULL; memcpy( Ptr->Buffer, Buffer2, count ); Ptr->Buffer[count] = 0; memcpy(m_sLastMessage,Buffer2,count); //copy to last message m_sLastMessage[count]=0; if( m_RXTail ) m_RXTail->Next = Ptr; else m_RXHead = Ptr; m_RXTail = Ptr; // All done so out of the CriticalSection ++m_iNbMsgWaiting; LeaveCriticalSection( &m_QueueLock ); pBuffer2=&Buffer2[0]; // reset out buffer count = 0; escape = 0; //new tilde = 0; tildeseqcount = 0; ///n bEventRequest = true; } } while (!bDone); // ATLTRACE ("CMMIO> Done\n"); //ZH #if 0 if(bEventRequest){ if (!(pParent->m_bNoFireEvent)) { // ATLTRACE("CmmIO Serial> Setting Event m_hNewRx \n"); SetEvent(m_hNewRx);// tell the system to fire event if needed... } else { // ATLTRACE("CmmIO Serial> Setting Event pParent->m_hCmmResponded \n"); // SetEvent(pParent->m_hCmmResponded); } } #endif // if(bEventRequest){ // if (pParent){ // SetEvent(pParent->m_hCmmResponded); // } // } // else ATLTRACE("--- AddReceived> Leaving with part of msg in buffer, pBuffer2= %x\n",pBuffer2); return(TRUE); } */ ///////////////////////////////////////////////////////////////////////////// // GetNextReceived() : Helper function, receives messages placed in the queue // by AddReceievd() // //ZH /* char *CPSerial::GetNextReceived(void) { struct SerialList *Free; // If there is a previous block then delete it delete[] m_RXTempPtr; m_RXTempPtr = NULL; // We are messing with pointers so use the CriticalSection EnterCriticalSection(&m_QueueLock); // If there any more to return if( m_RXHead ) { Free = m_RXHead; m_RXHead = m_RXHead->Next; // Point the temp pointer at the block m_RXTempPtr = Free->Buffer; // delete the list entry delete Free; --m_iNbMsgWaiting; // mp } if( m_RXHead == NULL ) m_RXTail = NULL; // All done so out of the CriticalSection LeaveCriticalSection( &m_QueueLock ); return( m_RXTempPtr ); } */ ///////////////////////////////////////////////////////////////////////////// // HexToInt() : Helper function, converts the specifed number of bytes from // ascii hex // int CPSerial::HexToInt(char *Data, int Bytes) { int Byte; int HexChar, Value; Value = 0; for( Byte = 0; Byte < Bytes; Byte++ ) { Value <<= 4; HexChar = *Data++ -= '0'; if( HexChar > 32 ) HexChar -= 39; else if( HexChar > 9 ) HexChar -= 7; Value += HexChar; } return( Value ); } ///////////////////////////////////////////////////////////////////////////// // Private functions // ///////////////////////////////////////////////////////////////////////////// // RegisterDebugWindow() : Registers the window class for the debug window. // //ZH /* void CPSerial::RegisterDebugWindow() { // Register the CSerialRaw window for future use WNDCLASS wndcls; memset( &wndcls, 0, sizeof( WNDCLASS ) ); wndcls.lpfnWndProc = DefWindowProc; wndcls.hInstance = AfxGetInstanceHandle( ); wndcls.hCursor = LoadCursor( NULL, IDC_ARROW ); wndcls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.lpszClassName = TEXT("CPSerial"); AfxRegisterClass( &wndcls ); } */ #if 0 ///////////////////////////////////////////////////////////////////////////// // SendBuffer() : Internal function, this writes the next block of data // queued to the serial port. void CPSerial::SendBuffer(int Next) { struct SerialList *Free; // DWORD BytesWritten; // We are messing with pointers so use the CriticalSection EnterCriticalSection(&m_QueueLock); // If we been told to go the next then do it if(Next && m_TXHead) { Free=m_TXHead; m_TXHead=m_TXHead->Next; delete[] Free->Buffer; delete Free; } // If there is still some data then send it if(m_TXHead) // WriteFile(m_PortHandle,m_TXHead->Buffer,m_TXHead->Bytes,&BytesWritten,&m_TransmitOLap); WritePort(m_TXHead->Buffer,m_TXHead->Bytes); else m_TXTail=NULL; // All done so out of the CriticalSection LeaveCriticalSection(&m_QueueLock); } #endif