<!-- markdownlint-disable MD013-->
# 認識 DNP3 通訊協定 - 應用層
在認識 Application Layer 前,建議讀者先瞭解 DNP3 的資料傳遞流程。
可以參考此篇:[認識 DNP3 通訊協定 - 概述](https://hackmd.io/@tm3u/dnp3-overview)
## 關鍵術語
### 點(Point)
在 DNP3 中,`點(Point)` 用於識別物理或邏輯實體。
Point 與特定的測量訊號或計算的模擬量有關:
* 輸入訊號
* 類比
* 數位
* 計數器
* 輸出訊號
* 類比
* 數位
此外,Point 除了值還會具有其他的 `屬性(Attribute)`。
`Point Type(點類型)` 是對於具有相關特徵、相似功能以及與物理硬體或邏輯空間相關的 Point 進行分類的方法。
每個 Point Type 都是獨立的陣列,透過索引來識別每個 Point。
> **以 Analog Input 為例:**
>
> * 單一個 Analog Input 就算是一個 Point。
> * 除了測量值以外,還可以具有名稱、比例因子、事件偵測的死區值和其他 Attribute。
> * Analog Input 屬於 Analog Input Point Type。
### 索引(Index)
DNP3 將 `索引(Index)` 用於識別 Point Type 陣列中的 Point,其最低值為 0。
> **以下圖為例:**
>
> 
>
> * 這是一個具有可傳輸五種 Point Type 的 DNP3 Outstation。
> * Analog Input Point Type 有五個 Analog Input Point。
> * 透過Index 編號 0 ~ 4 來識別這五個 Point。
### 群組(Group)
`群組(Group)` 用於分類訊息中的資料類型。
每個 Group 編號共享同一種 Point Type 以及資料產生或建立的方法,也可用於指定報告時間值、檔案傳輸以及設備資訊等所需的資料類型。
> **以 Analog Input 為例:**
>
> * Group 30 用於報告 Analog Input 當前值。
> * Group 31 用於報告 Analog Input 凍結值。
> * Group 32 用於報告 Analog Input 當前值變更的 Event。
> * Group 33 用於報告 Analog Input 凍結值變更的 Event。
設備並非支援所有 Group,而是依照供應商的系統設計而定。
### 變體(Variation)
DNP3 為許多資料類型提供了多種資料格式的選擇,這些資料格式稱為 `變體(Variation)`。
每個 Group 編號都有一組獨立的 Variation。
> **以 Group 30 為例:**
>
> * Variation 1 為帶有標誌 32-bit 的整數值
> * Variation 2 為帶有標誌 16-bit 的整數值
> * Variation 3 為無標誌 32-bit 的整數值
> * Variation 4 為無標誌 16-bit 的整數值
> * Variation 5 為帶有標誌 32-bit 的浮點數值
> * Variation 6 為帶有標誌 16-bit 的浮點數值
`標誌(Flag)` 用於表示當前的設備在線狀況、設備是否已重啟或值已超出測量範圍等補充內容或是條件。
* Object 通用 Flag(值為 Bool)
* ONLINE
* RESTART
* COMM_LOST
* REMOTE_FORCED
* LOCAL_FORCED
### DNP3 物件(DNP3 Object)
`DNP3 物件(DNP3 Object)` 由 Point 或其他結構的資料,進行編碼後所取得。
DNP3 Object 會根據其 Group 編號和 Variation 編號進行格式化以便資料透過訊息傳輸。
訊息中可以包含多個 Object,每個 Object 代表 Point 的當前值或是向 Point 發送的命令。
### 靜態(Static)
`靜態(Static)` 表示 Point 的當前值。
DNP3 允許透過 Request 來取得全部或部分 Static 的資料。
### 事件(Event)
`事件(Event)` 與有意義的事情發生有關。
有些 DNP3 Object 會稱其為 Event Object,這只是 Event 的表示方法,並非在 Outstation 中儲存的實際數據。
只有在 Event 資訊傳遞到 Master(在 DNP3 Object 中),
並且 Outstation 有接收到來自 Master 回傳 "確認收到 Event" 的 `確認(Confirmation)` 後,
Event 才會從 Outstation 的 `事件緩衝區(Event Buffer)` 中刪除。
> **以 Analog Input 為例:**
>
> * 狀態的變化。
> * 當前值的變化超過死區值。
> * 特定時間所取得的數據快照。
> * 等等...
DNP3 並沒有定義 Event Buffer 的數量、結構、資料組織以及內容,而是依照供應商的系統設計而定。
### 類別(Class)
DNP3 透過 `類別(Class)` 來將 Static 跟 Event 進行分類:
* Class 0 - Static Data
* Class 1, 2, 3 - Event Data
大多數資料類型的 Point 可以分配到其中一種 Class。
如果某個 Point 是被分配到 Class 0,
當 Master 向 Outstation 作 Class 0 `輪詢(Poll)` 時,必須回傳該 Point 的當前值(Event 不會被記錄或回傳)。
如果某個 Point 是被分配到 Class 1、Class 2 或 Class 3,
那麼 Outstation 則必須記錄該 Point 所發生的 Event,並在 Class 0 Poll 時回覆其當前值。
如果某個 Point 完全沒有被分配到任何 Class,
那麼 Outstation 則不會記錄該 Point 所發生的 Event,也不會在 Class 0 Poll 時回覆其當前值。
不過,當 Master 對該 Point 實施 Static Poll,Outstation 仍然必須回傳該 Point 的當前值。
不同的 Poll 對上不同的 Class,其運作如下表:
||無分配|Class 0|Class 1|Class 2|Class 3|
|-|:-:|:-:|:-:|:-:|:-:|
|**Static Poll**|✓|✓|✓|✓|✓|
|**Class 0 Poll**||✓|✓|✓|✓|
|**Class 1 Poll**|||✓|||
|**Class 2 Poll**||||✓||
|**Class 3 Poll**|||||✓|
## 訊息結構
Application Layer 的 `片段(Fragment)` 包含在 Request 或 Response 的訊息中,結構如下表:
|Application Header|First Object Header|DNP3 Object|...|Last Object Header|DNP3 Object|
|-|-|-|-|-|-|
每個 Fragment 都以包含控制資訊的 `應用標頭(Application Header)` 開始,無論它們出現在單一還是多個 Fragment 訊息中。
然而,通常單獨的 Application Header 不足以傳遞完整的訊息。
因此在 Application Header 後面會加上一或多組的 `物件標頭(Object Header)` 及 DNP3 Object 來形成完整訊息所需的附加資訊。
## Application Header
由 `應用控制(Application Control)` 跟 `功能碼(Function Code)` 組成。
不過 Response 的 Application Header 多了 `內部指示(Internal Indication, IIN)`。
* Application **Request** Header 的結構如下表:
|Application Control|Function Code|
|:-:|:-:|
|1 octet|1 octet|
* Application **Response** Header 的結構如下表:
|Application Control|Function Code|IIN|
|:-:|:-:|:-:|
|1 octet|1 octet|2 octets|
### Application Control
Application Control 的訊息大小為 1 個 Octet。
提供建構跟重組多個 Fragment 訊息所需的參數,並且指示接收方的 Application Layer 是否必須回覆 Confirmation 及協助檢測重複的訊息。
Application Control 由 FIR、FIN、CON、UNS 跟 SEQ 五種參數組成,其結構如下:

* **FIR - 1 bit**
* FIR = 0 表示這不是訊息中的第一個 Fragment
* FIR = 1 表示這是訊息中的第一個 Fragment
* **FIN - 1 bit**
* FIN = 0 表示後面還有其他的 Fragment
* FIN = 1 表示這是訊息中的最後一個 Fragment
* **CON - 1 bit**
* CON = 0 表示接收端不回覆 Confirmation
* CON = 1 表示接收端必須回覆 Confirmation
* **UNS - 1 bit**
* UNS = 0 表示這是 Response 的 Fragment
* UNS = 1 表示這是 Unsolicited Response 的 Fragment
* **SEQ - 4 bits**
用於驗證 Fragment 是否以正確的順序接收,並檢查是否重複。
值的範圍為 0 ~ 15,並以 1 為單位遞增。值為 15 後,下一個值為 0。
Response 跟 Unsolicited Response 的 SEQ 值,兩者獨立互不相關。
### Function Code
Function Code 的訊息大小為 1 個 Octet。
用於識別 Fragment 訊息的目的。
* Request 訊息所使用的 Function Code 範圍為 0x01 ~ 0x80
* Response 訊息所使用的 Function Code 範圍為 0x81 ~ 0xFF
* Confirmation 訊息所使用的 Function Code 為 0x00。
Function Code 的種類較多,因此放到這篇說明:[認識 DNP3 通訊協定 - Function Code](https://hackmd.io/@tm3u/dnp3-function_code)
### IIN
IIN 的訊息大小為 2 個 Octet。
透過特定位元來表示 Outstation 內的某些狀態或是錯誤,其結構如下:

DNP3 透過代碼的方式來指定特定位元:IIN x.y。
* x 表示第一個或是第二個 Octet
* y 則是表示 bit 的索引編號
關於 Internal Indication 的簡易說明如下表:
| 代碼 | 名稱 | 簡要說明 |
|:------- | --------------------- |:-------------------------------------------- |
| IIN 1.0 | ALL_STATIONS | 收到廣播訊息 |
| IIN 1.1 | CLASS_1_EVENTS | Outstation 有未報告的 Class 1 Event |
| IIN 1.2 | CLASS_2_EVENTS | Outstation 有未報告的 Class 2 Event |
| IIN 1.3 | CLASS_3_EVENTS | Outstation 有未報告的 Class 3 Event |
| IIN 1.4 | NEED_TIME | 需要時間同步 |
| IIN 1.5 | LOCAL_CONTROL | Outstation 的一或多個 Point 處於本地控制模式 |
| IIN 1.6 | DEVICE_TROUBLE | Outstation 存在 "異常" 或 "設備特定的情況" |
| IIN 1.7 | DEVICE_RESTART | Outstation 重啟 |
| IIN 2.0 | NO_FUNC_CODE_SUPPORT | Outstation 不支援該 Function Code。 |
| IIN 2.1 | OBJECT_UNKNOWN | Outstation 不支援該 Object 的 Request 操作 |
| IIN 2.2 | PARAMETER_ERROR | 偵測到參數錯誤 |
| IIN 2.3 | EVENT_BUFFER_OVERFLOW | Outstation 的 Event Buffer 出現溢位的情況 |
| IIN 2.4 | ALREADY_EXECUTING | 【可選】Request 的操作已執行 |
| IIN 2.5 | CONFIG_CORRUPT | 【可選】Outstation 偵測到損壞的設置 |
| IIN 2.6 | RESERVED_2 | 【值常駐為 0】保留 |
| IIN 2.7 | RESERVED_1 | 【值常駐為 0】保留 |
> **以發生 Outstation 不支援該 Object 的 Request 操作的狀態為例:**
>
> * 該狀態的 IIN 代碼為 IIN2.1
> * 因此在 IIN 的第二個 Octet 中,第二個 bit 的值會為 1
## Object Header
由 `物件類型符(Object Type Field)`、`限定符(Qualifier Field)` 以及 `範圍符(Range Field)` 組成。
Object Header 的結構如下表:
|Object Type Field|Qualifier Field|Range Field|
|:-:|:-:|:-:|
|2 octet|1 octet|N octets(取決於 Qualifier Field)|
> **以一個想要從 Outstation 讀取十個 Analog Input 的 Master 為例:**
>
> * Master 會使用 Function Code 0x01(READ)來制定 Request 訊息。
> * 而 Object Header 會指定:
> * 想要的 Analog Input Point Type 資料。
> * Outstation 傳送資料時,應該使用的整數或浮點格式。
> * 那十個 Analog Input Point 的 Index。
> * Request 中不會包含 DNP3 Object,只會有一或多個 Object Header。
> 因為 Master 不傳送值,只傳送讓 Outstation 知道 Master 需要的值或是資料格式。
> * 接著,Outstation 會使用 RESPONSE(0x81)的 Function Code 來制定 Response 訊息。
> 其中包含了與 Request 相同或相似的 Object Header,並在後面接著 DNP3 Object。
> 後面接著的所有 DNP3 Object,每個都包含來自單個 Index 的單一 Analog 值。
### Object Type Field
Object Type Field 的訊息大小為 2 個 Octet,**Group** 跟 **Variation** 各占一個 Octet。
關於 Group 跟 Variation 的說明請看本篇章節:[關鍵術語 - Group](#群組(Group)) 跟 [關鍵術語 - Variation](#變體(Variation))
### Qualifier Field 跟 Range Field
Qualifier Field 的訊息大小為 1 個 Octet,而 Range Field 則取決於 Qualifier Field 的內容。
Qualifier Field 由 Res、Object Prefix Code 跟 Range Specifier Code 三種參數組成,其結構如下:

* **Res - 1 bit**
保留,且值常駐為 0。
* **Object Prefix Code - 3 bits**
`前綴(Prefix)` 可以用來表示 Object 的 Index 編號或 Object 的大小。
它會出現在 Object Header 後的每個 DNP3 Object 前面,位置如下:

而 Object Prefix Code 則是用於指定 Prefix 的內容,其代碼說明如下表:
|值|說明|Prefix 的大小|
|-|-|-|
|0x00|不使用 Prefix||
|0x01|以 Index 作為 Prefix|1 octet|
|0x02|以 Index 作為 Prefix|2 octets|
|0x03|以 Index 作為 Prefix|4 octets|
|0x04|以 Object Size 作為 Prefix|1 octet|
|0x05|以 Object Size 作為 Prefix|2 octets|
|0x06|以 Object Size 作為 Prefix|4 octets|
|0x07|保留|
> **以 Master 想要讀取一連串 Index 為 "非連續排列" 的 Analog Input Point 為例:**
>
> * Master 會將 Object Prefix Code 設置為 0x01、0x02 或 0x03。
> * 並發送以 Index 為 Prefix 的 0 octet 的空 Object。
> * 該 Request 的 Fragment 將會如下:
>
> 
若讀取的是 Index 為 "連續排列" 的情況下,
接下來要介紹的 Range Specifier Code 就可以指定起始跟截止的 Index 範圍,
並且不需要加入 Prefix(Object Prefix Code 設置為 0x00)。
* **Range Specifier Code - 4 bits**
Range Specifier Code 指定是否使用 Range Field,並定義 Range Field 的內容及大小。
其代碼說明如下表:
|值|說明|Range Field 的大小|
|-|-|-|
|0x00|1 octet 的起始 Index 跟截止 Index|2 octets|
|0x01|2 octets 的起始 Index 跟截止 Index|4 octets|
|0x02|4 octets 的起始 Index 跟截止 Index|8 octets|
|0x03|1 octet 的起始虛擬地址跟截止虛擬地址|2 octets|
|0x04|2 octets 的起始虛擬地址跟截止虛擬地址|4 octets|
|0x05|4 octets 的起始虛擬地址跟截止虛擬地址|8 octets|
|0x06|不使用 Range Field|0 octet|
|0x07|1 octet 的 Objects 數量|1 octet|
|0x08|2 octets 的 Objects 數量|2 octets|
|0x09|4 octets 的 Objects 數量|4 octets|
|0x0A|保留||
|0x0B|1 octet 的 Objects 數量(可變長度的格式)|1 octet|
|0x0C|保留||
|0x0D|保留||
|0x0E|保留||
|0x0F|保留||
Range Field 的訊息大小以及內容取決於 Qualifier Field 的 Range Specifier Code。
### Unsolicited Response
`自發性回應(Unsolicited Response)` 是當重要的事件發生時,在 Master 沒有發起 Request 情況下由 Outstation 發送訊息。
此功能是否啟用,是依照供應商的系統設計而定。