diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 184c226..02a5cbe 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4746,9 +4746,9 @@ dependencies = [ [[package]] name = "sftool-lib" -version = "0.1.19" +version = "0.2.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b6bb52c1525995ed9a297993755382b6632cba74a754eccf10a394f010bd29" +checksum = "8d2bee57e1d58cdd668c2a61ff7e8ae6da45e3f3e9758dcb85f45d8cf6a5cc74" dependencies = [ "bitfield 0.19.0", "crc", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1e695cf..cb771ab 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,7 +26,7 @@ serde_json = "1" tauri-plugin-store = "2" tauri-plugin-updater = "2" serialport = "4.2" -sftool-lib = "0.1.19" +sftool-lib = "0.2.0-rc.1" tauri-plugin-dialog = "2" tauri-plugin-fs = "2" tauri-plugin-process = "2" diff --git a/src-tauri/src/commands/device.rs b/src-tauri/src/commands/device.rs index d72fbfe..ff28106 100644 --- a/src-tauri/src/commands/device.rs +++ b/src-tauri/src/commands/device.rs @@ -2,7 +2,7 @@ use crate::progress::TauriProgressCallback; use crate::state::AppState; use crate::types::{DeviceConfig, PortInfo}; use crate::utils::create_tool_instance_with_progress; -use sftool_lib::progress::ProgressCallbackArc; +use sftool_lib::progress::ProgressSinkArc; use std::sync::{Arc, Mutex}; use tauri::{AppHandle, State}; @@ -70,7 +70,7 @@ pub async fn connect_device( }; // 创建 Tauri 进度回调 - let progress_callback: ProgressCallbackArc = Arc::new(TauriProgressCallback::new(app_handle)); + let progress_callback: ProgressSinkArc = Arc::new(TauriProgressCallback::new(app_handle)); // 创建带进度回调的工具实例 let tool = create_tool_instance_with_progress(&device_config, progress_callback)?; diff --git a/src-tauri/src/progress/tauri_callback.rs b/src-tauri/src/progress/tauri_callback.rs index 5c5ea99..88c1926 100644 --- a/src-tauri/src/progress/tauri_callback.rs +++ b/src-tauri/src/progress/tauri_callback.rs @@ -1,19 +1,23 @@ -use crate::types::TauriProgressEvent; -use sftool_lib::progress::{ProgressCallback, ProgressId, ProgressInfo}; -use std::sync::atomic::{AtomicU64, Ordering}; +use crate::types::{ + TauriProgressContext, TauriProgressEvent, TauriProgressOperation, TauriProgressStatus, + TauriProgressType, +}; +use sftool_lib::progress::{ProgressEvent, ProgressSink}; +use std::collections::HashMap; +use std::sync::Mutex; use tauri::{AppHandle, Emitter}; // Tauri 进度回调实现 pub struct TauriProgressCallback { app_handle: AppHandle, - id_counter: AtomicU64, + contexts: Mutex>, } impl TauriProgressCallback { pub fn new(app_handle: AppHandle) -> Self { Self { app_handle, - id_counter: AtomicU64::new(1), + contexts: Mutex::new(HashMap::new()), } } @@ -24,65 +28,92 @@ impl TauriProgressCallback { } } -impl ProgressCallback for TauriProgressCallback { - fn start(&self, info: ProgressInfo) -> ProgressId { - let id = self.id_counter.fetch_add(1, Ordering::SeqCst); - let progress_id = ProgressId(id); +impl ProgressSink for TauriProgressCallback { + fn on_event(&self, event: ProgressEvent) { + match event { + ProgressEvent::Start { id, ctx } => { + let current = ctx.current; + let context = TauriProgressContext::from(ctx); + let total = total_from_progress_type(&context.progress_type); + self.contexts.lock().unwrap().insert(id.0, context.clone()); - let (total, current) = match info.progress_type { - sftool_lib::progress::ProgressType::Spinner => (None, None), - sftool_lib::progress::ProgressType::Bar { total } => (Some(total), info.current), - }; + self.emit_event(TauriProgressEvent { + id: id.0, + event_type: "start".to_string(), + step: context.step, + progress_type: context.progress_type, + operation: context.operation, + current, + total, + status: None, + }); + } + ProgressEvent::Update { id, ctx } => { + let context = TauriProgressContext::from(ctx); + self.contexts.lock().unwrap().insert(id.0, context.clone()); - let event = TauriProgressEvent { - id, - event_type: "start".to_string(), - step: info.prefix, - message: info.message, - current, - total, - }; + self.emit_event(TauriProgressEvent { + id: id.0, + event_type: "update".to_string(), + step: context.step, + progress_type: context.progress_type, + operation: context.operation, + current: None, + total: None, + status: None, + }); + } + ProgressEvent::Advance { id, delta } => { + let context = self.contexts.lock().unwrap().get(&id.0).cloned(); + let (step, progress_type, operation) = match context { + Some(ctx) => (ctx.step, ctx.progress_type, ctx.operation), + None => ( + 0, + TauriProgressType::Spinner, + TauriProgressOperation::Unknown, + ), + }; - self.emit_event(event); - progress_id - } - - fn update_message(&self, id: ProgressId, message: String) { - let event = TauriProgressEvent { - id: id.0, - event_type: "update".to_string(), - step: "".to_string(), - message, - current: None, - total: None, - }; - - self.emit_event(event); - } + self.emit_event(TauriProgressEvent { + id: id.0, + event_type: "increment".to_string(), + step, + progress_type, + operation, + current: Some(delta), + total: None, + status: None, + }); + } + ProgressEvent::Finish { id, status } => { + let context = self.contexts.lock().unwrap().remove(&id.0); + let (step, progress_type, operation) = match context { + Some(ctx) => (ctx.step, ctx.progress_type, ctx.operation), + None => ( + 0, + TauriProgressType::Spinner, + TauriProgressOperation::Unknown, + ), + }; - fn increment(&self, id: ProgressId, delta: u64) { - let event = TauriProgressEvent { - id: id.0, - event_type: "increment".to_string(), - step: "".to_string(), - message: "".to_string(), - current: Some(delta), - total: None, - }; - - self.emit_event(event); + self.emit_event(TauriProgressEvent { + id: id.0, + event_type: "finish".to_string(), + step, + progress_type, + operation, + current: None, + total: None, + status: Some(TauriProgressStatus::from(status)), + }); + } + } } +} - fn finish(&self, id: ProgressId, final_message: String) { - let event = TauriProgressEvent { - id: id.0, - event_type: "finish".to_string(), - step: "".to_string(), - message: final_message, - current: None, - total: None, - }; - - self.emit_event(event); +fn total_from_progress_type(progress_type: &TauriProgressType) -> Option { + match progress_type { + TauriProgressType::Spinner => None, + TauriProgressType::Bar { total } => Some(*total), } } diff --git a/src-tauri/src/types/progress.rs b/src-tauri/src/types/progress.rs index be3ea11..f978871 100644 --- a/src-tauri/src/types/progress.rs +++ b/src-tauri/src/types/progress.rs @@ -1,12 +1,202 @@ use serde::{Deserialize, Serialize}; +use sftool_lib::progress; + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum TauriProgressType { + Spinner, + Bar { total: u64 }, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TauriStubStage { + Start, + SignatureKey, + RamStub, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TauriEraseFlashStyle { + Complete, + Addressed, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TauriEraseRegionStyle { + LegacyFlashStartDecimalLength, + HexLength, + Range, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum TauriProgressOperation { + Connect, + DownloadStub { + stage: TauriStubStage, + }, + EraseFlash { + address: u32, + style: TauriEraseFlashStyle, + }, + EraseRegion { + address: u32, + len: u32, + style: TauriEraseRegionStyle, + }, + EraseAllRegions, + Verify { + address: u32, + len: u32, + }, + CheckRedownload { + address: u32, + size: u64, + }, + WriteFlash { + address: u32, + size: u64, + }, + ReadFlash { + address: u32, + size: u32, + }, + Unknown, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct TauriProgressContext { + pub step: i32, + pub progress_type: TauriProgressType, + pub operation: TauriProgressOperation, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "kind", content = "message", rename_all = "snake_case")] +pub enum TauriProgressStatus { + Success, + Retry, + Skipped, + Required, + NotFound, + Failed(String), + Aborted, +} // Tauri 进度事件结构 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TauriProgressEvent { pub id: u64, pub event_type: String, // "start", "update", "increment", "finish" - pub step: String, - pub message: String, + pub step: i32, + pub progress_type: TauriProgressType, + pub operation: TauriProgressOperation, + #[serde(skip_serializing_if = "Option::is_none")] pub current: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub total: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, +} + +impl From for TauriProgressType { + fn from(value: progress::ProgressType) -> Self { + match value { + progress::ProgressType::Spinner => Self::Spinner, + progress::ProgressType::Bar { total } => Self::Bar { total }, + } + } +} + +impl From for TauriStubStage { + fn from(value: progress::StubStage) -> Self { + match value { + progress::StubStage::Start => Self::Start, + progress::StubStage::SignatureKey => Self::SignatureKey, + progress::StubStage::RamStub => Self::RamStub, + } + } +} + +impl From for TauriEraseFlashStyle { + fn from(value: progress::EraseFlashStyle) -> Self { + match value { + progress::EraseFlashStyle::Complete => Self::Complete, + progress::EraseFlashStyle::Addressed => Self::Addressed, + } + } +} + +impl From for TauriEraseRegionStyle { + fn from(value: progress::EraseRegionStyle) -> Self { + match value { + progress::EraseRegionStyle::LegacyFlashStartDecimalLength => { + Self::LegacyFlashStartDecimalLength + } + progress::EraseRegionStyle::HexLength => Self::HexLength, + progress::EraseRegionStyle::Range => Self::Range, + } + } +} + +impl From for TauriProgressOperation { + fn from(value: progress::ProgressOperation) -> Self { + match value { + progress::ProgressOperation::Connect => Self::Connect, + progress::ProgressOperation::DownloadStub { stage } => Self::DownloadStub { + stage: stage.into(), + }, + progress::ProgressOperation::EraseFlash { address, style } => Self::EraseFlash { + address, + style: style.into(), + }, + progress::ProgressOperation::EraseRegion { + address, + len, + style, + } => Self::EraseRegion { + address, + len, + style: style.into(), + }, + progress::ProgressOperation::EraseAllRegions => Self::EraseAllRegions, + progress::ProgressOperation::Verify { address, len } => Self::Verify { address, len }, + progress::ProgressOperation::CheckRedownload { address, size } => { + Self::CheckRedownload { address, size } + } + progress::ProgressOperation::WriteFlash { address, size } => { + Self::WriteFlash { address, size } + } + progress::ProgressOperation::ReadFlash { address, size } => { + Self::ReadFlash { address, size } + } + } + } +} + +impl From for TauriProgressContext { + fn from(value: progress::ProgressContext) -> Self { + Self { + step: value.step, + progress_type: value.progress_type.into(), + operation: value.operation.into(), + } + } +} + +impl From for TauriProgressStatus { + fn from(value: progress::ProgressStatus) -> Self { + match value { + progress::ProgressStatus::Success => Self::Success, + progress::ProgressStatus::Retry => Self::Retry, + progress::ProgressStatus::Skipped => Self::Skipped, + progress::ProgressStatus::Required => Self::Required, + progress::ProgressStatus::NotFound => Self::NotFound, + progress::ProgressStatus::Failed(message) => Self::Failed(message), + progress::ProgressStatus::Aborted => Self::Aborted, + } + } } diff --git a/src-tauri/src/utils/tool_factory.rs b/src-tauri/src/utils/tool_factory.rs index 94e40a6..83261d6 100644 --- a/src-tauri/src/utils/tool_factory.rs +++ b/src-tauri/src/utils/tool_factory.rs @@ -1,7 +1,7 @@ use crate::types::DeviceConfig; use crate::utils::stub_ops::prepare_stub_path; use sftool_lib::{ - create_sifli_tool, progress::ProgressCallbackArc, BeforeOperation, ChipType, SifliTool, + create_sifli_tool, progress::ProgressSinkArc, BeforeOperation, ChipType, SifliTool, SifliToolBase, }; @@ -34,7 +34,7 @@ pub fn create_tool_instance(config: &DeviceConfig) -> Result, /// 创建带进度回调的 SifliTool 实例 pub fn create_tool_instance_with_progress( config: &DeviceConfig, - progress_callback: ProgressCallbackArc, + progress_callback: ProgressSinkArc, ) -> Result, String> { // 解析芯片类型 let chip_type = match config.chip_type.to_uppercase().as_str() { diff --git a/src/types/progress.ts b/src/types/progress.ts index 183aaaa..7cd17f0 100644 --- a/src/types/progress.ts +++ b/src/types/progress.ts @@ -18,9 +18,42 @@ export enum OperationType { ERASE = 'erase', DOWNLOAD = 'download', VERIFY = 'verify', + READ = 'read', + CONNECT = 'connect', + DOWNLOAD_STUB = 'download_stub', + CHECK = 'check', UNKNOWN = 'unknown', } +export type ProgressType = { kind: 'spinner' } | { kind: 'bar'; total: number }; + +export type StubStage = 'start' | 'signature_key' | 'ram_stub'; + +export type EraseFlashStyle = 'complete' | 'addressed'; + +export type EraseRegionStyle = 'legacy_flash_start_decimal_length' | 'hex_length' | 'range'; + +export type ProgressOperation = + | { kind: 'connect' } + | { kind: 'download_stub'; stage: StubStage } + | { kind: 'erase_flash'; address: number; style: EraseFlashStyle } + | { kind: 'erase_region'; address: number; len: number; style: EraseRegionStyle } + | { kind: 'erase_all_regions' } + | { kind: 'verify'; address: number; len: number } + | { kind: 'check_redownload'; address: number; size: number } + | { kind: 'write_flash'; address: number; size: number } + | { kind: 'read_flash'; address: number; size: number } + | { kind: 'unknown' }; + +export type ProgressFinishStatus = + | { kind: 'success' } + | { kind: 'retry' } + | { kind: 'skipped' } + | { kind: 'required' } + | { kind: 'not_found' } + | { kind: 'failed'; message: string } + | { kind: 'aborted' }; + // 进度项状态枚举 export enum ProgressStatus { WAITING = 'waiting', @@ -63,10 +96,12 @@ export interface TotalProgress { export interface ProgressEvent { id: number; event_type: 'start' | 'update' | 'increment' | 'finish'; - step: string; - message: string; + step: number; + progress_type: ProgressType; + operation: ProgressOperation; current?: number; total?: number; + status?: ProgressFinishStatus; } // 消息解析结果接口 diff --git a/src/utils/messageParser.ts b/src/utils/messageParser.ts index 4f7dde4..b270fe8 100644 --- a/src/utils/messageParser.ts +++ b/src/utils/messageParser.ts @@ -1,4 +1,4 @@ -import type { FlashFile } from '../types/progress'; +import type { FlashFile, ProgressOperation } from '../types/progress'; import { OperationType, type MessageParseResult } from '../types/progress'; /** @@ -8,13 +8,36 @@ export class MessageParser { /** * 解析进度消息,提取操作类型、地址和文件名信息 */ - static parseMessage(message: string, selectedFiles: FlashFile[]): MessageParseResult { + static parseMessage( + message: string | undefined, + selectedFiles: FlashFile[], + operation?: ProgressOperation + ): MessageParseResult { const result: MessageParseResult = { operationType: OperationType.UNKNOWN, address: null, fileName: null, }; + if (operation) { + const address = this.getOperationAddress(operation); + result.operationType = this.getOperationType(operation); + result.address = address; + + if (address !== null) { + const matchingFile = this.findFileByAddress(selectedFiles, address); + if (matchingFile) { + result.fileName = matchingFile.name; + } + } + + return result; + } + + if (!message) { + return result; + } + // 检查是否是擦除操作 if (message.includes('Erasing') || message.includes('Erase')) { result.operationType = OperationType.ERASE; @@ -47,6 +70,44 @@ export class MessageParser { return result; } + private static getOperationType(operation: ProgressOperation): OperationType { + switch (operation.kind) { + case 'write_flash': + return OperationType.DOWNLOAD; + case 'verify': + return OperationType.VERIFY; + case 'erase_flash': + case 'erase_region': + case 'erase_all_regions': + return OperationType.ERASE; + case 'read_flash': + return OperationType.READ; + case 'connect': + return OperationType.CONNECT; + case 'download_stub': + return OperationType.DOWNLOAD_STUB; + case 'check_redownload': + return OperationType.CHECK; + case 'unknown': + default: + return OperationType.UNKNOWN; + } + } + + private static getOperationAddress(operation: ProgressOperation): number | null { + switch (operation.kind) { + case 'write_flash': + case 'verify': + case 'erase_flash': + case 'erase_region': + case 'check_redownload': + case 'read_flash': + return operation.address; + default: + return null; + } + } + /** * 根据地址查找对应的文件 */ @@ -81,6 +142,14 @@ export class MessageParser { return '下载'; case OperationType.VERIFY: return '验证'; + case OperationType.READ: + return '读取'; + case OperationType.CONNECT: + return '连接'; + case OperationType.DOWNLOAD_STUB: + return '下载 Stub'; + case OperationType.CHECK: + return '检查'; case OperationType.UNKNOWN: default: return '操作'; diff --git a/src/utils/progressHandler.ts b/src/utils/progressHandler.ts index 70a1a42..70708d7 100644 --- a/src/utils/progressHandler.ts +++ b/src/utils/progressHandler.ts @@ -1,6 +1,14 @@ import { useLogStore } from '../stores/logStore'; import { MessageParser } from './messageParser'; -import { OperationType, ProgressStatus, type ProgressEvent, type ProgressItem } from '../types/progress'; +import { + OperationType, + ProgressStatus, + type ProgressEvent, + type ProgressFinishStatus, + type ProgressItem, + type ProgressOperation, + type StubStage, +} from '../types/progress'; // 导入 writeFlashStore 类型 import type { useWriteFlashStore } from '../stores/writeFlashStore'; @@ -41,14 +49,14 @@ export class ProgressHandler { * 处理开始事件 */ private handleStartEvent(event: ProgressEvent): void { - const { id, step, message, current, total } = event; + const { id, step, current, total, operation } = event; const now = Date.now(); - const parsed = MessageParser.parseMessage(message, this.store.selectedFiles); + const parsed = MessageParser.parseMessage(undefined, this.store.selectedFiles, operation); let fileName = parsed.fileName; - // 如果没有解析出文件名,使用默认逻辑 - if (!fileName && this.store.selectedFiles.length > 0) { + // 如果无法映射文件名,仅在单文件场景下回退到第一个文件 + if (!fileName && parsed.operationType === OperationType.DOWNLOAD && this.store.selectedFiles.length === 1) { fileName = this.store.selectedFiles[0].name; } @@ -61,13 +69,15 @@ export class ProgressHandler { } // 创建进度项 + const message = this.formatOperationMessage(operation); + const fallbackFileName = this.formatFallbackFileName(parsed, id); this.createProgressItem(id, { - step, + step: step.toString(), message, current, total, startTime: now, - fileName: fileName || `操作 ${id}`, + fileName: fileName || fallbackFileName, address: parsed.address || 0, operationType: parsed.operationType, }); @@ -75,7 +85,7 @@ export class ProgressHandler { // 记录日志 const logFileName = parsed.operationType === OperationType.DOWNLOAD - ? fileName || '文件' + ? fileName || fallbackFileName : MessageParser.getOperationName(parsed.operationType); this.logStore.addMessage(`[${logFileName}] ${message}`); } @@ -84,9 +94,21 @@ export class ProgressHandler { * 处理更新事件 */ private handleUpdateEvent(event: ProgressEvent): void { - const { id, message } = event; + const { id, operation } = event; const existing = this.store.progressMap.get(id); if (existing) { + const parsed = MessageParser.parseMessage(undefined, this.store.selectedFiles, operation); + if (parsed.operationType) { + existing.operationType = parsed.operationType; + } + if (parsed.address !== null) { + existing.address = parsed.address; + } + if (parsed.fileName) { + existing.fileName = parsed.fileName; + } + + const message = this.formatOperationMessage(operation); existing.message = message; this.logStore.addMessage(`[${existing.fileName}] ${message}`); } @@ -128,14 +150,26 @@ export class ProgressHandler { * 处理完成事件 */ private handleFinishEvent(event: ProgressEvent): void { - const { id, message } = event; + const { id, status, operation } = event; const finishedItem = this.store.progressMap.get(id); if (!finishedItem) return; + if (operation) { + const parsed = MessageParser.parseMessage(undefined, this.store.selectedFiles, operation); + finishedItem.operationType = parsed.operationType; + if (parsed.address !== null) { + finishedItem.address = parsed.address; + } + if (parsed.fileName) { + finishedItem.fileName = parsed.fileName; + } + } + finishedItem.status = ProgressStatus.COMPLETED; finishedItem.percentage = 100; + const message = this.formatFinishMessage(status); if (finishedItem.operationType === OperationType.DOWNLOAD) { this.handleDownloadComplete(finishedItem, message); } else { @@ -281,6 +315,83 @@ export class ProgressHandler { } // 工具方法 + private formatOperationMessage(operation: ProgressOperation | undefined): string { + if (!operation) return '操作'; + + const address = 'address' in operation ? this.formatAddress(operation.address) : ''; + const size = 'size' in operation ? this.formatBytes(operation.size) : ''; + + switch (operation.kind) { + case 'connect': + return '连接设备'; + case 'download_stub': + return `下载 Stub (${this.formatStubStage(operation.stage)})`; + case 'erase_flash': + return `擦除 Flash @ ${address}`; + case 'erase_region': + return `擦除区域 @ ${address} (${this.formatBytes(operation.len)})`; + case 'erase_all_regions': + return '擦除所有区域'; + case 'verify': + return `验证 @ ${address} (${this.formatBytes(operation.len)})`; + case 'check_redownload': + return `检查重下载 @ ${address} (${size})`; + case 'write_flash': + return `写入 @ ${address} (${size})`; + case 'read_flash': + return `读取 @ ${address} (${size})`; + case 'unknown': + default: + return '操作'; + } + } + + private formatFinishMessage(status?: ProgressFinishStatus): string { + if (!status) return '完成'; + switch (status.kind) { + case 'success': + return '完成'; + case 'retry': + return '需要重试'; + case 'skipped': + return '已跳过'; + case 'required': + return '需要重写'; + case 'not_found': + return '未找到'; + case 'failed': + return `失败: ${status.message}`; + case 'aborted': + return '已中止'; + default: + return '完成'; + } + } + + private formatStubStage(stage: StubStage): string { + switch (stage) { + case 'start': + return '开始'; + case 'signature_key': + return '签名'; + case 'ram_stub': + return 'RAM'; + default: + return '阶段'; + } + } + + private formatAddress(address: number): string { + return `0x${address.toString(16).toUpperCase().padStart(8, '0')}`; + } + + private formatFallbackFileName(parsed: { address: number | null }, id: number): string { + if (parsed.address !== null) { + return this.formatAddress(parsed.address); + } + return `操作 ${id}`; + } + private formatBytes(bytes: number | undefined): string { if (!bytes || bytes === 0) return '0 B'; const k = 1024; diff --git a/src/views/ReadFlashView.vue b/src/views/ReadFlashView.vue index 3545adf..9c3ba0b 100644 --- a/src/views/ReadFlashView.vue +++ b/src/views/ReadFlashView.vue @@ -238,6 +238,8 @@ import { useI18n } from 'vue-i18n'; import { listen } from '@tauri-apps/api/event'; import { useLogStore } from '../stores/logStore'; import { useReadFlashStore } from '../stores/readFlashStore'; +import { MessageParser } from '../utils/messageParser'; +import { OperationType, type ProgressEvent } from '../types/progress'; import ReadTaskCard from '../components/ReadTaskCard.vue'; const { t } = useI18n(); @@ -328,6 +330,10 @@ let lastProgressBytes = 0; let currentProgressBytes = 0; // 累计的进度字节数 let totalProgressBytes = 0; // 总字节数 +const formatAddress = (address: number): string => { + return `0x${address.toString(16).toUpperCase().padStart(8, '0')}`; +}; + // 初始化日志 const initializeLog = () => { logStore.initializeLog(); @@ -373,47 +379,41 @@ onUnmounted(() => { }); // 处理进度事件 -const handleProgressEvent = (event: any) => { +const handleProgressEvent = (event: ProgressEvent) => { // 只在读取进行中时处理进度事件,避免捕获设备连接等其他操作的进度 if (!readFlashStore.isReading) { return; } + if (event.operation?.kind !== 'read_flash') { + return; + } + const now = Date.now(); + const { address, size } = event.operation; if (event.event_type === 'start') { - readFlashStore.setCurrentOperation(event.step); + readFlashStore.setCurrentOperation(MessageParser.getOperationName(OperationType.READ)); lastProgressUpdate = now; lastProgressBytes = 0; currentProgressBytes = event.current || 0; - totalProgressBytes = event.total || 0; - - // 尝试从消息中解析地址以匹配任务 - // 消息格式通常为 "Reading from 0x12000000..." - if (event.message) { - const addressMatch = event.message.match(/0x[0-9a-fA-F]+/); - let fileName = event.message.split('/').pop() || event.message; - - if (addressMatch) { - const addressHex = addressMatch[0]; - const addressVal = parseInt(addressHex, 16); - const task = readFlashStore.tasks.find(t => parseInt(t.address, 16) === addressVal); - - if (task) { - readFlashStore.setCurrentReadingTaskId(task.id); - readFlashStore.setCurrentReadingFile(task.filePath); - fileName = task.filePath.split(/[/\\]/).pop() || task.filePath; - } - } + totalProgressBytes = event.total || size || 0; - readFlashStore.updateProgress({ - currentFileName: fileName, - totalCount: readFlashStore.tasks.length, - current: currentProgressBytes, - total: totalProgressBytes, - percentage: 0, - }); + const task = readFlashStore.tasks.find(t => parseInt(t.address, 16) === address); + const fileName = task ? task.filePath.split(/[/\\]/).pop() || task.filePath : formatAddress(address); + + if (task) { + readFlashStore.setCurrentReadingTaskId(task.id); + readFlashStore.setCurrentReadingFile(task.filePath); } + + readFlashStore.updateProgress({ + currentFileName: fileName, + totalCount: readFlashStore.tasks.length, + current: currentProgressBytes, + total: totalProgressBytes, + percentage: 0, + }); } else if (event.event_type === 'increment') { // increment 事件的 current 是增量 (delta),不是绝对值 const delta = event.current || 0;