강좌 & 팁
지난 시간까지 UI 를 구성 하고, 시리얼 통신 클래스를 등록 하는데 까지 했습니다.
이번 시간에는 실제 CMycomm 클래스의 각 메소드들의 기능 구현부분을 알아 보겠습니다.
1. 먼저 CMycomm 클래스를 다이얼로그 클래스에서 사용 하기 때문에 헤더파일을 추가해 줍니다.
--> serialDlg.h 파일을 열어 Mycomm.h 파일을 include 합니다.
2. Mycomm.h 파일을 열어 각 메소드 들을 선언 합니다.
#pragma once
#include <afxmt.h>
#define MAXBUF 50000
#define InBufSize 50000 #define OutBufSize 50000 #define
ASCII_XON 0x11 #define
ASCII_XOFF 0x13 #define WM_MYRECEIVE (WM_USER+1)
//데이터 수신 메시지 #define WM_MYCLOSE (WM_USER+2)
// 종료 메시지
// CMycomm
command target //클래스생성자 class CMycomm : public CCmdTarget { DECLARE_DYNAMIC(CMycomm)
public: CMycomm(); virtual
~CMycomm();
// Attributes public: HANDLE m_hComDev; HWND m_hWnd;
BOOL m_bIsOpenned; CString m_sComPort; CString m_sBaudRate; CString m_sParity; CString m_sDataBit; CString m_sStopBit; BOOL m_bFlowChk;
OVERLAPPED m_OLR,m_OLW; char m_sInBuf[MAXBUF*2]; int m_nLength; CEvent* m_pEvent;
// Operations public: void Clear(); int Receive(LPSTR inbuf, int len); BOOL Send(LPCTSTR outbuf, int len); BOOL Create(HWND hWnd); void HandleClose(); void Close(); void ResetSerial(); CMycomm(CString port,CString
baudrate,CString parity,CString databit,CString stopbit);
protected: DECLARE_MESSAGE_MAP() }; |
--> 붉은색 글씨로 표시된 코드들을 추가 합니다.
3. Mycomm.cpp 파일을 열어 실제 구현 부를 추가 해 줍니다.
// Mycomm.cpp : implementation file //
#include "stdafx.h" #include "serial.h" #include "Mycomm.h"
// CMycomm
IMPLEMENT_DYNAMIC(CMycomm,
CCmdTarget)
CMycomm::CMycomm() { }
CMycomm::~CMycomm() { if (m_bIsOpenned) Close(); delete
m_pEvent; }
CMycomm::CMycomm(CString port,CString baudrate,CString
parity,CString databit,CString stopbit) { m_sComPort
= port; m_sBaudRate
= baudrate; m_sParity
= parity; m_sDataBit
= databit; m_sStopBit
= stopbit; m_bFlowChk
= 1; m_bIsOpenned
= FALSE; m_nLength
= 0; memset(m_sInBuf,0,MAXBUF*2); m_pEvent
= new CEvent(FALSE,TRUE); }
BEGIN_MESSAGE_MAP(CMycomm,
CCmdTarget) END_MESSAGE_MAP()
// CMycomm message handlers void CMycomm::ResetSerial() { DCB dcb; DWORD DErr; COMMTIMEOUTS CommTimeOuts;
if
(!m_bIsOpenned) return;
ClearCommError(m_hComDev,&DErr,NULL); SetupComm(m_hComDev,InBufSize,OutBufSize); PurgeComm(m_hComDev,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
//
set up for overlapped I/O CommTimeOuts.ReadIntervalTimeout
= MAXDWORD ; CommTimeOuts.ReadTotalTimeoutMultiplier
= 0 ; CommTimeOuts.ReadTotalTimeoutConstant
= 0 ;
//
CBR_9600 is approximately 1byte/ms. For our purposes, allow //
double the expected time per character for a fudge factor. CommTimeOuts.WriteTotalTimeoutMultiplier
= 0; CommTimeOuts.WriteTotalTimeoutConstant
= 1000; SetCommTimeouts(m_hComDev,
&CommTimeOuts);
memset(&dcb,0,sizeof(DCB)); dcb.DCBlength
= sizeof(DCB);
GetCommState(m_hComDev,
&dcb);
dcb.fBinary
= TRUE; dcb.fParity
= TRUE;
if
(m_sBaudRate == "300") dcb.BaudRate
= CBR_300; else
if (m_sBaudRate == "600") dcb.BaudRate
= CBR_600; else
if (m_sBaudRate == "1200") dcb.BaudRate
= CBR_1200; else
if (m_sBaudRate == "2400") dcb.BaudRate
= CBR_2400; else
if (m_sBaudRate == "4800") dcb.BaudRate
= CBR_4800; else
if (m_sBaudRate == "9600") dcb.BaudRate
= CBR_9600; else
if (m_sBaudRate == "14400") dcb.BaudRate
= CBR_14400; else
if (m_sBaudRate == "19200") dcb.BaudRate
= CBR_19200; else
if (m_sBaudRate == "28800") dcb.BaudRate
= CBR_38400; else
if (m_sBaudRate == "33600") dcb.BaudRate
= CBR_38400; else
if (m_sBaudRate == "38400") dcb.BaudRate
= CBR_38400; else
if (m_sBaudRate == "56000") dcb.BaudRate
= CBR_56000; else
if (m_sBaudRate == "57600") dcb.BaudRate
= CBR_57600; else
if (m_sBaudRate == "115200") dcb.BaudRate
= CBR_115200; else
if (m_sBaudRate == "128000") dcb.BaudRate
= CBR_128000; else
if (m_sBaudRate == "256000") dcb.BaudRate
= CBR_256000; else
if (m_sBaudRate == "PCI_9600") dcb.BaudRate
= 1075; else
if (m_sBaudRate == "PCI_19200") dcb.BaudRate
= 2212; else
if (m_sBaudRate == "PCI_38400") dcb.BaudRate
= 4300; else
if (m_sBaudRate == "PCI_57600") dcb.BaudRate
= 6450; else
if (m_sBaudRate == "PCI_500K") dcb.BaudRate
= 56000;
if
(m_sParity == "None") dcb.Parity
= NOPARITY; else
if (m_sParity == "Even") dcb.Parity
= EVENPARITY; else
if (m_sParity == "Odd") dcb.Parity
= ODDPARITY;
if
(m_sDataBit == "7 Bit") dcb.ByteSize
= 7; else
if (m_sDataBit == "8 Bit") dcb.ByteSize
= 8;
if
(m_sStopBit == "1 Bit") dcb.StopBits
= ONESTOPBIT; else
if (m_sStopBit == "1.5 Bit") dcb.StopBits
= ONE5STOPBITS; else
if (m_sStopBit == "2 Bit") dcb.StopBits
= TWOSTOPBITS;
dcb.fRtsControl
= RTS_CONTROL_ENABLE; dcb.fDtrControl
= DTR_CONTROL_ENABLE; dcb.fOutxDsrFlow
= FALSE;
if
(m_bFlowChk) { dcb.fOutX
= FALSE; dcb.fInX
= FALSE; dcb.XonLim
= 2048; dcb.XoffLim
= 1024; } else
{ dcb.fOutxCtsFlow
= TRUE; dcb.fRtsControl
= RTS_CONTROL_HANDSHAKE; }
SetCommState(m_hComDev,
&dcb); SetCommMask(m_hComDev,EV_RXCHAR);
}
void CMycomm::Close() { if
(!m_bIsOpenned) return;
m_bIsOpenned
= FALSE; SetCommMask(m_hComDev,
0); EscapeCommFunction(m_hComDev,
CLRDTR); PurgeComm(m_hComDev,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); Sleep(500);
}
UINT CommThread(LPVOID lpData) { extern
short g_nRemoteStatus; DWORD ErrorFlags; COMSTAT ComStat; DWORD EvtMask ; char buf[MAXBUF]; DWORD Length; int size; int insize = 0;
CMycomm*
Comm = (CMycomm*)lpData;
while
(Comm->m_bIsOpenned) { EvtMask
= 0; Length
= 0; insize
= 0; memset(buf,'\0',MAXBUF); WaitCommEvent(Comm->m_hComDev,&EvtMask,
NULL); ClearCommError(Comm->m_hComDev,
&ErrorFlags, &ComStat); if
((EvtMask & EV_RXCHAR) && ComStat.cbInQue) { if
(ComStat.cbInQue > MAXBUF) size
= MAXBUF; else size
= ComStat.cbInQue; do { ClearCommError(Comm->m_hComDev,
&ErrorFlags, &ComStat); if
(!ReadFile(Comm->m_hComDev,buf+insize,size,&Length,&(Comm->m_OLR)))
{ //
에러 TRACE("Error
in ReadFile\n"); if
(GetLastError() == ERROR_IO_PENDING) { if
(WaitForSingleObject(Comm->m_OLR.hEvent, 1000) != WAIT_OBJECT_0) Length = 0; else GetOverlappedResult(Comm->m_hComDev,&(Comm->m_OLR),&Length,FALSE); } else Length
= 0; } insize
+= Length; }
while ((Length!=0) && (insize<size)); ClearCommError(Comm->m_hComDev,
&ErrorFlags, &ComStat);
if
(Comm->m_nLength + insize > MAXBUF*2) insize
= (Comm->m_nLength + insize) - MAXBUF*2;
Comm->m_pEvent->ResetEvent(); memcpy(Comm->m_sInBuf+Comm->m_nLength,buf,insize); Comm->m_nLength
+= insize; Comm->m_pEvent->SetEvent(); LPARAM
temp=(LPARAM)Comm; SendMessage(Comm->m_hWnd,WM_MYRECEIVE,Comm->m_nLength,temp); } } PurgeComm(Comm->m_hComDev,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); LPARAM
temp=(LPARAM)Comm; SendMessage(Comm->m_hWnd,WM_MYCLOSE,0,temp); return
0; }
void CMycomm::HandleClose() { CloseHandle(m_hComDev); CloseHandle(m_OLR.hEvent); CloseHandle(m_OLW.hEvent);
}
BOOL CMycomm::Create(HWND hWnd) { m_hWnd
= hWnd;
m_hComDev
= CreateFile(m_sComPort, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
if
(m_hComDev!=INVALID_HANDLE_VALUE) m_bIsOpenned
= TRUE; else return
FALSE;
ResetSerial();
m_OLW.Offset
= 0; m_OLW.OffsetHigh
= 0; m_OLR.Offset
= 0; m_OLR.OffsetHigh
= 0;
m_OLR.hEvent
= CreateEvent(NULL, TRUE, FALSE, NULL); if
(m_OLR.hEvent == NULL) { CloseHandle(m_OLR.hEvent); return
FALSE; } m_OLW.hEvent
= CreateEvent(NULL, TRUE, FALSE, NULL); if
(m_OLW.hEvent == NULL) { CloseHandle(m_OLW.hEvent); return
FALSE; } AfxBeginThread(CommThread,(LPVOID)this); EscapeCommFunction(m_hComDev,
SETDTR); return
TRUE;
}
BOOL CMycomm::Send(LPCTSTR outbuf, int len) { BOOL bRet=TRUE; DWORD ErrorFlags; COMSTAT ComStat;
DWORD BytesWritten; DWORD BytesSent=0;
ClearCommError(m_hComDev,
&ErrorFlags, &ComStat); if
(!WriteFile(m_hComDev, outbuf, len, &BytesWritten, &m_OLW)) { if
(GetLastError() == ERROR_IO_PENDING) { if
(WaitForSingleObject(m_OLW.hEvent,1000) != WAIT_OBJECT_0) bRet=FALSE; else GetOverlappedResult(m_hComDev,
&m_OLW, &BytesWritten, FALSE); } else
/* I/O error */ bRet=FALSE;
/* ignore error */ }
ClearCommError(m_hComDev,
&ErrorFlags, &ComStat);
return
bRet;
}
int CMycomm::Receive(LPSTR inbuf, int len) { CSingleLock
lockObj((CSyncObject*) m_pEvent,FALSE); //
argument value is not valid if
(len == 0) return
-1; else
if (len > MAXBUF) return
-1;
if
(m_nLength == 0) { inbuf[0]
= '\0'; return
0; } else
if (m_nLength <= len) { lockObj.Lock(); memcpy(inbuf,m_sInBuf,m_nLength); memset(m_sInBuf,0,MAXBUF*2); int
tmp = m_nLength; m_nLength
= 0; lockObj.Unlock(); return
tmp; } else { lockObj.Lock(); memcpy(inbuf,m_sInBuf,len); memmove(m_sInBuf,m_sInBuf+len,MAXBUF*2-len); m_nLength
-= len; lockObj.Unlock(); return
len; } }
void CMycomm::Clear() { PurgeComm(m_hComDev,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); memset(m_sInBuf,0,MAXBUF*2); m_nLength
= 0;
} |
--> 한번 만들어 보는 것을 목적으로 하기 때문에 자세한 코드 설명은 하지 않겠습니다. 대충 보면 알겠지만, 통신 환경을 설정 하는 부분이 있고, 수신부, 포트 닫기 등의 동작이 구현 되어 있습니다.
--> 데이터 수신은 스레드로 돌리고, 수신되면 데이터를 버퍼에 저장 후 헤더파일에 define 해놓은 메시지를 발생 시킵니다.
물론 스레드를 종료 할때도 메시지를 전달 하는식 입니다.
여기까지 해서 하나의 cpp 파일과 하나의 헤더 파일로 시리얼 통신을 사용 하기 위한 클래스는 완성 되었습니다. 이제 다이얼로그 클래스에서 이 시리얼통신 클래스의 객체를 하나 만들어 위 메소드들을 사용 하기만 하면 됩니다.
다음 시간에는 이 클래스를 사용 하는 방법을 알아 보겠습니다.
강의정말 잘 보고 있습니다.
질문드려도 괜찮을지 모르겠는데요 ㅠ ㅋ
통신환경 설정 수신부 등 설정 부분은 거의 모든 시리얼 통신 프로그램이 동일한건가요?