除錯與常見錯誤
Plugin 出問題,八成是下面這些。第一個動作永遠一樣:去看 Log。 載入失敗、被拒絕、崩潰、指令名稱撞名,主程式都會記一行; 沒有面板也沒關係,在批次模式下它們會直接印在標準輸出。
先看這三件事
plugin list:你的 Plugin 有沒有被掃到?沒有,往下看「載入類」。- Log(I2C Bus Log 面板,或批次模式的標準輸出):來源是
system的那些行說明它為什麼被跳過或拒絕。 ver:印出主程式版本與 ABI 版本(例如nexus_tui 1.0.0.8 (abi 4.1));在 x86 建置上還會說明有沒有例外隔離(SEH isolation)。
載入類:plugin list 看不到我的 Plugin
| 你在 Log 看到 | 原因 | 怎麼辦 |
|---|---|---|
plugin dll skipped: xxx.dll could not be loaded (win32 error 193) | 架構不符:x86 的 DLL 配 x64 的主程式,或反過來。訊息本身不會說「架構」 | 兩種架構各建一顆,用和主程式相同的那顆,見架構要對:x86/x64 |
plugin dll skipped: xxx.dll does not export NexusPluginGetInterface | 進入點沒有匯出,或名字寫錯 | 用範本的 NX_PLUGIN_EXPORT,函式名稱一字不差 |
plugin dll skipped: xxx.dll returned NULL from NexusPluginGetInterface | 你的進入點自己回了 NULL,通常是 ABI 主版本不符被你自己拒絕 | 看你的進入點為什麼回 NULL;用 PDK 的 sdk\ 重新編 |
plugin dll rejected: xxx.dll abi major N (expected 3); ... | ABI 主版本不符,訊息會印出雙方的版本與結構大小 | 用與主程式相同 ABI 的 PDK 重新編譯,見版本號與 ABI 相容 |
plugin dll skipped: xxx.dll crashed inside NexusPluginGetInterface | 進入點裡崩潰了 | 進入點只該填靜態結構,不要做別的事 |
plugin rejected: ... slaves == NULL 或 ... slave_decl_size N but this host was built with sizeof(NxSlaveDecl) == M 或 declares N slaves (allowed 1..16) | slaves[] 沒填好:沒指定、元素大小沒填 sizeof(NxSlaveDecl)、或個數不在 1~16 | 三個欄位 slaves、slave_count、slave_decl_size 都要填,見第一個 Plugin 1 |
plugin dll skipped: xxx.dll declares name 'yyy', already registered | 名稱重複:兩顆 Plugin 用了同一個名字,先註冊的贏 | 改 IcPlugin::name,常見原因是複製範本忘了改名字 |
| 什麼訊息都沒有 | DLL 根本不在被掃描的資料夾(要在主程式旁邊的 plugins\,不是 plugins\子資料夾\) | 檢查路徑;視窗版可以用選單重新掃描,終端機版重新啟動 |
註冊被拒絕,cleanup 或名稱是空的 | name 與 cleanup 是唯二強制的欄位,其餘 callback 才可以是 NULL | 補上 cleanup(可以是空函式) |
掛載類:載進來了,但 plugin switch 出問題
| 症狀 | 原因 | 怎麼辦 |
|---|---|---|
plugin switch: no focused panel; pass @p<N> | Plugin 有面板,但沒有指定要放哪一格,也沒有有焦點的面板 | 加 @p0(或其他象限)。Widget/Canvas 型的 Plugin 一律帶 @p<N> |
你正在看的那一格顯示 no plugin mounted,而且沒有錯誤訊息 | 掛到了別的面板:沒指定 @p<N> 時 Plugin 會掛到目前有焦點的面板 | 明確指定 @p<N>,見選單:Plugin |
| 面板一直是「沒有 Plugin」 | 你的 Plugin 宣告了 panel_mode = NX_PANEL_MODE_NONE(沒有面板),掛載一律 headless,@p<N> 被忽略 | 想要面板就改成 widget 或 canvas 模式,見架構總覽 |
掛載後 probe 失敗,面板標題出現 [FAILED]、widget 變灰 | IC 沒接、位址錯、或 probe() 回了非 ERR_OK;不加 force 時整顆 Plugin 進入失敗狀態保留(不再自動卸載,cleanup 延到重跑或卸載時才呼叫) | 接好硬體後 plugin restart(Core 偵測到 dongle 離線後又重新連線時會自動重跑一次,輪詢關閉時除外;Core 只在傳輸失敗後才察覺離線,沒有任何東西在讀寫該 dongle 時拔插不一定被察覺,仍需手動 plugin restart;視窗版可點標題列 ↻;重新掛回同一個面板,Log 末尾會寫出要打的指令);沒有硬體時改用 force(plugin restart @p0 force);用 scan 確認位址,見第一個 Plugin 1 |
面板標題出現 [FAILED](視窗版標題列右端還有一顆 ↻) | 你的某個 callback 崩潰了,這顆 Plugin 被標記故障並下線(不影響主程式) | 看 Log 的錯誤行找哪個 callback;修好後 plugin restart 重新掛載,視窗版直接點標題列的 ↻ 也可以(plugin 指令會列出失敗原因) |
自訂指令叫不到,Log 有 command 'x' is shadowed by the built-in 'x' command and can never be invoked; rename it | 指令名稱和內建指令同名(例如 run) | 改指令名稱;先用 help 看內建指令有哪些 |
執行類:Plugin 在跑,但行為不對
| 症狀 | 原因 | 怎麼辦 |
|---|---|---|
ExecSync 回 ERR_WRONG_THREAD(-85) | 在 UI Thread 上呼叫:放進了 init、on_key_press、on_config_save 這類 UI Thread 的 callback | 硬體存取放在 probe、poll_fn、自訂指令 handler、on_widget_event 這些 Worker 的 callback,見生命週期與執行緒 |
ERR_HW_OFFLINE(-40) | 沒接 dongle,或後端開不起來 | 沒有硬體時用 --dongle mock。注意:有實體硬體接著時,視窗版會忽略 --dongle mock,連執行期的 dongle mock 指令也一樣,見第一個 Plugin 1 |
ExecSync 打到錯的位址 | 你填了自己 slaves[] 裡宣告的位址,而不是 0 | NxJobRequest 用 memset 清成 0,位址留 0(=這次掛載目前綁定的位址),見讀寫硬體的三種方式 |
對沒有 bank 的掛載回 ERR_INVALID_PARAM | bank 填了非 0 | 沒有 bank 的掛載 bank 只能是 0 |
ERR_POLL_DISABLED(-61) | 使用者下了 poll off,你送了低優先級的 SubmitJob | 這是正確行為,不是錯 |
| 面板上的控制項不見了 | 空間不夠被隱藏:OPTIONAL、寬度不夠,或後面的列因面板太矮被隱藏 | 用 nxlayout --solve --w 40 --h 10 之類確認窄面板,把重要的放前面,見Widget 模式深入 |
LoadLayout 回 ERR_INVALID_PARAM | .nxlay 違反版面規則(y 遞減、widget_id 重複、METER/SPIN 沒填 num_max、GROUPBOX 沒填 h、label 超過 31 位元組……)。規則編號與 widget_id 會寫進你的 Plugin 的 Log(WARN),回傳碼只有錯誤類別 | 存檔前先用編輯器的 Error List 或 nxlayout --solve 驗證;在 init 裡檢查回傳值並 LogWrite |
LoadLayout 回 ERR_NOT_FOUND/ERR_NOT_SUPPORTED | ERR_NOT_FOUND:RCDATA 資源名稱對不上或 .rc 沒編進 DLL(檔案模式則是 .nxlay 沒放在 DLL 旁邊)。ERR_NOT_SUPPORTED:版面含主程式不認得或尚未實作的 type=,整份載入失敗 | 核對資源名稱與 .rc;把 type= 改成這個版本支援的型別 |
SetWidgetProp 回 ERR_FULL,值沒變 | label/aux 超過 31 位元組、blob 超過容量,或影子表文字配額(每面板 512 KiB、全體 4 MiB)用盡;舊值保留 | 縮短文字/分批;blob 記錄要補到 4 位元組 |
載入舊版 Plugin 時 Log 說 abi major 不符 | 以 ABI 3.x 的標頭編出的 DLL(4.0 起 widget 介面整批換掉)。Log 會說明方向:DLL 比主程式舊要「用目前主版本的 PDK 重新編譯」,比主程式新要「更新主程式」 | 用新的 PDK 重新編譯 |
改了 .nxlay,Plugin 行為完全沒變 | 版面是在載入時才讀的:嵌成 RCDATA 資源的要重新建置 DLL;檔案模式要把新的 .nxlay 複製到 DLL 旁邊。另外若改了 [w.N] 的編號,.h 裡的 WIDGET_ID_* 沒跟著重新產生,.c 就會用舊編號(建置不會替你跑 nxlayout --emit-header,也沒有任何檢查) | 改完 .nxlay 重新建置(或複製檔案),編號有變就重新 nxlayout --emit-header,見第一個 Plugin 4 |
| 改了程式,使用者說「還是舊的」 | 只建了一種架構,或忘了把新 DLL 複製進 plugins\,或主程式還開著沒有重新啟動 | 兩種架構都要重建;複製後關掉主程式重開 |
on_mouse_event 在 Widget 面板收不到 | 它只送給 Canvas 面板 | Widget 面板的互動走 on_widget_event |
視窗版按鍵沒送到 on_key_press | 有 widget(文字框等)有鍵盤焦點,按鍵被它吃掉 | 攔的鍵要避開方向鍵、Tab、Space、Enter |
on_layout_changed 沒有每次縮放都響 | 只在內容區尺寸真的變了才送 | 正常行為 |
on_config_save 寫的值沒進 config.ini | 回傳非 ERR_OK、輸出有不合格的行、超過 4094 位元組、崩潰,或是批次模式 | 逐項檢查見設定持久化 |
| 整個主程式被你的 Plugin 帶著崩潰 | 這個建置沒有例外隔離(x86 建置沒有) | 執行 ver 看有沒有例外隔離;x86 上 Plugin 的崩潰會波及主程式,見架構要對:x86/x64 |
Canvas Plugin 不畫了,Log 有 plugin render took N ms (limit 10 ms) ... marked faulted | ui_render_fn 超過 10 ms,被下線且下一幀起不再呼叫 | 見Canvas 模式:用 DrawRegion 一次畫,不要在 render 裡做耗時的事 |
批次裡 cmd N ('...') failed: unknown command | 指令名稱不存在,或你的 handler 超過 10 秒 | 見Batch/headless |
沒有硬體時的除錯
- 用模擬後端:
nexus_tui.exe --dongle mock(模擬後端預設有兩顆裝置,其中一顆在0x58)。 - 視窗版陷阱:有實體硬體接著的時候,視窗版會忽略
--dongle mock。沒有實體硬體時才能穩定用模擬後端。 - 要臨時測 UI 而不管
probe結果,掛載時加force。
還是找不到原因?
- 在批次模式重現:
nexus_tui.exe --batch --dongle mock --cmd "plugin switch 你的名稱" --cmd "你的指令",全部輸出都在標準輸出,方便存檔比對。 - 開啟 Log 的所有等級(
--log-level或視窗版的 Log 等級過濾)。 - 在 handler 開頭、結尾與每個錯誤分支用
LogWrite印出rc。 - 逐支函式的契約查 API 參考(先從 Plugin API 地圖找);規則細節與更多對照表在開發指南,例如常見錯誤對照表。兩份文件也隨 PDK 附在
docs\裡。