464 lines
12 KiB
C++
464 lines
12 KiB
C++
// 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-读数据长度为0,TCP链接断开......"));
|
||
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;
|
||
}
|