なんだか最近Socket通信に書くこと多いと気がします…まぁ、なぜかよく問い合わせきましたから。
なので今回三菱RCPUとPythonを通信すること書きます。
この記事は以下のBlogにも乗っています。よろしくおねがいします。
http://soup01.com/ja/2020/07/21/mels-iqr-sockt/
#前書き
Socket通信すには、Port番号の設定によって異なる外部デバイスと通信することができます。それらのデバイスはTCP/IP・UDP/IPも使えます。
そしてTCP/IP protocolはActive openとPassive Openがあります。
簡単にいいますと、Active OpenはClientですね。
Passive OpenはServerですね。
##Connectionを切断するタイミング
- Timeout
- 相手側から切断要求きたとき。そして同じのConnectionを再接続には最低限500msの間隔をあげましょう。(Manualがそういっただけですー)
##TCP/IP protocolに必要なもの
- 自分のIP
- 自分のPort
- 相手のIP
- 相手のPort
#使用するFunction
##SP_SOCOPEN
Connectionを開くFunctionです。
- EN:Bool
- 立ち上げるとFunctionを実行する
- U:String[1]
- Dummyです。
- S1:Word
- Connectionの番号です。
- S2: Array [0..9] of Word
- Control Data最初から始まるメモリ番地。
- d:Array[0..1]of Bool
- Function実行後の状態、もしエラーがある場合Array[0]とArray[1]も1サイクルOnになります。
- ENO:Bool
- 実行結果。
###Control Word
- S2+0
- 0000H-Open setting のエンジニアツールを使う、なんらんかはしりません。
- 8000H-S2+2~+9のコントロールデータを使います。今回はこれを利用します。
- S2+1
- 実行後のStatusです。もし0でなければエラーがあります。エラーコードはここで格納します。
- S2+2
- S2+3
- 自分のPort番号です。
- S2+4、+5
- 相手のIPアドレスです。
- S2+6
- 相手のPort番号です。
- S2+7-+9
- 使用禁止です。
##SP_SOCCLOSE
Connectionを切断するFunctionです。
- EN:Bool
- 立ち上げるとFunctionを実行する
- U:String[1]
- Dummyです。
- S1:Word
- Connectionの番号です。
- S2: Array [0..1] of Word
- Control Data最初から始まるメモリ番地。
- d:Array[0..1]of Bool
- Function実行後の状態、もしエラーがある場合Array[0]とArray[1]も1サイクルOnになります。
- ENO:Bool
- 実行結果。
##SP_SOCCINF
Connectionの設定をもらうFunctionです。
- EN:Bool
- 立ち上げるとFunctionを実行する
- U:String[1]
- Dummyです。
- S1:Word
- Connectionの番号です。
- S2::Array [0..1] of Word
- Control Data最初から始まるメモリ番地。Function実行後の状態、もしエラーがある場合Array[1]がエラーコードが格納されます。
- d:Array[0..4]of Word
- Connectionの設定格納されます。
- ENO:Bool
- 実行結果。
###d
##SP_SOCSND
データを送信するFunctionです。
- EN:Bool
- 立ち上げるとFunctionを実行する
- U:String[1]
- Dummyです。
- S1:Word
- Connectionの番号です。
- S2:Array[0..1] of Word
- ControlデータでもしFunction実行エラーがある場合Word[1]がエラーコードが格納されます。
- S3: Array [0..n] of Word
- 送信するデータ最初から始まるメモリ番地。Function実行後の状態、もしエラーがある場合Array[1]がエラーコードが格納されます。
- d:Array[0..4]of Word
- Connectionの設定格納されます。
- ENO
- Boolで実行結果。
###S3
- S3+0
- 送るデータの長さ(Byteで計算)
- S3+1-N
- データの番地
##S_SOCRCVS
データ受信するFunctionです。
- EN:Bool
- ONするとFunctionを実行する
- U:String[1]
- Dummyです。
- S:Word
- Connectionの番号です。
- d: Word
- データ受信するときこの番地から始まります。Dは受信するデータの長さ・D+1からはデータの格納先です。
- ENO:Bool
- 実行結果。
#実装
##Hardware設定
Parameter>R08CPU>Module ParameterでIPを設定します。
そしてExternal Device Configurationの隣“Detailed Setting”をクリックしConnectionの設定をします。
右のModule List>Ethernet Device(General)でActive Connection Moduleを選んでひっばります。
次はSocket Communicationを選んで、自分のPort番号設定します。今回の例は4000にします。次はちょっと右にScrollし…
こちらで相手側のIPとPortを設定し、
“Close with Reflecting the Setting”を。
最後はApply。
よし、これでConnection完了です。
##プログラム
Socket設定するためにFunctionを3つ作っています。
- FC_SocketIPConfig
- IPアドレスを綺麗に一つのDWORDにまとめるFunction。
- FC_SocketConectionConfig
- Active使うか、なにないツール使うかのに設定するワードをまとめるFunction。
- FC_SocketConfig
- FC_SocketIPConfig、FC_SocketConectionConfigも含め、Port番号を全部設定するFunctionです。
###FC_SocketIPConfig
####Program
例え192.168.0.251はHC0A800FBです。このように百倍ずつ大きくしてプラスすれば同じの値になります。
IPConfig1:=iConfig[1];
IPConfig2:=iConfig[2];
IPConfig3:=iConfig[3];
IPConfig4:=iConfig[4];
ipConfig:=IPConfig4+int_to_Dint(IPConfig3*H100)
+(IPConfig2*H10000)
+(IPConfig1*H1000000);
FC_SocketIPConfig:=ipConfig;
###FC_SocketConectionConfig
####Interface
- IConfig[0]はTCPを使うかどうか。
- IConfig[1]はSubFeatures、つまりPredefined Protocol Settingを使うかどうか。
- IConfig[2]はActive Open使うとき。
- IConfig[3]はFull Passive使うとき。
- IConfig[2]とIConfig[3]もONしないならUnPassiveを使う。
####Program
//init
ConnectionConfig:=0;
TCP:=iCconfig[0];
SubFeatures:=iCconfig[1];
Active:=iCconfig[2];
FullPassive:=iCconfig[3];
//b0-b7 Always OFF
ConnectionConfig.0:=FALSE;
ConnectionConfig.1:=FALSE;
ConnectionConfig.2:=FALSE;
ConnectionConfig.3:=FALSE;
ConnectionConfig.4:=FALSE;
ConnectionConfig.5:=FALSE;
ConnectionConfig.6:=FALSE;
ConnectionConfig.7:=FALSE;
IF TCP THEN
ConnectionConfig.8:=FALSE;
ELSE
ConnectionConfig.8:=TRUE;
END_IF;
//b9 Always ON
ConnectionConfig.9:=TRUE;
IF SubFeatures THEN
ConnectionConfig.a:=TRUE;
ELSE
ConnectionConfig.a:=FALSE;
END_IF;
//b11-13 Always OFF
ConnectionConfig.b:=FALSE;
ConnectionConfig.c:=FALSE;
ConnectionConfig.d:=FALSE;
IF Active THEN
ConnectionConfig.e:=FALSE;
ConnectionConfig.f:=FALSE;
ELSIF FullPassive THEN
ConnectionConfig.e:=TRUE;
ConnectionConfig.f:=TRUE;
ELSE
ConnectionConfig.e:=FALSE;
ConnectionConfig.f:=TRUE;
END_IF;
FC_SocketConnectionConfig:=ConnectionConfig;
###FC_SocketConfig
このControl Dataを組みます。
####Program
Z0:=iOffset;
//+0
IF iUseControlWord THEN
D0Z0:=H8000;
ELSE
D0Z0:=H0000;
END_IF;
//+2
Z0:=iOffset+2;
D0Z0:=FC_SocketConnectionConfig(iMyConfig);
//+3
Z0:=iOffset+3;
D0Z0:=iMyPort;
//+4,5
Z0:=iOffset+4;
_tDWord:=FC_SocketIPConfig(iParnterIP);
//D0Z0:D:=_tDWord;
DMOV(TRUE,_tDWord,D0Z0);
//+6
Z0:=iOffset+6;
D0Z0:=iParnterPort;
##Main Program
まずこんな感じの流れです。
STの文法などの説明ここでやめとおきます。ネット上で自分よりう前説明がたくさんあると思いますので…
####Program
//Init
insConNums :=1;
insConnDelay :=30;
insSendDelay :=2;
insRetry :=3;
insENO:=OUT_T(
NOT insInit //Timer Trigger1
AND NOT insSOCOPENSts[1] //Timer Trigger2
,InsT2 //Timer Register
,insConnDelay //Time Setup
);
//OPEN Connection
//IP Settings
insMyIP[1] :=192;
insMyIP[2] :=168;
insMyIP[3] :=0;
insMyIP[4] :=251;
//Connection Configs
insMyConfig[0] :=TRUE;
insMyConfig[1] :=FALSE;
insMyConfig[2] :=TRUE;
insMyConfig[3] :=FALSE;
//Config Setup Function
insENO:=FC_SocketConfig(
4000 //DB Offset
,TRUE //Use Control Word Or not
,insMyConfig //[0]=Use TCP?,
//[1]=SubFeatures?,
//[2]=Active Connection?,
//[3]=Full Passive Connection
,4000 //My Port
,4000 //Parnter Port
,insMyIP //Parnter IP
);
//Connection Delay
insSOCOPEN:=InsT2.S;
//Function
SP_SOCOPEN(
insSOCOPEN //EN
,insString //Dummy
,insConNums //Connection Numbers
,D4000 //Control Word
,insSOCOPENSts//Status
);
//Result
IF insSOCOPENSts[0] AND NOT insSOCOPENSts[1] THEN
insConnected:=TRUE;
insInit:=TRUE;
insSendRetry:=0;
insConnRetry:=0;
ELSIF insSOCOPENSts[0] AND insSOCOPENSts[1] THEN
insConnected:=FALSE;
END_IF;
//Retry Count
IF insSOCOPENSts[1] THEN
insConnRetry:=insConnRetry+1;
END_IF;
//Get Connection Info
//Config ControlWord for SP_SOCCINF
D200:D:=D4004:D; //Parnter IP
D202:=HFA0; //Parnter Port
D203:=HFA0; //My Port
D204:=D4002; //Connection Config
//Function
SP_SOCCINF(
insSOCCINF //EN
,insString //Dummy
,insConNums //Connection Numbers
,D200 //Control Words
,D600 //Result
);
//Send
//Data
D999:=20; //Length of bytes that need to sent
D1000:=H23; //Data
D1002:=H59; //Data
D1004:=H99; //Data
D1009:=H53; //Data
//Send Trigger
IF insConnected
AND NOT insSOCSNDSts[0]
AND NOT insSOCSNDSts[1]
AND insSendRetry < insRetry THEN
insSend:=TRUE;
ELSE
insSend:=FALSE;
END_IF;
//Retry Count
IF insSOCSNDSts[1] THEN
insSendRetry:=insSendRetry+1;
END_IF;
//Send Delay
insENO:=OUT_T(
insSend //Trigger
,insT1 //Timer Register
,insSendDelay //Time Setup
);
//Send Command
insSOCSND:=insT1.S;
//Function
SP_SOCSND(
insSOCSND //EN
,insString //Dummy
,insConNums //Connection Numbers
,D400 //Control Data,if Error,+1<>0
,D999 //Data Trasnfer
,insSOCSNDSts //Status
);
//Recv
//Data
D1099:=10;
//Function
S_SOCRCVS(
TRUE //EN
,insString //Dummy
,insConNums //Connection Numbers
,D1099 //Data
);
//Close
//Close Command
insSOCCLOSE:= insSendRetry >= insRetry;
//Function
SP_SOCCLOSE(
insSOCCLOSE //EN
,insString //Dummy
,insConNums //Connection Numbsers
,D300 //Control Data,if Error,+1<>0
,insSOCCLOSESts //Status
);
//Connect Again
IF insSOCCLOSESts[0] THEN
insInit:=FALSE;
END_IF;
#Python
import socket
import time
DESTINATION_ADDR = '192.168.0.39'
SOURCE_PORT, DESTINATION_PORT = 4000, 4000
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('192.168.0.251', SOURCE_PORT))
sock.listen(1)
conn, addr = sock.accept()
i =0
while i<3000:
if i %2 == 0:
ba1 = bytearray(b"4abcdefas0")
else:
ba1 = bytearray(b"9kaekfyei2")
response = conn.recv(1440)
conn.send(ba1)
# print(response)
print("current:"+str(i))
i+=1
sock.close()
conn.close()