三菱IQ-R#Socket通信

なんだか最近Socket通信に書くこと多いと気がします…まぁ、なぜかよく問い合わせきましたから。
なので今回三菱RCPUとPythonを通信すること書きます。

この記事は以下のBlogにも乗っています。よろしくおねがいします。
http://soup01.com/ja/2020/07/21/mels-iqr-sockt/

#前書き
Socket通信すには、Port番号の設定によって異なる外部デバイスと通信することができます。それらのデバイスはTCP/IP・UDP/IPも使えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/6832a48d-e66b-fb98-51c5-878843ed633d.png)

そしてTCP/IP protocolはActive openとPassive Openがあります。
簡単にいいますと、Active OpenはClientですね。
Passive OpenはServerですね。

##Active Open Flow
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/6410d5e1-7d8c-0a82-79ce-4270608857ad.png)

##Passive Open Flow
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/ab09da64-240f-3bb8-cf86-a901cbf26570.png)

##Connectionを切断するタイミング

- Timeout
- 相手側から切断要求きたとき。そして同じのConnectionを再接続には最低限500msの間隔をあげましょう。（Manualがそういっただけですー）

##TCP/IP protocolに必要なもの

- 自分のIP
- 自分のPort
- 相手のIP
- 相手のPort

###Flow
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f718dbb8-a2c4-15c6-f121-52d0a12f55cd.png)

#使用するFunction

##SP_SOCOPEN
Connectionを開くFunctionです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/d2497dd6-0277-7beb-6280-025f1b6c3636.png)

- 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の設定ですね。
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/308a36b6-470e-d840-41f1-410b0dde9117.png)
- S2+3
    - 自分のPort番号です。
- S2+4、+5
    - 相手のIPアドレスです。
- S2+6
    - 相手のPort番号です。
- S2+7-+9
    - 使用禁止です。

###Time-Chart
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f4548d7d-3743-f8f3-7a31-098791961330.png)

##SP_SOCCLOSE
Connectionを切断するFunctionです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/a097c033-80cc-1647-50c2-dbb9ee684f03.png)

- 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
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/8f0db236-766e-6b69-ac9d-59ddb2fa7890.png)

##SP_SOCCINF
Connectionの設定をもらうFunctionです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/3f60b703-637a-1c1d-d5f2-bc0bf07eace8.png)

- 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
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/b37c3e15-45ec-34f4-5877-199e4c6ed4b3.png)

##SP_SOCSND
データを送信するFunctionです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/a8acd7bd-8808-5042-1901-ab9ecf299e2d.png)

- 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
    - データの番地

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/2229e788-0e78-07ab-49ae-61efd56f5e53.png)

###Time Chart
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/7e1d38ae-d22f-9356-1102-4686a5f06df4.png)

##S_SOCRCVS
データ受信するFunctionです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/98be2f83-6ccd-60bb-e722-2073a07ef3d2.png)

- EN:Bool
    - ONするとFunctionを実行する
- U:String[1]
    - Dummyです。
- S:Word
    - Connectionの番号です。
- d: Word
    - データ受信するときこの番地から始まります。Dは受信するデータの長さ・D+1からはデータの格納先です。
- ENO:Bool
    - 実行結果。

###d
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/204453b6-b589-ddf5-c2f1-b472e30f60c9.png)

###Time Chart
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f8e28bc9-8f61-4ad7-8c65-0b40ec00782a.png)

#実装

##バージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/e3a95856-4abc-6e5e-92a2-12262fb59fef.png)

##CPU
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/ab6a0450-225c-3c66-2066-6abf54ed790c.png)

##Hardware設定
Parameter>R08CPU>Module ParameterでIPを設定します。
そしてExternal Device Configurationの隣“Detailed Setting”をクリックしConnectionの設定をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/48230038-d59f-29d9-12e9-7544a0d7eef8.png)
右のModule List>Ethernet Device(General)でActive Connection Moduleを選んでひっばります。
次はSocket Communicationを選んで、自分のPort番号設定します。今回の例は4000にします。次はちょっと右にScrollし…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/ee7c729a-4c76-fdf5-b857-ca0f2f16df92.png)
こちらで相手側のIPとPortを設定し、
“Close with Reflecting the Setting”を。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/3935f030-4dcd-d7a0-215d-364cf7d11406.png)
最後はApply。
よし、これでConnection完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/ce5cdd12-5372-b19b-6b8c-0a2aac69ef28.png)

##プログラム

Socket設定するためにFunctionを3つ作っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/00663c38-5949-bb36-abac-6a971fbb99f7.png)

- FC_SocketIPConfig
    - IPアドレスを綺麗に一つのDWORDにまとめるFunction。
- FC_SocketConectionConfig
    - Active使うか、なにないツール使うかのに設定するワードをまとめるFunction。
- FC_SocketConfig
    - FC_SocketIPConfig、FC_SocketConectionConfigも含め、Port番号を全部設定するFunctionです。

###FC_SocketIPConfig

####Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/5fa23360-c37f-39db-10de-d21647f0abe2.png)

####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
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/5986f500-01d0-b1bc-98f0-eba3d6835e2a.png)

###FC_SocketConectionConfig

このようなワークを組みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/f63dfef5-27cc-0644-0d42-e62bd28edc5d.png)

####Interface

- IConfig[0]はTCPを使うかどうか。
- IConfig[1]はSubFeatures、つまりPredefined Protocol Settingを使うかどうか。
- IConfig[2]はActive Open使うとき。
- IConfig[3]はFull Passive使うとき。
- IConfig[2]とIConfig[3]もONしないならUnPassiveを使う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/1490b070-b991-733f-3120-f665e3e1e557.png)

####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
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/089626f1-00c0-b104-4fe2-9e12b4839e39.png)


###FC_SocketConfig
このControl Dataを組みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/5901de64-4013-9a82-75bf-8b18802271de.png)

####Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/39321143-4aa7-1b89-c55d-82bf2f6892df.png)

####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
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/0b722faa-53d0-4574-527e-9c768a115961.png)

##Main Program
まずこんな感じの流れです。
STの文法などの説明ここでやめとおきます。ネット上で自分よりう前説明がたくさんあると思いますので…
![無題の図形描画.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/c541f62c-5ac9-d456-06ee-7dbd9f5950ab.jpeg)

####Interface
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/0b39b6ef-27b8-d427-c0b5-d1d2a706de0b.png)

####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

```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()

```