コード例 #1
0
ファイル: Program.cs プロジェクト: sunlight2728/ntminer
        static void Main(string[] args)
        {
            VirtualRoot.StartTimer();
            try {
                // 将服务器地址设为localhost从而使用内网ip访问免于验证用户名密码
                AssemblyInfo.OfficialServerHost = "localhost";
                NTMinerRegistry.SetAutoBoot("NTMiner.CalcConfigUpdater", true);
                VirtualRoot.On <Per10MinuteEvent>("每10分钟更新收益计算器", LogEnum.DevConsole,
                                                  action: message => {
                    UpdateAsync();
                });
                UpdateAsync();
                Write.UserInfo("输入exit并回车可以停止服务!");

                while (Console.ReadLine() != "exit")
                {
                }

                Write.UserOk($"服务停止成功: {DateTime.Now}.");
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }

            System.Threading.Thread.Sleep(1000);
        }
コード例 #2
0
        static void Main()
        {
            HomePath.SetHomeDirFullName(AppDomain.CurrentDomain.BaseDirectory);
            NTMinerConsole.DisbleQuickEditMode();
            try {
                VirtualRoot.StartTimer();
                // 将服务器地址设为localhost从而使用内网ip访问免于验证用户名密码
                RpcRoot.SetOfficialServerAddress(NTKeyword.Localhost);
                NTMinerRegistry.SetAutoBoot("NTMiner.CalcConfigUpdater", true);
                VirtualRoot.AddEventPath <Per10MinuteEvent>("每10分钟更新收益计算器", LogEnum.DevConsole,
                                                            action: message => {
                    UpdateAsync();
                }, location: typeof(Program));
                UpdateAsync();
                Write.UserInfo("输入exit并回车可以停止服务!");

                while (Console.ReadLine() != "exit")
                {
                }

                Write.UserOk($"服务停止成功: {DateTime.Now.ToString()}.");
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }

            System.Threading.Thread.Sleep(1000);
        }
コード例 #3
0
            private static void CreateLogfileProcess(IMineContext mineContext, string kernelExeFileFullName, string arguments)
            {
                string logFile = Path.Combine(SpecialPath.LogsDirFullName, $"{mineContext.Kernel.Code}_{DateTime.Now.Ticks.ToString()}.log");

                arguments = arguments.Replace("{logfile}", logFile);
                Write.UserOk($"\"{kernelExeFileFullName}\" {arguments}");
                Write.UserInfo("有请内核上场");
                ProcessStartInfo startInfo = new ProcessStartInfo(kernelExeFileFullName, arguments)
                {
                    UseShellExecute  = false,
                    CreateNoWindow   = true,
                    WorkingDirectory = AssemblyInfo.GlobalDirFullName
                };

                // 追加环境变量
                foreach (var item in mineContext.CoinKernel.EnvironmentVariables)
                {
                    startInfo.EnvironmentVariables.Add(item.Key, item.Value);
                }
                Process process = new Process {
                    StartInfo = startInfo
                };

                process.Start();
                ReadPrintLoopLogFileAsync(mineContext, logFile);
                Daemon(mineContext, null);
            }
コード例 #4
0
        static void Main()
        {
            using (var ws = new WebSocket("ws://localhost:8088/")) {
                ws.OnOpen += (sender, e) => {
                    Write.UserWarn($"WebSocket Open");
                    ws.Send("Hi!");
                };
                ws.OnMessage += (sender, e) => {
                    if (string.IsNullOrEmpty(e.Data) || e.Data[0] != '{' || e.Data[e.Data.Length - 1] != '}')
                    {
                        return;
                    }
                    WsMessage message = VirtualRoot.JsonSerializer.Deserialize <WsMessage>(e.Data);
                    if (message == null)
                    {
                        return;
                    }
                    switch (message.GetType())
                    {
                    case WsMessageType.GetSpeed:
                        ws.SendAsync(new WsMessage().SetType(WsMessageType.Speed)
                                     .SetData(new Dictionary <string, object> {
                            { "str", "hello" },
                            { "num", 111 },
                            { "date", DateTime.Now }
                        }).ToJson(), completed: null);
                        break;

                    default:
                        Write.UserInfo(e.Data);
                        break;
                    }
                };
                ws.OnError += (sender, e) => {
                    Write.UserError(e.Message);
                };
                ws.OnClose += (sender, e) => {
                    Write.UserWarn($"WebSocket Close {e.Code} {e.Reason}");
                };
                ws.Log.Level = LogLevel.Trace;
                ws.Connect();
                Windows.ConsoleHandler.Register(ws.Close);
                Console.WriteLine("\nType 'exit' to exit.\n");
                while (true)
                {
                    Console.Write("> ");
                    var action = Console.ReadLine();
                    if (action == "exit")
                    {
                        break;
                    }

                    if (!ws.IsAlive)
                    {
                        ws.Connect();
                    }
                }
            }
        }
コード例 #5
0
ファイル: VirtualRoot.cs プロジェクト: GangDang/ntminer
        public static void LocalMessage(LocalMessageChannel channel, string provider, LocalMessageType messageType, string content, OutEnum outEnum, bool toConsole)
        {
            switch (outEnum)
            {
            case OutEnum.None:
                break;

            case OutEnum.Info:
                Out.ShowInfo(content);
                break;

            case OutEnum.Warn:
                Out.ShowWarn(content, delaySeconds: 4);
                break;

            case OutEnum.Error:
                Out.ShowError(content, delaySeconds: 4);
                break;

            case OutEnum.Success:
                Out.ShowSuccess(content);
                break;

            default:
                break;
            }
            if (toConsole)
            {
                switch (messageType)
                {
                case LocalMessageType.Info:
                    Write.UserInfo(content);
                    break;

                case LocalMessageType.Warn:
                    Write.UserWarn(content);
                    break;

                case LocalMessageType.Error:
                    Write.UserError(content);
                    break;

                default:
                    break;
                }
            }
            Execute(new AddLocalMessageCommand(new LocalMessageData {
                Id          = Guid.NewGuid(),
                Channel     = channel.GetName(),
                Provider    = provider,
                MessageType = messageType.GetName(),
                Content     = content,
                Timestamp   = DateTime.Now
            }));
        }
