Fiwareを使って都市OSを動かしてみよう

Munu


データ仕様の現状と課題
スマートシティの標準規格(案)
データモデルのユースケース
ツール


Column
Link
用語集

Coppell

Technologies

 7. ブローカーとしてのfiware/orion 7.6節




7.6.Registrationの動作確認
2022年5月7日
今度はRegistrationを動かしてみましょう。Registationの指定の例として、以下の様に温度を読み出した時に、hostpcの3001番のボード番号でListenしているContext Providerのregistrations/temperatureに値を取りに行く指定とします。今度は四階の部屋を指定していますが、三階と四階はAttributeにtemperatureは指定していませんでした。つまり、ResistrationはEntity格納時に存在しないAttributeに対して設定することになります。余談ですが、うっかりEntityの格納時にAttributeを作ってしまうと、Registrationの指定は無視され、格納したAttributeの値が返却されます。

{
 "description": "Temperature Context Provider",
 "dataProvided": {
   "entities": [
     {
       "id": "urn:ngsi-ld:Room:001-004-001",
       "type": "Room"
     }
   ],
   "attrs": [
     "temperature"
   ]
 },
 "provider": {
   "http": {
     "url": "http://hostpc:3001/registrations/temperature"
   }
 }
}

このjsonをregistrationsというエンドポイントに対してPOSTします。

curl -iX POST -H 'Content-Type: application/json' -d @d:\postRegistration.json  'http://localhost:1026/v2/registrations'
登録が出来たら、次はContext Providerを作りましょう。Consumerと同じくPOSTで通知されるので、似たような内容になります。
以下が今回作ったProvicerです。温度はランダムな値を返却します。通常、Providerは色々な問い合わせに対応する必要があるためもっと複雑になりますが、このプログラムはこのチュートリアルに対応する機能しか実装していません。悪しからず。

import http.server as s
from urllib.parse import urlparse    #url anslysis module
import json
import random

class MyHandler(s.BaseHTTPRequestHandler):
   def do_POST(self):               #server_foreverからPOST時に呼ばれる
       # urlパラメータを取得
       parsed = urlparse(self.path) #url文字列を分解してnamed tupleに変換
       # urlパラメータを解析
       urlpath = parsed.path        #パスの文字列を取り出す
       urldir = urlpath.split('/')
       if (len(urldir) < 5 or urldir[1] != 'registrations'):
           # Registrationによる呼出しではない
           print ('Context provider error (1): invalid resource: ', urlpath)
           self.send_response(400)  #レスポンスのステータスを"Bad request"に設定
           self.send_header('Content-length', 0)
                                    #ヘッダのcontect-lengthを設定
           self.end_headers()       #ヘッダの後に空行を出力
           self.wfile.write(''.encode())
           return()
       if urldir[2] == 'temperature':#temperatureのregistration
           if (urldir[3] != 'op' or urldir[4] != 'query'):
                                    # legacyForwarding":falseではない
               print ('Context Provider error (2): invalid resource: ', urlpath)
               self.send_response(400)
               self.send_header('Content-length', 0)
               self.end_headers()
               self.wfile.write(''.encode())
               return()
           content_len = self.headers.get("content-length")
           if content_len =='':     #Headerのcontent-lengthの値を採取
               print ('Context Provider error (3): missing message body')
               self.send_response(400)
               self.send_header('Content-length', 0)
               self.end_headers()
               self.wfile.write(''.encode())
               return()
           content_len = int(content_len)                                    
           req_body = self.rfile.read(content_len).decode("utf-8")
           json_dict = json.loads(req_body)
           temperature = str(random.randrange(50))
           print('Providing temperature information of the room ('+json_dict['entities'][0]['id']+'): '+temperature)
           body = '[{"id":"'
           body+= json_dict['entities'][0]['id']
           body+= '","type":"'
           body+= json_dict['entities'][0]['type']
           body+= '","temperature":{"type":"Integer","value":'
           body+= temperature
           body+= '}}]'
       else:
           # 知らないRegistrationによる呼出し
           print ('Provider error (4): invalid resource: ', urlpath)
           self.send_response(400)
           self.send_header('Content-length', 0)
           self.end_headers()
           self.wfile.write(''.encode())
           return()
       # 返信を組み立て
       self.send_response(200)  #レスポンスのステータスを"正常"に設定
       self.send_header('Content-type', 'application/json')
                                    #ヘッダのcontect-typeを設定
       self.send_header('Content-length', len(body))
                                    #ヘッダのcontect-lengthを設定
       self.end_headers()           #ヘッダの後に空行を出力
       self.wfile.write(body.encode())
                                    #クライアントに応答
   def do_GET(self):                #server_foreverからGET時に呼ばれる
       print('Provider error (5): invalid method: GET')
       self.send_response(400)
       self.send_header('Content-length', '0')
       self.end_headers()
       self.wfile.write(''.encode())
   def do_PUT(self):                #server_foreverからPUT時に呼ばれる
       print('Provider error (5): invalid method: PUT')
       self.send_response(400)
       self.send_header('Content-length', '0')
       self.end_headers()
       self.wfile.write(''.encode())
       print('PUT request detected')

port = 3001
httpd = s.HTTPServer(("", port), MyHandler)
print('Context Providerを起動しました。ポート:%s' % port)
httpd.serve_forever()
}

筆者はこれをprovider.pyというファイルにメモ帳で格納しました。そのファイルをダブルクリックすると、起動されます。起動直後は以下のメッセージが表示されます。
Context Providerを起動しました。ポート:3001
起動出来たら、早速温度を読み出してみましょう。

curl -X GET 'http://localhost:1026/v2/entities/urn:ngsi-ld:Room:001-004-001?options=keyValues'
PS C:\Users\owner> curl -X GET 'http://localhost:1026/v2/entities/urn:ngsi-ld:Room:001-004-001?options=keyValues'
{"id":"urn:ngsi-ld:Room:001-004-001","type":"Room","name":{"type":"Text","value":"レクリエーションルーム"},"refFloor":{"type":"Text","value":"urn:ngsi-ld:Floor:001-004"},"temperature":36}
PS C:\Users\owner>
その際に、Providerのプログラムは次の様なメッセージが表示されます。
Providing temperature information of the room (urn:ngsi-ld:Room:001-004-001): 36
172.22.32.1 - - [07/May/2022 15:39:03] "POST /registrations/temperature/op/query HTTP/1.1" 200 -
この様にFiware/Orionから温度を読み出そうとすると、Fiware/OrionはContext Providerに温度情報を取りに行き、Context Providerから返却された温度の値を要求者に返却していることが分かります。