Initial commit - Delphi MES client project
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
unit uPLCDevice;
|
||||
|
||||
interface
|
||||
|
||||
uses System.Win.ScktComp,uSafeLog,uErrorInfo,System.SysUtils,FMX.Types,
|
||||
System.StrUtils,System.Classes,WinSock,WinSock2,Winapi.Windows;
|
||||
|
||||
type
|
||||
TPlcWorkEvent = procedure(Sender: TObject;sValue:string) of object;
|
||||
TPlcOpenEvent = procedure(Sender: TObject;sValue:string) of object;
|
||||
|
||||
//500000FFFF0300 0C00 010001040000 7A0000 A8 0100
|
||||
//500000FFFF0300 0e00 010001140000 7A0000 A8 0100 c9 00
|
||||
TMelsec=class //三棱plc
|
||||
private
|
||||
bIsDestory:boolean;
|
||||
FTimer: TTimer;
|
||||
FOnWork:TPlcWorkEvent;
|
||||
FOnShowState:TPlcWorkEvent;
|
||||
FOnOpen: TPlcOpenEvent;
|
||||
FSocket:TClientSocket;
|
||||
FNum:Integer; //编号
|
||||
procedure TimerTimer(Sender: TObject); //判断连接状态,断开的话,自动重连
|
||||
procedure SocketRead(Sender: TObject; Socket: TCustomWinSocket); //读缓存数据
|
||||
procedure SocketError(Sender: TObject; Socket: TCustomWinSocket; //出错代码处理
|
||||
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
||||
procedure SocketConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
procedure SocketDisConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
procedure SocketConnecting(Sender: TObject; Socket: TCustomWinSocket);
|
||||
public
|
||||
property Num:Integer read FNum write FNum;
|
||||
constructor Create(sIp:string;iPort:integer);
|
||||
destructor Destroy; override;
|
||||
procedure Close();
|
||||
procedure Connect();
|
||||
procedure DisConnect();
|
||||
procedure Open(Sender: TObject;sValue: string);
|
||||
property OnWork: TPlcWorkEvent read FOnWork write FOnWork;//业务处理事件
|
||||
property OnShowState:TPlcWorkEvent read FOnShowState write FOnShowState;//显示连接断开提示
|
||||
property OnOpen: TPlcOpenEvent read FOnOpen write FOnOpen;//提交协议plc处理事件
|
||||
end;
|
||||
|
||||
TOmron=class //欧姆龙plc
|
||||
private
|
||||
bIsDestory:boolean;
|
||||
FTimer: TTimer;
|
||||
FOnWork:TPlcWorkEvent;
|
||||
FOnShowState:TPlcWorkEvent;
|
||||
FOnOpen: TPlcOpenEvent;
|
||||
FSocket:TClientSocket;
|
||||
FNum:Integer; //编号
|
||||
procedure TimerTimer(Sender: TObject); //判断连接状态,断开的话,自动重连
|
||||
procedure SocketRead(Sender: TObject; Socket: TCustomWinSocket); //读缓存数据
|
||||
procedure SocketError(Sender: TObject; Socket: TCustomWinSocket; //出错代码处理
|
||||
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
||||
procedure SocketConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
procedure SocketDisConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
procedure SocketConnecting(Sender: TObject; Socket: TCustomWinSocket);
|
||||
public
|
||||
property Num:Integer read FNum write FNum;
|
||||
constructor Create(sIp:string;iPort:integer);
|
||||
destructor Destroy; override;
|
||||
procedure Open();
|
||||
procedure Close();
|
||||
procedure Connect();
|
||||
procedure DisConnect();
|
||||
property OnWork: TPlcWorkEvent read FOnWork write FOnWork;//业务处理事件
|
||||
property OnShowState:TPlcWorkEvent read FOnShowState write FOnShowState;//显示连接断开提示
|
||||
property OnOpen: TPlcOpenEvent read FOnOpen write FOnOpen;//提交协议plc处理事件
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TMelsec }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
constructor TMelsec.Create(sIp:string;iPort:integer); //参数固定
|
||||
begin
|
||||
inherited Create;
|
||||
bIsDestory:=false;
|
||||
FNum:=1;
|
||||
WorkLog.Debug('创建Melsec读头');
|
||||
FSocket:=TClientSocket.Create(nil);
|
||||
FSocket.OnRead:=SocketRead;
|
||||
FSocket.OnError:=SocketError;
|
||||
FSocket.OnConnect:=SocketConnect;
|
||||
FSocket.OnDisconnect:=SocketDisConnect;
|
||||
FSocket.OnConnecting:=SocketConnecting;
|
||||
FSocket.Address:=sIp;
|
||||
FSocket.Port:=iPort;
|
||||
FTimer:=TTimer.Create(nil);
|
||||
FTimer.OnTimer:=TimerTimer;
|
||||
FTimer.Enabled:=false;
|
||||
end;
|
||||
|
||||
procedure TMelsec.Connect();
|
||||
var
|
||||
tmp:string;
|
||||
begin
|
||||
tmp:=Format('连接Melsec读头%d:%s,%d',[FNum,FSocket.Address,FSocket.Port]);
|
||||
WorkLog.Debug(tmp);
|
||||
if Assigned(FOnShowState) then FOnShowState(self,tmp);
|
||||
FSocket.Active:=true;
|
||||
end;
|
||||
|
||||
procedure TMelsec.DisConnect();
|
||||
begin
|
||||
FSocket.Close;
|
||||
FTimer.Enabled:=false;
|
||||
end;
|
||||
|
||||
procedure TMelsec.SocketConnecting(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Melsec读头%d连接中...',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self,Format('Melsec读头%d连接中...',[FNum]));
|
||||
end;
|
||||
|
||||
destructor TMelsec.Destroy;
|
||||
begin
|
||||
bIsDestory:=true;
|
||||
FSocket.Close;
|
||||
FTimer.Enabled:=false;
|
||||
WorkLog.Debug('销毁Melsec读头');
|
||||
FSocket.Free;
|
||||
FTimer.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMelsec.SocketDisConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Melsec读头%d连接断开',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self,Format('Melsec读头%d连接断开',[FNum]));
|
||||
end;
|
||||
|
||||
//procedure TMelsec.SetKeepAlive(Socket: TCustomWinSocket);
|
||||
//var
|
||||
// opt:DWORD;
|
||||
// klive, outKlive: TTCP_KEEPALIVE;
|
||||
// i,j:integer;
|
||||
// // OptVal: DWORD;
|
||||
//begin
|
||||
// opt := 1;
|
||||
// if setsockopt(Socket.SocketHandle,SOL_SOCKET, SO_KEEPALIVE, PAnsiChar(@opt), SizeOf(opt)) = SOCKET_ERROR then
|
||||
// begin
|
||||
// //showInfo(Format('WinSock Error %d', [WSAGetLastError()]));
|
||||
// end;
|
||||
// klive.onoff := 1;
|
||||
// klive.keepalivetime := 5000;
|
||||
// klive.keepaliveinterval := 1;
|
||||
//
|
||||
// if WSAIoctl(Socket.SocketHandle, SIO_KEEPALIVE_VALS, PAnsiChar(@klive),
|
||||
// SizeOf(TTCP_KEEPALIVE), PAnsiChar(@outKlive),
|
||||
// SizeOf(TTCP_KEEPALIVE), opt,0,nil) = SOCKET_ERROR then
|
||||
// begin
|
||||
// //showInfo(Format('WinSock Error %d', [WSAGetLastError()]));
|
||||
// end;
|
||||
//
|
||||
//end;
|
||||
|
||||
procedure TMelsec.TimerTimer(Sender: TObject);
|
||||
begin
|
||||
if FTimer.Enabled then
|
||||
begin
|
||||
FTimer.Enabled:=false;
|
||||
FSocket.Close;
|
||||
FSocket.Open;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMelsec.SocketRead(Sender: TObject; Socket: TCustomWinSocket);
|
||||
var
|
||||
buf:array[0..1024] of byte;
|
||||
sHexString:string;
|
||||
i:Integer;
|
||||
sData:string;
|
||||
function ByteToString(const Value:PByte;iLen:Integer): String;
|
||||
var
|
||||
I: integer;
|
||||
S : String;
|
||||
begin
|
||||
S := '';
|
||||
for I := 0 to iLen-1 do
|
||||
begin
|
||||
if Value[i]=$0D then break;
|
||||
S := S+Chr(Value[I]);
|
||||
end;
|
||||
Result := S;
|
||||
end;
|
||||
begin
|
||||
if bIsDestory then Exit;
|
||||
|
||||
sHexString:='';
|
||||
for i:= 0 to Socket.ReceiveBuf(buf,1024) do
|
||||
begin
|
||||
sHexString:=sHexString+buf[i].ToHexString;
|
||||
end;
|
||||
WorkLog.Debug('Melsec读头%d读到数据:%s',[FNum,sHexString]);
|
||||
|
||||
sData:=ByteToString(@buf[0],Length(sHexString)div 2);
|
||||
if Assigned(FOnWork) then FOnWork(self,sData);
|
||||
end;
|
||||
|
||||
procedure TMelsec.SocketError(Sender: TObject; Socket: TCustomWinSocket;
|
||||
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
||||
var
|
||||
tmpError:string;
|
||||
begin
|
||||
if (ErrorCode=10060) OR (ErrorCode=10065) then
|
||||
begin
|
||||
tmpError:=Format('Melsec读头%d连接出错(%d):%s',[FNum,ErrorCode,SysErrorMessage(ErrorCode)]);
|
||||
if Assigned(FOnShowState) then FOnShowState(self,Format('Melsec读头%d连接失败,重新连接',[FNum]));
|
||||
end
|
||||
else
|
||||
begin
|
||||
tmpError:=Format('Melsec读头%d连接出错(%d):%s',[FNum,ErrorCode,SysErrorMessage(ErrorCode)]);
|
||||
if Assigned(FOnShowState) then FOnShowState(self,'Melsec读头连接出错:'+SysErrorMessage(ErrorCode));
|
||||
end;
|
||||
workLog.Error(tmpError);
|
||||
ErrorCode:=0;
|
||||
if bIsDestory then Exit;
|
||||
FTimer.Enabled:=true;
|
||||
end;
|
||||
|
||||
procedure TMelsec.SocketConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Melsec读头%d连接成功',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self, Format('Melsec读头%d连接成功',[FNum]));
|
||||
end;
|
||||
|
||||
procedure TMelsec.Close; //4C4F46460D
|
||||
VAR
|
||||
bBtye:array[0..4] of byte;
|
||||
begin
|
||||
bBtye[0]:=$4C;bBtye[1]:=$4F;bBtye[2]:=$46;
|
||||
bBtye[3]:=$46;bBtye[4]:=$0D;
|
||||
fSocket.Socket.SendBuf(bBtye[0],5);
|
||||
WorkLog.Debug(Format('Melsec读头%d关闭',[FNum]));
|
||||
end;
|
||||
|
||||
{
|
||||
* 50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 64 00 00 A8 14 0050 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 64 00 00 A8 14 00
|
||||
*
|
||||
* 电脑读命令: 50 00(命令) :表示发起指令,固定50 00;
|
||||
00(网路编号) :上位访问下位,固定00;
|
||||
FF(PLC编号) : 上位访问下位,固定FF;
|
||||
FF 03(请求目标模块IO编号) : 值要从小到大看,也就是反过来看,三菱所有的协值都是这样,所以这里是03FF,十进制是1023; 也是固定的;
|
||||
00(请求目标模块站编号) : 上位访问下位,固定00;
|
||||
0C 00 (应答数据物理长度): 也要反过来,值是000C,也就是12;表示后面的报文内容的长度是12(手工数一下,后面报文长度真的是12)
|
||||
10 00 (cpu监视定时器) : 表示等待PLC响应的timeout时间;这里 值是0010,十进制是16 ;相当与最大等待时间250ms*16=4秒;实际上PLC一般2,3个毫秒内就响应了;
|
||||
01 04 (命令) : 值是0401(所有值都要反过来看,再说就啰嗦了,后面不说了);表示批量读取;如果是1401就是随机读取;
|
||||
00 00 (子命令) : 值是0表示按字读取(1个字=16位),如果值是1就按位读取;
|
||||
64 00 00(首地址):地址因为跨度比较大,所以用了3个字节;这里的值是000064,十进制就是100
|
||||
A8 (软元件) : 表示读取PLC寄存器的类型: 这里的A8表示D点;其他常见的有: 90-M点;9C-X点;9D-Y点;B0-ZR外部存储卡
|
||||
14 00(读取长度) :值是0014,十进制就是20;
|
||||
}
|
||||
|
||||
procedure TMelsec.Open(Sender: TObject;sValue: string); //4C4F4E0d
|
||||
VAR
|
||||
bBtye:array[0..4] of byte;
|
||||
begin
|
||||
if bIsDestory then Exit;
|
||||
bBtye[0]:=$4C;bBtye[1]:=$4F;bBtye[2]:=$4E;
|
||||
bBtye[3]:=$0D;
|
||||
fSocket.Socket.SendBuf(bBtye[0],4);
|
||||
WorkLog.Debug(Format('Melsec读头%d打开',[FNum]));
|
||||
end;
|
||||
|
||||
{ TOmron }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
constructor TOmron.Create(sIp:string;iPort:integer); //参数固定
|
||||
begin
|
||||
inherited Create;
|
||||
bIsDestory:=false;
|
||||
FNum:=1;
|
||||
WorkLog.Debug('创建Omron_plc读头');
|
||||
FSocket:=TClientSocket.Create(nil);
|
||||
FSocket.OnRead:=SocketRead;
|
||||
FSocket.OnError:=SocketError;
|
||||
FSocket.OnConnect:=SocketConnect;
|
||||
FSocket.OnDisconnect:=SocketDisConnect;
|
||||
FSocket.OnConnecting:=SocketConnecting;
|
||||
FSocket.Address:=sIp;
|
||||
FSocket.Port:=iPort;
|
||||
FTimer:=TTimer.Create(nil);
|
||||
FTimer.OnTimer:=TimerTimer;
|
||||
FTimer.Enabled:=false;
|
||||
end;
|
||||
|
||||
procedure TOmron.Connect();
|
||||
var
|
||||
tmp:string;
|
||||
begin
|
||||
tmp:=Format('连接Omron_plc读头%d:%s,%d',[FNum,FSocket.Address,FSocket.Port]);
|
||||
WorkLog.Debug(tmp);
|
||||
if Assigned(FOnShowState) then FOnShowState(self, tmp);
|
||||
FSocket.Active:=true;
|
||||
end;
|
||||
|
||||
procedure TOmron.DisConnect();
|
||||
begin
|
||||
FSocket.Close;
|
||||
FTimer.Enabled:=false;
|
||||
end;
|
||||
|
||||
procedure TOmron.SocketConnecting(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Omron_plc读头%d连接中...',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self,Format('Omron_plc读头%d连接中...',[FNum]));
|
||||
end;
|
||||
|
||||
destructor TOmron.Destroy;
|
||||
begin
|
||||
bIsDestory:=true;
|
||||
FSocket.Close;
|
||||
FTimer.Enabled:=false;
|
||||
WorkLog.Debug('销毁Omron_plc读头');
|
||||
FSocket.Free;
|
||||
FTimer.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TOmron.SocketDisConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Omron_plc读头%d连接断开',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self, Format('Omron_plc读头%d连接断开',[FNum]));
|
||||
end;
|
||||
|
||||
procedure TOmron.TimerTimer(Sender: TObject);
|
||||
begin
|
||||
if FTimer.Enabled then
|
||||
begin
|
||||
FTimer.Enabled:=false;
|
||||
FSocket.Close;
|
||||
FSocket.Open;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TOmron.SocketRead(Sender: TObject; Socket: TCustomWinSocket);
|
||||
var
|
||||
buf:array[0..1024] of byte;
|
||||
sHexString:string;
|
||||
i:Integer;
|
||||
sData:string;
|
||||
function ByteToString(const Value:PByte;iLen:Integer): String;
|
||||
var
|
||||
I: integer;
|
||||
S : String;
|
||||
begin
|
||||
S := '';
|
||||
for I := 0 to iLen-1 do
|
||||
begin
|
||||
if Value[i]=$0D then break;
|
||||
S := S+Chr(Value[I]);
|
||||
end;
|
||||
Result := S;
|
||||
end;
|
||||
begin
|
||||
if bIsDestory then Exit;
|
||||
|
||||
sHexString:='';
|
||||
for i:= 0 to Socket.ReceiveBuf(buf,1024) do
|
||||
begin
|
||||
sHexString:=sHexString+buf[i].ToHexString;
|
||||
end;
|
||||
WorkLog.Debug('Omron_plc读头%d读到数据:%s',[FNum,sHexString]);
|
||||
|
||||
sData:=ByteToString(@buf[0],Length(sHexString)div 2);
|
||||
if Assigned(FOnWork) then FOnWork(self, sData);
|
||||
end;
|
||||
|
||||
procedure TOmron.SocketError(Sender: TObject; Socket: TCustomWinSocket;
|
||||
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
||||
var
|
||||
tmpError:string;
|
||||
begin
|
||||
if (ErrorCode=10060) OR (ErrorCode=10065) then
|
||||
begin
|
||||
tmpError:=Format('Omron_plc读头%d连接出错(%d):%s',[FNum,ErrorCode,SysErrorMessage(ErrorCode)]);
|
||||
if Assigned(FOnShowState) then FOnShowState(self, Format('Omron_plc读头%d连接失败,重新连接',[FNum]));
|
||||
end
|
||||
else
|
||||
begin
|
||||
tmpError:=Format('Omron_plc读头%d连接出错(%d):%s',[FNum,ErrorCode,SysErrorMessage(ErrorCode)]);
|
||||
if Assigned(FOnShowState) then FOnShowState(self, 'Omron_plc读头连接出错:'+SysErrorMessage(ErrorCode));
|
||||
end;
|
||||
workLog.Error(tmpError);
|
||||
ErrorCode:=0;
|
||||
if bIsDestory then Exit;
|
||||
FTimer.Enabled:=true;
|
||||
end;
|
||||
|
||||
procedure TOmron.SocketConnect(Sender: TObject; Socket: TCustomWinSocket);
|
||||
begin
|
||||
WorkLog.Debug(Format('Omron_plc读头%d连接成功',[FNum]));
|
||||
if Assigned(FOnShowState) then FOnShowState(self, Format('Omron_plc读头%d连接成功',[FNum]));
|
||||
end;
|
||||
|
||||
procedure TOmron.Close; //4C4F46460D
|
||||
Var
|
||||
bBtye:array[0..4] of byte;
|
||||
begin
|
||||
bBtye[0]:=$4C;bBtye[1]:=$4F;bBtye[2]:=$46;
|
||||
bBtye[3]:=$46;bBtye[4]:=$0D;
|
||||
fSocket.Socket.SendBuf(bBtye[0],5);
|
||||
WorkLog.Debug(Format('Omron_plc读头%d关闭',[FNum]));
|
||||
end;
|
||||
|
||||
{*
|
||||
* 头标识 46494E53 即为ASCII码:FINS
|
||||
长度 0000000C 后续字节长度=12
|
||||
命令码 00000000 为0
|
||||
错误代码 00000000 为0
|
||||
客户端节点地址 00000000 to 000000FE 0到254,为0服务端会自动分配节点号
|
||||
*
|
||||
* 服务端接收到连接请求后,返回帧格式如下:
|
||||
名称 内容 说明
|
||||
头标识 46494E53 ASCII:FINS
|
||||
长度 00000010 从命令码开始的数据长度
|
||||
命令码 00000001 固定值00000001
|
||||
错误码 4个字节错误信息 参考错误信息码表
|
||||
客户端节点地址 00000001 to 000000FE 1到254
|
||||
服务端节点地址 00000001 to 000000FE 1到254
|
||||
46 49 4E 53 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 71 00 00 00 0A
|
||||
*}
|
||||
|
||||
procedure TOmron.Open; //4C4F4E0d
|
||||
VAR
|
||||
bBtye:array[0..4] of byte;
|
||||
begin
|
||||
if bIsDestory then Exit;
|
||||
bBtye[0]:=$4C;bBtye[1]:=$4F;bBtye[2]:=$4E;
|
||||
bBtye[3]:=$0D;
|
||||
fSocket.Socket.SendBuf(bBtye[0],4);
|
||||
WorkLog.Debug(Format('Omron_plc读头%d打开',[FNum]));
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user