10 KiB
10 KiB
GS (DtrGuiSrv) Rust 重構分析
概述
分析是否可將 GS (DtrGuiSrv) C 語言程式完整用 Rust 重構為 raidguard_x_gui_server。
現有 GS 元件分析
GS (C) 元件清單
| 檔案 | 功能 | 複雜度 | 依賴 |
|---|---|---|---|
| cMain.c | 主程式入口、初始化 | 中 | 所有模組 |
| cNetWork.c | TCP 網路通訊 (Port 8922) | 高 | socket API |
| cInBand.c | In-Band API 處理 | 高 | RAID Driver |
| cPollingEvent.c | 事件輪詢執行緒 | 中 | 佇列、檔案 |
| cMail.c | Email 發送 (SMTP) | 中 | SMTP library |
| cShareMem.c | 共享記憶體 | 中 | POSIX SHM |
| cSrvConfig.c | 伺服器設定 | 低 | 檔案 I/O |
| cDTRIoCtl.c | RAID I/O Control | 高 | Driver API |
每個元件的關鍵功能
cMain.c
├── InitGUIServer() - 初始化
├── InitSMTP() - SMTP 初始化
├── InitRAIDCard() - RAID 卡初始化
└── Main Loop - 網路監聽
cNetWork.c
├── InitialSrvSocket() - 建立 TCP Server (Port 8922)
├── InitialClientSocket() - accept()
├── GUITrans() - TCP 傳輸 (Phase1 + Phase2)
└── ServerGUITrans() - 伺服器端傳輸
cInBand.c
├── HandleInBandReq() - 處理 In-Band 請求
├── HandleSrvConfigReq() - 處理伺服器設定
└── [RAID Driver I/O] - 與硬體通訊
cPollingEvent.c
├── PollingCtlrEvent() - 執行緒:輪詢事件
├── SaveInBandEvent() - 儲存到記憶體佇列
├── RecEventToFile() - 寫入事件檔案
└── HandleInBandEventReq() - 處理 GC 請求
cMail.c
├── InitMailEntryFreeQ() - 初始化郵件佇列
├── IssueMailOut() - 發送郵件
└── LoadMailAddrFromFile() - 載入郵件地址
cShareMem.c
├── InitialSHMForAllCtlr() - 初始化共享記憶體
└── 與 RAID Driver 共享資料
cDTRIoCtl.c
├── QuaryDTR() - 查詢 RAID 資訊
├── DevIoControl() - I/O Control
└── 硬體驅動程式介面
重構可行性分析
可以重構的部分 (✅)
| 元件 | Rust 方案 | 難度 |
|---|---|---|
| TCP 網路 | tokio, async-std | 中 |
| 協議解析 | nom, manual parsing | 低 |
| 事件輪詢 | tokio::spawn, interval | 低 |
| 設定管理 | serde_json, toml | 低 |
| 日誌系統 | tracing, log | 低 |
| Email 發送 | lettre | 中 |
| 錯誤處理 | thiserror, anyhow | 低 |
難以重構的部分 (⚠️)
| 元件 | 問題 | 建議 |
|---|---|---|
| cShareMem.c | POSIX 共享記憶體依賴 | 使用 rust-posix-shmem 或 FFI |
| cDTRIoCtl.c | RAID Driver I/O | 需要 FFI 綁定或重寫 Driver |
| 硬體驅動 | 低層級硬體存取 | 保持 C 或用 FFI |
需要 FFI 的部分
// 使用 FFI 呼叫現有 C 函數
#[repr(C)]
pub struct CtlrInfo {
pub bStatus: u8,
pub bCtlrSN: [c_char; 16],
// ...
}
extern "C" {
fn QuaryDTR(bDevNo: u8, bPageBuf: *mut u8, pbHeatsink: *mut u8) -> c_short;
fn DevIoControl(...) -> c_uchar;
fn GetCtlSemP(DeviceNum: c_short) -> c_short;
fn GetCtlSemV(DeviceNum: c_short) -> c_short;
}
現有 Rust 專案結構
raidguard_x_gui_server/
├── Cargo.toml # tokio, serde, tracing, lettre
├── src/
│ ├── main.rs # 入口
│ ├── lib.rs # library
│ ├── server.rs # TCP Server
│ ├── protocol/ # 協議解析
│ ├── handlers/ # 請求處理
│ ├── mock/ # 模擬數據
│ └── backend/ # 後端
重構架構建議
模組化設計
raidguard_x_gui_server/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ │
│ ├── network/ # 網路通訊
│ │ ├── mod.rs
│ │ ├── server.rs # TCP Server (Port 8922)
│ │ ├── transport.rs # Phase1/Phase2 傳輸
│ │ └── client.rs # Client 連線管理
│ │
│ ├── protocol/ # 協議
│ │ ├── mod.rs
│ │ ├── request.rs # 請求解析
│ │ ├── response.rs # 回應封裝
│ │ └── error.rs # 協議錯誤
│ │
│ ├── inband/ # In-Band API
│ │ ├── mod.rs
│ │ ├── handler.rs # 請求處理
│ │ ├── page.rs # Page 資料
│ │ └── password.rs # 密碼處理
│ │
│ ├── event/ # 事件處理
│ │ ├── mod.rs
│ │ ├── queue.rs # 事件佇列
│ │ ├── polling.rs # 輪詢執行緒
│ │ └── storage.rs # 事件儲存
│ │
│ ├── email/ # Email 通知
│ │ ├── mod.rs
│ │ ├── smtp.rs # SMTP 發送
│ │ └── template.rs # 郵件範本
│ │
│ ├── config/ # 設定管理
│ │ ├── mod.rs
│ │ ├── server.rs # 伺服器設定
│ │ └── storage.rs # 設定儲存
│ │
│ ├── driver/ # RAID Driver (FFI)
│ │ ├── mod.rs
│ │ ├── ffi.rs # FFI 綁定
│ │ └── types.rs # C 類型包裝
│ │
│ └── logging/ # 日誌
│ └── mod.rs
技術選型
| 功能 | Rust 選擇 | 說明 |
|---|---|---|
| 非同步 runtime | tokio | 完整 async/await 支援 |
| TCP 網路 | tokio::net | 內建 TCP 支援 |
| 協議解析 | nom / 手動 | 簡單協議,手動即可 |
| 序列化 | serde | JSON 支援 |
| lettre | SMTP client | |
| 日誌 | tracing | 結構化日誌 |
| 錯誤處理 | thiserror | 錯誤鏈 |
| 共享記憶體 | libc::sys::shm | FFI |
| 執行緒安全 | Arc<Mutex<>> | 內建 |
重構階段規劃
Phase 1: 基礎設施 (1-2 週)
- 專案初始化 (現有)
- TCP Server (Port 8922)
- 協議解析 (Phase1/Phase2)
- 基本請求/回應處理
- 日誌系統
Phase 2: In-Band API (2-3 週)
- GET_PAGE 處理 (0x01)
- SEND_PASSWORD 處理 (0x1D)
- 其他 OpCode 處理
- 與 RAID Driver FFI
Phase 3: 事件系統 (1-2 週)
- 事件輪詢執行緒
- 事件佇列
- 事件儲存 (檔案)
- GC 事件查詢
Phase 4: 通知系統 (1 週)
- Email 發送 (SMTP)
- SNMP Trap (可選)
- 設定管理
Phase 5: 整合測試 (1-2 週)
- 單元測試
- 整合測試
- 效能優化
- 文檔
挑戰與解決方案
挑戰 1: RAID Driver I/O
問題: cDTRIoctl.c 需要與硬體驅動程式直接通訊
解決方案:
// 方案 A: FFI 呼叫現有 C 函數
#[link(name = "DtrIoCtl")]
extern "C" {
fn DevIoControl(...) -> c_uchar;
}
// 方案 B: 將 Driver 程式碼移植到 Rust
// (需要大量時間)
// 方案 C: 保持 GS 為 C,只重構 GUI 部分
// (推薦: 混合架構)
挑戰 2: 共享記憶體
問題: cShareMem.c 使用 POSIX 共享記憶體
解決方案:
// 使用 nix crate
use nix::sys::shm::{shmat, shmget, ShmFlags};
// 或使用 libc
use libc::{shmget, shmat, shmctl, IPC_RMID};
挑戰 3: 執行緒同步
問題: C 的 Semaphore 需要轉換
解決方案:
// Rust 建議使用
use std::sync::{Mutex, Semaphore, Arc};
// 或使用 tokio::sync
use tokio::sync::{Mutex, Semaphore};
挑戰 4: 相容性
問題: 需要完全相容現有 GC (Java) 協議
解決方案:
// 確保位元組級別完全相同
#[repr(C)]
struct RequestHeader {
pub req_type: u8,
pub phase_code: u8,
pub length: u16, // Little-endian
}
// 測試覆蓋所有現有功能
推薦策略
策略 A: 完全重構 (全部 Rust)
優點:
- 單一語言,維護簡單
- 記憶體安全
- 現代化生態
缺點:
- 需要 FFI 或重寫 Driver
- 開發時間長
時間預估: 3-4 個月
策略 B: 混合架構 (推薦)
┌─────────────────────────────────────────────────────────────┐
│ 混合架構 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ GC (Java) │ │ Rust Server │ │ C Driver │ │
│ │ Port 8922 │◄──►│ │◄──►│ (現有) │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └──────┬──────┘ └─────────────┘ │
│ │ │
│ 共享記憶體/FDI │
│ │
└─────────────────────────────────────────────────────────────┘
優點:
- 保留穩定的 C Driver
- Rust 處理網路/業務邏輯
- 逐步遷移
缺點:
- FFI 複雜度
時間預估: 2-3 個月
策略 C: 只重構 GUI Client
維持 GS 為 C 程式,只重構 GC (Java) 為 Rust GUI Client。
結論
可以完整重構,但建議採用混合策略:
- 網路/協議/事件/Email → Rust (可完全重構)
- RAID Driver/共享記憶體 → 保持 C 或 FFI
- 優先順序: 先重構 GC (Java→Rust) → 再考慮 GS
主要原因:
- Driver 層變動風險高
- GS 相對穩定,需求變化少
- GC (GUI) 更有現代化價值
現有程式碼利用率
raidguard_x_gui_server (現有)
├── ✅ network/server.rs → 可基於 cNetWork.c
├── ✅ protocol/ → 可基於 GS 協議
├── ⚠️ handlers/ → 需要實作 In-Band API
└── ❌ backend/ → 需要連接 RAID Driver
下一步建議
- 短期: 完成現有 Rust Server 與 GS 的替換
- 中期: FFI 整合現有 C Driver
- 長期: 逐步將 Driver 移植到 Rust (如有需要)