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


///////////////////////////////////////////////////////////////////////////// 
// 
// FILE: ABComms.cpp : implementation file 
// 
// See "_README.CPP" 
//                     
// implementation of the CABCommsProcessor class. 
// 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "mod_RSsimdlg.h" 
#include "message.h" 
#include "ABCommsProcessor.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
// 
 
PCHAR parityNames[] = 
{ 
   "N ",     //NOPARITY     
   "O ",     //ODDPARITY    
   "E ",     //EVENPARITY   
   "Mark ",     //MARKPARITY   
   "Space"      //SPACEPARITY  
}; 
 
PCHAR stopNames[]= 
{ 
   "1", 
   "1.5", 
   "2" 
}; 
 
 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
// constructor to open port 
IMPLEMENT_DYNAMIC( CABCommsProcessor, CCommsProcessor); 
 
 
CABCommsProcessor::CABCommsProcessor(LPCTSTR portNameShort,  
                                     DWORD  baud,  
                                     DWORD byteSize,  
                                     DWORD parity,  
                                     DWORD stopBits, 
                                     DWORD rts, 
                                     int   responseDelay, 
									         BOOL bcc 
                                     )  
   : CCommsProcessor(portNameShort, 
                       19200, //baud,         
                       8,//byteSize,      
                       parity,        
                       ONESTOPBIT, //stopBits,      
                       RTS_CONTROL_ENABLE,//rts,           
                       responseDelay 
                       )  
{ 
CString portName; 
CString description; 
 
   if (PROTOCOL_SELAB232 == pGlobalDialog->m_selectedProtocol) 
      SetProtocolName("Allen-Bradley DF1"); 
   else 
      SetProtocolName("JOY SCC"); 
 
 
   m_ABmasterIDLETime = 100; 
   m_ABtimeoutValue = 300; 
   m_masterBusy = FALSE;               // master-mode PLC internal state flags 
   m_masterWaiting = FALSE; 
   m_guardJunk = 0xAAAAAAAA; 
   m_lastMasterTick = GetTickCount();  // 
   m_useBCCchecks = TRUE; 
   m_surfacePolls = 0; 
 
   m_CPUmode = (pGlobalDialog->m_PLCMaster? MODE_MASTER : MODE_SLAVE); 
   m_CPUstateEngineStep = ENG_STATE_IDLE; 
   m_noiseLength = 0; 
   //m_transactionID = 0; 
   memset(m_masterTN, 0 , sizeof(m_masterTN)); 
 
   m_JOYsource = 1;  
   m_JOYdest = 10;    
   m_JOYShields = 22; 
   m_JOYrunMode = TRUE; 
   m_JOYreadVariables = FALSE; 
   m_JOYwriteVariables = TRUE; 
   m_ABmasterIDLETime = 100; 
   m_ABtimeoutValue = 300; 
 
 
   if (PROTOCOL_SELAB232 == pGlobalDialog->m_selectedProtocol) 
   { 
      m_useBCCchecks = bcc; 
      // SLC PLC defaults for DF1 
      if ((pGlobalDialog->m_baud !=19200)|| 
          (pGlobalDialog->m_parity != NOPARITY)|| 
          (pGlobalDialog->m_byteSize != 8)|| 
          (pGlobalDialog->m_rts != RTS_CONTROL_ENABLE)|| 
          (pGlobalDialog->m_stopBits != ONESTOPBIT)) 
          if (IDYES == AfxMessageBox("Load DF1 port setting defaults Y/N?\n Settings: 19200,8,N,1 and RTS enabled", MB_YESNO)) 
      { 
         pGlobalDialog->m_baud = 19200; 
         pGlobalDialog->m_parity = NOPARITY; 
         pGlobalDialog->m_byteSize = 8; 
         pGlobalDialog->m_rts = RTS_CONTROL_ENABLE; 
         pGlobalDialog->m_stopBits = ONESTOPBIT; 
         pGlobalDialog->SaveApplicationSettings(); 
      } 
   } 
   else 
   { 
      m_CPUmode = MODE_MASTER; 
      //m_useBCCchecks = TRUE; 
      // JOY 
      if ((pGlobalDialog->m_baud !=19200)|| 
          (pGlobalDialog->m_parity != EVENPARITY)|| 
          (pGlobalDialog->m_byteSize != 8)|| 
          (pGlobalDialog->m_rts != RTS_CONTROL_ENABLE)|| 
          (pGlobalDialog->m_stopBits != ONESTOPBIT)) 
          if (IDYES == AfxMessageBox("Load JOY SCC port setting defaults Y/N?\n Settings: 19200,8,E,1 and RTS enabled", MB_YESNO)) 
      { 
         pGlobalDialog->m_baud = 19200; 
         pGlobalDialog->m_parity = EVENPARITY; 
         pGlobalDialog->m_byteSize = 8; 
         pGlobalDialog->m_rts = RTS_CONTROL_ENABLE; 
         pGlobalDialog->m_stopBits = ONESTOPBIT; 
         pGlobalDialog->SaveApplicationSettings(); 
      } 
   } 
 
   //  
   portName.Format("%s %d:%d-%s-%s", portNameShort, pGlobalDialog->m_baud, pGlobalDialog->m_byteSize, parityNames[pGlobalDialog->m_parity], stopNames[pGlobalDialog->m_stopBits]); 
 
   portName += (IsMaster()?" -Master":" -Slave"); 
   description.Format("Starting comms emulation : %s", portName); 
   RSDataMessage(description); 
 
   // open the port etc... 
   if (OpenPort(portNameShort)) 
   { 
   COMMTIMEOUTS timeout; 
   BOOL err; 
   DWORD reason; 
      ConfigurePort(pGlobalDialog->m_baud, pGlobalDialog->m_byteSize, pGlobalDialog->m_parity, pGlobalDialog->m_stopBits, pGlobalDialog->m_rts, (NOPARITY==parity?FALSE:TRUE)); 
      //set the timeouts shorter 
 
      // fill timeout structure 
      GetCommTimeouts(h232Port, &timeout); 
      timeout.ReadIntervalTimeout = 100;           //  
      timeout.ReadTotalTimeoutConstant = 200; 
      timeout.ReadTotalTimeoutMultiplier = 0;      // #chars to read does not add to timeout amount 
      timeout.WriteTotalTimeoutConstant = 2000; 
      timeout.WriteTotalTimeoutMultiplier = 60;    // 60ms per char sent 
                                                 
      err = SetCommTimeouts(h232Port, &timeout); 
      reason = GetLastError(); 
   } 
   m_responseDelay = responseDelay; 
 
} 
 
