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 | プログラムモードに切り替え |
| ReadUnitData | CPUユニット情報の読出し |
| ReadUnitStatus | メモリエリアの読み出し |
| ReadCycletime | メモリエリアの読み出し |
| Clock | 時間情報の読出し |
| SetClock | 時間情報の書込み |
| ErrorClear | 異常解除 |
| ErrorLogRead | 異常履歴の読出し |
| ErrorLogClear | 異常履歴のクリア |
| SendCommand | その他のFINSコマンドを送信 |
オブジェクトの作成
fins(IPAddress, 相手FinsAddress, 自分FinsAddress)
パラメータ
| IPAddress | 接続先IPアドレス |
| 相手FinsAddress | 接続先Finsアドレス |
| 自分FinsAddress | 接続元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)
読み出したデータはデータ変換メソッドで変換してください
メモリアドレス表記方法
メモリアドレスは以下のように記載します
| メモリ種別 | 記述例 |
| 数値 | CIO | 10 = 10CH |
| W | WR | W10 = WR10 |
| H | HR | H10 = HR10 |
| D | DM | D10 = DM10 |
| E0_ ~ EF_ | EM0 ~ EMF | E0_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 | 書込みデータ (バイト配列) |
戻り値
正常時の終了コード
例
# E0_0からdataを書込み
rcv = finsudp.write('E0_0', data)
fill(address, num, data)
メモリエリアの一括書込み (01 03)
連続したメモリエリアに同一のデータを書き込みます
パラメータ
| address | メモリアドレス |
| num | 書込み要素数 |
| data | 書込みデータ |
戻り値
例
# 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数 | 4 | 20 | 20 | 40 | 12 |
| 内容 | 05 01 00 00 | CPUユニットの形式 | システムバージョン | システム情報 | エリア情報 |
例
# CPUユニット情報の読出し
rcv = finsudp.ReadUnitData()
ReadUnitStatus()
ユニット情報の読出 (06 01)
戻り値
| byte数 | 4 | 1 | 1 | 2 | 2 | 2 | 2 | 16 |
| 内容 | 06 01 00 00 | 運転状態 | 動作モード | 運転停止異常情報 | 運転継続異常情報 | メッセージ有無 | 故障コード | 異常メッセージ |
例
# CPUユニットステータスの読出し
rcv = finsudp.ReadUnitStatus()
ReadCycletime()
サイクルタイム読出 (06 20)
戻り値
| byte数 | 4 | 4 | 4 | 4 |
| 内容 | 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数 | 4 | 2 | 2 | 2 | 10 | ... | 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 | ビット列 |
| WordtoBin | 16ビット単位のビット列 |
| toInt16 | 16bit数値 |
| toUInt16 | 16bit符号なし |
| toInt32 | 32bit数値 |
| toUInt32 | 32bit符号なし |
| toInt64 | 64bit数値 |
| toUInt64 | 64bit符号なし |
| 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からダウンロードできます
FinsCommand