コード例 #6
0
            private static void ContinueCreateProcess(IMineContext mineContext)
            {
                Thread.Sleep(1000);
                if (mineContext != Instance.LockedMineContext)
                {
                    Write.UserWarn("挖矿停止");
                    return;
                }
                // 解压内核包
                if (!mineContext.Kernel.ExtractPackage())
                {
                    VirtualRoot.RaiseEvent(new StartingMineFailedEvent("内核解压失败,请卸载内核重试。"));
                }
                else
                {
                    Write.UserOk("内核包解压成功");
                }

                // 执行文件书写器
                mineContext.ExecuteFileWriters();

                // 分离命令名和参数
                GetCmdNameAndArguments(mineContext, out string kernelExeFileFullName, out string arguments);
                // 这是不应该发生的,如果发生很可能是填写命令的时候拼写错误了
                if (!File.Exists(kernelExeFileFullName))
                {
                    Write.UserError(kernelExeFileFullName + "文件不存在,可能是被杀软删除导致,请退出杀毒软件重试或者QQ群联系小编,解释:大部分挖矿内核会报毒,不是开源矿工的问题也不是杀软的问题,也不是挖矿内核的问题,是挖矿这件事情的问题,可能是挖矿符合了病毒的定义。");
                }
                if (mineContext.KernelProcessType == KernelProcessType.Logfile)
                {
                    arguments = arguments.Replace(NTKeyword.LogFileParameterName, mineContext.LogFileFullName);
                }
                Write.UserOk($"\"{kernelExeFileFullName}\" {arguments}");
                Write.UserInfo($"有请内核上场:{mineContext.KernelProcessType}");
                if (mineContext != Instance.LockedMineContext)
                {
                    Write.UserWarn("挖矿停止");
                    return;
                }
                switch (mineContext.KernelProcessType)
                {
                case KernelProcessType.Logfile:
                    CreateLogfileProcess(mineContext, kernelExeFileFullName, arguments);
                    break;

                case KernelProcessType.Pip:
                    CreatePipProcess(mineContext, kernelExeFileFullName, arguments);
                    break;

                default:
                    throw new InvalidProgramException();
                }
                VirtualRoot.RaiseEvent(new MineStartedEvent(mineContext));
            }
コード例 #7
0
        static void Main()
        {
            using (var ws = new WebSocket("ws://localhost:8088/Echo")) {
                ws.OnOpen += (sender, e) => {
                    Write.UserWarn($"WebSocket Open");
                    ws.Send("Hi!");
                };
                ws.OnMessage += (sender, e) => {
                    Write.UserInfo(e.Data);
                };
                ws.OnError += (sender, e) => {
                    Write.UserError(e.Message);
                };
                ws.OnClose += (sender, e) => {
                    Write.UserWarn($"WebSocket Close {e.Code} {e.Reason}");
                };
                ws.Log.Level = LogLevel.Trace;
                ws.Connect();
                Windows.ConsoleHandler.Register(ws.Close);
                Console.WriteLine("\nType 'exit' to exit.\n");
                while (true)
                {
                    Console.Write("> ");
                    var msg = Console.ReadLine();
                    if (msg == "exit")
                    {
                        break;
                    }

                    if (!ws.IsAlive)
                    {
                        ws.Connect();
                    }
                    // Send a text message.
                    ws.Send(msg);
                }
            }
        }
コード例 #8
0
ファイル: NTMinerRoot.cs プロジェクト: yuanaichi/ntminer
 private void RefreshServerJsonFile()
 {
     OfficialServer.GetJsonFileVersionAsync(AssemblyInfo.ServerJsonFileName, (serverJsonFileVersion, minerClientVersion) => {
         AppVersionChangedEvent.PublishIfNewVersion(minerClientVersion);
         string localServerJsonFileVersion = GetServerJsonVersion();
         if (!string.IsNullOrEmpty(serverJsonFileVersion) && localServerJsonFileVersion != serverJsonFileVersion)
         {
             GetAliyunServerJson((data) => {
                 Write.UserInfo($"更新配置{localServerJsonFileVersion}->{serverJsonFileVersion}");
                 string rawJson = Encoding.UTF8.GetString(data);
                 SpecialPath.WriteServerJsonFile(rawJson);
                 SetServerJsonVersion(serverJsonFileVersion);
                 ReInitServerJson();
                 // 作业模式下界面是禁用的,所以这里的初始化isWork必然是false
                 ContextReInit(isWork: VirtualRoot.IsMinerStudio);
                 Write.UserInfo("更新成功");
             });
         }
         else
         {
             Write.DevDebug("server.json没有新版本");
         }
     });
 }
