www.pudn.com > mod_rssim6.zip > RS232Port.cpp


///////////////////////////////////////////////////////////////////////////// 
// 
// FILE: RS232Port.cpp : implementation file 
// 
// See _README.CPP 
// 
// Implementation of the CRS232Port class. 
// Requires MFC headder  
// 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "mod_RSsim.h" 
#include "RS232Port.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
 
// ------------------------------ AsyncFriend ------------------------------ 
// Worker thread function, this is a friend of the CRS232Port class 
// 
UINT AsyncFriend(LPVOID pParam) 
{ 
CHAR     debugStr2[MAX_DEBUG_STR_LEN]; 
CHAR     debugStr[MAX_DEBUG_STR_LEN]; 
CRS232Port* portPtr; 
 
   portPtr = (CRS232Port*)pParam; 
   try  
   {  
		 
      // call the function the thread will run in 
      if (portPtr->IsKindOf(RUNTIME_CLASS( CRS232Port))) 
      { 
         // wait untill the Application is ready for us 
         CSingleLock lk(&portPtr->m_threadStartupEvent); 
         lk.Lock(5000); // wait max 5 seconds 
 
         portPtr->RSStateChanged(RSPORTCURRENTLY_VOID); 
          
         // stays in a loop at this point 
         portPtr->Poll(debugStr); 
      } 
      else 
      { 
         sprintf(debugStr2, "CRS232Port AsyncFriend pointer corruption!!!!\n"); 
         OutputDebugString(debugStr2); 
      } 
   } 
   catch (...)  
   { 
      CString msg; 
         msg.Format("INTERNAL APPLICATION ERROR FILE %s LINE: %d\n%s\n%s",  
            __FILE__, __LINE__, __MY_APPVERSION__, __DATE__); 
      OutputDebugString(msg); 
 
      portPtr->RSDataMessage(msg); 
      //OutputDebugString( "Catch\n" ); 
      //sprintf(debugStr2, "CRS232Port AsyncFriend Exception !!!!\n"); 
      //OutputDebugString(debugStr2); 
   } 
   portPtr->m_listenThreadStatus = RS232_EX_TERMINATED; 
   { 
   CString d; 
      d.Format("[Comms thread %4d Terminating.]\n", GetCurrentThreadId()); 
      OutputDebugString(d); 
   } 
   portPtr->m_threadDeadEvent.SetEvent();// CEvent 
   OutputDebugString("Port object thread returning to System\n"); 
    
   //AfxEndThread(0); 
 
   return(0); 
} // AsyncFriend 
 
 
char commsParityStr[] = "NOEMS";   //NOPARITY ODDPARITY EVENPARITY MARKPARITY SPACEPARITY 
char commsStopStr[][4] = {// ONESTOPBIT ONE5STOPBITS TWOSTOPBITS 
   "1", 
   "1.5", 
   "2" 
}; 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
IMPLEMENT_DYNAMIC( CRS232Port, CObject); 
 
CRS232Port::CRS232Port() 
{ 
   m_debuggerStep = 0; 
   m_masterHasWork = FALSE; 
 
   h232Port = INVALID_HANDLE_VALUE; 
 
   // create the listening thread 
   m_listenThreadStatus = RS232_EX_PENDING; 
   m_pWorkerThread      = AfxBeginThread((AFX_THREADPROC)AsyncFriend, 
                                     this, 
                                     THREAD_PRIORITY_TIME_CRITICAL, 0, 
                                     CREATE_SUSPENDED 
                                    ); 
 
   rxBufferIndex = 0;   // set up our RX buffer 
   memset(rxBuffer, 0 , sizeof(rxBuffer)); 
   keepPolling = TRUE; 
   m_lastCharIncommingtime = GetTickCount() + PORT_MAX_IDLETIME*5; 
   InitializeCriticalSection(&critSec); 
} 
 
CRS232Port::~CRS232Port() 
{ 
   ClosePort(); 
   // caller must wait for thread to die, if not already dead 
   OutputDebugString("Port object deleted\n"); 
   TerminateThread(m_pWorkerThread,0); 
} 
 
