diff --git a/GC_Polling.md b/GC_Polling.md new file mode 100644 index 0000000..97f774c --- /dev/null +++ b/GC_Polling.md @@ -0,0 +1,413 @@ +# Polling 架構分析 + +## 概述 + +Polling 是 RAIDGuard X 用來定期檢測 RAID 控制器狀態的核心機制,負責: +- 取得控制器狀態 +- 取得事件日誌 +- 檢測連線狀態 +- 發送 SNMP Trap + +## 架構流程 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Polling 架構 │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ java.util.Timer (tmPolling) │ │ +│ │ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │ │ +│ │ │ wkPolling1 │ wkPolling2 │ wkEventPolling │wkEventPoll │ │ │ +│ │ │ (12秒) │ (30秒) │ (18秒) │ forSNMP │ │ │ +│ │ │ │ │ │ (22秒) │ │ │ +│ │ └─────────────┴─────────────┴─────────────┴─────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────────────┼────────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Polling() │ │ GetEvent() │ │EventPollFor │ │ +│ │ 狀態輪詢 │ │ 事件取得 │ │ SNMP() │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ PageRefresh() │ │IssueCmdAgent2│ │IssueCmdAgent2│ │ +│ │ 取得頁面資料 │ │ 發送命令 │ │ 發送命令 │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ In-Band API (Port 8922) │ │ +│ │ - 0x01 GET_PAGE │ │ +│ │ - 0xFF/0x02 Get InBand Event │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ RAID Controller │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +## Polling 任務 + +### Timer 排程 + +```java +// frmMain.java - 1460-1477 +java.util.Timer tmPolling = new java.util.Timer(); + +// 主動式輪詢 (線上狀態) +tmPolling.schedule(new wkPolling1(this), 5000, 12001); // 每 12 秒 +tmPolling.schedule(new wkPolling2(this), 5000, 3001); // 每 3 秒 + +// 被動式輪詢 (離線狀態 - 檢查是否重新上線) +tmPolling.schedule(new wkStatusRefresh(this), 5000, 1004); // 每 1 秒 + +// 事件輪詢 +tmPolling.schedule(new wkEventPolling(this), 5000, 18003); // 每 18 秒 +tmPolling.schedule(new wkEventPollingForSNMP(this), 5000, 22002); // 每 22 秒 +``` + +| 任務 | 週期 | 說明 | +|------|------|------| +| wkPolling1 | 12 秒 | 取得控制器頁面資料 | +| wkPolling2 | 3 秒 | 取得控制器頁面資料 + GC | +| wkStatusRefresh | 1 秒 | 狀態刷新 | +| wkEventPolling | 18 秒 | 取得事件日誌 | +| wkEventPollingForSNMP | 22 秒 | 取得事件並發送 SNMP Trap | + +## Polling 類別 + +### wkPolling1 (12秒) + +```java +class wkPolling1 extends java.util.TimerTask { + public void run() { + if (PageDataDB.Polling_Flag) { + fMainTmp.Polling(0x01); // iCareOffline = 0x01 (關心離線) + } + } +} +``` + +**行為:** +- 每 12 秒執行一次 +- 只在 Polling_Flag = true 時執行 +- 關心離線狀態 (iCareOffline = 0x01) +- 呼叫 Polling(0x01) + +### wkPolling2 (3秒) + +```java +class wkPolling2 extends java.util.TimerTask { + public void run() { + if (PageDataDB.Polling_Flag) { + fMainTmp.Polling(0x00); // iCareOffline = 0x00 (不關心離線) + } + Runtime.getRuntime().gc(); // 垃圾回收 + } +} +``` + +**行為:** +- 每 3 秒執行一次 +- 不關心離線狀態 (iCareOffline = 0x00) +- 呼叫 Polling(0x00) +- 執行 GC 垃圾回收 + +### wkEventPolling (18秒) + +```java +class wkEventPolling extends java.util.TimerTask { + public void run() { + if (PageDataDB.Polling_Flag) { + fMainTmp.GetEvent(); // 取得事件 + } + } +} +``` + +**行為:** +- 每 18 秒執行一次 +- 呼叫 GetEvent() 取得事件日誌 +- 更新 UI 事件列表 + +### wkEventPollingForSNMP (22秒) + +```java +class wkEventPollingForSNMP extends java.util.TimerTask { + public void run() { + if (fMainTmp.jtHostList.getSelectedRow() >= 0) { + fMainTmp.EventPollingForSNMP(); // 取得事件並發送 SNMP + } + } +} +``` + +**行為:** +- 每 22 秒執行一次 +- 需要有選中的控制器 +- 呼叫 EventPollingForSNMP() 取得事件並發送 SNMP Trap + +## Polling() 方法 + +```java +public void Polling(int iCareOffline) { + // 遍歷所有已註冊的控制器 + for (int i = 0; i < jtHostList.getRowCount(); i++) { + sHostName = jtHostList.getValueAt(i, iHostList_Host).toString(); + sIpAddr = jtHostList.getValueAt(i, iHostList_IP).toString(); + sSN = jtHostList.getValueAt(i, iHostList_SN).toString(); + + if (!sSN.equals("")) { + // 檢查連線失敗次數 + if (GetHostCmdFailCnt(sSN) > iMaxCommandFail) { + // 設定為離線狀態 + SetHostStatus(sSN, 0x00); + jtHostList.setValueAt(imgRed, i, iHostList_Net); + jtHostList.setValueAt("Disconnected", i, iHostList_Status); + ClearEventLog(); // 清除事件日誌 + } + + // 根據參數決定行為 + if (iCareOffline == 0x01) { + // 只關心線上的控制器 + if (GetHostStatus(sSN) == 0x01) { + PageRefresh(sIpAddr, sSN, new Point(0, 0)); + // 如果是 MAX Storage,刷新備援控制器 + if (Check_MAX_Storage(sSN)) { + PageRefresh(sIpAddr, GetSlaveSerialNumber(sSN), new Point(0, 0)); + } + } + } else { + // 刷新所有控制器 + PageRefresh(sIpAddr, sSN, new Point(0, 0)); + if (Check_MAX_Storage(sSN)) { + PageRefresh(sIpAddr, GetSlaveSerialNumber(sSN), new Point(0, 0)); + } + } + } + } +} +``` + +### iCareOffline 參數 + +| 值 | 行為 | +|----|------| +| 0x01 | 只刷新線上 (GetHostStatus == 0x01) 的控制器 | +| 0x00 | 刷新所有控制器 (不管離線與否) | + +## GetEvent() 方法 + +```java +public void GetEvent() { + // 取得目前選中的控制器事件 + GetEvent(sIpAddr, sSN); +} + +// PageDataDB.GetEvent() +public static void GetEvent(String sIP, String sSN) { + // 避免重複執行 + if (bIsGetEvent) return; + bIsGetEvent = true; + + // 建立命令 + CmdQueue cmdQueue = new CmdQueue(); + cmdQueue.NewCommand(sIP, sSN, 1, 1, 3); + + // 設定命令內容 + bCmds[0] = 0xFF; // Phase Code + bCmds[1] = 0x00; // Request Code + bCmds[2] = 0x00; // Controller No + bCmds[3] = 0x02; // InBand Event Cmd Code: Get InBand Event + + // 發送命令 + issueCmdAgent2 ICA = new issueCmdAgent2(); + ICA.IssusCmd(sIP, sSN, cmdQueue); + + // 處理回應 + ReplyTask(cmdQueue, iEventPollingCheckType); + + bIsGetEvent = false; +} +``` + +## EventPollingForSNMP() 方法 + +```java +public void EventPollingForSNMP() { + // 遍歷所有控制器 + for (int iHost = 0; iHost < iMaxHosts; iHost++) { + // 取得控制器資訊 + sTmpHostName = jtHostList.getValueAt(iHost, iHostList_Host); + sTmpIP = jtHostList.getValueAt(iHost, iHostList_IP); + sTmpSN = jtHostList.getValueAt(iHost, iHostList_SN); + + // 發送事件查詢命令 (OpCode 0xFF/0x02) + CmdQueue cmdQueue = new CmdQueue(); + cmdQueue.NewCommand(sTmpIP, sTmpSN, 1, 70, 3); + + bCmds[3] = 0x02; // Get InBand Event + + // 發送並處理回應 + issueCmdAgent2 ICA = new issueCmdAgent2(); + ICA.IssusCmd(sTmpIP, sTmpSN, cmdQueue); + + // 處理 SNMP 回應 + ReplyTaskForSNMP(sTmpHostName, sTmpIP, sTmpSN, cmdQueue); + } +} +``` + +## 通訊協定 + +### 取得事件 (Get InBand Event) + +``` +Request: + Phase Code: 0xFF + Request Code: 0x00 + Controller: 0x00 + OpCode: 0x02 (Get InBand Event) + +Response: + - 事件資料 (最多 70 bytes) + - 包含事件時間、事件 ID、事件描述 +``` + +## Polling 開關 + +```java +// 選單功能 - Polling On/Off +public void jmPollingSwitch_actionPerformed(ActionEvent e) { + if (PageDataDB.Polling_Flag) { + PageDataDB.Polling_Flag = false; + jmPollingSwitch.setText("Polling Off"); + } else { + PageDataDB.Polling_Flag = true; + jmPollingSwitch.setText("Polling On"); + } +} +``` + +## Polling 狀態判斷 + +``` +主機狀態 (Host Status): + 0x00 - 離線 (Offline) + 0x01 - 線上 (Online) + 0x10 - 更新中 (Updating) + +連線失敗計數: + - GetHostCmdFailCnt(sSN) > iMaxCommandFail → 判定離線 + - 每次命令失敗計數 +1 + - 每次成功計數歸零 +``` + +## Code Flow + +### 完整 Polling 流程 + +``` +1. Timer 觸發 + │ + ▼ +2. 檢查 Polling_Flag + │ true → 繼續 + │ false → 停止 + ▼ +3. 選擇 Polling 任務 + │ + ├── wkPolling1 ──► Polling(0x01) ──► PageRefresh() ──► In-Band API + │ + ├── wkPolling2 ──► Polling(0x00) ──► PageRefresh() ──► In-Band API + │ + ├── wkEventPolling ──► GetEvent() ──► In-Band API + │ + └── wkEventPollingForSNMP ──► EventPollingForSNMP() ──► In-Band API ──► SNMP Trap +``` + +### 事件處理流程 + +``` +控制器事件發生 + │ + ▼ +Polling 偵測到 (Get InBand Event) + │ + ▼ +解析事件內容 + │ + ├── 更新 UI Event List + │ + ├── 發送 SNMP Trap (EventPollingForSNMP) + │ + └── 發送 Email Alert (如果啟用) +``` + +## 關鍵變數 + +```java +// PageDataDB.java +public static boolean Polling_Flag = true; // Polling 開關 +public static boolean bIsGetEvent = false; // 防止重複取得事件 +public static int iMaxCommandFail = 3; // 最大允許連線失敗次數 + +// frmMain.java +java.util.Timer tmPolling; // Polling 計時器 +``` + +## 效能優化 + +1. **分層輪詢**: 不同任務使用不同週期 +2. **離線加速**: 離線後改用較短週期 (3秒 vs 12秒) +3. **事件鎖**: `bIsGetEvent` 防止重複取得 +4. **GC 排程**: wkPolling2 執行垃圾回收 + +## Rust 實作建議 + +```rust +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tokio::time::{interval, Duration}; + +pub struct PollingManager { + polling_flag: Arc, + // 其他狀態... +} + +impl PollingManager { + pub fn new() -> Self { + Self { + polling_flag: Arc::new(AtomicBool::new(true)), + } + } + + pub fn start(&self) { + let flag = self.polling_flag.clone(); + + tokio::spawn(async move { + let mut timer = interval(Duration::from_secs(12)); + loop { + timer.tick().await; + if flag.load(Ordering::SeqCst) { + self.polling().await; + } + } + }); + } + + pub fn stop(&self) { + self.polling_flag.store(false, Ordering::SeqCst); + } + + async fn polling(&self) { + // 取得控制器狀態 + // 刷新頁面資料 + } +} +```