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.