#ifdef _DEBUG 
VOID CRS232Port::Dump(CDumpContext& dc) const 
{ 
   // call the base class first 
   CObject::Dump(dc); 
 
   // dump our object to the debuggers output 
   // all important members can be dumped at this stage. 
   dc << "RS232 Port: " << "\n"; 
} // Dump 
#endif // _DEBUG 
 
// ------------------------- RSStateChanged ---------------------------- 
void CRS232Port::RSStateChanged(DWORD state) 
{ 
   // do nothing in the base class 
} // RSStateChanged 
/* 
// ------------------------- RSDataDebugger ---------------------------- 
void CRS232Port::RSDataDebugger(const BYTE * buffer, LONG length, BOOL transmit) 
{ 
CString prefix, ASCIIdata; 
LONG index, i; 
   if (transmit) 
      prefix = "TX:"; 
   else 
      prefix = "RX:"; 
   index = 0; 
   while (index < length) 
   { 
      i=0; 
      OutputDebugString("\n"); 
      OutputDebugString(prefix); 
      while ((index+i < length)&&(i<8)) 
      { 
         ASCIIdata.Format("%02X", buffer[index+i]); 
         OutputDebugString(ASCIIdata); 
         i++; 
      } 
      index +=8; 
   } 
} // RSDataDebugger 
  */ 
// --------------------------------- OpenPort --------------------------- 
BOOL CRS232Port::OpenPort(LPCTSTR oPortName) 
{ 
CHAR longPortName[MAX_PORT_NAME]; 
BOOL  error; 
CString msg; 
 
   portNameS = oPortName; 
   GetLongComPortName(portNameS, longPortName);  // get windows port name in long format    
   // for COM ports >COM9 Windows uses a different naming format, hence this  
   // little catch and we have to be compatible. 
 
   portName = longPortName; 
   h232Port =  CreateFile( portName,                    // PIP 1823-400-1997 
                               GENERIC_READ | GENERIC_WRITE, 
                               (DWORD)NULL,   // exclusive access 
                               NULL,          // no security 
                               OPEN_EXISTING, 
                               FILE_ATTRIBUTE_NORMAL, 
                               NULL           // hTemplate 
                             ); 
   if (INVALID_HANDLE_VALUE ==h232Port) 
   { 
      AfxMessageBox("Error: Could not open the RS232 port!", MB_OK|MB_ICONEXCLAMATION); 
      return FALSE; 
   } 
 
   msg.Format("Port %s opened.\n" , longPortName); 
//   OutputDebugString(msg); 
   RSDataMessage(msg); 
 
   // set up the comms parameters now that we have a handle 
   error = SetupComm(h232Port, 
                     (DWORD)2048, (DWORD)2048); 
 
   msg.Format("Port I/O buffers configured.\n"); 
   //OutputDebugString(msg); 
   RSDataMessage(msg); 
   RSStateChanged(RSPORTCURRENTLY_IDLE); 
 
   m_lastCharIncommingtime = GetTickCount() + PORT_MAX_IDLETIME*5; 
   return (TRUE); 
} // OpenPort 
 
void CRS232Port::OnHWError(DWORD dwCommError) 
{ 
   // do nothing 
} 
 
 
// ----------------------- ReConfigurePort ------------------------------ 
// 
BOOL CRS232Port::ReConfigurePort() 
{ 
   return (ConfigurePort(dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.fRtsControl, (NOPARITY==dcb.Parity?FALSE:TRUE))); 
} 
 
