static void DataReceivedFromMiner(string strData, CmdLineParams info) { //Process data received from the miner if (string.IsNullOrEmpty(strData)) { return; } //Try to colorize text received SortedDictionary <int, FindAndColorText> dicClrs = new SortedDictionary <int, FindAndColorText>(); foreach (FindAndColorText fac in gArrClrText) { for (int i = 0; ;) { int nFnd = strData.IndexOf(fac.strText, i, fac.bIgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture); if (nFnd == -1) { break; } dicClrs[nFnd] = fac; i = nFnd + fac.strText.Length; } } //Sort by index var sorted = dicClrs.ToList(); int j = 0; //And output on the console with color foreach (var kvp in sorted) { int nInd = kvp.Key; Console.Write(strData.Substring(j, nInd - j)); int nLn = kvp.Value.strText.Length; Console.ForegroundColor = kvp.Value.clr; Console.Write(strData.Substring(nInd, nLn)); Console.ForegroundColor = ConsoleColor.White; j = nInd + nLn; } if (j < strData.Length) { Console.Write(strData.Substring(j)); } Console.WriteLine(""); //Then analyze the data received from the miner AnalyzeDataReceivedFromMiner(strData, info); }
private static void threadWatchMiner(CmdLineParams cmd) { //Worker thread that watches the miner output //'cmd' = command line parameters Random rnd = new Random(); DateTime dtmUtcLastNotRunning = DateTime.MinValue; DateTime dtmUtcLastStatsShown = DateTime.UtcNow; for (;; System.Threading.Thread.Sleep(500)) { bool bMinerRunning = false; TimeSpan spnProcLifetime = new TimeSpan(); //Is miner running? Process procMiner = gWS.getMinerProcessClass(); if (procMiner != null && IsProcessRunning(procMiner)) { //Miner is running dtmUtcLastNotRunning = DateTime.MinValue; bMinerRunning = true; //How long ago did the miner process start running? DateTime dtmStarted = procMiner.StartTime; //How long ago was it? spnProcLifetime = DateTime.Now - dtmStarted; //Only if been running for 40 seconds //INFO: The miner will be passing its own initialization, so skip it .... double fSecRan = spnProcLifetime.TotalSeconds; if (fSecRan > 40.0) { int nPID = procMiner.Id; int nSleepDelaySec; double fSecSinceLastGH; double fSecKillAfter; //Get last time we had a good hash rate DateTime dtmUtc_LastGHR = gWS.getLastGoodHashRateTimeUTC(); if (dtmUtc_LastGHR != DateTime.MinValue) { TimeSpan spnSinceLGH = DateTime.UtcNow - dtmUtc_LastGHR; fSecSinceLastGH = spnSinceLGH.TotalSeconds; fSecKillAfter = 60.0 * 2; //When to restart miner - if no good hash rate after } else { //Never before fSecSinceLastGH = fSecRan; fSecKillAfter = 60.0 * 4; //When to restart miner - if no good hash rate after } //See if we need to kill the miner process if (fSecSinceLastGH > fSecKillAfter) { //Kill the miner try { gEventLog.logMessage(EventLogMsgType.ELM_TYP_Warning, "Will attempt to kill miner (PID=" + nPID + ") after no good hash rates for " + fSecKillAfter + " sec"); OutputConsoleError("Will attempt to kill miner (PID=" + nPID + ") after no good hash rates for " + fSecKillAfter + " sec"); procMiner.Kill(); //Reset counters gWS.setLastGoodHashRateTimeUTC(); gWS.setLastMinerExitTimeUTC(0xdead0001); nSleepDelaySec = rnd.Next(1000, 3000); } catch (Exception ex) { //Failed gEventLog.logMessage(EventLogMsgType.ELM_TYP_Critical, "Failed to kill miner (PID=" + nPID + ") after no good hash rates -- " + ex.ToString()); OutputConsoleError("ERROR: Kill miner process failed! Check the log for details..."); nSleepDelaySec = rnd.Next(3000, 10000); } //Wait a little Thread.Sleep(nSleepDelaySec); continue; } //Get last time we had an accepted hash double fSecSinceLastAcc; DateTime dtmUtc_LastAccept = gWS.getLastAccaptedTimeUTC(); if (dtmUtc_LastAccept != DateTime.MinValue) { TimeSpan spnSinceLAcc = DateTime.UtcNow - dtmUtc_LastAccept; fSecSinceLastAcc = spnSinceLAcc.TotalSeconds; fSecKillAfter = 60.0 * 30; //When to restart miner - if no good hash rate after } else { //Never before fSecSinceLastAcc = fSecRan; fSecKillAfter = 60.0 * 35; //When to restart miner - if no good hash rate after } //See if we need to kill the miner process if (fSecSinceLastAcc > fSecKillAfter) { //Kill the miner try { gEventLog.logMessage(EventLogMsgType.ELM_TYP_Warning, "Will attempt to kill miner (PID=" + nPID + ") after no accepted hashes for " + fSecKillAfter + " sec"); OutputConsoleError("Will attempt to kill miner (PID=" + nPID + ") after no accepted hashes for " + fSecKillAfter + " sec"); procMiner.Kill(); //Reset counters gWS.setLastAcceptedTimeUTC(); gWS.setLastMinerExitTimeUTC(0xdead0002); nSleepDelaySec = rnd.Next(1000, 3000); } catch (Exception ex) { //Failed gEventLog.logMessage(EventLogMsgType.ELM_TYP_Critical, "Failed to kill miner (PID=" + nPID + ") after no accepted hashes -- " + ex.ToString()); OutputConsoleError("ERROR: Kill miner process failed! Check the log for details..."); nSleepDelaySec = rnd.Next(3000, 10000); } //Wait a little Thread.Sleep(nSleepDelaySec); continue; } } } else { //Miner is not running if (dtmUtcLastNotRunning != DateTime.MinValue) { //See how long the miner wasn't running TimeSpan spnNotRunning = DateTime.UtcNow - dtmUtcLastNotRunning; double fSecNotRunning = spnNotRunning.TotalSeconds; //Check if miner was not running for too long if (fSecNotRunning > (5 * 60.0)) //5 minutes { //Need to reboot gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "Will attempt to REBOOT the rig after " + fSecNotRunning + " sec of miner process not running"); OutputConsoleError("CRITICAL: Will attempt to REBOOT the rig after " + fSecNotRunning + " sec of miner process not running"); Thread.Sleep(3 * 1000); //Force reboot rebootRig(true, "After " + fSecNotRunning + " of miner process not running"); //Reset counter dtmUtcLastNotRunning = DateTime.MinValue; } } else { dtmUtcLastNotRunning = DateTime.UtcNow; } } //See if we need to show stats DateTime dtmNowUtc = DateTime.UtcNow; double fSecSinceLastStats = (dtmNowUtc - dtmUtcLastStatsShown).TotalSeconds; //Show it every N seconds if (fSecSinceLastStats > 15.0) { dtmUtcLastStatsShown = dtmNowUtc; const string strFmtTimeSpan = "hh\\:mm\\:ss"; //Output watch stats string strStats = "WATCH: Miner=" + (bMinerRunning ? "On" : "OFF"); if (bMinerRunning) { strStats += " Runtime="; strStats += spnProcLifetime.ToString("dd\\:hh\\:mm\\:ss"); } int nExitCode; uint nNumRunning; gWS.getLastMinerExitTimeUTC(out nExitCode, out nNumRunning); strStats += " Restarts=" + (nNumRunning - 1); strStats += " LastAccepted="; DateTime dtmLastAcceptedUTC = gWS.getLastAccaptedTimeUTC(); if (dtmLastAcceptedUTC != DateTime.MinValue) { strStats += (dtmNowUtc - dtmLastAcceptedUTC).ToString(strFmtTimeSpan); } else { strStats += "Never"; } strStats += " LastGoodHash="; DateTime dtmLastGoodHashUTC = gWS.getLastGoodHashRateTimeUTC(); if (dtmLastGoodHashUTC != DateTime.MinValue) { strStats += (dtmNowUtc - dtmLastGoodHashUTC).ToString(strFmtTimeSpan); } else { strStats += "Never"; } //Output Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine(strStats); Console.ForegroundColor = ConsoleColor.White; } } }
public static EventLog gEventLog = new EventLog(); //Event log for diagnotic logging static void Main(string[] args) { //Do we have command line arguments? if (args.Length > 0) { if (args.Length >= 4) { CmdLineParams clp = new CmdLineParams(); int p = 0; if (uint.TryParse(args[p++], out clp.nHashRangeMin)) { if (uint.TryParse(args[p++], out clp.nHashRangeMax)) { if (clp.nHashRangeMax < clp.nHashRangeMin) { //Swap them uint v = clp.nHashRangeMin; clp.nHashRangeMin = clp.nHashRangeMax; clp.nHashRangeMax = v; } if (uint.TryParse(args[p++], out clp.nMaxAllowedMinerRestartsBeforeReboot)) { //Then clp.strMinerExePath = args[p++]; //Get command line parameters follow for (int a = p; a < args.Length; a++) { clp.arrMinerCmdParams.Add(args[a]); } //Set event log message gEventLog.logMessage(EventLogMsgType.ELM_TYP_Information, "[PID=" + Process.GetCurrentProcess().Id + "] Starting miner watch with parameters: " + String.Join(" ", args.ToArray()) + " -- Last boot time: " + GetLastBootTime().ToString("g")); //Set time when this app started gWS.setWhenThisAppStartedTimeUTC(); //Begin a watching thread Thread threadWatch = new Thread(() => { threadWatchMiner(clp); }); threadWatch.Start(); //And run the miner app MinerRunningLoop(clp); } else { OutputConsoleError("ERROR: Invalid RebootAfter"); } } else { OutputConsoleError("ERROR: Invalid MaxHash"); } } else { OutputConsoleError("ERROR: Invalid MinHash"); } } else { OutputConsoleError("ERROR: Not all command line parameters were provided"); } } else { //Show command line args string strAppName = Process.GetCurrentProcess().ProcessName; Console.WriteLine(gkstrAppName + " v." + gkstrAppVersion); Console.WriteLine("by www.dennisbabkin.com"); Console.WriteLine(""); Console.WriteLine("Usage:"); Console.WriteLine(strAppName + " MinHash MaxHash RebootAfter \"path-to\\ethminer.exe\" miner_commad_line"); Console.WriteLine(""); Console.WriteLine("where:"); Console.WriteLine(" MinHash = mininum allowed hash rate range (in Mh), ex: 80"); Console.WriteLine(" MaxHash = maximum allowed hash rate range (in Mh), or 0 if any, ex: 100"); Console.WriteLine(" RebootAfter = number of ethminer restarts before rebooting the rig, or 0 not to reboot"); Console.WriteLine(""); Console.WriteLine("Example:"); Console.WriteLine(""); Console.WriteLine(strAppName + " 80 100 32 \"path-to\\ethminer.exe\" -P stratum://0xETH_PUB_KEY:[email protected]:14444"); Console.WriteLine(""); } }
static void AnalyzeDataReceivedFromMiner(string strData, CmdLineParams info) { //Tokenzine by space string[] arrPs = strData.Trim().Split(' '); int nCnt = arrPs.Count(); // i 16:08:18 <unknown> Job: 23a95ff1... us2.ethermine.org [172.65.226.101:14444] // i 16:08:18 <unknown> Job: 4e9718f8... us2.ethermine.org [172.65.226.101:14444] // i 16:08:22 <unknown> Job: 03e90dfb... us2.ethermine.org [172.65.226.101:14444] // m 16:08:22 <unknown> 30:02 A2334:R4 90.96 Mh - cu0 22.65, cu1 22.77, cu2 22.77, cu3 22.77 // i 16:08:26 <unknown> Job: cea10285... us2.ethermine.org [172.65.226.101:14444] // m 16:08:27 <unknown> 30:02 A2334:R4 90.90 Mh - cu0 22.58, cu1 22.77, cu2 22.77, cu3 22.77 // i 16:08:30 <unknown> Job: e8275758... us2.ethermine.org [172.65.226.101:14444] // m 16:08:33 <unknown> 30:02 A2334:R4 90.97 Mh - cu0 22.65, cu1 22.77, cu2 22.77, cu3 22.77 // i 16:08:34 <unknown> Job: 78de40c4... us2.ethermine.org [172.65.226.101:14444] // i 16:08:37 <unknown> Job: 3884b399... us2.ethermine.org [172.65.226.101:14444] // i 16:08:37 <unknown> Job: 894baba6... us2.ethermine.org [172.65.226.101:14444] // m 16:08:38 <unknown> 30:02 A2334:R4 90.96 Mh - cu0 22.65, cu1 22.77, cu2 22.77, cu3 22.77 // i 16:08:41 <unknown> Job: 4c86d1d0... us2.ethermine.org [172.65.226.101:14444] // m 16:08:43 <unknown> 30:02 A2334:R4 90.90 Mh - cu0 22.58, cu1 22.77, cu2 22.77, cu3 22.77 // i 16:08:43 <unknown> Job: cc9d4a34... us2.ethermine.org [172.65.226.101:14444] // i 16:08:43 <unknown> Job: 4c34a4ad... us2.ethermine.org [172.65.226.101:14444] // i 16:08:47 <unknown> Job: cc8dced9... us2.ethermine.org [172.65.226.101:14444] // m 16:08:48 <unknown> 30:03 A2334:R4 90.97 Mh - cu0 22.65, cu1 22.77, cu2 22.77, cu3 22.77 //cu 16:08:48 cuda-1 Job: cc8dced9... Sol: 0xbca14f007c4cf6d7 // i 16:08:48 <unknown> **Accepted 27 ms. us2.ethermine.org [172.65.226.101:14444] // i 16:08:50 <unknown> Job: 24773f74... us2.ethermine.org [172.65.226.101:14444] // i 16:08:50 <unknown> Job: b7631f18... us2.ethermine.org [172.65.226.101:14444] // m 16:08:53 <unknown> 30:03 A2335:R4 90.90 Mh - cu0 22.59, cu1 22.77, cu2 22.77, cu3 22.77 //cu 16:08:54 cuda-2 Job: b7631f18... Sol: 0xbca14f017f7427cb // i 16:08:54 <unknown> **Accepted 26 ms. us2.ethermine.org [172.65.226.101:14444] // i 16:08:54 <unknown> Job: 8a7cbba8... us2.ethermine.org [172.65.226.101:14444] if (nCnt > 0 && (arrPs[0].CompareTo("m") == 0 || arrPs[0].CompareTo("i") == 0)) { for (int i = 1; i < nCnt; i++) { //Look for Mh if (arrPs[i].CompareTo("Mh") == 0) { //Look for: A53 89.96 Mh if (i - 2 > 0) { //Value before must be a float double fHashRate; if (double.TryParse(arrPs[i - 1], out fHashRate)) { if (arrPs[i - 2].IndexOf('A') == 0) { //This is a match bool bHashGood = false; //See if hashrate within range if (fHashRate >= info.nHashRangeMin) { //Good so far if (info.nHashRangeMax != 0) { //Need max range too if (fHashRate <= info.nHashRangeMax) { bHashGood = true; } } else { bHashGood = true; } } if (bHashGood) { //Register when we got a good hash rate gWS.setLastGoodHashRateTimeUTC(); } break; } } } } else if (arrPs[i].CompareTo("**Accepted") == 0) { //Hash was accepted gWS.setLastAcceptedTimeUTC(); break; } } } }
static MinerRunResult RunMiner(CmdLineParams info) { //Run the miner process and begin watching it MinerRunResult res = MinerRunResult.RES_MR_BAD_PARAMS_DIDNT_RUN; try { Process proc = new Process(); proc.StartInfo.FileName = info.strMinerExePath; if (info.arrMinerCmdParams.Count > 0) { //Make command line string strCmdLn = ""; foreach (string strCmd in info.arrMinerCmdParams) { if (!string.IsNullOrEmpty(strCmdLn)) { strCmdLn += " "; } if (strCmd.IndexOf(' ') == -1) { strCmdLn += strCmd; } else { strCmdLn += "\"" + strCmd + "\""; } } proc.StartInfo.Arguments = strCmdLn; proc.StartInfo.UseShellExecute = false; proc.StartInfo.CreateNoWindow = true; proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; proc.OutputDataReceived += new DataReceivedEventHandler((sender, e) => { try { DataReceivedFromMiner(e.Data, info); } catch (Exception ex) { //Failed gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "EXCEPTION_2: " + ex.ToString()); OutputConsoleError("EXCEPTION_2: " + ex.ToString()); } }); proc.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => { try { DataReceivedFromMiner(e.Data, info); } catch (Exception ex) { //Failed gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "EXCEPTION_3: " + ex.ToString()); OutputConsoleError("EXCEPTION_3: " + ex.ToString()); } }); //Start the process gWS.setMinerProcessClass(proc, true); proc.Start(); //Make the miner process exit with ours AttachChildProcessToThisProcess(proc); int nPID = proc.Id; gEventLog.logMessage(EventLogMsgType.ELM_TYP_Information, "Miner started (PID=" + nPID + ") ... with CMD: " + strCmdLn); proc.BeginErrorReadLine(); proc.BeginOutputReadLine(); proc.WaitForExit(); //Get exit code & remember it uint nExitCd = (uint)proc.ExitCode; gWS.setLastMinerExitTimeUTC(nExitCd); gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "Miner process (PID=" + nPID + ") has exited with error code 0x" + nExitCd.ToString("X")); OutputConsoleError("WARNING: Miner has exited with error code 0x" + nExitCd.ToString("X") + " ...."); res = MinerRunResult.RES_MR_MINER_EXITED; } else { //Error OutputConsoleError("ERROR: Not enough parameters to start a miner"); res = MinerRunResult.RES_MR_BAD_PARAMS_DIDNT_RUN; } } catch (Exception ex) { //Failed gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "EXCEPTION_1: " + ex.ToString()); OutputConsoleError("EXCEPTION_1: " + ex.ToString()); res = MinerRunResult.RES_MR_EXCEPTION; } return(res); }
static void MinerRunningLoop(CmdLineParams info) { //Loop that runs the miner and watches its performance Random rnd = new Random(); for (;;) { int nmsWait; //Run the miner MinerRunResult res = RunMiner(info); if (res == MinerRunResult.RES_MR_BAD_PARAMS_DIDNT_RUN) { gEventLog.logMessage(EventLogMsgType.ELM_TYP_Critical, "Quitting watch app due to unrecoverable error!"); OutputConsoleError("CRITICAL ERROR: Quitting watch app due to unrecoverable error!"); return; } else if (res == MinerRunResult.RES_MR_EXCEPTION) { //Wait for some time nmsWait = rnd.Next(10 * 1000, 60 * 1000); } else if (res == MinerRunResult.RES_MR_MINER_EXITED) { //Wait for some time nmsWait = rnd.Next(10 * 1000, 30 * 1000); } else { //Some other value gEventLog.logMessage(EventLogMsgType.ELM_TYP_Critical, "Quitting watch app due to unknown error! err=" + res); OutputConsoleError("CRITICAL ERROR: Quitting watch app due to unknown error!"); return; } //Wait before restarting Console.WriteLine("Waiting for " + nmsWait / 1000 + " sec before restarting the miner..."); Thread.Sleep(nmsWait); //Log message int nExitCode; uint nNumStarted; gWS.getLastMinerExitTimeUTC(out nExitCode, out nNumStarted); gEventLog.logMessage(EventLogMsgType.ELM_TYP_Warning, "MINER RESTART (number " + nNumStarted + ") after " + nmsWait / 1000 + " sec delay. Reason=" + res + ", exitCode=0x" + nExitCode.ToString("X")); //See if we've restarted too many times (and if we're allowed to reboot) if (info.nMaxAllowedMinerRestartsBeforeReboot > 0 && nNumStarted > info.nMaxAllowedMinerRestartsBeforeReboot) { //Need to reboot the system gEventLog.logMessage(EventLogMsgType.ELM_TYP_Error, "Will attempt to REBOOT the rig after " + nNumStarted + " miner restarts"); OutputConsoleError("CRITICAL: Will attempt to REBOOT the rig after " + nNumStarted + " miner restarts"); Thread.Sleep(3 * 1000); //Force reboot rebootRig(true, "After " + nNumStarted + "attempts to restart miner process"); break; } } }