Siemens#Modbus TCP serverとして使う

今回はSiemensのPLCでModbus TCP serverを使うときの手順を説明します。
ここにも文章が乗っています↓。
http://soup01.com/ja/2019/12/24/post-1948/

# TIAバージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/ce119728-ea7b-6001-101e-7aeb254352f2.png)
# 実機バージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/39222b49-e65d-4def-8f2d-c0cec08cf404.png)

# Function Block
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c090bd22-bc8a-c6e1-f816-6b657ac1d041.png)

ではまずF1でHelpをみましょう。

このMB_SERVER命令はModbus TCP serverとして動作しProfinet接続に通じてModbus TCP Clientと接続、送信、通信とModbusリクエストを処理することができます。

##S7-1200
- FirmwareV4.0ではこの命令のバージョンV3.1まで使えます。
- FirmwareV4.1ではこの命令バージョン全て使えます。


## S7-1500
- この命令バージョン全て使えます。

！注意

セキュリティ上ではModbus ClientはModbus holding register上のメモリエリア全て読み書きすることができます。なので、ServerにアクセスできるClientのIPアドレスは制限するのはおすすめです。

##INPUT
- DISCONNECT：Bool
    - 0：Passive Connectionが確立する。
    - 1：Connectionは“TCON_IP_v4”に元にいて設定初期化する。初期化成功したらSTATUSは3にアウトプットされます。


##InOut
- MB_HOLD_REG：Variant
    - Siemensを使っている人にもVariantに対してわけわからない人も多いと思いますが、私いつも“なんでもあり”ということを説明します。つまり、この変数は結局単なるBoolでもいいし、構造体でもいいし、配列でもいいし、なんでも変数として扱えるってことです。
このFunctionのVariantほしいのはMemory Areaです。Modbus サーパーとして働いてるから当然Clientから読み書きしたいMemory Areaがあります。例えばParameterなど…
    - 注意：
        - Memory Area最低２Byteにしましょう。
        - アクセスできるのはModbus Function 3（よみ）、６（書き）、１６（マルチ書き）と２３（一つJobでマルチ書き）だけになります。

- CONNECT：Variant
    - このCONNECTパラメータ使えいるのSDT(System Data Type)は二種類があります。
        - TCON_IP_V4
            - 通信成立するため必要なネットワークパラメータが含められてる。Defaultアドレスは0.0.0.0です。もちろんプログラム上で変更することが可能です。このSDTを使うと、通信成立するには“MB_SERVER”が呼びされてるときです。
        - TCON_Configured
            - アドレスパラメータのみが入っています。もしこのSDTを使うと、通信成立するにはCPUがハードウェア構成をダウンロードされたあとになります。
        
##Output
- NDR
    - “New Data Ready”
        - 0：新しいデータが来てません。
        - 1：新しいデータが来ています(つまりClientから書き込みあります)。
- DR
    - “Data Read”
        - 0：データが読み込まれいません。
        - 1：データがClientに読み込まれいます。
- ERROR
    - MB_SERVERが実行してるときにエーラがあれば1になります。STATUSにはそのエラーコードが帰っています。
- STATUS
    - MB_SERVERの実行状態が見えます。


##TCON_IP_V4とは？
まずData Typeのところに“TCON_IP_v4”を入れれば、自動的変数が作られいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/2c98a0e6-c4e0-093e-d718-9a47d533ad8a.png)

- InterfaceId
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/1c9e34f9-be65-69b2-09a1-3c59fdc96c1e.png)
    - あなたがModbus TCP/IPに使う予定のPortのIDです。今回はS7-1200のPortを使うので、64ですね。
- ID
    - 1個目のMB_SERVERはIDを1にしましょう。もし一台のCPUの中に複数のMB_SERVERがあればこのIDをかぶれないようにしましょう。
- ConnectionType
    - 11はTCP/IP、19はUDP
- ActiveEstablished
    - いわゆるActiveかPassiveですね。今回はPassiveにします。
