import 'dart:async'; import 'dart:io'; import 'package:logging/logging.dart'; // import 'package:ini/ini.dart'; import 'mining_manager.dart'; import 'mining_task_info.dart'; import '../utils/ini_utils.dart'; import '../utils/path_utils.dart'; /// 持续挖矿管理器 class SustainMiner { static final SustainMiner _instance = SustainMiner._internal(); factory SustainMiner() => _instance; SustainMiner._internal(); final Logger _logger = Logger('SustainMiner'); final MiningManager _miningManager = MiningManager(); SustainMiningConfig? _config; MiningConfig? _miningConfig; Timer? _monitorTimer; StreamSubscription? _processExitSubscription; bool _isRunning = false; bool _isPaused = false; /// 加载配置 Future loadConfig(MiningConfig miningConfig) async { try { _miningConfig = miningConfig; final configFile = File(PathUtils.binFile('mining.windows.conf')); if (!await configFile.exists()) { _logger.warning('配置文件不存在'); return false; } final content = await configFile.readAsString(); final config = parseIni(content); // ini 2.x: 使用 get(section, option) final enabledStr = (config.get('sustain', 'enabled') ?? '').toLowerCase(); final enabled = enabledStr == 'true'; if (!enabled) { _logger.info('持续挖矿未启用'); return false; } _config = SustainMiningConfig( enabled: enabled, algo: _trimQuotes(config.get('sustain', 'algo') ?? ''), coin: _trimQuotes(config.get('sustain', 'coin') ?? ''), miner: _trimQuotes(config.get('sustain', 'miner') ?? ''), poolUrl: _trimQuotes(config.get('sustain', 'pool_url') ?? ''), wallet: _trimQuotes(config.get('sustain', 'wallet') ?? ''), workerId: _trimQuotes(config.get('sustain', 'worker_id') ?? ''), poolUser: _trimQuotes(config.get('sustain', 'pool_user') ?? ''), walletMining: (config.get('sustain', 'wallet_mining') ?? '').toLowerCase() == 'true', ); // 验证配置 if (_config!.algo.isEmpty || _config!.coin.isEmpty || _config!.miner.isEmpty || _config!.poolUrl.isEmpty || _config!.wallet.isEmpty || _config!.workerId.isEmpty) { _logger.severe('持续挖矿配置不完整'); return false; } // 验证挖矿软件路径 final minerPath = _getMinerPath(_config!.miner); if (minerPath == null || minerPath.isEmpty) { _logger.severe('${_config!.miner} 路径未配置'); return false; } _logger.info('持续挖矿配置加载成功: 算法=${_config!.algo}, 币种=${_config!.coin}, 挖矿软件=${_config!.miner}'); return true; } catch (e) { _logger.severe('加载持续挖矿配置失败: $e'); return false; } } /// 启动持续挖矿 Future start() async { if (_config == null || !_config!.enabled) { return; } if (_isRunning) { _logger.info('持续挖矿已在运行中'); return; } if (_miningManager.isMining) { _logger.info('当前有挖矿任务,等待任务结束后启动持续挖矿'); return; } _isRunning = true; _isPaused = false; _logger.info('启动持续挖矿...'); await _startMining(); _startProcessMonitor(); } /// 停止持续挖矿 Future stop() async { _isRunning = false; _monitorTimer?.cancel(); _monitorTimer = null; _processExitSubscription?.cancel(); _processExitSubscription = null; if (_miningManager.isMining && _miningManager.currentTask == null) { // 只有持续挖矿任务在运行时才停止 await _miningManager.stopMining(); } _logger.info('持续挖矿已停止'); } /// 暂停持续挖矿 Future pause() async { if (!_isRunning || _isPaused) { return; } _logger.info('暂停持续挖矿(有新任务)...'); _isPaused = true; if (_miningManager.isMining && _miningManager.currentTask == null) { await _miningManager.stopMining(); } } /// 恢复持续挖矿 Future resume() async { if (!_isRunning || !_isPaused) { return; } if (_miningManager.isMining) { _logger.info('当前有挖矿任务,等待任务结束后恢复持续挖矿'); return; } _logger.info('恢复持续挖矿...'); _isPaused = false; await _startMining(); // 确保进程监控已启动 if (_processExitSubscription == null) { _startProcessMonitor(); } } /// 启动挖矿 Future _startMining() async { if (_config == null || _isPaused || _miningConfig == null) { return; } if (_miningManager.isMining) { return; } final task = MiningTaskInfo( coin: _config!.coin, algo: _config!.algo, pool: '', poolUrl: _config!.poolUrl, walletAddress: _config!.wallet, workerId: _config!.workerId, poolUser: _config!.poolUser, walletMining: _config!.walletMining, endTimestamp: DateTime.now().add(const Duration(days: 365)).millisecondsSinceEpoch ~/ 1000, // 持续挖矿设置很长的结束时间 miner: _config!.miner, ); await _miningManager.startMining(task, _miningConfig!); } /// 启动进程监控 void _startProcessMonitor() { _processExitSubscription?.cancel(); // 监听挖矿进程退出事件 _processExitSubscription = _miningManager.processExitStream.listen((_) { // 检查是否是持续挖矿进程退出 if (_isRunning && !_isPaused && !_miningManager.isMining && _miningManager.currentTask == null) { _logger.warning('持续挖矿进程意外退出,将在5秒后自动重启...'); // 延迟5秒后重启,避免频繁重启 Future.delayed(const Duration(seconds: 5), () { if (_isRunning && !_isPaused && !_miningManager.isMining) { _logger.info('自动重启持续挖矿...'); _startMining(); } }); } }); } String _trimQuotes(String value) { var v = value.trim(); if (v.length >= 2) { final first = v[0]; final last = v[v.length - 1]; if ((first == '"' && last == '"') || (first == "'" && last == "'")) { v = v.substring(1, v.length - 1); } } return v; } String? _getMinerPath(String miner) { if (_miningConfig == null) return null; switch (miner.toLowerCase()) { case 'lolminer': return _miningConfig!.lolMinerPath; case 'rigel': return _miningConfig!.rigelPath; case 'bzminer': return _miningConfig!.bzMinerPath; default: return null; } } bool get isRunning => _isRunning; bool get isPaused => _isPaused; } class SustainMiningConfig { final bool enabled; final String algo; final String coin; final String miner; final String poolUrl; final String wallet; final String workerId; final String poolUser; final bool walletMining; SustainMiningConfig({ required this.enabled, required this.algo, required this.coin, required this.miner, required this.poolUrl, required this.wallet, required this.workerId, required this.poolUser, required this.walletMining, }); }