我这里有一个设备,该设备能够提供rs-485通讯,这个设备是用被动的模式实现rs-485通讯的.通讯协议我有.需要有一套软件对该设备(可能是255个)进行轮询,而后捕获各个数据反馈数据采集到以后需要存储到数据库中.轮询的算法写不出来,请大家看看我的代码,帮忙提提建议,谢谢! 我的想法是: 用定时器每5分钟轮询一次,每个周期需要从5台设备query数据.
在一个轮询周期中,主机需要分别发一个命令给每个设备,收到设备回答后,方可继续query下一个设备. 依次查询,5台都查完之后, 等到下一个轮询周期到来.再分别发命令给5台设备. 我的困难是不知道如何实现上面的算法:
我在OnTimer 里面发命令给设备,COM1口如果有数据,即触发MSCOMM控件的OnCommMscomm1事件.在这个事件处理函数中,如果数据包没结束,则先KillTimer(暂停定时器,即下一个命令不能发), 如数据包接收完整,则再次SetTimer. 可是我的代码只能应对一台设备, 5台设备怎么办呢? 在OnTimer函数中for循环好像不行吧.
我要的是: 主机与一台设备对话时, 不能再发下一个命令, 直到收到当前设备回应. 收到后立马再与下一个设备通话, 5台全部通讯完, 停止等待下一个query周期的到来. 请大侠们帮忙,我的算法该如何实现呢,好急呀! 想两天了,还是没想出来. void CPanel::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CString m_strCommand;
m_strCommand = "{FA;FC; <Length> ¦64:PAC=1ABC;KDY=12A;KT0=13FB6;KYR=13FB6;RAD=1C2 ¦ <Crc> }";
Sleep(200);
sendCommand(m_strCommand); if (m_bOutput!="")
{
ParseData();
SaveToDataBase();
}
CDialog::OnTimer(nIDEvent);
}
BEGIN_EVENTSINK_MAP(CPanel, CDialog)
ON_EVENT(CPanel, IDC_MSCOMM1, 1, OnCommMscomm1, VTS_NONE)
END_EVENTSINK_MAP() void CPanel::OnCommMscomm1()
{ // TODO: Add your message handler code here
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
CString strtemp;
// CString strtemp_roselu;
static bool bEnd=false;
if (bEnd == true)
{
m_bOutput ="";
bEnd=false;
}
if(m_ctrlComm.get_CommEvent()==2) //事件值为2表示接收缓冲区内有字符
{ ////////以下你可以根据自己的通信协议加入处理代码
if(bEnd==false)
KillTimer(READER_EVENT_ID); variant_inp=m_ctrlComm.get_Input(); //读缓冲区
safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
len=safearray_inp.GetOneDimSize(); //得到有效数据长度
for(k=0;k <len;k++)
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
for(k=0;k <len;k++) //将数组转换为Cstring型变量
{
BYTE bt=*(char*)(rxdata+k); //字符型
strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
//m_strRXData+=strtemp; //加入接收编辑框对应字符串
m_bOutput+=strtemp; //加入接收编辑框对应字符串
if(strtemp=="}")
{
bEnd=true;
SetTimer(READER_EVENT_ID,READER_EVENT_TIME,NULL);
}
}
}
m_info.SetWindowText(m_bOutput); UpdateData(FALSE); //更新编辑框内容 }
在一个轮询周期中,主机需要分别发一个命令给每个设备,收到设备回答后,方可继续query下一个设备. 依次查询,5台都查完之后, 等到下一个轮询周期到来.再分别发命令给5台设备. 我的困难是不知道如何实现上面的算法:
我在OnTimer 里面发命令给设备,COM1口如果有数据,即触发MSCOMM控件的OnCommMscomm1事件.在这个事件处理函数中,如果数据包没结束,则先KillTimer(暂停定时器,即下一个命令不能发), 如数据包接收完整,则再次SetTimer. 可是我的代码只能应对一台设备, 5台设备怎么办呢? 在OnTimer函数中for循环好像不行吧.
我要的是: 主机与一台设备对话时, 不能再发下一个命令, 直到收到当前设备回应. 收到后立马再与下一个设备通话, 5台全部通讯完, 停止等待下一个query周期的到来. 请大侠们帮忙,我的算法该如何实现呢,好急呀! 想两天了,还是没想出来. void CPanel::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CString m_strCommand;
m_strCommand = "{FA;FC; <Length> ¦64:PAC=1ABC;KDY=12A;KT0=13FB6;KYR=13FB6;RAD=1C2 ¦ <Crc> }";
Sleep(200);
sendCommand(m_strCommand); if (m_bOutput!="")
{
ParseData();
SaveToDataBase();
}
CDialog::OnTimer(nIDEvent);
}
BEGIN_EVENTSINK_MAP(CPanel, CDialog)
ON_EVENT(CPanel, IDC_MSCOMM1, 1, OnCommMscomm1, VTS_NONE)
END_EVENTSINK_MAP() void CPanel::OnCommMscomm1()
{ // TODO: Add your message handler code here
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
CString strtemp;
// CString strtemp_roselu;
static bool bEnd=false;
if (bEnd == true)
{
m_bOutput ="";
bEnd=false;
}
if(m_ctrlComm.get_CommEvent()==2) //事件值为2表示接收缓冲区内有字符
{ ////////以下你可以根据自己的通信协议加入处理代码
if(bEnd==false)
KillTimer(READER_EVENT_ID); variant_inp=m_ctrlComm.get_Input(); //读缓冲区
safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
len=safearray_inp.GetOneDimSize(); //得到有效数据长度
for(k=0;k <len;k++)
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
for(k=0;k <len;k++) //将数组转换为Cstring型变量
{
BYTE bt=*(char*)(rxdata+k); //字符型
strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
//m_strRXData+=strtemp; //加入接收编辑框对应字符串
m_bOutput+=strtemp; //加入接收编辑框对应字符串
if(strtemp=="}")
{
bEnd=true;
SetTimer(READER_EVENT_ID,READER_EVENT_TIME,NULL);
}
}
}
m_info.SetWindowText(m_bOutput); UpdateData(FALSE); //更新编辑框内容 }
我觉得:
1. 你可以在 OnTimer 中仅仅向第一个设备发查询请求,发出后置一个标志为真,这个标志当最后一个设备返回结果后置假,OnTimer 中判断这个标志为假后才开始下一轮查询。2. 在 OnCommMscomm1 事件中决定是否向下一个设备发出查询请求,即:当第一个设备发回查询结果,会触发 OnCommMscomm1 事件,在此事件中做相应处理,然后向第二个设备发送查询请求,当第二个设备返回查询结果,也会触发 OnCommMscomm1 事件,就可以继续向第三个设备发送查询请求,依次类推。不知我的想法是否有问题,请考虑。
不过还有一个问题,我这种方法是在每个设备都能返回查询结果的前提下实现的,即一环套一环,是否向下一个设备发送查询请求,取决于上一个设备是否返回了查询结果,如果有一个设备没有返回结果,整个过程就被阻塞住了,永远不会往下执行,所以实际实现的话,最好再加一个 Timer,用来判断是否长时间未收到 Comm 事件,如果长时间没有收到过数据,就自动向下一个设备发送请求。
{
// TODO: Add your message handler code here and/or call default
//ndevice, m_strCommand要与ndevice关联,即ndevice决定向哪一台设备发送命令.
//如果OnCommMscomm1()因轮询没响应而不被触发, 达到timeout, OnTimer则记录出错信息,并接着query下一台设备.
CString m_strCommand, m_strDevice;
switch(nIDEvent)
{
case READER_EVENT_ID:
if (bContinueOnTimer == true)
{
//m_strCommand = "{FA;FC;<Length>|64:PAC=1ABC;KDY=12A;KT0=13FB6;KYR=13FB6;RAD=1C2|<Crc>}";
m_strDevice.Format( "%x ",ndevice);
m_strDevice.MakeUpper();
m_strCommand = "{FA;"+m_strDevice+";<Length>|64:PAC=1ABC;KDY=12A;KT0=13FB6;KYR=13FB6;RAD=1C2|<Crc>}";
sendCommand(m_strCommand);
Sleep(200);
}
break;
case RE_SEND:
t2=GetTickCount();//获得从系统运行起的到现在的一个毫秒数.
if ((t2-t1)> 3000000)
{
if (bContinueOnTimer == true)
{
Clog(3,"can not receive message from %d device\n",ndevice);
ndevice++;
m_strDevice.Format( "%x ",ndevice);
m_strDevice.MakeUpper();
m_strCommand = "{FA;"+m_strDevice+";<Length>|64:PAC=1ABC;KDY=12A;KT0=13FB6;KYR=13FB6;RAD=1C2|<Crc>}";
sendCommand(m_strCommand);
Sleep(200);
}
}
break;
default:
break; }//end swtich
CDialog::OnTimer(nIDEvent);
}