// ------------------------------- MasterTimedOut --------------------------- 
BOOL CABCommsProcessor::MasterTimedOut() 
{ 
LONG tick = (LONG)GetTickCount(); 
 
   if (tick - (LONG)m_lastMasterTick > (LONG)m_ABmasterIDLETime) //PORT_MAX_IDLETIME*3) 
   { 
      return(TRUE); 
   } 
   return(FALSE); 
} 
 
// ------------------------------------- DoMaster ---------------------------- 
// called in the IDLE state 
void CABCommsProcessor::DoMaster() 
{ 
static m_masterTN[256]; 
 
   //check if scripts are running 
   if (pGlobalDialog->ScriptIsRunning()) 
      return; 
    
   // check if we may send 
   if (m_masterBusy && (ENG_STATE_IDLE == m_CPUstateEngineStep)) 
   { 
      if (MasterTimedOut() || m_masterWaiting) 
      { 
         m_lastMasterTick = GetTickCount();  // transaction has ended 
         m_masterBusy = FALSE; 
         m_masterWaiting = FALSE; 
         return; 
      } 
   } 
   if (m_masterWaiting) 
      return;  // not timed out yet, hold on 
    
   // check if it is time to send again 
   if (GetTickCount() - m_lastMasterTick > m_ABtimeoutValue)//ABMASTER_IDLETIME ) 
   {  // send a message. 
   CPLCApplication *pApp; 
   WORD fileNumber, numElements = 10; 
static  WORD lastShieldNumber;      // starting shield number is arbitrary 
static  WORD lastCutterDirection;   // direction is arbitrary 
 
      // go back to shield 0 (non-existent) to make us move to shield 1 on the next transaction 
      if (lastShieldNumber >= JOY_NUMSHIELDS) 
         lastShieldNumber = 0; 
 
      m_masterBusy = TRUE; 
      m_masterWaiting = FALSE; 
      // we need to use the App layer to do this, so let's verify our app layer object 
      pApp = (CPLCApplication *)this; 
      ASSERT(pApp->IsKindOf(RUNTIME_CLASS(CPLCApplication))); 
 
      if (m_JOYreadVariables && (0 >= pApp->m_surfacePolls)) 
      { // do a shearer read 
         fileNumber = 100 + (m_masterRandom % (WORD)JOY_NUMSHIELDS) + (WORD)JOY_FIRSTSHIELD; 
         pApp->FetchPLCBlock(AB_MASTER_ID, // source 
                             JOY_SURFACEPLC_ID, 
                             GetNextTransactionID(JOY_SURFACEPLC_ID), // increments the TNS as well 
                             fileNumber, 
                             0,         // start offset 
                             numElements//40         // # elements 
                            ); 
         pApp->m_surfacePolls = JOY_SURFACEPLCPOLLCOUNT; 
      } 
      else 
      { 
         if (m_JOYwriteVariables) 
         { 
            // decide which PLC file # to send data for 
            if (0 == m_masterRandom % 10) 
            { 
            WORD direction, cutterPosition; 
            WORD lastCutterPosition;    // position 
            BOOL oldDirection; 
 
               // every 10 transactions, we send cutter motor position info again 
               fileNumber = 99; 
               numElements = 3;  // cutter info is 3 items only 
               lastCutterPosition = PLCMemory[99][0]; 
 
               if ((lastCutterPosition > JOY_NUMSHIELDS)||(0 == lastCutterPosition))// cutter pos wrap check 
                  lastCutterPosition = 1; 
 
               direction = PLCMemory[99][1]; 
               oldDirection = direction; 
               cutterPosition = lastCutterPosition; 
               switch(direction) 
               { 
               case 1 : //up 
                  if (lastCutterPosition == JOY_NUMSHIELDS) 
                     PLCMemory.SetAt(99, 1, 2); // change direction to DOWN 
                  else 
                     PLCMemory.SetAt(99, 1, 1); 
                  break; 
               case 2 : // down 
               default: 
                  if (lastCutterPosition == 1) 
                     PLCMemory.SetAt(99, 1, 1); // change direction to UP 
                  else 
                     PLCMemory.SetAt(99, 1, 2); 
                  break; 
               } 
                
               // use direction to determine shield # 
               direction = PLCMemory[99][1]; 
               if (direction == oldDirection) 
               { // cutter does not actually move on the "turn-around" 
                  switch(direction) 
                  { 
                  case 1 : //up 
                     cutterPosition = lastCutterPosition+1; 
                     break; 
                  case 2 : // down 
                  default: 
                     cutterPosition = lastCutterPosition-1; 
                     break; 
                  } 
               } 
               // update things 
               PLCMemory.SetAt(99, 0, cutterPosition); 
               PLCMemory.SetAt(99, 2, JOY_NUMSHIELDS); 
            } 
            else 
            { 
               // increment 
               lastShieldNumber++; 
               fileNumber = lastShieldNumber + 100; 
            } 
            //fileNumber = 101 + (m_masterRandom % JOY_NUMSHIELDS) + JOY_FIRSTSHIELD; 
 
            if (0 == m_masterRandom % 10) // every 10th message is the cutter position 
            { 
               fileNumber = 99; 
            } 
            pApp->SendPLCBlock((BYTE)AB_MASTER_ID,       // source 
                               (BYTE)JOY_SURFACEPLC_ID,  // destination 
                               GetNextTransactionID(JOY_SURFACEPLC_ID), // increments the TNS as well 
                               fileNumber,  
                               JOY_SURFACEELEMENTS_START, 
                               numElements );  // 10 registers 
            pApp->m_surfacePolls --; 
         } 
         else 
            pApp->m_surfacePolls = 0;  // read again 
      } 
      m_masterRandom++; 
   } 
} 
    
