Beckhoff#TwinCAT3 TF6760 HTTPS/REST GETリクエスト送信する

最近は色々バタバタ始まりてBlogの更新は少々遅れました。今回はTF6760 Iot HTTPS/RESTについて話しします。このTF6760を使用することによってServerにHTTPSリクエストを送信することや、JSON ObjectとPLC変数間の変換などができます。

System Requirement

インストール

特になにかダウンロードする必要がありません。

Driver: TcIotDrivers.sys(TwinCAT3 XAE・XARに含まれています)

PLC library: Tc3_IotBase(TwinCAT3 XAEに含まれています)

TF6760 Iot HTTPS/REST

下図のようにTF6760を使用することによって、PLCライブラリ Tc3_IotBaseのライブラリからClientを作成し、 REST APIにあるServerにHTTPコマンド(GET/PUTなど)からデータを受送信することができます。

HTTP?

HTTPは標準なProtocolでServerとClient間でIP通信することを実現します。Client側がよくみるのはWeb Browserですね。例えばGoogle.jpを入力するとWeb Browser実はServerにWebsiteをリクエストしています。そして今回使うのはREST-WebServicesです。

注意するのはHTTP Protocolはコマンドことに独立しています。つまりServerがClientへ返信したあとにConnectionが切断します。もちろんClient側が切らないでくださいーのようなリクエストもできます。

Methods

注意するのはServerはすべてのコマンドをSupportするわけでもないです。各サーバーのAPドキュメントをしっかり読みましょう。

Status Code

HTTP返信はHeaderとBodyに分けられています。そのStatus CodeはServer側がどんな返事をClientに返すかをCheckするのは非常重要です。

(よくPage見つからないときに404やServerエラー501がみえませんか?)

ライブラリ追加

Tc3_IotBase

Tc3_JsonXml

Function Block

ちなみに今回Sample Codeで使ってないMethodやFBは説明しませんので、ご了承ください。Function Block使用するときに色々なパラメタや返信を調べる必要の場合がありますので、Chromeから右クリックし>Inspectします。

このような画面が出てきます。

下にあるNetwork Tabをクリックします。

そして先Serverに送ったリクエストをクリックすると返信の中身は確認できます。

FB_IotHttpClient

そのFBはTwinCAT SystemをHTTP Serverと通信することを確立し、特定の一つServerと接続できます。Execute()は毎周期を呼び出ししHTTP Clientとして働き、通信確率したらFB_IotHttpRequest を使用しServerにリクエスト送ります。

VAR_INPUT
sHostNameSTRINGDefault=127.0.0.1、APIの接続先
nHostPortUINTDefault=80、使用Port
bKeepAliveBOOLDefault=True、Serverと接続はServer側かClient側が切るまでConnection続ける。*注意するのはすべてのServerにもSupportするではない
tConnectionTimeoutTIMETimeout時間
stTLSST_IotSocketTlsSerer側がTLS接続必要なときで構成する必要があるパラメタ
VAR_OUTPUT
bErrorSTRINGTrue=エラーある
hrErrorCodeHRESULTエラーCode
bConfiguredBOOLTrue=Block設定完了
bConnected        BOOLTrue=Hostと接続した

Method

DisconnectServerのConnectionを切断する
ExecuteBackground通信で使われてるメソッドです。そのメソッドは毎周期で呼び出す必要があります。

FB_IotHttpRequest

そのFBを使用するとTwinCAT SystemがServerにHTTP リクエスト送信することができます。Serverから来たHTTP返信はFB_IotHttpRequest.bBusyはTrue>False戻りのタイミングで処理し、最後はJSON Formatの文字列や特定のHeader Fieldから必要なデータを取り出します。

