private async Task <int> UpdateWtmSettings(Computer pc, CancellationToken token, int errorCount, int iteration, int jobCount, DateTime wtmSettingsDate)
        {
            pc.SwitchStatus = Computer.OperationStatus.OperationInProgress;

            var address = ViewModel.BuildServerAddress(pc.Name, Constants.StreamServer);
            var channel = Service.NewStreamChannel(address, TimeSpan.FromSeconds(60));

            MemoryStream stream        = NetHelper.SerializeToStream <WtmSettingsObject>(ViewModel.Instance.WtmSettings);
            var          uploadRequest = new StreamUploadRequest {
                Stream = stream
            };

            try
            {
                if (ViewModel.Instance.WtmSettings.ProxyPassword != null && ViewModel.Instance.WtmSettings.ProxyPassword.Length != 0)
                {
                    uploadRequest.ProxyPassword = ViewModel.Instance.WtmSettings.ProxyPassword.ToCharArray();
                }
                var response = await channel.UpdateWtmSettings(uploadRequest).WithCancellation(token).ConfigureAwait(false);

                if (response.ResponseFlag)
                {
                    pc.SwitchStatus    = Computer.OperationStatus.Success;
                    pc.WtmSettingsDate = response.Date;
                    if (pc.WtmSettingsDate >= wtmSettingsDate)
                    {
                        pc.UpdateStatus = Computer.OperationStatus.NotNecessary;
                    }
                }
            }
            catch (Exception ex)
            {
                errorCount++;
                pc.SwitchStatus = Computer.OperationStatus.Failure;
                Application.Current.Dispatcher.Invoke(() =>
                {
                    var p = new Paragraph();
                    p.Inlines.Add(new Run($"{pc.Name}: ").FontWeight(FontWeights.Bold).Color(Colors.Salmon));
                    p.Inlines.Add(new Run($"Failed to update.\r\n"));
                    p.Inlines.Add(new Run(ex.Message + "\r\n"));
                    NewParagraph = p;
                });
            }
            finally
            {
                if (uploadRequest.ProxyPassword.Length != 0)
                {
                    Array.Clear(uploadRequest.ProxyPassword, 0, uploadRequest.ProxyPassword.Length);
                }
                NetHelper.CloseChannel(channel);
                stream.Dispose();
                ReportTitle = $"Progress: {iteration + 1} of {jobCount}.";
            }
            return(errorCount);
        }
