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

437 lines
14 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
status: _getInitialStatus(),
miningInfo: _getMiningInfo(),
);
_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) {
// 判断当前状态:优先显示挖矿状态
ClientStatus newStatus;
if (_miningManager.isMining) {
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
newStatus = _currentMiningTask != null
? ClientStatus.mining
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
} else {
newStatus = status;
}
_clientInfo = ClientInfo(
version: _clientInfo!.version,
auth: _clientInfo!.auth,
gpus: _clientInfo!.gpus,
machineCode: _clientInfo!.machineCode,
status: newStatus,
miningInfo: _getMiningInfo(),
);
notifyListeners();
}
}
/// 挖矿任务变化回调
void _onMiningTaskChanged(MiningTaskInfo? task) async {
final previousTask = _currentMiningTask;
_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();
// 挖矿任务完成后,从日志中删除该任务
if (previousTask != null) {
await _database.finishMiningTask(previousTask);
}
// 恢复持续挖矿
await _sustainMiner.resume();
}
// 更新状态
_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信息只在启动时获取一次
ClientStatus status;
if (_miningManager.isMining) {
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
status = _currentMiningTask != null
? ClientStatus.mining
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
} else {
status = _clientCore.isConnected ? ClientStatus.online : ClientStatus.offline;
}
_clientInfo = ClientInfo(
version: _clientInfo!.version,
auth: _clientInfo!.auth,
gpus: _clientInfo!.gpus, // 使用已有的GPU信息不重新获取
machineCode: _clientInfo!.machineCode,
status: status,
miningInfo: _getMiningInfo(),
);
notifyListeners();
} catch (e) {
_logger.warning('刷新失败: $e');
}
}
/// 获取初始状态
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;
}
/// 重启客户端
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();
}
}