VAR_INPUT
sContentType    STRINGRequestのContect Type設定
eCompressionModeE_IotHttpCompressionMode                        compression Mode
VAR_OUTPUT
bErrorBOOLTrue=エラーある
bBusy           BOOLTrue=FB実行中
eErrorId        ETcIotHttpRequestErrorエラーID
nStatusCode             UINTHTTP Server返信のStatus Code
nContentSize    UDINTHTTP Server返信のSize

sContentTypeの設定を確認する見えます。

eCompressionModeの設定を確認する場所:

Method

GetContent

そのメソッド使用するとHTTP返信を変数として格納できます。注意するのはFB_IotHttpRequestのBusy FlagがTrue>Falseに戻ってからそのメソッドを実行ください。

pContentPVOIDReadDataのメモリアドレス
nContentSizeUDINTそのReadDataのSize
bSetNullTerminationBOOL
Return
GetContentBOOLTrue=Methodが呼び出し成功

GetHeaderField

そのメソッド使用するとHTTP返信のHeaderを指定し読み出します。注意するのはFB_IotHttpRequestのBusy FlagがTrue>Falseに戻ってからそのメソッドを実行ください。

sFieldSTRING取りたいHeader Field
pValuePVOIDReadDataのメモリアドレス
nValueSizeUDINTそのReadDataのSize
Return
GetHeaderFieldBOOLTrue=Methodが呼び出し成功

HeaderはInspectから確認できます。

GetJsonDomContent

そのメソッド使用するとHTTP 返信からJSON Objectに変換できます。注意するのはFB_IotHttpRequestのBusy FlagがTrue>Falseに戻ってからそのメソッドを実行ください。

fbJsonREFERENCE TO FB_JsonDomParserFB_JsonDomParserのインスタでJSON Obejctを分解する
Return
GetJsonDomContentSJsonValueJSON Root Node

SendRequest

そのメソッド使用するとHTTP Serverにリクエスト送ります。

sUri        STRINGHTTP リクエスト部分
fbClientREFERENCE TO FB_IotHttpClientFB_IotHttpClientをそのまま入れればよい
eRequestTypeETcIotHttpRequestTypeGET/POSTなどのコマンドタイプ
pContentPVOID
nContentSizeUDINT
fbHeaderREFERENCE TO FB_IotHttpHeaderFieldMap
Return
SendRequestBOOLTrue=Methodが呼び出し成功

FB_JsonDomParser

そのFunction BlockはJson Objectを別Objectを変換することが可能です。

Method

GetJson

そのメソッドはJSON Objectから文字列に変換します。もし文字列は255より長いならNULLに戻しします。

vSJsonValueJson Obejct
Return
GetJsonSTRING(255)JSON Objectから変換されたString

FindMember

そのメソッドはJSON Objectから指定のプロパティを検索し戻しします。もし該当するプロパティないなら0になります。

vSJsonValueJson Obejct
memberSTRING指定のプロパティ
Return
FindMemberSJsonValueそのプロパティに含まれてるJson Object

ParseDocument 

文字列からJSON Object変換する関数です。

sJsonSTRING変換したい文字列
Return
ParseDocumentSJsonValue変換されたJson Object

MemberBegin 

JSON Object内に最初にあるプロパティを返しします。そのSJsonIteratorをMemberEnd() とNextMember()を一緒に使用しJSON内のプロパティと値をLoopingします。

vSJsonValue取りたいの最初のプロパティ
Return
MemberBeginSJsonIteratorJSON Object内最初のプロパティ

MemberEnd 

JSON Object内に最後にあるプロパティを返しします。そのSJsonIteratorはMemberBegin() とNextMember()を一緒に使用しJSON内のプロパティと値をLoopingします。

vSJsonValue取りたいの最後のプロパティ
Return
MemberEndSJsonIteratorJSON Object内最後のプロパティ

NextMember

JSON Object内にいまプロパティの次のものを返しします。そのSJsonIteratorはMemberBegin() MemberEnd()を一緒に使用しJSON内のプロパティと値をLoopingします。

