Exemplo n.º 1
0
        public ValueTask ExecuteAsync(IConsole console)
        {
            Log.Info($"Starting {nameof(BalanceClans)}...");

            Log.Debug("Retrieving data diagnostics...");
            var dd = _provider.GetDataDiagnostic();

            Log.Info($"Scheduled Players Per Day: {dd.ScheduledPlayersPerDay:N0}");
            Log.Info($"Target Players Per Minute: {PlayersPerMinute:N0}");

            // Minutes, in average, per day, that the system is off, or not retrieving data
            const int averageOutTimeMinutes = 30;

            // The average clan size, as retrieved in 2020-07-30
            const int averageClanSize = 14;

            var maxPerDay = PlayersPerMinute * (60 * 24 - averageOutTimeMinutes);
            var minPerDay = maxPerDay - 2 * averageClanSize;

            Log.Info($"Min players per day: {minPerDay:N0}");
            Log.Info($"Max players per day: {maxPerDay:N0}");

            if (dd.ScheduledPlayersPerDay < minPerDay || dd.ScheduledPlayersPerDay > maxPerDay)
            {
                Log.Info($"Balancing between {minPerDay:N0} and {maxPerDay:N0}.");
                _recorder.BalanceClanSchedule(minPerDay, maxPerDay);
            }

            console.Output.WriteLine("Done!");
            Log.Info($"Done {nameof(BalanceClans)}.");

            return(default);
Exemplo n.º 2
0
        private static int Main(string[] args)
        {
            try
            {
                ParseParams(args, out var ageHours, out var maxRunMinutes,
                            out var hourToDeleteOldFiles, out var daysToKeepOnDelete, out var calculateMoe,
                            out var calculateReference, out var playersPerMinute, out var utcShiftToCalculate, out var waitForRemote);

                var resultDirectoryXbox = ConfigurationManager.AppSettings["ResultDirectory"];
                var resultDirectoryPs   = ConfigurationManager.AppSettings["PsResultDirectory"];

                Log.Info("------------------------------------------------------------------------------------");
                Log.Info("CalculateClanStats iniciando...");
                Log.InfoFormat(
                    "ageHours: {0}; maxRunMinutes: {1}; resultDirectory: {2}; resultDirectoryPs: {3}; utcShiftToCalculate: {4}; waitForRemote: {5}",
                    ageHours, maxRunMinutes, resultDirectoryXbox, resultDirectoryPs, utcShiftToCalculate, waitForRemote);

                var sw = Stopwatch.StartNew();

                var connectionString = ConfigurationManager.ConnectionStrings["Main"].ConnectionString;
                var provider         = new DbProvider(connectionString);
                var clans            = provider.GetClanCalculateOrder(ageHours).ToArray();
                Log.InfoFormat("{0} clãs devem ser calculados.", clans.Length);

                const int averageOutTimeMinutes = 30;
                const int averageClanSize       = 25;
                var       maxPerDay             = playersPerMinute * 60 * 24 - averageOutTimeMinutes * playersPerMinute;
                var       minPerDay             = maxPerDay - averageClanSize;
                Log.Info($"playersPerMinute: {playersPerMinute}; maxPerDay: {maxPerDay}; minPerDay: {minPerDay}");

                var recorder = new DbRecorder(connectionString);

                var putterXbox = new FtpPutter(ConfigurationManager.AppSettings["FtpFolder"],
                                               ConfigurationManager.AppSettings["FtpUser"],
                                               ConfigurationManager.AppSettings["FtpPassworld"]);

                var putterPs = new FtpPutter(ConfigurationManager.AppSettings["PsFtpFolder"],
                                             ConfigurationManager.AppSettings["PsFtpUser"],
                                             ConfigurationManager.AppSettings["PsFtpPassworld"]);

                var mailSender = new MailSender(ConfigurationManager.AppSettings["SmtpHost"],
                                                int.Parse(ConfigurationManager.AppSettings["SmtpPort"]),
                                                true, ConfigurationManager.AppSettings["SmtpUsername"],
                                                ConfigurationManager.AppSettings["SmtpPassword"])
                {
                    From = new MailAddress(ConfigurationManager.AppSettings["SmtpUsername"],
                                           "Calculate Service")
                };

                var webCacheAge    = TimeSpan.FromMinutes(1);
                var cacheDirectory = ConfigurationManager.AppSettings["CacheDirectory"];
                var fetcher        = new Fetcher(cacheDirectory)
                {
                    WebCacheAge      = webCacheAge,
                    WebFetchInterval = TimeSpan.FromSeconds(1),
                    ApplicationId    = ConfigurationManager.AppSettings["WgApi"]
                };

                // Obtém o status, por causa da data dos MoE, para disparar o cálculo assíncrono
                DateTime       lastMoeXbox, lastMoePs, lastReferencesXbox, lastReferencesPs;
                SiteDiagnostic siteStatusXbox, siteStatusPs;
                try
                {
                    siteStatusXbox = fetcher.GetSiteDiagnostic(
                        ConfigurationManager.AppSettings["RemoteSiteStatusApi"], ConfigurationManager.AppSettings["ApiAdminKey"]);
                    siteStatusPs = fetcher.GetSiteDiagnostic(
                        ConfigurationManager.AppSettings["PsRemoteSiteStatusApi"], ConfigurationManager.AppSettings["ApiAdminKey"]);

                    lastMoeXbox        = siteStatusXbox.TanksMoELastDate;
                    lastMoePs          = siteStatusPs.TanksMoELastDate;
                    lastReferencesXbox = siteStatusXbox.TankLeadersLastDate;
                    lastReferencesPs   = siteStatusPs.TankLeadersLastDate;
                }
                catch (Exception ex)
                {
                    Log.Error("Error getting initial site diagnostics", ex);

                    // Since the site id offline...
                    siteStatusXbox = siteStatusPs = new SiteDiagnostic(null);
                    GetLastDatesFromFiles(resultDirectoryXbox, out lastMoeXbox, out lastReferencesXbox);
                    GetLastDatesFromFiles(resultDirectoryPs, out lastMoePs, out lastReferencesPs);
                }

                Log.Info($"lastMoeXbox: {lastMoeXbox:yyyy-MM-dd}");
                Log.Info($"lastMoePs: {lastMoePs:yyyy-MM-dd}");

                Log.Info($"lastReferencesXbox: {lastReferencesXbox:yyyy-MM-dd}");
                Log.Info($"lastReferencesPs: {lastReferencesPs:yyyy-MM-dd}");

                // Dispara as tarefas de calculo gerais
                var calculationTask = Task.Run(() => RunCalculations(calculateReference, calculateMoe,
                                                                     lastMoeXbox, lastMoePs, lastReferencesXbox, lastReferencesPs, provider, recorder, mailSender,
                                                                     resultDirectoryXbox, resultDirectoryPs,
                                                                     putterXbox, putterPs, fetcher, utcShiftToCalculate));

                // Dispara a tarefa de apagar arquivos antigos dos servidores
                var deleteTask = Task.Run(() =>
                {
                    if (DateTime.UtcNow.Hour != hourToDeleteOldFiles || daysToKeepOnDelete < 28)
                    {
                        return;
                    }

                    // Apaga arquivos com a API administrativa
                    var cleanXBox = Task.Run(() =>
                    {
                        try
                        {
                            var cleaner = new Putter(Platform.XBOX, ConfigurationManager.AppSettings["ApiAdminKey"]);
                            cleaner.CleanFiles();
                        }
                        catch (Exception ex)
                        {
                            Log.Error("Erro cleaning XBOX", ex);
                        }
                    });

                    var cleanPs = Task.Run(() =>
                    {
                        try
                        {
                            var cleaner = new Putter(Platform.PS, ConfigurationManager.AppSettings["ApiAdminKey"]);
                            cleaner.CleanFiles();
                        }
                        catch (Exception ex)
                        {
                            Log.Error("Erro cleaning XBOX", ex);
                        }
                    });

                    if (waitForRemote)
                    {
                        Task.WhenAll(cleanXBox, cleanPs);
                    }
                });

                // Calcula cada clã
                var doneCount = 0;
                var timedOut  = false;
                Parallel.For(0, clans.Length, new ParallelOptions {
                    MaxDegreeOfParallelism = 2
                }, i =>
                {
                    if (sw.Elapsed.TotalMinutes > maxRunMinutes)
                    {
                        timedOut = true;
                        return;
                    }

                    var clan = clans[i];

                    Log.InfoFormat("Processando clã {0} de {1}: {2}@{3}...", i + 1, clans.Length, clan.ClanTag,
                                   clan.Plataform);
                    var csw = Stopwatch.StartNew();

                    var cc = CalculateClan(clan, provider, recorder);

                    Log.InfoFormat("Calculado clã {0} de {1}: {2}@{3} em {4:N1}s...",
                                   i + 1, clans.Length, clan.ClanTag, clan.Plataform, csw.Elapsed.TotalSeconds);

                    if (cc != null)
                    {
                        var fsw = Stopwatch.StartNew();
                        switch (cc.Plataform)
                        {
                        case Platform.XBOX:
                            {
                                var fileName = cc.ToFile(resultDirectoryXbox);
                                Log.InfoFormat("Arquivo de resultado escrito em '{0}'", fileName);

                                var putTask = Task.Run(() =>
                                {
                                    try
                                    {
                                        putterXbox.PutClan(fileName);
                                    }
                                    catch (Exception ex)
                                    {
                                        Log.Error($"Error putting XBOX clan file for {cc.ClanTag}", ex);
                                    }
                                });

                                if (waitForRemote)
                                {
                                    putTask.Wait();
                                }
                            }
                            break;

                        case Platform.PS:
                            {
                                var fileName = cc.ToFile(resultDirectoryPs);
                                Log.InfoFormat("Arquivo de resultado escrito em '{0}'", fileName);

                                var putTask = Task.Run(() =>
                                {
                                    try
                                    {
                                        putterPs.PutClan(fileName);
                                    }
                                    catch (Exception ex)
                                    {
                                        Log.Error($"Error putting PS clan file for {cc.ClanTag}", ex);
                                    }
                                });

                                if (waitForRemote)
                                {
                                    putTask.Wait();
                                }
                            }
                            break;

                        case Platform.Virtual:
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                        Log.InfoFormat("Upload do clã {0} de {1}: {2}@{3} em {4:N1}s...",
                                       i + 1, clans.Length, clan.ClanTag, clan.Plataform, fsw.Elapsed.TotalSeconds);
                    }
                    Interlocked.Increment(ref doneCount);
                    Log.InfoFormat("Processado clã {0} de {1}: {2}@{3} em {4:N1}s. {5} totais.",
                                   i + 1, clans.Length, clan.ClanTag, clan.Plataform, csw.Elapsed.TotalSeconds, doneCount);
                });
                var calculationTime = sw.Elapsed;

                // Envia o e-mail de status

                try
                {
                    siteStatusXbox = fetcher.GetSiteDiagnostic(ConfigurationManager.AppSettings["RemoteSiteStatusApi"], ConfigurationManager.AppSettings["ApiAdminKey"]);
                    siteStatusPs   = fetcher.GetSiteDiagnostic(ConfigurationManager.AppSettings["PsRemoteSiteStatusApi"], ConfigurationManager.AppSettings["ApiAdminKey"]);
                }
                catch (Exception ex)
                {
                    Log.Error("Error getting final site diagnostics", ex);

                    // The site is offline...
                    siteStatusXbox = siteStatusPs = new SiteDiagnostic(null);
                }

                Log.Debug("Obtendo informações do BD...");
                var dd = provider.GetDataDiagnostic();
                Log.InfoFormat("Filas: {0} jogadores; {1} clans; {2} cálculos.",
                               dd.PlayersQueueLenght, dd.MembershipQueueLenght, dd.CalculateQueueLenght);
                Log.InfoFormat(
                    "{0} jogadores; {1:N0} por dia; {2:N0} por hora; reais: {3:N0}; {4:N0}; {5:N0}; {6:N0}",
                    dd.TotalPlayers, dd.ScheduledPlayersPerDay, dd.ScheduledPlayersPerHour,
                    dd.AvgPlayersPerHourLastDay, dd.AvgPlayersPerHourLast6Hours, dd.AvgPlayersPerHourLast2Hours,
                    dd.AvgPlayersPerHourLastHour);

                Log.Debug("Enviando e-mail...");
                mailSender.SendStatusMessage(siteStatusXbox, siteStatusPs, dd, minPerDay, maxPerDay,
                                             calculationTime, doneCount, timedOut);

                if (dd.ScheduledPlayersPerDay < minPerDay || dd.ScheduledPlayersPerDay > maxPerDay)
                {
                    recorder.BalanceClanSchedule(minPerDay, maxPerDay);
                }

                Log.DebugFormat(
                    "DateTime.UtcNow: {0:o}; hourToDeleteOldFiles: {1}; daysToKeepOnDelete: {2}",
                    DateTime.UtcNow, hourToDeleteOldFiles, daysToKeepOnDelete);

                // Espera o cálculo e a deleção terminarem
                Task.WaitAll(calculationTask, deleteTask);

                // Tempo extra para os e-mails terminarem de serem enviados
                Thread.Sleep(TimeSpan.FromMinutes(2));

                Log.InfoFormat("CalculateClanStats terminando normalmente para {1} clãs em {0}.", sw.Elapsed,
                               clans.Length);
                return(0);
            }
            catch (Exception ex)
            {
                Log.Fatal(ex);
                Console.WriteLine(ex);
                return(1);
            }
        }