MELSEC Q03UDECPU Ethernet を Delphi XE5 (Indy10 TCP/UDP) を使う (2015/ 1/ 3 , 2017/ 2/25 更新)
IndyTCPClient 、IndeyUDPServer を使って、Q03UDECPU と通信します。
PLC側のPCパラメータオープン設定:
・TCP MCプロトコル 0401(ポート16進表示(10進だと、1025))
・UDP MCプロトコル 0402(ポート16進表示(10進だと、1026))
送信する文字列、返信される文字列は、TCP/UDP とも、まったく同じです。
TCP や RS232Cは、OPEN に時間がかかります。(MX Component も)
なので、アプリ起動と同時に OPEN 、終了とともに CLOSE するのが良いと思います。
実用するには、途中、切断されたら、自動的に再接続する処理が必要です。
また、周期的にデータを取得、操作する場合は、別スレッドにしたほうが、良いと思います。
UDP の場合は、アプリ起動と同時にポート 1026 を開いて、受信待ちにします。
あとは、勝手にコマンド送信するだけです。(相手に届いているかどうかは気にしない)
// IOHandler.Write.., Read.. を訂正 (2017/ 2/25)
unit MelsecQTCPUnit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdSocketHandle, IdUDPBase,
IdUDPServer, IdGlobal;
type
TForm1 = class(TForm)
IdTCPClient1: TIdTCPClient;
Edit1: TEdit;
Button1: TButton;
Edit2: TEdit;
Button2: TButton;
IdUDPServer1: TIdUDPServer;
Button3: TButton;
Edit3: TEdit;
Edit4: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Edit5: TEdit;
Edit6: TEdit;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Button4: TButton;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
const AData: TIdBytes; ABinding: TIdSocketHandle);
procedure Button4Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
// UDPにて受信文字列数確認のためのビット数
gb_iBits : integer;
// UDPにて受信後の処理分けのための番号
gb_iCmdNo : integer;
// 処理時間計測
gb_Ticks : Cardinal;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// *******************************
// 読み出し(TCP)
// *******************************
procedure TForm1.Button1Click(Sender: TObject);
var
cmd, res : string;
iBits : integer;
n : integer;
StrText : String;
Ticks : Cardinal;
B : TBytes;
begin
Edit1.Text := '';
Edit2.Text := '';
EDit3.Text := '';
// ビット単位読み出し数
iBits := 16;
// 送信文字列
// QnA互換3Eフレーム
cmd := '5000'; // サブヘッダ(PC->PLCへの伝文)
cmd := cmd + '00'; // ネットワーク番号
cmd := cmd + 'FF'; // PC番号
cmd := cmd + '03FF'; // 要求先ユニットI/O番号
cmd := cmd + '00'; // 要求先ユニット局番号
cmd := cmd + '0018'; // 要求データ長(16進)= 24バイト=これ以下の文字数
cmd := cmd + '0010'; // CPU監視タイマ(16進)
//ビットデバイス(X,Y,M...など)を1点単位で読みだす
cmd := cmd + '0401'; // コマンド
cmd := cmd + '0001'; // サブコマンド
cmd := cmd + 'X*000000'; // 先頭デバイス名(8文字)
cmd := cmd + IntToHex(iBits, 4); // 読み出し数(16進4桁)
// 送信文字列
Edit1.Text := cmd;
// Indy Version 10.6.0.5040
with IdTCPClient1 do begin
// タイムアウトを設定
ConnectTimeout := 200;
ReadTimeout := 100;
// PLC の設定に合わせる
// IPアドレス
Host := '169.254.251.71';
// ポート番号
Port := 1025;
// 処理時間の計測を開始
gb_Ticks := GetTickCount;
try
// 接続
if not Connected then
Connect;
Ticks := GetTickCount;
if Connected then begin
// コマンド送信
B := TEncoding.ANSI.GetBytes(cmd);
IOHandler.Write(TIdBytes(B), cmd.Length);
// レスポンス受信
SetLength(AData, 0);
IOHandler.ReadBytes(TIdBytes(B), -1);
res := TEncoding.ANSI.GetString(B);
// 受信文字列
Edit2.Text := res;
n := res.Length;
// レスポンスの判断
if (n >= 22) and (Copy(res, 1, 4) = 'D000') then begin
if (n = 22 + iBits) and (Copy(res, 19, 4) = '0000') then
// 正常取得(ビット単位で表示)
// オンオフを文字列で表す
// 文字列の左が先頭
Edit3.Text := Copy(res, 23 ,iBits)
else
// エラーコード表示
Edit3.Text := Copy(res, 19, 4);
end;
end;
// OPEN-CLOSE を含まない処理時間
Label10.Caption := IntToStr(GetTickCount - Ticks);
// 切断
Disconnect;
// OPEN-CLOSE を含む時間
Label9.Caption := IntToStr(GetTickCount - gb_Ticks);
except
// エラーメッセージ
on E: Exception do begin
StrText := E.ClassName + sLineBreak + E.Message;
Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// *******************************
// 書き込み(TCP)
// *******************************
procedure TForm1.Button2Click(Sender: TObject);
var
cmd, res : string;
sBits : string;
n : integer;
StrText : String;
Ticks : Cardinal;
begin
Edit1.Text := '';
Edit2.Text := '';
Edit3.Text := '';
// ビット操作文字列
// オンオフを文字列で表す
// 文字列の左が先頭
sBits := '1101001000111101';
// 送信文字列
// QnA互換3Eフレーム
cmd := '5000'; // サブヘッダ(PC->PLCへの伝文)
cmd := cmd + '00'; // ネットワーク番号
cmd := cmd + 'FF'; // PC番号
cmd := cmd + '03FF'; // 要求先ユニットI/O番号
cmd := cmd + '00'; // 要求先ユニット局番号
cmd := cmd + IntToHex(24 + Length(sBits), 4); //要求データ長(16進)= これ以下の文字数
cmd := cmd + '0010'; // CPU監視タイマ(16進)
//ビットデバイス(X,Y,M...など)を1点単位で書き込む
cmd := cmd + '1401'; // コマンド
cmd := cmd + '0001'; // サブコマンド
cmd := cmd + 'X*000000'; // 先頭デバイス名(8文字)
cmd := cmd + IntToHex(Length(sBits), 4); // 読み出し数(16進4桁)
cmd := cmd + sBits;
Edit1.Text := cmd;
// Indy Version 10.6.0.5040
with IdTCPClient1 do begin
// タイムアウトを設定
ConnectTimeout := 100;
ReadTimeout := 100;
// PLC の設定に合わせる
// IPアドレス
Host := '169.254.251.71';
// ポート番号
Port := 1025;
// 処理時間の計測を開始
gb_Ticks := GetTickCount;
try
// 接続
if not Connected then
Connect;
Ticks := GetTickCount;
if Connected then begin
// コマンド送信
IOHandler.WriteLn(cmd);
try
// レスポンス待ち
while IOHandler.CheckForDataOnSource(10) do ;
finally
// レスポンス受信
res := IOHandler.InputBufferAsString();
// 受信文字列
Edit2.Text := res;
n := res.Length;
// レスポンスの判断
if (n >= 22) and (Copy(res, 1, 4) = 'D000') then begin
if (Copy(res, 19, 4) = '0000') then
// 正常終了
Edit3.Text := 'OK'
else
// エラーコード表示
Edit3.Text := Copy(res, 19, 4);
end;
end;
end;
// OPEN-CLOSE を含まない処理時間
Label10.Caption := IntToStr(GetTickCount - Ticks);
// 切断
Disconnect;
// OPEN-CLOSE を含む処理時間
Label9.Caption := IntToStr(GetTickCount - gb_Ticks);
except
// エラーメッセージ
on E: Exception do begin
StrText := E.ClassName + sLineBreak + E.Message;
Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION);
end;
end;
end;
end;
// *******************************
// 読み出し(UDP)
// *******************************
procedure TForm1.Button3Click(Sender: TObject);
var
cmd : string;
// iBits : Integer;
StrText : String;
begin
Edit4.Text := '';
Edit5.Text := '';
Edit6.Text := '';
// ビット単位読み出し数
gb_iBits := 16;
// 送信文字列
// QnA互換3Eフレーム
cmd := '5000'; // サブヘッダ(PC->PLCへの伝文)
cmd := cmd + '00'; // ネットワーク番号
cmd := cmd + 'FF'; // PC番号
cmd := cmd + '03FF'; // 要求先ユニットI/O番号
cmd := cmd + '00'; // 要求先ユニット局番号
cmd := cmd + '0018'; // 要求データ長(16進)= 24バイト=これ以下の文字数
cmd := cmd + '0010'; // CPU監視タイマ(16進)
//ビットデバイス(X,Y,M...など)を1点単位で読みだす
cmd := cmd + '0401'; // コマンド
cmd := cmd + '0001'; // サブコマンド
cmd := cmd + 'X*000000'; // 先頭デバイス名(8文字)
cmd := cmd + IntToHex(gb_iBits, 4); // 読み出し数(16進4桁)
// 受信のために、送信番号を記憶
gb_iCmdNo := 101;
// 送信文字列
Edit4.Text := cmd;
// Indy Version 10.6.0.5040
// uses に IdGlobal を追加すること
try
// 処理時間の計測を開始
gb_Ticks := GetTickCount;
// Host , Port は、PLC の設定に合わせる
IdUDPServer1.Send('169.254.251.71', 1026, cmd, IndyTextEncoding_ASCII);
except
// エラーメッセージ
on E: Exception do begin
StrText := E.ClassName + sLineBreak + E.Message;
Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION);
end;
end;
end;
// *******************************
// 書き込み(UDP)
// *******************************
procedure TForm1.Button4Click(Sender: TObject);
var
cmd : string;
sBits : string;
StrText : String;
//res : string;
Host : string;
Port : Word;
//n : integer;
begin
Edit4.Text := '';
Edit5.Text := '';
Edit6.Text := '';
// ビット操作文字列
// オンオフを文字列で表す
// 文字列の左が先頭
sBits := '1111001000111111';
// 送信文字列
// QnA互換3Eフレーム
cmd := '5000'; // サブヘッダ(PC->PLCへの伝文)
cmd := cmd + '00'; // ネットワーク番号
cmd := cmd + 'FF'; // PC番号
cmd := cmd + '03FF'; // 要求先ユニットI/O番号
cmd := cmd + '00'; // 要求先ユニット局番号
cmd := cmd + IntToHex(24 + Length(sBits), 4); //要求データ長(16進)= これ以下の文字数
cmd := cmd + '0010'; // CPU監視タイマ(16進)
//ビットデバイス(X,Y,M...など)を1点単位で書き込む
cmd := cmd + '1401'; // コマンド
cmd := cmd + '0001'; // サブコマンド
cmd := cmd + 'X*000000'; // 先頭デバイス名(8文字)
cmd := cmd + IntToHex(Length(sBits), 4); // 読み出し数(16進4桁)
cmd := cmd + sBits;
// 送信文字列
Edit4.Text := cmd;
// 受信のために送信番号を記憶
gb_iCmdNo := 102;
try
// 処理時間の計測を開始
gb_Ticks := GetTickCount;
// Indy Version 10.6.0.5040
// Host , Port は、PLC の設定に合わせる
Host := '169.254.251.71';
Port := 1026;
IdUDPServer1.Send(Host, Port, cmd, IndyTextEncoding_ASCII);
except
// エラーメッセージ
on E: Exception do begin
StrText := E.ClassName + sLineBreak + E.Message;
Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION);
end;
end;
end;
// *******************************
// 読み出し、書き込みの受信(UDP)
// *******************************
procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
const AData: TIdBytes; ABinding: TIdSocketHandle);
var
res : string;
n : integer;
begin
// 受信文字列
res := BytesToString(AData, IndyTextEncoding_ASCII);
Edit5.Text := res;
n := res.Length;
// レスポンスの判断
if (n >= 22) and (Copy(res, 1, 4) = 'D000') then begin
if (Copy(res, 19, 4) = '0000') then begin
// 送信番号により、処理を分ける
if (gb_iCmdNo = 101) and (n = 22 + gb_iBits) then begin
// 正常取得(ビット単位で表示)
// オンオフを文字列で表す
// 文字列の左が先頭
Edit6.Text := Copy(res, 23 , gb_iBits)
end
else if gb_iCmdNo = 102 then begin
// 書き込み正常終了
Edit6.Text := 'OK';
end;
end
else
// エラーコード表示
Edit6.Text := Copy(res, 19, 4);
end;
// 処理時間を表示
Label9.Caption := IntToStr(GetTickCount - gb_Ticks);
Label10.Caption := '';
end;
// *******************************
// UDP
// 起動と同時にポートをオープンし、受信を開始する
// *******************************
procedure TForm1.FormCreate(Sender: TObject);
begin
IdUDPServer1.DefaultPort := 1026;
IdUDPServer1.Active := True;
end;
end.