1045 lines
34 KiB
C++
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;
|
|
}
|