// ------------------------------- StationIsEnabled --------------------------- 
// Return TRUE if station is enabled 
BOOL CABCommsProcessor::StationIsEnabled(LONG stationID) 
{ 
   if (stationID>0 && stationIDStationEnabled(stationID));//m_microTickState==1); 
   } 
   return TRUE; 
} // StationIsEnabled 
 
// --------------------------------- SendPLCMessage ------------------------------------ 
// pAppLayerMsg = buffer pointig to start of the app layer (stationID) 
// length = length of the app layer only (before DLE expansion) 
// 
BOOL CABCommsProcessor::SendPLCMessage(const BYTE* pAppLayerMsg, DWORD length)  // if False, then re-send 
{ 
BYTE telegram[MAX_AB_MESSAGELEN]; 
DWORD telegramLength; 
WORD  crc = 0; 
BYTE bcc = 0; 
BYTE *crcStartPtr; 
 
   crcStartPtr = (BYTE*)pAppLayerMsg; 
   m_noiseLength = 0;   // kill all stuff in the buffer before we start to respond 
 
   // send of DLE-STX 
   // calculate CRC 
   // send telegram (expand DLEs) 
   // send DLE-ETX and CRC 
 
   ////////////////////////////////////////////////////////// 
   // send DLE-STX 
   Send(2, txDLE_STX, FALSE, NULL); 
 
   // calc CRC 
   { 
   CHAR ETXBuff[2]; 
 
      crcStartPtr = (BYTE*)pAppLayerMsg; 
      CalcCRC(crcStartPtr, length, &crc);      // application layer CRC 
 
      ETXBuff[0] = 0x03; 
      CalcCRC((BYTE*)&ETXBuff[0], 1, &crc);      // include the ETX 
   } 
   // calc a bcc 
   bcc = CalcBCC(crcStartPtr, length); 
    
   // send telegram 
   Send(length, pAppLayerMsg, TRUE, NULL); 
    
   //make a copy in case it gets lost on the device side, and they NAK us 
   m_lastAppLength = length; 
   memcpy(m_lastAppBuffer, pAppLayerMsg, length); 
 
   //Send DLE-ETX 
   Send(2, txDLE_ETX, FALSE, NULL); 
 
   if (!m_useBCCchecks) 
   { 
	   // send CRC 
	   *(WORD*)telegram = crc; 
	   telegramLength = 2; 
	   Send(telegramLength, telegram, FALSE, NULL); 
   } 
   else 
   { 
	   // send a bcc 
	   *(BYTE*)telegram = bcc; 
	   telegramLength = 1; 
	   Send(telegramLength, telegram, FALSE, NULL); 
   } 
   return(TRUE); 
} 
 
// ----------------------------- SetEngineState ----------------------------- 
void CABCommsProcessor::SetEngineState(__EngineStates stateStep) 
{ 
   switch (stateStep) 
   { 
   case ENG_STATE_IDLE:    // wait for start 
         m_CPUstateEngineStepname = "idle"; 
         break; 
   case ENG_STATE_RECEIVE:   // RX rest of telegram + ETX + CRC 
         m_CPUstateEngineStepname = "rx request body"; 
         break; 
   case ENG_STATE_SENDACK:   // sending an ACK 
         m_CPUstateEngineStepname = "ack request"; 
         break; 
   case ENG_STATE_RESPOND:   // sending msg response 
         m_CPUstateEngineStepname = "respond"; 
         break; 
   case ENG_STATE_FINALACK:   // wait for final ACK 
         m_CPUstateEngineStepname = "wait for ack"; 
         break; 
   case ENG_STATE_MASTERIDLE: // master eneded last cycle, will start next transaction without waiting for an RX 
         m_CPUstateEngineStepname = "master end-of-cycle"; 
         m_masterHasWork = TRUE; // skip the RX, and do the DoMaster() again,  
                                 // since we just done one transaction, and want to do another right away 
         break; 
   default: 
         m_CPUstateEngineStepname = "unknown!!!"; 
         break; 
   } 
   m_CPUstateEngineStep = stateStep; 
} 
 
 
 
