unit RdComm;
interface uses Windows, SysUtils, Classes, inifiles, forms;
type TComInfo = record cCommNum: string; { com1..com4 } cBaudRate: integer; { 1200..19200 } // cDataLen: integer; { 6..8 } // cStopLen: integer; { 1..2 } cParity: integer; { non, odd, even } cHandle: THandle; { 串口打开后用来保存句柄 } end;
TMeterInfo = record { 仪表型号不同,数值也不一样 } DatPos: integer; { 过磅值在从仪表中读取的字符串中的位置 } DatLen: integer; { 过磅值的长度 } end;
function ReadMeter(MeterNum: integer; var MeterVal: Integer):Boolean; function ReadINI:Boolean; procedure OpenComm(num: integer); procedure CloseComm(num: integer);
var ComInf array[1..4] of TComInfo; MeterInf array[1..4] of TMeterInfo;
implementation
function ReadMeter(MeterNum: integer; var MeterVal: integer): Boolean; var k: integer; s: string;
// 发送命令给仪表 procedure SendCmd(cHandle: THandle; cmd: char); var bl: Boolean; rwBytes: Dword; begin sleep(5); bl:= WriteFile( cHandle, cmd, 1, rwBytes, nil); sleep(5); if (not bl) or (rwBytes<1) then bl:= WriteFile( cHandle, cmd, 1, rwBytes, nil); // 重试再次发送命令 if (not bl) or (rwBytes<1) then raise Exception.Create('ERR'); // 无法发送命令,提交异常来终止线程 end;
// 定位数据头 procedure SearchDataHead(cHandle: THandle); var i: integer; rwBytes: Dword; bDat: byte; bl: Boolean; begin i:=0; while i<50 do // 这个循环定位数据头 begin sleep(5); bl:= ReadFile( cHandle, bDat, 1, rwbytes, nil); if bl and (bDat = $02) then break; // 读到数据起始符 inc(i); end; // 没有能正确定位数据头则提交异常退出 if bDat <> $02 then raise Exception.Create('ERR2'); end;
// 读取数据串 function GetDataStr(cHandle: THandle):string ; var i: integer; rwBytes: Dword; bDat: byte; bl: Boolean; begin result:=''; i:=0; while i<50 do // 这个循环读取数据 begin sleep(5); bl:= ReadFile( ComInfo[MeterNum].cHandle, bDat, 1, rwBytes, nil); if bl then if bDat = $0D then break // 读到数据结束符 else result:= result + chr(bDat); inc(i); end; // 没有读取到结束符,则提交异常退出 if bDat <> $0D then raise Exception.Create('ERR2'); end;
begin MeterVal:= 0; s:=''; try OpenComm(MeterNum); try SendCmd(Cominfo[MeterNum].cHandle, 'P'); SearchDataHead(ComInfo[MeterNum].cHandle); s:= GetDataStr(ComInfo[MeterNum].cHandle); // 提取数值 s:= copy(s, MeterInfo[MeterNum].DatPos, MeterInfo[MeterNum].DatLen); try k:= StrToInt(s); // 此处有可能产生异常 except raise Exception.Create('ERR3'); end; finally CloseComm(MeterNum); end; MeterVal:= k; result:= true; except on E: Exception do begin s:= copy(E.Message,4,1); case StrToIntDef(s,4) of 1: MeterVal:=-1; // 串口打开失败 2: MeterVal:=-2; // 串口通讯失败 3: MeterVal:=-3; // 数值转换失败 4: MeterVal:=-4; // 其它错误失败 end; result:= False; end; end; end;
// 读串口参数INI文件 function ReadINI:Boolean; var lpIni: TIniFile; s: string; i: integer; begin Result:= False; s:= ExtractFileDir(Application.ExeName); if copy(s,Length(s),1)<>'\' then s:= s+'\'; s:= s+'LinkPara.Ini'; lpIni:= TIniFile.Create(s); try if not FileExists(s) then begin // 一号仪表 lpIni.WriteInteger('MeterInfo','1_No',1); // 使用的串口号 lpIni.WriteInteger('MeterInfo','1_BaudRate',1200); // 波特率 lpIni.WriteInteger('MeterInfo','1_Parity',2); // 校验 lpIni.WriteInteger('MeterInfo','1_Pos',4); // 数值定位 lpIni.WriteInteger('MeterInfo','1_Len',6); // 数值串长 // 二号仪表 lpIni.WriteInteger('MeterInfo','2_No',2); lpIni.WriteInteger('MeterInfo','2_BaudRate',1200); lpIni.WriteInteger('MeterInfo','2_Parity',2); lpIni.WriteInteger('MeterInfo','2_Pos',4); lpIni.WriteInteger('MeterInfo','2_Len',6); // 三号仪表 lpIni.WriteInteger('MeterInfo','3_No',3); lpIni.WriteInteger('MeterInfo','3_BaudRate',1200); lpIni.WriteInteger('MeterInfo','3_Parity',2); lpIni.WriteInteger('MeterInfo','3_Pos',4); lpIni.WriteInteger('MeterInfo','3_Len',6); // 四号仪表 lpIni.WriteInteger('MeterInfo','4_No',4); lpIni.WriteInteger('MeterInfo','4_BaudRate',1200); lpIni.WriteInteger('MeterInfo','4_Parity',2); lpIni.WriteInteger('MeterInfo','4_Pos',4); lpIni.WriteInteger('MeterInfo','4_Len',6); end; for i:= 1 to 4 do begin s:= IntToStr(i); ComInfo[i].cCommNum:= 'COM'+ IntToStr(lpIni.ReadInteger('MeterInfo',s+'_No',i)); ComInfo[i].cBaudRate:= lpIni.ReadInteger('MeterInfo',s+'_BaudRate',1200); ComInfo[i].cParity:= lpIni.ReadInteger('MeterInfo',s+'_Parity',2); MeterInfo[i].DatPos:= lpIni.ReadInteger('MeterInfo',s+'_Pos',4); MeterInfo[i].DatLen:= lpIni.ReadInteger('MeterInfo',s+'_Len',6); end; result:= True; finally lpIni.Free; end; end;
// 打开并设置串口 procedure OpenComm(num: integer); var ComDCB: TDCB; CT TCOMMTIMEOUTS; begin ComInfo[num].cHandle:= CreateFile( pchar(ComInfo[num].cCommNum), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0,0); if ComInfo[num].cHandle=INVALID_HANDLE_VALUE then raise Exception.Create('ERR1'); // 提交异常退出 try GetCommState(ComInfo[num].cHandle, ComDCB); ComDCB.BaudRate:= ComInfo[num].cBaudRate; ComDCB.ByteSize:= 7; ComDCB.Parity:= ComInfo[num].cParity; ComDCB.StopBits:= 2; if SetCommState(ComInfo[num].cHandle, ComDCB) = false then raise Exception.Create('ERR1'); // 提交异常退出
CTO.ReadIntervalTimeout:= 0; // 读区间超时 CTO.ReadTotalTimeoutConstant:= 0; // 读超时常数 CTO.ReadTotalTimeoutMultiplier:= 50; // 读总超时 CTO.WriteTotalTimeoutConstant:= 0; // 写超时常数 CTO.WriteTotalTimeoutMultiplier:= 50; // 写总超时 if SetCommTimeOUTs( ComInfo[num].cHandle, CTO ) = false then raise Exception.Create('ERR1'); // 提交异常退出 except CloseComm(num); raise; // 重新提交异常 end; end;
// 关闭串口 procedure CloseComm(num: integer); begin if ComInfo[num].cHandle <> INVALID_HANDLE_VALUE then CloseHandle( ComInfo[num].cHandle ); ComInfo[num].cHandle:= INVALID_HANDLE_VALUE; end;
end.