1. TOP
  2. python
  3. PLC通信
  4. OMRON Fins通信

概略

OMRONの通信プロトコルのFINSコマンドをpythonで送ります
FINSコマンドはメッセージサービス用の通信コマンドでPC-PLC間やPLC-デバイス間で使用されています
ここではpythonを使ってEthernetでPLCとメッセージ通信するクラスを作りました

動作環境

動作確認環境: Win10
python3.9
PLC OMRON CJ/NJ/NX series

構成

ファンクション一覧

メソッド内容
readメモリエリアの読み出し
writeメモリエリアへ書き込み
fillメモリエリアの一括書き込み
run動作モード切り替え
stopプログラムモードに切り替え
ReadUnitDataCPUユニット情報の読出し
ReadUnitStatusメモリエリアの読み出し
ReadCycletimeメモリエリアの読み出し
Clock時間情報の読出し
SetClock時間情報の書込み
ErrorClear異常解除
ErrorLogRead異常履歴の読出し
ErrorLogClear異常履歴のクリア
SendCommandその他のFINSコマンドを送信

オブジェクトの作成

fins(IPAddress, 相手FinsAddress, 自分FinsAddress)

パラメータ
IPAddress接続先IPアドレス
相手FinsAddress接続先Finsアドレス
自分FinsAddress接続元Finsアドレス

戻り値
戻り値finsオブジェクト

finsudp = fins('192.168.250.1', '0.1.0', '0.5.0')

finsクラスは通信相手のIPアドレス, 相手のFINSアドレス, 自分のFINSアドレスを指定してオブジェクトを作ります
FINSアドレスを省略すると相手のIPアドレスの下1桁の数値をノードとして相手先FINSアドレスとします

FINSアドレス

FINSアドレスとは 「FINSネットワークアドレス . ノードアドレス . 号機アドレス」の3つの数字を . (ドット)でつなぎます
ネットワークアドレスは直接接続することを想定して 0 (自ネットワーク)にしています
ノードアドレスはIPアドレスの一番下の数値と合わせて受信側(PLC)は 0.1.0 を、送信側(PC)はIPアドレスは192.168.250.5としているので 0.5.0 としています
号機アドレスは 0 (CPU宛て)とします
詳しくはFINSアドレスの説明を確認してください

メソッド

read(address, num)

I/Oメモリエリアの読出 (01 01)

パラメータ
addressメモリアドレス
num読出し要素数

戻り値
戻り値読出しデータ(バイト配列)

読み出したデータはデータ変換メソッドで変換してください

メモリアドレス表記方法

メモリアドレスは以下のように記載します
メモリ種別記述例
数値CIO10 = 10CH
WWRW10 = WR10
HHRH10 = HR10
DDMD10 = DM10
E0_ ~ EF_EM0 ~ EMFE0_10 = EM0_10

# E0_30000から10CH読出し
data = finsudp.read('E0_30000', 10)

上で作ったオブジェクトのreadメソッドでアドレスとCH数を指定して値を読み出します
アドレスはCIOは000、DMはD0、EMはE0_0、WRはW0のように指定します
読み出したデータはバイト列(bytes)で受け取ります


write(address, data)

メモリエリアの書込み (01 02)

パラメータ
addressメモリアドレス
data書込みデータ (バイト配列)

戻り値
0102終了コード

正常時の終了コード

01020000

# E0_0からdataを書込み
rcv = finsudp.write('E0_0', data)

fill(address, num, data)

メモリエリアの一括書込み (01 03)

連続したメモリエリアに同一のデータを書き込みます

パラメータ
addressメモリアドレス
num書込み要素数
data書込みデータ

戻り値
0103終了コード

# D110から10CH分(D110-119)に55を書込み
rcv = finsudp.fill('D110', 10, 55)

multiRead(addresses)

メモリエリアの複合読出 (01 04)

パラメータ
addressメモリアドレス カンマ区切り (MAX167)

