Beckhoff TwinCAT TF6310 では複数のTCP/IP Server/Client の実装をSupportできます。
- PLC library: Tc2_TcpIp library (basic TCP/IP and UDP/IP function)
- Background program: TwinCAT TCP/IP Connection Server
この記事ではまずTCP/IP ClientをTwinCAT上でどう実装するかを説明し、PythonのServerとやりとりを書きたいと思います。
よろしくお願いします。
System Requirment
Installation
以下のLinkにアクセスしInstallation fileをダウンロードします。
言語を選びます。
Next>
ライセンスに同意し>Next>します。
情報を入力し、Next>します。
Complete>Next>.
Next>
Installします。
しばらくお待ちください。
Done!
インストールされたらTF6310-TCP-IPのDirectory が出てきます。
Adding Library
次はLibraryを追加します。Reference>Add Library.
FB_Socket か Tc2_TcpIP を検索しProjectをLibarayに入れてください。
Function Block
FB_SocketConnect
こちらのFunction Blockを使用しTCP/IP Connectionを確立できます。
hSocketという変数が、Server接続に成功するときに出力します。そのhSocketはFB_SocketSend()、FB_SocketReceive()、 FB_SocketClose()のFunction BlockでInputパラメタとして必要です。
Serverは各Connectionに自動的にPort番号を振り分けます。
VAR_INPUT
Name | Type | Description |
sSrvNetId | T_AmsNetId := ”; | TwinCAT TCP/IP ServerのNetIdです。空文字ならLocal Serverになります。 |
sRemoteHost | T_IPv4Addr := ”; | ServerのIP |
nRemotePort | UDINT | ServerのPort |
bExecute | BOOL | 立ち上げ=実行 |
tTimeout | TIME := T#45s;(*!!!*) | FBの最大実行時間 |
VAR_OUTPUT
Name | Type | Description |
bBusy | BOOL | 1=FBが実行されてServerのAckまちです。 |
bError | BOOL | 1=Error |
nErrId | UDINT | Error情報 |
hSocket | T_HSOCKET | TCP/IP Connection handle |
FB_SocketClose
TCP/IP or UDP socket connectionをCloseするFunction Blockです。
VAR_INPUT
Name | Type | Description |
sSrvNetId | T_AmsNetId := ”; | TwinCAT TCP/IP ServerのNetIdです。空文字ならLocal Serverになります。 |
sRemoteHost | T_IPv4Addr := ”; | ServerのIP |
nRemotePort | UDINT | ServerのPort |
bExecute | BOOL | 立ち上げ=実行 |
tTimeout | TIME := T#45s;(*!!!*) | FBの最大実行時間 |
VAR_OUTPUT
Name | Type | Description |
bBusy | BOOL | 1=FBが実行されてServerのAckまちです。 |
bError | BOOL | 1=Error |
nErrId | UDINT | Error情報 |
hSocket | T_HSOCKET | TCP/IP Connection handle |
FB_SocketSend
このFunction Blockを使用すればServerにPacketを送信できます。
VAR_INPUT
Name | Type | Description |
sSrvNetId | T_AmsNetId := ”; | TwinCAT TCP/IP ServerのNetIdです。空文字ならLocal Serverになります。 |
hSocket | T_HSOCKET | TCP/IP Connection handle |
cbLen | UDINT | 送信するByte数 |
pSrc | POINTER TO BYTE | 送信BufferのAddress(Pointer) |
bExecute | BOOL | 立ち上げ=実行 |
tTimeout | TIME := T#45s;(*!!!*) | FBの最大実行時間 |
VAR_OUTPUT
Name | Type | Description |
bBusy | BOOL | 1=FBが実行されてServerのAckまちです。 |
bError | BOOL | 1=Error |
nErrId | UDINT | Error情報 |
FB_SocketReceive
Serverからのデータを受信するFunction Blockです。bExecuteは立ち上げ実行なので、100msごとにTriggerしたほうが簡単に実装できます。
そしてServerから受信したBytesはnRecBytesに出力します。
VAR_INPUT
Name | Type | Description |
sSrvNetId | T_AmsNetId := ”; | TwinCAT TCP/IP ServerのNetIdです。空文字ならLocal Serverになります。 |
hSocket | T_HSOCKET | TCP/IP Connection handle |
cbLen | UDINT | 受信するByte数 |
pSrc | POINTER TO BYTE | 受信BufferのAddress(Pointer) |
bExecute | BOOL | 立ち上げ=実行 |
tTimeout | TIME := T#45s;(*!!!*) | FBの最大実行時間 |
VAR_OUTPUT
Name | Type | Description |
bBusy | BOOL | 1=FBが実行されてServerのAckまちです。 |
bError | BOOL | 1=Error |
nErrId | UDINT | Error情報 |
nReceBytes | UDINT | Serverからの受信Bytes数 |
FB_ClientServerConnection
TwinCATのLibraryにはもっと簡単なFunction BlockでClient-Server connectionを実装できます。こちらのFB_ClientServerConnectionです。中身ではFB_SocketOpen と FB_SocketCloseがすでに含まれております。一回このFBを呼び出すと、次はアプリケーションよりB_SocketSend と FB_SocketReceiveを使うだけです。
そのFunction Blockでは診断情報eStateとErrorIDも出力されています。
VAR_INPUT
Name | Type | Description |
sSrvNetId | T_AmsNetId := ”; | TwinCAT TCP/IP ServerのNetIdです。空文字ならLocal Serverになります。 |
nMode | DWORD:= 0; | Parameters Flag |
sRemoteHost | T_IPv4Addr := ”; | ServerのIP |
nRemotePort | UDINT | ServerのPort |
bEnable | BOOL | 1=接続開始し成功まではRetryする |
tReconnect | TIME := T#45s;(*!!!*) | 接続のRetry時間間隔 |
VAR_OUTPUT
Name | Type | Description |
bBusy | BOOL | 1=FBが実行されてServerのAckまちです。 |
bError | BOOL | 1=Error |
nErrId | UDINT | Error情報 |
hSocket | T_HSOCKET | TCP/IP Connection handle |
eState | E_SocketConnectionState := eSOCKET_DISCONNECTED; | 現在のConnection Status |
Error: 1828
もしFunction Blockが実行するときError1828が出てくると、それはライセンスがないことを示しています。
SYSTEM>License.
Manage License Taを開いて> T6310>Add License.
Trial Licenseが追加されました。
7 Days Trial Licenseをクリックします。
magic Codeを入力します。
License file 生成されました。
最後はConfigurationをDownloadすればOK。
Data type
T_HSOCKET
まずこの変数はSocketがOpenされたときに更新されます。以後この変数でopen/close/send/receive をやりとりします。
E_SocketConnectionState
TCP/IP Connectionの現在状態を示すDUTです。
TYPE E_SocketConnectionState: ( eSOCKET_DISCONNECTED, eSOCKET_CONNECTED, eSOCKET_SUSPENDED ); END_TYPE |
Server with Python
最初にPythonで簡単なServerとClientを作成し、自分のServerが本当にOKかどうかをCheckします。
Server Side
ServerがClientに接続し、Clientから受信したデータをそのまま返すだけです。
import socket HOST = “127.0.0.1” PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() conn, addr = s.accept() with conn: print(f”Connected by {addr}”) while True: data = conn.recv(1024) if not data: break conn.sendall(data) |
Client Side
Severに接続しHello worldを送信する。
import socket HOST = “127.0.0.1” PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) s.sendall(b”Hello, world”) data = s.recv(1024) print(f”Received {data!r}”) |
Result-Server
Connected by (‘127.0.0.1’, 51649) |
Result-Client
Received b’Hello, world’ |
Play with TwinCAT
ではPython serverがちゃんと動作してるので次はClientをTwinCAT TF6310 TCP/IPに置き換えます。
SocketConnect/Close
Program1-まずTwinCAT側がServerとつながるかをテストします。
Program
pou001_Connect2PythonServer
VAR
PROGRAM pou001_Connect2PythonServerVAR SocketConnect :FB_SocketConnect; SocketClose :FB_SocketClose; hSocket :T_HSOCKET; bConnect :BOOL; bClose :BOOL;END_VAR |
Code
SocketConnect( sRemoteHost:=’127.0.0.1′ ,nRemotePort:=65432 ,bExecute:=bConnect ,hSocket=>hSocket ); IF bConnect AND (NOT SocketConnect.bBusy OR SocketConnect.bError)THEN bConnect:=FALSE; END_IF SocketClose( hSocket:=hSocket ); IF bClose THEN bClose:=FALSE; END_IF |
Result-TwinCAT Side
hsocket変数ではClientとServerのIPやPortが見えます。
これは接続してるんですね。
Result-Python Server Side
Server側ではClientから接続したことも確認できました。
Connected by (‘127.0.0.1’, 51665) |
Send/Receive the Data
よし、次はTwinCATからデータを送受信してみます。
Modify the Python server
PythonのServerを少し変更します。応答がもう少しわかりやすいようにメッセージを変えます。
import socket HOST = “127.0.0.1” PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() conn, addr = s.accept() with conn: print(f”Connected by {addr}”) while True: data = conn.recv(1024) if not data: break print(“data from client:”.encode()+data) conn.sendall(data+”,Data back from Server”.encode()) |
Porgram
pou002_SendReceiveData2PythonServer
VAR
PROGRAM pou002_SendReceiveData2PythonServer VAR SocketConnect :FB_SocketConnect; SocketClose :FB_SocketClose; SocketSend :FB_SocketSend; SocketReceive :FB_SocketReceive; hSocket :T_HSOCKET; bConnect :BOOL; bClose :BOOL; bSend :BOOL; bReceive :BOOL; TON :TON; tRefleshTime :TIME:=T#100MS; SendBytes :ARRAY[0..1023]OF BYTE; ReceiveByte :ARRAY[0..1023]OF BYTE; stSendString :STRING; SendLen :UDINT; stReceiveString :STRING; R_TRIG_ByteReceived :R_TRIG; END_VAR |
Code
//Establish the connection to Python Server SocketConnect( sRemoteHost:=’127.0.0.1′ ,nRemotePort:=65432 ,bExecute:=bConnect ,hSocket=>hSocket ); IF bConnect AND (NOT SocketConnect.bBusy OR SocketConnect.bError)THEN bConnect:=FALSE; END_IF //Send message to Python Server //Configure the Sending message stSendString:=’Hello world from TwinCAT!’; SendLen:=LEN(stSendString); //Copy MEMCPY( destAddr:=ADR(SendBytes) ,srcAddr:=ADR(stSendString) ,n:=SendLen ); //Function Block SocketSend( hSocket:=hSocket ,cbLen:=SendLen ,pSrc:=ADR(SendBytes) ,bExecute:=bSend ); IF bSend AND (NOT SocketSend.bBusy OR SocketSend.bError) THEN bSend:=FALSE; END_IF //Receive message from Python Server //Reflesh Timer TON( IN:=NOT TON.Q ,PT:=tRefleshTime ); //Function Block SocketReceive( hSocket:=hSocket ,cbLen:=SIZEOF(ReceiveByte) ,pDest:=ADR(ReceiveByte) ,bExecute:=NOT TON.Q ); //If bytes are received R_TRIG_ByteReceived( CLK:=SocketReceive.nRecBytes > 0 ); IF R_TRIG_ByteReceived.Q THEN MEMCPY( destAddr:=ADR(stReceiveString) ,srcAddr:=ADR(ReceiveByte) ,n:=SocketReceive.nRecBytes ); END_IF //Close the Connection //Function Block SocketClose( hSocket:=hSocket ); IF bClose OR SocketSend.bError OR SocketReceive.bError THEN bClose:=FALSE; END_IF |
Result-TwinCAT Side
TwinCATがデータを送信し、Serverからの応答もちゃんと見えました!
Result-Python Server Side
Connected by (‘127.0.0.1’, 63751) b’data from client:Hello world from TwinCAT!’ b’data from client:Hello world from TwinCAT!’ |
Bonus-Use Connection Helper
最後にFB_ScoketOpenとFB_SocketCloseではなくFB_ClientServerConnection Helper function blockでConnectionを実装してみます。
Program
pou003_UsingConnectionHelper
VAR
PROGRAM pou003_UsingConnectionHelper VAR RemoteServer :FB_ClientServerConnection; SocketSend :FB_SocketSend; SocketReceive :FB_SocketReceive; hSocket :T_HSOCKET; bConnect :BOOL; bClose :BOOL; bSend :BOOL; bReceive :BOOL; TON :TON; tRefleshTime :TIME:=T#100MS; SendBytes :ARRAY[0..1023]OF BYTE; ReceiveByte :ARRAY[0..1023]OF BYTE; stSendString :STRING; SendLen :UDINT; stReceiveString :STRING(255); R_TRIG_ByteReceived :R_TRIG; R_TRIG_SendFlag :R_TRIG; CopiedBytes:UDINT; counter:INT; i:INT; END_VAR |
Code
//Establish the connection to Python Server RemoteServer( sRemoteHost:=’127.0.0.1′ ,nRemotePort:=61000 ,bEnable:=bConnect ,hSocket=>hSocket ); R_TRIG_SendFlag( CLK:=RemoteServer.eState = eSOCKET_CONNECTED ); IF R_TRIG_SendFlag.Q THEN bSend:=TRUE; END_IF //Send message to Python Server //Configure the Sending message stSendString:=’Hello world from TwinCAT TF6310!’; SendLen:=LEN(stSendString); //Copy MEMCPY( destAddr:=ADR(SendBytes) ,srcAddr:=ADR(stSendString) ,n:=SendLen ); //Function Block SocketSend( hSocket:=hSocket ,cbLen:=SendLen ,pSrc:=ADR(SendBytes) ,bExecute:=bSend ); IF bSend AND (NOT SocketSend.bBusy OR SocketSend.bError) THEN FOR i:=0 TO 1023 DO ReceiveByte[i]:=16#00; END_FOR stReceiveString:=’ ‘; bSend:=FALSE; IF NOT SocketSend.bError THEN bReceive:=TRUE; END_IF END_IF //Receive message from Python Server //Reflesh Timer TON( IN:=NOT TON.Q ,PT:=tRefleshTime ); //Function Block SocketReceive( hSocket:=hSocket ,cbLen:=SIZEOF(ReceiveByte) ,pDest:=ADR(ReceiveByte) ,bExecute:=NOT TON.Q AND bReceive ); //If bytes are received R_TRIG_ByteReceived( CLK:=SocketReceive.nRecBytes > 0 ); IF R_TRIG_ByteReceived.Q THEN CopiedBytes:=MEMCPY( destAddr:=ADR(stReceiveString) ,srcAddr:=ADR(ReceiveByte) ,n:=SocketReceive.nRecBytes ); counter:=counter+1; bReceive:=FALSE; bSend:=TRUE; END_IF IF SocketReceive.bError THEN bReceive:=FALSE; END_IF |
Result-TwinCAT Side
we can get the same result!
よし、同じ結果が出てきます。
Result-Python Server Side
Connected by (‘127.0.0.1’, 64176) b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ b’data from client:Hello world from TwinCAT TF6310!’ |
Source Code
https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT%20Project_TF6310_Sample_PythonServer.zip