FinsTCP

概略

PCはEthernet経由でFINSコマンドをPLCに送信することでPLCのメモリの値を読み書きすることができます
FinsTCPの場合は最初に「FINS ノードアドレス情報送信コマンド」を送信してクライアントとサーバのFINSノードアドレスを取得します
取得したFINSノードアドレスをFINSヘッダにセットしてFINSフレームの前にFINS-TCPヘッダを付けて送信することになります

全体のメッセージは以下のようになります
FINS-TCPヘッダ FINSフレーム
Header LEN CMD Err FINSヘッダ FINSコマンド
'F' 'I' 'N' 'S'
※ 「FINS ノードアドレス情報送信コマンド」で取得したFINSノードアドレス

ここではFinsTCPを使ってDMの値を読み出します
Com

PLCのIPアドレス 192.168.250.1
PCのIPアドレス 192.168.250.48
送信コマンドの内容 DM100から10CH分の値を読出す

PLCのメモリの値
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9
DM100 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A

以下サンプルコードは同期通信でのやり方をC#で書いてあります
フォームの作成(ボタンの配置など)は省略しているので適宜ボタンなどは配置してください

同期通信

同期通信はコマンド送信してから受信待ちの状態になります
受信タイムアウトは1000msとしているので1秒以内にレスポンスがなければ受信待ち状態から抜けます
private void TcpFinsMessage()
{
  System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient("192.168.250.1", 9600);
  System.Net.Sockets.NetworkStream ns = tcp.GetStream();

  ns.ReadTimeout = 1000;
  ns.WriteTimeout = 1000;

  // FINS ノードアドレス情報送信コマンド
  byte[] FinsTcpHeader = new byte[20];
  FinsTcpHeader[0] = 0x46;       // "F"
  FinsTcpHeader[1] = 0x49;       // "I"
  FinsTcpHeader[2] = 0x4E;       // "N"
  FinsTcpHeader[3] = 0x53;       // "S"
  FinsTcpHeader[4] = 0x00;
  FinsTcpHeader[5] = 0x00;
  FinsTcpHeader[6] = 0x00;
  FinsTcpHeader[7] = 0x0C;
  FinsTcpHeader[8] = 0x00;
  FinsTcpHeader[9] = 0x00;
  FinsTcpHeader[10] = 0x00;
  FinsTcpHeader[11] = 0x00;
  FinsTcpHeader[12] = 0x00;
  FinsTcpHeader[13] = 0x00;
  FinsTcpHeader[14] = 0x00;
  FinsTcpHeader[15] = 0x00;
  FinsTcpHeader[16] = 0x00;
  FinsTcpHeader[17] = 0x00;
  FinsTcpHeader[18] = 0x00;
  FinsTcpHeader[19] = 0x00;

  ns.Write(FinsTcpHeader, 0, FinsTcpHeader.Length);

  byte[] resHeader = new byte[256];
  int resHeaderSize = ns.Read(resHeader, 0, resHeader.Length);

  if ((resHeader[8] == 0x00 && resHeader[9] == 0x00) &&
     (resHeader[10] == 0x00 && resHeader[11] == 0x01))
  {

    byte ClientNodeNo = resHeader[19];
    byte ServerNodeNo = resHeader[23];


    //FINS-TCPヘッダ+FINSフレームの送信
    byte[] cmd = new byte[34];
    // ----------------- FINS-TCPヘッダ
    cmd[0] = 0x46;          // "F"
    cmd[1] = 0x49;          // "I"
    cmd[2] = 0x4E;          // "N"
    cmd[3] = 0x53;          // "S"
    cmd[4] = 0x00;          // Length
    cmd[5] = 0x00;
    cmd[6] = 0x00;
    cmd[7] = 18 + 8;        // (Command以降のバイト数)
    cmd[8] = 0x00;          // Command
    cmd[9] = 0x00;
    cmd[10] = 0x00;
    cmd[11] = 0x02;
    cmd[12] = 0x00;         // ErrorCode
    cmd[13] = 0x00;
    cmd[14] = 0x00;
    cmd[15] = 0x00;
    // ----------------- FINSヘッダ
    cmd[16] = 0x80;          // ICF
    cmd[17] = 0x00;          // RSV
    cmd[18] = 0x02;          // GCT
    cmd[19] = 0;             // DNA  相手先ネットワークアドレス
    cmd[20] = ServerNodeNo;  // DA1  相手先ノードアドレス
    cmd[21] = 0;             // DA2  相手先号機アドレス
    cmd[22] = 0;             // SNA  発信元ネットワークアドレス
    cmd[23] = ClientNodeNo;  // SA1  発信元ノードアドレス
    cmd[24] = 0;             // SA2  発信元号機アドレス
    cmd[25] = 1;             // SID  識別子 00-FFの任意の数値
    // ----------------- FINSコマンド
    cmd[26] = 0x01;         // MRC  読出しコマンド 0101
    cmd[27] = 0x01;         // SRC
    cmd[28] = 0x82;         // MemoryType = DM
    cmd[29] = 0x00;         // ReadAddress = 100CH
    cmd[30] = 0x64;
    cmd[31] = 0x00;
    cmd[32] = 0x00;         // ReadSize = 10CH
    cmd[33] = 0x0A;

    ns.Write(cmd, 0, cmd.Length);

    byte[] resCmd = new byte[2048];
    int resCmdSize = ns.Read(resCmd, 0, resCmd.Length);

    if ((resCmd[8] == 0x00 && resCmd[9] == 0x00) &&
        (resCmd[10] == 0x00 && resCmd[11] == 0x02))
    {
      byte[] rcvData = new byte[resCmdSize];
      Array.Copy(resCmd, rcvData, resCmdSize);

      textBox1.AppendText(BitConverter.ToString(rcvData) + "\r\n");
    }
  }
  ns.Close();
  tcp.Close();
}
							