// ------------------------- GetNextTransactionID ---------------------------- 
WORD CABCommsProcessor::GetNextTransactionID(BYTE station) 
{ 
   return (++m_masterTN[station]); 
} 
 
// -------------------------- GetCurrentTransactionID ------------------------- 
WORD CABCommsProcessor::GetCurrentTransactionID(BYTE station) 
{ 
   return (m_masterTN[station]); 
} 
 
// -------------------------- SetCurrentTransactionID ------------------------- 
WORD CABCommsProcessor::SetCurrentTransactionID(BYTE station, WORD id) 
{ 
WORD last = m_masterTN[station]; 
   m_masterTN[station] = id; 
   return (last); 
} 
 
// ------------------------------ RSDataDebugger ------------------------------ 
void CABCommsProcessor::RSDataDebugger(const BYTE * buffer, LONG length, int transmit) 
{ 
CString debuggerString; 
BYTE *data; 
byte hiNib,loNib; 
 
   //convert BIN-ary to ASCII for display 
   data = new BYTE[(length*2)+1]; 
   for (int i = 0; i < length; i++) 
   { 
       hiNib = ( *(buffer+i) >>4) & 0x0f; 
       loNib = ( *(buffer+i)    ) & 0x0f; 
       data[(i*2)]   = ( (hiNib < 0x0A) ? ('0' + hiNib) : ('A' + hiNib-10) ); 
       data[(i*2)+1] = ( (loNib < 0x0A) ? ('0' + loNib) : ('A' + loNib-10) ); 
   } 
 
   data[(length*2)] = '\0'; 
 
   if (transmit) 
   { 
      m_lastTX = data; 
      if (length) 
         pGlobalDialog->OnCharactersSent(); 
   } 
   else 
   { 
      if (length) 
         m_lastRX = data; 
      if (length) 
         pGlobalDialog->OnCharactersReceived(); 
   } 
 
//   m_DiagnosticsFile.OutputScopeText((char*)data, (transmit ? FOREGROUND_IYELLOW: FOREGROUND_ICYAN)); 
   //call parent 
   CCommsProcessor::RSDataDebugger(buffer, length, transmit); 
 
   delete (data); 
} // RSDataDebugger 
 
// ------------------------------------------ Scentence ---------------------------- 
CString &Scentence(LPCTSTR text, BOOL startScentence) 
{ 
static CString scentence; 
 
   scentence = text; 
   if (startScentence) 
   { 
      if (islower(text[0])) 
         scentence.SetAt(0, toupper(text[0])); 
   } 
   else 
   { 
      if (isupper(text[0])) 
         scentence.SetAt(0, tolower(text[0])); 
   } 
   return(scentence); 
} 
 
// ------------------------------------------- Get232ErrorDisc ---------------------- 
// 
CString &Get232ErrorDisc(DWORD commError) 
{ 
DWORD mask = 0x01, index; 
BOOL and = FALSE; 
static CString description; 
#define AND    " and "; 
 
   description.Format ("x%X ", commError); 
   for (index = 0 ; index < 16; index++) 
   { 
      switch (commError & mask) 
      {                                                          
         case CE_BREAK    :    // The hardware detected a break condition. 
            if (and) 
               description += AND; 
            description += Scentence("The hardware detected a break condition", !and); 
            and = TRUE; 
            break; 
         case CE_FRAME    :    // The hardware detected a framing error. 
            if (and) 
               description += AND; 
            description += Scentence("The hardware detected a framing error", !and); 
            and = TRUE; 
            break; 
         case CE_IOE      :    // An I/O error occurred during communications with the device. 
            if (and) 
               description += AND; 
            description += Scentence("An I/O error occurred during communications with the device", !and); 
            and = TRUE; 
            break; 
         case CE_MODE     :    // The requested mode is not supported, or the hCommDev parameter is 
                               // invalid. If this bit set, this is the only valid error. 
            if (and) 
               description += AND; 
            description += Scentence("The requested mode is not supported", !and); 
            and = TRUE; 
            break; 
 
         case CE_OVERRUN  :    // A character-buffer overrun has occurred. The next character is lost. 
            if (and) 
               description += AND; 
            description += Scentence("A character-buffer overrun has occurred", !and); 
            and = TRUE; 
            break; 
         case CE_RXOVER   :    // An input buffer overflow has occurred. There is either no room in the input 
                               // buffer, or a character was received after the end-of-file (EOF) character 
                               //  was received. 
            if (and) 
               description += AND; 
            description += Scentence("An input buffer overflow has occurred", !and); 
            and = TRUE; 
            break; 
         case CE_RXPARITY :    // The hardware detected a parity error. 
            if (and) 
               description += AND; 
            description += Scentence("The hardware detected a parity error", !and); 
            and = TRUE; 
            break; 
         case CE_TXFULL   :    // The application tried to transmit a character, but the output buffer was full. 
            if (and) 
               description += AND; 
            description += Scentence("Transmision of a character failed, output buffer full", !and); 
            and = TRUE; 
            break; 
         default          : 
            break; 
      } 
      //next bit 
      mask = mask << 1; 
   } 
   description += "."; 
   return(description); 
} 
 
