奇怪的问题:
同样的软件在两台台式机同时运行,从设备中重复上传数据,每包间隔15ms左右。
设备与电脑USB线连接,USB转串口通讯,波特率设置比较高,921600,
运行时,没有规律出现死机的现象,
两台电脑,有一台运行从昨天15:30左右到今天10:39左右,就自动重启了。
另一台到现在还运行正常。设备和电脑软件都一样。
以前出现死机的现象,现在,还出现了自动重启,真奇怪。
同样的软件在两台台式机同时运行,从设备中重复上传数据,每包间隔15ms左右。
设备与电脑USB线连接,USB转串口通讯,波特率设置比较高,921600,
运行时,没有规律出现死机的现象,
两台电脑,有一台运行从昨天15:30左右到今天10:39左右,就自动重启了。
另一台到现在还运行正常。设备和电脑软件都一样。
以前出现死机的现象,现在,还出现了自动重启,真奇怪。
tzj 2008-02-25
}
unit Unit_Comm;interface
uses
Windows, SysUtils, StrUtils, Classes, Dialogs, forms; const
comBufferSize = 20480; type
//监视串口线程 (设备联机工作状态)
TcomWatchThread = class(TThread)
private
searchComId : integer;
function tryOpenNextCom : boolean;
procedure onCom_Connecting(); //当串口正在连接
{ Private declarations }
protected
procedure comWatch;
procedure Execute; override;
public
procedure onCom_Connect(); //当串口连接
procedure onCom_Disconnect(); //当串口断开连接
constructor Create; virtual;
end;
TComm = class
private
ReceiveAndDealWithDataFlag, SendDataFlag : boolean; RecvBuff : array[0..comBufferSize] of char; dcb:_DCB; //DCB结构用于配置串口,程序中涉及各域含义如下:
//DWORD DCBlength :DCB结构大小
//DWORD BaudRate : 波特率
//DWORD fBinary : 1 二进制模式
//DWORD fParity : 1 进行奇偶校验
//BYTE ByteSize: 字符位数 4~8
//BYTE Parity: 奇偶校验位 0-4分别表示无、奇、偶、传号、空号校验
//BYTE StopBits: 停止位数 0-2分别表示 1、1.5、2个停止位
//WORD XonLim :XON 阈值
//WORD XoffLim XOFF 阈值
//char XonChar: XON 字符
//char XoffChar: XOFF 字符
//char EvtChar: 事件字符
comStat:_COMSTAT; //COMSTAT结构用于存放有关通信设备的当前信息
//程序中涉及各域含义如下:
//cbInQue :接收缓冲区中字符个数
//cbOutQue:发送缓冲区中字符个数
dwErrorFlag:LongWord;
hCommDev:Thandle;//通信串口句柄和通信监视线程句柄
comMask,comBuf,comState:BOOL;
read_os,write_os:_OVERLAPPED; //OVERLAPPED 结构,用于异步操作的Win32函数中
//程序中涉及各域含义如下:
//DWORD Interval 保留给操作系统使用
//DWORD IntervalHigh 保留给操作系统使用
//DOWD hEvent 当I/O操作完成时被设置为有信号状态
//的事件;当调用ReadFile和WriteFile函数之前,调
//用进程设置该事件
postRecvEvent, postSendEvent :Thandle;//发送缓冲区空和接收到字符事件句柄
public
toSendList : TStringList; comNo : integer;//串口号
comName : String;//串口名
BaudRate: DWORD; //波特率
ByteSize: Byte; //数据长度
Parity: Byte; //校验方式
StopBits: Byte; //停止位
EvtChar: CHAR; //每次接收完成字符 openIni : String;
timeOutFlag, //超时标志
openFlag : boolean;
SendCommand,RecvCommand:String;//发送和接收到的命令 function comSend : boolean;
procedure com_init;//初始化
procedure com_open; //初始化切换台串口,返回状态字符
procedure com_close;
procedure com_abort; //中断串口 procedure CommSendNotifyDealWith;//串口接收到字符事件响应过程
procedure CommRecvNotifyDealWith; //串口发送缓冲区空事件响应过程 procedure DealWithRecvData;//处理接收的数据 procedure SendCommandStr(cmdStr : String);
procedure SendNextCommand; //发送下一个命令 procedure comReceive;
function comReceive_:boolean; function strtoByte(Str : String):String;
function byteToStr(Str : String):String;
end;var
lineNumber : integer;
implementationuses Unit_Communication, Unit_Menu, Unit_publicVar, unit_realDataView,
Unit_Hint, Unit_DevFileManage, Unit_devUploadData, Unit_Socket;{
procedure writeLogToFile(Str : String);
begin
if not Form_devUploadData.bsSkinGroupBox1.Visible then exit; try
if FileExists(appPath + 'log.txt') then
Form_menu.Memo_log.Lines.LoadFromFile(appPath + 'log.txt')
else
Form_menu.Memo_log.Lines.Clear;
Form_menu.Memo_log.Lines.Add(formatdateTime('hh:MM:ss ',now)+Str);
if Form_menu.Memo_log.Lines.Count > 100 then
Form_menu.Memo_log.Lines.Delete(0);
Form_menu.Memo_log.Lines.SaveToFile(appPath + 'log.txt');
except
end;
end; }function TcomWatchThread.tryOpenNextCom : boolean;
begin
findDevFlag := false; inc(searchComId);
if searchComId > 256 then searchComId := 1; onCom_Connecting; Comm.comNo := searchComId;
Comm.com_open; result := Comm.openFlag;
end;procedure TcomWatchThread.onCom_Connecting;
const pointCount = 50;
var
flagPos : integer;
begin
if appQuit then exit;
flagPos := trunc(searchComId * (pointCount / 256));
Form_menu.Status_devIni.Caption := '正在寻找' +
stringOfchar('.',flagPos)+
'|' +
stringOfchar('.',pointCount - flagPos);
Form_realDataView.Status_devIni.Caption := Form_menu.Status_devIni.Caption;
end;procedure TcomWatchThread.onCom_Connect;
begin
outputIni('------111-----'); if appQuit then exit;
outputIni('---111---222-----');
Form_Hint.Visible := false;
SetForegroundWindow( Application.Handle );
outputIni('------222-----'); receiveDataStr := '';
lastComStatus := ''; Communication_ECG.resetBtnStatus(true);
Form_menu.Status_devIni.Caption := '端口:'+Comm.comName+' SN:'+formatfloat('0-0000-0000-0000',devCodeStr)+''; Form_realDataView.Status_devIni.Caption := Form_menu.Status_devIni.Caption; if Form_DevFileManage.Visible then
begin
// sleep(100);
Form_DevFileManage.btn_refreshClick(nil); //刷新文件列表
end; if Form_devUploadData.Visible then
begin
Form_devUploadData.Timer_timeOut.Enabled := true;
end; outputIni('------333-----'); Communication_ECG.send_getDevVer;
if Form_realDataView.Visible then Form_realDataView.init;end;
procedure TcomWatchThread.onCom_Disconnect;
begin
if appQuit then exit; if findDevFlag then Form_Hint.Show; Communication_ECG.resetBtnStatus(false); comStatus := ''; with Form_menu do
begin
Status_devIni.Caption := '正在寻找';
lbl_DevVer.Caption := '硬件版本:_._._._';
end; with Form_realDataView do
if visible then
begin
stopReceive;
Status_devIni.Caption := Form_menu.Status_devIni.Caption;
end;end;
{ TcomWatchThread }constructor TcomWatchThread.Create;
begin
inherited Create(true);
FreeOnTerminate := True;
searchComId := 0;
end;procedure TcomWatchThread.comWatch;
var
dwTransfer,dwEvtMask,dwError:DWORD;
os:_OVERLAPPED;
bl:boolean;
begin
os.hEvent:=CreateEvent(nil, TRUE, FALSE, NIL); with comm do
begin
while openFlag and (not timeOutFlag) do//串口处于打开状态
begin
if appQuit then break; ClearCommError(hCommDev,dwErrorFlag,@ComStat); dwEvtMask:=0;
bl:=WaitCommEvent(hCommDev,dwEvtMask,@os); //查询所监视的通信事件是否
//已经发生
if bl=False then
begin
dwError:=GetLastError();
if dwError <> 0 then
if dwError=ERROR_IO_PENDING then
begin
GetOverlappedResult(hCOmmDev,os,dwTransfer,True)//若未监测到通信事件
//则在此等待事件发生
end
else
break;
end; //有事件,进行如下处理
if (dwEvtMask and EV_RXFLAG)=EV_RXFLAG then //判断是否为接收到 字符事件
begin
//self.Synchronize(
CommRecvNotifyDealWith;
continue; //处理完接收字符,继续监测通信事件
end
else
if (dwEvtMask and EV_TXEMPTY)=EV_TXEMPTY then //判断是否为发送缓冲区空事件
begin
self.Synchronize(commSendNotifyDealWith); //调用发送缓冲区空处理函数
// commSendNotifyDealWith; //调用发送缓冲区空处理函数
continue;//处理完,继续监测通信事件
end
else
if (dwEvtMask and $FFFFFFFF) = 0 then //通讯中断
begin
break;
end;
end;
end;
CloseHandle(os.hEvent);
end;
procedure TcomWatchThread.Execute;
begin
//*** 注意:不用Synchronize调用执行带窗口操作的过程或函数会造成程序死掉的严重现象 ****//
repeat
if tryOpenNextCom then //串口打开成功
begin
outputIni('成功');
receiveDataStr := '';
Communication_ECG.send_getDevCode; //读取设备编码
comWatch;//串口监视
//Form_menu.Memo1.Lines.Add(comm.comName+ ' com_close ');
self.Synchronize( comm.com_close );//关闭串口
self.Synchronize( onCom_Disconnect ); //串口断开提示
end
else //串口打开失败
begin
application.ProcessMessages;
sleep(1);
end; if appQuit then break;
until Terminated;
end;
///串口初始化函数
//该函数主要完成串口初始化设置和通信线程的启动
//入口参数:串口号
//返回值;初始化是否成功的状态字符
procedure TComm.com_open;
var
TimeOuts : COMMTIMEOUTS;
begin
outputIni('打开串口:'+formatfloat('000',self.comNo)); openFlag := false;
timeOutFlag := false; comName := '\\.\COM' + inttostr(comNo); ///打开串口
hCommDev:=CreateFile(pchar(comName), //串口号
GENERIC_READ or GENERIC_WRITE,//对串口以读写方式打开
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED,//允许重叠操作
0
);
if hCommDev=INVALID_HANDLE_VALUE then
begin
openIni := '打开串口"'+comName+'"失败.';
exit;
end; comBuf:=SetupComm(hCommDev,comBufferSize,comBufferSize);//设置接收和发送缓冲区大小皆为4096字节 if comBuf=False then
begin
openIni := '设置接收和发送缓冲区大小失败.';
exit;
end; //设置通讯超时
getCommTimeouts(hCommDev, TimeOuts);
TimeOuts.ReadIntervalTimeout := MAXDWORD; // 读间隔超时
TimeOuts.ReadTotalTimeoutMultiplier := 500; // 读时间系数
TimeOuts.ReadTotalTimeoutConstant := 2000; // 读时间常量
TimeOuts.WriteTotalTimeoutMultiplier := 500; // 写时间系数
TimeOuts.WriteTotalTimeoutConstant := 2000; // 写时间常量 //COMMTIMEOUTS结构的成员都以毫秒为单位。总超时的计算公式是:
//总超时=时间系数×要求读/写的字符数 + 时间常量
// 例如,如果要读入10个字符,那么读操作的总超时的计算公式为:
//读总超时=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant SetCommTimeouts(hCommDev, TimeOuts); // 设置超时 //清空缓冲区
PurgeComm(hCommDev,PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR ) ; //以下对串口进行配置
comState:=GetCommState(hCOmmDev,dcb); //得到缺省设置
if comState=False then
begin
openIni := '得到缺省设置失败.';
exit;
end; dcb.DCBlength:=sizeof(_DCB); dcb.BaudRate := BaudRate; //波特率 9600
dcb.ByteSize := ByteSize; //数据长度7位
dcb.Parity := Parity; //校验方式
dcb.StopBits := StopBits; //停止位 {dcb.Flags := 0; // Enable fBinary
dcb.Flags := dcb.Flags or 2; // Enable parity check
dcb.XonChar:= chr($00) ;
dcb.XoffChar:= chr($00) ;
dcb.XonLim:= 100 ;
dcb.XoffLim:= 100 ; }
dcb.EvtChar := EvtChar; comState:=SetCommState(hCommDev,dcb); //设置串口
if comState=False then
begin
openIni := '设置串口失败.';
exit;
end; comMask:=SetCommMask(hCommDev,EV_RXFLAG or EV_TXEMPTY);//设置监视的事件为接
//收到字符或发送缓冲区空
if comMask = False then
begin
openIni := '设置监视的事件失败';
exit;
end;
openIni := '打开并设置串口"'+comName+'"成功.'; //设置通信接收到字符事件句柄
postRecvEvent:=CreateEvent(NIL,
TRUE,//手工重置事件
TRUE, //初始化为有信号状态
NIL
); //设置读异步I/O操作事件句柄
read_os.hEvent:=CreateEvent(NIL,
// TRUE,//手工重置事件
False,//自动重置事件
FALSE, //初始化为无信号状态
NIL
);
//设置发送缓冲区空事件句柄
postSendEvent := CreateEvent(NIL,
TRUE,//手工重置事件
TRUE, //初始化为有信号状态
NIL);
//设置写异步I/O操作事件句柄
write_os.hEvent:=CreateEvent(NIL,
// TRUE,//手工重置事件
False,//自动重置事件
FALSE,//初始化为无信号状态
NIL);
//初始化变量
openFlag := true;//串口已成功打开 toSendList.Clear;
end;procedure TComm.comReceive;
var
receiveResult : Boolean;
begin
//writeLogToFile('r-');
//***** 注意:此处等条件必须为=WAIT_OBJECT_0,否则操作系统会死掉 *****//
if WaitForSingleObject(postRecvEvent,50) = WAIT_OBJECT_0 then//等待接收事件句柄为有信号状态
begin
if ResetEvent(postRecvEvent) then //置接收事件句柄为无信号状态,以免接收缓冲区被覆盖
begin
try
receiveResult := comReceive_;
if not receiveResult then //如果接收失败
begin
outputIni( 'SendCommandStr接收失败---closeComm--');
// com_abort; //中断串口 //时有发生,不要打开
// exit;
end;
finally
SetEvent(postRecvEvent); //置接收事件句柄为有信号状态,以便接收新字符
end;
end;
end
else
com_abort;
//writeLogToFile('r*');
end;procedure TComm.DealWithRecvData;//处理接收的数据
begin
//如果接收的数据包不为空,则调用数据包处理过程
if RecvCommand <> '' then
begin
Communication_ECG.onReceiveData(RecvCommand);
RecvCommand := '';
end;
end;procedure TComm.CommRecvNotifyDealWith;
begin
if ReceiveAndDealWithDataFlag then exit; try
ReceiveAndDealWithDataFlag := true;
//接收数据
comReceive;
//处理接收的数据
comWatchThread.Synchronize(DealWithRecvData);
finally
ReceiveAndDealWithDataFlag := false;
end; SendNextCommand; //发送下一个数据包
end;
//向串口发送数据
//-----------------------------------------
function TComm.comSend : boolean;
var
dwWriteByte,TxCount:DWORD;
dwError, dwR:DWORD;
WriteBuffer:PChar;
begin
try
result := false;
ClearCommError(hCommDev,dwErrorFlag,@ComStat);
{
if not ClearCommError(hCommDev,dwErrorFlag,@ComStat) then
begin
PurgeComm(hCommDev, PURGE_TXABORT or PURGE_TXCLEAR );
Exit;
end; } TxCount:=Length(SendCommand);
WriteBuffer := pchar(SendCommand); // dwWriteByte:=0; result := WriteFile(hCommDev,Byte(WriteBuffer^),TxCount,dwWriteByte,@write_os); dwError:=GetLastError();
if (not result) and (dwError = ERROR_IO_PENDING) then //正在发送数据
begin
result := (WaitForSingleObject(write_os.hEvent,100) = WAIT_OBJECT_0);
// result := GetOverLappedResult(hCommDev, write_os, dwWriteByte, False) //**** 注意最后一个参数必须为False,否则程序容易死掉 ****//
end
else
begin
com_abort; //中断串口
exit;
end; if result then
begin
//write_os.Offset := write_os.Offset + TxCount - dwWriteByte;
if RecCom then Form_Socket.Rec_sendStr(self.byteToStr(SendCommand));
end;
except
end;
end;
procedure TComm.SendNextCommand;
function SendNextCommand_:boolean;
begin
result := false;
if not openFlag then exit; //writeLogToFile('w-');
if WaitForSingleObject(postSendEvent,50) = WAIT_OBJECT_0 then//等待发送事件句柄为有信号状态
begin
if ResetEvent(postSendEvent) then //置发送事件句柄为无信号状态,,以免发送缓冲区被覆盖
begin
try
result := comSend;
if not result then //如果发送失败
begin
com_abort; //中断串口
exit;
end;
finally
setEvent(postSendEvent);//置发送事件有信号状态,以便进行下一次发送
end;
end;
end
else
com_abort; //writeLogToFile('w*');
end;
begin
if SendDataFlag then exit; try
SendDataFlag := true;
if Assigned(toSendList) then
begin
if toSendList.Count > 0 then
begin
SendCommand := toSendList.Strings[0];
toSendList.Delete(0);
SendNextCommand_;
end;
end;
finally
SendDataFlag := false;
end;
end;procedure TComm.SendCommandStr(cmdStr : String);
begin
if not openFlag then exit; if Assigned(toSendList) then
begin
if toSendList.Count < 10 then
toSendList.Add(cmdStr); comWatchThread.Synchronize( SendNextCommand );
SendNextCommand;
end;
end;
//发送缓冲区空处理过程
procedure TComm.CommSendNotifyDealWith;
begin
//if not ReceiveAndDealWithDataFlag then //不在接收和处理数据状态
SendNextCommand; //发送下一个数据包
end;
function TComm.comReceive_:boolean;
var
RxCount,dwReadByte, toReadByte :DWORD;
dwError, DwRes:DWORD;
i : integer;
begin
result := false;
RecvCommand := ''; try
ClearCommError(hCommDev,dwErrorFlag,@ComStat);
{
if not ClearCommError(hCommDev,dwErrorFlag,@ComStat) then
begin
PurgeComm(hCommDev, Purge_Rxabort or Purge_Rxclear);
Exit;
end;
} RxCount:=ComStat.cbInQue; //获取接收缓冲区的字符个数
if ((comBufferSize-1) < RxCount) then
toReadByte := comBufferSize-1
else
toReadByte := RxCount; if toReadByte>0 then
begin
fillchar(RecvBuff, toReadByte, #0); result := ReadFile(hCommDev,RecvBuff,toReadByte,dwReadByte,@read_os);
//需要注意的是如果该函数因为超时而返回,那么返回值是TRUE。参数lpOverlapped在
//重叠操作时应该指向一个OVERLAPPED结构,如果该参数为NULL,
//那么函数将进行同步操作,而不管句柄是否是由FILE_FLAG_OVERLAPPED标志建立的。 dwError:=GetLastError();
if not (result and (dwError = 0)) then
begin
result := false;
com_abort; //中断串口
exit;
end; if dwReadByte > 0 then //临时缓冲区中接收到字符个数大于1
begin
//read_os.Offset := read_os.Offset + dwReadByte;
RecvBuff[dwReadByte] := #0;
for i:=0 to dwReadByte-1 do
RecvCommand := RecvCommand + RecvBuff[i];
if RecCom then Form_Socket.Rec_receiveStr(self.byteToStr(RecvCommand));
end; end;
except
end;
end;
//中断串口
procedure TComm.com_abort;
begin
if openFlag then
begin
SetEvent(postRecvEvent);
SetEvent(postSendEvent);
PurgeComm(hCommDev,PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR ) ;
SetCommMask(hCommDev,0);
end;
end;
var
i, len : integer;
begin
len := length(Str);
result := '';
for i:=1 to len do
result := result + ' ' + rightStr('00'+inttohex(ord(Str[i]),0),2);
end;function TComm.strtoByte(Str: String): String;
var
i, len : integer;
begin
len := length(Str);
result := '';
for i:=0 to len div 2 - 1 do
result := result + char(strtoIntDef('$'+copy(Str,i*2 + 1,2),0));
end;
procedure TComm.com_close;
begin
//outputIni('关闭串口:'+formatfloat('000',self.comNo));
Form_menu.receiveTimer.Enabled := false; //停止超时定时器 Form_devUploadData.Timer_timeOut.Enabled := false;
if not openFlag then exit; //如果已经关闭则退出 openFlag := false;//串口已关闭 //关闭用到的句柄
CloseHandle(PostRecvEvent);
CloseHandle(read_os.hEvent);
CloseHandle(PostSendEvent);
CloseHandle(write_os.hEvent);
CloseHandle(hCommDev);
end;procedure TComm.com_init;
begin
with Comm do
begin
BaudRate := 921600; //波特率
ByteSize := 8; //数据长度
Parity := 0; //校验方式
StopBits := 0; //停止位
EvtChar := Char($FE); //每次接收完成字符
end; toSendList := TStringList.Create;
comWatchThread := TcomWatchThread.Create;
ReceiveAndDealWithDataFlag := false;
SendDataFlag := false;
end;end.
---------------------------
两个建议
1)用一定动作代替保存log试试。
2)只做收发之间的一个动作。以前俺的一个程序,在不同的线程里同时send了一种message,主程序就死。
hCommDev:=CreateFile(pchar(comName), //串口号
GENERIC_READ or GENERIC_WRITE,//对串口以读写方式打开
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED,//允许重叠操作
0
);
if hCommDev=INVALID_HANDLE_VALUE then
begin
openIni := '打开串口"'+comName+'"失败.';
exit;
end;
comBuf:=SetupComm(hCommDev,comBufferSize,comBufferSize);//设置接收和发送缓冲区大小皆为4096字节 if comBuf=False then
begin
openIni := '设置接收和发送缓冲区大小失败.';
//*************************************************
// 这儿就应当回收当前打开的文件;
//*************************************************
exit;
end;
串口操作,在操作系统里其实就是对一个文件进行操作;也就是说CreateFile是创建或是打开一个文件,如果成功:
返回值将是文件句柄;在设置串口时,如果操作失败,那么你就应当回收这个文件句柄资源,但在你的源码中没有看到回收![color=#FF0000][/color]
下面是SPCOMM的源码,可以给你些提示吧。{SPCOMM SOURCE CODE}
if (hCommFile <> 0) then
raise ECommsError.Create( 'This serial port already opened' ); hNewCommFile := CreateFile( PChar(FCommName),
GENERIC_READ or GENERIC_WRITE,
0, {not shared}
nil, {no security ??}
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED,
0 {template} ); if hNewCommFile = INVALID_HANDLE_VALUE then
raise ECommsError.Create( 'Error opening serial port' ); // Is this a valid comm handle?
if GetFileType( hNewCommFile ) <> FILE_TYPE_CHAR then
begin
CloseHandle( hNewCommFile );
raise ECommsError.Create( 'File handle is not a comm handle ' )
end; if not SetupComm( hNewCommFile, 4096, 4096 ) then
begin
CloseHandle( hCommFile );
raise ECommsError.Create( 'Cannot setup comm buffer' )
end;
............
CloseHandle(PostRecvEvent);
CloseHandle(read_os.hEvent);
CloseHandle(PostSendEvent);
CloseHandle(write_os.hEvent);
CloseHandle(hCommDev); 楼主,以上代码有啊.