FX5U TCP 通信 (2016/11/03, 2017/ 2/25 update)
・2017/ 2/25 Read / Write を訂正
・2016/11/03 バイナリ交信で、デバイス番号の指定方法が違っていたのを修正しました。
・2016/10/24a ASCII 更新で文字列書き込み、読み込みの文字列順が違うのを修正しました。
・2016/10/24 文字列書き込み、読み込みのコードを追加しました。
・2016/10/20 バイナリ交信のコードを追加しました。
■FX5U Ethernet ポートの設定
・IPアドレス、サブネットマスクを設定します。
・交信データコードは、パソコンから交信するデータに合わせます。(バイナリの時は、バイナリにします)
・相手機器接続構成設定の<詳細設定>をダブルクリックします。
・「ユニット一覧」から、「Ethernet 機器(汎用)」の「SLMP 接続機器」を赤矢印のように、ドラッグ&ドロップします。
・「SLMP接続機器」が追加されるので、プロトコルを TCP、ポート番号を 1025 に変更します。
・メニューの「設定を反映して閉じる」をクリックします。
・元の画面で、「適用」ボタンをクリックします。
・あとは、PLC へ PC パラメータを書き込み、PLC をリセットします。
■パソコン側のプログラム
・伝文フォーマットは、QCPU と同じ 「MC プロトコルの QnA 互換 3E フレーム」なので、QCPU との通信プログラムがそのまま動きます。
Delphi 10.1 Belin Starter Edition で、Indy10 IdTCPClient コンポーネントを使用しています。
// IOHandler.WriteLnRFC() を IOHandler.WriteLn() に変更しました (2017/ 1/ 6) unit FX5UTCPUnit; 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, IdGlobal; type TForm1 = class(TForm) IdTCPClient1: TIdTCPClient; Button1: TButton; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Label1: TLabel; Label2: TLabel; Edit4: TEdit; Edit5: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} // ASCII 交信で、読み込み // Y0 ~ Y7 procedure TForm1.Button1Click(Sender: TObject); var head, snd, cmd, res : string; iBits : integer; StrText : string; B : TBytes; begin Edit1.Text := ''; Edit2.Text := ''; EDit3.Text := ''; // ビット単位読み出し数 iBits := 8; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + '03FF'; // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 cmd := '0010'; // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で読みだす cmd := cmd + '0401'; // コマンド cmd := cmd + '0001'; // サブコマンド cmd := cmd + 'Y*000000'; // 先頭デバイス名(8文字) cmd := cmd + IntToHex(iBits, 4); // 読み出し数(16進4桁) snd := head; snd := snd + IntToHex(cmd.Length, 4); snd := snd + cmd; // 送信文字列 Edit1.Text := snd; // Indy Version 10.6.2.5341 with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin B := TEncoding.ANSI.GetBytes(snd); IOHandler.Write(TIdBytes(B), snd.Length); // レスポンス受信 SetLength(B, 0); IOHandler.ReadBytes(TIdBytes(B), -1); res := TEncoding.ANSI.GetString(B); // 受信文字列 Edit2.Text := res; // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (res.Length = 22 + iBits) and (Copy(res, 19, 4) = '0000') then // 正常取得(ビット単位で表示) // オンオフを文字列で表す // 文字列の左が先頭 Edit3.Text := Copy(res, 23, iBits) else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; // ASCII 交信で、書き込み // Y0 ~ Y7 procedure TForm1.Button2Click(Sender: TObject); var head, cmd, snd, res : string; StrText : string; B : TBytes; begin Edit1.Text := ''; Edit2.Text := ''; Edit3.Text := ''; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + '03FF'; // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := '0010'; // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で書き込む cmd := cmd + '1401'; // コマンド cmd := cmd + '0001'; // サブコマンド cmd := cmd + 'Y*'; // デバイスコード cmd := cmd + '000000'; // 先頭デバイス名(6文字) cmd := cmd + '0008'; // デバイス点数 cmd := cmd + '11001100'; // 書き込みデータ 0=ON, 1=OFF(デバイス点数分の文字数) snd := head; snd := snd + IntToHex(cmd.Length, 4); // 要求データ長(16進) snd := snd + cmd; // 送信文字列 Edit1.Text := snd; // Indy Version 10.6.2.5341 with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 B := TEncoding.ANSI.GetBytes(snd); IOHandler.Write(TIdBytes(B), snd.Length); // レスポンス受信 SetLength(B, 0); IOHandler.ReadBytes(TIdBytes(B), -1); res := TEncoding.ANSI.GetString(B); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; // ASCII 交信で、書き込み // Y0 ~ Y7 procedure TForm1.Button3Click(Sender: TObject); var head, cmd, snd, res : string; StrText : string; B : TBytes; begin Edit1.Text := ''; Edit2.Text := ''; Edit3.Text := ''; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + '03FF'; // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := '0010'; // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で書き込む cmd := cmd + '1401'; // コマンド cmd := cmd + '0001'; // サブコマンド cmd := cmd + 'Y*'; // デバイスコード cmd := cmd + '000000'; // 先頭デバイス名(6文字) cmd := cmd + '0008'; // デバイス点数 cmd := cmd + '00000000'; // 書き込みデータ 0=ON, 1=OFF(デバイス点数分の文字数) snd := head; snd := snd + IntToHex(cmd.Length, 4); // 要求データ長(16進) snd := snd + cmd; // 送信文字列 Edit1.Text := snd; // Indy Version 10.6.2.5341 with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 B := TEncoding.ANSI.GetBytes(snd); IOHandler.Write(TIdBytes(B), snd.Length); // レスポンス受信 SetLength(B, 0); IOHandler.ReadBytes(TIdBytes(B), -1); res := TEncoding.ANSI.GetString(B); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; // バイナリ交信のために、文字列のHLを反転 // ex '1234' -> '3412' function RevStrHL(const s: string): string; var i, n :integer; begin n := s.Length; if n > 0 then begin for i := 0 to n div 4 - 1 do result := result + s[i * 4 + 3] + s[i * 4 + 4] + s[i * 4 + 1] + s[i * 4 + 2]; if n mod 4 > 0 then for i := (n div 4) * 4 + 1 to n do result := result + s[i]; end; end; // 2016/11/03 追加 // バイナリ交信のために、文字列のHLを反転 // ex '123456' -> '563412' function RevStrHL3(const s: string): string; var n :integer; begin n := s.Length; if n = 6 then begin result := s[5] + s[6] + s[3] + s[4] + s[1] + s[2]; end; end; // バイナリ交信で、書き込み // Y0 ~ Y7 procedure TForm1.Button4Click(Sender: TObject); var head, cmd, snd, res : string; sndLen : integer; i : integer; StrText : string; buf: TIdBytes; s : string; begin // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + RevStrHL('03FF'); // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := RevStrHL('0010'); // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で書き込む cmd := cmd + RevStrHL('1401'); // コマンド cmd := cmd + RevStrHL('0001'); // サブコマンド // ASCII交信と順序が異なる // 2016/11/03 修正 cmd := cmd + RevStrHL3(IntToHex(0, 6)); // 先頭デバイス名(6文字)、16進 cmd := cmd + '9D'; // 'M*'='90', 'Y*'='9D' デバイスコード cmd := cmd + RevStrHL('0008'); // デバイス点数 // 書き込みデータ 0=ON, 1=OFF(デバイス点数分の文字数) // 文字列の左が、若い番号 // 奇数の時は、ダミーを追加して偶数にする (Ex: '1' -> '10') cmd := cmd + '10000000'; snd := head; // 要求データ長(16進)バイナリに変えるので、文字列長さの1/2 snd := snd + RevStrHL(IntToHex(cmd.Length div 2 , 4)); snd := snd + cmd; // 送信データ数 sndLen := snd.Length div 2; SetLength(buf, sndLen); // 2文字ずつバイナリに変換し、バッファに格納 for i := 0 to snd.Length div 2 -1 do buf[i] := StrToInt('$' + Copy(snd, i * 2 + 1, 2)); // 確認のためバッファを文字列として表示 s := ''; for i := 0 to Length(buf) -1 do s := s + IntToHex(buf[i], 2); Edit1.Text := s; with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 IOHandler.Write(buf, sndLen); // レスポンス待ち while IOHandler.CheckForDataOnSource(10) do ; // 受信のため、バッファをクリア SetLength(buf, 0); // レスポンス受信 IOHandler.ReadBytes(buf, -1); res := ''; for i := 0 to Length(buf) -1 do res := res + IntToHex(buf[i], 2); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; // バイナリ交信で、書き込み // Y0 ~ Y7 procedure TForm1.Button5Click(Sender: TObject); var head, cmd, snd, res : string; sndLen : integer; i : integer; StrText : string; buf: TIdBytes; s : string; begin // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + RevStrHL('03FF'); // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := RevStrHL('0010'); // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で書き込む cmd := cmd + RevStrHL('1401'); // コマンド cmd := cmd + RevStrHL('0001'); // サブコマンド // ASCII交信と順序が異なる // 2016/11/03 修正 cmd := cmd + RevStrHL3(IntToHex(0, 6)); // 先頭デバイス名(6文字)、16進 cmd := cmd + '9D'; // 'M*='90'、'Y*'='9D'デバイスコード cmd := cmd + RevStrHL('0008'); // デバイス点数 // 書き込みデータ 0=ON, 1=OFF(デバイス点数分の文字数) // 文字列の左が、若い番号 // 奇数の時は、ダミーを追加し偶数にする (ex: '1' -> '10') cmd := cmd + '00000000'; snd := head; // 要求データ長(16進)バイナリに変えるので、文字列長さの1/2 snd := snd + RevStrHL(IntToHex(cmd.Length div 2 , 4)); snd := snd + cmd; // 送信データ数 sndLen := snd.Length div 2; SetLength(buf, sndLen); // 2文字ずつバイナリに変換し、バッファに格納 for i := 0 to snd.Length div 2 -1 do buf[i] := StrToInt('$' + Copy(snd, i * 2 + 1, 2)); // 確認のためバッファを文字列として表示 s := ''; for i := 0 to Length(buf) -1 do s := s + IntToHex(buf[i], 2); Edit1.Text := s; with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 IOHandler.Write(buf, sndLen); // レスポンス待ち while IOHandler.CheckForDataOnSource(10) do ; // 受信のため、バッファをクリア SetLength(buf, 0); // レスポンス受信 IOHandler.ReadBytes(buf, -1); res := ''; for i := 0 to Length(buf) -1 do res := res + IntToHex(buf[i], 2); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; // バイナリ交信で、読み込み // Y0 ~ Y7 procedure TForm1.Button6Click(Sender: TObject); var head, cmd, snd, res : string; sndLen : integer; i : integer; StrText : string; buf: TIdBytes; s : string; iBits : integer; begin // ビット単位読み出し数 iBits := 8; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + RevStrHL('03FF'); // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := RevStrHL('0010'); // CPU監視タイマ(16進) //ビットデバイス(X,Y,M...など)を1点単位で読み込む cmd := cmd + RevStrHL('0401'); // コマンド cmd := cmd + RevStrHL('0001'); // サブコマンド // ASCII交信と順序が異なる // 2016/11/03 修正 cmd := cmd + RevStrHL3(IntTiHex(0, 6)); // 先頭デバイス名(6文字) 16進表記 cmd := cmd + '9D'; // 'M*'='90'、'Y*'='9D'デバイスコード cmd := cmd + RevStrHL(IntToHex(iBits, 4)); // デバイス点数 snd := head; // 要求データ長(16進)バイナリに変えるので、文字列長さの1/2 snd := snd + RevStrHL(IntToHex(cmd.Length div 2 , 4)); snd := snd + cmd; // 送信データ数 sndLen := snd.Length div 2; SetLength(buf, sndLen); // 2文字ずつバイナリに変換し、バッファに格納 for i := 0 to snd.Length div 2 -1 do buf[i] := StrToInt('$' + Copy(snd, i * 2 + 1, 2)); // 確認のためバッファを文字列として表示 s := ''; for i := 0 to Length(buf) -1 do s := s + IntToHex(buf[i], 2); Edit1.Text := s; with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 IOHandler.Write(buf, sndLen); // レスポンス待ち while IOHandler.CheckForDataOnSource(10) do ; // 受信のため、バッファをクリア SetLength(buf, 0); // レスポンス受信 IOHandler.ReadBytes(buf, -1); res := ''; for i := 0 to Length(buf) -1 do res := res + IntToHex(buf[i], 2); // 受信文字列 Edit2.Text := res; if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (res.Length = 22 + iBits) and (Copy(res, 19, 4) = '0000') then // 正常取得(ビット単位で表示) // オンオフを文字列で表す // 文字列の左が先頭 Edit3.Text := Copy(res, 23, iBits) else // エラーコード表示 Edit3.Text := Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin StrText := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(StrText), '情報', MB_ICONINFORMATION); end; end; end; end; end. // ************************* // 以下、2016/10/24 追加分 // ************************* // ASCII 文字列書き込み procedure TForm1.Button12Click(Sender: TObject); var head, cmd, snd, res : string; strTExt : string; srcText, sndText : string; wordsCount : integer; i : integer; s : string; n : integer; B : TBytes; begin // 2文字ずつ入れ替え s := Edit12.Text; n := s.Length; for i := 0 to n div 2 -1 do srcText := srcText + Copy(s, i * 2 + 2, 1) + Copy(s, i * 2 + 1, 1); if n mod 2 > 0 then srcText := srcText + #00 + Copy(s, n, 1); // 文字列をASCIIコードに変換し、それを16進2桁の文字列にする // 文字列は、上位、下位で格納される for i := 1 to srcText.Length do sndText := sndText + IntToHex(Ord(srcText[i]), 2); // 書き込みワード数=必要ワード数 wordsCount := srcText.Length div 2; if srcText.Length mod 2 > 0 then begin // 1文字余った時 sndText := sndText + '00'; Inc(wordsCount); end; Edit1.Text := ''; Edit2.Text := ''; Edit3.Text := ''; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + '03FF'; // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := '0010'; // CPU監視タイマ(16進) //ワードデバイス(D...など)を1ワード単位で書き込む cmd := cmd + '1401'; // コマンド cmd := cmd + '0000'; // サブコマンド cmd := cmd + 'D*'; // デバイスコード cmd := cmd + '000000'; // 先頭デバイス名(6文字) cmd := cmd + IntToHex(wordsCount, 4); // ワードデバイス点数 cmd := cmd + sndText; // 書き込みデータ snd := head; snd := snd + IntToHex(cmd.Length, 4); // 要求データ長(16進) snd := snd + cmd; // 送信文字列 Edit1.Text := snd; // Indy Version 10.6.2.5341 with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 B := TEncoding.ANSI.GetBytes(snd); IOHandler.Write(TIdBytes(B), snd.Length); // レスポンス受信 SetLength(B, 0); IOHandler.ReadBytes(TIdBytes(B), -1); res := TEncoding.ANSI.GetString(B); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := 'ErrorCode = ' + Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end; end; // ASCII 文字列読込 procedure TForm1.Button13Click(Sender: TObject); var head, snd, cmd, res : string; strTExt : string; wordsCount : integer; readLen : integer; readText : string; src : string; i : integer; n : integer; s : string; B : TBytes; begin Edit1.Text := ''; Edit2.Text := ''; EDit3.Text := ''; // 読み込み文字列数 readLen := Length(Edit12.Text); // 読み込みワード数 wordsCount := readLen div 2; if readLen mod 2 > 0 then Inc(wordsCount); // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + '03FF'; // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 cmd := '0010'; // CPU監視タイマ(16進) //ワードデバイス(D...など)を1ワード単位で読みだす cmd := cmd + '0401'; // コマンド cmd := cmd + '0000'; // サブコマンド cmd := cmd + 'D*000000'; // 先頭デバイス名(8文字) cmd := cmd + IntToHex(wordsCount, 4); // 読み出しワード数(16進4桁) snd := head; snd := snd + IntToHex(cmd.Length, 4); snd := snd + cmd; // 送信文字列 Edit1.Text := snd; // Indy Version 10.6.2.5341 with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 B := TEncoding.ANSI.GetBytes(snd); IOHandler.Write(TIdBytes(B), snd.Length); // レスポンス受信 SetLength(B, 0); IOHandler.ReadBytes(TIdBytes(B), -1); res := TEncoding.ANSI.GetString(B); // 受信文字列 Edit2.Text := res; // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (res.Length = 22 + wordsCount * 4) and (Copy(res, 19, 4) = '0000') then begin // 正常取得(ビット単位で表示) // 文字列の左が先頭 src := Copy(res, 23, wordsCount*4); for i := 1 to src.Length div 2 do // ASCIIコードを文字列に変換 readText := readText + Char(StrToInt('$' + src[i * 2 - 1] + src[i * 2])); n := readText.Length; s := ''; // 2文字ずつ入れ替え for i := 0 to n div 2 -1 do s := s + Copy(readText, i * 2 + 2, 1) + Copy(readText, i * 2 + 1, 1); if n mod 2 > 0 then s := s + Copy(readText, n, 1); Edit3.Text := s; end else // エラーコード表示 Edit3.Text := 'ErrorCode = ' + Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end; end; // Binary 文字列書込 procedure TForm1.Button14Click(Sender: TObject); var head, cmd, snd, res : string; sndLen : integer; i : integer; strTExt : string; buf: TIdBytes; s : string; srcText, sndText : string; wordsCount : integer; begin srcText := Edit12.Text; // 文字列をASCIIコードに変換し、それを16進2桁の文字列にする // 文字列は、上位、下位で格納される for i := 1 to srcText.Length do sndText := sndText + IntToHex(Ord(srcText[i]), 2); // 書き込みワード数=必要ワード数 wordsCount := srcText.Length div 2; if srcText.Length mod 2 > 0 then begin // 1文字余った時 sndText := sndText + '00'; Inc(wordsCount); end; // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号 head := head + 'FF'; // PC番号 head := head + RevStrHL('03FF'); // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号 // 送信データには、これ以降の文字列数が必要 cmd := RevStrHL('0010'); // CPU監視タイマ(16進) //ワードデバイス(D...など)を1ワード点単位で書き込む cmd := cmd + RevStrHL('1401'); // コマンド cmd := cmd + RevStrHL('0000'); // サブコマンド // ASCII交信と順序が異なる // 2016/11/03 修正 cmd := cmd + RevStrHL3(IntToHex(0, 6)); // 先頭デバイス名(6文字) 16進表記 cmd := cmd + 'A8'; // 'D*'='A8'デバイスコード cmd := cmd + RevStrHL(IntToHex(wordsCount, 4)); // デバイス点数 cmd := cmd + sndText; snd := head; // 要求データ長(16進)バイナリに変えるので、文字列長さの1/2 snd := snd + RevStrHL(IntToHex(cmd.Length div 2 , 4)); snd := snd + cmd; // 送信データ数 sndLen := snd.Length div 2; SetLength(buf, sndLen); // 2文字ずつバイナリに変換し、バッファに格納 for i := 0 to snd.Length div 2 -1 do buf[i] := StrToInt('$' + Copy(snd, i * 2 + 1, 2)); // 確認のためバッファを文字列として表示 s := ''; for i := 0 to Length(buf) -1 do s := s + IntToHex(buf[i], 2); Edit1.Text := s; with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 IOHandler.Write(buf, sndLen); // レスポンス待ち while IOHandler.CheckForDataOnSource(10) do ; // 受信のため、バッファをクリア SetLength(buf, 0); // レスポンス受信 IOHandler.ReadBytes(buf, -1); res := ''; for i := 0 to Length(buf) -1 do res := res + IntToHex(buf[i], 2); // 受信文字列 Edit2.Text := res; // 正常終了時:'D00000FF03FF0000040000' // レスポンスの判断 if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (Copy(res, 19, 4) = '0000') then begin // 正常終了 Edit3.Text := 'OK'; end else // エラーコード表示 Edit3.Text := 'ErrorCode = ' + Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end; end; // Binary 文字列読込 procedure TForm1.Button15Click(Sender: TObject); var head, cmd, snd, res : string; sndLen : integer; i : integer; strTExt : string; buf: TIdBytes; s : string; wordsCount : integer; readLen : integer; readText : string; begin // 読み込み文字列数 readLen := Length(Edit12.Text); // 読み込みワード数 wordsCount := readLen div 2; if readLen mod 2 > 0 then Inc(wordsCount); // 送信文字列 // QnA互換3Eフレーム head := '5000'; // サブヘッダ(PC->PLCへの伝文) head := head + '00'; // ネットワーク番号(PLC側の設定による) head := head + 'FF'; // PC番号(固定) head := head + RevStrHL('03FF'); // 要求先ユニットI/O番号 head := head + '00'; // 要求先ユニット局番号(PLC側の設定による) // 送信データには、これ以降の文字列数が必要 cmd := RevStrHL('0010'); // CPU監視タイマ(16進) //ワードデバイス(D...など)を1ワード単位で読み込む cmd := cmd + RevStrHL('0401'); // コマンド cmd := cmd + RevStrHL('0000'); // サブコマンド // ASCII交信と順序が異なる // 2016/11/03 修正 cmd := cmd + RevStrHL3(IntToHex(0, 6)); // 先頭デバイス名(6文字) cmd := cmd + 'A8'; // 'D*'='A8'デバイスコード cmd := cmd + RevStrHL(IntToHex(wordsCount, 4)); // デバイス点数 snd := head; // 要求データ長(16進)バイナリに変えるので、文字列長さの1/2 snd := snd + RevStrHL(IntToHex(cmd.Length div 2 , 4)); snd := snd + cmd; // 送信データ数 sndLen := snd.Length div 2; SetLength(buf, sndLen); // 2文字ずつバイナリに変換し、バッファに格納 for i := 0 to snd.Length div 2 -1 do buf[i] := StrToInt('$' + Copy(snd, i * 2 + 1, 2)); // 確認のためバッファを文字列として表示 s := ''; for i := 0 to Length(buf) -1 do s := s + IntToHex(buf[i], 2); Edit1.Text := s; with IdTCPClient1 do begin // タイムアウトを設定 ConnectTimeout := 1000; ReadTimeout := 200; // PLC の設定に合わせる // IPアドレス Host := Edit4.Text; //'169.254.17.243'; // ポート番号 Port := StrToIntDef(Edit5.Text, 1025); try // 接続 if not Connected then Connect; if Connected then begin // コマンド送信 IOHandler.Write(buf, sndLen); // レスポンス待ち while IOHandler.CheckForDataOnSource(10) do ; // 受信のため、バッファをクリア SetLength(buf, 0); // レスポンス受信 IOHandler.ReadBytes(buf, -1); res := ''; for i := 0 to Length(buf) -1 do res := res + IntToHex(buf[i], 2); // 受信文字列 Edit2.Text := res; if (res.Length >= 22) and (Copy(res, 1, 4) = 'D000') then begin if (res.Length = 22 + wordsCount * 4) and (Copy(res, 19, 4) = '0000') then begin // 正常取得 s := Copy(res, 23, wordsCount * 4); for i := 1 to s.Length div 2 do // ASCIIコードを文字列に変換 readText := readText + Char(StrToInt('$' + s[i * 2 - 1] + s[i * 2])); Edit3.Text := readText; end else // エラーコード表示 Edit3.Text := 'ErrorCode = ' + Copy(res, 19, 4); end; end; // 切断 Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end; end;