テクセル
Rubyでシーケンサ固定バッファ通信(Qシリーズ)

三菱電機(株)製シーケンサ(PLC)QシリーズのEthernetインターフェースユニットとRubyにより固定バッファ通信を行います。 実行環境は、以下となります。

項目 内容
Rubyのバージョン  ruby 2.4.0p0 (ActiveScriptRuby)
対象としたユニット Ethernetインターフェースユニット QJ71E71-100
通信手順  固定バッファ交信

固定バッファ通信では、PLC側にも送信、受信用のソフトが必要です。マニュアルに従い作成して下さい。 また、下記プログラ例では、「wincons.rb」が必要です。

1.EthernetインターフェースユニットQJ71E71-100での設定

GX Works2によりネットワークパラメータEthernet/CC IE/MELSECNETで設定します。
ここでは、TCPの手順あり、バイナリコードに設定します。

1)動作設定

項目 内容
交信データコード設定 「バイナリコード交信」を選択
イニシャルタイミング設定 「常にOPEN待ち(STOP中交信可能)」を選択
IPアドレス設定 入力形式「10進数」を選択
IPアドレスをここでは、「192.168.1.88」と設定します。
送信フレーム設定 「Ethernet(V2.0)」を選択
RUN中書き込み チェックマークあり(許可)とします。
TCP生存確認設定 「KeepAliveを使用」を選択

2)オープン設定

コネクション1を受信用,コネクション2を送信用として以下のように設定します。

項目 1(コネクション1) 2(コネクション2)
プロトコル 「TCP」を選択 「TCP」を選択
オープン方式 「Unpassive」を選択 「Active」を選択
固定バッファ 「受信」を選択 「送信」を選択
固定バッファ交信手順 「手順あり」を選択 「手順あり」を選択
ペアリングオープン 「ペアにしない」を選択 「ペアにしない」を選択
生存確認 「確認しない」を選択 「確認しない」を選択
自局ポート番号 「2000」(10進) 「2001」(10進)
交信相手IPアドレス 設定不要 「192.168.1.80」
交信相手ポート番号 設定不要 「12000」

オープン設定

オープン設定

2.送信、受信個別コネクション バイナリコード

パソコンのIPアドレスをオープン設定の交信相手IPアドレスと同じにして下さい。上記設定では、192.168.1.80です。

1)プログラム例 PLC(コネクション2)→パソコン(ruby)

パソコン側がサーバとなりPLCからの送信データを受信します。下記ソフトを実行後、PLCから送信を行って下さい。 下記ソフトは、クライアント接続数が1です。'q'キーで終了します。
PLCからデータ数3でデータ「1234,5678,9012」を送信すると下記受信データ変数radは、[96, 3, 1234, 5678, 9012]と なります。96(0x60)は、サブヘッダ値です。


# coding: utf-8
# 固定バッファ交信サーバ 手順あり バイナリ
require "socket"
require "./wincons"

cons1 = Console.new(__ENCODING__)

svr = TCPServer.open("", 12000)        # TCPサーバ ポート番号12000番でopen
#p svr
sock = nil
bcon = false
srcv = ""

th_acp = Thread.start{
   loop do
      sock = svr.accept                # PLCからの接続を受け付ける
      bcon = true
      srcv = ""
      puts "接続"
      while bcon == true do
         sleep 0.01
      end
   end
}

th_rcv = Thread.start{
   loop do
      while bcon == true do
         srcv = sock.recv(128)         # 受信
         if srcv == ""                 # 切断された?
            sock.close
            bcon = false
            puts "切断"
            break
         end
      end
      sleep 0.01
   end
}

loop do
   if bcon == true
      if srcv != ""
         rad = srcv.unpack("s*")       # 受信データ
         p rad
         ssd = ["E000"].pack("H*")
         sock.send(ssd,0)              # レスポンス送信
         p ssd
         srcv = ""
      end
   end

   ca, = cons1.inkey
   if ca == "q" || ca == "Q"
      break
   end
   sleep 0.01
end

sock.close
svr.close
th_acp.exit if th_acp.alive?
th_rcv.exit if th_rcv.alive?
   

2)プログラム例 パソコン(ruby)→PLC(コネクション1)

