昨天咱們已經知道怎麼建置 History Data 類別,也了解如何觸發並儲存它們,接下來的問題就是客戶端怎麼把這些資料取回。
IDD Record Access Control Point 是讓 GATT Client 取得 History Data 的 characteristic,其指令格式如下:
有 6 種 Op Code:
咱們來看 Report Stored Records 的命令格式:
由上表知道它支援 6 種運算子,那現在首要問題就是:怎麼從昨天使用的 BTree database,取得特定範圍內的資料。
還記得昨天是怎麼儲存 History Data 的嗎?假設有 5 筆 History Data:
| Key (Sequence Number) | Value (Event Type + Sequence Number + Relative Offset + Event Data) | 
|---|---|
| 1 | value 1 | 
| 2 | value 2 | 
| 3 | value 3 | 
| 4 | value 4 | 
| 5 | value 5 | 
因 Sequence Number 是由小排到大(若不考慮因為 History Data 筆數超過規定上限,而繞回到開頭的情況),那麼在 MicroPython 的 BTree database 裡,與 IDD RACP 運算子相應的動作如下:
| 運算子 | 運算元 1 | 運算元 2 | BTree 操作 | 
|---|---|---|---|
| All records | n/a | n/a | tuple(btree.values()) | 
| Less than or equal to | 3 | n/a | tuple(btree.values(None, bytes((0, 0, 0, 4)))) | 
| Greater than or equal to | 2 | n/a | tuple(btree.values(bytes((0, 0, 0, 2)))) | 
| Within range of (inclusive) | 2 | 4 | tuple(btree.values(bytes((0, 0, 0, 2)), bytes((0, 0, 0, 5)))) | 
| First record | n/a | n/a | tuple(btree.values())[0] | 
| Last record | n/a | n/a | tuple(btree.values())[-1] | 
btree.values() 的原型如下:
btree.values([start_key[, end_key[, flags]]])
由上面的例子可以發現,當 end_key 有指定值時,傳回的項目並不包括 end_key,若想要讓其包含 end_key 指代的項目,可以如下使用:
tuple(
    btree.values(
        None,
        bytes((0, 0, 0, 3)),
        btree.INCL
    )
)
# 傳回一樣的結果
tuple(
    btree.values(
        None,
        bytes((0, 0, 0, 4))
    )
)
看官可以到官方網站看 btree.values 的完整說明,定會發現比目前介紹的更好的使用方式。
既然知道如何由 BTree 取得資料了,那麼就將這些功能加進 HistoryManager 類別:
class HistoryManager:
    def get_first_history(self) -> bytes | None:
        try:
            return next(iter(self._db.values()))
        except StopIteration:
            return None
    def get_last_history(self) -> bytes | None:
        try:
            return next(iter(self._db.values(None, None, btree.DESC)))
        except StopIteration:
            return None
    def get_histories(self, start: int | None, end: int | None):
        """取得 Sequence Number 在 start 和 end (含) 之間的資料。支援:
        All: start == None and end == None
        Less than or equal to: start == None and end != None
        Greater than or equal to: start != None and end == None
        Within range of (inclusive): start != None and end != None"""
        yield from self._db.values(self._get_key(start), self._get_key(end), btree.INCL)
那麼 HistoryManager 類別的功能便算是完成了,接下來只要完成 IDD Record Access Control Point 和 IDD History Data 這兩個 characteristics 的實作,咱們的 IDD Server 便大功告成!