多串口数据接收系统

       分享一个几年前写的多串口(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

点击   下载该案例全部完整代码

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

previewer1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值