なんだか最近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番号を切り替えてください。