各位看官~今天咱們來進行第一次的重構吧~
為了更好管理不同功能的開發,我們將 IDS server 分為幾個模組:
目錄名稱 | 功能 |
---|---|
ble | 藍牙相關 |
common | 所有模組都可能用到的共通功能,如 log 的輸出、SFLOAT 的轉換等 |
core | 核心功能,如胰島素注射、歷史紀錄的儲存等 |
而根目錄只包含 main.py 和設定相關的程式和檔案。
雖然 ESP32 支援多執行緒,但為了節省記憶體,以及簡化設計,本系統只會使用主執行緒和中斷處理程序。
接著,咱們來製作所有模組都需要的 log 輸出功能吧~
此檔案只有一個公開方法 write():
def write(msg: str):
"""不建議在 ISR 內呼叫。"""
now = machine.RTC().datetime()
print(
f"{now[0]}-{now[1]:02d}-{now[2]:02d} "
f"{now[4]:02d}:{now[5]:02d}:{now[6]:02d}.{now[7]:06d} "
f"{msg}"
)
根據 MicroPython 的限制,不應該在 ISR 內由 heap 配置記憶體,而字串拼接,尤其格式化,通常需要配置記憶體來處理,因此不建議在 ISR 內使用。
一開始咱們用 machine.RTC() 來得到 ESP32 的 RTC,然後以其 datetime() 來取得目前的日期時間,其回傳格式是 tuple:
索引 | 意義 |
---|---|
0 | 年 |
1 | 月 |
2 | 日 |
3 | 星期,0 ~ 6,星期一為 0 |
4 | 時 |
5 | 分 |
6 | 秒 |
7 | 毫秒 |
這裡有個要注意的點,雖然根據文件描述,machine.RTC() 似乎會創立一個新的 RTC 實例,但在咱們的環境下,無論呼叫幾次,都會得到同一個實例(以 id() 來驗證)。
在 write() 裡,因為需要格式化,所以使用 f-string 來達成,當然也可以用 str.format() 或 %-formatting。這裡咱們不考慮複雜的使用情境,只探討最簡單的字串拼接:
def test_f_str(n, s):
for i in range(times):
t = f"arg ({n}, {s})"
def test_concate(n, s):
for i in range(times):
t = "arg (" + str(n) + ", " + s + ")"
def test_join(n, s):
for i in range(times):
t = "".join(("arg (", str(n), ", ", s, ")"))
def test_percent(n, s):
for i in range(times):
t = "".join("arg (%d, %s)" % (n, s))
執行 3 輪,每輪各執行 10000 次:
第 1 輪:
第 2 輪:
第 3 輪:
撇除 %-formatting,可以發現使用 join() 是執行最快,但記憶體使用量卻可能最多;而 f-string 耗費的記憶體最少。
不過這只是一種情境,若將程式改為(重複使用 s):
def test_f_str(n, s):
for i in range(times):
s = f"arg ({n}, {s})"
def test_concate(n, s):
for i in range(times):
s = "arg (" + str(n) + ", " + s + ")"
def test_join(n, s):
for i in range(times):
s = "".join(("arg (", str(n), ", ", s, ")"))
def test_percent(n, s):
for i in range(times):
s = "".join("arg (%d, %s)" % (n, s))
執行 3 輪,每輪各執行 100 次(太多次會導致 MemoryError):
第 1 輪:
第 2 輪:
第 3 輪:
join() 還是最快,但記憶體使用量也是最少。
不過這種測試只能做為一種參考,畢竟有些參數不同,結果可能就有變化。而且在輸出 log 時,需要拼接的字串不至於太長,耗費的時間和記憶體應該有限。
在執行時,注意要將 common 這個資料夾先上傳到板子上,否則執行 main.py 時會出現找不到 common 模組的錯誤:
main.py 可以不用上傳,只要在 Thonny 開啟並執行就好。若 main.py 也上傳到板子,那以後板子通電後,就會直接執行 main.py,這在開發階段會有一些不方便。