// ---------------------------------- ConfigurePort --------------------- 
// 
BOOL CRS232Port::ConfigurePort(DWORD  baud,  
                               DWORD byteSize,  
                               DWORD parity,  
                               DWORD stopBits, 
                               DWORD rts, 
                               DWORD checkParity) 
{ 
BOOL error; 
COMMTIMEOUTS timeout; 
 
   if (INVALID_HANDLE_VALUE==h232Port) 
   { 
   CString errMsg("Error: Could not set up RS232 port. Port not open!"); 
 
      RSDataMessage(errMsg); 
      AfxMessageBox(errMsg, MB_OK|MB_ICONEXCLAMATION); 
      return FALSE; 
   } 
    
   // Set up the DCB with our settings 
   // Get it first so we fill all members 
   error = GetCommState(h232Port, 
                           &dcb); 
   dcb.BaudRate = baud; 
   dcb.ByteSize = (BYTE)byteSize; 
   dcb.Parity = (BYTE)parity; 
   dcb.fParity = checkParity; 
   dcb.StopBits = (BYTE)stopBits; 
 
   // set the default parameters 
   dcb.fOutxDsrFlow = FALSE; 
   dcb.fDsrSensitivity = FALSE; 
   dcb.fOutxCtsFlow = FALSE; // TRUE; 
 
   // set the port for hardware handshaking 
   dcb.fRtsControl = rts; 
   dcb.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_HANDSHAKE; 
   dcb.fAbortOnError = TRUE; 
   dcb.fBinary = TRUE; 
   dcb.fTXContinueOnXoff  = FALSE; 
   dcb.fOutX = FALSE; 
   dcb.fInX = FALSE; 
   dcb.fErrorChar = 0; 
   dcb.fNull = 0; 
   dcb.fDummy2 = 0; 
 
   // assign it 
   error = SetCommState(h232Port, 
                           &dcb); 
   CString paramsSet; 
   if (!error) 
   { 
   CHAR  errorMsg[MAX_ERROR_STR_LEN]; 
   CHAR *errorMsgPtr = errorMsg; 
   DWORD ntError = GetLastError(); 
 
      // call the API using the default system language 
      errorMsg[0] = '\0'; 
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,  
                    NULL,                    // ignored, search the system only 
                    ntError,                 // error code passed in 
                    GetSystemDefaultLangID(),// English ,English US 
                    errorMsg,                // message buffer 
                    sizeof(errorMsg),        // length 
                    NULL);                   // optional parameter list 
      //  
      while(*errorMsgPtr)                    // Find any '\r' and replace with ' ' 
      { 
         errorMsgPtr++; 
         if (( *errorMsgPtr == '\r') | ( *errorMsgPtr == '\n')) 
            *errorMsgPtr = ' ';   
      } 
      paramsSet.Format("Error configuring port for %d,%d,%c,%s : (error %d) - %s",  
                                                      baud,  
                                                      byteSize, 
                                                      commsParityStr[parity],  
                                                      commsStopStr[stopBits], 
                                                      ntError, 
                                                      errorMsg); 
      RSDataMessage(paramsSet); 
      AfxMessageBox( paramsSet, MB_OK | MB_ICONEXCLAMATION); 
      ClosePort(); 
      return FALSE; 
   } 
   else 
   { 
      paramsSet.Format("Port configured %d,%d,%c,%s", baud,  
                                                      byteSize, 
                                                      commsParityStr[parity],  
                                                      commsStopStr[stopBits]); 
      RSDataMessage(paramsSet); 
   } 
 
 
   // Set up the timeouts to use, they are quite short, since we will loop anyway. 
   // Do not make them zero, else we will have a CPU load problem. Too large a value,  
   // and we have to wait for comms to time out when shutting down 
 
   // fill timeout structure 
   GetCommTimeouts(h232Port, &timeout); 
   timeout.ReadIntervalTimeout = 100;           // 500ms between incomming chars. 
   timeout.ReadTotalTimeoutConstant = 500; 
   timeout.ReadTotalTimeoutMultiplier = 0;      // #chars to read does not add to timeout amount 
   timeout.WriteTotalTimeoutConstant = 2000; 
   timeout.WriteTotalTimeoutMultiplier = 60;    // 60ms per char sent 
                                                 
   error = SetCommTimeouts(h232Port, &timeout); 
   if (! error) 
   { 
   CString errMsg("Error: Could configure the timeouts."); 
 
      RSDataMessage(errMsg); 
      AfxMessageBox(errMsg, MB_OK|MB_ICONEXCLAMATION); 
      ClosePort(); 
      return FALSE; 
   } 
    
   //RSDataMessage("Timeouts configured\n"); 
   // done 
   m_lastCharIncommingtime = GetTickCount() + PORT_MAX_IDLETIME*5; 
   return (TRUE); 
} // ConfigurePort 
 