// --------------------------------- OnHWError --------------------------------- 
void CABCommsProcessor::OnHWError(DWORD dwCommError) 
{ 
   // restart interpreter 
   if (0 == dwCommError) 
   { 
      RSDataMessage("COMM IDLE: Restarting interpretation."); 
   } 
   else 
   { 
      RSDataMessage(Get232ErrorDisc(dwCommError)); 
   } 
   m_noiseLength = 0; 
   // TODO! if recieving a response, and it is corrupted in this way,  
   // send a DLE-ENQ and stay on this step for 3 retries 
   if (m_CPUstateEngineStep != ENG_STATE_IDLE) 
   { 
      if (dwCommError) 
         Send(2, txDLE_ENQ, FALSE, NULL); // send a ??? out 
 
      SetEngineState(ENG_STATE_IDLE); 
   } 
} 
 
// ------------------------------- RSStateChanged ----------------------- 
void CABCommsProcessor::RSStateChanged(DWORD state) 
{ 
   EnterCriticalSection(&stateCS); 
   if (NULL==pGlobalDialog) 
      return; 
   pGlobalDialog->m_ServerRSState = state; 
   LeaveCriticalSection(&stateCS); 
} // RSStateChanged 
 
// ------------------------------ RSDataMessage ------------------------------ 
void CABCommsProcessor::RSDataMessage(LPCTSTR msg) 
{ 
CString message; 
   EnterCriticalSection(&stateCS); 
   message = "##"; 
   message += msg; 
   //OutputDebugString("##"); 
   OutputDebugString(message); 
   OutputDebugString("\n"); 
   if (NULL!=pGlobalDialog) 
      pGlobalDialog->AddCommsDebugString(message); 
   LeaveCriticalSection(&stateCS); 
} 
 
