三菱電機(株)製シーケンサ(PLC)QシリーズのEthernetインターフェースユニットとRubyにより固定バッファ通信を行います。 実行環境は、以下となります。
項目 | 内容 |
---|---|
Rubyのバージョン | ruby 2.4.0p0 (ActiveScriptRuby) |
対象としたユニット | Ethernetインターフェースユニット QJ71E71-100 |
通信手順 | 固定バッファ交信 |
固定バッファ通信では、PLC側にも送信、受信用のソフトが必要です。マニュアルに従い作成して下さい。 また、下記プログラ例では、「wincons.rb」が必要です。
GX Works2によりネットワークパラメータEthernet/CC IE/MELSECNETで設定します。
ここでは、TCPの手順あり、バイナリコードに設定します。
項目 | 内容 |
---|---|
交信データコード設定 | 「バイナリコード交信」を選択 |
イニシャルタイミング設定 | 「常にOPEN待ち(STOP中交信可能)」を選択 |
IPアドレス設定 | 入力形式「10進数」を選択 IPアドレスをここでは、「192.168.1.88」と設定します。 |
送信フレーム設定 | 「Ethernet(V2.0)」を選択 |
RUN中書き込み | チェックマークあり(許可)とします。 |
TCP生存確認設定 | 「KeepAliveを使用」を選択 |
コネクション1を受信用,コネクション2を送信用として以下のように設定します。
項目 | 1(コネクション1) | 2(コネクション2) |
---|---|---|
プロトコル | 「TCP」を選択 | 「TCP」を選択 |
オープン方式 | 「Unpassive」を選択 | 「Active」を選択 |
固定バッファ | 「受信」を選択 | 「送信」を選択 |
固定バッファ交信手順 | 「手順あり」を選択 | 「手順あり」を選択 |
ペアリングオープン | 「ペアにしない」を選択 | 「ペアにしない」を選択 |
生存確認 | 「確認しない」を選択 | 「確認しない」を選択 |
自局ポート番号 | 「2000」(10進) | 「2001」(10進) |
交信相手IPアドレス | 設定不要 | 「192.168.1.80」 |
交信相手ポート番号 | 設定不要 | 「12000」 |
オープン設定
パソコンのIPアドレスをオープン設定の交信相手IPアドレスと同じにして下さい。上記設定では、192.168.1.80です。
パソコン側がサーバとなり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?
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
ASCIIコード交信は、動作設定の交信データコード設定を「ASCIIコード交信」にします。
上記固定バッファ交信サーバソフトの受信処理部を以下のようにします。
rad = srcv.scan(/.{1,4}/).map{|d| d.hex} # 受信データ
p rad
ssd = "E000"
sock.send(ssd,0) # レスポンス送信
p ssd
上記固定バッファ交信クライアントの送信処理部を以下のようにします。
sa = ""
adata.each{|d| sa += sprintf("%04X",d)}
sdt = shd + sa
htcp.send(sdt,0) # 送信
p sdt
rdata = htcp.recv(20) # レスポンス受信
p rdata
ペアリングオープン設定によりポート番号は、送受信で同じ値となります。また。PLCをサーバとします。
コネクション1、2をペアリングオープンとして以下のように設定します。
項目 | 1(コネクション1) | 2(コネクション2) |
---|---|---|
プロトコル | 「TCP」を選択 | 「TCP」を選択 |
オープン方式 | 「Unpassive」を選択 | 「Unpassive」を選択 |
固定バッファ | 「受信」を選択 | 「送信」を選択 |
固定バッファ交信手順 | 「手順あり」を選択 | 「手順あり」を選択 |
ペアリングオープン | 「ペアにする」を選択 | 「ペアにする」を選択 |
生存確認 | 「確認しない」を選択 | 「確認しない」を選択 |
自局ポート番号 | 「2000」(10進) | 「2000」(10進) |
交信相手IPアドレス | 設定不要 | 設定不要 |
交信相手ポート番号 | 設定不要 | 設定不要 |
オープン設定
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