vSJsonValue取りたいの次のプロパティ
Return
NextMemberSJsonIteratorJSON Object内次のプロパティ

GetMemberName 

現在SJsonIteratorにあるJSONプロパティの名前を返しします。そ関数はMemberBegin() 、NextMember()とNextMember()を一緒に使用しJSON内のプロパティと値をLoopingします。

iSJsonIterator
Return
GetMemberNameSTRINGいまプロパティの名前

GetMemberValue 

現在SJsonIteratorにあるJSONプロパティの値を返しします。そ関数はMemberBegin() 、NextMember()とNextMember()を一緒に使用しJSON内のプロパティと値をLoopingします。

iSJsonIterator
Return
GetMemberValueSTRINGいまプロパティの値

TEST API

次はTest APIに話します。Google上でたくさんありますのでこれはその一つだけです。

https://gorest.co.in/

今回はGET リクエストを使用し、赤枠のリクエストをServerに試します。

ChromeやFirefoxなどのBrowserで以下のLinkを入力します。

https://gorest.co.in/public/v1/users

Browserからこのような返信がきています。

PLCでは長い文字列を処理するのは少々面倒なので赤線の分だけの返信を取りたいと考えています。

リクエストを最後に/1666を入れるとid=1666のUserだけ返信が返してきます。

https://gorest.co.in/public/v1/users/1666

Example

これからはTESTAPIからHTTP RESTリクエスト送り、返事をJSONから文字列に変換したり、特定のHeaderやPropertyから値をとったりします。

Codeは少し長いですが、よろしくお願います。


_CallRestAPI

この_CallRestAPIではClientを作成・Serverと接続・リクエスト送りの仕事をやっています。次回はFunction Blockにする予定です。

VAR

VAR

//Client
IotHttpClient:FB_IotHttpClient;

//Request
IotHttpRequest:FB_IotHttpRequest;
JsonDomParser:FB_JsonDomParser;
R_TRIG :R_TRIG;
bSendRequest:BOOL;
bGetContentResult :BOOL;
sContent:STRING(511);
//Json
jsonDoc:SJsonValue;
jsonVal:SJsonValue;
JsonSaxWriter:FB_JsonReadWriteDataType;
jsonDocWithSpeicificMember:SJsonValue;
jsonCurrentIterator:SJsonIterator;
jsonLastIterator :SJsonIterator;
sProperty:STRING;
jsonLoopValue:SJsonValue;
sHeaderValues:STRING;
jsonDataWithSpecificMember:STRING(511);
sJsonPropertyValue:STRING;
//Unitiy
nReqCount:UDINT;
iState:INT;
nVaildCount:INT:=0;
bConfigurated:BOOL;
iVaildHeaderCount:INT;
bReset:BOOL;
bError:BOOL;
nError:INT;

END_VAR

VAR CONSTANT

VAR CONSTANT
ciStepInit :INT:=0;
ciStepSendRequest:INT:=10;
ciStepGetContent:INT:=20;
ciStepError :INT:=8000;
ciHTTPSPort :UINT:=443;
END_VAR

Code

//Start to Send the Request
R_TRIG(
CLK:=bSendRequest
);

CASE iState OF

ciStepInit:
IF NOT IotHttpClient.bConfigured THEN
IotHttpClient.nHostPort:=ciHTTPSPort;
IotHttpClient.sHostName:=’gorest.co.in’;
IotHttpClient.bKeepAlive:=TRUE;
IotHttpClient.tConnectionTimeout:=T#10S;
IotHttpClient.stTLS.bNoServerCertCheck:=TRUE;
nReqCount:=0;
nVaildCount:=0;
bError:=FALSE;
bReset:=FALSE;
nError:=DUTE_HTTPRequest_ERRORCode.E_ERR_Noraml;
ELSE
iState:=ciStepSendRequest;
END_IF