// ------------------------------ GetRALength -------------------------------- 
// Return the # bytes still to read 
LONG CABCommsProcessor::GetRALength() 
{ 
WORD minFrameLength = 18; 
 
   if (this->m_useBCCchecks) 
      minFrameLength --; 
 
   switch (m_CPUstateEngineStep) 
   { 
      case ENG_STATE_IDLE: 
      case ENG_STATE_FINALACK: 
         if ((m_noiseLength==1) && (m_noiseBuffer[0] == ALLENBRADLEY_DLE)) 
            return(1); 
          return(2); 
          break; 
      case ENG_STATE_RECEIVE: 
         { 
         LONG length=1; 
            if (m_noiseLength < minFrameLength) 
               length = minFrameLength-m_noiseLength; 
            if ((m_noiseLength > AB_LENGTHFRAMEOFF) &&  
                ((m_noiseBuffer[AB_COMMANDBYTEOFF]== ALLENBRADLEY_SLC_CMD) && 
                 (m_noiseBuffer[AB_FUNCBYTEOFF]== ALLENBRADLEY_WORD_WRITE)) 
               ) 
            { 
            DWORD correctDataLen; 
               // write data function, read up to # registers sent 
               // calculate minimum length for a 4-byte address, the data and the ETX and CRC 
               correctDataLen = m_noiseBuffer[AB_LENGTHFRAMEOFF] + 4 + (AB_LENGTHFRAMEOFF+1) + 4; 
               length = correctDataLen-m_noiseLength; 
               if (length <=0) 
                  length=1; 
            } 
            return (length); 
         } 
         break; 
   } 
   return(1); 
} 
 
 
// --------------------------------- ProcessData ----------------------------- 
// Interpret MODBUS request pBuffer, and respond to it. 
// 
BOOL CABCommsProcessor::OnProcessData(const CHAR *pBuffer, DWORD numBytes, BOOL *discardData) 
{ 
int   i=0; 
WORD  guardword1=1; 
WORD  guardword2=2; 
WORD minFrameLength = 18; 
WORD minRESP_FrameLength = 12; 
 
   if (m_useBCCchecks) 
   { 
      minFrameLength--; 
      minRESP_FrameLength--; 
   } 
 
   // build noise telegram 
   if (numBytes) 
   { //append recieved bytes to the noise telegram 
      if (m_noiseLength + numBytes >= sizeof(m_noiseBuffer)) 
      { 
         RSDataMessage("OVERFLOW:Restarting interpretation."); 
 
         m_noiseLength = 0; 
         SetEngineState(ENG_STATE_IDLE); 
         return(TRUE); 
      } 
      // append bytes to noise telegram 
      memcpy(&m_noiseBuffer[m_noiseLength], pBuffer, numBytes); 
      m_noiseLength += numBytes; 
      *discardData = TRUE; 
   } 
   //else 
      Sleep(0); 
    
//   ASSERT((m_noiseLength<50) || (0==numBytes)); 
   if (m_masterBusy && MasterTimedOut()) 
   { // SEND message timed out 
      m_noiseLength = 0; 
      SetEngineState(ENG_STATE_IDLE); 
   } 
 
   switch (m_CPUstateEngineStep) 
   { 
      case ENG_STATE_MASTERIDLE : 
         SetEngineState(ENG_STATE_IDLE); 
      case ENG_STATE_IDLE : 
         m_messageNAKs = 0; 
         if (IsMaster()) 
         { 
            // PLC master may initiate a transaction now 
            DoMaster(); 
            if ((m_masterBusy && (!m_masterWaiting))) 
            { 
               return(FALSE); 
            } 
            // otherwize, master is idle, Or waiting for response, continue. 
         } 
         if (m_noiseLength >= 2) 
         { 
            if (( m_noiseBuffer[0] == ALLENBRADLEY_DLE)&& 
                (m_noiseBuffer[1] == ALLENBRADLEY_STX )) 
            { 
               RSDataMessage("DLE-STX recieved"); 
               SetEngineState(ENG_STATE_RECEIVE); 
               if (m_noiseLength > 2) 
                  return(OnProcessData((char*)m_noiseBuffer, 0, discardData)); 
               return TRUE; 
            } 
            // strip the leading char from the noise buffer 
            m_noiseLength--; 
            memmove(m_noiseBuffer, &m_noiseBuffer[1], m_noiseLength); 
            // recurse to keep looking at the buffer untill we get a match or an empty buffer 
            if (m_noiseLength >= 2) 
            { 
               return(OnProcessData((char*)m_noiseBuffer, 0, discardData)); 
            } 
            return(TRUE); 
         } 
         //wait for more data 
         return(FALSE); 
         break; 
      case ENG_STATE_RECEIVE : 
         // DLE STX ---> DST SRC CMD STS TNS TNS FNC CNT FIL ADDR ADDR [ADDR] .... DLE ETX CRC CRC 
         { 
         BYTE  nodleBuffer[MAX_ABPORT_MESSAGELEN]; 
         DWORD nodleLen; 
         BOOL valid; 
 
            if (m_noiseLength >= 2) 
            { 
               if (( m_noiseBuffer[0] == ALLENBRADLEY_DLE)&& 
                   (m_noiseBuffer[1] == ALLENBRADLEY_ACK )) 
               { 
                  RSDataMessage("GOT ACK: await remaining response."); 
                  // strip these 2 chars 
                  m_noiseLength-= 2; 
                  memmove(m_noiseBuffer, &m_noiseBuffer[2], m_noiseLength); 
                  memset(&m_noiseBuffer[m_noiseLength], 0xAA, 2); 
               } 
            } 
            // strip double DLE's  
            RemoveDLE(m_noiseBuffer, m_noiseLength, nodleBuffer, &nodleLen); 
 
            valid= (nodleLen >= minFrameLength); 
            if ((nodleLen >= minRESP_FrameLength/*12*/) && ((nodleBuffer[AB_COMMANDBYTEOFF] & 0x40)==0x40)) // command response 
               valid = TRUE; 
            if (valid) 
            { 
            CHAR ETXBuff[2]; 
            BYTE *crcStartPtr; 
            WORD  crc = 0; 
            WORD *crcPtr; 
            BYTE  bccCode; 
            BYTE *bccPtr; 
            BOOL checksummingOK = FALSE; 
 
               //memcpy(&nodleBuffer[nodleLen], &m_receivedBuffer[m_noiseLength-2],2); 
 
               crcPtr = (WORD*)&nodleBuffer[nodleLen-2];   //skip the DLE STX 
               bccPtr = (BYTE*)&nodleBuffer[nodleLen-1];   
               crcStartPtr = (BYTE*)&nodleBuffer[2]; 
 
               crc = 0; 
               CalcCRC(crcStartPtr, nodleLen-6, &crc);      // Only one buffer to calc crc of 
               bccCode = CalcBCC(crcStartPtr, nodleLen-5);       
               // include the ETX, remember that the DLE gets ignored 
               ETXBuff[0] = 0x03; 
               CalcCRC((BYTE*)&ETXBuff[0], 1, &crc);      // include the ETX 
 
               if (!m_useBCCchecks) 
                  checksummingOK = (crc == *crcPtr); 
               else 
                  checksummingOK = (bccCode == *bccPtr); 
 
               if (checksummingOK) 
               { 
               CABMessage msg(&nodleBuffer[2], nodleLen-2-4, FALSE); 
 
                  ActivateStation((BYTE)nodleBuffer[AB_SOURCEBYTEOFF]); 
                  ActivateStation((BYTE)nodleBuffer[AB_DESTINATIONBYTEOFF]); 
                  if (pGlobalDialog->StationEnabled(msg.stationIDDest)) 
                  { 
                  DWORD dataLength = nodleLen-2-4; // exclude the STX, ETX and CRC portions 
                     // send an ACK, and process the message 
                     RSDataMessage("ACK device message."); 
                     Send(2, txDLE_ACK, FALSE, NULL); 
                     if (pGlobalDialog->m_useBCC) 
                        dataLength++; 
                     BOOL result = OnMessageReceived(&nodleBuffer[2], dataLength ); //(-2-4)  
                     // will do it's own SetEngineState(ENG_STATE_FINALACK); 
                  } 
                  else 
                  { 
                  CString message; 
                     message.Format("Station %d is off-line, no response will be sent", msg.stationIDDest); 
                     RSDataMessage(message); 
                     SetEngineState(ENG_STATE_IDLE); 
                  } 
                  m_noiseLength = 0; 
                  return (TRUE); 
               } 
               else 
               { 
               DWORD etxPos=-1; 
               DWORD stxPos = -1; 
 
                  // try find a DLE-ETX 
                  if (FindFragment(nodleBuffer, nodleLen, txDLE_ETX, 2, &etxPos)) 
                     if (etxPos+4 <= nodleLen) 
                  { 
                     FindFragment(nodleBuffer, nodleLen, txDLE_STX, 2, &stxPos); 
                     if (stxPos < etxPos) 
                     { // prevent parsing of an STX occuring after an ETX 
                        RSDataMessage("Cannot match STX-ETX framing, removing DLE-ETX."); 
                        m_noiseLength-= etxPos+2; 
                        // swallow all chars up to & including the DLE-ETX 
                        memmove(m_noiseBuffer, &m_noiseBuffer[etxPos+2], m_noiseLength); 
                        memset(&m_noiseBuffer[m_noiseLength], 0xAA, sizeof(m_noiseBuffer)-m_noiseLength); 
                        SetEngineState(ENG_STATE_IDLE); 
                        return(TRUE); 
                     } 
                     crcStartPtr = (BYTE*)&nodleBuffer[stxPos + 2]; 
                     crcPtr = (WORD*)&nodleBuffer[etxPos+2];   //skip the DLE STX 
                     crc = 0; 
                     bccCode = 0; 
                     ASSERT(stxPos > etxPos); // Fix this possible - number 
                     CalcCRC(crcStartPtr, etxPos-2- stxPos, &crc);      // Only one buffer to calc crc of 
                      
                     bccCode = CalcBCC(crcStartPtr, etxPos-2- stxPos); 
                     // include the ETX, remember that the DLE gets ignored 
                     ETXBuff[0] = 0x03; 
                     CalcCRC((BYTE*)&ETXBuff[0], 1, &crc);      // include the ETX 
 
                     if (!m_useBCCchecks) 
                        checksummingOK = (crc == *crcPtr); 
                     else 
                        checksummingOK = (bccCode == *bccPtr); 
 
                     if (checksummingOK) 
                     { 
                        ActivateStation((BYTE)nodleBuffer[AB_SOURCEBYTEOFF]); 
                        ActivateStation((BYTE)nodleBuffer[AB_DESTINATIONBYTEOFF]); 
                   
                        // send an ACK, and process the message 
                        RSDataMessage("ACK device message."); 
                        Send(2, txDLE_ACK, FALSE, NULL); 
                        BOOL result = OnMessageReceived(&nodleBuffer[2], nodleLen-2-4); //(-2-4) exclude the STX, ETX and CRC portions 
                        // will do it's own SetEngineState(ENG_STATE_FINALACK); 
                        //m_noiseLength = 0; 
                        return (TRUE); 
                     } 
                     else 
                     {  // kill this frame, I cannot match a CRC for it 
                        if (FindFragment(m_noiseBuffer, m_noiseLength, txDLE_ETX, 2, &etxPos)) 
                        { 
                           RSDataMessage("Skipping frame, cannot match the CRC."); 
                           m_noiseLength-= etxPos+4; 
                           memmove(m_noiseBuffer, &m_noiseBuffer[etxPos+4], m_noiseLength); 
                           SetEngineState(ENG_STATE_IDLE); 
                           return(TRUE); 
                        } 
                     } 
                  } 
               } 
 
            } 
         } 
         return(TRUE); 
         break; 
      case ENG_STATE_FINALACK : 
         if (m_noiseLength >= 2) 
         { 
            if (( m_noiseBuffer[0] == ALLENBRADLEY_DLE)&& 
                (m_noiseBuffer[1] == ALLENBRADLEY_ACK )) 
            { 
               // strip these 2 chars 
               m_noiseLength-= 2; 
               memmove(m_noiseBuffer, &m_noiseBuffer[2], m_noiseLength); 
               // determine next state 
               SetEngineState(ENG_STATE_IDLE);   //return to idle state 
               RSDataMessage("SEND: completed OK:"); 
               if (m_noiseLength) 
               { 
                  // process the leftover data/next request in our buffer 
                  return(OnProcessData((char*)m_noiseBuffer, 0, discardData)); 
               } 
               return TRUE; 
            } 
            // NAK 
            if (( m_noiseBuffer[0] == ALLENBRADLEY_DLE)&& 
                (m_noiseBuffer[1] == ALLENBRADLEY_NAK )) 
            { 
               m_messageNAKs++; 
               if (m_messageNAKs >= AB_MAXIMUM_NAKS) 
               { 
                  RSDataMessage("Too many NAKs, will abort:"); 
                  Send(2, txDLE_ENQ, FALSE, NULL); 
                  SetEngineState(ENG_STATE_IDLE);   // wait for ACK again 
                  m_noiseLength = 0; 
                  return TRUE; 
               } 
               RSDataMessage("NAK, will re-send:"); 
               SendPLCMessage(m_lastAppBuffer, m_lastAppLength); 
               SetEngineState(ENG_STATE_FINALACK);   // wait for ACK again 
               m_noiseLength = 0; 
               return TRUE; 
            } 
            // strip the leading char from the noise buffer 
            RSDataMessage("Bad device final ACK response, restarting interpreter:"); 
            SetEngineState(ENG_STATE_IDLE);   //return to idle state 
            m_noiseLength = 0; 
            return(TRUE); 
         } 
         //wait for more data 
         return(FALSE); 
          
         break; 
      default: 
         return(TRUE); 
         break; 
   } 
 
   return(TRUE); 
} 
 
 
// ----------------------------- LoadRegisters ----------------------------- 
// load binary dump of the register values from file. 
BOOL CABCommsProcessor::LoadRegisters() 
{ 
CFileException ex; 
CFile dat; 
LONG area; 
DWORD wordIndex; 
 
   if (!dat.Open("ABDATA.DAT", CFile::modeRead|CFile::shareDenyRead, &ex) ) 
   { 
      // complain if an error happened 
      // no need to delete the exception object 
 
      TCHAR szError[1024]; 
      ex.GetErrorMessage(szError, 1024); 
      OutputDebugString( "Couldn't open source file: "); 
      OutputDebugString( szError); 
       
      return FALSE; 
   } 
   // read it in 
   for (area=0;area < MAX_AB_MEMFILES ; area++) 
   { 
   DWORD numRead,totalRead=0; 
   WORD dataWord; 
   DWORD maxIndex = PLCMemory[area].GetSize(); 
 
      // lock the memory for reading 
//      CMemWriteLock lk(pGlobalDialog->m_pMemWriteSync); 
      CMemWriteLock  lk(PLCMemory.GetMutex()); 
      // loop thru all registers (WORD) 
      if (!lk.IsLocked()) 
      { 
         for (wordIndex=0; wordIndex < maxIndex/*MAX_MOD_MEMWORDS*/; wordIndex++) 
         { 
            numRead = dat.Read((BYTE*)&dataWord, sizeof(WORD)); 
            PLCMemory.SetAt(area, wordIndex, dataWord); 
            totalRead +=numRead; 
            if (numRead != sizeof(WORD)) 
               return FALSE; 
         } 
         // Read past the rest of the block 
         while (wordIndex < MAX_MOD_MEMWORDS) 
         { 
            numRead = dat.Read((BYTE*)&dataWord, sizeof(WORD)); 
            totalRead +=numRead; 
            if (numRead != sizeof(WORD)) 
               return FALSE; 
            wordIndex++; 
         } 
      } 
      else 
      { 
      CString errorMsg; 
         //error 
         errorMsg = "an error occured!";//errorMsg.LoadString(IDS_SYNC_READING); 
         AfxMessageBox(errorMsg, MB_ICONEXCLAMATION); 
         return FALSE; 
      } 
   } 
   return TRUE; 
} // LoadRegisters 
 
 
// --------------------------------------- SaveRegisters --------------------------- 
// save a binary dump of the values to file. 
BOOL CABCommsProcessor::SaveRegisters() 
{ 
CFileException ex; 
CFile dat; 
LONG area; 
DWORD wordIndex; 
 
 
   if (!dat.Open("ABDATA.DAT", CFile::modeWrite | CFile::shareExclusive | CFile::modeCreate, &ex) ) 
   { 
      // complain if an error happened 
      // no need to delete the ex object 
 
      TCHAR szError[1024]; 
      ex.GetErrorMessage(szError, 1024); 
      OutputDebugString( "Couldn't open source file: "); 
      OutputDebugString( szError); 
       
      return FALSE; 
   } 
   // read it in 
   for (area=0;area < MAX_AB_MEMFILES; area++) 
   { 
   WORD wordData; 
   DWORD maxIndex = PLCMemory[area].GetSize(); 
      // lock the memory for writting 
      CMemWriteLock lk(PLCMemory.GetMutex()); 
      // loop thru all registers (WORD) 
      if (!lk.IsLocked()) 
      { 
         for (wordIndex=0; wordIndex < maxIndex/*MAX_MOD_MEMWORDS*/; wordIndex++) 
         { 
            wordData = PLCMemory[area][wordIndex]; 
            dat.Write((BYTE*)&wordData, sizeof(WORD)); 
         } 
         // Fill the rest with NULLs 
         while (wordIndex < maxIndex) 
         { 
            wordData = 0; 
            dat.Write((BYTE*)&wordData, sizeof(WORD)); 
            wordIndex++; 
         } 
      } 
      else 
      { 
      CString errorMsg; 
         errorMsg = "an error occured!";//.LoadString(IDS_SYNC_WRITTING); 
         AfxMessageBox(errorMsg, MB_ICONEXCLAMATION); 
         // error 
         return FALSE; 
      } 
   } 
    
   return TRUE; 
} // SaveRegisters 
 