PLC側がサーバとなり、パソコン側からデータを送信します。


# coding: utf-8
# 固定バッファ交信クライアント 手順あり バイナリ
require "socket"

htcp = TCPSocket.open("192.168.1.88", 2000)      # PLC接続先

shd = "6000"                      # サブヘッダ
adata = [3, 9876, 5432, 1098]     # データ個数 + データ

sa = adata.pack("s*")
sdt = [shd].pack("H*") + sa
htcp.send(sdt,0)                  # 送信
p sdt

rdt = htcp.recv(20)               # レスポンス受信
p rdt

htcp.close
   

3.送信、受信個別コネクション ASCIIコード

ASCIIコード交信は、動作設定の交信データコード設定を「ASCIIコード交信」にします。

1)プログラム例 PLC(コネクション2)→パソコン(ruby)

上記固定バッファ交信サーバソフトの受信処理部を以下のようにします。


rad = srcv.scan(/.{1,4}/).map{|d| d.hex}  # 受信データ
p rad
ssd = "E000"
sock.send(ssd,0)                    # レスポンス送信
p ssd
   

2)プログラム例 パソコン(ruby)→PLC(コネクション1)

上記固定バッファ交信クライアントの送信処理部を以下のようにします。


sa = ""
adata.each{|d| sa += sprintf("%04X",d)}

sdt = shd + sa
htcp.send(sdt,0)                  # 送信
p sdt

rdata = htcp.recv(20)             # レスポンス受信
p rdata
   

4.ペアリングオープン バイナリ

ペアリングオープン設定によりポート番号は、送受信で同じ値となります。また。PLCをサーバとします。

1)オープン設定

コネクション1、2をペアリングオープンとして以下のように設定します。

項目 1(コネクション1) 2(コネクション2)
プロトコル 「TCP」を選択 「TCP」を選択
オープン方式 「Unpassive」を選択 「Unpassive」を選択
固定バッファ 「受信」を選択 「送信」を選択
固定バッファ交信手順 「手順あり」を選択 「手順あり」を選択
ペアリングオープン 「ペアにする」を選択 「ペアにする」を選択
生存確認 「確認しない」を選択 「確認しない」を選択
自局ポート番号 「2000」(10進) 「2000」(10進)
交信相手IPアドレス 設定不要 設定不要
交信相手ポート番号 設定不要 設定不要

オープン設定

オープン設定

2)プログラム例

PLC側がサーバとなり、パソコン(ruby)側からデータの送受信を行います。下記例では、受信処理は、スレッドで行っています。 また、sキーでデータ送信を行います。


# coding: utf-8
# 固定バッファ交信クライアント ペアリングオープン 手順あり バイナリ
require "socket"
require "./wincons"

cons1 = Console.new(__ENCODING__)
sock = TCPSocket.open("192.168.1.88", 2000)      # PLC接続
srcv = ""

th_rcv = Thread.start{            # 受信スレッド
   while srcv = sock.recv(128)
#      p srcv
   end
}

shd = "6000"                      # サブヘッダ
adata = [3, 9876, 5432, 1098]     # 送信データ データ個数 + データ

loop{
   if srcv != ""
      if srcv[0].ord == 0x60      # データ受信?
         rad = srcv.unpack("s*")  # 受信データ
         p rad
         ssd = ["E000"].pack("H*")
         sock.send(ssd,0)         # レスポンス送信
         p ssd
      end
      if srcv[0].ord == 0xE0      # レスポンス受信?
         print "レスポンス受信¥r¥n"
      end
      srcv = ""
   end

   ca, = cons1.inkey
   if ca == "s" || ca == "S"
      sa = adata.pack("s*")
      sdt = [shd].pack("H*") + sa
      sock.send(sdt,0)            # データ送信
      p sdt
   end
   if ca == "q" || ca == "Q"
      break
   end
   sleep 0.01
}

th_rcv.exit if th_rcv.alive?
sock.close
   
参考資料
・Q対応Ethernetインターフェースユニット ユーザーズマニュアル(基本編)(三菱電機(株))
Rubyユーティリティ
©2017 TEXCELL CORPORATION
テクセル株式会社