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.