云算力平台windows桌面应用
This commit is contained in:
408
lib/providers/client_provider.dart
Normal file
408
lib/providers/client_provider.dart
Normal file
@@ -0,0 +1,408 @@
|
||||
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: ClientStatus.online,
|
||||
miningInfo: _currentMiningTask != null
|
||||
? MiningInfo(
|
||||
coin: _currentMiningTask!.coin,
|
||||
algo: _currentMiningTask!.algo,
|
||||
pool: _currentMiningTask!.pool,
|
||||
poolUrl: _currentMiningTask!.poolUrl,
|
||||
walletAddress: _currentMiningTask!.walletAddress,
|
||||
workerId: _currentMiningTask!.workerId,
|
||||
pid: null, // Dart 进程管理可能需要额外处理
|
||||
miner: _currentMiningTask!.miner,
|
||||
endTimestamp: _currentMiningTask!.endTimestamp,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
||||
_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) {
|
||||
final newStatus = status == ClientStatus.mining || _miningManager.isMining
|
||||
? ClientStatus.mining
|
||||
: status;
|
||||
|
||||
_clientInfo = ClientInfo(
|
||||
version: _clientInfo!.version,
|
||||
auth: _clientInfo!.auth,
|
||||
gpus: _clientInfo!.gpus,
|
||||
machineCode: _clientInfo!.machineCode,
|
||||
status: newStatus,
|
||||
miningInfo: _currentMiningTask != null
|
||||
? 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,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
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();
|
||||
}
|
||||
|
||||
_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信息只在启动时获取一次)
|
||||
final status = _miningManager.isMining
|
||||
? ClientStatus.mining
|
||||
: (_clientCore.isConnected ? ClientStatus.online : ClientStatus.offline);
|
||||
|
||||
_clientInfo = ClientInfo(
|
||||
version: _clientInfo!.version,
|
||||
auth: _clientInfo!.auth,
|
||||
gpus: _clientInfo!.gpus, // 使用已有的GPU信息,不重新获取
|
||||
machineCode: _clientInfo!.machineCode,
|
||||
status: status,
|
||||
miningInfo: _currentMiningTask != null
|
||||
? 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,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
_logger.warning('刷新失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 重启客户端
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user