📄 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 + -