Exemple #2
0
        private async void ScanLanCommand(object obj)
        {
            ScanLanIsInProgress = true;
            ScanLanCancelSource = new CancellationTokenSource();
            var token = ScanLanCancelSource.Token;

            var taskList = new List <Task>();

            foreach (var table in ProfitTables.Tables)
            {
                foreach (var pc in table.Computers)
                {
                    pc.OnlineStatus = Computer.OperationStatus.OperationInProgress;
                    Task task = Task.Run(async() =>
                                         //Func<Task> function = (async () =>
                    {
                        var serverAddress = "net.tcp://" + pc.Name + ":" + NetHelper.Port + Constants.AccessPoint;
                        var channel       = Service.NewChannel(serverAddress, TimeSpan.FromSeconds(15));
                        try
                        {
                            var info = await channel.GetInfoAsync().WithCancellation(token).ConfigureAwait(false);
                            Debug.WriteLine($"{pc.Name} coin: {info?.CurrentCoin?.GetSymbol()}.");
                            if (info != null)
                            {
                                pc.OnlineStatus      = Computer.OperationStatus.Success;
                                pc.CurrentCoinName   = info?.CurrentCoin?.GetCoinInfo("Name");
                                pc.CurrentCoinSymbol = info?.CurrentCoin?.GetCoinInfo("SYMBOL");
                                pc.ApplicationMode   = info.ApplicationMode;
                            }
                            else
                            {
                                pc.OnlineStatus = Computer.OperationStatus.Failure;
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine($"{pc.Name} is not responding. " + ex.Message);
                            pc.OnlineStatus = Computer.OperationStatus.Failure;
                        }
                        finally
                        {
                            NetHelper.CloseChannel(channel);
                        }
                    });

                    taskList.Add(task);
                }
            }


            await Task.WhenAll(taskList);

            ScanLanIsInProgress = false;
        }
        private async void TestConnectionCommand(object obj)
        {
            var serverAddress = BuildServerAddress(WtmSettings.ServerName, Constants.AccessPoint);
            var channel       = Service.NewChannel(serverAddress);

            TestConnection.SetCanExecute(false);
            Helpers.MouseCursorWait();

            var             cancelSource = new CancellationTokenSource();
            var             token        = cancelSource.Token;
            ProgressManager pm           = new ProgressManager($"Accessing {WtmSettings.ServerName}...", cancelSource, 1000, true);

            try
            {
                var testResult = await channel.GetInfoAsync().WithCancellation(token).ConfigureAwait(false);

                if (testResult != null)
                {
                    pm.IsAlive = false;
                    pm.Close();
                    await Task.Delay(100);

                    Helpers.MouseCursorNormal();
                    if (testResult.ApplicationMode == "Server")
                    {
                        MessageBox.Show($"{Constants.AppName} server is alive and responsive on {WtmSettings.ServerName}.", "Success", MessageBoxButton.OK);
                    }
                    else
                    {
                        MessageBox.Show($"{Constants.AppName} is up on {WtmSettings.ServerName} but it is not in server mode.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                pm.Close();
                await Task.Delay(100);

                Helpers.MouseCursorNormal();
                MessageBox.Show("No response from the server.\n\n" + ex.Message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                var error = NetHelper.CloseChannel(channel);
                Application.Current.Dispatcher.Invoke(() => TestConnection.SetCanExecute(true));
                if (pm.IsAlive)
                {
                    pm.Close();
                }
            }
        }
        private async Task QueryRemoteWtmSettingsDate(Computer pc, CancellationToken token, DateTime wtmSettingsDate)
        {
            var address = ViewModel.BuildServerAddress(pc.Name, Constants.AccessPoint);
            var channel = Service.NewChannel(address);

            try
            {
                var info = await channel.GetInfoAsync().WithCancellation(token).ConfigureAwait(false);

                var remoteSettingsDate = await channel.GetWtmSettingsDateAsync().WithCancellation(token).ConfigureAwait(false);

                if (info != null)
                {
                    pc.OnlineStatus    = Computer.OperationStatus.Success;
                    pc.ApplicationMode = info.ApplicationMode;
                    pc.WtmSettingsDate = remoteSettingsDate;
                    if (wtmSettingsDate > remoteSettingsDate)
                    {
                        pc.Switch       = true;
                        pc.SwitchStatus = Computer.OperationStatus.Possible;
                        pc.UpdateStatus = Computer.OperationStatus.Necessary;
                    }
                    else
                    {
                        pc.SwitchStatus = Computer.OperationStatus.NotPossible;
                        pc.UpdateStatus = Computer.OperationStatus.NotNecessary;
                    }
                }
            }
            catch (Exception)
            {
                pc.OnlineStatus = Computer.OperationStatus.Failure;
                pc.SwitchStatus = Computer.OperationStatus.NotPossible;
            }
            finally
            {
                NetHelper.CloseChannel(channel);
            }
            return;
        }
Exemple #5
0
        private async Task UpdateWorkersFromServer(StreamWriter logFile)
        {
            bool addressOk = ValidateServerAddress(logFile);

            if (!addressOk)
            {
                return;
            }

            var serverAddress = BuildServerAddress(WtmSettings.ServerName, Constants.AccessPoint);
            var channel       = Service.NewChannel(serverAddress);

            DateTime remoteWorkersDate = default(DateTime);
            string   errorMessage      = "Failed to update Workers from local server.";

            try
            {
                remoteWorkersDate = await channel.GetWorkersDateAsync();
            }
            catch (Exception ex)
            {
                logFile?.WriteLine(errorMessage + " " + ex.Message);
                NetHelper.CloseChannel(channel);
                return;
            }

            if (remoteWorkersDate == default(DateTime))
            {
                NetHelper.CloseChannel(channel);
                logFile?.WriteLine(errorMessage);
                return;
            }

            var localWorkersDate = Workers.GetWorkersLastUpdateTime();

            if (localWorkersDate >= remoteWorkersDate)
            {
                logFile?.WriteLine($"{Constants.WorkersFile} is up to date.");
                NetHelper.CloseChannel(channel);
                return;
            }

            NetHelper.CloseChannel(channel);

            // Open stream channel for workers download
            serverAddress = BuildServerAddress(WtmSettings.ServerName, Constants.StreamServer);
            var streamChannel = Service.NewStreamChannel(serverAddress);

            Workers workers = null;

            try
            {
                var response = await streamChannel.GetWorkersAsync().ConfigureAwait(false);

                if (response != null)
                {
                    var memoryStream = new MemoryStream();
                    await response.Stream.CopyToAsync(memoryStream);

                    workers = NetHelper.DeserializeFromStream <Workers>(memoryStream);
                }

                if (workers == null)
                {
                    throw new NullReferenceException("The received data are null.");
                }
            }
            catch (Exception ex)
            {
                logFile?.WriteLine(errorMessage + " " + ex.Message);
                NetHelper.CloseChannel(streamChannel);
                return;
            }

            bool wResult = Workers.SaveWorkers(workers);

            if (!wResult)
            {
                logFile?.WriteLine("Workers have been received from local server but could not be saved.");
                NetHelper.CloseChannel(streamChannel);
                return;
            }

            IsInitializingWtm = true;

            Workers.PropertyChanging -= Workers_PropertyChanging;
            Workers.PropertyChanged  -= Workers_PropertyChanged;

            Workers = new Workers(workers.WorkerList, workers.PowerCost, workers.CoinType, workers.DisplayCoinAs, workers.NetworkScanMethod);

            Workers.PropertyChanging += Workers_PropertyChanging;
            Workers.PropertyChanged  += Workers_PropertyChanged;

            IsInitializingWtm = false;

            logFile?.WriteLine("Workers have been received from local server and successfully updated.");
            NetHelper.CloseChannel(streamChannel);
        }
Exemple #6
0
        public async Task <SwitchResult> SwitchStandalone()
        {
            var streamServerAddress = BuildServerAddress(WtmSettings.ServerName, Constants.StreamServer);

            using (var logFile = new StreamWriter(Constants.SwitchLog, true))
            {
                if (new FileInfo(Constants.SwitchLog).Length > 0)
                {
                    logFile.WriteLine(string.Empty);
                }
                logFile.WriteLine(DateTime.Now);

                // Respect the DelayNextSwitchTime setting
                if (WtmSettings.DelayNextSwitchTime != 0)
                {
                    var    lastTime    = new DateTime();
                    var    lastTimeObj = GetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess");
                    string lastTimeStr = lastTimeObj as string;
                    if (lastTimeStr != null && lastTimeStr != string.Empty)
                    {
                        lastTime = Convert.ToDateTime(lastTimeStr, DateTimeFormatInfo.InvariantInfo).ToUniversalTime();
                        var now = DateTime.UtcNow;
                        var lastTimePlusDelay = lastTime.AddHours(WtmSettings.DelayNextSwitchTime);
                        if (now < lastTimePlusDelay)
                        {
                            logFile.WriteLine($"Because of the Delay option checked Auto Switch can proceed only after {lastTimePlusDelay.ToLocalTime()}.");
                            return(SwitchResult.DelayIsNotOver);
                        }
                    }
                }

                // Update Workers from server
                if (WtmSettings.ApplicationMode == "Client" && WtmSettings.UpdateWorkersFromServer)
                {
                    await UpdateWorkersFromServer(logFile);
                }

                //Find localhost name in Workers list (used in Standalone and Client modes )
                var    thisPcName = Environment.MachineName;
                Worker thisWorker = GetWorkerByPCName(thisPcName);
                if (thisWorker == null && WtmSettings.ApplicationMode != "Server")
                {
                    string msg = $"{thisPcName} was not found in any worker.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.ThisPcIsNotListed);
                }

                // Get coin names from worker description
                var    coinList      = new List <string>();
                var    allCoinList   = new List <string>();
                var    workersList   = new List <Worker>();
                string noCoinChecked = string.Empty;
                if (thisWorker != null)
                {
                    noCoinChecked = $"No coin is checked as switchable in {thisWorker.Name}.";
                }
                else
                {
                    noCoinChecked = "No coin is checked as switchable.";
                }
                if (WtmSettings.ApplicationMode != "Server") // Standalone or Client mode - just the worker that contains localhost name
                {
                    workersList = new List <Worker> {
                        thisWorker
                    };
                }
                else // Server mode - all workers, all coins
                {
                    foreach (var w in Workers.WorkerList)
                    {
                        workersList.Add(w.Clone());
                    }
                    allCoinList = Workers.GetCoins(workersList, false, false);
                    if (allCoinList == null || allCoinList.Count == 0)
                    {
                        logFile.WriteLine(noCoinChecked);
                        return(SwitchResult.NothingToDo);
                    }
                    if (!allCoinList.Contains("Bitcoin"))
                    {
                        allCoinList.Add("Bitcoin");
                    }
                }
                coinList = Workers.GetCoins(workersList, false, true); // Only coins with Switch checked
                if (coinList == null || coinList.Count == 0)
                {
                    logFile.WriteLine(noCoinChecked);
                    return(SwitchResult.NothingToDo);
                }
                if (!coinList.Contains("Bitcoin"))
                {
                    coinList.Add("Bitcoin");
                }


                // Get WTM coins JSON data
                Dictionary <string, WtmData> wtmDataDict = new Dictionary <string, WtmData>();
                // Attempt to download data from local server
                HistoricalData localDataCopy = null;
                if (WtmSettings.ApplicationMode == "Client")
                {
                    var channel = Service.NewStreamChannel(streamServerAddress, TimeSpan.FromSeconds(60));
                    try
                    {
                        var response = await channel.GetWtmLocalDataAsync();

                        if (response != null)
                        {
                            var memoryStream = new MemoryStream();
                            await response.Stream.CopyToAsync(memoryStream);

                            localDataCopy = NetHelper.DeserializeFromStream <HistoricalData>(memoryStream);
                        }

                        if (localDataCopy == null)
                        {
                            throw new NullReferenceException("Local data from server are null.");
                        }
                    }
                    catch (Exception ex)
                    {
                        logFile.WriteLine($"Failed to download proxy data from local server {WtmSettings.ServerName}. " + ex.Message);
                        if (!WtmSettings.QueryWtmOnLocalServerFail)
                        {
                            return(SwitchResult.NoWtmData);
                        }
                    }
                    finally
                    {
                        NetHelper.CloseChannel(channel);
                    }

                    // Check if the received data are up to date
                    if (localDataCopy != null)
                    {
                        bool dataTimestampGood = EvaluateWtmDataTimeRange(localDataCopy.Date);
                        if (!dataTimestampGood && !WtmSettings.QueryWtmOnLocalServerFail)
                        {
                            logFile.WriteLine("The server cache data is expired."
                                              + " Make sure that AutoSwitch on the client launches later than on the server."
                                              + " For example, if AutoSwitch runs on a daily basis schedule AutoSwitch on the server to run at 9:00 and on the client machines at anyting between 10:00-23:59."
                                              + " This way the cache data obtained in the morning is valid for the rest of the day.");
                            return(SwitchResult.Terminate);
                        }
                        if (dataTimestampGood)
                        {
                            // Filter out coins downloded from server that are not checked for AutoSwitch locally
                            wtmDataDict = localDataCopy.PriceData.Where(x => coinList.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
                        }
                    }
                }
                // Download data from WhatToMine (Standalone or Server)
                if (wtmDataDict.Count == 0)
                {
                    StatusBarText = "Downloading coin definitions from whattomine.com...";
                    (Dictionary <string, WtmData> data, WhatToMine.GetWtmCoinDataResult result)wtmDataResult = (null, WhatToMine.GetWtmCoinDataResult.OK);
                    if (WtmSettings.ApplicationMode == "Server")
                    {
                        var allCoinHashList = GetHashrates(allCoinList);
                        wtmDataResult = await WhatToMine.GetWtmCoinData(allCoinHashList, false, logFile).ConfigureAwait(false);
                    }
                    else
                    {
                        var coinHashList = GetHashrates(coinList);
                        wtmDataResult = await WhatToMine.GetWtmCoinData(coinHashList, false, logFile).ConfigureAwait(false);
                    }
                    if (wtmDataResult.result == WhatToMine.GetWtmCoinDataResult.CoinNotFound)
                    {
                        return(SwitchResult.CoinNotFound);
                    }
                    wtmDataDict = wtmDataResult.data;
                    if (wtmDataDict == null)
                    {
                        return(SwitchResult.CoinNotFound);
                    }
                }

                // Update WTM coin data with historical averages from local database if opted to
                var  historicalDataList     = new List <HistoricalData>();
                bool historicalDataUpToDate = false;
                if (WtmSettings.UseHistoricalAverage)
                {
                    if (WtmSettings.ApplicationMode == "Client")
                    {
                        //Needs longer timeout for the server might be downloading all coins from whattomine.com
                        var channel = Service.NewStreamChannel(streamServerAddress, TimeSpan.FromSeconds(180));

                        try
                        {
                            var response = await channel.GetPriceHistoryAsync(new StreamDownloadRequest { Period = WtmSettings.HistoricalAveragePeriod }).ConfigureAwait(false);

                            if (response != null)
                            {
                                var memoryStream = new MemoryStream();
                                await response.Stream.CopyToAsync(memoryStream);

                                historicalDataList     = NetHelper.DeserializeFromStream <List <HistoricalData> >(memoryStream);
                                historicalDataUpToDate = true;
                            }
                        }
                        catch (Exception ex)
                        {
                            logFile.WriteLine($"Failed to obtain historical prices from local server {WtmSettings.ServerName}. " + ex.Message);
                            return(SwitchResult.Error);
                        }
                        finally
                        {
                            NetHelper.CloseChannel(channel);
                        }
                    }
                    else
                    {
                        var result = await UpdatePriceHistory();

                        if (result == UpdatePriceHistoryResult.Success || result == UpdatePriceHistoryResult.AlreadyUpToDate)
                        {
                            historicalDataList     = ReadHistoricalData(WtmSettings.HistoricalAveragePeriod);
                            historicalDataUpToDate = true;
                        }
                    }
                    // Calculate historical prices for wtmDataList
                    if (historicalDataUpToDate)
                    {
                        GetHistoricalAverages(wtmDataDict, historicalDataList);
                    }
                }

                // Update WtmLocalData if in server mode
                if (WtmSettings.ApplicationMode == "Server")
                {
                    if (wtmDataDict != null && wtmDataDict.Count != 0)
                    {
                        using (var db = new LiteDatabase(Constants.DataBase))
                        {
                            var collection = db.GetCollection <HistoricalData>(Constants.LightDB_WtmCacheCollection);
                            collection.Delete(LiteDB.Query.All());
                            collection.Insert(new HistoricalData(DateTime.Now, wtmDataDict));
                        }
                    }
                }

                // Abandon if in server mode and DontSwitchServer is checked
                if (WtmSettings.ApplicationMode == "Server" && WtmSettings.DontSwitchServer)
                {
                    return(SwitchResult.NoNeedToSwitch);
                }
                if (WtmSettings.ApplicationMode == "Server" && !WtmSettings.DontSwitchServer && thisWorker == null)
                {
                    logFile.WriteLine("The server cannot switch because it is not listed in any worker table.");
                    return(SwitchResult.ThisPcIsNotListed);
                }

                // Calculate profit table for this PC and analize it
                var            profitTables = WhatToMine.CreateProfitTables(wtmDataDict, workersList, (decimal)Workers.PowerCost, WtmSettings, true);
                var            profitTable = profitTables.First();
                var            currentCoinShortcut = Shortcut.GetCurrentCoin(); // Get current coin from Startup folder .lnk
                ProfitTableRow maxCoinRow = null; decimal maxProfit = 0; decimal currentProfit = 0;
                bool           nothingChecked = false;
                WhatToMine.GetProfit(profitTable, currentCoinShortcut, out maxCoinRow, out maxProfit, out currentProfit, out nothingChecked);
                if (nothingChecked)
                {
                    string msg = "Nothing to do. Make sure there are actual switchable coins defined in worker.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NothingToDo);
                }
                if (maxProfit == 0)
                {
                    string msg = $"There is no coin with profit above 0 for {thisPcName}.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NothingToDo);
                }

                //Check profitability and price margin
                bool currentCoinMatch             = currentCoinShortcut?.GetName() == maxCoinRow.Name;
                bool currentCoinPathMatch         = currentCoinShortcut?.Path == maxCoinRow.Path;
                bool currentPforitIsHigherOrEqual = maxProfit <= currentProfit;
                bool currentProfitIsWithinMargin  = false;
                if (WtmSettings.PriceMargin > 0)
                {
                    currentProfitIsWithinMargin = maxProfit <= (currentProfit + (currentProfit * WtmSettings.PriceMargin / 100));
                }

                if (currentPforitIsHigherOrEqual)
                {
                    string msg;
                    msg = $"No need to switch. {thisPcName} is already set to mine the most profitable coin {currentCoinShortcut?.GetNameAndSymbol()}.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NoNeedToSwitch);
                }
                if (currentProfitIsWithinMargin)
                {
                    string msg;
                    if (currentCoinMatch && !currentCoinPathMatch)
                    {
                        msg = $"No need to switch. {thisPcName} is set to mine {currentCoinShortcut?.GetNameAndSymbol()} started by \"{currentCoinShortcut?.Path}\". It is less profitable than {maxCoinRow.NameAndSymbol} started by \"{maxCoinRow.Path}\" but the difference is within price margin.";
                    }
                    else
                    {
                        msg = $"No need to switch. {thisPcName} is set to mine {currentCoinShortcut?.GetNameAndSymbol()}. It is less profitable than {maxCoinRow.NameAndSymbol} but the difference is within price margin.";
                    }
                    logFile.WriteLine(msg);
                    return(SwitchResult.NoNeedToSwitch);
                }

                // Check if executable path exists
                if (!File.Exists(maxCoinRow.Path))
                {
                    logFile.WriteLine($"{maxCoinRow.Path} - file does not exist on {thisPcName}.");
                    return(SwitchResult.CoinNotFound);
                }

                bool startOk = false; // Start miner process flag
                try
                {
                    Shortcut.CreateCoinShortcut(maxCoinRow.Name, maxCoinRow.Symbol, maxCoinRow.Algorithm, maxCoinRow.Path, maxCoinRow.Arguments);
                    if (WtmSettings.RestartComputer)
                    {
                        RestartComputerPending = true;
                    }
                    if (WtmSettings.RestartMiner)
                    {
                        // Kill processes from KillList
                        var killResponse = KillProcesses(WtmSettings.KillList);
                        if (!killResponse.success && killResponse.failList != null)
                        {
                            string errorMessage = $"Startup shortcut to mine {maxCoinRow.NameAndSymbol} has been created but some processes could not be killed:";
                            logFile.WriteLine(errorMessage);
                            foreach (var entry in killResponse.failList)
                            {
                                if (entry != string.Empty)
                                {
                                    logFile.WriteLine(entry);
                                }
                            }
                            logFile.WriteLine($"{Environment.MachineName} will restart.");
                            RestartComputerPending = true;
                            SetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess", DateTime.UtcNow.ToString("o"));
                            return(SwitchResult.SwitchedSuccessfully);
                        }

                        if (!System.IO.File.Exists(maxCoinRow.Path))
                        {
                            logFile.WriteLine($"ERROR: File not found. {maxCoinRow.Path}");
                            return(SwitchResult.Error);
                        }

                        // Start miner process
                        var     startInfo = new ProcessStartInfo(maxCoinRow.Path, maxCoinRow.Arguments);
                        Process process   = new Process();
                        process.StartInfo = startInfo;
                        startOk           = process.Start();
                        if (!startOk)
                        {
                            throw new Exception($"\"{Path.GetFileName(maxCoinRow.Path)}\" failed to start.");
                        }
                    }
                    SetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess", DateTime.UtcNow.ToString("o"));
                    logFile.WriteLine($"Switched to {maxCoinRow.NameAndSymbol} successfully.");
                    return(SwitchResult.SwitchedSuccessfully);
                }
                catch (Exception ex)
                {
                    logFile.WriteLine($"Failed to switch to {maxCoinRow.NameAndSymbol}: {ex.Message}");
                    if (WtmSettings.RestartMiner && !startOk)
                    {
                        RestartComputerPending = true;
                        logFile.WriteLine($"{Environment.MachineName} will restart.");
                    }
                    return(SwitchResult.Error);
                }
            }
        }
        private async Task <int> UpdateVersion(Computer pc, CancellationToken token, int errorCount, int iteration, int jobCount, string appVersion)
        {
            pc.SwitchStatus      = Computer.OperationStatus.OperationInProgress;
            pc.UpdateSuccessfull = new TaskCompletionSource <bool>();

            var        address      = ViewModel.BuildServerAddress(pc.Name, Constants.StreamServer);
            var        channel      = Service.NewStreamChannel(address, TimeSpan.FromSeconds(60 * 10));
            FileStream outputStream = null;

            try
            {
                string             appFileName = Helpers.ApplicationPath();
                System.IO.FileInfo info        = new System.IO.FileInfo(appFileName);
                outputStream = new FileStream(appFileName, FileMode.Open, FileAccess.Read, FileShare.Read, Service.StreamBuffer, true);
                FileRequest request = new FileRequest();
                request.CallerHostName = Environment.MachineName;
                request.FileName       = info.Name;
                request.Length         = info.Length;
                request.FileStream     = outputStream;

                var reply = await channel.UpdateApplication(request).WithCancellation(token).ConfigureAwait(false);

                var awaitCallbackTask = pc.UpdateSuccessfull.Task;
                await Task.WhenAny(awaitCallbackTask, Task.Delay(1000 * 30)).WithCancellation(token).ConfigureAwait(false);

                bool callbackResult = false;
                if (awaitCallbackTask.Status == TaskStatus.RanToCompletion)
                {
                    callbackResult = awaitCallbackTask.Result;
                }
                if (!callbackResult)
                {
                    throw new Exception($"{pc.Name} has downloaded the new version but failed to report success.");
                }

                // Update row colors, green (NotNecessary) - the version is up to date, red (Necessary) - the version is older.
                if (new Version(appVersion).CompareTo(new Version(pc.Version)) == 1)
                {
                    pc.UpdateStatus = Computer.OperationStatus.Necessary;
                }
                else
                {
                    pc.UpdateStatus = Computer.OperationStatus.NotNecessary;
                }
            }
            catch (Exception ex)
            {
                errorCount++;
                pc.SwitchStatus = Computer.OperationStatus.Failure;
                Application.Current.Dispatcher.Invoke(() =>
                {
                    var p = new Paragraph();
                    p.Inlines.Add(new Run($"{pc.Name}: ").FontWeight(FontWeights.Bold).Color(Colors.Salmon));
                    p.Inlines.Add(new Run($"Failed to update.\r\n"));
                    p.Inlines.Add(new Run(ex.Message + "\r\n"));
                    NewParagraph = p;
                });
            }
            finally
            {
                NetHelper.CloseChannel(channel);
                if (outputStream != null)
                {
                    outputStream.Close();
                }
                ReportTitle = $"Progress: {iteration + 1} of {jobCount}.";
            }
            return(errorCount);
        }
        public async Task InitializeInterface()
        {
            // Delete registry key left-over
            var startWithWindows = GetRegistryKeyValue(Constants.RunRegistryKey, Constants.AppName);

            if (startWithWindows != null && !WtmSettings.StartWithWindows)
            {
                DeleteRegistryKeyValue(Constants.RunRegistryKey, Constants.AppName);
            }

            ApplyServerSettingsCommand(null); // Start server if app's role is 'Server'

            NetHelper.Port        = WtmSettings.TcpPort;
            AccessPoinServiceHost = new ServiceHost(typeof(Service));
            AccessPoinServiceHost.Open();

            string failedToDecrypt = "The encryption is based on machine configuration. "
                                     + "If you have copied settings to a different machine, logged on as a different user or changed your Windows password you need to re-enter ";

            ScanLanCancelSource = new System.Threading.CancellationTokenSource();
            var token = ScanLanCancelSource.Token;

            Func <Task> taskLoadWtmCoins = (async() =>
            {
                //Load Whattomine available coins
                if (WtmSettings.ProxyPasswordEncrypted != null && WtmSettings.ProxyPasswordEncrypted != string.Empty)
                {
                    try
                    {
                        BypassUndo(() => WtmSettings.ProxyPassword = WtmSettings.ProxyPasswordEncrypted.DecryptSecure());
                    }
                    catch (Exception)
                    {
                        WtmSettings.ProxyPasswordEncrypted = null;
                        string msg = "Failed to decrypt WhatToMine proxy password. " + failedToDecrypt + "the proxy password.";
                        var t = Task.Run(() => MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error));
                    }
                }
                await LoadWtmCoins(token);

                // Update coins statuses
                if (WtmCoins != null)
                {
                    UpdateCoinStatus();
                }
                IsInitializingWtm = false;
            });

            Func <Task> taskLoadYahooCurrencies = (async() =>
            {
                //Load Yahoo currencies
                SaveUndoIsEnabled = false;
                await WtmSettings.GetYahooRates();
                SaveUndoIsEnabled = true;
            });

            var tasks = new List <Task>()
            {
                taskLoadWtmCoins()
            };

            if (WtmSettings.UseYahooRates)
            {
                tasks.Add(taskLoadYahooCurrencies());
            }
            try
            {
                await Task.WhenAll(tasks).WithCancellation(token);
            }
            catch (Exception ex)
            {
                Task <MessageBoxResult> t = null;
                if (ex.Message != "The operation was canceled.")
                {
                    t = Task.Run(() => MessageBox.Show("Error while waiting for taskLoadWtmCoins or taskLoadYahooCurrencies.\n" + ex.Message, "", MessageBoxButton.OK, MessageBoxImage.Error));
                }
            }
            finally
            {
                IsInitializingWtm = false;
            }

            // Update callback
            using (RegistryKey key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Constants.BaseRegistryKey, true))
            {
                if (key != null)
                {
                    var callerHostName = key.GetValue("CallerHostName") as string;
                    if (callerHostName != null && callerHostName != string.Empty)
                    {
                        var address = BuildServerAddress(callerHostName, Constants.AccessPoint);
                        var channel = Service.NewChannel(address, TimeSpan.FromSeconds(60));
                        try
                        {
                            await channel.UpdateApplicationCallback(Environment.MachineName, Helpers.ApplicationVersion());
                        }
                        catch { }
                        finally
                        {
                            NetHelper.CloseChannel(channel);
                        }
                        key.DeleteValue("CallerHostName");
                    }
                }
            }

            bool workersExist  = File.Exists(Constants.WorkersFile);
            bool settingsExist = File.Exists(Constants.WtmSettingsFile);

            if (!workersExist && !settingsExist)
            {
                var t = Task.Run(() =>
                                 MessageBox.Show($"If you're running a fresh copy of {Constants.AppName} please define your workers inside 'Workers' tab and click 'Save' icon.", "Default configuration used", MessageBoxButton.OK, MessageBoxImage.Information));
            }

            else
            {
                if (DefaultWorkers)
                {
                    var t = Task.Run(() =>
                                     MessageBox.Show($"{Constants.WorkersFile} is missing or corrupt.", "", MessageBoxButton.OK, MessageBoxImage.Warning));
                }
                if (DefaultWtmSettings)
                {
                    var t = Task.Run(() =>
                                     MessageBox.Show($"{Constants.WtmSettingsFile} is missing or corrupt. Default settings are used.", "", MessageBoxButton.OK, MessageBoxImage.Warning));
                }
            }

            // Check if there are any pending jobs in registry
            bool   switchJobIsInProgress      = false;
            bool   updatePriceJobIsInProgress = false;
            string switchTime            = string.Empty;
            string switchLastTime        = string.Empty;
            string updatePriceTime       = string.Empty;
            string updatePriceLastUpdate = string.Empty;

            using (RegistryKey switchKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Constants.SwitchRegistryKey, true))
                using (RegistryKey updatePriceKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Constants.UpdatePriceHistoryRegistryKey, true))
                {
                    if ((string)switchKey.GetValue("IsInProgress") == "True")
                    {
                        switchJobIsInProgress = true;
                    }
                    switchTime = (string)switchKey.GetValue("Schedule");
                    if (switchTime != null && switchTime != string.Empty)
                    {
                        SwitchSchedule = Convert.ToDateTime(switchTime, DateTimeFormatInfo.InvariantInfo).ToLocalTime();
                    }
                    switchLastTime = (string)switchKey.GetValue("LastUpdate");
                    if (switchLastTime != null && switchLastTime != string.Empty)
                    {
                        SwitchLastTime = Convert.ToDateTime(switchLastTime, DateTimeFormatInfo.InvariantInfo).ToLocalTime();
                    }

                    if ((string)updatePriceKey.GetValue("IsInProgress") == "True")
                    {
                        updatePriceJobIsInProgress = true;
                    }
                    updatePriceTime = (string)updatePriceKey.GetValue("Schedule");
                    if (updatePriceTime != null && updatePriceTime != string.Empty)
                    {
                        HistoricalPricesSchedule = Convert.ToDateTime(updatePriceTime, DateTimeFormatInfo.InvariantInfo).ToLocalTime();
                    }
                    updatePriceLastUpdate = (string)updatePriceKey.GetValue("LastUpdate");
                    if (updatePriceLastUpdate != null && updatePriceLastUpdate != string.Empty)
                    {
                        HistoricalPricesLastTime = Convert.ToDateTime(updatePriceLastUpdate, DateTimeFormatInfo.InvariantInfo).ToLocalTime();
                    }
                }

            // Start job if one is still pending after system restart
            if (switchJobIsInProgress && workersExist)
            {
                await SwitchTaskWrapper();

                switchTime = string.Empty;
            }
            if (updatePriceJobIsInProgress && workersExist)
            {
                await UpdatePriceHistoryTaskWrapper();

                updatePriceTime = string.Empty;
            }

            // Delete Mass Update left-overs if they exist
            Helpers.DeleteUpdateLeftOvers();

            // Check if Switch job has been missed today/this hour
            if (WtmSettings.AutoSwitch)
            {
                if (switchTime != string.Empty)
                {
                    DateTime schedule   = Convert.ToDateTime(switchTime, DateTimeFormatInfo.InvariantInfo);
                    DateTime now        = DateTime.Now;
                    DateTime expiryTime = GetSwitchExpiry(schedule);
                    if (schedule < now && now < expiryTime)
                    {
                        await SwitchTaskWrapper();

                        switchTime = string.Empty;
                    }
                }
            }

            // Check if UpdateHistoricalPrices job has been missed today
            DateTime updatePriceLastUpdateTime;

            if (WtmSettings.BackupHistoricalPrices)
            {
                if (updatePriceLastUpdate != string.Empty)
                {
                    updatePriceLastUpdateTime = Convert.ToDateTime(updatePriceLastUpdate, DateTimeFormatInfo.InvariantInfo);
                    DateTime now = DateTime.Now;
                    DateTime historyBackupTime = new DateTime(now.Year, now.Month, now.Day, WtmSettings.HistoryTimeTo.Hour, WtmSettings.HistoryTimeTo.Minute, 0, DateTimeKind.Local);
                    if (now > historyBackupTime && updatePriceLastUpdateTime.Day < now.Day) // Now is past daily schedule and last update had been done before today
                    {
                        await UpdatePriceHistoryTaskWrapper();

                        updatePriceTime = string.Empty;
                    }
                }
            }

            // Schedule jobs
            JobManager.UseUtcTime();
            if (WtmSettings.AutoSwitch == true && workersExist)
            {
                Debug.WriteLine(DateTime.Now + " Scheduling a Switch task at startup.");
                if (switchTime != string.Empty)
                {
                    ResetScheduledJob(JobType.Switch, switchTime);
                }
                else
                {
                    ResetScheduledJob(JobType.Switch);
                }
            }
            if (WtmSettings.BackupHistoricalPrices == true && workersExist)
            {
                if (updatePriceTime != string.Empty)
                {
                    ResetScheduledJob(JobType.UpdatePriceHistory, updatePriceTime);
                }
                else
                {
                    ResetScheduledJob(JobType.UpdatePriceHistory);
                }
            }
            UpdateNextJobStatus();
        }
        private async void StartCommand(object obj)
        {
            SwitchIsInProgress = true;

            ManualSwitchCancelSource = new CancellationTokenSource();
            var          token      = ManualSwitchCancelSource.Token;
            var          taskList   = new List <Task>();
            var          jobCount   = ProfitTables.Sum(x => x.Computers.Count);
            var          failList   = new List <ProfitTable>();
            FlowDocument report     = new FlowDocument();
            int          errorCount = 0;

            await Task.Run(() =>
            {
                ReportTitle = $"Progress: 0 of {jobCount}.";
                int i       = 1;

                foreach (var table in ProfitTables)
                {
                    var currentCoinRow = table.ProfitList[0];

                    foreach (var pc in table.Computers)
                    {
                        pc.RestartStatus = pc.Restart ? Computer.OperationStatus.OperationInProgress : Computer.OperationStatus.Indeterminate;
                        pc.SwitchStatus  = pc.Switch ? Computer.OperationStatus.OperationInProgress : Computer.OperationStatus.Indeterminate;

                        Func <Task> function = (async() =>
                        {
                            var serverAddress = "net.tcp://" + pc.Name + ":" + NetHelper.Port + Constants.AccessPoint;
                            var channel = Service.NewChannel(serverAddress, TimeSpan.FromSeconds(10));

                            try
                            {
                                try
                                {
                                    if (pc.Switch)
                                    {
                                        bool switchResult = await channel.SetCurrentCoinAsync(
                                            currentCoinRow.Name,
                                            currentCoinRow.Symbol,
                                            currentCoinRow.Algorithm,
                                            currentCoinRow.Path,
                                            currentCoinRow.Arguments).WithCancellation(token);
                                        if (switchResult)
                                        {
                                            pc.SwitchStatus = Computer.OperationStatus.Success;
                                        }
                                        else
                                        {
                                            pc.SwitchStatus = Computer.OperationStatus.Failure;
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    errorCount++;
                                    pc.SwitchStatus = Computer.OperationStatus.Failure;
                                    Application.Current.Dispatcher.Invoke(() =>
                                    {
                                        var p = new Paragraph();
                                        p.Inlines.Add(new Run($"{table.Name}:").FontWeight(FontWeights.Bold).Color(Colors.Salmon));
                                        p.Inlines.Add(new Run($"{pc.Name} Failed to switch to {currentCoinRow.NameAndSymbol}.\r\n"));
                                        p.Inlines.Add(new Run(ex.Message + "\r\n"));
                                        NewParagraph = p;
                                    });
                                    if (pc.Restart)
                                    {
                                        pc.RestartStatus = Computer.OperationStatus.Failure;
                                    }
                                    else
                                    {
                                        pc.RestartStatus = Computer.OperationStatus.NotPossible;
                                    }
                                    return;
                                }
                                try
                                {
                                    if (pc.Restart)
                                    {
                                        // Schedule delayed restart if pc is localhost
                                        if (string.Equals(pc.Name, Environment.MachineName, StringComparison.CurrentCultureIgnoreCase))
                                        {
                                            RestartPending = true;
                                            pc.RestartStatus = Computer.OperationStatus.Pending;
                                        }
                                        else
                                        {
                                            bool restartResult = await channel.RestartComputerAsync(5).WithCancellation(token);
                                            if (restartResult)
                                            {
                                                pc.RestartStatus = Computer.OperationStatus.Success;
                                            }
                                            else
                                            {
                                                pc.RestartStatus = Computer.OperationStatus.Failure;
                                            }
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    errorCount++;
                                    pc.RestartStatus = Computer.OperationStatus.Failure;
                                    Application.Current.Dispatcher.Invoke(() =>
                                    {
                                        var p = new Paragraph();
                                        p.Inlines.Add(new Run($"{table.Name}:").FontWeight(FontWeights.Bold).Color(Colors.Salmon));
                                        p.Inlines.Add(new Run($"{pc.Name} Failed to restart.\r\n"));
                                        p.Inlines.Add(new Run(ex.Message + "\r\n"));
                                        NewParagraph = p;
                                    });
                                }
                            }
                            finally
                            {
                                NetHelper.CloseChannel(channel);
                                ReportTitle = $"Progress: {i} of {jobCount}.";
                                i++;
                            }
                        });
                        // This line blocks UI upon DNS lookup of a non-existing or disconnected machine.
                        // That is why the entire block is wrapped into Task.Run().
                        Task task = function();
                        taskList.Add(task);
                    }
                }
            });

            await Task.WhenAll(taskList);

            if (errorCount == 0)
            {
                ReportTitle  = "Report:";
                NewParagraph = new Paragraph(new Run("Operation has finished successfully."));
            }
            else
            {
                ReportTitle = "Error report:";
            }

            SwitchIsInProgress = false;
            SwitchIsFinished   = true;
        }