// ------------------------------------------ TestReceiveMessage ----------------------------- 
BOOL CABCommsProcessor::TestReceiveMessage() 
{ 
   return(FALSE); 
} 
 
// ----------------------------------------- OnMessageReceived ------------------------------- 
BOOL CABCommsProcessor::OnMessageReceived(BYTE * pAppMessage, DWORD length) 
{ 
   return(FALSE); 
} 
 
// --------------------------- RemoveDLE ---------------------------------- 
LONG CABCommsProcessor::RemoveDLE(const BYTE * buffer, DWORD len, BYTE * destBuff, DWORD *destLen) 
{ 
DWORD dleCount=0; 
BYTE *pSource, *pDest; 
DWORD i=0; 
 
   pSource = (BYTE*)buffer; 
   pDest = destBuff; 
   *destLen = len; 
   while( i < len) 
   { 
      if ((*pSource == ALLENBRADLEY_DLE) &&     // Detect first DLE and post inc Ptr 
       // check if it's a double 
         (pSource[1] == ALLENBRADLEY_DLE)) 
      { // copy one of the doubled chars only 
         pSource++; 
         i++; 
         if (i < len) 
         { // verify this was not the buffer end 
            *pDest++ = *pSource++; 
            i++; 
            dleCount++; 
         } 
      } 
      else 
      { // copy char 
         *pDest++ = *pSource++; 
         i++; 
      } 
   }    
   // subtract the # of double-DLEs actually removed 
   *destLen -= dleCount; 
   return(dleCount); 
}