map/das-dn/submodbusrtu/sub_modbus_rtu.cpp
2024-07-08 10:27:17 +08:00

1045 lines
34 KiB
C++

#include "sub_modbus_rtu.h"
/*!
* Constants which defines the format of a modbus frame. The example is
* shown for a Modbus RTU/ASCII frame. Note that the Modbus PDU is not
* dependent on the underlying transport.
*
* <code>
* <------------------------ MODBUS SERIAL LINE PDU (1) ------------------->
* <----------- MODBUS PDU (1') ---------------->
* +-----------+---------------+----------------------------+-------------+
* | Address | Function Code | Data | CRC/LRC |
* +-----------+---------------+----------------------------+-------------+
* | | | |
* (2) (3/2') (3') (4)
*
* (1) ... MB_SER_PDU_SIZE_MAX = 256
* (2) ... MB_SER_PDU_ADDR_OFF = 0
* (3) ... MB_SER_PDU_PDU_OFF = 1
* (4) ... MB_SER_PDU_SIZE_CRC = 2
*
* (1') ... MB_PDU_SIZE_MAX = 253
* (2') ... MB_PDU_FUNC_OFF = 0
* (3') ... MB_PDU_DATA_OFF = 1
* </code>
*/
CSubModbusRtuProcessItem::CSubModbusRtuProcessItem()
{
m_YCRegCount = 0;
m_YMRegCount = 0;
m_YXRegCount = 0;
m_pYCRegTab = NULL;
m_pYMRegTab = NULL;
m_pYXRegTab = NULL;
}
CSubModbusRtuProcessItem::~CSubModbusRtuProcessItem()
{
if (m_pYCRegTab)
{
delete[] m_pYCRegTab;
m_pYCRegTab = NULL;
}
if (m_pYMRegTab)
{
delete[] m_pYMRegTab;
m_pYMRegTab = NULL;
}
if (m_pYXRegTab)
{
delete[] m_pYXRegTab;
m_pYXRegTab = NULL;
}
}
void CSubModbusRtuProcessItem::Attach(int uid, int physicsAddress /* = 0 */, int commonAddress /* = 0 */, int originatorAddress /* = 0 */)
{
CPortProcessItem::Attach(uid, physicsAddress);
}
void CSubModbusRtuProcessItem::Release(void)
{
CPortProcessItem::Release();
}
CSubModbusRtuProcess::CSubModbusRtuProcess()
{
}
CSubModbusRtuProcess::~CSubModbusRtuProcess()
{
}
CPortProcessItem *CSubModbusRtuProcess::CreateItem(int ord)
{
return dynamic_cast<CPortProcessItem *>(new CSubModbusRtuProcessItem);
}
void CSubModbusRtuProcess::DestroyItem(int ord, BOOLEAN bDeleted /* = FALSE */)
{
CSubModbusRtuProcessItem *pItem = (CSubModbusRtuProcessItem *)GetItem(ord);
if (pItem != NULL && !bDeleted)
{
delete pItem;
return CPortProcess::DestroyItem(ord, TRUE);
}
return CPortProcess::DestroyItem(ord, bDeleted);
}
BOOLEAN CSubModbusRtuProcess::OnPreCreate(int id)
{
int i;
int uid;
int port;
BYTE addr;
DWORD ulBaudRate;
CSubModbusRtuProcessItem *pItem;
if (!CPortProcess::OnPreCreate(id)) return FALSE;
port = config.processes[id].order;
if (port < 0 || port >= HARDWARE_PORTS_NUM) return FALSE;
ulBaudRate = config.hardware.ports[port].baud;
if (ulBaudRate > 19200)
{
usTimerT35_50us = 1750; /* 1800us. */
}
else
{
/* The timer reload value for a character is given by:
*
* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
* = 11 * Ticks_per_1s / Baudrate
* = 220000 / Baudrate
* The reload for t3.5 is 1.5 times this value and similary
* for t3.5.
*/
usTimerT35_50us = 38500000L / ulBaudRate;
}
for (i = 0; i < PROCESS_UNIT_NUM; i++)
{
pItem = (CSubModbusRtuProcessItem *)GetItem(i);
if (NULL == pItem) continue;
uid = GetUnitID(i);
if (uid < 0 || uid >= UNIT_NUM) continue;
if (GetUnitAddr(uid, &addr, 1))
{ //获取单元地址成功,则该段原地址采用配置地址,否则该单元为无效地址。
pItem->Attach(uid, addr);
//pItem->m_addr = addr; //配置地址
}
pItem->m_YCRegCount = GetUnitYCCount(uid);
if (m_YC_Type == M_ME_NC)
{
pItem->m_YCRegCount = (GetUnitYCCount(uid) << 1); //采用浮点数上传
}
pItem->m_YMRegCount = (GetUnitYMCount(uid) << 1); //遥脉采用4字节传输
pItem->m_YXRegCount = GetUnitYXCount(uid);
if (pItem->m_YCRegCount)
{
pItem->m_pYCRegTab = new WORD[pItem->m_YCRegCount];
if (pItem->m_pYCRegTab == NULL) return FALSE;
}
if (pItem->m_YMRegCount)
{
pItem->m_pYMRegTab = new WORD[pItem->m_YMRegCount];
if (pItem->m_pYMRegTab == NULL) return FALSE;
}
if (pItem->m_YXRegCount)
{
pItem->m_pYXRegTab = new BYTE[pItem->m_YXRegCount];
if (pItem->m_pYXRegTab == NULL) return FALSE;
}
}
return TRUE;
}
BOOLEAN CSubModbusRtuProcess::Run(void)
{
//if (!CPortProcess::Run()) return FALSE;
if (!CProcess::Run()) return FALSE;
SerialPortRead(usTimerT35_50us);
//数据发送
SerialPortWrite();
int i, j, uid;
FeedDog();
for (i = 0; i < PROCESS_UNIT_NUM; i++)
{
CSubModbusRtuProcessItem *pItem = (CSubModbusRtuProcessItem *)GetItem(i);
if (NULL == pItem) continue;
uid = pItem->GetUnitID();
if (uid < 0) continue;
//刷新寄存器
float *fvalue = NULL;
short *svalue = NULL;
DWORD *dvalue = NULL;
BYTE *bvalue = NULL;
if (m_YC_Type == M_ME_NC) fvalue = (float *)pItem->m_pYCRegTab;
else svalue = (short *)pItem->m_pYCRegTab;
for (j = 0; j < GetUnitYCCount(uid); j++)
{
if (m_YC_Type == M_ME_NC)
{
*fvalue = GetUnitYCReal(uid, j); fvalue++;
}
else
{
*svalue = (short)GetUnitYC(uid, j); svalue++;
}
}
dvalue = (DWORD *)pItem->m_pYMRegTab;
for (j = 0; j < GetUnitYMCount(uid); j++)
{
*dvalue = (DWORD)GetUnitYM(uid, j); dvalue++;
}
bvalue = (BYTE *)pItem->m_pYXRegTab;
for (j = 0; j < GetUnitYXCount(uid); j++)
{
*bvalue = (BYTE)GetUnitYX(uid, j); bvalue++;
}
int order;
BYTE action, result;
DWORD value;
BYTE by_value;
if (GetUnitYT(uid, order, value, action, result))
{
vLog(LOG_DEBUG, "Unit(%d) set point(%d) %d state is %s result is %s\n", uid, order, value, val_to_str(action, yt_state, "STATE=%d"), val_to_str(result, yt_result, "RESULT=%d"));
if (YTS_SELED == action && YTR_FAIL == result)
{
action = YTS_ABRED;
}
else if (YTS_SELREQ == action && YTR_OVER == result)
{
action = YTS_ABRED;
}
else if (YTS_SELING == action && YTR_OVER == result)
{
action = YTS_ABRED;
}
if (YTS_SELED == action)
{
SetUnitYT(uid, order, value, YTS_EXEREQ, YTR_IDLE);
vLog(LOG_DEBUG, "Unit(%d) set point(%d) %d state is YTS_EXEREQ result is YTR_IDLE.\n", uid, order, value);
}
else if (YTS_ABRED == action)
{
SetUnitYT(uid, order, value, YTS_ABRREQ, YTR_IDLE);
vLog(LOG_DEBUG, "Unit(%d) set point(%d) %d state is YTS_ABRREQ result is YTR_IDLE.\n", uid, order, value);
}
}
if (GetUnitYK(uid, order, by_value, action, result))
{
vLog(LOG_WARN, "Unit(%d) yk(%d) %s state is %s result is %s\n", uid, order, (by_value ? "CLOSE" : "TRIP"), val_to_str(action, yk_state, "STATE=%d"), val_to_str(result, yk_result, "RESULT=%d"));
if (YKS_SELED == action && YKR_FAIL == result)
{
action = YKS_ABRED;
}
else if (YKS_SELREQ == action && YKR_OVER == result)
{
action = YKS_ABRED;
}
else if (YKS_SELING == action && YKR_OVER == result)
{
action = YKS_ABRED;
}
if (YKS_ABRED == action)
{
SetUnitYK(uid, order, by_value, YKS_ABRREQ, YKR_IDLE);
vLog(LOG_WARN, "Unit(%d) yk(%d) %s state is YKS_ABRREQ result is YKR_IDLE.\n", uid, order, (by_value ? "CLOSE" : "TRIP"));
}
else if (YKS_SELED == action)
{
SetUnitYK(uid, order, by_value, YKS_EXEREQ, YKR_IDLE);
vLog(LOG_WARN, "Unit(%d) yk(%d) %s state is YKS_EXEREQ result is YKR_IDLE.\n", uid, order, (by_value ? "CLOSE" : "TRIP"));
}
}
}
return TRUE;
}
BOOLEAN CSubModbusRtuProcess::OnTimer(void)
{
if (!CPortProcess::OnTimer()) return FALSE;
return TRUE;
}
BOOLEAN CSubModbusRtuProcess::OnReceiveData(BYTE* pData, int count)
{
return TRUE;
}
BOOLEAN CSubModbusRtuProcess::SerialPortTimeout(void)
{
eMBErrorCode eStatus = MB_ENOERR;
BYTE ucRcvAddress;
BYTE *pucFrame;
WORD usLength;
WORD usRcvBufferPos = GetDataCount();
if (!GetData(ucRTUBuf, usRcvBufferPos))
{
vLog(LOG_ERROR, "%s GetData() ERROR!\n", __FILE__);
return FALSE;
}
/* Length and CRC check */
if ((usRcvBufferPos >= MB_SER_PDU_SIZE_MIN) && (GetCRC16((BYTE *)ucRTUBuf, usRcvBufferPos) == 0))
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
ucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
usLength = (WORD)(usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC);
/* Return the start of the Modbus PDU to the caller. */
pucFrame = (BYTE *)&ucRTUBuf[MB_SER_PDU_PDU_OFF];
DropData(usRcvBufferPos);
int uid, order;
CSubModbusRtuProcessItem* pItem;
uid = GetUnitByAddr(&ucRcvAddress, 1);
if (uid < 0 || uid >= UNIT_NUM) return FALSE;
order = GetOrderByUnitID(uid);
if (order < 0 || order >= PROCESS_UNIT_NUM) return FALSE;
pItem = (CSubModbusRtuProcessItem *)GetItem(order);
if ((ucRcvAddress != pItem->m_physicsAddress) && (ucRcvAddress != MB_ADDRESS_BROADCAST))
{
return TRUE;
}
BYTE ucFunctionCode = pucFrame[MB_PDU_FUNC_OFF];
eMBException eException;
DisplayRxData(ucRTUBuf, usRcvBufferPos, TRUE, uid);
UnitFeedDog(uid);
eException = MB_EX_ILLEGAL_FUNCTION;
switch (ucFunctionCode)
{
case MB_FUNC_READ_INPUT_REGISTER:
eException = eMBFuncReadInputRegister(pItem, pucFrame, &usLength);
break;
case MB_FUNC_READ_HOLDING_REGISTER:
eException = eMBFuncReadHoldingRegister(pItem, pucFrame, &usLength);
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
eException = eMBFuncWriteMultipleHoldingRegister(pItem, pucFrame, &usLength);
break;
case MB_FUNC_WRITE_REGISTER:
eException = eMBFuncWriteHoldingRegister(pItem, pucFrame, &usLength);
break;
case MB_FUNC_READ_COILS:
eException = eMBFuncReadCoils(pItem, pucFrame, &usLength);
break;
case MB_FUNC_WRITE_SINGLE_COIL:
eException = eMBFuncWriteCoil(pItem, pucFrame, &usLength);
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
eException = eMBFuncWriteMultipleCoils(pItem, pucFrame, &usLength);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
eException = eMBFuncReadDiscreteInputs(pItem, pucFrame, &usLength);
break;
default:
break;
}
if (ucRcvAddress != MB_ADDRESS_BROADCAST)
{
if (eException != MB_EX_NONE)
{
/* An exception occured. Build an error frame. */
usLength = 0;
pucFrame[usLength++] = (BYTE)(ucFunctionCode | MB_FUNC_ERROR);
pucFrame[usLength++] = eException;
}
eStatus = eMBRTUSend(ucRcvAddress, pucFrame, usLength);
}
}
else
{
DropData(usRcvBufferPos);
eStatus = MB_EIO;
}
if (eStatus != MB_ENOERR)
{
vLog(LOG_ERROR, "modbus have error(%d)!\n", eStatus);
return FALSE;
}
return TRUE;
}
eMBErrorCode CSubModbusRtuProcess::eMBRTUSend(BYTE ucSlaveAddress, const BYTE *pucFrame, WORD usLength)
{
eMBErrorCode eStatus = MB_ENOERR;
BYTE *pucSndBufferCur;
WORD usSndBufferCount;
WORD usCRC16;
pucSndBufferCur = (BYTE *) pucFrame - 1;
usSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
usCRC16 = GetCRC16((BYTE *) pucSndBufferCur, usSndBufferCount);
ucRTUBuf[usSndBufferCount++] = (BYTE)(usCRC16 >> 8);
ucRTUBuf[usSndBufferCount++] = (BYTE)(usCRC16 & 0xFF);
if (WriteData(ucRTUBuf, usSndBufferCount))
{
DisplayTxData(ucRTUBuf, usSndBufferCount, TRUE);
eStatus = MB_EIO;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::prveMBError2Exception(eMBErrorCode eErrorCode)
{
eMBException eStatus;
switch (eErrorCode)
{
case MB_ENOERR:
eStatus = MB_EX_NONE;
break;
case MB_ENOREG:
eStatus = MB_EX_ILLEGAL_DATA_ADDRESS;
break;
case MB_ETIMEDOUT:
eStatus = MB_EX_SLAVE_BUSY;
break;
default:
eStatus = MB_EX_SLAVE_DEVICE_FAILURE;
break;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncReadInputRegister(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usRegCount;
BYTE *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);
usRegCount = (WORD)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8);
usRegCount |= (WORD)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1]);
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usRegCount >= 1) && (usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX))
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = (BYTE)(usRegCount << 1);
*usLen += 1;
eRegStatus = eMBRegInputCB(pItem, pucFrameCur, usRegAddress, usRegCount);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
*usLen += usRegCount << 1;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read input register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncWriteHoldingRegister(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1]);
/* Make callback to update the value. */
eRegStatus = eMBRegHoldingCB(pItem, &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF], usRegAddress, 1, MB_REG_WRITE);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncWriteMultipleHoldingRegister(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usRegCount;
BYTE ucRegByteCount;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen >= (MB_PDU_FUNC_WRITE_MUL_SIZE_MIN + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1]);
usRegCount = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8);
usRegCount |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1]);
ucRegByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
if ((usRegCount >= 1) && (usRegCount <= MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX) && (ucRegByteCount == (BYTE) (usRegCount << 1)))
{
/* Make callback to update the register values. */
eRegStatus = eMBRegHoldingCB(pItem, &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF], usRegAddress, usRegCount, MB_REG_WRITE);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
/* The response contains the function code, the starting
* address and the quantity of registers. We reuse the
* old values in the buffer because they are still valid.
*/
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncReadHoldingRegister(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usRegCount;
BYTE *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);
usRegCount = (WORD)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8);
usRegCount = (WORD)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1]);
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usRegCount >= 1) && (usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX))
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = (BYTE) (usRegCount << 1);
*usLen += 1;
/* Make callback to fill the buffer. */
eRegStatus = eMBRegHoldingCB(pItem, pucFrameCur, usRegAddress, usRegCount, MB_REG_READ);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
*usLen += usRegCount << 1;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncReadCoils(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usCoilCount;
BYTE ucNBytes;
BYTE *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);
usCoilCount = (WORD)(pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8);
usCoilCount |= (WORD)(pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1]);
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usCoilCount >= 1) && (usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX))
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_COILS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if ((usCoilCount & 0x0007) != 0)
{
ucNBytes = (BYTE)((usCoilCount >> 3) + 1);
}
else
{
ucNBytes = (BYTE)(usCoilCount >> 3);
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus = eMBRegCoilsCB(pItem, pucFrameCur, usRegAddress, usCoilCount, MB_REG_READ);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncWriteCoil(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
BYTE ucBuf[2];
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1]);
if ((pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00) && ((pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF) || (pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00)))
{
ucBuf[1] = 0;
if (pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF)
{
ucBuf[0] = 1;
}
else
{
ucBuf[0] = 0;
}
eRegStatus = eMBRegCoilsCB(pItem, &ucBuf[0], usRegAddress, 1, MB_REG_WRITE);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncWriteMultipleCoils(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usCoilCnt;
BYTE ucByteCount;
BYTE ucByteCountVerify;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen > (MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1]);
usCoilCnt = (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8);
usCoilCnt |= (WORD)(pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1]);
ucByteCount = pucFrame[MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF];
/* Compute the number of expected bytes in the request. */
if ((usCoilCnt & 0x0007) != 0)
{
ucByteCountVerify = (BYTE)(usCoilCnt / 8 + 1);
}
else
{
ucByteCountVerify = (BYTE)(usCoilCnt / 8);
}
if ((usCoilCnt >= 1) &&
(usCoilCnt <= MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX) &&
(ucByteCountVerify == ucByteCount))
{
eRegStatus = eMBRegCoilsCB(pItem, &pucFrame[MB_PDU_FUNC_WRITE_MUL_VALUES_OFF], usRegAddress, usCoilCnt, MB_REG_WRITE);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen = MB_PDU_FUNC_WRITE_MUL_BYTECNT_OFF;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid write coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBException CSubModbusRtuProcess::eMBFuncReadDiscreteInputs(CSubModbusRtuProcessItem *pItem, BYTE* pucFrame, WORD* usLen)
{
WORD usRegAddress;
WORD usDiscreteCnt;
BYTE ucNBytes;
BYTE *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN))
{
usRegAddress = (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);
usRegAddress |= (WORD)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);
usDiscreteCnt = (WORD)(pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF] << 8);
usDiscreteCnt |= (WORD)(pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF + 1]);
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if ((usDiscreteCnt >= 1) && (usDiscreteCnt < MB_PDU_FUNC_READ_DISCCNT_MAX))
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_DISCRETE_INPUTS;
*usLen += 1;
/* Test if the quantity of coils is a multiple of 8. If not last
* byte is only partially field with unused coils set to zero. */
if ((usDiscreteCnt & 0x0007) != 0)
{
ucNBytes = (BYTE) ((usDiscreteCnt >> 3) + 1);
}
else
{
ucNBytes = (BYTE) (usDiscreteCnt >> 3);
}
*pucFrameCur++ = ucNBytes;
*usLen += 1;
eRegStatus = eMBRegDiscreteCB(pItem, pucFrameCur, usRegAddress, usDiscreteCnt);
/* If an error occured convert it into a Modbus exception. */
if (eRegStatus != MB_ENOERR)
{
eStatus = prveMBError2Exception(eRegStatus);
}
else
{
/* The response contains the function code, the starting address
* and the quantity of registers. We reuse the old values in the
* buffer because they are still valid. */
*usLen += ucNBytes;;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid read coil register request because the length
* is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
eMBErrorCode CSubModbusRtuProcess::eMBRegInputCB(CSubModbusRtuProcessItem *pItem, BYTE* pucRegBuffer, WORD usAddress, WORD usNRegs)
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if ((usAddress >= 0) && (usAddress + usNRegs <= 0 + pItem->m_YCRegCount))
{
iRegIndex = (int)(usAddress - 0);
while (usNRegs > 0)
{
*pucRegBuffer++ = (BYTE)(pItem->m_pYCRegTab[iRegIndex] >> 8);
*pucRegBuffer++ = (BYTE)(pItem->m_pYCRegTab[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode CSubModbusRtuProcess::eMBRegHoldingCB(CSubModbusRtuProcessItem *pItem, BYTE* pucRegBuffer, WORD usAddress, WORD usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
WORD usRegHoldingValue;
int iRegIndex;
int uid;
uid = pItem->GetUnitID();
if ((usAddress >= 0) && (usAddress + usNRegs <= 0 + pItem->m_YMRegCount))
{
iRegIndex = (int)(usAddress - 0);
switch (eMode)
{ /* Pass current register values to the protocol stack. */
case MB_REG_READ:
while (usNRegs > 0)
{
*pucRegBuffer++ = (BYTE)(pItem->m_pYMRegTab[iRegIndex] >> 8);
*pucRegBuffer++ = (BYTE)(pItem->m_pYMRegTab[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
/* Update current register values with new values from the
* protocol stack. */
case MB_REG_WRITE:
while (usNRegs > 0)
{
usRegHoldingValue = *pucRegBuffer++ << 8;
usRegHoldingValue |= *pucRegBuffer++;
//写寄存器
SetUnitYT(uid, iRegIndex, usRegHoldingValue, YTS_EXEREQ, YTR_IDLE);
vLog(LOG_DEBUG, "Unit(%d) set point(%d) %d state is YTS_EXEREQ result is YTR_IDLE.\n", uid, iRegIndex, usRegHoldingValue);
iRegIndex++;
usNRegs--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode CSubModbusRtuProcess::eMBRegCoilsCB(CSubModbusRtuProcessItem *pItem, BYTE* pucRegBuffer, WORD usAddress, WORD usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
WORD usCoilGroups = ((usNCoils - 1) / 8 + 1);
BYTE ucStatus = 0;
BYTE usRegCoilsValue;
BYTE ucBits = 0;
BYTE ucDisp = 0;
int iRegIndex = usAddress - 0;
int uid;
uid = pItem->GetUnitID();
if ((usAddress >= 0) && (usAddress + usNCoils <= 0 + pItem->m_YXRegCount))
{
iRegIndex = (int)(usAddress - 0);
switch (eMode)
{ /* Pass current register values to the protocol stack. */
case MB_REG_READ:
while (usCoilGroups--)
{
ucDisp = 0;
ucBits = 8;
ucStatus = 0;
while((usNCoils--) != 0 && (ucBits--) != 0)
{
ucStatus |= (pItem->m_pYXRegTab[iRegIndex++] << (ucDisp++));
}
*pucRegBuffer++ = ucStatus;
}
break;
/* Update current register values with new values from the
* protocol stack. */
case MB_REG_WRITE:
while (usCoilGroups--)
{
ucStatus = *pucRegBuffer++;
ucBits = 8;
while ((usNCoils--) != 0 && (ucBits--) != 0)
{
usRegCoilsValue = ucStatus & 0X01;
//写线圈
SetUnitYK(uid, iRegIndex, usRegCoilsValue, YKS_SELREQ, YKR_IDLE);
vLog(LOG_WARN, "Unit(%d) yk(%d) %s state is YKS_EXEREQ result is YKR_IDLE.\n", uid, iRegIndex, (usRegCoilsValue ? "CLOSE" : "TRIP"));
iRegIndex++;
ucStatus >>= 1;
}
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode CSubModbusRtuProcess::eMBRegDiscreteCB(CSubModbusRtuProcessItem *pItem, BYTE* pucRegBuffer, WORD usAddress, WORD usNDiscrete)
{
eMBErrorCode eStatus = MB_ENOERR;
WORD usDiscreteGroups = ((usNDiscrete - 1) / 8 + 1);
BYTE ucStatus = 0;
BYTE ucBits = 0;
BYTE ucDisp = 0;
int iRegIndex = usAddress - 0;
if ((usAddress >= 0) && (usAddress + usNDiscrete <= 0 + pItem->m_YXRegCount))
{
iRegIndex = (int)(usAddress - 0);
while (usDiscreteGroups--)
{
ucDisp = 0;
ucBits = 8;
ucStatus = 0;
while((usNDiscrete--) != 0 && (ucBits--) != 0)
{
ucStatus |= (pItem->m_pYXRegTab[iRegIndex++] << (ucDisp++));
}
*pucRegBuffer++ = ucStatus;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}