この記事ではOMRON NX-1 CPUにOPC UA Serverを立ち上げ、次はUaExpert・Beckoff TwinCAT TF6100 PLC OPEN Library・最後はCtrlX Virtual CoreとNode Red Appsの連携でそのServerをアクセスしてみます。よろしくお願いします。
Implementation1
最初の実機はまずNX CPUにOPC UA Serverを立ち上げ、UaExpertから接続確認を行います。
Reference Link
http://soup01.com/ja/category/omron%e3%82%aa%e3%83%a0%e3%83%ad%e3%83%b3/
OMRON Side
Port 1 Configuration
Configuration and Setup>Controller Setup>Operation Settings>Built-in Ethernet/IP Port SettingsからPort1のIPを変更できます。注意するのはOPC UA Serverとして利用できるのはPort1のみです。
Enable Server
次はOPC UA Server Settings>OPC UA ServerをUseにします。
Create User
次はUserを設定します。OPC UA Settings>OPC UA Server Server Settings>右クリック>Security Settingsします。
+Buttonで新しいUserを設定します。
User名とPasswordを追加しましょう。
Create Node
次はOPC UA Server用のNodeを定義します。Programming>Data>Global Variablesをクリックします。
Global VariablesでNetwork PublishのFieldあり、そこから該当する変数をOPC UA Serverに公開するか設定できます。
Nodes
ManaulからNetwork Publish項目にあまり詳しく説明していませんので、Do not Publish・Publish Only・Input・Output・構造体、5つの変数を追加しテストします。
Result
プロジェクトをCPUにDownloadし、ツールをMonitor Modeに切り替えます。
OPC UA Settings>OPC UA Server Settings>右クリック>Server Statusをクリックします。
現在NX CPUのOPC UA Server稼働状況を確認できます。
いまはOPCUA Serverは”Use”の状態でServer operating statusがRunningになっています。よし、OPC UA Serverが稼働していますね。
次はUaExpertから接続状態を確認します。+ButtonでConnection追加します。
Custom Discovery>+ <Double click to Add Server>で新規のServer Connectionを追加します。
URLはOmronのIPアドレスを入れましょう。
Noneで接続します。
次は追加されたServerを選び>接続します。
Done!次はRoot>Objects>new_Controller_0>GlobalVarsまで展開すると最初に追加したNodeが確認できます。それらのNodeを選び右にある空きのところにDropします。
Read OK
まずは読み取りがOkっぽいですね。Do not Publish以外の変数は全部アクセスできます。
Write OK
次は書き込みですね。同じくDo not Publish以外の変数は全部変更できますね。
どうやら関係あるのはDo not Publishだけです。
BadCertificate TimeInvalid..?
もしUaExpertからアクセスするときBadCertificate TimeInvalidエラーが発生した場合、ServerのCertificateをTrusted Folderに移動することができません、そして毎回も同じのエラーが表示されます。
sysmac studio からController>Controller Clockをクリックします。
Controllerの時間調整画面が表示されます。
Synchronize with computerのButtonからControllerの時間を自分のPCと同期します。
最後はApplyすればOkです。
次はControllerのServer証明書を更新します。OPC UA Settings>OPC UA Server Settings>右クリック>Server Certificateします。
Server証明書の管理画面が表示されます。
そこでServer 証明書の有効期限がすでに切れていることがわかります。それはControllerの時間が古いのせいで証明書もその古い日付から生成されたので、UaExpertからはこの期限切れの証明書を信頼できません。
Regenerate Certificateで現在の時間からもう一度証明書を生成します。
OPC UA Server証明書の情報を入力し、OKします。
Yesで進みます。
暫く待ちます…
Done!新しい証明書が発行されました。
もう一度UaExpertからOmronのOPC UA Serverにアクセスすると、Trust Server CertificateのButtonが押せるようになりました。
ContinueでOKです。
Implementation2
今回はBeckoff TwinCAT TF6100でPLC OPEN OPC UA Client LibraryIからOMRON NX-1 CPUのOPC UA Serverにアクセスしてみます。
Reference Link
Add PLC
TwinCATで新しいプロジェクトを作成し、PLC>Add New Itemで新しいPLCを追加します。
Standard PLC Projectを選び、Addします。
Add Library
次はOPC UA APIのLibraryを追加します。References>Add libraryクリックします。
Tc3_PLCOpen_OpcUaを検索し追加しましょう。
Done!
Check Namespace
PLC openのAPIを使用するには、まずアクセスしたいNodeのNamespaceを確認する必要があります。Root>Objects>Server>NamespaceArrayを展開します。
そのNamespaceはString Array[5]で中に5つのString変数があります。
Check identifier
次は各NodeのAttributesからIdentifierの項目があり、そのIdentifierはAPIを使用するときに必要です。
DUT
PLCプロジェクトの中で構造体を作成します。
DUT_OPCUA
こちらはOmron NX1 CPUの構造体 Nodeに合わせて定義したNodeです。
TYPE DUT_OPCUA : STRUCT b1:BOOL; int1:INT; real1:REAL; END_STRUCT END_TYPE |
uDUT_Real
次は実数データと4 Bytesに分解できるようにUnion構造体を定義します。
TYPE uDUT_Real : UNION myReal :REAL; _ar :ARRAY[0..3]OF BYTE; END_UNION END_TYPE |
MAIN
次はMain Programです。
VAR
PLCOPENのAPIで使用するFunction Block用のInstanceを宣言します。
PROGRAM MAIN VAR //UA Connect UA_Connect:UA_Connect; SessionConnectInfo :ST_UASessionConnectInfo; //UA Disconnect UA_Disconnect:UA_Connect; //Polling Functionlity //Get NameIndex UA_GetNamespaceIndex:UA_GetNamespaceIndex; nNameSpaceIndex :UINT; //Get Handle UA_NodeGetHandle:UA_NodeGetHandle; NodeID :ST_UANodeID; nNodeHdl :DWORD; //UA Read UA_Read :UA_Read; stIndexRange : ARRAY [1..nMaxIndexRange] OF ST_UAIndexRange; nIndexRangeCount : UINT; stNodeAddInfo : ST_UANodeAdditionalInfo; nReadData : INT; cbDataRead : UDINT; //UA Node Release Handle UA_NodeReleaseHandle : UA_NodeReleaseHandle; // iStep :INT; bConnect :BOOL; bDisconnect :BOOL; ConnectionHdl :DWORD; myStructureNode :DUT_OPCUA; cbData_R :UDINT; myInt :INT; myReal :uDUT_Real; _tempByte :BYTE; TON :TON; ErrorMessage :STRING; ErrorID :DWORD; bReset :BOOL; END_VAR VAR CONSTANT cSererUrl :STRING:=’opc.tcp://192.168.11.18:4840′; cNameSpace :STRING:=’urn:OMRON:NxOpcUaServer:FactoryAutomation’; END_VAR |
PROGRAM
OPC UA Serverと接続>NameSpaceIndexを取得>NodeのHandleを取得>Node値を読み取る>Loop Backのようなプログラムです。
myInt:=SHL(myStructureNode.int1,8)+SHR(myStructureNode.int1,8); myReal.myReal:=myStructureNode.real1; _tempByte:=myReal._ar[0]; myReal._ar[0]:=myReal._ar[1]; myReal._ar[1]:=_tempByte; _tempByte:=myReal._ar[2]; myReal._ar[2]:=myReal._ar[3]; myReal._ar[3]:=_tempByte; CASE iStep OF 0: IF bConnect THEN UA_Connect(Execute:=FALSE); UA_Disconnect(Execute:=FALSE); UA_GetNameSpaceIndex(Execute:=FALSE); UA_NodeGetHandle(Execute:=FALSE); UA_Read(Execute:=FALSE); UA_NodeReleaseHandle(Execute:=FALSE); SessionConnectInfo.eSecurityMode:=eUASecurityMsgMode_None; SessionConnectInfo.eSecurityPolicyUri:=eUASecurityPolicy_None; SessionConnectInfo.eTransportProfileUri:=eUATransportProfileUri_UATcp; SessionConnectInfo.tConnectTimeout:=T#1M; SessionConnectInfo.tSessionTimeout:=T#1M; IF NOT UA_Connect.Busy AND NOT UA_Disconnect.Busy AND NOT UA_GetNameSpaceIndex.Busy AND NOT UA_NodeGetHandle.Busy AND NOT UA_NodeGetHandle.Busy AND NOT UA_NodeReleaseHandle.Busy THEN iStep:=10; bConnect:=FALSE; end_if; END_IF 10: UA_Connect( Execute:=TRUE ,ServerUrl:=cSererUrl ,SessionConnectInfo:=SessionConnectInfo ,ConnectionHdl=>ConnectionHdl ); IF UA_Connect.Error THEN iStep:=999; END_IF IF UA_Connect.Done AND NOT ua_Connect.Error THEN iStep:=20; UA_Connect(Execute:=FALSE); ELSIF ua_connect.Error THEN iStep:=999; END_IF 20: UA_GetNamespaceIndex( Execute:=TRUE ,ConnectionHdl:=ConnectionHdl ,NamespaceUri :=cNameSpace ,NamespaceIndex=>nNameSpaceIndex ); IF UA_GetNamespaceIndex.Done AND NOT UA_getNamespaceIndex.Error THEN iStep:=30; UA_GetNamespaceIndex(execute:=FALSE); ELSIF UA_getNamespaceIndex.Error THEN iStep:=999; END_IF 30: NodeID.eIdentifierType:=eUAIdentifierType_String; NodeID.nNamespaceIndex:=nNameSpaceIndex; NodeID.sIdentifier:=’MyDUTNode’; UA_NodeGetHandle( Execute:=TRUE ,ConnectionHdl:=ConnectionHdl ,NodeID:=NodeID ,NodeHdl=>nNodeHdl ); IF UA_NodeGetHandle.Done AND NOT UA_NodeGetHandle.Error THEN UA_NodeGetHandle(Execute:=FALSE); iStep:=40; ELSIF UA_NodeGetHandle.Error THEN iStep:=999; END_IF 40: UA_Read( Execute:=TRUE ,ConnectionHdl:=ConnectionHdl ,NodeHdl:=nNodeHdl ,cbData:=SIZEOF(myStructureNode) ,stNodeAddInfo:=stNodeAddInfo ,pVariable:=ADR(myStructureNode) ); IF UA_Read.Done AND NOT UA_Read.Error THEN UA_Read( Execute:=FALSE ,cbData_R=>cbData_R ); iStep:=45; ELSIF UA_Read.Error THEN iStep:=999; END_IF; 45: TON(in:=TRUE,PT:=T#0.2S); IF ton.Q THEN TON(in:=FALSE); IF bdisconnect THEN iStep:=50; ELSE istep:=40; END_IF; END_IF 50: UA_NodeReleaseHandle( Execute:=TRUE ,ConnectionHdl:=ConnectionHdl ,NodeHdl:=nNodeHdl ); IF UA_NodeReleaseHandle.Done AND NOT UA_NodeReleaseHandle.Error THEN UA_NodeReleaseHandle(Execute:=FALSE); iStep:=200; ELSIF UA_NodeReleaseHandle.Error THEN iStep:=999; END_IF 200: IF bDisconnect THEN iStep:=210; bDisconnect:=FALSE; END_IF 210: UA_Disconnect( Execute:=TRUE ,ServerUrl:=cSererUrl ,SessionConnectInfo:=SessionConnectInfo ,ConnectionHdl=>ConnectionHdl ); IF UA_Disconnect.Error THEN iStep:=999; END_IF IF UA_Disconnect.Done AND NOT UA_Disconnect.Error THEN iStep:=0; UA_Disconnect(Execute:=FALSE); END_IF 999: IF UA_Connect.Error THEN ErrorMessage:=’Error in UA_Connect.’; ErrorID :=UA_Connect.ErrorID; ELSIF UA_Disconnect.Error THEN ErrorMessage:=’Error in UA_Disconnect.’; ErrorID :=UA_Disconnect.ErrorID; ELSIF UA_GetNamespaceIndex.Error THEN ErrorMessage:=’Error in UA_GetNamespaceIndex.’; ErrorID :=UA_GetNamespaceIndex.ErrorID; ELSIF UA_NodeGetHandle.Error THEN ErrorMessage:=’Error in UA_NodeGetHandle.’; ErrorID :=UA_NodeGetHandle.ErrorID; ELSIF UA_Read.Error THEN ErrorMessage:=’Error in UA_Read.’; ErrorID :=UA_Read.ErrorID; ELSIF UA_NodeReleaseHandle.Error THEN ErrorMessage:=’Error in UA_NodeReleaseHandle.’; ErrorID :=UA_NodeReleaseHandle.ErrorID; END_IF; IF breset THEN ErrorMessage:=”; ErrorID:=0; END_IF END_CASE |
Result
Done!Omron NX1 CPUのデータを読みました。
構造体で一気変数を取ることになったので、ByteのLittle EndianとBig Endianが異なると現在値がおかしくなります。なので、簡単なSwap プログラムを作成しByteの並び順を変更します。
正しい現在値になりました!
ちなみに、こちらはBig EndianのByte並び順です。
こちらはLittle EndianのByte並び順です。
Implementation3
最後はBosch rexrothのCtrlXとNode Red Appsの連携でOmron Nx-1 CPUのOPC UA Server Nodeを読み取ります。
NODE-RED APP?
Node-Red AppはBosch Rexroth Ctrlx automationから提供されたNode-Red Frameworkで、そのOpen source toolはApach2 2.0ライセンスとIBM社がGraphic開発環境を開発しました。
Bosch rexroth Ctrlx AutomationのNode-Red AppはCtrl storeからDownloaでき、インストールすればCtrlx Nodeも使用でき、より簡単でCtrlx CoreとData Layer上でデータ交換が可能なリます。
- Node-RED Runtime
- Flow Editor
- Dashboard
Reference Link
Omron Side
今回はOmron側の変数を変換できるように簡単なプログラムを作成します。
Delete the Ladder program
まずPOUs>ProgramsでDefault作成されたラダープログラムを削除します。
Yesで進みます。
Add ST Program
Programs>右クリック>Add>STでSTプログラムを追加します。
Internals
こちらはSTプログラムの内部変数です。
myTimerとMyStepは変数の変化 Reflesh用です。
Programs
MyDUTNode.int1:=MyDUTNode.int1+1; CASE myStep OF 0: MyDUTNode.b1:=TRUE; myTimer(In:=True,PT:=T#1s); IF myTimer.Q THEN myTimer(In:=False); myStep:=1; END_IF; 1: MyDUTNode.b1:=False; myTimer(In:=True,PT:=T#1s); IF myTimer.Q THEN MyDUTNode.real1:=MyDUTNode.real1+1.1; myTimer(In:=False); myStep:=0; END_IF; END_CASE; |
Task Settings
新しいPOUが追加されたので、Task Settingsから該当するプログラムを割り付ける必要があります。Configuration and Setup>Task Settingsを開きます。
+Buttonで新しい実行POUを追加します。
Done!
ctrlx Side
PLC Side
TwinCATと同じくctrlX 側にもOmron NX1 CPUの構造体Nodeに合わせるように宣言する必要があります。
Add DUT
Applications>Add Object>DUTで構造体を追加します。
Omron NX1 CPUと同じような構造を宣言しましょう。
TYPE DUT_TestFromOPCUA : STRUCT b1:BOOL; int1:INT; real1:REAL; END_STRUCT END_TYPE |
GVL
そしてGVLで変数を宣言します。
{attribute ‘qualified_only’} VAR_GLOBAL myData:INT; Node,Node_Buffer:DUT_TestFromOPCUA; END_VAR |
Node-Red APPs
ctrlx Storeからctrlx CORE-NODE-RED AppをDownloadし、自分のVirtualCoreにインストールしてください。
VirtualCoreのWeb serverからNode-Redが追加されたはずです。
中にFlow EditorとDashboardがありますが、Flow Editorを開きNode-Red Flowを追加します。
いつも通りのNode-Red編集画面に変わりました。
Node Installation
OPC UA Nodeをインストールするため、Manage paletteをクリックします。
OPCUAを検索し、node-red-contrib-opcua Nodeをインストールします。
Installで進みます。
Done!OPC UA Nodeが追加されました。
IF Error..
もしNode-redで新しいNodeをインストールするときDNSエラーがあったら、それは
Vritual Coreは実際Internetの接続に問題があるんです。なぜかというとVirtualCoreは仮想環境で稼働していますからね!
npm ERR! request to https://registry.npmjs.org/express failed, reason: getaddrinfo EAI_AGAIN registry.npmjs.org |
VirtualCoreを一旦停止し、NetworkをPort Forwardingに設定します。
次はVirtualCoreを起動し、CoreのWeb serverからSettings>Connectivelyを開きます。
Network interfaceのDHCP機能 Enableします。
Enable IP ForwardingをTrueします。そうするとNodeのインストールができると思います。
Port Configuration
NX-CPUと接続するには、Port 設定はPort Forwardingにし、Extended Accessは実際NX-CPUと接続してるInterface cardを設定しましょう。
VirtualCoreのIP addressは該当するInterface card:8443に変わります。
次はVirtualCoreを起動し、CoreのWeb serverからSettings>Connectivelyを開きます。
DHCPをEnableします。
Enable IP forwardingをTrueします。
Flow-1Test with ctrlX Node
Setupが終わったらまずCtrlx Nodeをテストしてみます。そのNodeを使用することによりCtrlX Virtual CoreのData Layerに簡単にアクセスできます。
Data Layer RequestをFlowに追加します。
Add Inject Node
周期でCtrlX Requset Nodeをリクエスト発行するようにInject Nodeを追加します。
InjectとCtrlX Requset Nodeを繋がります。
Add Debug Node
CtrlX Requsetの結果も確認したいので、Debug Nodeを追加します。
DebugのInputとCtrlX RequsetのOutputと繋がります。
Ctrlx Request Ndoe
最後はCtrlx Nodeを編集し、Device FieldはVritual CoreのIP:8443を入力しましょう。
Path
Path FieldからCtrlXのData Layerの変数をBrowseできます。
(ある意味では仮のConnection Testで設定したパラメータが正しいかを確認)
Done!設定が間違ってないようです。
アクセスしたい変数を選んだら、自動的にPathが設定されます。
Payload
Payloadは実際Ctrlx Nodeにリクエストを送信するときのPayload Formatを設定できます。DefaultあValue Onlyです。
今回の記事ではValue+ Type(Json)を設定します。
Method-Read
Methodは該当するPathのリクエスト種類を設定できます。SUB/PUB/WRITE/READなど様々な方法が用意されています。
MethodをRead選択します。
Doneで設定を保存します。
Done!データが読み取れました!
CtrlX Nodeの下にautherlicatedと緑四角が表示され、それは接続してるだと示しています。
Method-Write
今度はCtrlx Data Layerの変数に新たな値を書き込みます。
MethodをWRITEに設定します。
Function Nodeを追加します。Inject Nodeが一回Function Nodeを通ってからCtrlx NodeにInputと繋がります。
Function の中に簡単なjavascriptを追加します。
var m={}; m.payload={“type”:”int16″,”value”:1234}; |
Done!データが正常に書き込まれました!
Node.int1の現在値も1234に変わりました。
Connect with Omron OPC UA Server
Ctrlx Nodeのテストが終わったところで、次はOmron NX1 CPUと接続しにいきます。
Add OpcUa -Client Node
Node-Red FlowからOpcUa – Client Ndoeを追加します。
Endpointなどの設定はOmron NX1 CPUに合わせて設定しましょう。
もしEndpointが変更が必要の場合、鉛筆ICONをクリックしてください。
Security Settingsなどの接続設定を変更できます。
Add inject Node
Inject Nodeを追加します。
今回はNX-CPUのOPC UA Serverにある構造体変数MyDUTNodeにアクセスしたいので、UaExpertからそのNodeIDを確認します。
Inject Ndoeにmsg.topicを追加し、先ほど調べたNodeIDを入れます。そしてRepeat 設定をIntervalに変更し、周期が1sだと設定します。
Done!
Add Function Node
Function Ndoeを追加し、OPC UA Serverにアクセスし取得したデータをそれらのNode経由でCtrlx Data Layerに書き込む前の簡単な前処理です。
Write_node_b1
var m = {}; m.payload = { “type”: “bool8”, “value”: msg.payload.b1 }; return m; |
write_node_int1
var m = {}; m.payload = { “type”: “int16”, “value”: msg.payload.int1 }; return m; |
write_node_real1
var m = {}; m.payload = { “type”: “float”, “value”: msg.payload.real1 }; return m; |
Add ctrlX Request
Ctrlx Nodeを追加します。
Function Nodeの出力をCtrlx Nodeの入力と繋がります。
Ctrlx Nodeの接続設定はFlow-1 Testingを参照してやりましょう。
Add debug Node
最後はDebug Ndoeを追加し結果を確認できるようにします。
出力するのはPayloadだけでよいでしょう。
Done!
最後はCtrlX Nodeの出力をDebugの入力と繋がります。
Finally Flow
こちらは全体的のFlowです。
Result
OPC UA Client Nodeの下に緑四角とActive readingのメッセージが表示されたらOKです!
データも正常に読み書きできました。
IF can not connect
もしどうしても接続できないなら、Firewallのルールに問題があるかもしれません。
Outbound RulesでPort 4840をEnableします。
Inputboud RulesもPort 4840をEnableします。