- RemoteAddress
    - 長さ4のByte配列です。ここで接続しようとするClientのIPを設定できます。0.0.0.0ならどこのClientでも接続できます。
- RemotePort
    - 相手のPortです。もしClientに特にPortのアクセス制限がなければ0にいいでしょう 。
- LocalPort
    - 自分のPortで接続されたClientと通信を監視するPortになります。
    

これで、パラメータある程度把握できましたね。

##実装
これは私のやりかたですが、こういう構造体（Variant）が必要されるとき、もしパラメータそんなに多くなければいつも細かくばらします。つまりこうですね。これは私が作ったClass_ModbusServerのFunction Blockです。
Inputのところが多いと思われますが、よくみたら単なるTCON_IP_V4とMB_SERVERの変数だけです。そしてOutputはMB_SERVERの出力だけですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/9d7bdacc-0b5c-881b-c0ac-7e2be1e78311.png)

注意するのはioMB_HOLD_REGですね。ここに入れるのはP#DB3.DBX0.0です。このDB中には51のIntが入っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/5d92ddee-74e4-4ffe-9eab-cbe6cfd81d8b.png)

###Input Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/0b6d1951-e959-6821-7460-5e550c153ce1.png)
###Output Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f3a6ebe8-3277-b2d9-2e19-1d13787b6512.png)

###InOut Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/56b30726-b0a1-eb87-2a1e-8c7ef9896d94.png)

###Static Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/b75fbf06-d518-ad99-14fc-5c74aea2931b.png)

####Network3-10
パラメータをCONNECT変数に転送するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/58ad7d0f-0657-03ab-9b8b-dd881479d793.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/255a1f6a-5349-b009-3ae4-d41749e4b664.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/b75db873-f779-76e0-63ee-c99b263d8e25.png)

####Network11
MB_SERVERを呼び出します。先Network3-10まで転送した変数CONNECTをこおで使えいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c139f7a2-fcb1-291e-6bea-21d22e6a3b86.png)

####Network13　
DISCONNECT=1はつまりパラメータ初期化すること。ここで読み書きのCounterをリセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c951b5f9-43de-f166-d455-37090062a5e5.png)


####Netowork14
まぁ、Clientにデータを読まれたら＋１するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/55ab3dd4-d044-5d5f-1c67-465ea7079b5c.png)


####Network15
まぁ、Clientがデータを書き込んでたときを＋１するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f686e622-b923-95b6-2c6c-af6f046e17e0.png)


####Network17-20
MB_SERVERの実行状態を出力するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/06053efd-cddf-3500-52a8-3c0bf766e87c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/1cf3e24d-12a6-176e-c953-67f20edf8934.png)





お疲れ様です！と言いたいですが、せっかくですからここでPythonを使ってModbus TCP Clientを作ってアクセスしてみよう。

#PythonでModbus TCP/IPクライアント

##使用するOSとバージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/10042933-a39c-d290-8320-415e12c9d8a4.png)

##実装
```python
#Library Import
from pyModbusTCP.client import ModbusClient
import time
import random

#Host,Port Settings
SERVER_HOST='192.168.0.17'
SERVER_PORT=502

#Client
client=ModbusClient()

#Debug
client.debug(False)

#Config
client.host(SERVER_HOST)
client.port(SERVER_PORT)

#Connect
client.open()

#Read and Write
while True:
    if not client.is_open():
        if not client.open():
            print('Can not connect to {},{}'.format(SERVER_HOST,SERVER_PORT))
    if client.is_open():
        regs=client.read_holding_registers(0,10)
        if regs:
            print('data:'+str(regs))
        else:
            print('can not read.')
        
        reg_list=[random.randint(1,32000) for i in range(10)]
        
        regs=client.write_multiple_registers(0,reg_list)
        
        if regs:
            print('data is sent.')

        else:
            print('data can not sent')
    time.sleep(0.02)

#Disconnect
client.close()
```
ではーお疲れ様です！