「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。
ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ
私はソフテックに中途で入社してから7年目になる中堅社員になります。ソフテックに入社してから様々な通信プロトコルの開発に携わりました。そのうちの一つにBACnetがあります。ソフテックだより第239号 「BACnetを利用した組み込みシステム開発」では、BACnetの概要およびBACnetシステムの実装例について紹介しました。
今回のソフテックだよりではBACnet通信プロトコルを実装する上で把握しておく必要のあるBACnet通信プロトコルの電文構成および通信電文の表記方法であるAbstract Syntax Notation One(ASN.1)について紹介します。
通信プロトコルを開発する場合、通信仕様書に記載された通信電文フォーマットに則り開発を行います。私が今まで開発した通信プロトコルの場合、ソフテックだより第133号 「PLCでModbus通信 〜横河電機社製での実装例〜」やソフテックだより第159号 「LinuxOSでのDNP3.0通信開発」に記載されている通り、メッセージ形式が図で示されており、データフィールド(通信プロトコルを構成する要素)はバイト単位(オクテット単位)で記載されていました。
ただ、BACnetの場合は図で示されているのではなく、ASN.1という表記方法で表現されていました。ASN.1は図で示すのではなく、プログラムの構造体のような表記方法での記載となります。
ASN.1はマシン固有の表現方法に依存せず、曖昧さのない記述を可能とする形式規則を提供します。ASN.1では抽象構文(注1)で通信電文を定義し、符号化規則に則り、データのエンコード(符号化)、デコード(複合化)を行います。
今回のソフテックだよりでは、ASN.1での表記内容を元に、Binary-Inputのインスタンス番号1025の現在値(注2)を取得する電文(PDU(注3))の作成例を紹介します。ただし、説明の簡略化のため、実際のBACnet仕様書に記載されている内容と異なります。
ASN.1での通信電文表記方法を紹介します。
ASN.1の基本的な表記方法は以下になります。
名前 :== 型名 {
要素1
要素2
・・・
}
また、今回の例の電文は以下の構成になります。
図1. binary-input, 1025, present-value 取得電文構成
以下にBACnet電文の符号化を行う上での基本的な流れおよびASN.1での定義を示します。
括弧の番号はASN.1定義の括弧の番号に対応します。
図2. BACnet電文符号化の流れ
BACnet電文をどのPDUの種類で作成するかを選択する定義になります。
PDU-Type :== CHOICE {
confirmed-request-PDU [0] Confirmed-Request-PDU
- (2) PDU構成要素の定義に対応します。
unconfirmed-request-PDU [1] Unconfirmed-Request-PDU
}
PDU構成要素の定義になります。
Confirmed-Request-PDU :== SEQUECNE {
pdu-type [0] 符号無し整数(0,,15)
- このPDU型の場合0にする
service-choice [1] ConfirmedService
- (3) サービスの選択に対応します。
service-request [2] Confirmed-Service-Request
- (4) サービス定義の選択に対応します。
}
※pdu-type, service-choiceは1オクテット
※service-requestは符号化された値
サービスに対応する値の定義になります。
ConfirmedService :== CHOICE {
confirmedCOVNotification (1),
confirmedEventNotification (2),
readProperty (12),
}
Confirmed-Service-Request :== CHOICE {
confirmedCOVNotification [1] ConfirmedCOVNotification-Request
confirmedEventNotification [2] ConfirmedEventNotification-Request
・・・・・・・・・・
readProperty [12] ReadProperty-Request
- (5) サービス定義に対応します。
}
roperty情報を取得するためのサービスであるReadProperty-Requestの構成要素の定義になります。
ReadProperty-Request :== SEQUENCE {
objectId [0] ObjectId
- (6) アプリケーションタイプの定義に対応します。
propertyId [1] PropertyId
- (7) アプリケーションタイプの定義に対応します。
}
BACnetで使用する型の定義になります。
-- NULL [APPLICATION 0]と等価
-- INTEGER(符号付き整数) [APPLICATION 1]と等価
-- ENUMURATED [APPLICATION 9]と等価
-- ObjcetId [APPLICATION 12] オクテット列
・APPLICATION 12 (ObjectId)
図3. APPLICATION 12 (ObjectId)
※ObjectTypeは(7) 基本型の定義のObjectTypeに対応します。
BACnetで使用する型の定義になります。
PropertyId :== ENUMERATED {
present-value (85),
status-flags (111),
}
ObjectType :== ENUMERATED{
binary-input (3),
}
各構成要素はタグと呼ぶ単位で符号化します。タグは要素の先頭、および条件によって、要素の後尾にも使用します。タグは以下のように定義されています。
図4. タグ構成
Tag Number = タグが属するクラスにおけるタグ番号
Class = タグのクラス(アプリケーションまたはコンテクスト規定)
以下に、ReadProperty-Requestの符号化例を紹介します。
図5. ReadProperty-Request符号化の流れ
電文が符号化されて通知されるため、解析のために復号化を行います。
復号化は符号化の逆の手順を行います。
図6. ReadProperty-Request復号化の流れ
BACnet電文の表記に使用しているASN.1ですが、以下のメリットデメリットがあると感じました。
メリット
デメリット
BACnetはサービスやパラメータが多く、また、サイズもかなり自由なプロトコルになります。そのため、ASN.1の表記方法がマッチしていると感じます。
ただ、Modbusのような単純なプロトコルをASN.1で表記した場合、無駄に難易度が高くなり、採用されにくくなるように感じます。
そのため、ただ単に表現しやすいから、拡張性をしやすいからという理由で選択するのではなく、本当にそれに合った方法を採用するのが良い選択になるのだと感じました。
最後までお読みいただきありがとうございました。
(S.O.)
関連ページへのリンク
関連するソフテックだより