ciStepSendRequest:
IF R_TRIG.Q THEN
IF IotHttpRequest.SendRequest(
sUri:=’/public/v1/users/62′
,fbClient:=IotHttpClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=0
)
THEN
nReqCount:=nReqCount+1;
bSendRequest:=FALSE;
iState:=ciStepGetContent;
END_IF;
END_IF
ciStepGetContent:
bGetContentResult:=IotHttpRequest.GetContent(
pContent:=ADR(sContent)
,nContentSize:=SIZEOF(sContent)
,bSetNullTermination:=TRUE
);
IF IotHttpClient.bError THEN
iState:=ciStepError;
nError:=DUTE_HTTPRequest_ERRORCode.E_ERR_FB_TioHttpRequest;

ELSE
CASE IotHttpRequest.nStatusCode OF
200..299:
//get the Json Dom from Response
jsonDoc:=IotHttpRequest.GetJsonDomContent(
fbJson:=JsonDomParser
);
//get the Header
IF IotHttpRequest.GetHeaderField(
sField:=’Server’
,pValue:=ADR(sHeaderValues)
,nValueSize:=SIZEOF(sHeaderValues)
)
THEN
iVaildHeaderCount:=iVaildHeaderCount+1;
END_IF;

//get ‘data’ property from JSON DOM
jsonDataWithSpecificMember:=JsonDomParser.GetJson(
v:=JsonDomParser.FindMember(
jsonDoc
,’data’
)
);

//Convert it to Dom again
jsonDocWithSpeicificMember:=JsonDomParser.ParseDocument(
jsonDataWithSpecificMember
);

//While loop + NextMember(),MemberBegin(),MemberEnd
jsonCurrentIterator:=JsonDomParser.MemberBegin(jsonDoc);
jsonLastIterator:=JsonDomParser.MemberEnd(jsonDoc);
WHILE jsonCurrentIterator <> jsonLastIterator DO
sProperty:=JsonDomParser.GetMemberName(
jsonCurrentIterator
);
jsonLoopValue:=JsonDomParser.GetMemberValue(
jsonCurrentIterator
);

IF sProperty =’email’ THEN
JsonDomParser.CopyString(
jsonLoopValue
,sJsonPropertyValue
,SIZEOF(sJsonPropertyValue)
);
END_IF
jsonCurrentIterator:=JsonDomParser.NextMember(
jsonCurrentIterator
);

END_WHILE
iState:=ciStepSendRequest;

//if json Dom from Response is 0, somethings error in your program
IF jsonDoc <> 0 THEN
nVaildCount:=nVaildCount+1;
ELSE
iState:=ciStepError;
nError:=DUTE_HTTPRequest_ERRORCode.E_ERR_JsonDOM_IsZero;
END_IF

ciStepError:
IF bReset THEN
iState:=ciStepInit;
END_IF
END_CASE
END_IF;




END_CASE

IotHttpClient.Execute();

MAIN

最後は先のPOUを呼び出しだけですね。

_CallRestAPI();

結果

こちらはServerから返ってきたの返事です。

特定したHeader値です。

While loop使用しmail Propertiesの値をとります。

最後に

SampleCodeどうぞ↓

https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT%20Project_RESET_Example1_GetRequest2ExampleServer.tnzip

シーメンスのS71500版は昔書いたことありますのでよかったらどうぞ↓

​​http://soup01.com/ja/2021/07/21/post-3045/

はーい、お疲れ様です。

もしなにか質問あれば、メール・コメント・Twitterなどでもどうぞ!

Twitterのご相談:@3threes2

メールのご相談:soup01threes*gmail.com (*を@に)

Footer_Basic

Find ME

Twitter:@3threes2
Email:soup01threes*gmail.com (* to @)
YoutubeChannel:https://www.youtube.com/channel/UCQ3CHGAIXZAbeOC_9mjQiWQ

シェアする

  • このエントリーをはてなブックマークに追加

フォローする