概述
固件刷写是一个多阶段的复杂过程,涉及设备检测、DRAM 初始化、U-Boot 下载、分区刷写等步骤。OpenixCLI 的 Process 模块提供全局进度追踪系统,使用原子操作实现线程安全的状态管理,同时支持 CLI 进度条和 TUI 实时轮询两种模式。
模块结构
src/process/ ├── mod.rs # 模块导出 ├── global_progress.rs # GlobalProgress 核心实现 ├── stages.rs # FlashStages 阶段定义 └── reporter.rs # ProgressReporter 简化封装
|
核心导出
pub use global_progress::{multi_progress, StageType}; pub use reporter::ProgressReporter; pub use stages::FlashStages;
|
架构与依赖
双模式进度显示
| 模式 |
进度显示 |
状态同步 |
适用场景 |
| CLI |
indicatif ProgressBar |
实时更新 |
命令行刷写 |
| TUI |
ProgressSnapshot 轮询 |
定时获取 |
交互界面 |
模块依赖关系
Process Module ├── indicatif (外部 crate) - 进度条 ├── once_cell (外部 crate) - 全局状态延迟初始化 ├── std::sync::atomic - AtomicU64/AtomicBool 线程安全计数器 ├── std::sync::Mutex - 复合类型互斥保护 └── std::time::Instant - 速度计算时间戳
|
核心数据结构
StageType 阶段枚举
定义刷写过程中的所有阶段:
#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StageType { Init,
FelDram,
FelUboot,
FelReconnect,
FesQuery,
FesErase,
FesMbr,
FesPartitions,
FesBoot,
FesMode, }
impl StageType { pub fn name(&self) -> &'static str { match self { StageType::Init => "Initializing", StageType::FelDram => "DRAM Init", StageType::FelUboot => "U-Boot Download", StageType::FelReconnect => "Reconnecting", StageType::FesQuery => "Query Device", StageType::FesErase => "Erasing", StageType::FesMbr => "Writing MBR", StageType::FesPartitions => "Flashing Partitions", StageType::FesBoot => "Writing Boot", StageType::FesMode => "Setting Mode", } } }
|
GlobalProgress 结构
全局进度追踪器,使用原子类型实现线程安全:
pub struct GlobalProgress { progress_bar: Mutex<Option<ProgressBar>>,
total_weight: AtomicU64,
completed_weight: AtomicU64,
current_stage: AtomicUsize,
stage_progress: AtomicU64,
total_bytes: AtomicU64,
global_written_bytes: AtomicU64,
stages: Mutex<Vec<StageInfo>>,
started: AtomicUsize,
current_partition: Mutex<String>,
last_update_time: Mutex<Option<Instant>>,
last_update_bytes: AtomicU64,
current_speed: Mutex<f64>,
precise_progress: Mutex<f64>, }
|
StageInfo 结构
#[derive(Debug, Clone)] pub struct StageInfo { pub stage_type: StageType,
pub weight: u64,
pub completed: bool,
pub sub_total: u64, }
|
ProgressSnapshot 结构
用于 TUI 模式定时轮询进度状态:
pub struct ProgressSnapshot { pub precise_progress: f64,
pub stage_progress: u64,
pub total_bytes: u64,
pub speed: f64,
pub current_partition: String,
pub current_stage_index: usize,
pub stages: Vec<StageInfo>, }
|
全局状态管理
全局实例
use once_cell::sync::Lazy; use std::sync::Arc;
static MULTI_PROGRESS: Lazy<Arc<MultiProgress>> = Lazy::new(|| Arc::new(MultiProgress::new()));
pub fn multi_progress() -> Arc<MultiProgress> { Arc::clone(&MULTI_PROGRESS) }
static GLOBAL_PROGRESS: Lazy<Arc<GlobalProgress>> = Lazy::new(|| Arc::new(GlobalProgress::new()));
pub fn global_progress() -> Arc<GlobalProgress> { Arc::clone(&GLOBAL_PROGRESS) }
|
TUI 模式控制
static TUI_MODE: AtomicBool = AtomicBool::new(false);
pub fn set_tui_mode(enabled: bool) { TUI_MODE.store(enabled, Ordering::SeqCst); }
pub fn is_tui_mode() -> bool { TUI_MODE.load(Ordering::SeqCst) }
|
FlashStages 阶段序列
FEL 模式阶段序列
pub fn for_fel_mode() -> Self { let mut instance = Self::new(); instance.stages = vec![ StageType::Init, StageType::FelDram, StageType::FelUboot, StageType::FelReconnect, StageType::FesQuery, StageType::FesErase, StageType::FesMbr, StageType::FesPartitions, StageType::FesBoot, StageType::FesMode, ]; instance }
|
FES 模式阶段序列
pub fn for_fes_mode() -> Self { let mut instance = Self::new(); instance.stages = vec![ StageType::Init, StageType::FesQuery, StageType::FesErase, StageType::FesMbr, StageType::FesPartitions, StageType::FesBoot, StageType::FesMode, ]; instance }
|
阶段进度计算
预定义权重分配
pub fn define_stages(&self, stage_types: &[StageType]) { let mut stages = self.stages.lock().unwrap(); stages.clear();
let mut cumulative_percent = 0u64; for stage_type in stage_types { let end_percent = match stage_type { StageType::Init => 3, StageType::FelDram => 5, StageType::FelUboot => 8, StageType::FelReconnect => 10, StageType::FesQuery => 12, StageType::FesErase => 14, StageType::FesMbr => 20, StageType::FesPartitions => 100, StageType::FesBoot => 100, StageType::FesMode => 100, };
stages.push(StageInfo { stage_type: *stage_type, weight: end_percent - cumulative_percent, completed: false, sub_total: 0, }); cumulative_percent = end_percent; }
self.total_weight.store(100, Ordering::SeqCst); self.completed_weight.store(0, Ordering::SeqCst); self.current_stage.store(0, Ordering::SeqCst); }
|
分区阶段权重动态设置
pub fn set_partition_stage_weight(&self, total_bytes: u64) { let current = self.current_stage.load(Ordering::SeqCst); let mut stages = self.stages.lock().unwrap();
if current < stages.len() && stages[current].stage_type == StageType::FesPartitions { let completed_weight: u64 = stages .iter() .filter(|s| s.completed) .map(|s| s.weight) .sum();
stages[current].weight = 80; stages[current].sub_total = total_bytes;
self.completed_weight.store(completed_weight, Ordering::SeqCst); self.total_bytes.store(total_bytes, Ordering::SeqCst); self.stage_progress.store(0, Ordering::SeqCst); self.global_written_bytes.store(0, Ordering::SeqCst); } }
|
进度计算公式
总进度 = completed_weight + (stage_progress / sub_total) * stage_weight
其中: - completed_weight: 已完成阶段的权重总和 - stage_progress: 当前阶段内进度(字节) - sub_total: 当前阶段总字节数 - stage_weight: 当前阶段权重
|
速度计算机制
pub fn update_stage_progress_with_speed(&self, progress: u64) { let now = Instant::now(); let mut last_time = self.last_update_time.lock().unwrap(); let last_bytes = self.last_update_bytes.load(Ordering::SeqCst);
if let Some(last) = *last_time { let elapsed = now.duration_since(last).as_secs_f64(); if elapsed > 0.0 { let bytes_diff = progress.saturating_sub(last_bytes); let speed = bytes_diff as f64 / elapsed; *self.current_speed.lock().unwrap() = speed; } }
*last_time = Some(now); self.last_update_bytes.store(progress, Ordering::SeqCst);
self.update_stage_progress(progress);
self.update_progress_message(); }
|
速度显示格式化
pub fn update_progress_message(&self) { let partition = self.current_partition.lock().unwrap(); let speed = *self.current_speed.lock().unwrap(); let progress = self.stage_progress.load(Ordering::SeqCst); let total = self.total_bytes.load(Ordering::SeqCst);
let speed_str = if speed > 1024.0 * 1024.0 { format!("{:.2} MB/s", speed / (1024.0 * 1024.0)) } else if speed > 1024.0 { format!("{:.2} KB/s", speed / 1024.0) } else { format!("{:.0} B/s", speed) };
let progress_str = if total > 0 { let progress_mb = progress as f64 / (1024.0 * 1024.0); let total_mb = total as f64 / (1024.0 * 1024.0); format!("{:.1}/{:.1} MB", progress_mb, total_mb) } else { String::new() };
let message = if partition.is_empty() { format!("{} {}", speed_str, progress_str) } else { format!("[{}] {} {}", partition, speed_str, progress_str) };
if let Some(pb) = self.progress_bar.lock().unwrap().as_ref() { pb.set_message(message); } }
|
TUI 模式快照机制
pub fn snapshot(&self) -> ProgressSnapshot { let stages = self.stages.lock().unwrap().clone(); let partition = self.current_partition.lock().unwrap().clone(); let speed = *self.current_speed.lock().unwrap(); let precise = *self.precise_progress.lock().unwrap();
ProgressSnapshot { precise_progress: precise, stage_progress: self.stage_progress.load(Ordering::SeqCst), total_bytes: self.total_bytes.load(Ordering::SeqCst), speed, current_partition: partition, current_stage_index: self.current_stage.load(Ordering::SeqCst), stages, } }
|
TUI 中的使用方式:
let snapshot = global_progress().snapshot();
app.progress = snapshot.precise_progress; app.current_partition = snapshot.current_partition; app.speed = format_speed(snapshot.speed); app.stages = snapshot.stages;
|
ProgressReporter 封装
pub struct ProgressReporter { progress: Arc<GlobalProgress>, }
impl ProgressReporter { pub fn new() -> Self { Self { progress: global_progress(), } }
pub fn start(&self) { self.progress.start(); }
pub fn define_stages(&self, stages: &[StageType]) { self.progress.define_stages(stages); }
pub fn begin_stage(&self, stage_type: StageType) { self.progress.start_stage(stage_type); }
pub fn set_partition_stage_weight(&self, total_bytes: u64) { self.progress.set_partition_stage_weight(total_bytes); }
pub fn set_current_partition(&self, partition_name: &str) { self.progress.set_current_partition(partition_name); }
pub fn update_progress_with_speed(&self, current: u64) { self.progress.update_stage_progress_with_speed(current); }
pub fn complete_stage(&self) { self.progress.complete_stage(); }
pub fn finish(&self) { self.progress.finish(); } }
|
调用流程分析
刷写流程中的进度更新
impl Flasher { pub async fn execute(&self) -> FlashResult<()> { let logger = Logger::new();
let stages = if is_fel_mode { FlashStages::for_fel_mode() } else { FlashStages::for_fes_mode() };
logger.define_stages(stages.stages()); logger.start_global_progress();
logger.begin_stage(StageType::Init); logger.complete_stage();
if is_fel_mode { logger.begin_stage(StageType::FelDram); logger.complete_stage();
logger.begin_stage(StageType::FelUboot); logger.complete_stage();
logger.begin_stage(StageType::FelReconnect); logger.complete_stage(); }
logger.begin_stage(StageType::FesQuery); logger.complete_stage();
if need_erase { logger.begin_stage(StageType::FesErase); logger.complete_stage(); }
logger.begin_stage(StageType::FesMbr); logger.complete_stage();
logger.begin_stage(StageType::FesPartitions); logger.set_partition_stage_weight(total_partition_bytes);
for partition in partitions { logger.set_current_partition(&partition.name);
for chunk in download_chunks { logger.update_progress_with_speed(bytes_written); } } logger.complete_stage();
logger.begin_stage(StageType::FesBoot); logger.complete_stage();
logger.begin_stage(StageType::FesMode); logger.complete_stage();
logger.finish_progress();
Ok(()) } }
|
CLI 进度条显示效果
⠋ [00:00:05] [██████████░░░░░░░░░░░░░░░░░░░░░░] 42% [rootfs] 2.5 MB/s 45.2/128.0 MB
|
代码亮点
1. AtomicU64 线程安全计数
total_weight: AtomicU64, completed_weight: AtomicU64, stage_progress: AtomicU64,
self.total_weight.store(100, Ordering::SeqCst); let weight = self.completed_weight.fetch_add(weight, Ordering::SeqCst);
|
原子操作类型:
| 类型 |
作用 |
| AtomicU64 |
64 位整数原子操作 |
| AtomicUsize |
usize 原子操作(索引) |
| AtomicBool |
布尔值原子操作 |
2. Ordering::SeqCst 内存序
TUI_MODE.store(enabled, Ordering::SeqCst);
|
3. Mutex 保护复合类型
stages: Mutex<Vec<StageInfo>>, progress_bar: Mutex<Option<ProgressBar>>, current_partition: Mutex<String>,
|
4. 原子操作的 swap 方法
pub fn start(&self) { if self.started.swap(1, Ordering::SeqCst) == 1 { return; }
}
|
5. once_cell::Lazy 全局单例
static GLOBAL_PROGRESS: Lazy<Arc<GlobalProgress>> = Lazy::new(|| Arc::new(GlobalProgress::new()));
|
6. indicatif 进度条样式
pb.set_style( ProgressStyle::default_bar() .template( "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos:>3}% {msg}", ) .unwrap() .progress_chars("#>-"), ); pb.enable_steady_tick(Duration::from_millis(100));
|
阶段进度时线图
gantt
title FEL 模式刷写阶段进度
dateFormat X
axisFormat %s
section FEL 阶段
Init :0, 3
DRAM Init :3, 5
U-Boot Download :5, 8
Reconnecting :8, 10
section FES 阶段
Query Device :10, 12
Erasing :12, 14
Writing MBR :14, 20
Flashing Partitions :20, 100
Writing Boot :crit, 95, 98
Setting Mode :crit, 98, 100
实践示例
CLI 模式使用
use openixcli::process::{FlashStages, global_progress, StageType}; use openixcli::utils::Logger;
fn main() { let logger = Logger::new();
let stages = FlashStages::for_fel_mode(); logger.define_stages(stages.stages());
logger.start_global_progress();
for stage in stages.stages() { logger.begin_stage(*stage); println!("阶段: {}", stage.name());
for i in 0..100 { logger.update_progress_with_speed(i); std::thread::sleep(std::time::Duration::from_millis(10)); }
logger.complete_stage(); }
logger.finish_progress(); }
|
TUI 模式使用
use openixcli::process::{set_tui_mode, global_progress};
fn run_tui() { set_tui_mode(true);
spawn_flash_task();
loop { let snapshot = global_progress().snapshot();
draw_progress_bar(snapshot.precise_progress); draw_speed(snapshot.speed); draw_partition(snapshot.current_partition); draw_stages(snapshot.stages);
if snapshot.precise_progress >= 100.0 { break; }
std::thread::sleep(std::time::Duration::from_millis(100)); } }
|
获取当前进度
let progress = global_progress();
let percent = progress.get_progress(); println!("当前进度: {}%", percent);
let snapshot = progress.snapshot(); println!("当前分区: {}", snapshot.current_partition); println!("传输速度: {:.2} MB/s", snapshot.speed / (1024.0 * 1024.0)); println!("已完成阶段: {}", snapshot.stages.iter().filter(|s| s.completed).count());
|
数据结构关系图
classDiagram
class GlobalProgress {
+progress_bar: Mutex~Option~ProgressBar~~
+total_weight: AtomicU64
+completed_weight: AtomicU64
+current_stage: AtomicUsize
+stages: Mutex~Vec~StageInfo~~
+current_speed: Mutex~f64~
+define_stages(stage_types)
+start()
+start_stage(stage_type)
+update_stage_progress(progress)
+complete_stage()
+snapshot() ProgressSnapshot
+finish()
}
class StageInfo {
+stage_type: StageType
+weight: u64
+completed: bool
+sub_total: u64
}
class ProgressSnapshot {
+precise_progress: f64
+stage_progress: u64
+total_bytes: u64
+speed: f64
+current_partition: String
+stages: Vec~StageInfo~
}
class StageType {
+Init
+FelDram
+FelUboot
+FelReconnect
+FesQuery
+FesErase
+FesMbr
+FesPartitions
+FesBoot
+FesMode
+name() String
}
class FlashStages {
+stages: Vec~StageType~
+for_fel_mode()
+for_fes_mode()
}
class ProgressReporter {
+progress: Arc~GlobalProgress~
+start()
+begin_stage()
+update_progress_with_speed()
+complete_stage()
}
GlobalProgress --> StageInfo
GlobalProgress --> ProgressSnapshot
FlashStages --> StageType
ProgressReporter --> GlobalProgress