コード例 #9
0
            // 创建管道,将输出通过管道转送到日志文件,然后读取日志文件内容打印到控制台
            private static void CreatePipProcess(IMineContext mineContext, string kernelExeFileFullName, string arguments)
            {
                SECURITY_ATTRIBUTES saAttr = new SECURITY_ATTRIBUTES {
                    bInheritHandle       = true,
                    lpSecurityDescriptor = IntPtr.Zero,
                    length = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES))
                };

                //set the bInheritHandle flag so pipe handles are inherited

                saAttr.lpSecurityDescriptor = IntPtr.Zero;
                //get handle to current stdOut

                IntPtr mypointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(STARTUPINFO)));

                Marshal.StructureToPtr(saAttr, mypointer, true);
                var bret = CreatePipe(out var hReadOut, out var hWriteOut, mypointer, 0);

                //ensure the read handle to pipe for stdout is not inherited
                SetHandleInformation(hReadOut, HANDLE_FLAG_INHERIT, 0);
                ////Create pipe for the child process's STDIN
                STARTUPINFO lpStartupInfo = new STARTUPINFO {
                    cb          = (uint)Marshal.SizeOf(typeof(STARTUPINFO)),
                    dwFlags     = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW,
                    wShowWindow = SW_HIDE, // SW_HIDE; //SW_SHOW
                    hStdOutput  = hWriteOut,
                    hStdError   = hWriteOut,
                    hStdInput   = IntPtr.Zero
                };
                string cmdLine = $"\"{kernelExeFileFullName}\" {arguments}";

                Write.UserOk(cmdLine);
                Write.UserInfo("有请内核上场");
                StringBuilder lpEnvironment = new StringBuilder();
                // 复制父进程的环境变量
                IDictionary dic = Environment.GetEnvironmentVariables();

                // 追加环境变量
                foreach (var item in mineContext.CoinKernel.EnvironmentVariables)
                {
                    dic.Add(item.Key, item.Value);
                }
                foreach (var key in dic.Keys)
                {
                    if (key == null || key.ToString().Contains("\0"))
                    {
                        continue;
                    }
                    var value = dic[key];
                    if (value == null || value.ToString().Contains("\0"))
                    {
                        continue;
                    }
                    lpEnvironment.Append($"{key.ToString()}={value.ToString()}\0");
                }
                if (CreateProcess(
                        lpApplicationName: null,
                        lpCommandLine: new StringBuilder(cmdLine),
                        lpProcessAttributes: IntPtr.Zero,
                        lpThreadAttributes: IntPtr.Zero,
                        bInheritHandles: true,
                        dwCreationFlags: NORMAL_PRIORITY_CLASS,
                        lpEnvironment: lpEnvironment,
                        lpCurrentDirectory: Path.GetDirectoryName(kernelExeFileFullName),
                        lpStartupInfo: ref lpStartupInfo,
                        lpProcessInformation: out _))
                {
                    if (bret == false)
                    {
                        int lasterr = Marshal.GetLastWin32Error();
                        VirtualRoot.Happened(new StartingMineFailedEvent($"管道型进程创建失败 lasterr:{lasterr}"));
                    }
                    else
                    {
                        Bus.DelegateHandler <MineStopedEvent> closeHandle = null;
                        bool isHWriteOutHasClosed = false;
                        Daemon(mineContext, () => {
                            if (!isHWriteOutHasClosed)
                            {
                                CloseHandle(hWriteOut);
                                isHWriteOutHasClosed = true;
                            }
                            VirtualRoot.UnPath(closeHandle);
                        });
                        closeHandle = VirtualRoot.On <MineStopedEvent>("挖矿停止后关闭非托管的日志句柄", LogEnum.DevConsole,
                                                                       action: message => {
                            // 挖矿停止后摘除挖矿内核进程守护器
                            if (_sDaemon != null)
                            {
                                VirtualRoot.UnPath(_sDaemon);
                                _sDaemon = null;
                            }
                            if (!isHWriteOutHasClosed)
                            {
                                CloseHandle(hWriteOut);
                                isHWriteOutHasClosed = true;
                            }
                            VirtualRoot.UnPath(closeHandle);
                        });
                        string pipLogFileFullName = Path.Combine(SpecialPath.LogsDirFullName, mineContext.PipeFileName);
                        Task.Factory.StartNew(() => {
                            FileStream fs = new FileStream(pipLogFileFullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                            using (StreamReader sr = new StreamReader(fs)) {
                                byte[] buffer = new byte[1024];
                                int ret;
                                // Read会阻塞,直到读取到字符或者hWriteOut被关闭
                                while ((ret = Read(buffer, 0, buffer.Length, hReadOut)) > 0)
                                {
                                    fs.Write(buffer, 0, ret);
                                    if (buffer[ret - 1] == '\r' || buffer[ret - 1] == '\n')
                                    {
                                        fs.Flush();
                                    }
                                }
                            }
                            CloseHandle(hReadOut);
                        }, TaskCreationOptions.LongRunning);
                        ReadPrintLoopLogFileAsync(mineContext, pipLogFileFullName);
                    }
                }
                else
                {
                    VirtualRoot.Happened(new StartingMineFailedEvent($"内核启动失败,请重试"));
                }
            }
