//线程
unit CommManager;
//(4,53)按"."直接退出:目的地不会单一,至少有两种可能
//采集的项目列表可能也需要模仿选择工序的显示方法,不过如果少则不需要
interface
uses
Windows, Classes,SysUtils,ComCtrls,PComm,
StrUtils,ScktComp,StdCtrls,PublishDM,DateUtils,PublicFiles;
type
TCommThread = class(TThread)
private
{ Private declarations }
fCom: Integer;
fBortRate: Integer;
fLoopTime: Integer;//系统轮循等待时间
fComState: Integer;//端口状态变量 0初始状态;1正常;2失败
fThreadExit : Boolean;//线程退出标识
fMemo:TMemo;
fRevString : string;
sleeptime: integer;
fHandle:Integer;
fHcom,fPost_Event:Thandle; //Comm接收参数
fLpolW,fLpolR
overlapped; //Comm接收参数
fCommData : PCOMMDATA; //端口设置参数
m_buf : array [0..512] of Char;
function PortSet():boolean; //端口参数设置
protected
Public
ExistExecute : Boolean;
fTermIndex : Integer; //当前终端索引号
fCurTerm : PTermInfo;
fEdit : TEdit;
procedure Over;
procedure Execute; overRIDE;
constructor Create(CreateSuspended: Boolean=FALSE);
destructor Destroy; override;
Function SetNextTerm()
TermInfo;
procedure SendData();
Procedure DoForRevData();
//Moxa卡
Function InitComm():Boolean;//初始化端口
procedure SetCommParam(BaudRate:integer=B9600; Parity:integer=P_NONE; ByteSize:integer=BIT_8; StopBits:integer=STOP_1;
ibaudrate:integer=12; iparity:integer=0; ibytesize:integer=3; istopbits:integer=0;
Hw:Boolean=False; Sw:Boolean=False; Dtr: boolean=True; Rts : boolean=True);//设置端口参数
function ReTrySendData(strInfo:string):Longint;
//发送数据到终端
function CommInitialize():Boolean;
Procedure CommDestroy;
Procedure ReadStr();
Function WriteStr(Com:integer;const Str:String):Boolean;
Published
property Com :integer read fCom write fCom default 1;
property Handle : Integer read fHandle write fHandle default 0;
property BortRate :integer read fBortRate write fBortRate default 9600;
property LoopTime:Integer read fLoopTime write fLoopTime default 10;//系统轮循等待时间
property ComState:Integer read fComState write fComState default 0;//端口状态变量
property ExitThread: Boolean read fThreadExit write fThreadExit;
property Hcom:Thandle read fHcom write fHcom ;
property Post_Event:Thandle read fPost_Event write fPost_Event ;
property Memo:TMemo read fMemo write fMemo;
property Edit:TEdit read fEdit write fEdit;
end;
implementation
procedure TCommThread.Over;
begin
ExitThread := True;
ExistExecute := False;
end;
function TCommThread.CommInitialize():Boolean;
Var Lpdcb:TDCB;
begin
Result := True;
hcom:=createFile(pChar('com'+inttostr(fCom)), //串口名,可为com1-com4
generic_read or Generic_write,//访问模式
0, //共享模式,必须为0
nil, //安全属性指针
open_existing, ///找开方式必须为open_existing
File_Flag_Overlapped,//文件属性,本文设为交迭标志
0); //临时文件句柄,必须为0
if hcom<>invalid_Handle_Value then
begin
//BaudRate:波特率,可直接设为110、300、600、1200、2400、4800、9600、19200等值。
//ByteBits:数据位长度,可高为4-8。
//Parity:奇偶校验方式,0-4分别为无、偶、奇、空
//StopBits:停止位长度,0,1,2分别为1、1.5、2位
SetupComm(hcom,4096,4096); //设置缓冲区长度
getCommState(hcom,lpdcb); //设置串口
lpdcb.baudrate:=fBortRate;
lpdcb.stopbits:=0;
lpdcb.bytesize:=8;
lpdcb.parity:=0;
setCommState(hcom,lpdcb);
SetCommMask(Hcom,ev_Rxchar); //设置串口事件屏蔽
end else begin
//无法打开串口
Result := False;
exit;
end;
SendData;
New(flpolW);
New(flpolR);
fLpolW^.Internal:=0;
fLpolW^.InternalHigh:=0;
fLpolW^.Offset:=0;
fLpolW^.OffsetHigh:=0;
fLpolW^.hEvent:=Createevent(nil,true,False,nil);
fLpolr^.Internal:=0;
fLpolr^.InternalHigh:=0;
fLpolr^.Offset:=0;
fLpolr^.OffsetHigh:=0;
fLpolr^.hEvent:=Createevent(nil,true,False,nil);
PurgeComm(Hcom,Purge_TxAbort or Purge_RxAbort or Purge_Txclear or Purge_Rxclear);
fPost_Event:=Createevent(nil,true,true,nil);
//不行就用下面被注释的语句
//EscapeCommFunction(hcom,CLRDTR);
//EscapeCommFunction(hcom,CLRRTS);
EscapeCommFunction(hcom,CLRDTR); //第四针 置低电平
EscapeCommFunction(hcom,CLRRTS); //第七针 置低电平
Sleep(50); //延时50毫秒
EscapeCommFunction(hcom,SETDTR); //第四针 置低高平
EscapeCommFunction(hcom,SETRTS); //第七针 置低高平
end;
Procedure TCommThread.CommDestroy; //释放内存
begin
if (fLpolW<>nil) then begin
CloseHandle(fLpolW^.hEvent);
dispose(flpolW);
fLpolW:=Nil;
end;
if (fLpolR<>nil) then begin
CloseHandle(fLpolR^.hEvent);
dispose(flpolR);
fLpolR:=Nil;
end;
SetEvent(fPost_Event);
CloseHandle(fPost_Event);
CloseHandle(fHcom);
end;
Procedure TCommThread.ReadStr(); //接收数据
var
clear:boolean;
coms:TComStat;
cbNum,Cbread,lpErrors
word;
S,S1,S2,S3,S_Temp:String;
i,iPos,i1,i2:integer;
P
ProcBarCodeInfo;
pTmp
TermInfo;
begin
clear:=clearCommerror(hcom,lperrors,@Coms);
if clear then
begin
cbnum:=Coms.cbInQue; //获取接收缓冲区待接收字节数
setlength(S_Temp,cbnum+1); //分配内存
ReadFile(hcom,PChar(S_Temp)^,cbnum,Cbread,fLpolR);//读串口
setlength(S_Temp,cbread); //分配
//读取数据
fRevString:=fRevString+S_Temp;
S:=TransferHexToString(fRevString);
try
if S='' then begin
if Length(fRevString)>100 then begin
fRevString:='';
end;
end else begin
fRevString:='';
iPos:=-1;
//获得地址、条码、标志
S1:=Copy(S,2,1);
if Copy(S,Length(S),1)='1' then S3:='OK'
else S3:='NG';
if MonitorType=3 then begin
//获得条码和OK/NG标志
S2:=Copy(S,3,Length(S)-3);
if S2<>'' then begin
for i:=0 to fTermList.Count-1 do begin
pTmp:=PTermInfo(fTermList
);
if pTmp.TermiAddr=S1 then begin
iPos:=i;
Break;
end;
//if pTmp.TermiAddr=S1 then begin
// if pTmp.StateValue<>'OK' then iPos:=i;
// Break;
//end;
end;
if iPos<>-1 then begin
if (pTmp.BarCode<>S2) or (pTmp.StateValue<>S3) then begin
pTmp.BarCode:=S2;
pTmp.StateValue:=S3;
new(P);
P.TermiAddr:=S1; //十六进制表示
P.BarCode:=S2;
P.StateValue:=S3;
SaveInfoList.Add(P);
end;
end;
end;
end else begin
//仅获得OK/NG标志
G_TempState:=S3;
if SaveInfoList<>nil then begin
if (SaveInfoList.Count>0) and (S3<>'') then begin
P:=PProcBarCodeInfo(SaveInfoList[0]);
if UpperCase(Trim(P.StateValue))<>S3 then begin
//默认OK就可以了,不需要采集NG
if UpperCase(Trim(P.StateValue))<>'OK' then begin
P.StateValue:=S3;
P.SaveState:=True;
end;
end;
end;
end;
end;
end;
finally
P:=nil; //检查一下这样做之后是否能够得到数据?????????????
pTmp:=nil;
end;
SetEvent(fPost_Event); //同步事件置位
end else begin
CommError:=False;
end;
end;
Procedure TCommThread.DoForRevData(); //处理收到终端的信息
var
cbNum,Cbread,lpErrorsword;
S,S1,S2,S3,S_Temp:String;
i,iPos,i1,i2:integer;
PProcBarCodeInfo;
pTmpTermInfo;
begin
fRevString:=fRevString+S_Temp;
S:=TransferHexToString(fRevString);
try
if S='' then begin
if Length(fRevString)>100 then begin
fRevString:='';
end;
end else begin
fmemo.Lines.Add(s);
fRevString:='';
iPos:=-1;
//获得地址、条码、标志
S1:=Copy(S,2,1);
if Copy(S,Length(S),1)='1' then S3:='OK'
else S3:='NG';
fMemo.Lines.Add(S);
if MonitorType=3 then begin
//获得条码和OK/NG标志
S2:=Copy(S,3,Length(S)-3);
if S2<>'' then begin
for i:=0 to fTermList.Count-1 do begin
pTmp:=PTermInfo(fTermList);
if pTmp.TermiAddr=S1 then begin
iPos:=i;
Break;
end;
//if pTmp.TermiAddr=S1 then begin
// if pTmp.StateValue<>'OK' then iPos:=i;
// Break;
//end;
end;
if iPos<>-1 then begin
if (pTmp.BarCode<>S2) or (pTmp.StateValue<>S3) then begin
pTmp.BarCode:=S2;
pTmp.StateValue:=S3;
new(P);
P.TermiAddr:=S1; //十六进制表示
P.BarCode:=S2;
P.StateValue:=S3;
SaveInfoList.Add(P);
end;
end;
end;
end else begin
//仅获得OK/NG标志
if SaveInfoList<>nil then begin
if (SaveInfoList.Count>0) and (S3<>'') then begin
P:=PProcBarCodeInfo(SaveInfoList[0]);
if UpperCase(Trim(P.StateValue))<>S3 then begin
//默认OK就可以了,不需要采集NG
if UpperCase(Trim(P.StateValue))<>'OK' then begin
P.StateValue:=S3;
P.SaveState:=True;
end;
end;
end;
end;
end;
end;
finally
P:=nil; //检查一下这样做之后是否能够得到数据?????????????
pTmp:=nil;
end;
end;
Function TCommThread.WriteStr(Com:integer;const Str:String):Boolean;
var
DwCharsWritten,DwResword;
S_DATA:String;
BRes:boolean;
Begin
BRes:=False;
S_Data:=Str;
if fHcom<>INVALID_HANDLE_VALUE then
begin
DwCharsWritten:=0;
BRes:=WriteFile(fHcom,PChar(S_Data)^,Length(S_Data),
DwCharsWritten,fLpolW); //返回True,数据立即发送完成
if not BRes then
begin
if GetLastError()=Error_IO_Pending then
begin //正在发送数据
DwRes:=WaitForSingleObject(fLpolW^.hEvent,Infinite);
if DwRes=Wait_Object_0 then // 如果不相等,出错
BRes:=GetOverLappedResult(fhcom,fLpolW^,DwCharsWritten,False) //返回False,出错
else BRes:=true; //数据发送完成
end;
end;
end;
Result:=Bres;
end;
procedure TCommThread.Execute;
var
//Comm接收参数
dwEvtmask,dwOvres,bbword;
RXFinish:Bool;
Len,i : integer;
begin
fRevString:='';
SleepTime := 0;
ExistExecute := true;
if fTermList.Count<=0 then exit;
fCurTerm := fTermList[0];
fTermIndex := 0;
While ExistExecute do begin
try
if CommListonType=0 then begin
DwEvtMask:=0;
RXFinish:=WaitCommEvent(fHcom,dwevtmask,fLpolR); //等待串口事件EV_RXCHAR
if not RXFinish then //如果返回True,已立即完成,否则继续判断
if GetLastError()=ERROR_IO_PENDING then //正在接收数据
begin
bb:=WaitForSingleObject(fLpolR^.hEvent,5);//等待5ms
Case bb of
Wait_Object_0: RXFinish:=GetOverLappedResult(fHcom,fLpolR^,dwOvRes,False);
//返回False,出错
Wait_TimeOut: RXFinish:=False;//定时溢出
else RXFinish:=False; //出错
end;
end else begin
RXFinish:=False;
CommError:=False;
end;
if RXFinish then
begin
if WaitForsingleobject(fPost_Event,infinite)=Wait_Object_0 then //等待同步事件置位
begin
resetEvent(fPost_Event); //同步事件复位
//在这里可以触发串口接收事件
ReadStr;
//Synchronize(ReadStr);
end;
end;
end else if CommListonType=1 then begin
len := sio_read(fCom,@m_buf,512);
while len>0 do begin //直到本次所有接收数据完毕
m_buf[len] := Char(0);
fRevString := fRevString + m_buf;
IF LENGTH(fRevString)>100 THEN BEGIN //防止接收数据超出正常的数据包大小,将该包不要
fRevString := '';
break;
END;
sleep(3);
len := sio_read(fCom,@m_buf,512);
end;
DoForRevData;
end;
SendData;
//Synchronize(SendData);
Sleep(LateTime);
except
end;
END;
// WriteLog(TermLog,'T');
end;
constructor TCommThread.Create(CreateSuspended: Boolean);
begin
fThreadExit := false;
inherited Create(CreateSuspended);
end;
destructor TCommThread.Destroy;
begin
inherited Destroy;
CommDestroy;
if fCommData <> nil then dispose(fCommData);
end;
//获取下一可用终端
Function TCommThread.SetNextTerm()TermInfo;
VAR I: INTEGER;
Tmp: pTermInfo;
Str1: String;
begin
try
result := nil;
FOR I :=0 TO fTermList.Count -1 do begin
Inc(fTermIndex);
IF fTermIndex >= ftermlist.Count THEN BEGIN
fTermIndex := 0;
END;
Tmp := ftermlist[fTermIndex];
if Tmp.bStop THEN begin //终端不使用
continue;
end else begin ////终端在使用
result := Tmp;
break;
end;
end;
finally
Tmp:=nil;
end;
end;
Function TCommThread.InitComm():Boolean;//初始化端口
var ret:Integer;
begin
result := false;
ret := sio_open(fCom);
if ret <> SIO_OK then Exit;
if PortSet = false then begin
sio_close(fcom);
Exit;
end;
result := true;
end;
function TCommThread.PortSet():boolean;
//端口参数设置
var
mode : integer;
ret : longint;
begin
{ com port default setting }
result := false;
if fCommData = nil then SetCommParam();
mode := fCommData.Parity or fCommData.ByteSize or fCommData.StopBits;
ret := sio_ioctl(fcom,fCommData.BaudRate,mode);
if ret<>SIO_OK then Exit;
result := True;
end;
procedure TCommThread.SetCommParam(BaudRate:integer=B9600;
Parity:integer=P_NONE;
ByteSize:integer=BIT_8;
StopBits:integer=STOP_1;
ibaudrate:integer=12;
iparity:integer=0;
ibytesize:integer=3;
istopbits:integer=0;
Hw:Boolean=False;
Sw:Boolean=False;
Dtr: boolean=True;
Rts : boolean=True);
begin
if fCommData= nil then New (fCommData);
fCommData.iBaudRate := BaudRate;
fCommData.iparity := iparity;
fCommData.ibytesize := ibytesize;
fCommData.istopbits := istopbits;
fCommData.BaudRate := BaudRate;
fCommData.Parity := Parity;
fCommData.ByteSize := ByteSize;
fCommData.StopBits := StopBits;
fCommData.Hw := Hw;
fCommData.Sw := Sw;
fCommData.Dtr := Dtr;
fCommData.Rts := Rts;
end;
function TCommThread.ReTrySendData(strInfo:string):Longint;//发送数据到终端
var len,ret:longint;
begin
len := length(strInfo);
ret := sio_write(fcom,PCHAR(strInfo),len);
result := Ret;
end;
//发送数据到终端
procedure TCommThread.SendData();
var ptTermInfo;
S,S1,S2:String;
D1,D2:Boolean;
wParam,lParam,Result:Integer;
begin
try
if MonitorType=2 then begin
if fFileList<>nil then begin
if fFileList.Count>0 then
ShellChangeNotifierChangeMonitor(ProcDirect,ProcDirectValue,PO,PPP,fMemo);
end;
end else if MonitorType=3 then begin
if LocalBarCodeNumber=2 then begin
D1:=PerformTextInfoSingle(1);
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);
D2:=PerformTextInfo(2);
if D2 then begin
if fEdit.Text='' then
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3)
else
PostMessage(fHandle,Cardinal(WM_DEFMSG),1,3);
end else
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);
end else begin
D2:=PerformTextInfo(2);
if D2 then begin
if fEdit.Text='' then
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3)
else
PostMessage(fHandle,Cardinal(WM_DEFMSG),1,3);
end else
PostMessage(fHandle,Cardinal(WM_DEFMSG),0,3);
end;
end;
fCurTerm:=SetNextTerm;
PT := fCurTerm;
If PT= nil then exit;
//因为线程是不停的发,第一次错,第二次可能就对了,所以校验码其实作用不大
if CommListonType=0 then begin
if MonitorType=2 then begin
S:=Chr(HexMod256ToAsc('A'+TermiAddr));
S:=CharToHex(TermiAddr)+CharToHex(S);
WriteStr(fCom,Chr($48)+Chr($34)+Chr($31)+S+Chr($47));
end else begin
S:=Chr(HexMod256ToAsc('B'+pt.TermiAddr));
S:=CharToHex(pt.TermiAddr)+CharToHex(S);
WriteStr(fCom,Chr($48)+Chr($34)+Chr($32)+S+Chr($47));
end;
end else if CommListonType=1 then begin
if MonitorType=2 then begin
S:=Chr(HexMod256ToAsc('A'+TermiAddr+'1'));
S:=CharToHex(TermiAddr)+CharToHex(S);
ReTrySendData(Chr($48)+Chr($34)+Chr($31)+S+Chr($47));
end else begin
S:=Chr(HexMod256ToAsc('B'+pt.TermiAddr));
S:=CharToHex(pt.TermiAddr)+CharToHex(S);
ReTrySendData(Chr($48)+Chr($34)+Chr($32)+S+Chr($47));
end;
end;
finally
Pt:=nil;
end;
end;
end.
//调用
ComThread:TCommThread;
ComThread := TCommThread.create(True);
ComThread.Com := ComPort;
ComThread.Edit:= Edit4;
ComThread.Handle:=Self.Handle;
ComThread.BortRate:=strtoint(BortRate);
if ComThread.CommInitialize=False then begin
showmessage('Comm Port Error!');
CommError:=False;
exit;
end else CommError:=True;
if fTermList.Count>0 then begin
fCurTerm := fTermList[0];
fTermIndex := 0;
//启动线程
ComThread.Resume;
end;
//退出
ComThread.ExistExecute:=False;
麻烦你自己去掉一些多余代码