Files
windows-application/lib/providers/client_provider.dart

432 lines
14 KiB
Dart
Raw Normal View History

2026-01-22 15:14:27 +08:00
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import '../core/client_core.dart';
import '../core/system_info.dart';
import '../core/mining_manager.dart';
import '../core/sustain_miner.dart';
import '../core/database.dart';
import '../core/mining_task_info.dart';
import '../services/config_service.dart';
import '../services/update_service.dart';
import '../models/client_status.dart';
/// 客户端状态管理 Provider
class ClientProvider with ChangeNotifier {
final Logger _logger = Logger('ClientProvider');
final ClientCore _clientCore = ClientCore();
final SystemInfoService _systemInfo = SystemInfoService();
final MiningManager _miningManager = MiningManager();
final SustainMiner _sustainMiner = SustainMiner();
final DatabaseService _database = DatabaseService();
final ConfigService _configService = ConfigService();
final UpdateService _updateService = UpdateService();
Timer? _refreshTimer;
ClientInfo? _clientInfo;
bool _isInitialized = false;
bool _isLoading = false;
MiningTaskInfo? _currentMiningTask;
// 服务器地址(从配置文件读取)
String? _serverUrl;
// 版本信息
String? _remoteVersion;
bool _hasUpdate = false;
ClientProvider() {
_initialize();
}
/// 初始化客户端
Future<void> _initialize() async {
_isLoading = true;
notifyListeners();
try {
// 初始化数据库
await _database.initialize();
// 加载系统信息(先加载简单的文件读取,延迟执行 Process 调用)
final version = await _systemInfo.getVersion();
final auth = await _systemInfo.getAuth();
// 检查权限
final hasPermission = await _systemInfo.checkAdminPermission();
if (!hasPermission) {
_logger.warning('未检测到管理员权限');
}
// 加载挖矿配置
final miningConfig = await _configService.parseMiningConfig();
// 从配置文件读取服务器地址
_serverUrl = miningConfig?.serverUrl ?? '18.183.240.108:2345';
// 检查版本更新(如果有配置 update_url
if (miningConfig?.updateUrl != null) {
_checkForUpdates(miningConfig!.updateUrl!, version);
}
// 获取机器码和GPU信息延迟执行避免启动时崩溃
// 只在启动时获取一次,如果失败就显示空列表
String machineCode = '正在获取...';
List<GPUInfo> gpus = [];
try {
// 延迟3秒后获取避免启动时崩溃
await Future.delayed(const Duration(seconds: 3));
_logger.info('开始获取系统信息...');
machineCode = await _systemInfo.getMachineCode();
gpus = await _systemInfo.getGPUInfo();
_logger.info('系统信息获取完成: MAC=$machineCode, GPUs=${gpus.length}');
} catch (e) {
_logger.warning('获取系统信息失败: $e');
// 获取失败时使用默认值
machineCode = '获取失败';
gpus = [];
}
// 准备GPU信息转换为服务器需要的格式
final gpusMap = <String, dynamic>{};
for (var gpu in gpus) {
gpusMap[gpu.index.toString()] = gpu.toJson();
}
// 获取支持的挖矿软件列表
final miningSofts = _getMiningSofts(miningConfig);
// 初始化客户端核心(先连接,但不发送身份认证)
final initialized = await _clientCore.initialize(
serverUrl: _serverUrl ?? '18.183.240.108:2345',
auth: auth,
machineCode: machineCode, // 此时可能还是"正在获取...",需要等待真实值
gpusInfo: gpusMap,
miningSofts: miningSofts,
);
if (!initialized) {
_logger.severe('客户端初始化失败');
// 即使初始化失败,也创建 ClientInfo 以便显示基本信息
_clientInfo = ClientInfo(
version: version,
auth: auth,
gpus: gpus,
machineCode: machineCode,
status: ClientStatus.offline,
miningInfo: null,
);
_isInitialized = true;
_isLoading = false;
notifyListeners();
return;
}
// 设置回调
_clientCore.onStatusChanged = _onStatusChanged;
_clientCore.onMiningTaskChanged = _onMiningTaskChanged;
// 等待机器码获取完成后,发送身份认证消息(使用 身份信息::硬盘身份码 格式)
if (machineCode != '正在获取...' && machineCode != '获取失败') {
// 更新客户端核心的机器码
_clientCore.updateMachineCode(machineCode, gpusMap, miningSofts);
// 发送身份认证消息
_clientCore.sendMachineCode();
}
// 恢复未完成的挖矿任务
final unfinishedTask = await _database.loadUnfinishedTask();
if (unfinishedTask != null) {
if (miningConfig != null) {
await _miningManager.startMining(unfinishedTask, miningConfig);
_currentMiningTask = unfinishedTask;
}
}
// 加载持续挖矿配置并启动(恢复任务之后)
if (miningConfig != null) {
final loaded = await _sustainMiner.loadConfig(miningConfig);
if (loaded && _currentMiningTask == null) {
await _sustainMiner.start();
}
}
// 更新客户端信息
_clientInfo = ClientInfo(
version: version,
auth: auth,
gpus: gpus.map((gpu) => GPUInfo(
index: gpu.index,
brand: gpu.brand,
model: gpu.model,
memory: gpu.memory,
)).toList(),
machineCode: machineCode,
2026-01-23 16:11:20 +08:00
status: _getInitialStatus(),
miningInfo: _getMiningInfo(),
2026-01-22 15:14:27 +08:00
);
_isInitialized = true;
_startAutoRefresh();
} catch (e, stackTrace) {
_logger.severe('初始化失败: $e', e, stackTrace);
// 即使初始化失败,也创建基本的 ClientInfo 以便显示错误信息
try {
final version = await _systemInfo.getVersion();
final auth = await _systemInfo.getAuth();
_clientInfo = ClientInfo(
version: version,
auth: auth,
gpus: [],
machineCode: '初始化失败',
status: ClientStatus.offline,
miningInfo: null,
);
} catch (e2) {
_logger.severe('创建基本 ClientInfo 也失败: $e2');
// 如果连基本信息都获取不到,至少创建一个空的信息对象
_clientInfo = ClientInfo(
version: '未知',
auth: '未知',
gpus: [],
machineCode: '错误',
status: ClientStatus.offline,
miningInfo: null,
);
}
} finally {
_isLoading = false;
notifyListeners();
}
}
/// 状态变化回调
void _onStatusChanged(ClientStatus status) {
if (_clientInfo != null) {
2026-01-23 16:11:20 +08:00
// 判断当前状态:优先显示挖矿状态
ClientStatus newStatus;
if (_miningManager.isMining) {
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
newStatus = _currentMiningTask != null
? ClientStatus.mining
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
} else {
newStatus = status;
}
2026-01-22 15:14:27 +08:00
_clientInfo = ClientInfo(
version: _clientInfo!.version,
auth: _clientInfo!.auth,
gpus: _clientInfo!.gpus,
machineCode: _clientInfo!.machineCode,
status: newStatus,
2026-01-23 16:11:20 +08:00
miningInfo: _getMiningInfo(),
2026-01-22 15:14:27 +08:00
);
notifyListeners();
}
}
/// 挖矿任务变化回调
void _onMiningTaskChanged(MiningTaskInfo? task) async {
_currentMiningTask = task;
if (task != null) {
// 暂停持续挖矿
await _sustainMiner.pause();
// 启动挖矿
final miningConfig = await _configService.parseMiningConfig();
if (miningConfig != null) {
await _miningManager.startMining(task, miningConfig);
await _database.insertMiningTask(task);
}
} else {
// 停止挖矿
await _miningManager.stopMining();
// 恢复持续挖矿
await _sustainMiner.resume();
}
2026-01-23 16:11:20 +08:00
// 更新状态
2026-01-22 15:14:27 +08:00
_onStatusChanged(_clientCore.isConnected ? ClientStatus.online : ClientStatus.offline);
}
/// 开始自动刷新
void _startAutoRefresh() {
_refreshTimer?.cancel();
_refreshTimer = Timer.periodic(const Duration(seconds: 5), (_) {
refresh();
});
}
/// 手动刷新不重新获取GPU信息只更新状态
Future<void> refresh() async {
if (!_isInitialized || _clientInfo == null) return;
try {
// 只更新状态不重新获取GPU信息GPU信息只在启动时获取一次
2026-01-23 16:11:20 +08:00
ClientStatus status;
if (_miningManager.isMining) {
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
status = _currentMiningTask != null
? ClientStatus.mining
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
} else {
status = _clientCore.isConnected ? ClientStatus.online : ClientStatus.offline;
}
2026-01-22 15:14:27 +08:00
_clientInfo = ClientInfo(
version: _clientInfo!.version,
auth: _clientInfo!.auth,
gpus: _clientInfo!.gpus, // 使用已有的GPU信息不重新获取
machineCode: _clientInfo!.machineCode,
status: status,
2026-01-23 16:11:20 +08:00
miningInfo: _getMiningInfo(),
2026-01-22 15:14:27 +08:00
);
notifyListeners();
} catch (e) {
_logger.warning('刷新失败: $e');
}
}
2026-01-23 16:11:20 +08:00
/// 获取初始状态
ClientStatus _getInitialStatus() {
if (_miningManager.isMining) {
return _currentMiningTask != null
? ClientStatus.mining
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
}
return _clientCore.isConnected ? ClientStatus.online : ClientStatus.offline;
}
/// 获取挖矿信息(支持租约挖矿和持续挖矿)
MiningInfo? _getMiningInfo() {
if (_currentMiningTask != null) {
// 租约挖矿任务
return MiningInfo(
coin: _currentMiningTask!.coin,
algo: _currentMiningTask!.algo,
pool: _currentMiningTask!.pool,
poolUrl: _currentMiningTask!.poolUrl,
walletAddress: _currentMiningTask!.walletAddress,
workerId: _currentMiningTask!.workerId,
pid: null,
miner: _currentMiningTask!.miner,
endTimestamp: _currentMiningTask!.endTimestamp,
);
} else if (_miningManager.isMining && _sustainMiner.isRunning) {
// 持续挖矿任务
final sustainTask = _miningManager.currentTask;
if (sustainTask != null) {
return MiningInfo(
coin: sustainTask.coin,
algo: sustainTask.algo,
pool: sustainTask.pool,
poolUrl: sustainTask.poolUrl,
walletAddress: sustainTask.walletAddress,
workerId: sustainTask.workerId,
pid: null,
miner: sustainTask.miner,
endTimestamp: sustainTask.endTimestamp,
);
}
}
return null;
}
2026-01-22 15:14:27 +08:00
/// 重启客户端
Future<void> restart() async {
_clientCore.stop();
await _miningManager.stopMining();
await _sustainMiner.stop();
_isInitialized = false;
await _initialize();
}
/// 获取支持的挖矿软件列表(辅助方法)
List<String> _getMiningSofts(MiningConfig? miningConfig) {
final miningSofts = <String>[];
if (miningConfig?.lolMinerPath != null && miningConfig!.lolMinerPath!.isNotEmpty) {
miningSofts.add('lolminer');
}
if (miningConfig?.rigelPath != null && miningConfig!.rigelPath!.isNotEmpty) {
miningSofts.add('rigel');
}
if (miningConfig?.bzMinerPath != null && miningConfig!.bzMinerPath!.isNotEmpty) {
miningSofts.add('bzminer');
}
return miningSofts;
}
/// 获取日志流
Stream<String> get logStream => _clientCore.logStream;
ClientInfo? get clientInfo => _clientInfo;
bool get isLoading => _isLoading;
bool get isInitialized => _isInitialized;
bool get hasUpdate => _hasUpdate;
String? get remoteVersion => _remoteVersion;
/// 检查版本更新
Future<void> _checkForUpdates(String updateUrl, String localVersion) async {
try {
final remoteInfo = await _updateService.checkVersion(updateUrl);
if (remoteInfo != null) {
_remoteVersion = remoteInfo.version;
_hasUpdate = _updateService.isNewerVersion(remoteInfo.version, localVersion);
if (_hasUpdate) {
_logger.info('发现新版本: $localVersion -> ${remoteInfo.version}');
}
notifyListeners();
}
} catch (e) {
_logger.warning('检查更新失败: $e');
}
}
/// 执行更新
Future<bool> performUpdate() async {
if (!_hasUpdate || _remoteVersion == null) {
return false;
}
try {
final miningConfig = await _configService.parseMiningConfig();
if (miningConfig?.updateUrl == null) {
_logger.warning('更新URL未配置');
return false;
}
// 获取下载URL
final remoteInfo = await _updateService.checkVersion(miningConfig!.updateUrl!);
if (remoteInfo == null || remoteInfo.downloadUrl.isEmpty) {
_logger.warning('无法获取下载URL');
return false;
}
// 下载并更新
final success = await _updateService.downloadAndUpdate(remoteInfo.downloadUrl);
if (success) {
_logger.info('更新下载完成,将在下次启动时应用');
}
return success;
} catch (e) {
_logger.severe('执行更新失败: $e');
return false;
}
}
@override
void dispose() {
_refreshTimer?.cancel();
_clientCore.stop();
_database.close();
super.dispose();
}
}