コード例 #10
0
 private static void ReadPrintLoopLogFileAsync(IMineContext mineContext, string logFile)
 {
     Task.Factory.StartNew(() => {
         bool isLogFileCreated = true;
         int n = 0;
         while (!File.Exists(logFile))
         {
             if (n >= 20)
             {
                 // 20秒钟都没有建立日志文件,不可能
                 isLogFileCreated = false;
                 Write.UserFail("呃!意外,竟然20秒钟未产生内核输出文件,请联系开发人员解决。");
                 break;
             }
             Thread.Sleep(1000);
             if (n == 0)
             {
                 Write.UserInfo("等待内核出场");
             }
             if (mineContext != Instance.CurrentMineContext)
             {
                 Write.UserInfo("挖矿上下文变更,结束内核输出等待。");
                 isLogFileCreated = false;
                 break;
             }
             n++;
         }
         if (isLogFileCreated)
         {
             Write.UserOk("内核已上场,下面把舞台交给内核。");
             StreamReader sreader = null;
             try {
                 sreader = new StreamReader(File.Open(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.Default);
                 while (mineContext == Instance.CurrentMineContext)
                 {
                     string outline = sreader.ReadLine();
                     if (string.IsNullOrEmpty(outline) && sreader.EndOfStream)
                     {
                         Thread.Sleep(1000);
                     }
                     else
                     {
                         string input        = outline;
                         Guid kernelOutputId = mineContext.Kernel.KernelOutputId;
                         Instance.KernelOutputFilterSet.Filter(kernelOutputId, ref input);
                         ConsoleColor color = ConsoleColor.White;
                         Instance.KernelOutputTranslaterSet.Translate(kernelOutputId, ref input, ref color, isPre: true);
                         // 使用Claymore挖其非ETH币种时它也打印ETH,所以这里需要纠正它
                         if ("Claymore".Equals(mineContext.Kernel.Code, StringComparison.OrdinalIgnoreCase))
                         {
                             if (mineContext.MainCoin.Code != "ETH" && input.Contains("ETH"))
                             {
                                 input = input.Replace("ETH", mineContext.MainCoin.Code);
                             }
                         }
                         Instance.KernelOutputSet.Pick(kernelOutputId, ref input, mineContext);
                         if (IsUiVisible)
                         {
                             Instance.KernelOutputTranslaterSet.Translate(kernelOutputId, ref input, ref color);
                         }
                         if (!string.IsNullOrEmpty(input))
                         {
                             if (Instance.KernelOutputSet.TryGetKernelOutput(kernelOutputId, out IKernelOutput kernelOutput))
                             {
                                 if (kernelOutput.PrependDateTime)
                                 {
                                     Write.UserLine($"{DateTime.Now}    {input}", color);
                                 }
                                 else
                                 {
                                     Write.UserLine(input, color);
                                 }
                             }
                             else
                             {
                                 Write.UserLine(input, color);
                             }
                         }
                     }
                 }
             }
             catch (Exception e) {
                 Logger.ErrorDebugLine(e);
             }
             finally {
                 sreader?.Close();
                 sreader?.Dispose();
             }
             Write.UserInfo("内核表演结束");
         }
     }, TaskCreationOptions.LongRunning);
 }
コード例 #11
0
            public static void CreateProcessAsync(IMineContext mineContext)
            {
                Task.Factory.StartNew(() => {
                    lock (_locker) {
                        try {
                            Write.UserInfo("清理内核进程");
#if DEBUG
                            VirtualRoot.Stopwatch.Restart();
#endif
                            // 清理除当前外的Temp/Kernel
                            Cleaner.Clear();
#if DEBUG
                            Write.DevWarn($"耗时{VirtualRoot.Stopwatch.ElapsedMilliseconds}毫秒 {nameof(MinerProcess)}.{nameof(CreateProcessAsync)}[{nameof(Cleaner)}.{nameof(Cleaner.Clear)}]");
#endif
                            Write.UserOk("内核进程清理完毕");
                            Thread.Sleep(1000);
                            Write.UserInfo($"解压内核包{mineContext.Kernel.Package}");
                            // 解压内核包
                            if (!mineContext.Kernel.ExtractPackage())
                            {
                                VirtualRoot.Happened(new StartingMineFailedEvent("内核解压失败,请卸载内核重试。"));
                            }
                            else
                            {
                                Write.UserOk("内核包解压成功");
                            }

                            // 执行文件书写器
                            mineContext.ExecuteFileWriters();

                            Write.UserInfo("总成命令");
                            // 组装命令
                            BuildCmdLine(mineContext, out string kernelExeFileFullName, out string arguments);
                            bool isLogFile = arguments.Contains("{logfile}");
                            // 这是不应该发生的,如果发生很可能是填写命令的时候拼写错误了
                            if (!File.Exists(kernelExeFileFullName))
                            {
                                Write.UserError(kernelExeFileFullName + "文件不存在,可能是小编拼写错误导致,请QQ群联系小编。");
                            }
                            if (isLogFile)
                            {
                                Logger.InfoDebugLine("创建日志文件型进程");
                                // 如果内核支持日志文件
                                // 推迟打印cmdLine,因为{logfile}变量尚未求值
                                CreateLogfileProcess(mineContext, kernelExeFileFullName, arguments);
                            }
                            else
                            {
                                Logger.InfoDebugLine("创建管道型进程");
                                // 如果内核不支持日志文件
                                CreatePipProcess(mineContext, kernelExeFileFullName, arguments);
                            }
                            VirtualRoot.Happened(new MineStartedEvent(mineContext));
                        }
                        catch (Exception e) {
                            Logger.ErrorDebugLine(e);
                            Write.UserFail("挖矿内核启动失败,请联系开发人员解决");
                        }
                    }
                });
            }
コード例 #12
0
        private static void ThisLocalMessage(string provider, LocalMessageType messageType, string content, OutEnum outEnum, bool toConsole)
        {
            switch (outEnum)
            {
            case OutEnum.None:
                break;

            case OutEnum.Info:
                Out.ShowInfo(content);
                break;

            case OutEnum.Warn:
                Out.ShowWarn(content, delaySeconds: 4);
                break;

            case OutEnum.Error:
                Out.ShowError(content, delaySeconds: 4);
                break;

            case OutEnum.Success:
                Out.ShowSuccess(content);
                break;

            case OutEnum.Auto:
                switch (messageType)
                {
                case LocalMessageType.Info:
                    Out.ShowInfo(content);
                    break;

                case LocalMessageType.Warn:
                    Out.ShowWarn(content, delaySeconds: 4);
                    break;

                case LocalMessageType.Error:
                    Out.ShowError(content, delaySeconds: 4);
                    break;

                default:
                    break;
                }
                break;

            default:
                break;
            }
            if (toConsole)
            {
                switch (messageType)
                {
                case LocalMessageType.Info:
                    Write.UserInfo(content);
                    break;

                case LocalMessageType.Warn:
                    Write.UserWarn(content);
                    break;

                case LocalMessageType.Error:
                    Write.UserError(content);
                    break;

                default:
                    break;
                }
            }
            LocalMessages.Add(LocalMessageChannel.This.GetName(), provider, messageType.GetName(), content);
        }
コード例 #13
0
ファイル: Program.cs プロジェクト: sunlight2728/ntminer
        private static void UpdateAsync()
        {
            Task.Factory.StartNew(() => {
                try {
                    var vdsUUDataTask = GetHtmlAsync("https://uupool.cn/api/getAllInfo.php");
                    var vdsZtDataTask = GetHtmlAsync("https://www.zt.com/api/v1/symbol");
                    var htmlDataTask  = GetHtmlAsync("https://www.f2pool.com/");
                    byte[] htmlData   = null;
                    byte[] vdsUUData  = null;
                    byte[] vdsZtData  = null;
                    try {
                        Task.WaitAll(new Task[] { vdsUUDataTask, vdsZtDataTask, htmlDataTask }, 30 * 1000);
                        htmlData  = htmlDataTask.Result;
                        vdsUUData = vdsUUDataTask.Result;
                        vdsZtData = vdsZtDataTask.Result;
                    }
                    catch {
                    }
                    if (htmlData != null && htmlData.Length != 0)
                    {
                        Write.UserOk($"{DateTime.Now} - 鱼池首页html获取成功");
                        string html      = Encoding.UTF8.GetString(htmlData);
                        string vdsUUHtml = string.Empty;
                        string vdsZtHtml = string.Empty;
                        if (vdsUUData != null && vdsUUData.Length != 0)
                        {
                            vdsUUHtml = Encoding.UTF8.GetString(vdsUUData);
                        }
                        if (vdsZtData != null && vdsZtData.Length != 0)
                        {
                            vdsZtHtml = Encoding.UTF8.GetString(vdsZtData);
                        }
                        double usdCny = PickUsdCny(html);
                        Write.UserInfo($"usdCny={usdCny}");
                        List <IncomeItem> incomeItems = PickIncomeItems(html);
                        IncomeItem vdsIncomeItem      = PickVDSIncomeItem(vdsUUHtml, vdsZtHtml, usdCny);
                        if (vdsIncomeItem != null && incomeItems.All(a => a.CoinCode != "VDS"))
                        {
                            incomeItems.Add(vdsIncomeItem);
                        }
                        Write.UserInfo($"鱼池首页有{incomeItems.Count}个币种");
                        FillCny(incomeItems, usdCny);
                        NeatenSpeedUnit(incomeItems);
                        if (incomeItems != null && incomeItems.Count != 0)
                        {
                            Login();
                            OfficialServer.CalcConfigService.GetCalcConfigsAsync(data => {
                                Write.UserInfo($"NTMiner有{data.Count}个币种");
                                HashSet <string> coinCodes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                                foreach (CalcConfigData calcConfigData in data)
                                {
                                    IncomeItem incomeItem = incomeItems.FirstOrDefault(a => string.Equals(a.CoinCode, calcConfigData.CoinCode, StringComparison.OrdinalIgnoreCase));
                                    if (incomeItem != null)
                                    {
                                        coinCodes.Add(calcConfigData.CoinCode);
                                        calcConfigData.Speed           = incomeItem.Speed;
                                        calcConfigData.SpeedUnit       = incomeItem.SpeedUnit;
                                        calcConfigData.IncomePerDay    = incomeItem.IncomeCoin;
                                        calcConfigData.IncomeUsdPerDay = incomeItem.IncomeUsd;
                                        calcConfigData.IncomeCnyPerDay = incomeItem.IncomeCny;
                                        calcConfigData.ModifiedOn      = DateTime.Now;
                                    }
                                }
                                OfficialServer.CalcConfigService.SaveCalcConfigsAsync(data, callback: (res, e) => {
                                    if (!res.IsSuccess())
                                    {
                                        Write.UserFail(res.ReadMessage(e));
                                    }
                                });
                                foreach (IncomeItem incomeItem in incomeItems)
                                {
                                    if (coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    Write.UserInfo(incomeItem.ToString());
                                }

                                foreach (var incomeItem in incomeItems)
                                {
                                    if (!coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    Write.UserOk(incomeItem.ToString());
                                }

                                Write.UserOk($"更新了{coinCodes.Count}个币种:{string.Join(",", coinCodes)}");
                                int unUpdatedCount = data.Count - coinCodes.Count;
                                Write.UserWarn($"{unUpdatedCount}个币种未更新{(unUpdatedCount == 0 ? string.Empty : ":" + string.Join(",", data.Select(a => a.CoinCode).Except(coinCodes)))}");
                            });
                        }
                    }
                }
                catch (Exception e) {
                    Logger.ErrorDebugLine(e);
                }
            });
        }
コード例 #14
0
 private static void ReadPrintLoopLogFileAsync(IMineContext mineContext, bool isWriteToConsole)
 {
     Task.Factory.StartNew(() => {
         bool isLogFileCreated = true;
         int n = 0;
         while (!File.Exists(mineContext.LogFileFullName))
         {
             if (n >= 20)
             {
                 // 20秒钟都没有建立日志文件,不可能
                 isLogFileCreated = false;
                 Write.UserFail("呃!意外,竟然20秒钟未产生内核输出。常见原因:1.挖矿内核被杀毒软件删除; 2.没有磁盘空间了; 3.反馈给开发人员");
                 break;
             }
             Thread.Sleep(1000);
             if (n == 0)
             {
                 Write.UserInfo("等待内核出场");
             }
             if (mineContext != Instance.CurrentMineContext)
             {
                 Write.UserWarn("结束内核输出等待。");
                 isLogFileCreated = false;
                 break;
             }
             n++;
         }
         if (isLogFileCreated)
         {
             StreamReader sreader = null;
             try {
                 sreader = new StreamReader(File.Open(mineContext.LogFileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.Default);
                 while (mineContext == Instance.CurrentMineContext)
                 {
                     string outline = sreader.ReadLine();
                     if (string.IsNullOrEmpty(outline) && sreader.EndOfStream)
                     {
                         Thread.Sleep(1000);
                     }
                     else
                     {
                         string input        = outline;
                         Guid kernelOutputId = Guid.Empty;
                         if (mineContext.KernelOutput != null)
                         {
                             kernelOutputId = mineContext.KernelOutput.GetId();
                         }
                         Instance.KernelOutputFilterSet.Filter(kernelOutputId, ref input);
                         ConsoleColor color = ConsoleColor.White;
                         // 前译
                         Instance.KernelOutputTranslaterSet.Translate(kernelOutputId, ref input, ref color, isPre: true);
                         // 使用Claymore挖其非ETH币种时它也打印ETH,所以这里需要纠正它
                         if ("Claymore".Equals(mineContext.Kernel.Code, StringComparison.OrdinalIgnoreCase))
                         {
                             if (mineContext.MainCoin.Code != "ETH" && input.Contains("ETH"))
                             {
                                 input = input.Replace("ETH", mineContext.MainCoin.Code);
                             }
                         }
                         Instance.KernelOutputSet.Pick(ref input, mineContext);
                         if (isWriteToConsole)
                         {
                             if (!string.IsNullOrEmpty(input))
                             {
                                 Write.UserLine(input, ConsoleColor.White);
                             }
                         }
                     }
                 }
             }
             catch (Exception e) {
                 Logger.ErrorDebugLine(e);
             }
             finally {
                 sreader?.Close();
                 sreader?.Dispose();
             }
             Write.UserWarn("内核表演结束");
         }
         if (_kernelProcess != null)
         {
             lock (_kernelProcessLocker) {
                 if (_kernelProcess != null)
                 {
                     _kernelProcess.Dispose();
                     _kernelProcess = null;
                 }
             }
         }
     }, TaskCreationOptions.LongRunning);
 }
コード例 #15
0
ファイル: WebApiRoot.cs プロジェクト: nextbtc/NtMiner
        static void Main()
        {
            NTMinerConsole.DisbleQuickEditMode();
            HomePath.SetHomeDirFullName(AppDomain.CurrentDomain.BaseDirectory);
            try {
                bool mutexCreated;
                try {
                    // 锁名称上带上本节点的端口号,从而允许一个服务器上运行多个WebApiServer节点,这在软升级服务端程序时有用。
                    // 升级WebApiServer程序的时候步骤是:
                    // 1,在另一个端口启动新版本的程序;
                    // 2,让Widnows将来自旧端口的所有tcp请求转发到新端口;
                    // 3,退出旧版本的程序并更新到新版本;
                    // 4,删除第2步添加的Windows的端口转发;
                    // 5,退出第1步运行的节点;
                    // TODO:实现软升级策略
                    _sMutexApp = new Mutex(true, $"NTMinerServicesMutex{ServerRoot.HostConfig.GetServerPort().ToString()}", out mutexCreated);
                }
                catch {
                    mutexCreated = false;
                }
                if (mutexCreated)
                {
                    try {
                        // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
                        string queue        = $"{ServerAppType.WebApiServer.GetName()}.{ServerRoot.HostConfig.ThisServerAddress}";
                        string durableQueue = queue + MqKeyword.DurableQueueEndsWith;
                        AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
                            new UserMqMessagePath(durableQueue),
                            new MinerClientMqMessagePath(queue)
                        };
                        _serverContext = ServerContext.Create(mqClientTypeName: ServerAppType.WebApiServer.GetName(), mqMessagePaths);
                        if (_serverContext == null)
                        {
                            Write.UserError("启动失败,无法继续,因为服务器上下文创建失败");
                            return;
                        }
                        Console.Title = $"{ServerAppType.WebApiServer.GetName()}_{ServerRoot.HostConfig.ThisServerAddress}";
                        _ossClient    = new OssClient(ServerRoot.HostConfig.OssEndpoint, ServerRoot.HostConfig.OssAccessKeyId, ServerRoot.HostConfig.OssAccessKeySecret);
                        var minerClientMqSender  = new MinerClientMqSender(_serverContext.Channel);
                        var userMqSender         = new UserMqSender(_serverContext.Channel);
                        var wsServerNodeMqSender = new WsServerNodeMqSender(_serverContext.Channel);

                        var minerRedis     = new MinerRedis(_serverContext.RedisConn);
                        var speedDataRedis = new SpeedDataRedis(_serverContext.RedisConn);
                        var userRedis      = new UserRedis(_serverContext.RedisConn);
                        var captchaRedis   = new CaptchaRedis(_serverContext.RedisConn);

                        WsServerNodeSet   = new WsServerNodeSet(wsServerNodeMqSender);
                        UserSet           = new UserSet(userRedis, userMqSender);
                        UserAppSettingSet = new UserAppSettingSet();
                        CaptchaSet        = new CaptchaSet(captchaRedis);
                        CalcConfigSet     = new CalcConfigSet();
                        NTMinerWalletSet  = new NTMinerWalletSet();
                        ClientDataSet clientDataSet = new ClientDataSet(minerRedis, speedDataRedis, minerClientMqSender);
                        ClientDataSet          = clientDataSet;
                        CoinSnapshotSet        = new CoinSnapshotSet(clientDataSet);
                        MineWorkSet            = new UserMineWorkSet();
                        MinerGroupSet          = new UserMinerGroupSet();
                        NTMinerFileSet         = new NTMinerFileSet();
                        OverClockDataSet       = new OverClockDataSet();
                        KernelOutputKeywordSet = new KernelOutputKeywordSet(SpecialPath.LocalDbFileFullName, isServer: true);
                        ServerMessageSet       = new ServerMessageSet(SpecialPath.LocalDbFileFullName, isServer: true);
                        UpdateServerMessageTimestamp();
                        if (VirtualRoot.LocalAppSettingSet.TryGetAppSetting(nameof(KernelOutputKeywordTimestamp), out IAppSetting appSetting) && appSetting.Value is DateTime value)
                        {
                            KernelOutputKeywordTimestamp = value;
                        }
                        else
                        {
                            KernelOutputKeywordTimestamp = Timestamp.UnixBaseTime;
                        }
                    }
                    catch (Exception e) {
                        Write.UserError(e.Message);
                        Write.UserError(e.StackTrace);
                        Write.UserInfo("按任意键退出");
                        Console.ReadKey();
                        return;
                    }
                    VirtualRoot.StartTimer();
                    NTMinerRegistry.SetAutoBoot("NTMinerServices", true);
                    Type thisType = typeof(WebApiRoot);
                    Run();
                }
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }
        }
コード例 #16
0
            public static void CreateProcessAsync(IMineContext mineContext)
            {
                Task.Factory.StartNew(() => {
                    lock (_locker) {
                        try {
                            Write.UserInfo("清理内核进程");
                            HashSet <string> allKernelProcessNames = Instance.KernelSet.GetAllKernelProcessNames();
#if DEBUG
                            VirtualRoot.Stopwatch.Restart();
#endif
                            foreach (var processName in allKernelProcessNames)
                            {
                                Windows.TaskKill.Kill(processName, waitForExit: true);
                            }
#if DEBUG
                            Write.DevWarn($"耗时{VirtualRoot.Stopwatch.ElapsedMilliseconds}毫秒 {nameof(MinerProcess)}.{nameof(CreateProcessAsync)}[{nameof(allKernelProcessNames)}]");
#endif
                            Write.UserOk("内核进程清理完毕");
                            Thread.Sleep(1000);
                            Write.UserInfo($"解压内核包{mineContext.Kernel.Package}");
                            // 解压内核包
                            if (!mineContext.Kernel.ExtractPackage())
                            {
                                Write.UserFail("内核解压失败");
                                VirtualRoot.Happened(new StartingMineFailedEvent("内核解压失败。"));
                            }
                            else
                            {
                                Write.UserOk("内核包解压成功");
                            }

                            Write.UserInfo("总成命令");
                            // 组装命令
                            BuildCmdLine(mineContext, out var kernelExeFileFullName, out var arguments);
                            bool isLogFile = arguments.Contains("{logfile}");
                            // 这是不应该发生的,如果发生很可能是填写命令的时候拼写错误了
                            if (!File.Exists(kernelExeFileFullName))
                            {
                                Write.UserError(kernelExeFileFullName + "文件不存在,可能是小编拼写错误导致,请QQ群联系小编。");
                            }
                            if (isLogFile)
                            {
                                Logger.InfoDebugLine("创建日志文件型进程");
                                // 如果内核支持日志文件
                                // 推迟打印cmdLine,因为{logfile}变量尚未求值
                                CreateLogfileProcess(mineContext, kernelExeFileFullName, arguments);
                            }
                            else
                            {
                                Logger.InfoDebugLine("创建管道型进程");
                                // 如果内核不支持日志文件
                                CreatePipProcess(mineContext, kernelExeFileFullName, arguments);
                            }
                            VirtualRoot.Happened(new MineStartedEvent(mineContext));
                        }
                        catch (Exception e) {
                            Logger.ErrorDebugLine(e);
                            Write.UserFail("挖矿内核启动失败,请联系开发人员解决");
                        }
                    }
                });
            }
コード例 #17
0
 public void ShowInfo(string message, string header = "信息", int autoHideSeconds = 4, bool toConsole = false)
 {
     Write.UserInfo(message);
 }
コード例 #18
0
 public void Init(Action callback)
 {
     Task.Factory.StartNew(() => {
         bool isWork = Environment.GetCommandLineArgs().Contains("--work", StringComparer.OrdinalIgnoreCase);
         if (isWork)
         {
             DoInit(isWork, callback);
             if (IsMinerClient)
             {
                 NTMinerRegistry.SetIsLastIsWork(true);
             }
             return;
         }
         else
         {
             if (IsMinerClient)
             {
                 NTMinerRegistry.SetIsLastIsWork(false);
             }
         }
         // 如果是Debug模式且不是群控客户端且不是作业则使用本地数据库初始化
         if (DevMode.IsDebugMode && !VirtualRoot.IsMinerStudio)
         {
             DoInit(isWork: false, callback: callback);
             return;
         }
         Logger.InfoDebugLine("开始下载server.json");
         SpecialPath.GetAliyunServerJson((data) => {
             // 如果server.json未下载成功则不覆写本地server.json
             if (data != null && data.Length != 0)
             {
                 Logger.InfoDebugLine("GetAliyunServerJson下载成功");
                 var serverJson = Encoding.UTF8.GetString(data);
                 if (!string.IsNullOrEmpty(serverJson))
                 {
                     SpecialPath.WriteServerJsonFile(serverJson);
                 }
                 OfficialServer.GetJsonFileVersionAsync(AssemblyInfo.ServerJsonFileName, (serverJsonFileVersion, minerClientVersion) => {
                     SetServerJsonVersion(serverJsonFileVersion);
                     AppVersionChangedEvent.PublishIfNewVersion(minerClientVersion);
                 });
             }
             else
             {
                 Logger.InfoDebugLine("GetAliyunServerJson下载失败");
             }
             DoInit(isWork, callback);
         });
         #region 发生了用户活动时检查serverJson是否有新版本
         VirtualRoot.On <UserActionEvent>("发生了用户活动时检查serverJson是否有新版本", LogEnum.DevConsole,
                                          action: message => {
             OfficialServer.GetJsonFileVersionAsync(AssemblyInfo.ServerJsonFileName, (serverJsonFileVersion, minerClientVersion) => {
                 AppVersionChangedEvent.PublishIfNewVersion(minerClientVersion);
                 string localServerJsonFileVersion = GetServerJsonVersion();
                 if (!string.IsNullOrEmpty(serverJsonFileVersion) && localServerJsonFileVersion != serverJsonFileVersion)
                 {
                     SpecialPath.GetAliyunServerJson((data) => {
                         Write.UserInfo($"server.json配置文件有新版本{localServerJsonFileVersion}->{serverJsonFileVersion}");
                         string rawJson = Encoding.UTF8.GetString(data);
                         SpecialPath.WriteServerJsonFile(rawJson);
                         SetServerJsonVersion(serverJsonFileVersion);
                         ReInitServerJson();
                         bool isUseJson = !DevMode.IsDebugMode || VirtualRoot.IsMinerStudio;
                         if (isUseJson)
                         {
                             // 作业模式下界面是禁用的,所以这里的初始化isWork必然是false
                             ContextReInit(isWork: VirtualRoot.IsMinerStudio);
                             Write.UserInfo("刷新完成");
                         }
                         else
                         {
                             Write.UserInfo("不是使用的server.json,无需刷新");
                         }
                     });
                 }
                 else
                 {
                     Write.DevDebug("server.json没有新版本");
                 }
             });
         });
         #endregion
     });
     // 因为这个操作大概需要200毫秒
     Task.Factory.StartNew(() => {
         NVIDIAGpuSet.NvmlInit();
     });
 }
コード例 #19
0
        private static void UpdateAsync()
        {
            Task.Factory.StartNew(() => {
                try {
                    Task <byte[]> htmlDataTask = GetHtmlAsync("https://www.f2pool.com/");
                    byte[] htmlData            = null;
                    try {
                        Task.WaitAll(new Task[] { htmlDataTask }, 60 * 1000);
                        htmlData = htmlDataTask.Result;
                    }
                    catch {
                    }
                    if (htmlData != null && htmlData.Length != 0)
                    {
                        Write.UserOk($"{DateTime.Now.ToString()} - 鱼池首页html获取成功");
                        string html   = Encoding.UTF8.GetString(htmlData);
                        double usdCny = PickUsdCny(html);
                        Write.UserInfo($"usdCny={usdCny.ToString()}");
                        List <IncomeItem> incomeItems = PickIncomeItems(html);
                        Write.UserInfo($"鱼池首页有{incomeItems.Count.ToString()}个币种");
                        FillCny(incomeItems, usdCny);
                        NeatenSpeedUnit(incomeItems);
                        if (incomeItems != null && incomeItems.Count != 0)
                        {
                            RpcRoot.SetRpcUser(new RpcUser(ServerRoot.HostConfig.RpcLoginName, HashUtil.Sha1(ServerRoot.HostConfig.RpcPassword)), isOuterNet: false);
                            RpcRoot.OfficialServer.CalcConfigService.GetCalcConfigsAsync(data => {
                                Write.UserInfo($"NTMiner有{data.Count.ToString()}个币种");
                                HashSet <string> coinCodes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                                foreach (CalcConfigData calcConfigData in data)
                                {
                                    IncomeItem incomeItem = incomeItems.FirstOrDefault(a => string.Equals(a.CoinCode, calcConfigData.CoinCode, StringComparison.OrdinalIgnoreCase));
                                    if (incomeItem != null)
                                    {
                                        coinCodes.Add(calcConfigData.CoinCode);
                                        calcConfigData.Speed           = incomeItem.Speed;
                                        calcConfigData.SpeedUnit       = incomeItem.SpeedUnit;
                                        calcConfigData.NetSpeed        = incomeItem.NetSpeed;
                                        calcConfigData.NetSpeedUnit    = incomeItem.NetSpeedUnit;
                                        calcConfigData.IncomePerDay    = incomeItem.IncomeCoin;
                                        calcConfigData.IncomeUsdPerDay = incomeItem.IncomeUsd;
                                        calcConfigData.IncomeCnyPerDay = incomeItem.IncomeCny;
                                        calcConfigData.ModifiedOn      = DateTime.Now;
                                        if (calcConfigData.ModifiedOn.AddMinutes(15) > calcConfigData.ModifiedOn.Date.AddDays(1))
                                        {
                                            calcConfigData.BaseNetSpeed     = calcConfigData.NetSpeed;
                                            calcConfigData.BaseNetSpeedUnit = calcConfigData.NetSpeedUnit;
                                        }
                                        else if (calcConfigData.BaseNetSpeed != 0)
                                        {
                                            if (calcConfigData.NetSpeedUnit == calcConfigData.BaseNetSpeedUnit)
                                            {
                                                calcConfigData.DayWave = (calcConfigData.NetSpeed - calcConfigData.BaseNetSpeed) / calcConfigData.BaseNetSpeed;
                                            }
                                            else
                                            {
                                                if (string.IsNullOrEmpty(calcConfigData.BaseNetSpeedUnit))
                                                {
                                                    calcConfigData.BaseNetSpeedUnit = calcConfigData.NetSpeedUnit;
                                                }
                                                var netSpeed           = calcConfigData.NetSpeed.FromUnitSpeed(calcConfigData.NetSpeedUnit);
                                                var baseNetSpeed       = calcConfigData.BaseNetSpeed.FromUnitSpeed(calcConfigData.BaseNetSpeedUnit);
                                                calcConfigData.DayWave = (netSpeed - baseNetSpeed) / baseNetSpeed;
                                            }
                                        }
                                    }
                                }
                                RpcRoot.OfficialServer.CalcConfigService.SaveCalcConfigsAsync(data, callback: (res, e) => {
                                    if (!res.IsSuccess())
                                    {
                                        VirtualRoot.Out.ShowError(res.ReadMessage(e), autoHideSeconds: 4);
                                    }
                                });
                                foreach (IncomeItem incomeItem in incomeItems)
                                {
                                    if (coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    Write.UserInfo(incomeItem.ToString());
                                }

                                foreach (var incomeItem in incomeItems)
                                {
                                    if (!coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    Write.UserOk(incomeItem.ToString());
                                }

                                Write.UserOk($"更新了{coinCodes.Count.ToString()}个币种:{string.Join(",", coinCodes)}");
                                int unUpdatedCount = data.Count - coinCodes.Count;
                                Write.UserWarn($"{unUpdatedCount.ToString()}个币种未更新{(unUpdatedCount == 0 ? string.Empty : ":" + string.Join(",", data.Select(a => a.CoinCode).Except(coinCodes)))}");
                            });
                        }
                    }
                }
                catch (Exception e) {
                    Logger.ErrorDebugLine(e);
                }
            });
        }
コード例 #20
0
 private static void ReadPrintLoopLogFileAsync(IMineContext mineContext, bool isWriteToConsole)
 {
     Task.Factory.StartNew(() => {
         bool isLogFileCreated = true;
         int n = 0;
         while (!File.Exists(mineContext.LogFileFullName))
         {
             if (n >= 20)
             {
                 // 20秒钟都没有建立日志文件,不可能
                 isLogFileCreated = false;
                 Write.UserFail("呃!意外,竟然20秒钟未产生内核输出。常见原因:1.挖矿内核被杀毒软件删除; 2.没有磁盘空间了; 3.反馈给开发人员");
                 break;
             }
             Thread.Sleep(1000);
             if (n == 0)
             {
                 Write.UserInfo("等待内核出场");
             }
             if (mineContext != Instance.LockedMineContext)
             {
                 Write.UserWarn("结束内核输出等待。");
                 isLogFileCreated = false;
                 break;
             }
             n++;
         }
         if (isLogFileCreated)
         {
             StreamReader sreader = null;
             try {
                 sreader = new StreamReader(File.Open(mineContext.LogFileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.Default);
                 while (mineContext == Instance.LockedMineContext)
                 {
                     string outline = sreader.ReadLine();
                     if (string.IsNullOrEmpty(outline) && sreader.EndOfStream)
                     {
                         Thread.Sleep(1000);
                     }
                     else
                     {
                         string input        = outline;
                         Guid kernelOutputId = Guid.Empty;
                         if (mineContext.KernelOutput != null)
                         {
                             kernelOutputId = mineContext.KernelOutput.GetId();
                         }
                         // 前译
                         Instance.ServerContext.KernelOutputTranslaterSet.Translate(kernelOutputId, ref input, isPre: true);
                         Instance.ServerContext.KernelOutputSet.Pick(ref input, mineContext);
                         var kernelOutputKeywords = Instance.KernelOutputKeywordSet.GetKeywords(mineContext.KernelOutput.GetId());
                         if (kernelOutputKeywords != null && kernelOutputKeywords.Count != 0)
                         {
                             foreach (var keyword in kernelOutputKeywords)
                             {
                                 if (input.Contains(keyword.Keyword))
                                 {
                                     if (keyword.MessageType.TryParse(out LocalMessageType messageType))
                                     {
                                         string content = input;
                                         if (!string.IsNullOrEmpty(keyword.Description))
                                         {
                                             content += $" 大意:{keyword.Description}";
                                         }
                                         VirtualRoot.LocalMessage(LocalMessageChannel.Kernel, nameof(MinerProcess), messageType, content, OutEnum.None, toConsole: false);
                                     }
                                 }
                             }
                         }
                         if (isWriteToConsole)
                         {
                             if (!string.IsNullOrEmpty(input))
                             {
                                 Write.UserLine(input, ConsoleColor.White);
                             }
                         }
                     }
                 }
             }
             catch (Exception e) {
                 Logger.ErrorDebugLine(e);
             }
             finally {
                 sreader?.Close();
                 sreader?.Dispose();
             }
             Write.UserWarn("内核表演结束");
         }
         if (_kernelProcess != null)
         {
             lock (_kernelProcessLocker) {
                 if (_kernelProcess != null)
                 {
                     _kernelProcess.Dispose();
                     _kernelProcess = null;
                 }
             }
         }
     }, TaskCreationOptions.LongRunning);
 }