// ----------------------------------- Purge --------------------------- 
// remove all characters from the RX buffer (eliminates back-log) 
// 
BOOL CRS232Port::Purge() 
{ 
BOOL ret; 
 
   if (!keepPolling) 
      return FALSE; 
   ASSERT(INVALID_HANDLE_VALUE != h232Port); 
   // interlock 
   EnterCriticalSection(&critSec); 
 
   ret = PurgeComm(h232Port, PURGE_RXCLEAR); 
   LeaveCriticalSection(&critSec); 
   return (ret); 
} // Purge 
 
// ---------------------------------- ClosePort ------------------------- 
// PURPOSE: 
BOOL CRS232Port::ClosePort() 
{ 
    
   // interlock 
   EnterCriticalSection(&critSec); 
   if (INVALID_HANDLE_VALUE != h232Port) 
   { 
      // tell dialog that we are closing it (this is so fast anyway) 
      RSStateChanged(RSPORTCURRENTLY_CLOSING); 
      RSDataMessage("Closing port\n"); 
      CloseHandle(h232Port); 
   } 
   // set HANDLE to closed so we know in future 
   h232Port = INVALID_HANDLE_VALUE; 
   LeaveCriticalSection(&critSec); 
   return (TRUE); 
} // ClosePort 
 
// ---------------------------------- Send -------------------------------- 
// PURPOSE: This one should be clear 
// 
LONG CRS232Port::Send(int numberOfBytestoWrite, const BYTE* bufferPtr, CHAR* debugStrPtr) 
{ 
BOOL error; 
DWORD numberOfBytesWritten=0; 
 
   ASSERT (numberOfBytestoWrite>0); 
   if (!keepPolling) 
      return FALSE; 
   // send the bytes out on the wire 
   if (INVALID_HANDLE_VALUE==h232Port) 
   { 
      OutputDebugString("Error: Cannot send. Port closed!\n");   // busy shutting down the app 
      return FALSE; 
   } 
    
   // flag our intention 
   RSStateChanged(RSPORTCURRENTLY_WRITTING); 
   // interlock 
   EnterCriticalSection(&critSec); 
   error = WriteFile(h232Port, 
                      bufferPtr, 
                      numberOfBytestoWrite,                 // nNumberOfBytesToWrite, 
                      &numberOfBytesWritten, 
                      NULL);  
   LeaveCriticalSection(&critSec); 
    
   // output it in windows debugger 
   RSDataDebugger(bufferPtr, numberOfBytesWritten, TRUE); 
   return (TRUE); 
} // Send 
 
 
 
