なんだか最近Socket通信に書くこと多いと気がします…まぁ、なぜかよく問い合わせきましたから。
なので今回三菱RCPUとPythonを通信すること書きます。
前書き
Socket通信すには、Port番号の設定によって異なる外部デバイスと通信することができます。それらのデバイスはTCP/IP・UDP/IPも使えます。
そしてTCP/IP protocolはActive openとPassive Openがあります。
簡単にいいますと、Active OpenはClientですね。
Passive OpenはServerですね。
Active Open Flow
Passive Open Flow
Connectionを切断するタイミング
- Timeout
- 相手側から切断要求きたとき。そして同じのConnectionを再接続には最低限500msの間隔をあげましょう。(Manualがそういっただけですー)
TCP/IP protocolに必要なもの
- 自分のIP
- 自分のPort
- 相手のIP
- 相手のPort
Flow
使用する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
- Connectionの設定ですね。
- S2+3
- 自分のPort番号です。
- S2+4、+5
- 相手のIPアドレスです。
- S2+6
- 相手のPort番号です。
- S2+7-+9
- 使用禁止です。
Time-Chart
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
- 実行結果。
Time Chart
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
- S2+0,+1
- 相手のIPアドレス
- S2+2
- 相手のPort
- S2+3
- 自分のPort
- S2+4
- Connectionの構成Word
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
- データの番地
Time Chart
S_SOCRCVS
データ受信するFunctionです。
- EN:Bool
- ONするとFunctionを実行する
- U:String[1]
- Dummyです。
- S:Word
- Connectionの番号です。
- d: Word
- データ受信するときこの番地から始まります。Dは受信するデータの長さ・D+1からはデータの格納先です。
- ENO:Bool
- 実行結果。
d
Time Chart
実装
バージョン
CPU
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
Interface
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;
Properties
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;
Properties
FC_SocketConfig
このControl Dataを組みます。
Interface
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;
Properties
Main Program
まずこんな感じの流れです。
STの文法などの説明ここでやめとおきます。ネット上で自分よりう前説明がたくさんあると思いますので…
Interface
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()
コメント
SP_SOCSNDのControl dataの先頭デバイス番号は、D400→D4000の間違いでしょうか?
SP_SOCOPENはD4000を使用していますが・・・
先ほどのD4000の質問をした者です。
引数の説明を読み、授受するデータの内容が異なることに気付きました。(同じデバイス番号を指定すると、SOCOPENのコントロールデータを壊してしまいますね)
先ほどの質問は取り下げます。ご迷惑お掛けしました。
コメントありがとうございます。
大分前書いてプログラムなので…自分も忘れてしまいました 笑
SP_SOCSNDのControl WordはD400で、SP_SOCOPENはD4000ですねね。
SP_SOCSNDは多分Connection番号さえ指定しておけばOKです。
自分はあまり三菱使わないので。。そのへんのアドレス間隔も少しあけてたほうがよいかもしれませんね!
教えて下さい。
U:String[1]
Dummyです。
ダミーとあるのは特に文字列ならば何でもいいって事ですか?
S1:Word
Connectionの番号です。
デバイスが2台、3台と増えれば2、3と変化すればいいのですか?
Soket通信については素人なのでご教授頂ければ助かります。
Commentありがとうございます。
U:String[1]
なんでもいいだと思いますが、なにも書き込まないほうが一番よいだと思います。
S1:Word
基本はそのとおりです。ですが実際のDoneやBusyの状態をみてからConnection番号を切り替えてください。