解説

3-4行目


System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient("192.168.250.1", 9600);
System.Net.Sockets.NetworkStream ns = tcp.GetStream();
								

PCのIPアドレスとTCPポートNoを指定してTCPクライアントを作成します
データ送受信で使用するネットワークストリームをGetStreamで作成します

6-7行目


ns.ReadTimeout = 1000;
ns.WriteTimeout = 1000;
								

読み取りと書き込みのタイムアウト時間を1000msに設定します

9-30行目


// FINS ノードアドレス情報送信コマンド
byte[] FinsTcpHeader = new byte[20];
FinsTcpHeader[0] = 0x46;       // "F"
~
FinsTcpHeader[19] = 0x00;
								

FINSノードアドレス情報送信コマンドを送信するバイト配列にセットします
FINS/TCPでは空いているFINSノードアドレスを取得するためにこのコマンドを送信します
詳しくはFINS/UDPとFINS/TCPの比較を参照してください

32行目


ns.Write(FinsTcpHeader, 0, FinsTcpHeader.Length);
								

FINSノードアドレス情報送信コマンドを送信します

34-35行目


byte[] resHeader = new byte[256];
int resHeaderSize = ns.Read(resHeader, 0, resHeader.Length);
								

FINSノードアドレス情報送信コマンドに対するレスポンスを受信します

37-38行目


if ((resHeader[8] == 0x00 && resHeader[9] == 0x00) &&
	 (resHeader[10] == 0x00 && resHeader[11] == 0x01))
								

レスポンスのFINS/TCPヘッダのCommandの項目の値が0x00000001のときに処理を進めます

41-42行目


byte ClientNodeNo = resHeader[19];
byte ServerNodeNo = resHeader[23];
								

レスポンスの19byte目を発信元(Client)ノード番号、23byte目を相手先(server)ノード番号として格納します

45-83行目


//FINS-TCPヘッダ+FINSフレームの送信
byte[] cmd = new byte[34];
// ----------------- FINS-TCPヘッダ
cmd[0] = 0x46;          // "F"
~
cmd[33] = 0x0A;
								

FINS-TCPヘッダを含んでFINSコマンドを送信用バイト配列にセットします
ここではDM100から10CH分のデータを読み出すコマンドをセットしています
FINSコマンドの内容についてはFINSコマンドを参照してください

85行目


ns.Write(cmd, 0, cmd.Length);
								

FINSコマンドを送信します

87-88行目


byte[] resCmd = new byte[2048];
int resCmdSize = ns.Read(resCmd, 0, resCmd.Length);
								

FINSコマンドに対するレスポンスを受信します

90-97行目


if ((resCmd[8] == 0x00 && resCmd[9] == 0x00) &&
		(resCmd[10] == 0x00 && resCmd[11] == 0x02))
{
	byte[] rcvData = new byte[resCmdSize];
	Array.Copy(resCmd, rcvData, resCmdSize);

	textBox1.AppendText(BitConverter.ToString(rcvData) + "\r\n");
}
								

レスポンスのFINS/TCPヘッダのCommandの項目の値が0x00000002のときにレスポンスデータをrcvDataに格納してテキストボックス(txtBox1)に追加書込みします

99-1000行目


ns.Close();
tcp.Close();
								

ネットワークストリームとTCPクライアントのインスタンスを破棄します


サンプルソース

GitHub SampleFins

GitHubからここで紹介したソースがダウンロードできます


この記事へのコメント