- core/localization: 新增约 60 个翻译键(含参数化方法),中英双语覆盖 - shared/widgets: CommonDialog 默认参数国际化 - features/home: 完成页操作步骤指引、状态栏串口连接状态、程序列表状态标签 - features/programs: 表头状态列、表单验证提示、导入/模板操作反馈、删除确认(参数化) - features/program_detail: 步骤列表/表单标题、删除确认、速度档位显示(参数化) - features/device: run_state_provider 错误消息改为错误码 - features/settings: 升级页、密码面板、语言面板、U盘导入面板、串口配置面板全部替换 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
203 lines
5.3 KiB
Dart
203 lines
5.3 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../programs/models/program.dart';
|
|
import '../../programs/models/step.dart';
|
|
import '../../programs/services/program_service.dart';
|
|
import '../services/runner_interface.dart';
|
|
import 'serial_provider.dart';
|
|
|
|
/// 运行状态枚举
|
|
enum RunStatus {
|
|
idle, // 待机
|
|
running, // 运行中
|
|
paused, // 已暂停
|
|
completed, // 已完成
|
|
error, // 错误
|
|
}
|
|
|
|
/// 运行状态
|
|
class RunState {
|
|
final RunStatus status;
|
|
final Program? currentProgram;
|
|
final List<Step> steps;
|
|
final int currentStepIndex;
|
|
final int remainingSeconds;
|
|
final double progress;
|
|
final String? currentWell;
|
|
final String? errorMessage;
|
|
|
|
const RunState({
|
|
this.status = RunStatus.idle,
|
|
this.currentProgram,
|
|
this.steps = const [],
|
|
this.currentStepIndex = 0,
|
|
this.remainingSeconds = 0,
|
|
this.progress = 0,
|
|
this.currentWell,
|
|
this.errorMessage,
|
|
});
|
|
|
|
RunState copyWith({
|
|
RunStatus? status,
|
|
Program? currentProgram,
|
|
List<Step>? steps,
|
|
int? currentStepIndex,
|
|
int? remainingSeconds,
|
|
double? progress,
|
|
String? currentWell,
|
|
String? errorMessage,
|
|
bool clearProgram = false,
|
|
bool clearWell = false,
|
|
bool clearError = false,
|
|
}) {
|
|
return RunState(
|
|
status: status ?? this.status,
|
|
currentProgram:
|
|
clearProgram ? null : (currentProgram ?? this.currentProgram),
|
|
steps: steps ?? this.steps,
|
|
currentStepIndex: currentStepIndex ?? this.currentStepIndex,
|
|
remainingSeconds: remainingSeconds ?? this.remainingSeconds,
|
|
progress: progress ?? this.progress,
|
|
currentWell: clearWell ? null : (currentWell ?? this.currentWell),
|
|
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
|
|
);
|
|
}
|
|
|
|
/// 获取当前步骤
|
|
Step? get currentStep {
|
|
if (steps.isEmpty || currentStepIndex >= steps.length) return null;
|
|
return steps[currentStepIndex];
|
|
}
|
|
|
|
/// 格式化剩余时间 (HH:MM:SS)
|
|
String get formattedRemainingTime {
|
|
final hours = remainingSeconds ~/ 3600;
|
|
final minutes = (remainingSeconds % 3600) ~/ 60;
|
|
final seconds = remainingSeconds % 60;
|
|
return '${hours.toString().padLeft(2, '0')}:'
|
|
'${minutes.toString().padLeft(2, '0')}:'
|
|
'${seconds.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
/// 格式化进度百分比
|
|
String get formattedProgress {
|
|
return '${(progress * 100).toStringAsFixed(0)}%';
|
|
}
|
|
}
|
|
|
|
/// 运行状态 Notifier
|
|
class RunStateNotifier extends StateNotifier<RunState> {
|
|
final Runner _runner;
|
|
final ProgramService _programService;
|
|
|
|
RunStateNotifier(this._runner, this._programService) : super(const RunState());
|
|
|
|
/// 开始运行程序
|
|
Future<void> start(Program program) async {
|
|
if (state.status == RunStatus.running ||
|
|
state.status == RunStatus.paused) {
|
|
return;
|
|
}
|
|
|
|
final steps = await _programService.getStepsByProgramId(program.id!);
|
|
if (steps.isEmpty) {
|
|
state = state.copyWith(
|
|
status: RunStatus.error,
|
|
errorMessage: 'PROGRAM_STEPS_EMPTY',
|
|
);
|
|
return;
|
|
}
|
|
|
|
state = state.copyWith(
|
|
status: RunStatus.running,
|
|
currentProgram: program,
|
|
steps: steps,
|
|
currentStepIndex: 0,
|
|
progress: 0,
|
|
currentWell: steps.first.position,
|
|
clearError: true,
|
|
);
|
|
|
|
_runner.start(
|
|
program,
|
|
steps,
|
|
RunnerCallbacks(
|
|
onProgress: (stepIndex, remaining, progress, well) {
|
|
state = state.copyWith(
|
|
currentStepIndex: stepIndex,
|
|
remainingSeconds: remaining,
|
|
progress: progress,
|
|
currentWell: well,
|
|
);
|
|
},
|
|
onComplete: () {
|
|
state = state.copyWith(
|
|
status: RunStatus.completed,
|
|
progress: 1,
|
|
currentWell: steps.last.position,
|
|
);
|
|
},
|
|
onError: (msg) {
|
|
state = state.copyWith(
|
|
status: RunStatus.error,
|
|
errorMessage: msg,
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 暂停运行
|
|
void pause() {
|
|
if (state.status == RunStatus.running) {
|
|
_runner.pause();
|
|
state = state.copyWith(status: RunStatus.paused);
|
|
}
|
|
}
|
|
|
|
/// 继续运行
|
|
void resume() {
|
|
if (state.status == RunStatus.paused) {
|
|
_runner.resume();
|
|
state = state.copyWith(status: RunStatus.running);
|
|
}
|
|
}
|
|
|
|
/// 停止运行
|
|
void stop() {
|
|
if (state.status == RunStatus.running ||
|
|
state.status == RunStatus.paused) {
|
|
_runner.stop();
|
|
}
|
|
state = const RunState(status: RunStatus.idle);
|
|
}
|
|
|
|
/// 重置状态
|
|
void reset() => stop();
|
|
}
|
|
|
|
/// ProgramService Provider
|
|
final programServiceProvider = Provider<ProgramService>((ref) {
|
|
return ProgramService.instance;
|
|
});
|
|
|
|
/// 运行状态 Provider
|
|
final runStateProvider =
|
|
StateNotifierProvider<RunStateNotifier, RunState>((ref) {
|
|
final runner = ref.watch(runnerProvider);
|
|
final programService = ref.watch(programServiceProvider);
|
|
return RunStateNotifier(runner, programService);
|
|
});
|
|
|
|
/// 是否正在运行 Provider
|
|
final isRunningProvider = Provider<bool>((ref) {
|
|
final status = ref.watch(runStateProvider).status;
|
|
return status == RunStatus.running;
|
|
});
|
|
|
|
/// 是否已暂停 Provider
|
|
final isPausedProvider = Provider<bool>((ref) {
|
|
final status = ref.watch(runStateProvider).status;
|
|
return status == RunStatus.paused;
|
|
});
|