📄 mod232commsprocessor.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
//
// FILE: MODCommsProcessor.cpp : implementation file
//
// See "_README.CPP"
//
// implementation of the CMOD232CommsProcessor class.
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ABCommsProcessor.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CString CAB232CommsProcessor::m_protocolName = "Allen-Bradley DF1";
CString CMOD232CommsProcessor::m_protocolName = "MODBUS RTU";
CString CMODEthCommsProcessor::m_protocolName = "MODBUS Eth.";
// English meanings for MODBUS (Exception) error codes
PCHAR MODBUSplcError[9] =
{
"No error", // 0x0
"Illegal Function", // 0x1
"Illegal Data Address", // 0x2
"Illegal Data value", // 0x3
"Slave Device failure", // 0x4
"Acknowledge", // 0x5
"Slave device busy", // 0x6
"Negative acknowledge", // 0x7
"Memory Parity error" // 0x8
};
BOOL CMODMessage::m_protocolEthernet = FALSE; // default to serial
// ----------------------- constructor -----------------------------------
CMODMessage::CMODMessage(const CHAR * pMessageRX, DWORD len)
{
BYTE *pTelePtr;
BYTE *crcPtr; // CRC bytes here
BYTE *crcStartPtr = (BYTE*)pMessageRX;
WORD crc = 0;
static BYTE EthernetHeadder[4]= {
0,0,0,0
};
m_packError = FALSE;
frameEthernet = m_protocolEthernet;
frameASCII = FALSE;
// break it down
pTelePtr = (BYTE*)pMessageRX;
totalLen = (WORD)len;
count = 0;
if (m_protocolEthernet)
{
m_EthernetTransNum = *(WORD*)pTelePtr;
pTelePtr += sizeof(EthernetHeadder);
frameLength = *(WORD*)pTelePtr;
frameLength = SwapBytes(frameLength);
pTelePtr += sizeof(WORD);
}
//Pre-Amble
if (frameASCII)
{
stationID = (BYTE)UnPackASCIIField(&pTelePtr, 1, m_packError); // 2 char
functionCode = (BYTE)UnPackASCIIField(&pTelePtr, 1, m_packError); // 2 char
address = UnPackASCIIField(&pTelePtr, 2, m_packError); // 2 chars
}
else
{
stationID = (BYTE)UnPackField(&pTelePtr, 1); // 2 char
functionCode = (BYTE)UnPackField(&pTelePtr, 1); // 2 char
address = UnPackField(&pTelePtr, 2); // 2 chars
}
switch (functionCode)
{
case MOD_WRITE_SINGLE_COIL /*0x05*/ : //Write single coils dont have count bytes
// Therefore just adjust buffer to go through common unpacker/write-updater.
/*if (0xff == *pTelePtr)
{
*pTelePtr = 0x00;
*(pTelePtr+1) = 0x01;
}
else
{
*pTelePtr = 0x00;
*(pTelePtr+1) = 0x00;
} */
byteCount = 2;
overalLen = byteCount;
break;
case MOD_WRITE_MULTIPLE_COILS:
byteCount = UnPackField(&pTelePtr, 2); // 2 chars, the count is in bits
count = byteCount;
overalLen = (WORD)ceil(byteCount/8.0);
pTelePtr++; // increment past the #bytes byte which is the # bytes of data to expect (max 255)
overalLen += 3;
break;
case MOD_READ_COILS :
case MOD_READ_DIGITALS :
case MOD_READ_REGISTERS :
case MOD_READ_HOLDING :
case MOD_READ_EXTENDED :
// byteCount= # bytes to read
byteCount = UnPackField(&pTelePtr, 2); // 2 chars, the count is in REGISTERS
overalLen = 2;
break;
case MOD_WRITE_HOLDING:
case MOD_WRITE_EXTENDED:
// byteCount=# bytes if a write, else # bytes to read
byteCount = UnPackField(&pTelePtr, 2)*2; // 2 chars, the count is in bytes
overalLen = byteCount;
pTelePtr++; // increment past the #bytes byte which is the # bytes of data to expect (max 255)
overalLen+=3; // skip the 3 bytes for the req. size (byte) and length/quantity word
break;
case MOD_WRITE_SINGLEHOLDING:
byteCount = 2; // 2 chars, only 1 register
overalLen = byteCount;
break;
default : //All other commands not supported
//ASSERT (0);
overalLen = 0;
byteCount = 0;
break;
}
overalLen += 4; //now it points to the CRC
//Now (at last) pTelePtr points to the data to read/write
dataPtr = pTelePtr; // data starts here
// ASSERT(totalLen >= overalLen + MODBUS_CRC_LEN); // range-check here
if (totalLen < overalLen + MODBUS_CRC_LEN)
{
// turf this message it is duff!
overalLen = totalLen - MODBUS_CRC_LEN;
m_packError = TRUE;
}
if (frameEthernet)
{
ASSERT(totalLen >= overalLen - ETH_PREAMBLE_LENGTH); // range-check here
// Ethernet frame does not have an embedded CRC
if (totalLen < overalLen - ETH_PREAMBLE_LENGTH)
{
overalLen = totalLen - ETH_PREAMBLE_LENGTH;
// turf this message it is duff!
m_packError = TRUE;
}
else
crcCheckedOK = TRUE;
}
else
{
// check the CRC
crcPtr = (BYTE*)&pMessageRX[overalLen];
crcStartPtr = (BYTE*)pMessageRX;
crc = 0xffff;
CalcCRC(crcStartPtr, overalLen, &crc); // Only one buffer to calc crc of
if (*(WORD *)crcPtr != crc)
{
// CRC did not match
crcCheckedOK = FALSE;
}
else
crcCheckedOK = TRUE;
}
} // CMODMessage
// --------------------------- CMODMessage --------------------------
// PURPOSE: copy constructor used to build responses, does not actually
// copy the message.
CMODMessage::CMODMessage(const CMODMessage & oldMODMessage)
{
m_packError = FALSE;
//Copy in common stuff from both messages here!
this->stationID = oldMODMessage.stationID;
this->functionCode = oldMODMessage.functionCode;
this->address = oldMODMessage.address; // where to copy data from
this->byteCount = oldMODMessage.byteCount; // length of data to copy
this->overalLen = 0; //New message so 0 for now!
this->dataPtr = (BYTE*)buffer; //Nice an fresh pointer to the beginning!
this->m_EthernetTransNum = oldMODMessage.m_EthernetTransNum;
}
// ------------------------------ BuildMessagePreamble -------------------------
// PURPOSE: Builds the STN,FN and LEN bytes of the telegram.
// on completion dataPtr pointsto where the data must be packed in (if any)
CHAR * CMODMessage::BuildMessagePreamble(BOOL error, WORD errorCode)
{
BYTE *pWorkArea;
BYTE numBytesData;
//
pWorkArea = (BYTE*)buffer;
*pWorkArea++ = (BYTE)stationID;
if (error)
{ // error flag 80 + error meaning byte
*pWorkArea++ = (BYTE)(functionCode|0x80);
*pWorkArea++ = (BYTE)errorCode;
}
else
{
// normal processing
*pWorkArea++ = (BYTE)functionCode;
switch (functionCode)
{
case MOD_WRITE_HOLDING :
case MOD_WRITE_EXTENDED :
// HF fixed the return address.
*pWorkArea++ = HIBYTE(address);
*pWorkArea++ = LOBYTE(address);
*pWorkArea++ = HIBYTE(byteCount/2);
*pWorkArea++ = LOBYTE(byteCount/2);
break;
case MOD_WRITE_SINGLEHOLDING :
*pWorkArea++ = HIBYTE(address); // CDB fixed return address rev 7.0
*pWorkArea++ = LOBYTE(address);
//*pWorkArea++ = HIBYTE(PLCMemory[GetAddressArea(functionCode)][address]);
//*pWorkArea++ = LOBYTE(PLCMemory[GetAddressArea(functionCode)][address]);
break;
case MOD_WRITE_MULTIPLE_COILS :
*pWorkArea++ = HIBYTE(address);
*pWorkArea++ = LOBYTE(address);
*pWorkArea++ = HIBYTE(byteCount); // # bits actually
*pWorkArea++ = LOBYTE(byteCount);
break;
case MOD_WRITE_SINGLE_COIL :
//*pWorkArea++ = HIBYTE(address/2); //coil #
//*pWorkArea++ = LOBYTE(address/2);
*pWorkArea++ = HIBYTE(address); //coil # rev 7.4, no longer divide by 2, dunno why I did this (divide thing) before
*pWorkArea++ = LOBYTE(address);
break;
case MOD_READ_DIGITALS : // in
case MOD_READ_COILS : // out
numBytesData = (BYTE)ceil((float)byteCount/8.0); // # registers*2
*pWorkArea++ = numBytesData;
break;
case MOD_READ_REGISTERS :
case MOD_READ_HOLDING :
case MOD_READ_EXTENDED :
numBytesData = byteCount*2; // # registers*2
*pWorkArea++ = numBytesData;
break;
}
}
dataPtr = pWorkArea; // must now point to 1st byte of data
return (buffer);
} // BuildMessagePreamble
// ----------------------------- SetEthernetFrames --------------------------
// supply FALSE for normal serial 232 frames
BOOL CMODMessage::SetEthernetFrames(BOOL ethernetFrames/* = TRUE*/)
{
BOOL oldV = m_protocolEthernet;
m_protocolEthernet = ethernetFrames;
return (m_protocolEthernet);
}
// ------------------------------ BuildMessageEnd -------------------------------
// PURPOSE: glue a CRC onto the end of the message
// totalLen must be = the full telegram length (+CRC) when this is called.
CHAR * CMODMessage::BuildMessageEnd()
{
WORD length;
BYTE *pCrcStart = (BYTE*)buffer;
WORD crc = 0xFFFF;
BYTE *crcPtr;
// Add the CRC bytes
length = totalLen - MODBUS_CRC_LEN; //calc the CRC of all bytes but the 2 CRC bytes
CalcCRC(pCrcStart, length, &crc);
crcPtr = (BYTE*)&buffer[length];
*(WORD *)crcPtr = crc;
return (buffer);
} // BuildMessageEnd
// ------------------------------ GetAddressArea --------------------
// Returns: A supported MEM area index for any MOD address class
// Parameter: A modbus command (e.g. 3 =read holding register)
//
WORD CMODMessage::GetAddressArea(WORD classCode // modbus command byte
)
{
switch(classCode)
{
// read commands
case MOD_READ_COILS : return(0); break;
case MOD_READ_DIGITALS : return(1); break;
case MOD_READ_REGISTERS : return(2); break;
case MOD_READ_HOLDING : return(3); break;
case MOD_READ_EXTENDED : return(4); break;
// write commands
case MOD_WRITE_HOLDING : return(3); break;
case MOD_WRITE_SINGLEHOLDING : return(3); break;
case MOD_WRITE_SINGLE_COIL : return(0); break;
case MOD_WRITE_MULTIPLE_COILS : return(0); break;
case MOD_WRITE_EXTENDED : return(4); break;
}
return(3); //Default here for now, Should never get here anyways!
} // GetAddressArea
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
IMPLEMENT_DYNAMIC( CMOD232CommsProcessor, SimulationSerialPort);
//////////////////////////////////////////////////////////////////////
// constructor to open port
CMOD232CommsProcessor::CMOD232CommsProcessor(LPCTSTR portNameShort,
DWORD baud,
DWORD byteSize,
DWORD parity,
DWORD stopBits,
DWORD rts,
int responseDelay,
BOOL MOSCADchecks,
BOOL modifyThenRespond,
BOOL disableWrites) : SimulationSerialPort()
{
CString description;
InitializeCriticalSection(&stateCS);
m_noiseLength = 0;
description.Format("Starting comms emulation : %s", "MODBUS RS-232");
RSDataMessage(description);
// open the port etc...
if (OpenPort(portNameShort))
{
ConfigurePort(baud, byteSize, parity, stopBits, rts, (NOPARITY==parity?FALSE:TRUE));
}
m_responseDelay = responseDelay;
SetEmulationParameters(MOSCADchecks, modifyThenRespond, disableWrites);
m_pWorkerThread->ResumeThread(); //start thread off here
}
CMOD232CommsProcessor::~CMOD232CommsProcessor()
{
}
void CMOD232CommsProcessor::SetEmulationParameters(BOOL moscadChecks,
BOOL modifyThenRespond,
BOOL disableWrites)
{
m_MOSCADchecks = moscadChecks;
m_modifyThenRespond = modifyThenRespond;
m_disableWrites = disableWrites;
}
// ------------------------------ RSDataDebugger ------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -