www.pudn.com > mod_rssim6.zip > MOD232CommsProcessor.cpp
/////////////////////////////////////////////////////////////////////////////
//
// FILE: MODCommsProcessor.cpp : implementation file
//
// See "_README.CPP"
//
// implementation of the CMOD232CommsProcessor class.
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "mod_RSsim.h"
#include "MOD232CommsProcessor.h"
#include "ABCommsProcessor.h"
#include "mod_RSsimdlg.h"
#include "RS232Noise.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/2);
*pWorkArea++ = LOBYTE(address/2);
*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++ = (PLCMemory[GetAddressArea(functionCode)][address]?0xFF : 0x00);
//*pWorkArea++ = 0x00;
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 ------------------------------
void CMOD232CommsProcessor::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)
{
if (length)
pGlobalDialog->OnCharactersSent();
debuggerString.Format("TX:%s\n", data);
}
else
{
if (length)
pGlobalDialog->OnCharactersReceived();
debuggerString.Format("RX:%s\n", data);
}
// Send to debugger list-box
if (length)
pGlobalDialog->AddCommsDebugString(debuggerString);
#ifdef _COMMS_DEBUGGING
OutputDebugString(debuggerString);
#endif
delete (data);
} // RSDataDebugger
// ------------------------------- RSStateChanged -----------------------
void CMOD232CommsProcessor::RSStateChanged(DWORD state)
{
EnterCriticalSection(&stateCS);
if (NULL==pGlobalDialog)
return;
pGlobalDialog->m_ServerRSState = state;
LeaveCriticalSection(&stateCS);
} // RSStateChanged
// ------------------------------ RSDataMessage ------------------------------
void CMOD232CommsProcessor::RSDataMessage(LPCTSTR msg)
{
EnterCriticalSection(&stateCS);
OutputDebugString("##");
if (NULL!=pGlobalDialog)
pGlobalDialog->AddCommsDebugString(msg);
LeaveCriticalSection(&stateCS);
}
// ------------------------------ OnProcessData --------------------------------
BOOL CMOD232CommsProcessor::OnProcessData(const CHAR *pBuffer, DWORD numBytes, BOOL *discardData)
{
// 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);
}
memcpy(&m_noiseBuffer[m_noiseLength], pBuffer, numBytes);
m_noiseLength += numBytes;
*discardData = TRUE;
}
if (m_noiseLength < MODBUS_NORMAL_LEN)
return(FALSE);
CMODMessage::SetEthernetFrames(FALSE);
CMODMessage msg((char*)m_noiseBuffer, m_noiseLength);
if (msg.CRCOK())
{
BOOL ret;
// build a response etc
ret = ProcessData((char*)m_noiseBuffer, msg.overalLen + 2); //+2 for the CRC
m_noiseLength = 0;
return(ret);
}
else
{
// try strip away leading byte "noise"?
m_noiseLength--;
memmove(m_noiseBuffer, &m_noiseBuffer[1], m_noiseLength);
return(TRUE);
}
*discardData = FALSE;
return(FALSE);
}
// --------------------------------- ProcessData -----------------------------
// Interpret MODBUS request pBuffer, and respond to it.
//
BOOL CMOD232CommsProcessor::ProcessData(const CHAR *pBuffer, DWORD numBytes)
{
BYTE telegramBuffer[MAX_RX_MESSAGELENGTH+MODBUS_FRAME_LENGTH_MAX];
CHAR debugStr[160];
BYTE *pDataPortion;
int i=0;
WORD guardword1=1;
WORD requestMemArea; // telegram read/write are being referenced=0..MAX_MOD_MEMTYPES
WORD guardword2=2;
WORD startRegister, endRegister, MBUSerrorCode=0;
BOOL MBUSError = TRUE;
WORD numBytesInReq;
WORD numRegs;
CString deb;
m_debuggerStep = 100;
// inc counter
pGlobalDialog->PacketsReceivedInc();
// simulate the I/O and network delays of a real PLC
Sleep(m_responseDelay);
//
// Parse the telegram
//
// 1. break up the telegram
memcpy(telegramBuffer, pBuffer, numBytes);
CMODMessage::SetEthernetFrames(FALSE);
CMODMessage modMsg((CHAR*)telegramBuffer, numBytes);
ActivateStation(modMsg.stationID);
if (!StationIsEnabled(modMsg.stationID))
return(TRUE);
// 2. parse it
if (!modMsg.CRCOK())
{
// bail
}
//Get memory area which to update or retrieve from
requestMemArea = modMsg.GetAddressArea(modMsg.functionCode);
if (requestMemArea >= MAX_MOD_MEMTYPES)
{
// TO DO!
// handle the error
Beep(2000,200);
requestMemArea = 3; // for now just default to "Holding" for now!
}
// validate the request is a valid command code
startRegister = modMsg.address;
//endRegister = startRegister + modMsg.byteCount/2;
endRegister = startRegister + modMsg.byteCount;
if ((modMsg.functionCode == MOD_READ_COILS)|| // 01
(modMsg.functionCode == MOD_READ_DIGITALS)|| // 02
(modMsg.functionCode == MOD_READ_REGISTERS)|| // 04
(modMsg.functionCode == MOD_READ_HOLDING)|| // 03
(modMsg.functionCode == MOD_READ_EXTENDED)|| // 14
(modMsg.functionCode == MOD_WRITE_SINGLE_COIL)|| // 05
(modMsg.functionCode == MOD_WRITE_MULTIPLE_COILS)|| // 0F
(modMsg.functionCode == MOD_WRITE_HOLDING)|| // 10
(modMsg.functionCode == MOD_WRITE_SINGLEHOLDING)|| // 06 (testing)
(modMsg.functionCode == MOD_WRITE_EXTENDED) // 15
)
{
// Check the request length against our PDU size.
if ((modMsg.functionCode == MOD_READ_COILS)|| // 01
(modMsg.functionCode == MOD_READ_DIGITALS)|| // 02
(modMsg.functionCode == MOD_WRITE_MULTIPLE_COILS)) // 0F
numBytesInReq = modMsg.byteCount/8; // # bits
else
numBytesInReq = modMsg.byteCount*2; // # registers
if (numBytesInReq > m_PDUSize)
{
MBUSError = TRUE;
MBUSerrorCode = MOD_EXCEPTION_ILLEGALVALUE; // too long data field
}
else
MBUSError = FALSE;
}
else
{
MBUSError = TRUE;
MBUSerrorCode = MOD_EXCEPTION_ILLEGALFUNC; // 01
}
/* if (modMsg.m_packError)
{
// request message has a corrupted field somewhere
MBUSError = TRUE;
MBUSerrorCode = MOD_EXCEPTION_ILLEGALVALUE; // too long data field
}*/
// 3. build response
CMODMessage responseModMsg(modMsg); //Call copy constructor
if ((m_MOSCADchecks)&& // Is a (Analog/holding/extended register)
((requestMemArea == 2)||(requestMemArea == 3)||(requestMemArea == 4))
)
{
WORD startTable,endTable; // table #
WORD startCol,endCol; // col #
endTable = MOSCADTABLENUMBER(endRegister); // MOSCAD specify register # on the wire for the formula
endCol = MOSCADCOLNUMBER(endRegister);
startTable = MOSCADTABLENUMBER(startRegister);
startCol = MOSCADCOLNUMBER(startRegister);
// test that this request does not bridge 2 columns.
// , else we cannot job/request them together.
if ((endTable != startTable) ||
(endCol != startCol))
{
MBUSError = TRUE;
MBUSerrorCode = MOD_EXCEPTION_ILLEGALADDR; // 02
}
}
// if we want to disable all writes
if ((m_disableWrites) &&
((modMsg.functionCode == MOD_WRITE_SINGLE_COIL) ||
(modMsg.functionCode == MOD_WRITE_SINGLEHOLDING) ||
(modMsg.functionCode == MOD_WRITE_MULTIPLE_COILS) ||
(modMsg.functionCode == MOD_WRITE_HOLDING) ||
(modMsg.functionCode == MOD_WRITE_EXTENDED)
)
)
{
CString deb;
MBUSError = TRUE;
MBUSerrorCode = MOD_EXCEPTION_ILLEGALFUNC; // 02
deb.Format("Writting to registers or I/O is disabled!\n");
OutputDebugString(deb);
RSDataMessage(deb);
}
// do a address+length range check too
if (!MBUSError)
if (PLCMemory[requestMemArea].GetSize() < endRegister)
{
MBUSError = TRUE;
MBUSerrorCode = (PLCMemory[requestMemArea].GetSize() < startRegister ?
MOD_EXCEPTION_ILLEGALADDR:MOD_EXCEPTION_ILLEGALVALUE); // 02
}
if (MBUSError)
{
CString msg;
msg.Format("Modbus message in error x%02X\n", MBUSerrorCode);
OutputDebugString(msg);
RSDataMessage(msg);
}
// 1st 3 bytes + any others up to data get
// added in at this time
responseModMsg.BuildMessagePreamble(MBUSError,
MBUSerrorCode);
// A read must now pack into dataPtr onwards! and calc len etc!
// writes must update our mem areas accordingly.
if (!MBUSError)
{
// 4. fill in the data portion of telegram
switch (modMsg.functionCode)
{
case MOD_READ_COILS : // 01 READ
case MOD_READ_DIGITALS : // 02 READ
pDataPortion = responseModMsg.dataPtr; //Get offset to fill in data
if (MAX_MOD_MEMWORDS >= modMsg.address + modMsg.byteCount)
{
WORD memValueTemp;
WORD bitOffset;
//#ifdef _COMMS_DEBUGGING
deb.Format("Read In/output from %d for %d bits.\n", modMsg.address, modMsg.byteCount);
OutputDebugString(deb);
RSDataMessage(deb);
//#endif
// pack SIM memory: one WORD of sim memory for every BIT in the data.
// Ie the sim uses one word for each I/O bit in modbus.
numBytesInReq = modMsg.byteCount/8;
if (modMsg.byteCount%8) // if we overflow the byte
numBytesInReq++;
for (i=0; i = modMsg.address + modMsg.byteCount)
{
WORD memValueTemp;
//#ifdef _COMMS_DEBUGGING
deb.Format("Read Register from %d for %d .\n", modMsg.address, modMsg.byteCount);
OutputDebugString(deb);
RSDataMessage(deb);
//#endif
for (i=0; i = modMsg.address + modMsg.byteCount)
{
// lock memory for writting
CMemWriteLock lk(PLCMemory.GetMutex());
pDataPortion = responseModMsg.dataPtr; //Get offset to fill in data
if (!lk.IsLocked())
//...Update
switch (modMsg.functionCode)
{
case MOD_WRITE_SINGLE_COIL :
{
CString deb;
deb.Format("Write single output %d.\n", modMsg.address);
OutputDebugString(deb);
RSDataMessage(deb);
}
//data gets copied in now
if (m_modifyThenRespond)
PLCMemory.SetAt(requestMemArea, modMsg.address, (*(WORD*)modMsg.dataPtr?1:0));
pDataPortion = responseModMsg.dataPtr; //Get offset to fill in data
*pDataPortion++ = (PLCMemory[requestMemArea][(modMsg.address)+i] ? 0xFF : 0x00);
*pDataPortion++ = 0x00;
if (!m_modifyThenRespond)
PLCMemory.SetAt(requestMemArea, modMsg.address, (*(WORD*)modMsg.dataPtr?1:0));
numRegs = 1; // repaint 1 item
break;
case MOD_WRITE_MULTIPLE_COILS :
// unpack into the SIM memory on WORD of sim memory for every BIT in the data
//WORD numBytes;
numBytesInReq = modMsg.count/8;
if (modMsg.count%8) // if we overflow a byte
numBytesInReq++;
{
CString deb;
deb.Format("Write multiple outputs from %d for %d bits.\n", modMsg.address, modMsg.count);
OutputDebugString(deb);
RSDataMessage(deb);
}
numRegs = numBytesInReq; // repaint X bits
for (i=0;iGetListDisplayedWidth();
pGlobalDialog->RedrawListItems(modMsg.GetAddressArea(modMsg.functionCode),
modMsg.address/(cols),
(modMsg.address+(numRegs-1))/(cols)
); // repaint only the needed rows
}
break;
}
}
else
{ // error occurred
pDataPortion = responseModMsg.dataPtr; //Get offset to fill in data
}
if (0 == modMsg.stationID) // broadcast, don't respond at all.
return (TRUE);
// finnish building the response
responseModMsg.totalLen = (WORD)((LONG)pDataPortion-(LONG)responseModMsg.buffer);
responseModMsg.totalLen += MODBUS_CRC_LEN;
// 5. append the CRC
//OutputDebugString("Calculate CRC\n");
{
responseModMsg.BuildMessageEnd();
}
// 6. send it back
m_debuggerStep = 102;
#ifdef _COMMS_DEBUGGING
sprintf(debugStr, "Send %d bytes\n", responseModMsg.totalLen);
OutputDebugString(debugStr);
#endif
RSStateChanged(RSPORTCURRENTLY_WRITTING);
// Send it on the wire , but first kill any incomming messages as well so we don't overflow or anything
Purge();
if (!m_NoiseSimulator.GetErrorFrequency())
Send(responseModMsg.totalLen, (BYTE*)responseModMsg.buffer, debugStr);
else
{
m_NoiseSimulator.InjectErrors((CRS232Port*)this, (BYTE*)responseModMsg.buffer, responseModMsg.totalLen, debugStr);
RSDataMessage("Error simulation active\n");
}
m_debuggerStep = 103;
// inc our counter
pGlobalDialog->PacketsSentInc();
RSDataMessage(".\n");
/*if (TRUE)
{
BOOL linesSet;
linesSet = EscapeCommFunction(h232Port,
Sleep(300);
linesSet = EscapeCommFunction(h232Port,
} */
return (TRUE);
} // ProcessData
// ----------------------------- LoadRegistersIMP -----------------------------
// STATIC
BOOL CMOD232CommsProcessor::LoadRegistersIMP()
{
CFileException ex;
CFile dat;
LONG area;
DWORD wordIndex;
if (!dat.Open("MODDATA.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_MOD_MEMTYPES;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.LoadString(IDS_SYNC_READING);
AfxMessageBox(errorMsg, MB_ICONEXCLAMATION);
return FALSE;
}
}
return(TRUE);
}
// ----------------------------- LoadRegisters -----------------------------
// load binary dump of the register values from file.
BOOL CMOD232CommsProcessor::LoadRegisters()
{
BOOL ret = LoadRegistersIMP();
if (ret)
RSDataMessage("Register values loaded OK\n");
return (ret);
} // LoadRegisters
// --------------------------------------- SaveRegisters ---------------------------
// save a binary dump of the values to file.
BOOL CMOD232CommsProcessor::SaveRegisters()
{
BOOL ret = SaveRegistersIMP();
if (ret)
RSDataMessage("Register values saved OK\n");
return (ret);
}
// --------------------------------------- SaveRegistersIMP ---------------------------
// STATIC
BOOL CMOD232CommsProcessor::SaveRegistersIMP()
{
CFileException ex;
CFile dat;
LONG area;
DWORD wordIndex;
if (!dat.Open("MODDATA.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_MOD_MEMTYPES;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 < MAX_MOD_MEMWORDS)
{
wordData = 0;
dat.Write((BYTE*)&wordData, sizeof(WORD));
wordIndex++;
}
}
else
{
CString errorMsg;
errorMsg.LoadString(IDS_SYNC_WRITTING);
AfxMessageBox(errorMsg, MB_ICONEXCLAMATION);
// error
return FALSE;
}
}
return TRUE;
} // SaveRegistersIMP
// ------------------------------- ActivateStation ---------------------------
void CMOD232CommsProcessor::ActivateStation(LONG stationID)
{
if (stationID>0 && stationIDm_microTicksCountDown[stationID] = pGlobalDialog->GetAnimationOnPeriod();
// it will count down untill it extinguishes
}
} // ActivateStation
// ------------------------------- StationIsEnabled ---------------------------
// Return TRUE if station is enabled
BOOL CMOD232CommsProcessor::StationIsEnabled(LONG stationID)
{
if (stationID>0 && stationIDStationEnabled(stationID));//m_microTickState==1);
}
return TRUE;
} // StationIsEnabled