戻り値
戻り値読出しデータ(バイト配列)

# 複合読出し D1000,D1010,D1020
data = finsudp.multiRead('D1000, D1010, D1020')

run(mode)

動作モード切り替え (04 01)

# 動作モード切り替え (0x02=Monitor 0x04=Run)
rcv = finsudp.run(0x02)

stop()

プログラムモードに切り替え (04 02)

# プログラムモードに切り替え
rcv = finsudp.stop()

ReadUnitData()

CPUユニット情報の読出し (05 01)

戻り値
byte数420204012
内容05 01 00 00CPUユニットの形式システムバージョンシステム情報エリア情報

# CPUユニット情報の読出し
rcv = finsudp.ReadUnitData()

ReadUnitStatus()

ユニット情報の読出 (06 01)

戻り値

byte数411222216
内容06 01 00 00運転状態動作モード運転停止異常情報運転継続異常情報メッセージ有無故障コード異常メッセージ

# CPUユニットステータスの読出し
rcv = finsudp.ReadUnitStatus()

ReadCycletime()

サイクルタイム読出 (06 20)

戻り値

byte数4444
内容06 20 00 00平均最大最小

# サイクルタイム読出し
rcv = finsudp.ReadCycletime()

Clock()

時間情報の読出 (07 01)

戻り値 type:datetime.datetime

# 時間情報の読出し
rcv = finsudp.Clock()

SetClock(datetime)

時間情報の書込み(PCの時間を書込み)

# 時間情報の書込み(PCの時間を書込み)
rcv = finsudp.SetClock(datetime.now())

ErrorClear()

発生中の異常を全解除 (21 01)

# 異常解除
rcv = finsudp.ErrorClear()

ErrorLogRead()

異常履歴の読出し (21 02)

戻り値

byte数422210...10
内容21 02 00 00レコード最大数格納数読出しレコード数異常履歴データ...異常履歴データ

# 異常履歴の読出し 最新10件
rcv = finsudp.ErrorLogRead()

ErrorLogClear()

異常履歴のクリア (21 03)

# 異常履歴のクリア
rcv = finsudp.ErrorLogClear()

SendCommand(cmd)

FINSコマンドを直接送信

cmd = bytearray([0x05,0x01])
rcv = finsudp.SendCommand(cmd)

データ変換用メソッド

print (finsudp.toInt16(data))

受け取ったデータのバイト列(bytes)を変換するためのメソッド
バイト列のデータがどのようなデータかによってメソッドが変わります

メソッド名変換するデータの長さ
toBinビット列
WordtoBin16ビット単位のビット列
toInt1616bit数値
toUInt1616bit符号なし
toInt3232bit数値
toUInt3232bit符号なし
toInt6464bit数値
toUInt6464bit符号なし
toFloat浮動小数点
toDouble倍精度
toString文字列

使用例

# Sample
# インスタンス作成
# NXではポート2を使用
finsudp = fins('192.168.251.1', '0.1.0', '0.5.0')	#接続先IPアドレス, 接続先FINSアドレス, 自分FINSアドレス

# 0CHから5CH分読出し  ビット表記
data = finsudp.read('0', 1)
print(finsudp.toBin(data))                  # ゼロサプレス表記
print(finsudp.WordToBin(data))              # ゼロ埋め表記
print(list(finsudp.WordToBin(data)))        # ゼロ埋めのリスト

# W0から5CH分読出し  ビット表記
data = finsudp.read('W0', 2)
print(finsudp.toBin(data))                # out> 100010000000000010010
print(finsudp.WordToBin(data))            # out> 00000000000100010000000000010010
print(list(finsudp.WordToBin(data)))      # out> ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '0', '0', '0', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '0', '0', '1', '0']

# H0から5CH分読出し  ビット表記
data = finsudp.read('H0', 4)
print(finsudp.toBin(data))
print(finsudp.WordToBin(data))
print(list(finsudp.WordToBin(data)))

