?? gpscomm.cpp
字號:
///////////////////////////////////////////////////////////////////////////////
// GpsComm.cpp : implementation of the CGpsComm class
//
// Copyright (c) 2004,上海合眾思壯科技有限責任公司GIS部
//
// All rights reserved
//
// 文件名稱:GpsComm.cpp
//
// 摘要 :通訊基類
//
// 作者 :Hansom
//
// 當前版本:1.1
//
// 完成日期:2004年04月12日
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "GpsComm.h"
#include "Nmea0183.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
const long MSG_SIZE = 256; // 定義消息數常量
const long MSG_NUMBER = 8; // 定義消息的記錄數目
///////////////////////////////////////////////////////////////////////////////
// 初始化靜態成員
CGpsComm *CGpsComm::c_GpsCommObj = NULL;
///////////////////////////////////////////////////////////////////////////////
// 返回當前使用的通訊對象指針
IGpsComm *GetCurrentGpsComm()
{
return CGpsComm::GetGpsCommObjPtr();
}
///////////////////////////////////////////////////////////////////////////////
// 創建接口指針,并返回需要的指針類型
IGpsComm *CreateGpsComm(GPS_SERIAL_TYPE type)
{
IGpsComm *pGpsCommPtr = CGpsComm::GetGpsCommObjPtr();
switch (type)
{
case Gps_Type_Nmea0183:
pGpsCommPtr = new CNmea0183; // 創建0183類指針
break;
case Gps_Type_GarMin:
//pGpsCommPtr = new CGarMin; // 創建指向GarMin類的指針
break;
default:
pGpsCommPtr = NULL; // 創建失敗,沒有指向任何類的對象
break;
}
return pGpsCommPtr;
}
///////////////////////////////////////////////////////////////////////////////
// 關閉并刪除GPSCOMM對象
BOOL CloseGpsComm()
{
CGpsComm *pGpsCommPtr = CGpsComm::GetGpsCommObjPtr();
if(pGpsCommPtr != NULL)
{
delete pGpsCommPtr;
pGpsCommPtr = NULL;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 通訊基類構造函數
CGpsComm::CGpsComm()
{
m_hComm = NULL;
m_Thread = NULL;
m_bThreadAlive = FALSE;
memset(&m_SerialPara, 0, sizeof(ST_COMM_PARA));
m_pchRevBuffer = NULL;
m_nRevRPtr = 0;
m_nRevWPtr = 0;
m_nMsgCur = 0;
// 初始化讀寫緩沖區和消息隊列
m_pchRevBuffer = new char[BUFFER_SIZE];
m_struMsg = new ST_MSG[MSG_NUMBER];
for(int i = 0; i < MSG_NUMBER; i++)
{
m_struMsg[i].pMsg = new char[MSG_SIZE];
}
c_GpsCommObj = this;
InitializeCriticalSection(&m_csCommunicationSync);
}
///////////////////////////////////////////////////////////////////////////////
// 通訊基類析構函數
CGpsComm::~CGpsComm()
{
if(m_hComm != NULL) //關閉端口句柄
{
EnterCriticalSection(&m_csCommunicationSync);
CloseHandle(m_hComm);
LPDWORD lpExitCode = 0;
GetExitCodeThread(m_hComm, lpExitCode);
TerminateThread(m_hComm, DWORD(lpExitCode));
m_hComm = NULL;
LeaveCriticalSection(&m_csCommunicationSync);
}
if(m_Thread != NULL) //關閉讀線程句柄
{
LPDWORD lpExitCode = 0;
GetExitCodeThread(m_Thread, lpExitCode);
TerminateThread(m_Thread, DWORD(lpExitCode));
m_Thread = NULL;
}
if(m_pchRevBuffer != NULL)
{
delete [] m_pchRevBuffer;
m_pchRevBuffer = NULL;
}
for(int i = 0; i < MSG_NUMBER; i++)
{
if(m_struMsg[i].pMsg != NULL)
{
delete [] m_struMsg[i].pMsg;
m_struMsg[i].pMsg = NULL;
}
}
if(m_struMsg != NULL)
{
delete [] m_struMsg;
m_struMsg = NULL;
}
m_listWnd.RemoveAll();
}
///////////////////////////////////////////////////////////////////////////////
// 啟動端口任務
BOOL CGpsComm::OpenComm(CWnd *pwnd, GPS_DATA_TYPE dwMsgType)
{
if(pwnd == NULL)
{
return FALSE;
}
ST_LOG_WND LogWnd;
LogWnd.pWnd = pwnd;
LogWnd.dwMsgType = dwMsgType;
// 將窗口句柄加入活動列表
m_listWnd.AddHead(LogWnd);
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 開始端口任務
BOOL CGpsComm::Start()
{
//如果線程處于激活狀態,那么先要關閉該線程
if(m_bThreadAlive)
{
do
{
LPDWORD lpExitCode = 0;
GetExitCodeThread(m_Thread, lpExitCode);
m_bThreadAlive = FALSE;
TerminateThread(m_Thread, DWORD(lpExitCode));
}
while (m_bThreadAlive);
}
// 如果串口已經打開,需要關閉串口
if(m_hComm != NULL)
{
EnterCriticalSection(&m_csCommunicationSync);
CloseHandle(m_hComm);
m_hComm = NULL;
LeaveCriticalSection(&m_csCommunicationSync);
}
CString strPort;
strPort.Format(L"COM%d:", m_SerialPara.cPort);
// 創建串口HANDLE
m_hComm = CreateFile(strPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,OPEN_EXISTING, 0, NULL);
if (m_hComm == INVALID_HANDLE_VALUE)
{
TCHAR msgstr[MSG_NUMBER]; //發送錯誤信息
m_hComm = NULL;
wsprintf(msgstr, L"無法打開端口 %d. 錯誤號:%d", m_SerialPara.cPort, GetLastError());
AfxMessageBox(msgstr, MB_ICONERROR, MB_OK);
return FALSE;
}
// 設置串口參數
DCB dcb;
GetCommState(m_hComm, &dcb);
dcb.BaudRate = DWORD(m_SerialPara.lBaud);
dcb.fParity = BYTE(m_SerialPara.cParity);
dcb.StopBits = BYTE(m_SerialPara.cStopBits);
dcb.Parity = BYTE(m_SerialPara.cParity);
dcb.ByteSize = BYTE(m_SerialPara.cDataBits);
dcb.fNull = FALSE;
dcb.fAbortOnError = FALSE;
if(!SetCommState(m_hComm, &dcb))
{
CloseHandle(m_hComm);
m_hComm = NULL;
return FALSE;
}
// 設置超時時間
COMMTIMEOUTS CommTimeouts;
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
CommTimeouts.ReadTotalTimeoutConstant = 50;
CommTimeouts.WriteTotalTimeoutMultiplier = MAXDWORD;
CommTimeouts.WriteTotalTimeoutConstant = 0;
if(!SetCommTimeouts(m_hComm, &CommTimeouts) || !SetCommMask(m_hComm, EV_BREAK | EV_ERR | EV_RXCHAR))
{
CloseHandle(m_hComm);
m_hComm = NULL;
return FALSE;
}
// 清除串口數據
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
if(IsPortOpen())
{
// 創建線程,如果失敗則返回
if (!(m_Thread = AfxBeginThread((AFX_THREADPROC)CommThread, this)))
{
return FALSE;
}
m_bThreadAlive = TRUE;
m_nRevRPtr = 0;
m_nRevWPtr = 0;
m_nMsgCur = 0;
}
else
{
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 暫停端口任務
BOOL CGpsComm::Stop()
{
if(m_hComm != NULL) //關閉端口句柄
{
EnterCriticalSection(&m_csCommunicationSync);
CloseHandle(m_hComm);
LPDWORD lpExitCode = 0;
GetExitCodeThread(m_hComm, lpExitCode);
TerminateThread(m_hComm, DWORD(lpExitCode));
m_hComm = NULL;
LeaveCriticalSection(&m_csCommunicationSync);
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 暫停端口任務
BOOL CGpsComm::CloseComm(CWnd * pwnd)
{
if(pwnd == NULL)
{
return TRUE;
}
ST_LOG_WND LogWnd;
POSITION oldPos = NULL;
// This method retrieves the position of the head element of this list
POSITION pos = m_listWnd.GetHeadPosition();
while(pos != NULL)
{
oldPos = pos;
LogWnd = (ST_LOG_WND)m_listWnd.GetNext(pos);
// 找到需要注銷的窗口句柄將其移出活動列表
if(LogWnd.pWnd == pwnd)
{
m_listWnd.RemoveAt(oldPos);
break;
}
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 得到需要的消息
BOOL CGpsComm::GetNeedMsg(DWORD dwMsgType, GPS_DATA_TYPE emMsg)
{
return (((dwMsgType & ENUM_All_GPS) && (emMsg < ENUM_ALL_NAVI)) ||
((dwMsgType & ENUM_ALL_NAVI)) && (emMsg > ENUM_ALL_NAVI));
};
///////////////////////////////////////////////////////////////////////////////
// 設置當前串口參數
BOOL CGpsComm::SetCommPara(const ST_COMM_PARA &pSerialPara)
{
// 串口參數設置
memcpy(&m_SerialPara, &pSerialPara, sizeof(ST_COMM_PARA));
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// 得到端口參數
ST_COMM_PARA CGpsComm::GetCommPara() const
{
return m_SerialPara;
}
///////////////////////////////////////////////////////////////////////////////
// 函數名:ReceiveData
// 功能 :讀取串口接收緩沖區的所有數據,寫到接收緩沖區中。
// 并調用協議的成員函數將數據轉換為對應的消息結構
// 發送消息到當前注冊的需要數據的窗口。
// 返回值:無
///////////////////////////////////////////////////////////////////////////////
void CGpsComm::ReceiveData()
{
DWORD dwError = 0;
DWORD BytesRead = 0;
DWORD dwReadNum = 0;
DWORD dwNum = 0;
BOOL bResult;
char RXBuff;
COMSTAT comstat;
if(m_hComm == NULL)
{
return;
}
bResult = ClearCommError(m_hComm, &dwError, &comstat);
dwNum = comstat.cbInQue;
while(dwNum > 0)
{
// 從串口讀出一個字符
bResult = ReadFile(m_hComm, &RXBuff, 1, &BytesRead, NULL);
// 發生讀錯誤
if(!bResult)
{
DWORD dwRead = ::GetLastError();
if(dwRead == ERROR_OPERATION_ABORTED)
{
::ClearCommError(m_hComm, &dwError, &comstat);
}
break;
}
if (1 != BytesRead)
{
break;
}
// 將字符寫入接收緩沖區
WriteData(RXBuff);
dwReadNum++;
dwNum--;
}
if(dwReadNum > 0)
{
// 有新數據讀出
int nRtn;
do
{
nRtn = this->SearchOneFrame(m_pchRevBuffer, BUFFER_SIZE, m_nRevRPtr,
m_nRevWPtr, m_struMsg + m_nMsgCur);
if(nRtn == 0)
{
break; // 結束
}
// 幀正確
if(nRtn > 0)
{
// 可以在此處輸出幀數據
POSITION pos = m_listWnd.GetHeadPosition();
ST_LOG_WND LogWnd;
for(int i = 0; i < m_listWnd.GetCount(); i++)
{
LogWnd = (ST_LOG_WND)m_listWnd.GetNext(pos);
if((LogWnd.pWnd != NULL) &&
(GetNeedMsg(LogWnd.dwMsgType, (m_struMsg+m_nMsgCur)->MsgHead.enumMsgType)))
{
::PostMessage(LogWnd.pWnd->m_hWnd,
WM_COMM_RXCHAR, (WPARAM)(m_struMsg + m_nMsgCur), 0);
}
}
m_nMsgCur++;
if(m_nMsgCur >= MSG_NUMBER)
{
m_nMsgCur = 0;
}
}
}while(nRtn != 0);
}
}
/*
BOOL CGpsComm::SendData(strucMsg* pMsg)
{
DWORD BytesSent = 0;
BOOL bResult;
//串口未打開
if(m_hComm == NULL)
return FALSE;
//將數據結果轉換為協議幀格式
int nFrmLen;
int nFrmNo = 0;
do
{//nFrmNo代表幀序號,為零代表無后續幀,該幀為結束幀
nFrmLen = m_pProtocol->TranslateData(pMsg,m_pchSendBuffer,nFrmNo);
if(nFrmLen == 0)
return FALSE;
EnterCriticalSection(&m_csCommunicationSync);
bResult = WriteFile(m_hComm,m_pchSendBuffer,nFrmLen,&BytesSent,NULL);
LeaveCriticalSection(&m_csCommunicationSync);
if((!bResult) || ((int)BytesSent != nFrmLen))
//寫錯誤
return FALSE;
//連續發多幀,間隔100ms
if(nFrmNo != 0)
Sleep(100);
}while(nFrmNo != 0);
return TRUE;
}
*/
///////////////////////////////////////////////////////////////////////////////
// 串口監聽線程
UINT CGpsComm::CommThread(LPVOID pParam)
{
CGpsComm *port = (CGpsComm*)pParam;
if(port == NULL)
{
return 0;
}
DWORD CommEvent = 0;
DWORD dwError = 0;
DWORD dwBytesRead = 0;
char RxBuff = 0;
BOOL bResult = TRUE;
COMSTAT comstat;
// 用ReadFile()監聽串口
while(port->IsPortOpen())
{
if(NULL == port->m_hComm)
{
break;
}
EnterCriticalSection(&port->m_csCommunicationSync);
bResult = ReadFile(port->m_hComm, &RxBuff, 1, &dwBytesRead, NULL);
LeaveCriticalSection(&port->m_csCommunicationSync);
if(NULL == port->m_hComm)
{
break;
}
if(!bResult)
{
// 讀出錯
DWORD dwRead = ::GetLastError();
if(dwRead == ERROR_OPERATION_ABORTED)
{
::ClearCommError(port->m_hComm, &dwError, &comstat);
}
continue;
}
if(1 != dwBytesRead)
{
continue;
}
// 將字符寫入接收緩沖區
port->WriteData(RxBuff);
// 接收剩余其他字符
EnterCriticalSection(&port->m_csCommunicationSync);
port->ReceiveData();
LeaveCriticalSection(&port->m_csCommunicationSync);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// 函數名: WriteData
// 功能 : 將一個字符寫入接收緩沖區
// 參數 : chData: 被寫入的字符
// 返回值: 無
///////////////////////////////////////////////////////////////////////////////
void CGpsComm::WriteData(char chData)
{
// 將字符寫入接收緩沖區
m_pchRevBuffer[m_nRevWPtr] = chData;
m_nRevWPtr++;
if(m_nRevWPtr >= BUFFER_SIZE)
{
m_nRevWPtr = 0; // 收滿緩沖區
}
if(m_nRevWPtr == m_nRevRPtr)
{
m_nRevRPtr++;
if(m_nRevRPtr >= BUFFER_SIZE)
{
m_nRevRPtr = 0;
}
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -