Delphi OMRON FINS/TCP 通信 2020.05.03
非常に分かりにくいですが、サンプルコードがあったので、何とかなりました。
FINS/TCP では、接続後FINS ノードアドレス情報の取得が必要で、取得後コネクション確立となり、切断まで有効となるようです。
unit OmronCJ2MTestUnit7; 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 TForm7 = class(TForm) Memo1: TMemo; Button2: TButton; IdTCPClient1: TIdTCPClient; procedure Button2Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form7: TForm7; implementation {$R *.dfm} { 下記マニュアルは必須 ・SYSMAC CS/CJシリーズ Ethernetユニット ユーザーズマニュアル アプリケーション構築編 SBCD-330 「7-4 FINS/TCP 方式」 ・SYSMAC CS/CJ/CPシリーズ SYSMAC One NSJシリーズ 通信コマンドリファレンスマニュアル SBCA-304 「5 FINSコマンドリファレンス」 注意 ・「FINS コマンドフレームの作成」は接続時に1回だけ必要 FINS/TCP クライアントとしてコネクションが確立した後、再び送信してはいけない } procedure TForm7.Button2Click(Sender: TObject); // uses ... , IdGlobal; var AData, BData : TBytes; i: integer; s : string; ret : Boolean; cli_node_no, srv_node_no : Byte; sid : Byte; begin sid := 0; IdTCPClient1.Host := '192.168.250.1'; IdTCPClient1.Port := 9600; IdTCPClient1.ConnectTimeout := 2000; IdTCPClient1.ReadTimeout := 200; IdTCPClient1.Connect; if IdTCPClient1.Connected then begin Memo1.Lines.Clear; Memo1.Lines.Add('Connected'); // ------------------------------------- // FINS ノードアドレス情報送信コマンド // クライアント(PC) -> サーバ(PLC ) // ------------------------------------- // FINS/TCP クライアントとしてコネクションが確立した後、 // このコマンドを再び送信してはいけない! SetLength(AData, 20); // ヘッダ (4バイト):'FINS' AData[0] := $46; AData[1] := $49; AData[2] := $4E; AData[3] := $53; // コマンド以降のデータ長 (4バイト) // $0C = 12 バイト AData[4] := $00; AData[5] := $00; AData[6] := $00; AData[7] := $0C; // コマンド (4バイト) AData[8] := $00; AData[9] := $00; AData[10] := $00; AData[11] := $00; // エラーコード (4バイト): 未使用 AData[12] := $00; AData[13] := $00; AData[14] := $00; AData[15] := $00; // FINS/TCP クラインアントの FINS ノードアドレス // 0 = 自動取得 AData[16] := $00; AData[17] := $00; AData[18] := $00; AData[19] := $00; // 送信 IdTCPClient1.IOHandler.Write(TIdBytes(AData), 20); // レスポンス受信 SetLength(AData, 0); IdTCPClient1.IOHandler.ReadBytes(TIdBytes(Adata), -1); // 受信データ数 = 24 ret := (Length(AData) = 24) and (AData[8] = 0) and (AData[9] = 0) and (AData[10] = 0) and (AData[11] = 1); if not ret then Memo1.Lines.Add('TCP receive error'); if ret then begin // ノードアドレスを保持 cli_node_no := AData[19]; // PC srv_node_no := AData[23]; // PLC Memo1.Lines.Add('FINS/TCP client Node No.= ' + cli_node_no.ToHexString); Memo1.Lines.Add('FINS/TCP server Node No.= ' + srv_node_no.ToHexString); // 必ず「FINS フレーム送信コマンド」を FINS フレームの先頭に付加する // 「FINS フレーム送信コマンド」内のデータ長から、その後に続く TCP データから、 // FINSフレームが切り出される // ------------------------------------- // FINS コマンドフレームの作成 // ------------------------------------- SetLength(AData, 16); // ヘッダ:'FINS' AData[0] := $46; AData[1] := $49; AData[2] := $4E; AData[3] := $53; // コマンド以降のデータ長 (4バイト) // $0C = 12 バイト AData[4] := $00; AData[5] := $00; AData[6] := $00; AData[7] := 8 + 18; // コマンド以降 FINS フレーム末端までのデータ長 // コマンド (4バイト) AData[8] := $00; AData[9] := $00; AData[10] := $00; AData[11] := $02; // エラーコード (4バイト) 未使用 AData[12] := $00; AData[13] := $00; AData[14] := $00; AData[15] := $00; // FINS/TCP コマンドの送信 IdTCPClient1.IOHandler.Write(TIdBytes(AData), 16); // ------------------------------------- // 変数エリアの読み出しコマンド // ------------------------------------- SetLength(AData, 18); AData[0] := $80; // ICF(インフォメーション・コントロール・フィールド) AData[1] := $00; // RSV(システム予約項目) 固定 AData[2] := $02; // GCT(許容ゲートウェイ通過数)固定 AData[3] := $00; // DNA (送信先 FINS ネットワークアドレス)00 Hex :自ネットワーク AData[4] := srv_node_no; // DA1: Ethernetユニットの FINS ノードアドレス AData[5] := $00; // DA2 (送信先号機アドレス)00 Hex :PLC 本体(CPU ユニット) AData[6] := $00; // SNA (送信元 FINS ネットワークアドレス) AData[7] := cli_node_no; // 自動取得した PC 側の FINS ノードアドレス AData[8] := $00; // SA2 (送信元 FINS ネットワークアドレス) // SID: 発信元のプロセス識別子 00~FF Hex の任意のデータを指定 // レスポンスを返信するノードはコマンドの「SID」と同じ値を返すので、 // 同一ユニットに連続してコマンドを発行したときなどに、レスポンスの判定用に使用 Inc(sid); AData[9] := sid; // SID // FINS コマンドコード 0101 :連続した I/O メモリエリアの内容の読み出し AData[10] := $01; // MRC AData[11] := $01; // SRC AData[12] := $82; // 変数種別:DM // 読み出し開始アドレス(3バイト) // 2 バイト(16 進 4 桁)がチャネル指定、1 バイト(16 進 2 桁)がビット指定 // 計 3 バイト(16 進 6 桁) AData[13] := $00; // 読み出し開始アドレス(3バイト):100CH AData[14] := $64; AData[15] := $00; AData[16] := $00; // 読み出しCH 数(2バイト):150CH AData[17] := $96; s := ''; for i := 0 to Length(AData) -1 do s := s + IntToHex(AData[i], 2) + ' '; Memo1.Lines.Add('cmd:' + #13#10 + s); // FINS コマンドフレームの送信 IdTCPClient1.IOHandler.Write(TIdBytes(AData), 18); // レスポンス受信 SetLength(BData, 0); IdTCPClient1.IOHandler.ReadBytes(TIdBytes(Bdata), -1); // 返信データ数は 2バイト(1CH) × 150CH = 300バイト +固定部分(30バイト) Memo1.Lines.Add('resLength=' + Length(BData).ToString); // 150CH:330bytes s := ''; for i := 0 to Length(BData) -1 do s := s + IntToHex(BData[i], 2) + ' '; Memo1.Lines.Add('res:' + #13#10 + s); // 受信データには、コマンドフレーム(16バイト)と FINS レスポンスフレーム(14バイト以上)が含まれる if Length(BData) < (16 + 14) then begin Memo1.Lines.Add('FINS length error'); end else if (AData[3] <> BData[16 + 6]) or (AData[4] <> BData[16 + 7]) or (AData[5] <> BData[16 + 8]) then begin Memo1.Lines.Add('illegal source address error'); s := ''; for i := 3 to 5 do s := s + IntToHex(AData[i], 2) + ':'+ IntToHex(BData[i + 16 + 3], 2) + ' '; Memo1.Lines.Add('address= ' + s); end else if (AData[9] <> BData[9 + 16]) then begin Memo1.Lines.Add('illegal SID error'); end else begin // 正常受信 end; end; // 切断 IdTCPClient1.Disconnect; end; end; end.