fast/Plugin/Plc/ModbusClient.cpp
2025-01-20 10:30:01 +08:00

464 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ClientSocketThread.cpp : implementation file
//
#include "stdafx.h"
#include "ModbusClient.h"
// CModbusClient
const UINT glServerPort = 502;
#define RECVDATA_BUFLEN 1024000 //数据接收缓冲区长度
#define DATALEN_PERTIME 512 //单次从Scoket接收的数据长度
#define DATADEAL_BUFLEN 512 //数据处理缓冲区长度(TCP帧最大数据长度)
#define SOCKET_TIME_OUT 500
IMPLEMENT_DYNCREATE(CModbusClient, CWinThread)
CModbusClient::CModbusClient(CString strIp, int nPort, HWND hWnd)
{
m_pRecvDataBuffer = NULL;
m_pDealBuf = NULL;
m_iWritePos = 0;
m_iReadPos = 0;
m_llDataTotalLen = 0;
m_llReadTotal = 0;
m_strSerIP = strIp;
m_hWnd = hWnd;
m_bConnected = FALSE;
m_ReadEventHandle = CreateEvent(NULL, TRUE, FALSE, NULL); //必须手动复位
m_WriteEventHandle = CreateEvent(NULL, TRUE, FALSE, NULL); //必须手动复位
}
CModbusClient::CModbusClient()
{
}
CModbusClient::~CModbusClient()
{
if (m_pDealBuf)
{
delete m_pDealBuf;
m_pDealBuf =NULL;
}
if (m_pRecvDataBuffer)
{
delete m_pRecvDataBuffer;
m_pRecvDataBuffer =NULL;
}
}
BOOL CModbusClient::InitInstance()
{
if (!AfxSocketInit()) // 初始化CSocket必须调用的
{
CWinThread::InitInstance(); //立刻退出
return FALSE;
}
//创建socket
if (!m_socket.Create())
{
//创建socket失败
CWinThread::InitInstance(); //立刻退出
return FALSE;
}
// 设置发送超时时间,单位毫秒
int nTimeout = SOCKET_TIME_OUT;
m_socket.SetSockOpt(SO_SNDTIMEO, (char*)&nTimeout, sizeof(nTimeout), SOL_SOCKET);
// 设置接收超时时间,单位毫秒
nTimeout = SOCKET_TIME_OUT;
m_socket.SetSockOpt(SO_RCVTIMEO, (char*)&nTimeout, sizeof(nTimeout), SOL_SOCKET);
//m_socket.m_pMainDlg = m_pMainDlg;
m_socket.m_pThrd = this;
if (!m_socket.Connect(m_strSerIP, glServerPort))
{
CWinThread::InitInstance(); //立刻退出
m_bConnected = FALSE;
//return FALSE;
}
else
{
m_bConnected = TRUE;
//连接成功后,通知主界面
PostMessage(m_hWnd, WM_NETMESSAGE, NET_CONNECT_OK, (LPARAM)m_bDevID);
}
m_pRecvDataBuffer = new BYTE[RECVDATA_BUFLEN];
ASSERT(m_pRecvDataBuffer != NULL);
m_pDealBuf = new BYTE[DATADEAL_BUFLEN];
ASSERT(m_pDealBuf != NULL);
m_iWritePos =0;
m_iReadPos =0;
m_llDataTotalLen =0;
m_llReadTotal =0;
m_wTcpID =0;
return TRUE; //线程不会立刻退出
}
int CModbusClient::ExitInstance()
{
if (m_socket.m_hSocket != INVALID_SOCKET)
{
m_socket.ShutDown(2);
m_socket.Close();
}
//m_bConnected = FALSE;
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(CModbusClient, CWinThread)
ON_THREAD_MESSAGE(WM_THREAD_RECV, OnCustomMsg)
END_MESSAGE_MAP()
// CModbusClient message handlers
void CModbusClient::OnCustomMsg(WPARAM wParam,LPARAM lParam)
{
BYTE pReadBf[DATALEN_PERTIME+1] = {0};
int iRdLen =0;
switch(wParam)
{
case WP_WPARA_RECV:
// 接收数据,先把接收数据事件关掉
m_socket.AsyncSelect(FD_CLOSE);
//读取数据
iRdLen = m_socket.Receive(pReadBf,DATALEN_PERTIME);
if (0 ==iRdLen)
{
//ToDo:对端断开链接
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
OutputDebugString(_T("CSerSocketThread:1-读数据长度为0TCP链接断开......"));
return;
}
//缓冲区尾部不能容纳读取的数据,要分开放入缓冲区尾部、头部
if(m_iWritePos + iRdLen > RECVDATA_BUFLEN)
{
int iTailLen = RECVDATA_BUFLEN - m_iWritePos;
memcpy(m_pRecvDataBuffer +m_iWritePos,pReadBf,iTailLen);
int iHeadLen = iRdLen - iTailLen;
memcpy(m_pRecvDataBuffer,pReadBf + iTailLen,iHeadLen);
m_iWritePos = iHeadLen;
}else{
//缓冲区尾部足够容纳本次数据
memcpy(m_pRecvDataBuffer+m_iWritePos,pReadBf,iRdLen);
m_iWritePos += iRdLen;
}
//接收数据总长度
m_llDataTotalLen += iRdLen;
AnalysisRecvData();
//重新打开接收数据事件
m_socket.AsyncSelect(FD_READ|FD_CLOSE);
break;
case WP_WPARA_CLOSE:
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
//m_socket.Close();
OutputDebugString(_T("TCP链接断开......"));
break;
case WP_WPARA_THREAD_QUIT:
PostQuitMessage(0);
break;
}
}
void CModbusClient::AnalysisRecvData()
{
if (m_llReadTotal + sizeof(ST_MODBUS_MBAP_HEADER) < m_llDataTotalLen) //缓冲区中未读数据长度大于MBAP长
{
memset(m_pDealBuf,0,DATADEAL_BUFLEN);
//写指针随时变动,记录当前位置
int iRdCurrPos = m_iReadPos;
int iWrtCurrPos = m_iWritePos;
ST_MODBUS_MBAP_HEADER stMbap;
if(iRdCurrPos < iWrtCurrPos) //写指针超前读指针,数据在两个指针之间
{
memcpy(&stMbap,m_pRecvDataBuffer+iRdCurrPos,sizeof(ST_MODBUS_MBAP_HEADER));
if (m_llReadTotal + sizeof(ST_MODBUS_MBAP_HEADER) +(ntohs(stMbap.wLen) -1) <= m_llDataTotalLen)
{
memcpy(m_pDealBuf,m_pRecvDataBuffer+iRdCurrPos,sizeof(ST_MODBUS_MBAP_HEADER)+ ntohs(stMbap.wLen) -1);
DealFrameData(m_pDealBuf);
m_iReadPos +=(sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1);
m_llReadTotal += (sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1);
}else{
return; //不够一帧
}
}
else{//写指针滞后读指针,数据在首尾两头
if (iRdCurrPos +sizeof(ST_MODBUS_MBAP_HEADER) <= RECVDATA_BUFLEN)
{
memcpy(&stMbap,m_pRecvDataBuffer+iRdCurrPos,sizeof(ST_MODBUS_MBAP_HEADER));
if (m_llReadTotal+sizeof(ST_MODBUS_MBAP_HEADER)+ ntohs(stMbap.wLen) -1 <= m_llDataTotalLen)
{
if (iRdCurrPos + sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1 <= RECVDATA_BUFLEN) //写指针滞后读指针,读指针到缓冲区尾仍够一帧数据
{
memcpy(m_pDealBuf,m_pRecvDataBuffer+iRdCurrPos,sizeof(ST_MODBUS_MBAP_HEADER)+ ntohs(stMbap.wLen) -1);
DealFrameData(m_pDealBuf);
m_iReadPos +=(sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1);
m_llReadTotal += (sizeof(ST_MODBUS_MBAP_HEADER) +ntohs(stMbap.wLen) -1);
m_iReadPos =m_iReadPos % RECVDATA_BUFLEN;
}else{
//当前读指针到缓冲区尾够MBAP头长但数据不够
memcpy(m_pDealBuf,m_pRecvDataBuffer+iRdCurrPos,RECVDATA_BUFLEN - iRdCurrPos);
memcpy(m_pDealBuf+RECVDATA_BUFLEN - iRdCurrPos,m_pRecvDataBuffer,sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1 -(RECVDATA_BUFLEN - iRdCurrPos));
DealFrameData(m_pDealBuf);
m_iReadPos = sizeof(ST_MODBUS_MBAP_HEADER) +ntohs(stMbap.wLen) -1 -(RECVDATA_BUFLEN - iRdCurrPos);
m_llReadTotal += (sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1);
}
}
else{
return;//不够一帧
}
}else{
//当前读指针到缓冲区尾不够MBAP头大部分数据在缓冲区头
memcpy(m_pDealBuf,m_pRecvDataBuffer+iRdCurrPos,RECVDATA_BUFLEN -iRdCurrPos);
memcpy(m_pDealBuf+RECVDATA_BUFLEN -iRdCurrPos,m_pRecvDataBuffer,sizeof(ST_MODBUS_MBAP_HEADER) -(RECVDATA_BUFLEN - iRdCurrPos));
memcpy(&stMbap,m_pDealBuf,sizeof(ST_MODBUS_MBAP_HEADER));
if (m_llReadTotal+sizeof(ST_MODBUS_MBAP_HEADER)+ ntohs(stMbap.wLen) -1 <= m_llDataTotalLen)
{
memcpy(m_pDealBuf,m_pRecvDataBuffer+iRdCurrPos,RECVDATA_BUFLEN -iRdCurrPos);
memcpy(m_pDealBuf+RECVDATA_BUFLEN -iRdCurrPos,m_pRecvDataBuffer,sizeof(ST_MODBUS_MBAP_HEADER) -(RECVDATA_BUFLEN - iRdCurrPos) + ntohs(stMbap.wLen) -1);
DealFrameData(m_pDealBuf);
m_iReadPos = sizeof(ST_MODBUS_MBAP_HEADER) -(RECVDATA_BUFLEN - iRdCurrPos) + ntohs(stMbap.wLen) -1;
m_llReadTotal += (sizeof(ST_MODBUS_MBAP_HEADER) + ntohs(stMbap.wLen) -1);
}
else{
return;//不够一帧
}
}
}
}
}
void CModbusClient::DealFrameData(BYTE* pData)
{
BYTE bSampleData[512] ={0};
NET_PACKET* pNetPacket = NULL;
int nLen = 0;
ST_MODBUS_MBAP_HEADER stMbap;
BYTE bFuncCode = pData[sizeof(ST_MODBUS_MBAP_HEADER)];
memcpy(&stMbap,pData,sizeof(ST_MODBUS_MBAP_HEADER));
int iFrmLen = ntohs(stMbap.wLen) +6;
memcpy(bSampleData,pData,iFrmLen);
memset(&m_stReadRspFrm, 0, sizeof(ST_MODBUS_SERVER_RSPREAD_FRAME));
memset(&m_stWrtRspFrm, 0, sizeof(ST_MODBUS_SERVER_RSPWRT_FRAME));
memset(&m_stRspErrFrm, 0, sizeof(ST_MODBUS_SERVER_RSPERR_FRAME));
int iDataLen =ntohs(stMbap.wLen) -1;
switch(bFuncCode)
{
case EN_MODBUS_READ_SINGLECOIL:
case EN_MODBUS_READ_PERSISREG:
memcpy(&m_stReadRspFrm,pData,iFrmLen); //ST_MODBUS_SERVER_RSPREAD_FRAME的成员bRtnData定义了255字节实际占用不到255不能用sizeof作为拷贝长度
SetEvent(m_ReadEventHandle);
//((CMechanicalArmOptDlg*)m_pMainDlg)->OnDevReadRspDataToBuffer(m_bDevID,stReadRspFrm);
/*pNetPacket = new NET_PACKET;
pNetPacket->enType = NET_RD_MSG_RET;
pNetPacket->nDevId = m_bDevID;
pNetPacket->pData = new char[iFrmLen + 1];
memcpy(pNetPacket->pData, pData, iFrmLen);
pNetPacket->pData[iFrmLen] = '\0';
pNetPacket->lLen = iFrmLen;
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DATA_MSG, (LPARAM)pNetPacket);*/
//delete[] pNetPacket->pData;
//delete pNetPacket;
break;
case EN_MODBUS_WRITE_SINGLECOIL:
case EN_MODBUS_WRITE_MULTIREG:
memcpy(&m_stWrtRspFrm,pData,sizeof(ST_MODBUS_SERVER_RSPWRT_FRAME));
SetEvent(m_WriteEventHandle);
//((CMechanicalArmOptDlg*)m_pMainDlg)->OnDevWrtRspDataToBuffer(m_bDevID,stWrtRspFrm);
/*nLen = sizeof(ST_MODBUS_SERVER_RSPWRT_FRAME);
pNetPacket = new NET_PACKET;
pNetPacket->enType = NET_WR_MSG_RET;
pNetPacket->nDevId = m_bDevID;
pNetPacket->pData = new char[nLen + 1];
memcpy(pNetPacket->pData, pData, nLen);
pNetPacket->pData[nLen] = '\0';
pNetPacket->lLen = nLen;
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DATA_MSG, (LPARAM)pNetPacket);*/
break;
//ToDo:错误处理
case EN_MODBUS_READ_SINGLECOIL_RSPERR:
case EN_MODBUS_READ_PERSISREG_RSPERR:
case EN_MODBUS_WRITE_SINGLECOIL_RSPERR:
case EN_MODBUS_WRITE_MULTIREG_RSPERR:
memcpy(&m_stWrtRspFrm,pData,sizeof(ST_MODBUS_SERVER_RSPERR_FRAME));
break;
}
}
int CModbusClient::WriteMultipleRegisters(int nRegAddr, int nRegCnt, short * pWriteData)
{
if (m_bConnected == FALSE)
{
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
return -1;
}
BYTE bData[512] = { 0 };
if (m_wTcpID + 1 == 0xFFFF)
{
m_wTcpID = 1;
}
else {
m_wTcpID++;
}
//发送写多个寄存器报文
ST_MODBUS_CLIENT_RQTWRTMULTIREG_FRAME frame;
ST_MODBUS_MBAP_HEADER stMbapHeader;
stMbapHeader.wTransFlag = htons(m_wTcpID);
stMbapHeader.wProtocolFlag = 0;
stMbapHeader.wLen = htons(7 + nRegCnt*2);
stMbapHeader.bUnitFlag = 0x01;
frame.stMbapHeader = stMbapHeader;
frame.bFuncConde = 0x10; //功能码
frame.wStartAddr = htons(nRegAddr - 40001); //寄存器地址
frame.wRegNum = htons(nRegCnt); //寄存器的个数
frame.bLen = nRegCnt * 2; //数据字节长度(寄存器个数*2)
for (int i = 0; i < nRegCnt; i++)
{
UINT16 uintValue = htons(*(pWriteData+i));
char* byteArray = (char*)(&uintValue); // 创建一个4字节的字节数组
for (int j = 0; j < 2; ++j)
{
frame.bRegVal[i*2 + j] = byteArray[j];
}
}
int iRtnsend = m_socket.Send(&frame, sizeof(ST_MODBUS_CLIENT_RQTWRTMULTIREG_FRAME) - (255 - nRegCnt * 2));
if (iRtnsend > 0)
{
//等待读返回的信号量
switch (::WaitForSingleObject(m_WriteEventHandle, SOCKET_TIME_OUT))
{
case WAIT_OBJECT_0:
{
::ResetEvent(m_WriteEventHandle);
break;
}
case WAIT_TIMEOUT:
case WAIT_FAILED:
default:
{
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
break;
}
}
}
return iRtnsend;
}
int CModbusClient::ReadMultipleRegisters(int nRegAddr, int nRegCnt, short * pReadData)
{
if (m_bConnected == FALSE)
{
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
return -1;
}
BYTE bData[512] = { 0 };
if (m_wTcpID + 1 == 0xFFFF)
{
m_wTcpID = 1;
}
else {
m_wTcpID++;
}
//发送读多个寄存器报文
ST_MODBUS_CLIENT_RQTREAD_FRAME frame;
ST_MODBUS_MBAP_HEADER stMbapHeader;
stMbapHeader.wTransFlag = htons(m_wTcpID);
stMbapHeader.wProtocolFlag = 0;
stMbapHeader.wLen = htons(6);
stMbapHeader.bUnitFlag = 0x01;
frame.stMbapHeader = stMbapHeader;
frame.bFuncConde = 0x03; //功能码
frame.wStartAddr = htons(nRegAddr - 40001); //寄存器地址
frame.wRegNum = htons(nRegCnt); //寄存器的个数
int iRtnsend = m_socket.Send(&frame, sizeof(ST_MODBUS_CLIENT_RQTREAD_FRAME));
if (iRtnsend > 0)
{
//等待读返回的信号量
switch (::WaitForSingleObject(m_ReadEventHandle, SOCKET_TIME_OUT))
{
case WAIT_OBJECT_0:
{
short *pData = (short *)(m_stReadRspFrm.bRtnData);
for (int i = 0; i < m_stReadRspFrm.bLen / 2; i++)
{
pReadData[i] = htons(*(pData + i));
}
::ResetEvent(m_ReadEventHandle);
break;
}
case WAIT_TIMEOUT:
case WAIT_FAILED:
default:
{
PostMessage(m_hWnd, WM_NETMESSAGE, NET_DISCONNECT, (LPARAM)m_bDevID);
break;
}
}
}
return iRtnsend;
}