今回はKeyence KV8000を使用しSocket通信を実装します。ClientはKV8000でサーバーはPythonのものになります。よろしくおねがいします。
What is a Socket?
ソケットとは、簡単に言いますとIPアドレスとポート番号を組み合わせたものです。IPアドレスは通信端末を特定し、ポート番号は端末で利用するサービスを特定します。
TCPプロトコルはまさにそのIPアドレス+ポート番号間の接続になります。最初に確立され、確立された接続に基づくパスを使用してデータが送受信されます。
UDPプロトコルでは、IPアドレスとポート番号の組み合わせで通信先を指定し、データを送受信します。ただし、TCP とは異なり、通信先とのコネクションは最初から確立されていません。
KV socket communication?
KV ソケット通信とは、TCP/IP または UDP/IP プロトコルを使用して Ethernet 上の機器と任意のデータを送受信するための機能です。 PCやワークステーションだけでなくさまざまな対応機器と通信できます。
また、ソケット通信用に設定されたバッファメモリやリレーを使用しグラムを組むことで、CPUユニットと相手機器間のデータ送受信を行うことができます。
TCP (non-procedural)-based communication
TCP/IPベースのデータ送受信ではフロー制御を行っているため、実際に受信機が受信するデータ長は、1回の送信プログラムで送信したデータ長と異なる場合があります。この場合、送信するデータ長は受信側のユーザプログラムで制御する必要があります。
TCP(無手順)の場合、KV-8000/7500が実際に受信したデータ長は「受信データ長(結果)」Buffer Memoryに格納されます。
Relays
Relayの先頭アドレスは=n+4000+(KV Socket番号*100)
n=はRelay Leading No.の設定になります。
例えば、私はSocket0を使用し、Leading Relay NoはR3000だとします。
プログラムの中に使用するRelayの先頭アドレスは:
30000+4000(0*100)=R34000になります。
Flow
TCP Active Open
こちらは今回の記事で使用するTCP Active OpenのFlowです。ここでLeading RelayはR30000、Socketは0だとします。
- U_SOPENを使用し、IPなどの情報を書き込みます。
R34002(n+4002)はTrueします。
R36002(n+6002)Complete Flagが回答します。
R34002(n+4002)をFalseします。
それでConnectionが確立され、もしR36003(n+6003)がTrueになったらConnectionエラーになり、エラー処理を行ってください。接続エラーが発生した場合、U_SSTAT関数を使用し詳しいエラー情報を取得できます。
TCP Send
こちらは今回の記事で使用するTCP Active の送信Flowです。ここでLeading RelayはR30000、Socketは0だとします。
- U_SWRBUFを使用し、送信データなどの情報を書き込みます。
- R34006(n+4006)SendリクエストをTrueします。
- R36006(n+6006)Complete Flagが回答します。
- R34006(n+4006)をFalseします。
それで送信完了です。もしR36007(n+6007)がTrueになったらConnectionエラーになり、エラー処理を行ってください。接続エラーが発生した場合、U_SSTAT関数を使用し詳しいエラー情報を取得できます。
TCP Receive
こちらは今回の記事で使用するTCP Active の受信Flowです。ここでLeading RelayはR30000、Socketは0だとします。
- SRDBUFを使用し、Server受信データなどの情報を書き込みます。
- R34008(n+4008)ReceiveリクエストをTrueします。
- R36010(n+6010)ReceiveありのFlag返答きます。
- R36008(n+6010)Complete Flagが回答します。
- R34006(n+4006)をFalseします。
それで送信完了です。もしR36009(n+6009)がTrueになったらConnectionエラーになり、エラー処理を行ってください。接続エラーが発生した場合、U_SSTAT関数を使用し詳しいエラー情報を取得できます。
TCP Close
こちらは今回の記事で使用するTCP Active のConnection Close Flowです。ここでLeading RelayはR30000、Socketは0だとします。
- R34012(n+4012)CloseリクエストをTrueします。
- R36012(n+6012)Complete Flagが回答します
- R36013(n+6013)接続OpenしてるFlagがFalseになります。
- R34012(n+4012)をFalseします。
Function
次は今回の記事で使用する関数を紹介します。
U_SOPEN
Connectionの接続パラメータをBuffer Memoryに転送する関数です。
Format
U_SOPEN ([EN,] Unit, Socket, Setting)
Argument
Parameter Name | Description |
EN | True=関数実行する |
Unit | Unit番号を指定します。0=KV8000本体のPort |
Socket | KV Socket番号を指定します。0-15まで設定できます。 |
Setting | KV Socketの設定データ |
Settings Mapping
Memeory | Buffer Memory | Description |
Setting+0 | #25000+Socket番号x1500 | KV8000のLcocal Port |
Setting+1 | #25001+Socket番号x1500 | 接続先のIPアドレス Byte1 |
Setting+2 | #25002+Socket番号x1500 | 接続先のIPアドレス Byte2 |
Setting+3 | #25003+Socket番号x1500 | 接続先のIPアドレス Byte3 |
Setting+4 | #25004+Socket番号x1500 | 接続先のIPアドレス Byte4 |
Setting+5 | #25005+Socket番号x1500 | 接続先のPort番号 |
Setting+6 | #25006+Socket番号x1500 | TimeOut(ms) |
Setting+7 | #25007+Socket番号x1500 | UDP Flag,1=UDP |
U_SWRBUF
該当するConnectionに送信するデータをBuffer Memoryに送信する関数です。
Format
U_SOPEN ([EN,] Unit, Socket, SendData)
Argument
Parameter Name | Description |
EN | True=関数実行する |
Unit | Unit番号を指定します。0=KV8000本体のPort |
Socket | KV Socket番号を指定します。0-15まで設定できます。 |
SendData | 送信するデータ(Bytes数) |
Settings Mapping
Memeory | Buffer Memory | Description |
SendData+0 | #25009+Socket番号x1500 | 送信データの長さ(Byte) |
SendData+1 | #25010+Socket番号x1500 | 送信データ(Byte0,1) |
SendData+2 | #25011+Socket番号x1500 | 送信データ(Byte2,3) |
SendData+3 | #25012+Socket番号x1500 | 送信データ(Byte4.5) |
Etc.. | Etc.. | Etc.. |
U_SRDBUF
該当するConnectionに受信するデータをBuffer Memoryからプログラムデバイスに転送する関数です。
Format
U_SRDBUF([EN,] Unit, Socket, Dst)
Argument
Parameter Name | Description |
EN | True=関数実行する |
Unit | Unit番号を指定します。0=KV8000本体のPort |
Socket | KV Socket番号を指定します。0-15まで設定できます。 |
Dst | 受信するデータの格納先(Bytes) |
Settings Mapping
Memeory | Buffer Memory | Description |
Dst+0 | #25759+Socket番号x1500 | 受信データの長さ(Byte) |
Dst+1 | #25760+Socket番号x1500 | 受信データ(Byte0,1) |
Dst+2 | #25761+Socket番号x1500 | 受信データ(Byte2,3) |
Dst+3 | #25762+Socket番号x1500 | 受信データ(Byte4.5) |
Etc.. | Etc.. | Etc.. |
U_SSTAT
該当するConnectionのStatusを取得する関数です。
Format
U_SOPEN ([EN,] Unit, Socket, Dst)
Argument
Parameter Name | Description |
EN | True=関数実行する |
Unit | Unit番号を指定します。0=KV8000本体のPort |
Socket | KV Socket番号を指定します。0-15まで設定できます。 |
Dst | 受信するデータの格納先 |
Settings Mapping
Memeory | Buffer Memory | Description |
Dst+0 | #25748+Socket番号x1500 | 接続状態 |
Dst+1 | #25749+Socket番号x1500 | 接続先IPアドレス(Bye1) |
Dst+2 | #25750+Socket番号x1500 | 接続先IPアドレス(Bye2) |
Dst+3 | #25751+Socket番号x1500 | 接続先IPアドレス(Bye3) |
Dst+4 | #25752+Socket番号x1500 | 接続先IPアドレス(Bye4) |
Dst+5 | #25753+Socket番号x1500 | 接続先のPort |
Dst+6 | #25754+Socket番号x1500 | 接続Open状態 |
Dst+7 | #25755+Socket番号x1500 | 送信状態 |
Dst+8 | #25756+Socket番号x1500 | Response状態 |
Dst+9 | #25757+Socket番号x1500 | 受信状態 |
Dst+10 | #25758+Socket番号x1500 | 接続Close状態 |
Dst+11 | #25759+Socket番号x1500 | 受信したデータ長さ |
Status
自分は基本的に0,4見ればよいだと思います。
Code | Description |
0 | CLOSED,Connection がClose中 |
1 | LISTEN,Connection持っています |
2 | SYS SENT,Active Open稼働中、SYNは送信した |
3 | SYS RCVD,ServerからSYN受信した |
4 | ESTABLISHED,Connectionが確立された |
5 | CLOSE WAIT,FINが受信された |
6 | FIN WAIT1,FINが送信した |
7 | FIN WAIT2,1,FINが受信された |
8 | CLOSING,FINが受信されConnectionがClose中 |
9 | LAST AKC,FIN受信されFINも送信した |
Implmentation
ここからは実装です。
Keyence Side
Configuration
KV Studioを起動し、Unit Editorを開きます。
Socket Function
Function>Socket functionをUsedで機能を有効化します。
Leading relay No
Leading relay No.DefaultはR3000になっており、つまりConnectionの制御やStatusデータはR3000から始めます。
IP address
IP addressもアプリケーションに合わせて設定してください。
Socketx Setting
Socket0からSocket15までの設定ができます。
Kv SocketをTCP(Non-procedure)TCP無手順に設定します。
H->LはByteをSwapかどうかを設定します。
Program
次はプログラムを組みます。
DUT
DUT_U_SSTAT_TCPStatus
こちらの構造体は関数U_SSTATのConnections Statusを明確にするためです。
Closed BOOL Listen BOOL SynSent BOOL SynRCVD BOOL Established BOOL CloseWait BOOL FinWait1 BOOL FinWait2 BOOL Closing BOOL LastAck BOOL |
Variable
Global
Global変数Socket0が必要なリクエストとResponseを絶対アドレスの代わりに変数に代用します。
gbTcp0ActiveOpenReq BOOL R34002 gbTcp0ActiveOpenComp BOOL R36002 gbTcp0ActiveOpenFail BOOL R36003 gbSocket0SendReq BOOL R34006 gbSocket0SendComp BOOL R36006 gbSocket0SendFail BOOL R36007 gbSocket0ReceveData BOOL R36010 gbSocket0ReceveInCorrect BOOL R36011 gbSocket0ReceveReq BOOL R34008 gbSockte0ReceveEnd BOOL R36008 gbSocket0CloseReq BOOL R34012 gbSocket0ReceveFail BOOL R36009 |
Local
Local変数はUnit番号・Socket番号など通信プログラムを制御するデバイスを定義します。
wUnitNo INT wstextLen UINT wSomeValue UINT wKVSocketNo INT stSocket0Status DUT_U_SSTAT_TCPStatus stext STRING[255] sSomeText STRING[10] iStep UINT iCounter UINT bStart BOOL bReset BOOL bError BOOL arrControlData ARRAY[0..7] OF UINT |
Main
Configure your Sending Data
ランダムの整数を文字列に変換しHello from Keyence KV8000!の一番うしろに付けてPython Serverに送信します。そうすると実際データが送信されたか、常に確認できます。
また、整数の現在値によって変換された文字列数も定数ではなくなりますので、LEN()関数を使用し文字列の長さを取得します。
wSomeValue:=wSomeValue+1; stext:=’Hello from Keyence KV8000!’; sSomeText:=STR(wSomeValue); stext:=CONCAT(stext,sSomeText); wstextLen:=LEN(stext); |
Move to DM
Keyenceの関数ではどうしても絶対アドレスのほうが取り扱いやすいので、SMOV関数を使用し先程設定した文字列を一括転送でDM101を送信します。(DM100は送信サイズを格納する場所なので)
Get Connection Status
CR2004を使用し100ms CycleでSocket0の状態を取得します。
U_SSTAT( EN:=CR2004 ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM10 ); stSocket0Status.Closed:=DM10 =0; stSocket0Status.Listen:=DM10 =1; stSocket0Status.SynSent:=DM10 =2; stSocket0Status.SynRCVD:=DM10 =3; stSocket0Status.Established:=DM10 =4; stSocket0Status.CloseWait:=DM10 =5; stSocket0Status.FinWait1:=DM10 =6; stSocket0Status.FinWait2:=DM10=7; stSocket0Status.Closing:=DM10=8; stSocket0Status.LastAck:=DM10=9; |
Socket Communication
こちらはMainの部分でbstartでプログラムを開始します。
- Step10
Connectionのパラメータを設定・受信Bufferをクリア・各関数を実行してない状態にリセットします。 - Step20
U_SOPENを使用し、ConnectionパラメータをBuffer Memoryに転送します。gbTcp0ActiveOpenReqをTrueしてPython Serverと接続開始しgbTcp0ActiveOpenComp FlagがTrueになったら次のStepに進む。 - Step30
Connection OpenしてたときエラーがあるかをCheck、エラーであれば999に進み、問題なければ次のStepに進む。 - Step40
gbTcp0ActiveOpenReqをリセットします。U_SWRBUF関数を使用し送信するデータをBuffer Memoryに転送、gbSocket0SendReqをTrueにしてデータをPython Serverに送信します。gbSocket0SendCompがTrueになると、送信完了だと示すので次のStepに進む。 - Step50
データ送信してたときエラーがあるかをCheck、エラーであれば999に進み、問題なければ次のStepに進む。 - Step60
受信Bufferをリセットします。 - Step70
U_SRDBUF関数を使用しPython Serverから受信したデータの格納先をBuffer Memoryに転送します。gbSocket0ReceveReqをTrueにして受信開始する。gbSockte0ReceveEndがTrueになると、受信完了だと示すので次のStepに進む。 - Step80
データ受信してたときエラーがあるかをCheck、エラーであれば999に進み、問題なければ次のStepに進む。 - Step90
次の送信オペレーションまでのDelay Stepです。 - Step999
エラー処理のStepです。
if bStart and iStep =0 then iStep:=10; bStart:=FALSE; end_if; CASE iStep of 10: //Init the data //Control Data arrControlData[0]:=9000; //local port arrControlData[1]:=192; //Server ip byte1 arrControlData[2]:=168; //Server ip byte2 arrControlData[3]:=1; //Server ip byte3 arrControlData[4]:=194; //Server ip byte4 arrControlData[5]:=5000; //Server Port arrControlData[6]:=1000; //Timeout arrControlData[7]:=0; //UDP Flag //Receive Data Buffer FMOV(0,DM1000,100); //Function Block U_SRDBUF( en:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM1000 ); U_SWRBUF( EN:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,SendData:=DM100 ); U_SOPEN(EN:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Setting:=arrControlData ); U_SSTAT( EN:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM10 ); iStep:=20; 20://TCP Open Req gbTcp0ActiveOpenReq:=True; U_SOPEN( EN:=True ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Setting:=arrControlData ); if gbTcp0ActiveOpenComp then iStep:=30; end_if; 30://Check TCP Open Status if gbTcp0ActiveOpenFail then iStep:=999; ELSE iStep:=40; end_if; 40://TCP Send Data gbTcp0ActiveOpenReq:=False; DM100:=wstextLen; U_SWRBUF(EN:=True ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,SendData:=DM100 ); gbSocket0SendReq:=True; if gbSocket0SendComp then iStep:=50; U_SWRBUF(EN:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,SendData:=DM100 ); gbSocket0SendReq:=False; end_if; 50://Check TCP Send Status if gbSocket0SendFail then iStep:=999; else iStep:=60; gbSocket0SendReq:=False; end_if; 60: //Rset the Receive Buffer FMOV(0,DM1000,100); iStep:=70; 70://TCP Receive Data U_SRDBUF( en:=true ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM1000 ); gbSocket0ReceveReq:=True; if gbSockte0ReceveEnd or not stSocket0Status.Established then U_SRDBUF( en:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM1000 ); gbSocket0ReceveReq:=false; iStep:=80; end_if; 80://Check Receive Status if gbSocket0ReceveInCorrect or gbSocket0ReceveFail then iStep:=999; else iStep:=90; iCounter:=0; end_if; 90://10ms Pulse,Step Back Delay if CR2004 then iCounter:=iCounter+1; end_if; if iCounter>=600 THEN iStep:=40; iCounter:=0; end_if; 999://Error,Wait Reset bError:=True; bReset:=True;//Auto Reset U_SRDBUF( en:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Dst:=DM1000 ); U_SWRBUF( EN:=False ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,SendData:=DM100 ); U_SOPEN(EN:=FALSE ,Unit:=wUnitNo ,Socket:=wKVSocketNo ,Setting:=arrControlData ); if CR2004 and bReset then iCounter:=iCounter+1; end_if; gbSocket0CloseReq:=True; gbSocket0ReceveReq:=False; gbTcp0ActiveOpenReq:=False; gbSocket0SendReq:=False; if iCounter>=600 then gbSocket0CloseReq:=False; iStep:=10; bError:=False; bReset:=False; iCounter:=0; end_if; END_CASE; |
Python Side
こちらはPython側の実装です。
import socket HOST = ‘192.168.1.194’ PORT = 12344 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()) |
Implementation-2
次はREST APIを使用するよくあるGET リクエストの実装方法を簡単に紹介します。
Keyence Side
最初に文字列の設定するところをGET リクエストに必要な内容をすべて無理やり書いておけばOKです。
stext:=’GET /mydata HTTP/1.1$R$LHost: $R$LUSER-Agent: Keyence$R$LAccept: text/html$R$LAccept-Encoding: identity$R$LAccept-Charset: utf-8$R$L$R$L’; wstextLen:=LEN(stext); |
Python Side
from flask import Flask app=Flask(__name__) @app.route(‘/’) def hello_world(): return “hello” @app.route(‘/mydata’) def get_data(): return “data-1-2-3-4”,200 if __name__==’__main__’: app.run(host=”192.168.1.194″) |
Result
Download Source Project
GithubからSample ProjectをDownloadしてください。
https://github.com/soup01Threes/Keyence/blob/main/SocketTest_Get_Finally.7z