いまの工場ではIOT40の時代に既に様々なデータを可視化するのが課題です。しかも可視化だけではなく安全に繋がる、拡張性ある、簡単に使える、リモート操作などの要求も出てきてます。
今回はMeraki D-1 グランプという活動がたまたまネットで見つかり、参加してみようと思ってCiscoさんからMerakiのスイッチを借りてきました。なので今回の記事ではそのCisco Meraki MS120-8FPスイッチを例にしてみます。Cisco Merakiのデバイスでは設定はCloud上で管理し、APIが提供して3rd Third Partyからデバイスの状態を取得や変更することもできます。さらにWebhook機能がついておりイベント発生するときリアルタイムでお知らせすることができます。
Configuration
こちらが今回の構成になります。Cisco Meraki MS120-8FPスイッチは自宅のルータとつながりMerakiのCloudと接続させます。
Beckhoff社のTwinCAT3(SoftwarePLC)は別PCであり、無線で別Networkからインタネット接続し、TF6760使用しMeraki APIをアクセスしてCisco Meraki MS120-8FPの状態を取得しTF1800表示します。
さらにRaspberry Piがあり、Python製のLocal Serverを立ち上げ、Ngrokサービス経由でLocal ServerをPublishします。Cisco Meraki のWebhookサービスとそのNrgok サービスを接続しEvent発生するときのお知らせをRaspberry Piにも受け取ることができます。
Raspberry PiではMQTT Brokerを構築しておりCisco Meraki のWebhookから受け取った情報をMQTT TopicにMessage をPublishします。そTopicはTwinCATがSubscribeしており、最終的にTwinCAT3が間接的Webhookの情報を受け取ることになる。MessageをLog貯めてTF1800に表示する。
Meraki Side
まず以下の記事から基本のセットアップを完了してください。
次は以下の記事でAPIを有効にする方法を書いています。
あなたのCisco Meraki Deviceは基本Setup完了し、API Keyも持ってるはずです。
Raspberry PI Side
MQTT Broker セットアップはこちらの記事を参考にしてください。
PythonからSubscribe/Publishする
Local Server をNgrokにPublush
Cisco Meraki WebhookとRaspberryの連携:
Webhook をMQTTに変換する
データをWehbookから受け取ったあとにMQTT Message でTopicをPublishします。
以下はそのコードです。
import http.server import socketserver import json import paho.mqtt.client as mqtt import time def on_connect(client,userdata,flag,rc): print(‘connected with result code’+str(rc)) def on_disconnect(client,userdata,rc): if rc!=0: print(‘something wrong..’) def on_publish(client,userdata,mid): print(‘publish:{0}’.format(mid)) URL=’127.0.0.1′ PORT=1883 BASE=’/Switch1′ FIX_TOPIC_WEBHOOK=’Wehook’ FIX_TOPIC_ALERT=’Alert’ FIX_TOPIC_DATE=’Date’ class MyHandler(http.server.BaseHTTPRequestHandler): def do_POST(self): self.send_response(200) self.end_headers() print(self.path) content_length = int(self.headers[‘Content-Length’]) #post_data = self.rfile.read(content_length) #post_data_string=post_data.decode() #post_data_json=json.loads(post_data_string) data_in_json=json.loads(self.rfile.read(content_length).decode()) #d[‘sentAt’] #d[‘deviceName’] #d[‘deviceSerial’] #d[‘alertType’] #d[‘alertTypeId’] #d[‘alertLevel’] #d[‘occurredAt’] client=mqtt.Client() client.on_connect=on_connect client.on_disconnect=on_disconnect client.on_publish=on_publish client.connect(URL,PORT,60) #client.publish(‘/Switch1/hello’,’hallo’) NetworkName=data_in_json[‘networkName’] DeviceName=data_in_json[‘deviceName’] client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/ID/’,data_in_json[‘alertTypeId’]) client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Type/’,data_in_json[‘alertType’]) client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Level/’,data_in_json[‘alertLevel’]) client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Occurred/’,data_in_json[‘occurredAt’]) client.disconnect() def do_GET(self): self.send_response(200,”GET is received.”) print(“GET is received”) self.end_headers() with socketserver.TCPServer((“”, 8001), MyHandler) as httpd: httpd.serve_forever() |
Raspberry PIはいまMQTT Broker立ち上げてる状態でCisco Meraki Webhook サービスからのお知らせを受け取ることができ、MQTT MessageをPublishします。
TwinCAT Side
Raspberry PiとCisco Meraki側も準備が終わったところで次はBeckhoff社のIPCです。少し難しそうにみえますが、使用するLibraryはTF6701とTF6760、そして表示用のTF1800だけなので複雑ではありません。
Library
TF6701
TF6701を使えばMQTTを使ってBrokerと接続できます。BrokerはLocalなものだけではなくCloud ServicesのAWS・Google・MS Azureなども可能です。
TF6760
TF6760を使用することによってServerにHTTPSリクエストを送信することや、JSON ObjectとPLC変数間の変換などができます。
TF1800
表示用のLibraryです。
Points when using Meraki API
FIXHeader
MerakiのAPI使用するにはすべてのRequestもX-Cisco-Meraki-API-KeyのHeaderをつける必要があります。TwinCATでHeaderを追加するにFB_IotHttpHeaderFieldMapというFunction Blockを使用しています。
{ “X-Cisco-Meraki-API-Key”: <Meraki_API_Key> } |
もしそのHeaderなしのリクエストをAPIに送信したら401 UnauthorizedがReturnします。
{ “errors”: [ “Invalid API key” ] } |
https://developer.cisco.com/meraki/api-v1/#!authorization
Response 302
APIのドキュメントによりますと、そのAPI Base URLはこちらです。
でもTwinCAT3のTF6760を使用するとリクエストは確かに正常に届きますが、Response 302が戻ります。Beckhoff TF6760のManualによりますと、TF6760のIOT DriverはRedirect Supportしません、なのでRedirect後のメッセージ、つまり100%正しいURLを引数として渡す必要があります。例えばHTTP Status Code 301ではServerがClientにリソースを別の場所に移動し、LocationのHeader FieldはそのHTTP Responsに付けてReturnしますね。
なので、最初にStatus 301,302がReturnされたらAPI URLをHeader Fieldの値に置き換えましょう。
ちなみに、Merakiの場合は以下のURLになります。
n424.meraki.com |
Array of Json Object
Meraki APIの返答はたまにJsonの配列がくるので、GetArrayValueByIdxやGetArraySizeのMethodでJSONを引き出す必要があります。
Function Block
次は今回使用するFunction Blockを紹介します。一部は前回のTutorialで説明したのでよかったら参考にしてください。
FB_IotHttpHeaderFieldMap
このFunction BlockをHTTPリクエストにHeaderを追加できます。そしてAddField () Methodを呼び出してHeaderを追加します。最後はそのFunction BlockをHttp Commandの引数として渡します。
VAR_OUTPUT
Variables | Data Type | Description |
bError | BOOL | True=エラー発生中 |
hrErrorCode | HRESULT | ADS Return Code |
Method – AddField
呼び出すとHeaderを追加します。
VAR_INPUT
Variables | Data Type | Description |
sField | STRING | HTTP Requestに追加するHeader |
sValue | STRING | そのHeader値 |
bAppendValue | BOOL | False=現在のHeader付加するTrue=現在のHeaderをクリアして追加 |
Return Value
Variables | Data Type | Description |
AddField | BOOL | True=Method 実行成功 |
Implementation – Meraki API Side
GET Organizations
現在のorganizationsをリストアップして戻す。
https://developer.cisco.com/meraki/api-v1/#!get-organizations
Request
GET /organizations curl https://api.meraki.com/api/v1/organizations \ -L -H ‘X-Cisco-Meraki-API-Key: {MERAKI-API-KEY}’ |
Response
JSON Arrayが回答します。
Flow
Json Obejctが有効かをCheck>そしてJSON処理Reference が有効かCheck>Json Array からJsonObjectを取り出し、該当するMemeberがあるとさらにそのJson変数をTwinCAT変数に変換します。
DUT_Meraki_API_organizations
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_organizations : STRUCT Id :STRING; Url :STRING; Name :STRING; Licensing :STRING; API :Bool; Cloud :STRING; END_STRUCT END_TYPE |
FC_Meraki_Organizations_JsonData_Encoder
その関数はAPIから返答されたJSON Objectを引数として渡し、内部で文字列などに変換します。
VAR
FUNCTION FC_Meraki_Organizations_JsonData_Encoder : DUT_Meraki_API_organizations VAR_INPUT jsonDoc :SJsonValue; FBJson :REFERENCE TO FB_JsonDomParser; END_VAR VAR udi32JsonObjectLength: UDINT; jsonDocBase:SJsonValue; jsonval: SJsonValue; bValid:BOOL; END_VAR |
PROGRAM
// Reset all the Return to Zero FC_Meraki_Organizations_JsonData_Encoder.Id:=”; FC_Meraki_Organizations_JsonData_Encoder.Cloud:=”; FC_Meraki_Organizations_JsonData_Encoder.API:=FALSE; FC_Meraki_Organizations_JsonData_Encoder.Licensing:=”; FC_Meraki_Organizations_JsonData_Encoder.Name:=”; FC_Meraki_Organizations_JsonData_Encoder.Url:=”; //Check the Reference is Valid or not bValid:=__ISVALIDREF(FBJson); IF jsonDoc <> 0 and bValid THEN //Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1); //Get id IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’); FC_Meraki_Organizations_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval); END_IF //Get Url IF fbJson.HasMember(v:=jsonDocBase,member:=’url’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’url’); FC_Meraki_Organizations_JsonData_Encoder.Url:=fbJson.GetString(v:=jsonval); END_IF //Get Name IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’name’); FC_Meraki_Organizations_JsonData_Encoder.name:=fbJson.GetString(v:=jsonval); END_IF //Get Licensing IF fbJson.HasMember(v:=jsonDocBase,member:=’licensing’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’licensing’); jsonval:=fbJson.FindMember(v:=jsonval,member:=’model’); FC_Meraki_Organizations_JsonData_Encoder.Licensing:=fbJson.GetString(v:=jsonval); END_IF //Get Api IF fbJson.HasMember(v:=jsonDocBase,member:=’api’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’api’); jsonval:=fbJson.FindMember(v:=jsonval,member:=’enabled’); FC_Meraki_Organizations_JsonData_Encoder.API:=fbJson.GetBool(v:=jsonval); END_IF //Get Cloud IF fbJson.HasMember(v:=jsonDocBase,member:=’cloud’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’cloud’); jsonval:=fbJson.FindMember(v:=jsonval,member:=’region’); jsonval:=fbJson.FindMember(v:=jsonval,member:=’name’); FC_Meraki_Organizations_JsonData_Encoder.Cloud:=fbJson.GetString(v:=jsonval); END_IF END_IF |
GET Networks
現在Network情報が返答されます。
https://developer.cisco.com/meraki/api-v1/#!get-network
Request
/networks/{networkId} curl -L –request GET \ –url https://api.meraki.com/api/v1/networks/{networkId} \ –header ‘Content-Type: application/json’ \ –header ‘Accept: application/json’ \ –header ‘X-Cisco-Meraki-API-Key: 6bec40cf957de430a6f1f2baa056b99a4fac9ea0’ |
Response
Network ObjectがReturnされます。
Flow
Json Obejctが有効かをCheck>そしてJSON処理Reference が有効かCheck>Json Array からJsonObjectを取り出し、該当するMemeberがあるとさらにそのJson変数をTwinCAT変数に変換します。
DUT_Meraki_API_Networks
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_Networks : STRUCT Id :STRING; OrganizationId :STRING; NetworkType :STRING; Name :STRING; TimeZone :STRING; END_STRUCT END_TYPE |
FC_Meraki_Networks_JsonData_Encoder
その関数はAPIから返答されたJSON Objectを引数として渡し、内部で文字列などに変換します。
VAR
FUNCTION FC_Meraki_Networks_JsonData_Encoder : DUT_Meraki_API_Networks VAR_INPUT jsonDoc :SJsonValue; FBJson :REFERENCE TO FB_JsonDomParser; END_VAR VAR udi32JsonObjectLength: UDINT; jsonDocBase:SJsonValue; jsonval: SJsonValue; bValid:BOOL; END_VAR |
PROGRAM
// Reset all the Return to Zero FC_Meraki_Networks_JsonData_Encoder.Id:=”; FC_Meraki_Networks_JsonData_Encoder.Name:=”; FC_Meraki_Networks_JsonData_Encoder.NetworkType:=”; FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=”; FC_Meraki_Networks_JsonData_Encoder.TimeZone:=”; //Check the Reference is Valid or not bValid:=__ISVALIDREF(FBJson); IF jsonDoc <> 0 AND bValid THEN //Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1); //Get id IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’); FC_Meraki_Networks_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’organizationId’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’organizationId’); FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN FC_Meraki_Networks_JsonData_Encoder.Name:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’timeZone’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’timeZone’); FC_Meraki_Networks_JsonData_Encoder.TimeZone:=fbJson.GetString(v:=jsonval); END_IF END_IF |
Get Device
現在のDevice情報が返答されます。
https://developer.cisco.com/meraki/api-v1/#!get-device
Request
GET /devices/{serial} curl -L –request GET \ –url https://api.meraki.com/api/v1/devices/{serial} \ –header ‘Content-Type: application/json’ \ –header ‘Accept: application/json’ \ –header ‘X-Cisco-Meraki-API-Key: MERAKI-API-KEY-MERAKI-API-KEYMERAKI-API-KEY’ |
Response
要求したSerial番号のデバイス情報がReturnされます。
Flow
Json Obejctが有効かをCheck>そしてJSON処理Reference が有効かCheck>Json Array からJsonObjectを取り出し、該当するMemeberがあるとさらにそのJson変数をTwinCAT変数に変換します。
DUT_Meraki_API_Devices
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_Devices : STRUCT Name :STRING; Lat :LREAL; Lng :LREAL; Serial :STRING; Mac :STRING; Address :STRING; Notes :STRING; Lanip :STRING; Firmware :STRING; END_STRUCT END_TYPE |
FC_Meraki_Devices_SetRequestURL
リクエストのURLが複雑になり、別の関数を作成しURLを構築します。
VAR
FUNCTION FC_Meraki_Devices_SetRequestURL : String VAR_INPUT id:STRING; END_VAR VAR sId:STRING; END_VAR |
PROGRAM
sId:=”; sId:=CONCAT(‘/api/v1/networks/’,id); sId:=CONCAT(sId,’/devices’); FC_Meraki_Devices_SetRequestURL:=sId; |
FC_Meraki_Networks_JsonData_Encoder
その関数はAPIから返答されたJSON Objectを引数として渡し、内部で文字列などに変換します。
VAR
FUNCTION FC_Meraki_Networks_JsonData_Encoder : DUT_Meraki_API_Networks VAR_INPUT jsonDoc :SJsonValue; FBJson :REFERENCE TO FB_JsonDomParser; END_VAR VAR udi32JsonObjectLength: UDINT; jsonDocBase:SJsonValue; jsonval: SJsonValue; bValid:BOOL; END_VAR |
PROGRAM
// Reset all the Return to Zero FC_Meraki_Networks_JsonData_Encoder.Id:=”; FC_Meraki_Networks_JsonData_Encoder.Name:=”; FC_Meraki_Networks_JsonData_Encoder.NetworkType:=”; FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=”; FC_Meraki_Networks_JsonData_Encoder.TimeZone:=”; //Check the Reference is Valid or not bValid:=__ISVALIDREF(FBJson); IF jsonDoc <> 0 AND bValid THEN //Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1); //Get id IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’); FC_Meraki_Networks_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’organizationId’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’organizationId’); FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN FC_Meraki_Networks_JsonData_Encoder.Name:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’timeZone’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’timeZone’); FC_Meraki_Networks_JsonData_Encoder.TimeZone:=fbJson.GetString(v:=jsonval); END_IF END_IF |
Get Device Switch Ports
デバイスのPorts情報全部取得します。
Request
GET /devices/{serial}/switch/ports/statuses curl -L –request GET \ –url https://api.meraki.com/api/v1/devices/{serial}/switch/ports/statuses \ –header ‘Content-Type: application/json’ \ –header ‘Accept: application/json’ \ –header ‘X-Cisco-Meraki-API-Key: ‘MERAKI-API-KEY-MERAKI-API-KEYMERAKI-API-KEY’ |
Response
す. すべてのPorts情報が含まれてるJSON ArrayがReturnされます。
Flow
Json Obejctが有効かをCheck>そしてJSON処理Reference が有効かCheck>Json Array からJsonObjectを取り出し、該当するMemeberがあるとさらにそのJson変数をTwinCAT変数に変換します。
DUT_Meraki_API_Ports
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_Ports : STRUCT PortID :STRING; Name :STRING; Enabled :BOOL; PoeEnabled :BOOL; PortType :STRING; Vlan :LINT; VoiceVlan :LINT; IsolationEnabled :BOOL; LinkNegotiation :STRING; Udid :STRING; END_STRUCT END_TYPE |
FC_Meraki_Ports_SetRequestUrl
リクエストのURLが複雑になり、別の関数を作成しURLを構築します。
VAR
FUNCTION FC_Meraki_Ports_SetRequestUrl : String VAR_INPUT id:STRING; END_VAR VAR sId:STRING; END_VAR |
PROGRAM
sId:=”; sId:=CONCAT(‘/api/v1/devices/’,id); sId:=CONCAT(sId,’/switch/ports’); FC_Meraki_Ports_SetRequestUrl:=sId; |
FC_Meraki_Ports_JsonData_Enconder
その関数はAPIから返答されたJSON Objectを引数として渡し、内部で文字列などに変換します。
VAR
FUNCTION FC_Meraki_Ports_JsonData_Enconder : ARRAY[1..10] OF DUT_Meraki_API_Ports VAR_INPUT jsonDoc:SJsonValue; FBJson :REFERENCE TO FB_JsonDomParser; END_VAR VAR udi32JsonObjectLength: UDINT; jsonDocBase:SJsonValue; jsonval: SJsonValue; bValid:BOOL; i:INT; END_VAR |
PROGRAM
// Reset all the Return to Zero FOR i := 1 TO 10 DO FC_Meraki_Ports_JsonData_Enconder[i].Enabled:=FALSE; FC_Meraki_Ports_JsonData_Enconder[i].IsolationEnabled:=FALSE; FC_Meraki_Ports_JsonData_Enconder[i].LinkNegotiation:=”; FC_Meraki_Ports_JsonData_Enconder[i].Name:=”; FC_Meraki_Ports_JsonData_Enconder[i].PoeEnabled:=FALSE; FC_Meraki_Ports_JsonData_Enconder[i].PortID:=”; FC_Meraki_Ports_JsonData_Enconder[i].PortType:=”; FC_Meraki_Ports_JsonData_Enconder[i].Udid:=”; FC_Meraki_Ports_JsonData_Enconder[i].Vlan:=0; FC_Meraki_Ports_JsonData_Enconder[i].VoiceVlan:=0; END_FOR //Check the Reference is Valid or not bValid:=__ISVALIDREF(FBJson); IF jsonDoc <> 0 AND bValid THEN //Get the Basic Object, Json arary is returned from Meraki Api udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); FOR i:=1 TO UDINT_TO_INT(udi32JsonObjectLength) DO jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=i-1); //Get id IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’name’); FC_Meraki_Ports_JsonData_Enconder[i].Name:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’portId’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’portId’); FC_Meraki_Ports_JsonData_Enconder[i].PortID:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’enabled’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’enabled’); FC_Meraki_Ports_JsonData_Enconder[i].Enabled:=fbJson.GetBool(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’poeEnabled’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’poeEnabled’); FC_Meraki_Ports_JsonData_Enconder[i].PoeEnabled:=fbJson.GetBool(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’type’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’type’); FC_Meraki_Ports_JsonData_Enconder[i].PortType:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’vlan’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’vlan’); FC_Meraki_Ports_JsonData_Enconder[i].Vlan:=fbJson.GetInt64(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’voiceVlan’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’voiceVlan’); FC_Meraki_Ports_JsonData_Enconder[i].VoiceVlan:=fbJson.GetInt64(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’isolationEnabled’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’isolationEnabled’); FC_Meraki_Ports_JsonData_Enconder[i].IsolationEnabled:=fbJson.GetBool(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’linkNegotiationCapabilities’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’linkNegotiationCapabilities’); FC_Meraki_Ports_JsonData_Enconder[i].LinkNegotiation:=fbJson.GetString(v:=jsonval); END_IF IF fbJson.HasMember(v:=jsonDocBase,member:=’udld’) THEN jsonval:=fbJson.FindMember(v:=jsonDocBase,’udld’); FC_Meraki_Ports_JsonData_Enconder[i].Udid:=fbJson.GetString(v:=jsonval); END_IF END_FOR END_IF |
Get Device Switch Ports Statuses
該当するデバイスのすべて PortsのMonitor Statusを取得します。
https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses
Request
GET /devices/{serial}/switch/ports/statuses curl -L –request GET \ –url https://api.meraki.com/api/v1/devices/{serial}/switch/ports/statuses \ –header ‘Content-Type: application/json’ \ –header ‘Accept: application/json’ \ –header ‘X-Cisco-Meraki-API-Key: MERAKI-API-KEY-MERAKI-API-KEYMERAKI-API-KEY’ |
Response
Json ArrayがReturnします。
Flow
DUT_Meraki_API_MonitorPortStatus_configOverrides
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_MonitorPortStatus_configOverrides : STRUCT configType :STRING; allowedVlans :STRING; vlan :LINT; END_STRUCT END_TYPE |
DUT_Meraki_API_MonitorPortStatus_lldp
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_MonitorPortStatus_lldp : STRUCT systemName :STRING; systemDescription :STRING; portId :STRING; portDescription :STRING; chassisId :STRING; managementVlan :LINT; portVlan :LINT; managementAddress :LINT; systemCapabilities :STRING; END_STRUCT END_TYPE |
DUT_Meraki_API_MonitorPortStatus_secureConnect
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_MonitorPortStatus_secureConnect : STRUCT enabled :BOOL; active :BOOL; authenticationStatus :STRING; configOverrides :DUT_Meraki_API_MonitorPortStatus_configOverrides; END_STRUCT END_TYPE |
DUT_Meraki_API_MonitorPortStatus_usageInKb
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_MonitorPortStatus_usageInKb : STRUCT Total :LINT; Sent :LINT; Recv :LINT; END_STRUCT END_TYPE |
DUT_Meraki_API_MonitorPortStatus_usageInKbs
APIから返答されるObjectにそってDUTを作成します。
TYPE DUT_Meraki_API_MonitorPortStatus_usageInKbs : STRUCT Total :LREAL; Sent :LREAL; Recv :LREAL; END_STRUCT END_TYPE |
DUT_Meraki_API_MonitorPortStatus
TYPE DUT_Meraki_API_MonitorPortStatus : STRUCT portId :STRING; enabled :BOOL; status :STRING; isUplink :BOOL; errors :STRING; warnings :STRING; Speed :STRING; duplex :STRING; usageInKb :DUT_Meraki_API_MonitorPortStatus_usageInKb; cdp :DUT_Meraki_API_MonitorPortStatus_cdp; lldp :DUT_Meraki_API_MonitorPortStatus_lldp; clientCount :LINT; powerUsageInWh :LREAL; trafficInKbps :DUT_Meraki_API_MonitorPortStatus_usageInKbs; secureConnect :DUT_Meraki_API_MonitorPortStatus_secureConnect; END_STRUCT END_TYPE |
FC_Meraki_MonitorStatus_SetRequestURL
リクエストのURLが複雑になり、別の関数を作成しURLを構築します。
VAR
FUNCTION FC_Meraki_MonitorStatus_SetRequestURL : String VAR_INPUT id:STRING; END_VAR VAR sId:STRING; END_VAR |
PROGRAM
// https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses // /api/v1/devices/{serial}/switch/ports/statuses sId:=”; sId:=CONCAT(‘/api/v1/devices/’,id); sId:=CONCAT(sId,’/switch/ports/statuses’); FC_Meraki_MonitorStatus_SetRequestURL:=sId; |
FC_Meraki_MonitorStatus_JsonData_Enconder
その関数はAPIから返答されたJSON Objectを引数として渡し、内部で文字列などに変換します。
VAR
FUNCTION FC_Meraki_MonitorStatus_JsonData_Enconder : DUT_Meraki_API_MonitorPortStatus VAR_INPUT jsonDoc :SJsonValue; FBJson :REFERENCE TO FB_JsonDomParser; END_VAR VAR udi32JsonObjectLength: UDINT; jsonval: SJsonValue; jsonvalBuffer:SJsonValue; bValid:BOOL; END_VAR |
PROGRAM
// Reset all the Return to Zero FC_Meraki_MonitorStatus_JsonData_Enconder.cdp.address:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.clientCount:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.duplex:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.enabled:=FALSE; FC_Meraki_MonitorStatus_JsonData_Enconder.errors:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.isUplink:=FALSE; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.chassisId:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.managementAddress:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.managementVlan:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.portId:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.portVlan:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemCapabilities:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemDescription:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemName:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.portId:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.powerUsageInWh:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.active:=FALSE; FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.authenticationStatus:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.allowedVlans:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.configType:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.vlan:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.Speed:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.status:=”; FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Recv:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Sent:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Total:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Recv:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Sent:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Total:=0; FC_Meraki_MonitorStatus_JsonData_Enconder.warnings:=”; //Check the Reference is Valid or not bValid:=__ISVALIDREF(FBJson); IF jsonDoc <> 0 AND bValid THEN //usageInKb IF fbJson.HasMember(v:=jsonDoc,member:=’usageInKb’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’usageInKb’); //total IF fbJson.HasMember(v:=jsonval,member:=’total’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’total’); FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Total:=fbJson.GetInt64(v:=jsonvalBuffer); END_IF //sent IF fbJson.HasMember(v:=jsonval,member:=’sent’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’sent’); FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Sent:=fbJson.GetInt64(v:=jsonvalBuffer); END_IF //recv IF fbJson.HasMember(v:=jsonval,member:=’recv’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’recv’); FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.recv:=fbJson.GetInt64(v:=jsonvalBuffer); END_IF END_IF //trafficInKbps IF fbJson.HasMember(v:=jsonDoc,member:=’trafficInKbps’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’trafficInKbps’); //total IF fbJson.HasMember(v:=jsonval,member:=’total’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’total’); FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Total:=fbJson.GetDouble(v:=jsonvalBuffer); END_IF //sent IF fbJson.HasMember(v:=jsonval,member:=’sent’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’sent’); FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Sent:=fbJson.GetDouble(v:=jsonvalBuffer); END_IF //recv IF fbJson.HasMember(v:=jsonval,member:=’recv’) THEN jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’recv’); FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.recv:=fbJson.GetDouble(v:=jsonvalBuffer); END_IF END_IF //portId IF fbJson.HasMember(v:=jsonDoc,member:=’portId’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’portId’); FC_Meraki_MonitorStatus_JsonData_Enconder.portId:=fbJson.GetString(v:=jsonval); END_IF //enabled IF fbJson.HasMember(v:=jsonDoc,member:=’enabled’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’enabled’); FC_Meraki_MonitorStatus_JsonData_Enconder.enabled:=fbJson.GetBool(v:=jsonval); END_IF //status IF fbJson.HasMember(v:=jsonDoc,member:=’status’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’status’); FC_Meraki_MonitorStatus_JsonData_Enconder.status:=fbJson.GetString(v:=jsonval); END_IF //isUplink IF fbJson.HasMember(v:=jsonDoc,member:=’isUplink’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’isUplink’); FC_Meraki_MonitorStatus_JsonData_Enconder.isUplink:=fbJson.GetBool(v:=jsonval); END_IF //errors IF fbJson.HasMember(v:=jsonDoc,member:=’errors’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’errors’); FC_Meraki_MonitorStatus_JsonData_Enconder.errors:=fbJson.GetString(v:=jsonval); END_IF //warnings IF fbJson.HasMember(v:=jsonDoc,member:=’warnings’) THEN jsonval:=fbJson.FindMember(v:=jsonDoc,’warnings’); FC_Meraki_MonitorStatus_JsonData_Enconder.warnings:=fbJson.GetString(v:=jsonval); END_IF END_IF |
pMerakiAPI
Flow
VAR
PROGRAM pMerakiAPI VAR fbClient :FB_IotHttpClient; fbRequest :FB_IotHttpRequest; fbheader :FB_IotHttpHeaderFieldMap; // bSend:BOOL; Step:INT; sConect:STRING(9999); StringBuffer:STRING(512); //json jsonDoc:SJsonValue; fbJson : FB_JsonDomParser; fbJson2 : FB_JsonDomParser; bgetfromJson:BOOL; sId:STRING; //Meraki Meraki :DUT_Meraki; si2: UDINT; udi32JsonObjectLength: UDINT; jsonDocBase:SJsonValue; i:INT; udi32JsonSubObjectLength: UDINT; jsonvalBuffer:SJsonValue; END_VAR VAR CONSTANT cStep_Init :INT:=0; cStep_Meraki_Get_Organizations :INT:=10; cStep_Meraki_Process_Oranizations_Data :INT:=15; cStep_Meraki_GetNetworks :INT:=20; cStep_Meraki_Process_Networks_Data :INT:=21; cStep_Meraki_GetNetworkDevices :INT:=30; cStep_Meraki_Process_NetworkDevices_Data :INT:=31; cStep_Meraki_Get_Ports :INT:=40; cStep_Meraki_Process_Ports_Data :INT:=41; cStep_Meraki_Get_MonitorStatus :INT:=50; cStep_Meraki_Process_MonitorStatus_Data :INT:=51; END_VAR |
PROGRAM
CASE Step OF cStep_Init: (* Configurate the Fix Header of Cisco API The Meraki Dashboard API requires a header parameter of X-Cisco-Meraki-API-Key to provide authorization for each request. Basic Foramt curl https://api.meraki.com/api/v1/organizations \ -H ‘X-Cisco-Meraki-API-Key: {MERAKI-API-KEY}’ *) fbClient.sHostName:=’n424.meraki.com’; fbClient.nHostPort:=443; fbClient.stTLS.bNoServerCertCheck:=TRUE; fbheader.AddField( sField:=’X-Cisco-Meraki-API-Key’ ,sValue:=’YourAPIKEY’ ,bAppendValue:=FALSE ); bSend:=TRUE; Step:=cStep_Meraki_Get_Organizations; cStep_Meraki_Get_Organizations: (* List the organizations that the user has privileges on /organizations https://developer.cisco.com/meraki/api-v1/#!get-organizations *) IF bSend THEN fbRequest.sContentType:=’application/json’; IF fbRequest.SendRequest( sUri:=’/api/v1/organizations’ ,fbClient:=fbClient ,eRequestType:=ETcIotHttpRequestType.HTTP_GET ,pContent:=0 ,nContentSize:=0 ,fbHeader:=fbheader ) THEN Step:=cStep_Meraki_Process_Oranizations_Data; END_IF; bSend:=FALSE; END_IF cStep_Meraki_Process_Oranizations_Data: IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN fbRequest.GetContent( pContent:=ADR(sConect) ,nContentSize:=SIZEOF(sConect) ,TRUE ); Meraki.Organizations:= FC_Meraki_Organizations_JsonData_Encoder( jsonDoc:=fbRequest.GetJsonDomContent(fbJson) ,FBJson:=fbJson2 ); Step:=cStep_Meraki_GetNetworks; END_IF cStep_Meraki_GetNetworks: sId:=”; sId:=CONCAT(‘/api/v1/organizations/’,Meraki.Organizations.Id); sId:=CONCAT(sId,’/networks’); IF fbRequest.SendRequest( sUri:=sId ,fbClient:=fbClient ,eRequestType:=ETcIotHttpRequestType.HTTP_GET ,pContent:=0 ,nContentSize:=0 ,fbHeader:=fbheader ) THEN Step:=cStep_Meraki_Process_Networks_Data; END_IF; cStep_Meraki_Process_Networks_Data: IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN fbRequest.GetContent( pContent:=ADR(sConect) ,nContentSize:=SIZEOF(sConect) ,TRUE ); Meraki.Networks:= FC_Meraki_Networks_JsonData_Encoder(jsonDoc:=fbRequest.GetJsonDomContent(fbJson),FBJson:=fbJson2); Step:=cStep_Meraki_GetNetworkDevices; END_IF cStep_Meraki_GetNetworkDevices: sId:=”; sId:=CONCAT(‘/api/v1/networks/’,Meraki.Networks.Id); sId:=CONCAT(sId,’/devices’); sId:=FC_Meraki_Devices_SetRequestURL(id:=Meraki.Networks.Id); IF fbRequest.SendRequest( sUri:=sId ,fbClient:=fbClient ,eRequestType:=ETcIotHttpRequestType.HTTP_GET ,pContent:=0 ,nContentSize:=0 ,fbHeader:=fbheader ) THEN Step:=cStep_Meraki_Process_NetworkDevices_Data; END_IF; cStep_Meraki_Process_NetworkDevices_Data: IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN fbRequest.GetContent( pContent:=ADR(sConect) ,nContentSize:=SIZEOF(sConect) ,TRUE ); Meraki.Devices:=FC_Meraki_Devices_JsonData_Enconder( jsonDoc:=fbRequest.GetJsonDomContent(fbJson) ,FBJson:=fbJson2 ); Step:=cStep_Meraki_Get_Ports; END_IF cStep_Meraki_Get_Ports: // /api/v1/devices/{serial}/switch/ports sId:=FC_Meraki_Ports_SetRequestUrl(id:=Meraki.Devices.Serial); IF fbRequest.SendRequest( sUri:=sId ,fbClient:=fbClient ,eRequestType:=ETcIotHttpRequestType.HTTP_GET ,pContent:=0 ,nContentSize:=0 ,fbHeader:=fbheader ) THEN Step:=cStep_Meraki_Process_Ports_Data; END_IF; cStep_Meraki_Process_Ports_Data: IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN fbRequest.GetContent( pContent:=ADR(sConect) ,nContentSize:=SIZEOF(sConect) ,TRUE ); Meraki.Ports:=FC_Meraki_Ports_JsonData_Enconder( jsonDoc:=fbRequest.GetJsonDomContent(fbJson) ,FBJson:=fbJson2 ); Step:=cStep_Meraki_Get_MonitorStatus; END_IF cStep_Meraki_Get_MonitorStatus: // https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses // /api/v1/devices/{serial}/switch/ports/statuses sId:=FC_Meraki_MonitorStatus_SetRequestURL(id:=Meraki.Devices.Serial); IF fbRequest.SendRequest( sUri:=sId ,fbClient:=fbClient ,eRequestType:=ETcIotHttpRequestType.HTTP_GET ,pContent:=0 ,nContentSize:=0 ,fbHeader:=fbheader ) THEN Step:=cStep_Meraki_Process_MonitorStatus_Data; END_IF; cStep_Meraki_Process_MonitorStatus_Data: IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN fbRequest.GetContent( pContent:=ADR(sConect) ,nContentSize:=SIZEOF(sConect) ,TRUE ); jsonDoc:=fbRequest.GetJsonDomContent(fbJson); // IF jsonDoc <>0 THEN udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1); //Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc); FOR i:=1 TO UDINT_TO_INT(udi32JsonObjectLength) DO jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=i-1); Meraki.MonitorStatus[i]:=FC_Meraki_MonitorStatus_JsonData_Enconder(jsonDocBase,fbJson2); END_FOR Step:=cStep_Meraki_Get_Ports; END_IF END_IF END_CASE fbClient.Execute(); |
Implementation – Meraki Webhook
こちらはRaspberryにあるMQTT Brokerと接続するパラメータを初期化し>そのBrokerと接続します。接続に成功したらSubscribeしたTopicが新規メッセージが送信できたかをCheckします。新規メッセージがきたらそのTopicと取得したTopicが一致してるかをCheckし、TwinCAT内のBufferに格納します。すべてのTopicが来たらLogging用の配列に転送しBufferをクリアします。
Flow
pGetWebhockFromPi
VAR
PROGRAM pGetWebhockFromPi VAR MQTTClient:FB_IotMqttClient; payload:STRING(255); fbMessageQueue : FB_IotMqttMessageQueue; fbMessage: FB_IotMqttMessage; sTopic :STRING; bScbscribed:BOOL; sTopicReceived:STRING(255); WebhookBuffer :DUT_Meraki_Webhook; Record :ARRAY[0..99]OF DUT_Meraki_Webhook; bShift :BOOL; i :INT; currentIndex :INT; END_VAR |
PROGRAM
//MQTT Client Setup //Parameters MQTTClient.sHostName:=’192.168.3.10′; MQTTClient.nKeepAlive:=60; MQTTClient.ipMessageQueue:=fbMessageQueue; //Execute() must always Run MQTTClient.Execute(bConnect:=TRUE); //Subscribe Topic sTopic:=’/MyPlant1/#’; IF MQTTClient.bConnected THEN IF NOT bScbscribed THEN MQTTClient.Subscribe( sTopic:=sTopic ,eQoS:=0 ); END_IF END_IF //Check Message From Broker IF fbMessageQueue.nQueuedMessages > 0 THEN IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN //Get the Topic fbMessage.GetTopic(pTopic:=ADR(sTopicReceived), nTopicSize:=SIZEOF(sTopicReceived)); //Compare IF fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/ID/’) THEN fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.ID), nPayloadSize:=SIZEOF(WebhookBuffer.ID), bSetNullTermination:=FALSE); ELSIF fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Type/’) THEN fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.MessageType), nPayloadSize:=SIZEOF(WebhookBuffer.MessageType), bSetNullTermination:=FALSE); ELSIF fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Level/’) THEN fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.Level), nPayloadSize:=SIZEOF(WebhookBuffer.Level), bSetNullTermination:=FALSE); ELSIF fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Occurred/’) THEN fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.Occurred), nPayloadSize:=SIZEOF(WebhookBuffer.Occurred), bSetNullTermination:=FALSE); END_IF END_IF END_IF IF WebhookBuffer.ID <> ” AND WebhookBuffer.Level <> ” AND WebhookBuffer.MessageType <>” AND WebhookBuffer.Occurred <> ” THEN Record[0].ID:=WebhookBuffer.ID; Record[0].Level:=WebhookBuffer.Level; Record[0].MessageType:=WebhookBuffer.MessageType; Record[0].Occurred:=WebhookBuffer.Occurred; MEMCPY( destAddr:=ADR(Record[1]) ,srcAddr:=ADR(Record[0]) ,n:=SIZEOF(Record[0])*99 ); WebhookBuffer.ID:=”; WebhookBuffer.Level:=”; WebhookBuffer.MessageType:=”; WebhookBuffer.Occurred:=”; Record[0].ID:=”; Record[0].Level:=”; Record[0].MessageType:=”; Record[0].Occurred:=”; END_IF |
DUT_Meraki
TYPE DUT_Meraki : STRUCT Organizations :DUT_Meraki_API_organizations; Networks :DUT_Meraki_API_Networks; Devices :DUT_Meraki_API_Devices; Ports :ARRAY[1..10]OF DUT_Meraki_API_Ports; MonitorStatus :ARRAY[1..10]OF DUT_Meraki_API_MonitorPortStatus; END_STRUCT END_TYPE |
Implement – Main
PROGRAM
pMerakiAPI(); pGetWebhockFromPi(); |
Visitilization
Result
Sample Code
Summary
最初に感じたのはこれから様々な機器でもHTTP/REST APIをSupportすることになると思いますので、NetworkやWEBの知識がますます重要になります。今回使用したCisco Meraki MS120-8FPは想像以上に設定が簡単で、APIのドキュメントのかなり詳しいExampleも用意されています。Ciscoのデバイスはずっと黒いTerminalで操作すると思い込んでたので結構面白かったです。
次はBeckhoff社のTwinCAT3 TF6760もかなりできて少しびっくりしました。そのTF6760はCisco Meraki のAPIでJSONの配列 Payloadでも完璧にEncodeでき、しかも難しくありません。多分PLCじゃなくてもよいのではありませんか?と思う人が多いかもしれませんが、確かにその通りでRaspberry PI x Pythonで実装するのはもっと簡単でやすいです。
でもFA現場では、丈夫・Firmware変更なし・保証ある・止まらない・誰でもメンテナンスできるのは一番必須で、そう考えるとWindowsベースのBeckhoff TwinCAT3 Runtimeを実行したほうがよいと判断しました。TwinCAT3は実機なくてもOK、機能は7日無料試せる、ライブラリが多いという利点でTwinCAT3にしました。
そして従来の工業用ネットワークスイッチでは、
- 専用Software・Switch のLocal Web Serverから構築
- 現場まで足運ぶのは必要
- 取得できる情報が限定的
- その情報を取得できる機器も限定的
- Cloud管理は難しい
このような悩みを一部解決できるのではないか?とわたしは思っています。
最後に、みなさんもこの絵をみたことがあると思います。最初に本当にこのCisco MerakiのスイッチとTwinCATうまく繋がれるの?APIも難しそうだし、TwinCATのTF6760も初期的なことしかやっていませんし…などなどの心配がいっぱいです。
でもやってるとどんどん不安から楽しいに変わり、調べれば調べるほど面白いことやみたことないことが増えてきます。Meraki D-1グランプリの作品として他の参加者より立派ではないが、すごく勉強になりました。また使ってみたいです。