<commit_before>﻿using MultiMiner.Xgminer.Parsers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace MultiMiner.Xgminer
{
    public class Miner
    {
        //events
        // delegate declaration 
        public delegate void LogLaunchHandler(object sender, LogLaunchArgs ea);
        public delegate void LaunchFailedHandler(object sender, LaunchFailedArgs ea);

        // event declaration 
        public event LogLaunchHandler LogLaunch;
        public event LaunchFailedHandler LaunchFailed;

        private readonly MinerConfiguration minerConfiguration;

        public Miner(MinerConfiguration minerConfig)
        {
            this.minerConfiguration = minerConfig;
        }

        //uses --ndevs, returns platform information
        public List<Device> EnumerateDevices()
        {
            string arguments = MinerParameter.EnumerateDevices;
            bool redirectOutput = true;

            Process minerProcess = StartMinerProcess(arguments, redirectOutput);

            List<string> output = new List<string>();

            while (!minerProcess.StandardOutput.EndOfStream)
            {
                string line = minerProcess.StandardOutput.ReadLine();
                output.Add(line);
            }

            List<Device> result = new List<Device>();
            EnumerateDevicesParser.ParseTextForDevices(output, result);

            return result;
        }

        //uses -d?, returns driver information
        public List<Device> DeviceList()
        {
            string arguments = MinerParameter.DeviceList;
            bool redirectOutput = true;

            if (minerConfiguration.MinerBackend == MinerBackend.Bfgminer)
                arguments = String.Format("{0} {1}", arguments, Bfgminer.MinerParameter.ScanSerialAll);

            Process minerProcess = StartMinerProcess(arguments, redirectOutput);

            List<string> output = new List<string>();

            while (!minerProcess.StandardOutput.EndOfStream)
            {
                string line = minerProcess.StandardOutput.ReadLine();
                output.Add(line);
            }

            List<Device> result = new List<Device>();
            DeviceListParser.ParseTextForDevices(output, result);

            return result;
        }

        public Process Launch(string reason = "")
        {
            bool redirectOutput = false;
            bool ensureProcessStarts = true;

            //otherwise cgminer output shows under *nix in the terminal
            //DONT do it for Windows though or cgminer will close after opening

            //commented out for now - seems to eventually cause issues under .nix too
            //the miner keeps mining but Accepted shares stop updating
            //if (OSVersionPlatform.GetGenericPlatform() == PlatformID.Unix)
            //    redirectOutput = true;

            string arguments = minerConfiguration.Arguments;

            if (minerConfiguration.MinerBackend == MinerBackend.Bfgminer)
                arguments = String.Format("{0} {1}", arguments, Bfgminer.MinerParameter.ScanSerialAll);

            foreach (MiningPool pool in minerConfiguration.Pools)
            {
                //trim Host to ensure proper formatting
                string argument = string.Format("-o {0}:{1} -u {2}", pool.Host.Trim(), pool.Port, pool.Username);

                //some pools do not require a password
                //but the miners require some password
                string password = "\"\"";
                if (!String.IsNullOrEmpty(pool.Password))
                    password = <PASSWORD>;

                argument = String.Format("{0} -p {1}", argument, password);

                arguments = string.Format("{0} {1}", arguments, argument);
            }

            foreach (int deviceIndex in minerConfiguration.DeviceIndexes)
                arguments = string.Format("{0} -d {1}", arguments, deviceIndex);

            if (minerConfiguration.Algorithm == CoinAlgorithm.Scrypt)
                arguments = arguments + " --scrypt";

            if (minerConfiguration.ApiListen)
                arguments = string.Format("{0} --api-listen --api-port {1} --api-allow W:127.0.0.1", arguments, minerConfiguration.ApiPort);

            Process process = StartMinerProcess(arguments, redirectOutput, ensureProcessStarts, reason);

            return process;
        }

        private Process StartMinerProcess(string arguments, bool redirectOutput, 
            bool ensureProcessStarts = false, string reason = "")
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            
            startInfo.FileName = minerConfiguration.ExecutablePath;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.CreateNoWindow = true;

            startInfo.Arguments = arguments;
            if (minerConfiguration.DisableGpu)
            {
                startInfo.Arguments = startInfo.Arguments + " --disable-gpu";

                if (minerConfiguration.MinerBackend == MinerBackend.Cgminer)
                {
                    //otherwise it still requires OpenCL.dll - not an issue with bfgminer
                    if (OSVersionPlatform.GetConcretePlatform() == PlatformID.Unix)
                        startInfo.FileName = startInfo.FileName + "-nogpu";
                    else
                        startInfo.FileName = minerConfiguration.ExecutablePath.Replace("cgminer.exe", "cgminer-nogpu.exe");
                }
            }

            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = redirectOutput;

            if (LogLaunch != null)
            {
                LogLaunchArgs args = new LogLaunchArgs();

                args.DateTime = DateTime.Now;
                args.ExecutablePath = startInfo.FileName;
                args.Arguments = startInfo.Arguments;
                args.Reason = reason;

                LogLaunch(this, args);
            }

            Process process = StartProcessAndCheckResponse(startInfo);
            
            if (ensureProcessStarts)
                //store the returned process
                process = EnsureProcessStarts(process, startInfo);

            if (!process.HasExited)
                process.PriorityClass = minerConfiguration.Priority;

            return process;
        }

        private Process StartProcessAndCheckResponse(ProcessStartInfo startInfo)
        {
            bool userWillWatchOutput = startInfo.RedirectStandardOutput;

            startInfo.RedirectStandardOutput = true;

            Process process = new Process();
            process.StartInfo = startInfo;

            if (!userWillWatchOutput)
                process.OutputDataReceived += HandleProcessOutput;

            if (process.Start() && !userWillWatchOutput)
                process.BeginOutputReadLine();

            return process;
        }

        private void HandleProcessOutput(object sender, DataReceivedEventArgs e)
        {
            if (String.IsNullOrEmpty(e.Data))
                return;

            if (e.Data.Contains("auth failed") && (LaunchFailed != null))
            {
                LaunchFailedArgs args = new LaunchFailedArgs();
                args.Reason = "Authentication failed for your pool. Verify your pool settings and try again.";
                LaunchFailed(this, args);
            }

            if (e.Data.Contains("detected new block"))
                ((Process)sender).CancelOutputRead();
        }

        private Process EnsureProcessStarts(Process process, ProcessStartInfo startInfo)
        {
            //any lower than this seems to have a decent chance of a USB ASIC miner process not
            //successfully stopping & restarting
            const int timeout = 3500;

            //newest cgminer, paired with USB ASIC's, likes to die on startup a few times saying the specified device
            //wasn't detected, happens when stopping/starting mining on USB ASIC's repeatedly
            Thread.Sleep(timeout);

            int retries = 0;
            const int maxRetries = 5;

            while (process.HasExited)
            {
                if (retries >= maxRetries)
                    throw new Exception(
                        string.Format("Miner keeps exiting after launching - retried {0} times. Exit code {1}.\n" +
                        "Executable: {2}\nArguments: {3}", 
                        retries, process.ExitCode, startInfo.FileName, startInfo.Arguments));

                //ensure the new process is stored and returned
                process = StartProcessAndCheckResponse(startInfo);
                Thread.Sleep(timeout);
                retries++;
            }

            return process;
        }
    }
}
<commit_msg>Show the errors returned by the miner if the process cannot be launched<commit_after>﻿using MultiMiner.Xgminer.Parsers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace MultiMiner.Xgminer
{
    public class Miner
    {
        //events
        // delegate declaration 
        public delegate void LogLaunchHandler(object sender, LogLaunchArgs ea);
        public delegate void LaunchFailedHandler(object sender, LaunchFailedArgs ea);

        // event declaration 
        public event LogLaunchHandler LogLaunch;
        public event LaunchFailedHandler LaunchFailed;

        private readonly MinerConfiguration minerConfiguration;

        public Miner(MinerConfiguration minerConfig)
        {
            this.minerConfiguration = minerConfig;
        }

        //uses --ndevs, returns platform information
        public List<Device> EnumerateDevices()
        {
            string arguments = MinerParameter.EnumerateDevices;
            bool redirectOutput = true;

            Process minerProcess = StartMinerProcess(arguments, redirectOutput);

            List<string> output = new List<string>();

            while (!minerProcess.StandardOutput.EndOfStream)
            {
                string line = minerProcess.StandardOutput.ReadLine();
                output.Add(line);
            }

            List<Device> result = new List<Device>();
            EnumerateDevicesParser.ParseTextForDevices(output, result);

            return result;
        }

        //uses -d?, returns driver information
        public List<Device> DeviceList()
        {
            string arguments = MinerParameter.DeviceList;
            bool redirectOutput = true;

            if (minerConfiguration.MinerBackend == MinerBackend.Bfgminer)
                arguments = String.Format("{0} {1}", arguments, Bfgminer.MinerParameter.ScanSerialAll);

            Process minerProcess = StartMinerProcess(arguments, redirectOutput);

            List<string> output = new List<string>();

            while (!minerProcess.StandardOutput.EndOfStream)
            {
                string line = minerProcess.StandardOutput.ReadLine();
                output.Add(line);
            }

            List<Device> result = new List<Device>();
            DeviceListParser.ParseTextForDevices(output, result);

            return result;
        }

        public Process Launch(string reason = "")
        {
            bool redirectOutput = false;
            bool ensureProcessStarts = true;

            //otherwise cgminer output shows under *nix in the terminal
            //DONT do it for Windows though or cgminer will close after opening

            //commented out for now - seems to eventually cause issues under .nix too
            //the miner keeps mining but Accepted shares stop updating
            //if (OSVersionPlatform.GetGenericPlatform() == PlatformID.Unix)
            //    redirectOutput = true;

            string arguments = minerConfiguration.Arguments;

            if (minerConfiguration.MinerBackend == MinerBackend.Bfgminer)
                arguments = String.Format("{0} {1}", arguments, Bfgminer.MinerParameter.ScanSerialAll);

            foreach (MiningPool pool in minerConfiguration.Pools)
            {
                //trim Host to ensure proper formatting
                string argument = string.Format("-o {0}:{1} -u {2}", pool.Host.Trim(), pool.Port, pool.Username);

                //some pools do not require a password
                //but the miners require some password
                string password = "\"\"";
                if (!String.IsNullOrEmpty(pool.Password))
                    password = pool.Password;

                argument = String.Format("{0} -p {1}", argument, password);

                arguments = string.Format("{0} {1}", arguments, argument);
            }

            foreach (int deviceIndex in minerConfiguration.DeviceIndexes)
                arguments = string.Format("{0} -d {1}", arguments, deviceIndex);

            if (minerConfiguration.Algorithm == CoinAlgorithm.Scrypt)
                arguments = arguments + " --scrypt";

            if (minerConfiguration.ApiListen)
                arguments = string.Format("{0} --api-listen --api-port {1} --api-allow W:127.0.0.1", arguments, minerConfiguration.ApiPort);

            Process process = StartMinerProcess(arguments, redirectOutput, ensureProcessStarts, reason);

            return process;
        }

        private Process StartMinerProcess(string arguments, bool redirectOutput, 
            bool ensureProcessStarts = false, string reason = "")
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            
            startInfo.FileName = minerConfiguration.ExecutablePath;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.CreateNoWindow = true;

            startInfo.Arguments = arguments;
            if (minerConfiguration.DisableGpu)
            {
                startInfo.Arguments = startInfo.Arguments + " --disable-gpu";

                if (minerConfiguration.MinerBackend == MinerBackend.Cgminer)
                {
                    //otherwise it still requires OpenCL.dll - not an issue with bfgminer
                    if (OSVersionPlatform.GetConcretePlatform() == PlatformID.Unix)
                        startInfo.FileName = startInfo.FileName + "-nogpu";
                    else
                        startInfo.FileName = minerConfiguration.ExecutablePath.Replace("cgminer.exe", "cgminer-nogpu.exe");
                }
            }

            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = redirectOutput;

            if (LogLaunch != null)
            {
                LogLaunchArgs args = new LogLaunchArgs();

                args.DateTime = DateTime.Now;
                args.ExecutablePath = startInfo.FileName;
                args.Arguments = startInfo.Arguments;
                args.Reason = reason;

                LogLaunch(this, args);
            }

            Process process = StartProcessAndCheckResponse(startInfo);
            
            if (ensureProcessStarts)
                //store the returned process
                process = EnsureProcessStarts(process, startInfo);

            if (!process.HasExited)
                process.PriorityClass = minerConfiguration.Priority;

            return process;
        }

        private Process StartProcessAndCheckResponse(ProcessStartInfo startInfo)
        {
            bool userWillWatchOutput = startInfo.RedirectStandardOutput;

            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;

            Process process = new Process();
            process.StartInfo = startInfo;

            if (!userWillWatchOutput)
                process.OutputDataReceived += HandleProcessOutput;

            if (process.Start() && !userWillWatchOutput)
                process.BeginOutputReadLine();

            return process;
        }

        private void HandleProcessOutput(object sender, DataReceivedEventArgs e)
        {
            if (String.IsNullOrEmpty(e.Data))
                return;

            if (e.Data.Contains("auth failed") && (LaunchFailed != null))
            {
                LaunchFailedArgs args = new LaunchFailedArgs();
                args.Reason = "Authentication failed for your pool. Verify your pool settings and try again.";
                LaunchFailed(this, args);
            }

            if (e.Data.Contains("detected new block"))
                ((Process)sender).CancelOutputRead();
        }

        private Process EnsureProcessStarts(Process process, ProcessStartInfo startInfo)
        {
            //any lower than this seems to have a decent chance of a USB ASIC miner process not
            //successfully stopping & restarting
            const int timeout = 3500;

            //newest cgminer, paired with USB ASIC's, likes to die on startup a few times saying the specified device
            //wasn't detected, happens when stopping/starting mining on USB ASIC's repeatedly
            Thread.Sleep(timeout);

            int retries = 0;
            const int maxRetries = 5;

            while (process.HasExited)
            {
                if (retries >= maxRetries)
                {
                    string errors = ReadErrorsFromProcess(process);

                    throw new Exception(
                        string.Format("Miner keeps exiting after launching - retried {0} times. Exit code {1}.\n" +
                        "Error: {4}\nExecutable: {2}\nArguments: {3}",
                        retries, process.ExitCode, startInfo.FileName, startInfo.Arguments, errors));
                }

                //ensure the new process is stored and returned
                process = StartProcessAndCheckResponse(startInfo);
                Thread.Sleep(timeout);
                retries++;
            }

            return process;
        }

        private static string ReadErrorsFromProcess(Process process)
        {
            string errors = String.Empty;
            List<string> output = new List<string>();
            while (!process.StandardError.EndOfStream)
            {
                string line = process.StandardError.ReadLine();
                output.Add(line);
            }

            if (output.Count > 0)
                errors = String.Join(Environment.NewLine, output.ToArray());

            return errors;
        }
    }
}

{'fraction_non_alphanumeric': 0.08163265306122448, 'fraction_numerical': 0.0034737299174989146, 'mean_word_length': 1.8341793570219966, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '634899', 'n_tokens_mistral': 4442, 'n_tokens_neox': 4198, 'n_words': 1134}