From 211ca669564c764da1d08b820bc4e60333e0db45 Mon Sep 17 00:00:00 2001 From: TAO Cheng Date: Thu, 30 Oct 2014 11:47:54 +0800 Subject: [PATCH] Add thread. --- .../Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.cpp | 262 +++++++++++++----- .../Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.h | 83 +++++- .../UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.cpp | 58 +++- .../UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.h | 1 + .../Hsi/Tools/UsbUtility/UsbUtil_VS2010.suo | Bin 150016 -> 150016 bytes 5 files changed, 320 insertions(+), 84 deletions(-) diff --git a/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.cpp b/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.cpp index 502a3ab..1c95dcf 100644 --- a/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.cpp +++ b/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.cpp @@ -10,6 +10,11 @@ const int WSA_MINOR_VERSION = 1; const char BufferHeader[4]={0x46,0x49,0x4E,0x53}; const char HandShaking[]={0x46,0x49,0x4E,0x53,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + +HANDLE CSo7_TCPIP::m_Thread_Id=NULL; +HANDLE CSo7_TCPIP::m_Thread_Mutex=NULL; +int CSo7_TCPIP::m_Thread_State=TCPIP_THREAD_PAUSED; + int CSo7_TCPIP::m_iReceiveMaxBufSize=1024; int CSo7_TCPIP::m_iSendMaxBufSize=1024; @@ -17,20 +22,30 @@ SOCKET CSo7_TCPIP::m_Socket=INVALID_SOCKET; in_addr CSo7_TCPIP::m_SreverIPAddress; in_addr CSo7_TCPIP::m_ClientIPAddress; u_short CSo7_TCPIP::m_iServerPortNumber=static_cast(0); +struct_so7_tcpip_buff CSo7_TCPIP::m_TCPIPBuf[lChannelSize]; +struct_so7_tcpip_data CSo7_TCPIP::m_TCPIPData; + //================================================================ CSo7_TCPIP::CSo7_TCPIP() { m_hMsgWnd=NULL; - m_ReceiveBuf=NULL; - m_SendBuf=NULL; - m_iBytesToReceive = 0; - m_iBytesReceived = 0; - m_iBytesToSend = 0; - m_iBytesSent = 0; - m_ReceiveBuf = new char[m_iReceiveMaxBufSize]; - m_SendBuf = new char[m_iSendMaxBufSize]; + for (int i=0;iSendBuffer(); + break; + } + case TCPIP_THREAD_PAUSED: + { + break; + } + case TCPIP_THREAD_EXIT: + { + ExitThread(0); + break; + } + } + }; + m_Thread_State=TCPIP_THREAD_EXIT; + ExitThread(0); } //================================================================ int CSo7_TCPIP::Init_Winsock() @@ -83,6 +187,7 @@ TCPIP_RETURN_CODE CSo7_TCPIP::Connect(const HWND& _hWnd,const in_addr& _IPAddres } else { + Create_Thread(); // 设置socket为窗口通知消息类型 ::WSAAsyncSelect(m_Socket, m_hMsgWnd,WM_SOCKET, FD_CONNECT | FD_CLOSE | FD_WRITE | FD_READ); @@ -110,8 +215,10 @@ TCPIP_RETURN_CODE CSo7_TCPIP::DisConnect() { return TCPIP_INVAILD_SOCKET; } - ::closesocket(m_Socket); + closesocket(m_Socket); m_Socket = INVALID_SOCKET; + m_ClearSendBuf(); + Exit_Thread(); return TCPIP_CONNECT_OK; } //================================================================ @@ -138,41 +245,16 @@ TCPIP_RETURN_CODE CSo7_TCPIP::GetHostIPAddr(in_addr& _IPAddress) //================================================================ TCPIP_RETURN_CODE CSo7_TCPIP::Handshaking() { + WaitForSingleObject(m_Thread_Mutex, INFINITE); m_ClearSendBuf(); - m_iBytesToSend=sizeof(HandShaking); - memcpy(m_SendBuf,HandShaking,m_iBytesToSend); - m_SendBuf[m_iBytesToSend-1]=m_ClientIPAddress.S_un.S_un_b.s_b4; - return SendBuffer(); -} -//================================================================ -TCPIP_RETURN_CODE CSo7_TCPIP::SendBuffer() -{ - int errorCode, numBytesSent; - bool bInfinite=true; - while(bInfinite) - { - numBytesSent = send(m_Socket, &(m_SendBuf[m_iBytesSent]), m_iBytesToSend-m_iBytesSent, 0); - if(numBytesSent == SOCKET_ERROR) - { - errorCode = WSAGetLastError(); - if(errorCode == WSAEWOULDBLOCK) - { - return (TCPIP_RETURN_CODE)errorCode; //Should get a FD_WRITE event if this happens. Send the rest from there. - } - return (TCPIP_RETURN_CODE)errorCode; //should check for other errors here, and set an error code or something. - } - else - { - m_iBytesSent += numBytesSent; - } - if(m_iBytesSent>=m_iBytesToSend) - { - m_iBytesToSend = 0; - m_iBytesSent = 0; - break; - } - } - return TCPIP_CONNECT_OK; + m_TCPIPBuf[CH_SEND]._size=sizeof(HandShaking); + memcpy(m_TCPIPBuf[CH_SEND]._buffer,HandShaking,m_TCPIPBuf[CH_SEND]._size); + m_TCPIPBuf[CH_SEND]._buffer[m_TCPIPBuf[CH_SEND]._size-1]=m_ClientIPAddress.S_un.S_un_b.s_b4; + m_TCPIPBuf[CH_SEND]._save_send_cmd=TCPIP_CMD_HANDSHAKING; + m_Thread_State=TCPIP_THREAD_RUNNING; + _do_single_threaded_tcpip_comm(); + ReleaseMutex(m_Thread_Mutex); + return m_TCPIPData.s_status._SendReturnCode; } //================================================================ @@ -238,10 +320,46 @@ void CSo7_TCPIP::m_ClearSendBuf(void) int i; for(i=0;i=m_TCPIPBuf[CH_SEND]._size) + { + m_TCPIPBuf[CH_SEND]._size = 0; + m_TCPIPBuf[CH_SEND]._CompletedSize = 0; + m_TCPIPBuf[CH_SEND]._hProtoPending=FALSE; + break; + } } - m_iBytesSent=0; - m_iBytesToSend=0; } //================================================================ @@ -249,16 +367,17 @@ void CSo7_TCPIP::m_ProcessSocketWriteEvent(void) { int errorCode, numBytesSent; SOCKET s = m_Socket; - if(m_iBytesToSend <= 0) + if(m_TCPIPBuf[CH_SEND]._size <= 0) return; - char* sendBuf = m_SendBuf; + char* sendBuf = m_TCPIPBuf[CH_SEND]._buffer; while(1) { - numBytesSent = send(s, &(sendBuf[m_iBytesSent]), m_iBytesToSend-m_iBytesSent, 0); + numBytesSent = send(s, &(sendBuf[m_TCPIPBuf[CH_SEND]._CompletedSize]), m_TCPIPBuf[CH_SEND]._size-m_TCPIPBuf[CH_SEND]._CompletedSize, 0); if(numBytesSent == SOCKET_ERROR) { errorCode = WSAGetLastError(); + m_TCPIPData.s_status._SendReturnCode=(TCPIP_RETURN_CODE)errorCode; if(errorCode == WSAEWOULDBLOCK) { return; //Should get a FD_WRITE event if this happens. Send the rest from there. @@ -271,11 +390,11 @@ void CSo7_TCPIP::m_ProcessSocketWriteEvent(void) } else { - m_iBytesSent += numBytesSent; + m_TCPIPBuf[CH_SEND]._CompletedSize += numBytesSent; } - if(m_iBytesSent>=m_iBytesToSend) + if(m_TCPIPBuf[CH_SEND]._CompletedSize>=m_TCPIPBuf[CH_SEND]._size) { - m_iBytesSent = 0; + m_TCPIPBuf[CH_SEND]._CompletedSize = 0; m_ClearSendBuf(); break; } @@ -286,10 +405,11 @@ void CSo7_TCPIP::m_ProcessSocketReadEvent(SOCKET s) { int bytesReceived=0; int errorCode; - bytesReceived = recv(s, &(m_ReceiveBuf[0]), m_iReceiveMaxBufSize, 0); + bytesReceived = recv(s, &(m_TCPIPBuf[CH_RECV]._buffer[0]), m_iReceiveMaxBufSize, 0); if(bytesReceived == SOCKET_ERROR) { errorCode = WSAGetLastError(); + m_TCPIPData.s_status._RecvReturnCode=(TCPIP_RETURN_CODE)errorCode; if (errorCode == WSAEWOULDBLOCK) { //have to wait for the next receive event @@ -301,11 +421,23 @@ void CSo7_TCPIP::m_ProcessSocketReadEvent(SOCKET s) return; } } - m_iBytesReceived += bytesReceived; - if(m_iBytesReceived < sizeof(unsigned long) + sizeof(char)) - {//like this will ever happen... Have to wait for the next receive event - return; + m_TCPIPBuf[CH_RECV]._CompletedSize += bytesReceived; + m_TCPIPBuf[CH_RECV]._size = m_TCPIPBuf[CH_RECV]._CompletedSize; + switch (m_TCPIPBuf[CH_SEND]._save_send_cmd) + { + case TCPIP_CMD_HANDSHAKING: + if (m_TCPIPBuf[CH_RECV]._CompletedSize==24) + { + m_TCPIPData.s_status._handshaking=true; + } + else + { + m_TCPIPData.s_status._handshaking=false; + } + break; + default:break; } - m_iBytesToReceive = m_iBytesReceived; - m_iBytesReceived = 0; + m_TCPIPBuf[CH_RECV]._CompletedSize = 0; + m_TCPIPBuf[CH_RECV]._hProtoPending=FALSE; + ::PostMessage(m_hMsgWnd,WM_TCPIP_RECV_DATA,m_TCPIPBuf[CH_SEND]._save_send_cmd,m_TCPIPData.s_status._RecvReturnCode); } diff --git a/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.h b/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.h index 455a111..0d71cfc 100644 --- a/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.h +++ b/PcDmis/Base/Interfac/Msi/Hsi/SevenOcean/So7_TCPIP.h @@ -10,7 +10,32 @@ #include #include #include -#define WM_SOCKET WM_USER + 0x1 +#define WM_SOCKET WM_USER + 0x060 +#define WM_TCPIP_RECV_DATA WM_USER + 0x061 + +#define lChannelSize 2 +#define CH_SEND 0 +#define CH_RECV 1 + +#define TCPIP_MAX_BUFF_SIZE 1024 +#define TCPIP_MAX_DAT_SIZE 1024 + +#define TCPIP_THREAD_RUNNING 0 +#define TCPIP_THREAD_PAUSED 1 +#define TCPIP_THREAD_EXIT -1 + +#pragma pack(push) +#pragma pack(1) + +enum TCPIP_CMD +{ + TCPIP_CMD_NONE=0, + TCPIP_CMD_HANDSHAKING, + TCPIP_CMD_READ_DATA, + TCPIP_CMD_WRITE_DATA, + + TCPIP_CMD_TATAL +}; typedef enum { TCPIP_CONNECT_OK=0, @@ -20,8 +45,38 @@ typedef enum { TCPIP_INVAILD_IP_ADDRESS, TCPIP_INVAILD_PORT_NUMBER, TCPIP_CONNECT_SERVER_FAILED, -} TCPIP_RETURN_CODE; + TCPIP_CONNECT_STATUS_UNKNOWN +} TCPIP_RETURN_CODE; +//====================== +struct struct_so7_tcpip_buff +{ + BYTE _save_send_cmd; + BYTE _save_send_cmd0; + BYTE _save_send_cmd1; + BYTE _save_send_cmd2; + char *_buffer; // MAX_BUFF_SIZE + int _size; + int _CompletedSize; + BOOL _hProtoPending; + HANDLE _event; +}; +struct struct_so7_tcpip_data +{ // g_machine structure + struct s_recv_data + { + int _type; + int _DataSize; + double *_dbBuff; + } s_recv_data; + struct s_status + { + TCPIP_RETURN_CODE _SendReturnCode; + TCPIP_RETURN_CODE _RecvReturnCode; + bool _handshaking; + } s_status; +}; +#pragma pack(pop) //====================================================================================== class CSo7_TCPIP { @@ -29,24 +84,27 @@ public: CSo7_TCPIP(); virtual ~CSo7_TCPIP(); + static struct_so7_tcpip_buff m_TCPIPBuf[lChannelSize]; + static struct_so7_tcpip_data m_TCPIPData; + + static unsigned __stdcall m_Thread(LPVOID pThis) ; + static HANDLE m_Thread_Id; + static HANDLE m_Thread_Mutex; + static int m_Thread_State; + TCPIP_RETURN_CODE Connect(const HWND& _hWnd,const in_addr& _IPAddress,const u_short& _nPortNumber); TCPIP_RETURN_CODE Send(int _Addr,int _Data); TCPIP_RETURN_CODE Recv(int _Addr,int& _Data,bool _bWait); TCPIP_RETURN_CODE DisConnect(); TCPIP_RETURN_CODE GetHostIPAddr(in_addr& _IPAddress); - TCPIP_RETURN_CODE Handshaking(); - + TCPIP_RETURN_CODE Handshaking(); LRESULT OnSocket(WPARAM wParam, LPARAM lParam); -private: +private: HWND m_hMsgWnd; - char *m_ReceiveBuf; - char *m_SendBuf; double *m_dReceiveDBuf; int m_iReceiveDBufSize; - int m_iBytesToReceive,m_iBytesReceived; - int m_iBytesToSend,m_iBytesSent; static int m_iReceiveMaxBufSize; static int m_iSendMaxBufSize; @@ -55,9 +113,14 @@ private: static in_addr m_ClientIPAddress; static u_short m_iServerPortNumber; - TCPIP_RETURN_CODE SendBuffer(); + void SendBuffer(); void m_ClearSendBuf(); + void Create_Thread(); + void Exit_Thread(); + void _do_single_threaded_tcpip_comm(bool _bWaitForRsponse=false); + + int Init_Winsock(); void m_ProcessSocketWriteEvent(); void m_ProcessSocketReadEvent(SOCKET s); diff --git a/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.cpp b/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.cpp index 6984f47..a6eda6c 100644 --- a/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.cpp +++ b/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.cpp @@ -13,8 +13,8 @@ extern CSo7_TCPIP* g_pSo7_TCPIP; IMPLEMENT_DYNAMIC(CSo7_Util_PLC_TCPIP, CDialog) -CSo7_Util_PLC_TCPIP::CSo7_Util_PLC_TCPIP(CWnd* pParent /*=NULL*/) - : CDialog(CSo7_Util_PLC_TCPIP::IDD, pParent) + CSo7_Util_PLC_TCPIP::CSo7_Util_PLC_TCPIP(CWnd* pParent /*=NULL*/) + : CDialog(CSo7_Util_PLC_TCPIP::IDD, pParent) { } @@ -25,9 +25,9 @@ CSo7_Util_PLC_TCPIP::~CSo7_Util_PLC_TCPIP() void CSo7_Util_PLC_TCPIP::DoDataExchange(CDataExchange* pDX) { - CDialog::DoDataExchange(pDX); + CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT_MSG, m_edMSG); - + } @@ -39,6 +39,7 @@ BEGIN_MESSAGE_MAP(CSo7_Util_PLC_TCPIP, CDialog) ON_BN_CLICKED(IDC_BUTTON_PLC_TCPIP_CLEAR_MSG, &CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipClearMsg) ON_BN_CLICKED(IDCANCEL, &CSo7_Util_PLC_TCPIP::OnBnClickedCancel) ON_MESSAGE(WM_SOCKET,&CSo7_Util_PLC_TCPIP::OnSocket) + ON_MESSAGE(WM_TCPIP_RECV_DATA,&CSo7_Util_PLC_TCPIP::OnTCPIPRecv) ON_BN_CLICKED(IDCANCEL, &CSo7_Util_PLC_TCPIP::OnBnClickedCancel) ON_BN_CLICKED(IDC_BUTTON_PLC_TCPIP_HANDSHAKING, &CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipHandshaking) END_MESSAGE_MAP() @@ -52,10 +53,10 @@ void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipConnect() in_addr IPAddress; IPAddress.S_un.S_addr=inet_addr("192.168.0.5"); TCPIP_RETURN_CODE rCode=g_pSo7_TCPIP->Connect(m_hWnd,IPAddress,9600); - m_csMSG.Format(_T("Server:192.168.0.5[9600] Connect return code:%d."),rCode); + m_csMSG.Format(_T("Server:192.168.0.5[9600] Connect ReturnCode:%d."),rCode); OutputWithScroll(m_csMSG,m_edMSG); rCode=g_pSo7_TCPIP->GetHostIPAddr(IPAddress); - m_csMSG.Format(_T("Client:%d.%d.%d.%d return code:%d."), + m_csMSG.Format(_T("Client:%d.%d.%d.%d ReturnCode:%d."), IPAddress.S_un.S_un_b.s_b1,IPAddress.S_un.S_un_b.s_b2, IPAddress.S_un.S_un_b.s_b3,IPAddress.S_un.S_un_b.s_b4,rCode); OutputWithScroll(m_csMSG,m_edMSG); @@ -65,7 +66,7 @@ void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipConnect() void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipDisconnect() { TCPIP_RETURN_CODE rCode=g_pSo7_TCPIP->DisConnect(); - m_csMSG.Format(_T("DisConnect return code:%d."),rCode); + m_csMSG.Format(_T("[DisConnect] ReturnCode:%d."),rCode); OutputWithScroll(m_csMSG,m_edMSG); } @@ -78,14 +79,14 @@ void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipRead() void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipHandshaking() { TCPIP_RETURN_CODE rCode=g_pSo7_TCPIP->Handshaking(); - m_csMSG.Format(_T("Handshaking return code:%d."),rCode); + m_csMSG.Format(_T("[Handshaking] Send ReturnCode:%d."),rCode); OutputWithScroll(m_csMSG,m_edMSG); } void CSo7_Util_PLC_TCPIP::OnBnClickedButtonPlcTcpipWrite() { TCPIP_RETURN_CODE rCode=g_pSo7_TCPIP->Send(1001,0); - m_csMSG.Format(_T("Write return code:%d."),rCode); + m_csMSG.Format(_T("Write ReturnCode:%d."),rCode); OutputWithScroll(m_csMSG,m_edMSG); } @@ -131,3 +132,42 @@ LRESULT CSo7_Util_PLC_TCPIP::OnSocket(WPARAM w, LPARAM p) return( lResult ); } +LRESULT CSo7_Util_PLC_TCPIP::OnTCPIPRecv(WPARAM w, LPARAM p) +{ + LRESULT lResult=0; + switch(w) + { + case TCPIP_CMD_HANDSHAKING: + { + if (g_pSo7_TCPIP) + { + m_csMSG.Format(_T(">> Handshaking status:%d."),g_pSo7_TCPIP->m_TCPIPData.s_status._handshaking); + OutputWithScroll(m_csMSG,m_edMSG); + } + m_csMSG=_T("[Handshaking]"); + break; + } + default: + { + m_csMSG=_T("[Unknown]"); + } + break; + } + if (g_pSo7_TCPIP) + { + CString csTmp; + csTmp.Format(_T("ReturnCode:%d;"),p); + m_csMSG+=csTmp; + csTmp.Format(_T("RecvSize:%d;"),g_pSo7_TCPIP->m_TCPIPBuf[CH_RECV]._size); + m_csMSG+=csTmp; + m_csMSG+=_T("RecvData:"); + for (int i=0;im_TCPIPBuf[CH_RECV]._size;i++) + { + csTmp.Format(_T("0x%X "),g_pSo7_TCPIP->m_TCPIPBuf[CH_RECV]._buffer[i]); + m_csMSG+=csTmp; + } + } + m_csMSG+=_T("."); + OutputWithScroll(m_csMSG,m_edMSG); + return( lResult ); +} diff --git a/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.h b/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.h index 6118f4f..f5b3920 100644 --- a/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.h +++ b/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil/So7_Util_PLC_TCPIP.h @@ -28,5 +28,6 @@ public: afx_msg void OnBnClickedButtonPlcTcpipClearMsg(); afx_msg void OnBnClickedCancel(); afx_msg LRESULT OnSocket(WPARAM w, LPARAM p); + afx_msg LRESULT OnTCPIPRecv(WPARAM w, LPARAM p); afx_msg void OnBnClickedButtonPlcTcpipHandshaking(); }; diff --git a/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil_VS2010.suo b/PcDmis/Base/Interfac/Msi/Hsi/Tools/UsbUtility/UsbUtil_VS2010.suo index c3df894b1b2a5aa9814ab8d8fb02641be216c094..9e0d9f1c459f2ab5a579f0787cfa5bd17efb9b10 100644 GIT binary patch delta 3544 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 Hz775hy#R%; delta 3617 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 Wd@UiPem1uY`)?sdSvH$L5B~w-q=kC`