昨天咱們已將警示通知的基本型設計好了,接下來就是利用它製作需要輔助欄位的警示通知。
以下是 IDS 定義的 Temperature 通知的輔助欄位:
AuxInfo2 Temperature Flags
AuxInfo2 Context
咱們先直接根據以上規格來定義它的建構函數:
class TemperatureAnnunciation(core.annunc.base.BaseAnnunciation):
def __init__(
self,
value: float | None,
temperature_flags: int | None,
context: int | None,
lower_bound: float | None = None,
upper_bound: float | None = None,
*,
flags: int | None = core.annunc.consts.ANNUNCIATION_PRESENT
| core.annunc.consts.AUXINFO1_PRESENT
| core.annunc.consts.AUXINFO2_PRESENT,
id: int | None = 0,
status: int | None = core.annunc.consts.STATUS_PENDING,
fired_timestamp: int = -1,
remaining_snoozed_time: int = 0,
):
super().__init__(
core.annunc.consts.TEMPERATURE,
flags=flags,
id=id,
status=status,
fired_timestamp=fired_timestamp,
remaining_snoozed_time=remaining_snoozed_time,
)
self.value = value
self.temperature_flags = temperature_flags
self.context = context
self.lower_bound = lower_bound
self.upper_bound = upper_bound
value
、temperature_flags
和 context
沒有預設值。value
、temperature_flags
和 context
還是可以被設定為 None
,表示欄位不存在。lower_bound
和 upper_bound
的預設值為 None
。flags
預設包含 AUXINFO1_PRESENT
和 AUXINFO2_PRESENT
因 Temperature 通知最多需要 4 個輔助欄位,所以要覆寫以下方法:
class TemperatureAnnunciation(core.annunc.base.BaseAnnunciation):
def get_aux_info_1(self) -> int | None:
if self.value is not None:
return common.sfloat.float_to_sfloat(self.value)
return None
def get_aux_info_2(self) -> int | None:
n = 0
is_set = False
if self.temperature_flags is not None:
n = self.temperature_flags
is_set = True
if self.context is not None:
n |= self.context << 8
is_set = True
return n if is_set else None
def get_aux_info_3(self) -> int | None:
if self.lower_bound is not None:
return common.sfloat.float_to_sfloat(self.lower_bound)
return None
def get_aux_info_4(self) -> int | None:
if self.upper_bound is not None:
return common.sfloat.float_to_sfloat(self.upper_bound)
return None
這幾個方法的思路都很直觀,若欄位的值不是 None
,便將其轉成規格定義的格式,並回傳;否則便回傳 None
,表示欄位不存在。
最後因為 Temperature 通知有自己的輔助欄位,所以必須覆寫 to_dict()
和 from_dict()
:
class TemperatureAnnunciation(core.annunc.base.BaseAnnunciation):
def to_dict(self):
result = super().to_dict()
result["value"] = self.value
result["temperature_flags"] = self.temperature_flags
result["context"] = self.context
result["lower_bound"] = self.lower_bound
result["upper_bound"] = self.upper_bound
return result
@classmethod
def from_dict(cls, d):
return cls(
d["value"],
d["temperature_flags"],
d["context"],
d["lower_bound"],
d["upper_bound"],
flags=d["flags"],
id=d["id"],
status=d["status"],
fired_timestamp=d["fired_timestamp"],
remaining_snoozed_time=d["remaining_snoozed_time"],
)
IDS server 在運作期間會產生許多警示通知,而雖然可以用 IDD Annunciation Status 來讀取,但它只會傳回優先權最高的項目。不過 IDS 規格裡並沒有明文定義何謂優先權最高,而是交由廠商自行決定,所以咱們訂定幾個簡單規則:
嚴重性
分為不同層級Status
為 Pending
和 Snoozed
的警示通知會被納入考量,也就是 Confirmed
的項目會視為不存在Pending
的優先權高於 Snoozed
fired_timestamp
值愈大,優先權愈高嚴重性
> Status
> fired_timestamp
根據上述規則,咱們先定義 4 種嚴重性:
# 值越小,優先權越高
_PRIORITY_ERROR = micropython.const(0)
_PRIORITY_MAINTENANCE = micropython.const(1)
_PRIORITY_WARNING = micropython.const(2)
_PRIORITY_REMINDER = micropython.const(3)
先前咱們也有用過 micropython.const()
,只是都沒說明,所以這裡稍微解釋一下。根據 MicroPython 文件說明,當用到以 micropython.const()
定義的變數時,它不是以變數名來引用,而是直接使用其常數值,類似於 C/C++ 的 const
或 #define
的作用,只是效果是否有必較好就不清楚了。
好!回來!接下來,本喵非常武斷地指派嚴重性給各警告通知:
# annunciation 的緊急程度
_ANNUNC_PRIORITY_MAP = {
core.annunc.consts.SYSTEM_ISSUE: _PRIORITY_ERROR,
core.annunc.consts.MECHANICAL_ISSUE: _PRIORITY_ERROR,
core.annunc.consts.OCCLUSION_DETECTED: _PRIORITY_MAINTENANCE,
core.annunc.consts.RESERVOIR_ISSUE: _PRIORITY_MAINTENANCE,
core.annunc.consts.RESERVOIR_EMPTY: _PRIORITY_MAINTENANCE,
core.annunc.consts.RESERVOIR_LOW: _PRIORITY_WARNING,
core.annunc.consts.PRIMING_ISSUE: _PRIORITY_MAINTENANCE,
core.annunc.consts.INFUSION_SET_INCOMPLETE: _PRIORITY_MAINTENANCE,
core.annunc.consts.INFUSION_SET_DETACHED: _PRIORITY_MAINTENANCE,
core.annunc.consts.POWER_SOURCE_INSUFFICIENT: _PRIORITY_MAINTENANCE,
core.annunc.consts.BATTERY_EMPTY: _PRIORITY_MAINTENANCE,
core.annunc.consts.BATTERY_LOW: _PRIORITY_WARNING,
core.annunc.consts.BATTERY_MEDIUM: _PRIORITY_REMINDER,
core.annunc.consts.BATTERY_FULL: _PRIORITY_REMINDER,
core.annunc.consts.TEMPERATURE_OUT_OF_RANGE: _PRIORITY_MAINTENANCE,
core.annunc.consts.AIR_PRESSURE_OUT_OF_RANGE: _PRIORITY_MAINTENANCE,
core.annunc.consts.BOLUS_CANCELED: _PRIORITY_WARNING,
core.annunc.consts.TBR_OVER: _PRIORITY_REMINDER,
core.annunc.consts.TBR_CANCELED: _PRIORITY_WARNING,
core.annunc.consts.MAX_DELIVERY: _PRIORITY_WARNING,
core.annunc.consts.DATE_TIME_ISSUE: _PRIORITY_WARNING,
core.annunc.consts.TEMPERATURE: _PRIORITY_REMINDER,
}
因為咱們規定「Pending 的優先權高於 Snoozed」,所以來看一下 Annunciation Status 有哪些定義:
現在可以來設計如何由已觸發的警示通知中,取出最高優先權的項目了。為了方便管理警示通知,咱們設計一個類別來處理:
class AnnunciationManager:
def get_top_priority_annunc(self) -> core.annunc.base.BaseAnnunciation | None:
# 先比較 priority,再比較 status,若都一樣,則取最新的項目
# 加負號是為讓愈小的 priority 和 status 愈大
best_key = (-0xFF, -0xFF, -1) # (priority, status, fired_timestamp)
best_item = None
for a in self._config.annunc_fired:
if (
a.status != core.annunc.consts.STATUS_PENDING
and a.status != core.annunc.consts.STATUS_SNOOZED
):
continue
key = (
-_ANNUNC_PRIORITY_MAP.get(a.type, _PRIORITY_REMINDER),
-a.status,
a.fired_timestamp,
)
if key > best_key:
best_key = key
best_item = a
return best_item
嚴重性
> Status
> fired_timestamp
,所以將它們組成 tuple
來比較。但這方式會為 tuple
配置記憶體,且因這方法會在 IDD Annunciation Status 的 Read IRQ 中被呼叫,所以若想避免記憶體配置,就自行一個一個比較,如此也不須將特定值取負了。