// ------------------------------ Recieve ------------------------------------ 
// return TRUE if a telegram was read. 
// return false if no packet was read, this routine will keep trying to build a  
// full telegram. 
LONG CRS232Port::Recieve(DWORD * numberOfBytesLong, CHAR* bufferPtr, CHAR* debugStrPtr) 
{ 
BOOL error; 
DWORD numberOfBytesWritten=0; 
DWORD readAheadLength;        // # bytes we probably need (depends on what we have in the buffer so far) 
DWORD numberOfBytesRead, dwCommError, tickCount; 
BYTE * rxBuffPtr; 
CString debuggerStr; 
 
   RSStateChanged(RSPORTCURRENTLY_READING); 
   if (!keepPolling) 
      return FALSE; 
   // get ready to read bytes 
   if (INVALID_HANDLE_VALUE==h232Port) 
   { 
      OutputDebugString("Error: Cannot RX. Port closed!\n");   // busy shutting down the app 
      Sleep(0); 
      return FALSE; 
   } 
 
   // check if port has been idle for too long 
   tickCount = GetTickCount(); 
   if (((LONG)tickCount - (LONG)m_lastCharIncommingtime) > PORT_MAX_IDLETIME) 
   { 
      m_lastCharIncommingtime = tickCount + PORT_MAX_IDLETIME*5; 
      if (rxBufferIndex) 
      { 
      CString msg; 
         msg = "Port idle, buffer cleared!\n";   // idle 
         OutputDebugString(msg); 
         RSDataMessage(msg); 
         //OnHWError(0); 
      } 
      // start recieving from fresh again 
      rxBufferIndex = 0;   // set up our RX buffer 
      memset(rxBuffer, 0 , sizeof(rxBuffer)); 
      OnHWError(0); 
   } 
 
   rxBuffPtr = &rxBuffer[rxBufferIndex]; 
   *numberOfBytesLong = 0; 
 
   // Work out our read-ahead length 
   // 
   readAheadLength = SimulationSerialPort::CalculateReadAheadLength(rxBuffer, rxBufferIndex); 
 
   ASSERT(rxBufferIndex + readAheadLength < sizeof(rxBuffer)); 
#ifdef _COMMS_DEBUGGING 
   //debuggerStr.Format("Read-ahead %d bytes\n", readAheadLength); 
   //OutputDebugString(debuggerStr); 
#endif 
    
   // read what-ever is on the port, up to a maximum of readAheadLength. 
   numberOfBytesRead = 0; 
   EnterCriticalSection(&critSec); 
   error = ReadFile(h232Port, 
                      rxBuffPtr, 
                      readAheadLength,                 // nNumberOfBytesToWrite, 
                      &numberOfBytesRead, 
                      NULL); 
   LeaveCriticalSection(&critSec); 
   rxBufferIndex+=numberOfBytesRead; 
    
   // display the chars in the debugger 
   RSDataDebugger(rxBuffPtr, numberOfBytesRead, FALSE); 
   // keep idle tick count alive since we just got a char(s) 
   if (numberOfBytesRead) 
      m_lastCharIncommingtime = GetTickCount(); 
    
   if (0==error) 
   { 
      // clear it 
      ClearCommError(h232Port, &dwCommError, NULL); 
      // clear the buffers 
      rxBufferIndex = 0; 
      numberOfBytesRead = 0; 
      OnHWError(dwCommError); 
   } 
   // done reading 
   RSStateChanged(RSPORTCURRENTLY_IDLE); 
   if (rxBufferIndex) 
   { 
      *numberOfBytesLong = numberOfBytesRead; 
      memcpy(bufferPtr, rxBuffPtr, numberOfBytesRead); 
      return(TRUE); 
   } 
   return (FALSE);   // no data avail yet 
} // Recieve 
 
// -------------------------------------- Poll ------------------------------ 
// The main simulator thread loop 
// 
void CRS232Port::Poll(CHAR * debugStr) 
{ 
DWORD numBytes; 
CHAR buffer[MAX_MODBUS_MESSAGELEN]; 
CHAR debugMsg[80]; 
BOOL discard; 
 
   while (keepPolling)  // check to see if we must quit this loop 
   { 
      discard = FALSE; 
      if ((! m_masterHasWork) && (Recieve(&numBytes, buffer, debugMsg)) ) 
      { 
         // test to see if they want this thread to die. 
         if (!keepPolling) 
            break; 
         //CMODMessage msg(buffer, numBytes); 
         //if (msg.CRCOK()) 
         //{ 
            // build a response etc 
            OnProcessData(buffer, numBytes, &discard);   // 
         //} 
      } 
      else 
      { 
         m_masterHasWork = FALSE; 
         if (! OnProcessData(buffer, 0 , &discard)) 
            Sleep(10);  // allow this thread some "space" when it is not doing anything 
                        // only needed at times when the port is not available. 
      } 
      if (discard) 
         rxBufferIndex = 0; 
   } 
   OutputDebugString("Poll loop exiting\n"); 
} // Poll