# D1000から1CH分のデータを読出し ビット表記
data = finsudp.read('D1000', 1)
print(finsudp.toBin(data))                  # out> 11110000011
print(finsudp.WordToBin(data))              # out> 0000011110000011
print(list(finsudp.WordToBin(data)))        # out> ['1', '1', '1', '1', '0', '0', '0', '0', '0', '1', '1']

# D1001を読出しINT
data = finsudp.read('D1001', 1)
print(finsudp.toInt16(data))                #out> [2229]

# D1002-D1003を読出しDINT
data = finsudp.read('D1002', 2)
print(finsudp.toInt32(data))                # out> [-99694]

# D1004-D1007を読出しLINT
data = finsudp.read('D1004', 4)
print(finsudp.toInt64(data))                # out> [-19999999694]

# D1008を読出しUINT
data = finsudp.read('D1008', 1)
print(finsudp.toUInt16(data))               # out> [2233]

# D1009-D1010を読出しUDINT
data = finsudp.read('D1009', 2)
print(finsudp.toUInt32(data))               # out> [100217]

# D1011-D1014を読出しULINT
data = finsudp.read('D1011', 4)
print(finsudp.toUInt64(data))               # out> [2000000149]

# D1015-D1016を読出しFLOAT
data = finsudp.read('D1015', 2)
print(finsudp.toFloat(data))                # out> [229.90484619140625]

# D1017-D1020を読出しDOUBLE
data = finsudp.read('D1017', 4)
print(finsudp.toDouble(data))               # out> [230.89999999999117]

# D1021-D1025を読出しSTRING
data = finsudp.read('D1021', 5)
print(finsudp.toString(data))               # out> ABCD2229


# D1100から10CH分のデータを読出し
data = finsudp.read('D1100', 10)
print(finsudp.toUInt16(data))               # out> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

# E0_0から上で読み出したデータを10CH分を書込み
rcv = finsudp.write('E0_0', data)
print(rcv)                                  # out> b'\x01\x02\x00\x00'

# D110から10CH分に55を書込み
rcv = finsudp.fill('E0_100', 10, 55)
print(rcv)                                  # out> b'\x01\x03\x00\x00'

# 複合読出し D1000,D1010,D1020
data = finsudp.multiRead('D1000, D1010, D1020')
print(finsudp.toUInt16(data))               # out> [456, 1092, 4096]

# モニタモードに切り替え (0x02=Monitor 0x04=Run)
rcv = finsudp.run(0x02)
print(rcv)                                 # out> b'\x04\x01\x00\x00'

# プログラムモードに切り替え
rcv = finsudp.stop()
print(rcv)                                 # out> b'\x04\x02\x00\x00'

# CPUユニット情報の読出し
rcv = finsudp.ReadUnitData()
print(rcv)

# CPUユニットステータスの読出し
rcv = finsudp.ReadUnitStatus()
print(rcv)

# サイクルタイム読出し
rcv = finsudp.ReadCycletime()
print(rcv)

# 時間情報の読出し
rcv = finsudp.Clock()
print(rcv)

# 時間情報の書込み(PCの時間を書込み)
rcv = finsudp.SetClock(datetime.now())
print(rcv)

# 異常解除
rcv = finsudp.ErrorClear()
print(rcv)

# 異常履歴の読出し 最新10件
rcv = finsudp.ErrorLogRead()
print(rcv)

# 異常履歴のクリア
rcv = finsudp.ErrorLogClear()
print(rcv)


# その他のFINSコマンドを送信するときはこちら
# 例)0x05 0x01 0x01 CPUユニット情報の読出し
cmd = bytearray([0x05,0x01])
rcv = finsudp.SendCommand(cmd)
print(rcv)
		

Qiita記事

OMRON FINSコマンドをPythonで送信する

GitHub

GitHubからダウンロードできます

GitHub FinsCommand

この記事へのコメント