分享一个几年前写的多串口(8个)数据接收系统,每个串口可分别设置不同的波特率,数据可保存在不同的文件(路径可设),且文件到达设定的大小,自动创建新文件;磁盘空间不足时声音报警。
编程语言VC,串口通信使用了大名鼎鼎的CSerialPort(原作者Remon Spekreijse);数据接收和保存使用了多线程技术。
下面介绍下本案例的主要类和函数:
一、主视图类CMultiComSampSysView
1、CPP内关键全局变量的定义
//各通道没收到数据的报警时长(ms)
#define NODATA_ALERTTIME_LEN1 10000
#define NODATA_ALERTTIME_LEN2 10000
#define NODATA_ALERTTIME_LEN3 10000
#define NODATA_ALERTTIME_LEN4 10000
#define NODATA_ALERTTIME_LEN5 10000
#define NODATA_ALERTTIME_LEN6 10000
#define NODATA_ALERTTIME_LEN7 10000
#define NODATA_ALERTTIME_LEN8 10000
//报警磁盘空间(MB)
#define DISKSPACE_ALERTNUM 500
//串口接收缓冲区长度
#define SERIALCOM_DATABUF_LEN 10240
//串口接收数据缓冲区指针
BYTE* glpComRecvBuffer[8];
//各串口每次写入文件的长度
static int glWriteToFilePerTime[8];
//各串口接收数据总长度
static __int64 glRecvTotalLen[8];
//各串口保存数据总长度
static __int64 glSaveTotalLen[8];
//各串口写入缓冲区的指针位置
static int glWritePos[8];
//各串口从缓冲区读的指针位置
static int glReadPos[8];
//各通道上一次接收数据的TickCount值
static DWORD gldwLastRecvTime[8];
//各通道没收到数据报警超时
static DWORD gldwAlertTime[8];
每个串口都分别设置了数据接收缓冲区glpComRecvBuffer,串口接收到的数据先放到数据缓冲区,数据保存线程从缓存区中读取数据再保存到文件中,使用此机制防止数据丢失。数据缓冲区的长度可修改宏定义,每个通道的数据报警间隔也可通过宏定义修改。
2、串口初始化
void CMultiComSampSysView::InitAllPorts()
{
CString strInfo;
BOOL bInit =FALSE;
for (int i=0; i< 8; i++)
{
m_uiRecvMsgID[i] = WM_RECVDATA_DEV1 +i;
m_serialPort[i].InitMessageID(m_uiRecvMsgID[i],i);
bInit = m_serialPort[i].InitPort(this->m_hWnd,m_stComPara[i].iComID,m_stComPara[i].dwBaudRate,m_stComPara[i].bparity,m_stComPara[i].bDataBits,m_stComPara[i].bStopBits);
if (!bInit)
{
strInfo.Format("设备%d串口初始化错误.",i+1);
::MessageBox(NULL,strInfo,_T("初始化"),MB_OK|MB_ICONERROR);
break;
}
strInfo.Format("设备%d串口初始化成功,串口号:%d,波特率:%d.",i+1,m_stComPara[i].iComID,m_stComPara[i].dwBaudRate);
m_lstComLog.InsertString(0,strInfo);
}
}
m_serialPort是在头文件定义的8个串口类,每个串口对应一个。数据接收后通过不同的消息传递,所以在初始化时分别传入不同的消息ID:WM_RECVDATA_DEV1~8.
3、启动数据传输
void CMultiComSampSysView::StartSerialComTrans()
{
m_bComThreadRun = TRUE;
for (int i=0 ;i< 8; i++)
{
gldwLastRecvTime[i] =0;
ST_COMTHREAD_PARA* pComThreadPara = new ST_COMTHREAD_PARA;
pComThreadPara->pView = this;
pComThreadPara->iDevID =i;
m_comDataSerialize[i].InitFilePath(m_stComPara[i].strPath,i,m_stComPara[i].uiFileSize);
m_hComThread[i] = ::CreateThread (NULL, 0, SerialComThread, (LPVOID)pComThreadPara, 0, NULL);
ASSERT(m_hComThread != NULL);
BOOL bStart =m_serialPort[i].StartMonitoring();
ASSERT(bStart);
}
m_lstComLog.InsertString(0,"数据接收已启动......");
//启动定时器,检查磁盘空间:10分钟检查一次
SetTimer(1,10*60*1000,NULL);
SetTimer(2,2*1000,NULL);
}
启动串口接收前,先创建报文文件InitFilePath、创建数据处理与保存线程SerialComThread.然后启动两个定时器,定时探测剩余的磁盘空间、定时统计数据接收间隔。
4、数据接收处理---->保存到缓冲区
void CMultiComSampSysView::SaveComDataToBuf(ST_RECVCOMDATA* pComData)
{
int iDevID = pComData->iDevID;
glRecvTotalLen[iDevID] += pComData->dwLength;
if (glWritePos[iDevID] +pComData->dwLength < SERIALCOM_DATABUF_LEN)
{
memcpy(glpComRecvBuffer[iDevID]+ glWritePos[iDevID],pComData->pData,pComData->dwLength);
glWritePos[iDevID] +=pComData->dwLength;
}
else if(glWritePos[iDevID] +pComData->dwLength == SERIALCOM_DATABUF_LEN){
memcpy(glpComRecvBuffer[iDevID]+ glWritePos[iDevID],pComData->pData,pComData->dwLength);
glWritePos[iDevID] = 0;;
}
else{
int iTailLen = SERIALCOM_DATABUF_LEN - glWritePos[iDevID] ;
int iHeadLen = pComData->dwLength - iTailLen;
memcpy(glpComRecvBuffer[iDevID]+ glWritePos[iDevID],pComData->pData,iTailLen);
memcpy(glpComRecvBuffer[iDevID], pComData->pData + iTailLen, iHeadLen);
glWritePos[iDevID] = iHeadLen;
}
}
5、数据保存处理---->从缓冲区取出保存到文件
DWORD CMultiComSampSysView::SerialComThread(LPVOID pParam)
{
ST_COMTHREAD_PARA* pstThreadPara =(ST_COMTHREAD_PARA*)pParam;
CMultiComSampSysView *pView = (CMultiComSampSysView*)pstThreadPara->pView;
int iDevID = pstThreadPara->iDevID;
BYTE bTempArray[128] ={0};
while(pView->m_bComThreadRun)
{
if (glSaveTotalLen[iDevID] + glWriteToFilePerTime[iDevID] <= glRecvTotalLen[iDevID])
{
//读指针位置 + 读长度 <= 写指针
if (glReadPos[iDevID] + glWriteToFilePerTime[iDevID] <= glWritePos[iDevID])
{
pView->m_comDataSerialize[iDevID].SaveData(glpComRecvBuffer[iDevID] +glReadPos[iDevID] ,glWriteToFilePerTime[iDevID]);
glReadPos[iDevID] += glWriteToFilePerTime[iDevID];
}else if (glReadPos[iDevID] > glWritePos[iDevID]) //读指针位置 》写指针位置
{
if (glReadPos[iDevID] + glWriteToFilePerTime[iDevID] <= SERIALCOM_DATABUF_LEN) //读指针位置+ 读长度 《 缓冲区总长度
{
pView->m_comDataSerialize[iDevID].SaveData(glpComRecvBuffer[iDevID] +glReadPos[iDevID] ,glWriteToFilePerTime[iDevID]);
glReadPos[iDevID] += glWriteToFilePerTime[iDevID];
}
else{
int iTailLen = SERIALCOM_DATABUF_LEN - glReadPos[iDevID] ;
int iHeadLen = glWriteToFilePerTime[iDevID] - iTailLen;
memcpy(bTempArray,glpComRecvBuffer[iDevID] + glReadPos[iDevID],iTailLen);
memcpy(bTempArray +iTailLen,glpComRecvBuffer[iDevID],iHeadLen);
glReadPos[iDevID] = iHeadLen;
pView->m_comDataSerialize[iDevID].SaveData(bTempArray,glWriteToFilePerTime[iDevID]);
}
}
glSaveTotalLen[iDevID] += glWriteToFilePerTime[iDevID];
}
Sleep(10);
}
delete pstThreadPara;
return 0;
}
二 、其他类
1、文件初始化及数据保存类CComDataSerial
2、串口参数设置窗口类CDlgComParaSet
3、文件路径及单个文件大小设置窗口类CDlgDevFileSet
4、ini文件操作类CZpwSysparaSet
点击 下载该案例全部完整代码
3557

被折叠的 条评论
为什么被折叠?



