Melhoria do processo de erros definitivos e retry.
Melhoria do processo de estimativa de dados faltantes para atender metodologia da CCEE. Adapatação de modelos para poder inserir dados nulos (faltantes) no banco de dados.
This commit is contained in:
parent
460598c6b5
commit
a8d87addad
@ -1,9 +1,10 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Domain;
|
||||
using Infrastructure;
|
||||
using System.Globalization;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace Application
|
||||
{
|
||||
@ -36,8 +37,6 @@ namespace Application
|
||||
var datas = Enumerable.Range(0, (dataFim - dataIni).Days).Select(i => dataIni.AddDays(i));
|
||||
|
||||
await Parallel.ForEachAsync(perfis, async (perfil, ct) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"{DateTime.Now}: Iniciado ponto {perfil.CodigoSCDE}");
|
||||
if (perfil.Codigo5Minutos == "0" || perfil.Codigo5Minutos == string.Empty)
|
||||
@ -47,21 +46,66 @@ namespace Application
|
||||
return;
|
||||
}
|
||||
|
||||
var existentes = (await _postgresRepository.ObterMedicoesAsync(perfil.CodigoSCDE, dataIni, dataFim, ct));
|
||||
var existentes = await ObterMedicoesComRetry(perfil.CodigoSCDE, dataIni, dataFim, ct, errosPersistentes);
|
||||
|
||||
foreach (DateTime dia in datas)
|
||||
{
|
||||
int tentativas = 0;
|
||||
bool sucesso = false;
|
||||
await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, ct);
|
||||
}
|
||||
Console.WriteLine($"{DateTime.Now}: Finalizado ponto {perfil.CodigoSCDE}");
|
||||
});
|
||||
|
||||
while (tentativas < 5 && !sucesso)
|
||||
if (errosPersistentes.Count > 0)
|
||||
{
|
||||
File.WriteAllLines(caminhoLog, new[] { "Perfil;Ponto;Status;Message" }.Concat(errosPersistentes));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDictionary<(string, double, int), Medicao>> ObterMedicoesComRetry(
|
||||
string codigoSCDE, DateTime dataIni, DateTime dataFim, CancellationToken ct, ConcurrentBag<string> errosPersistentes)
|
||||
{
|
||||
int tentativas = 0;
|
||||
while (tentativas < 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _postgresRepository.ObterMedicoesAsync(codigoSCDE, dataIni, dataFim, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tentativas++;
|
||||
if (tentativas >= 3)
|
||||
{
|
||||
errosPersistentes.Add($"{codigoSCDE};Erro;{ex.Message.Replace("\n", "-n-")}");
|
||||
throw;
|
||||
}
|
||||
int backoff = (int)Math.Pow(2, tentativas) * 1000;
|
||||
Console.WriteLine($"Erro ao acessar Postgres ({ex.Message}), tentativa {tentativas}. Aguardando {backoff / 1000}s...");
|
||||
await Task.Delay(backoff, ct);
|
||||
}
|
||||
}
|
||||
return new Dictionary<(string, double, int), Medicao>();
|
||||
}
|
||||
|
||||
private async Task ProcessarDiaAsync(
|
||||
Perfil perfil,
|
||||
DateTime dia,
|
||||
IDictionary<(string, double, int), Medicao> existentes,
|
||||
Uri endpoint,
|
||||
ConcurrentBag<string> errosPersistentes,
|
||||
CancellationToken ct)
|
||||
{
|
||||
if (perfil.DataDeMigracao > dia)
|
||||
{
|
||||
Console.WriteLine($"Pular {perfil.CodigoSCDE} - {dia.ToShortDateString()} (antes da migração)");
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};Fora da data de migração {perfil.DataDeMigracao} x {dia}");
|
||||
break; // não tentar antes da data de migração
|
||||
return;
|
||||
}
|
||||
|
||||
int tentativas = 0;
|
||||
bool sucesso = false;
|
||||
while (tentativas < 5 && !sucesso)
|
||||
{
|
||||
try
|
||||
{
|
||||
string payload = Xml_requisicao(dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, 1);
|
||||
@ -79,33 +123,24 @@ namespace Application
|
||||
}
|
||||
catch (SoapFaultException ex)
|
||||
{
|
||||
if (ex.ErrorCode == "2003") // limite de requisições
|
||||
if (ex.ErrorCode == "2003")
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var delay = 60000 - (now.Second * 1000 + now.Millisecond);
|
||||
Console.WriteLine($"!! Limite de requisições atingido. Aguardando até {DateTime.Now.AddMilliseconds(delay)}");
|
||||
await Task.Delay(delay, ct); // tentar de novo sem contar como falha
|
||||
await Task.Delay(delay, ct);
|
||||
continue;
|
||||
}
|
||||
if (ex.ErrorCode == "4001") // Dados não encontrados
|
||||
if (ex.ErrorCode == "4001" || ex.ErrorCode == "2001")
|
||||
{
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};SOAP Fault: {ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}");
|
||||
break;
|
||||
}
|
||||
if (ex.ErrorCode == "2001") // Sem acesso
|
||||
{
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};SOAP Fault: {ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await ProcessarXMLAsync(resposta, dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, existentes, ct, 1);
|
||||
|
||||
sucesso = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -117,26 +152,13 @@ namespace Application
|
||||
}
|
||||
else
|
||||
{
|
||||
int backoff = (int)Math.Pow(2.4, tentativas) * 1000; // exponencial
|
||||
int backoff = (int)Math.Pow(2.4, tentativas) * 1000;
|
||||
Console.WriteLine($"Erro na requisição ({ex.Message}), tentativa {tentativas}. Aguardando {backoff / 1000}s...");
|
||||
await Task.Delay(backoff);
|
||||
await Task.Delay(backoff, ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"{DateTime.Now}: Finalizado ponto {perfil.CodigoSCDE}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
});
|
||||
|
||||
if (errosPersistentes.Count > 0)
|
||||
{
|
||||
File.WriteAllLines(caminhoLog, new[] { "Perfil;Ponto;Status;Message" }.Concat(errosPersistentes));
|
||||
}
|
||||
}
|
||||
private async Task ProcessarXMLAsync(
|
||||
string xml,
|
||||
DateTime dia,
|
||||
@ -208,8 +230,7 @@ namespace Application
|
||||
{
|
||||
var logica = g.FirstOrDefault(x => x.Origem == "Inspeção Lógica");
|
||||
return logica ?? g.First();
|
||||
});
|
||||
|
||||
}).ToList();
|
||||
|
||||
var minutosEsperados = new[] { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 };
|
||||
|
||||
@ -218,55 +239,53 @@ namespace Application
|
||||
.ToList();
|
||||
|
||||
var medidasComEstimativa = new List<Medicao>();
|
||||
|
||||
foreach (var grupoHora in medidasPorHora)
|
||||
for (int hora = 0; hora < 24; hora++)
|
||||
{
|
||||
var lista = grupoHora.OrderBy(m => m.Minuto).ToList();
|
||||
var grupoHora = medidasPorHora.Where(h => h.Key.Hora == hora).ToList();
|
||||
var lista = grupoHora.SelectMany(g => g).OrderBy(m => m.Minuto).ToList();
|
||||
var minutosPresentes = lista.Select(m => m.Minuto).ToHashSet();
|
||||
var minutosEsperadosAbsolutos = minutosEsperados.Select(m => m + (60 * grupoHora.Key.Hora)).ToList();
|
||||
var minutosEsperadosAbsolutos = minutosEsperados.Select(m => m + (60 * hora)).ToList();
|
||||
var faltantes = minutosEsperadosAbsolutos.Except(minutosPresentes).OrderBy(m => m).ToList();
|
||||
|
||||
// Use apenas valores reais para interpolação
|
||||
var reais = lista.ToDictionary(m => m.Minuto, m => m);
|
||||
|
||||
// Identifique sequências de minutos faltantes consecutivos
|
||||
var sequencias = new List<List<int>>();
|
||||
List<int> atual = null;
|
||||
int? ultimo = null;
|
||||
foreach (var min in faltantes)
|
||||
{
|
||||
if (atual == null || ultimo == null || min != ultimo + 5)
|
||||
{
|
||||
atual = new List<int>();
|
||||
sequencias.Add(atual);
|
||||
}
|
||||
atual.Add(min);
|
||||
ultimo = min;
|
||||
}
|
||||
|
||||
var estimadas = new List<Medicao>();
|
||||
|
||||
foreach (var seq in sequencias)
|
||||
foreach (var faltante in faltantes)
|
||||
{
|
||||
if (faltantes.Count > 3)
|
||||
{
|
||||
// Se mais de 3 faltantes na hora, não faz estimativa
|
||||
var estimada = new Medicao(
|
||||
ponto + "P",
|
||||
(dia.ToOADate() - dia.ToOADate() % 1),
|
||||
faltante,
|
||||
"Faltante",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
estimadas.Add(estimada);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
int minIni = seq.First();
|
||||
int minFim = seq.Last();
|
||||
|
||||
// Busca anterior real
|
||||
var anterior = reais.Values.Where(m => m.Minuto < minIni).OrderByDescending(m => m.Minuto).FirstOrDefault();
|
||||
var anterior = reais.Values.Where(m => m.Minuto < faltante).OrderByDescending(m => m.Minuto).FirstOrDefault();
|
||||
// Busca posterior real
|
||||
var posterior = reais.Values.Where(m => m.Minuto > minFim).OrderBy(m => m.Minuto).FirstOrDefault();
|
||||
var posterior = reais.Values.Where(m => m.Minuto > faltante).OrderBy(m => m.Minuto).FirstOrDefault();
|
||||
|
||||
foreach (var minFaltante in seq)
|
||||
{
|
||||
var ativaConsumo = Interpolar(anterior?.Minuto, anterior?.AtivaConsumo, posterior?.Minuto, posterior?.AtivaConsumo, minFaltante) ?? 0;
|
||||
var ativaGeracao = Interpolar(anterior?.Minuto, anterior?.AtivaGeracao, posterior?.Minuto, posterior?.AtivaGeracao, minFaltante) ?? 0;
|
||||
var reativaConsumo = Interpolar(anterior?.Minuto, anterior?.ReativaConsumo, posterior?.Minuto, posterior?.ReativaConsumo, minFaltante) ?? 0;
|
||||
var reativaGeracao = Interpolar(anterior?.Minuto, anterior?.ReativaGeracao, posterior?.Minuto, posterior?.ReativaGeracao, minFaltante) ?? 0;
|
||||
var ativaConsumo = Interpolar(anterior?.Minuto, anterior?.AtivaConsumo, posterior?.Minuto, posterior?.AtivaConsumo, faltante) ?? 0;
|
||||
var ativaGeracao = Interpolar(anterior?.Minuto, anterior?.AtivaGeracao, posterior?.Minuto, posterior?.AtivaGeracao, faltante) ?? 0;
|
||||
var reativaConsumo = Interpolar(anterior?.Minuto, anterior?.ReativaConsumo, posterior?.Minuto, posterior?.ReativaConsumo, faltante) ?? 0;
|
||||
var reativaGeracao = Interpolar(anterior?.Minuto, anterior?.ReativaGeracao, posterior?.Minuto, posterior?.ReativaGeracao, faltante) ?? 0;
|
||||
|
||||
var estimada = new Medicao(
|
||||
grupoHora.Key.Ponto,
|
||||
grupoHora.Key.DiaNum,
|
||||
minFaltante,
|
||||
grupoHora.First().Key.Ponto,
|
||||
grupoHora.First().Key.DiaNum,
|
||||
faltante,
|
||||
"Estimado",
|
||||
ativaConsumo,
|
||||
ativaGeracao,
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
double DiaNum,
|
||||
int Minuto,
|
||||
string Origem,
|
||||
double AtivaConsumo,
|
||||
double AtivaGeracao,
|
||||
double ReativaConsumo,
|
||||
double ReativaGeracao
|
||||
double? AtivaConsumo,
|
||||
double? AtivaGeracao,
|
||||
double? ReativaConsumo,
|
||||
double? ReativaGeracao
|
||||
);
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ namespace Infrastructure
|
||||
|
||||
string sql = $"SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 AND Unidade_gerenciada ORDER BY cod_smart_unidade";
|
||||
//string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and (Cliente = 'RMC ALIMENTOS' OR Cliente = 'FERREIRA SUPERMERCADO' OR Cliente = 'VANGUARDA ALIMENTOS') AND Unidade_gerenciada ORDER BY PerfilCCEE";
|
||||
//string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'ALMAVIVA' and Unidade = 'GUARULHOS' AND unidadade_gerenciada ORDER BY PerfilCCEE";
|
||||
//string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'aeroflex' AND Unidade_gerenciada ORDER BY PerfilCCEE";
|
||||
//string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'ABEVÊ' and Unidade = 'ABV LOJA 29 - COXIM' AND Unidade_gerenciada ORDER BY PerfilCCEE";
|
||||
//string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'calvi granitos' AND Unidade_gerenciada ORDER BY PerfilCCEE";
|
||||
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
using var reader = await command.ExecuteReaderAsync(ct);
|
||||
|
||||
@ -36,10 +36,10 @@ namespace Infrastructure
|
||||
reader.GetDouble(1),
|
||||
reader.GetInt32(2),
|
||||
reader.GetString(3),
|
||||
reader.GetDouble(4),
|
||||
reader.GetDouble(5),
|
||||
reader.GetDouble(6),
|
||||
reader.GetDouble(7)
|
||||
reader.IsDBNull(4) ? (double?)null : reader.GetDouble(4),
|
||||
reader.IsDBNull(5) ? (double?)null : reader.GetDouble(5),
|
||||
reader.IsDBNull(6) ? (double?)null : reader.GetDouble(6),
|
||||
reader.IsDBNull(7) ? (double?)null : reader.GetDouble(7)
|
||||
);
|
||||
|
||||
existentes[(medicao.Ponto, medicao.DiaNum, medicao.Minuto)] = medicao;
|
||||
@ -88,10 +88,10 @@ namespace Infrastructure
|
||||
AND minuto = @minuto;");
|
||||
|
||||
cmd.Parameters.AddWithValue("origem", m.Origem);
|
||||
cmd.Parameters.AddWithValue("ativa_consumo", m.AtivaConsumo);
|
||||
cmd.Parameters.AddWithValue("ativa_geracao", m.AtivaGeracao);
|
||||
cmd.Parameters.AddWithValue("reativa_consumo", m.ReativaConsumo);
|
||||
cmd.Parameters.AddWithValue("reativa_geracao", m.ReativaGeracao);
|
||||
cmd.Parameters.AddWithValue("ativa_consumo", NpgsqlDbType.Numeric, m.AtivaConsumo ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("ativa_geracao", NpgsqlDbType.Numeric, m.AtivaGeracao ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("reativa_consumo", NpgsqlDbType.Numeric, m.ReativaConsumo ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("reativa_geracao", NpgsqlDbType.Numeric, m.ReativaGeracao ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("ponto", m.Ponto);
|
||||
cmd.Parameters.AddWithValue("dia_num", m.DiaNum);
|
||||
cmd.Parameters.AddWithValue("minuto", m.Minuto);
|
||||
|
||||
@ -12,8 +12,8 @@ class Program
|
||||
string caminhoLog = $@"\\srv-dados\documentos\Back\Carteira x.x\Codigo\Erros\log_erros_{inicio:MM_dd_HH_mm}.csv";
|
||||
//DateTime dataIni = new DateTime(inicio.Year, inicio.Month, 1);
|
||||
//DateTime dataFim = new DateTime(inicio.Year, inicio.Month, inicio.Day);
|
||||
DateTime dataIni = new DateTime(inicio.Year, 1, 1);
|
||||
DateTime dataFim = new DateTime(inicio.Year, 2, 1);
|
||||
DateTime dataIni = new DateTime(inicio.Year, 10, 1);
|
||||
DateTime dataFim = new DateTime(inicio.Year, 10, 14);
|
||||
|
||||
// Configuração de dependências (pode usar um container DI depois)
|
||||
var postgresRepo = new PostgresRepository(PG_CONN_STRING_PROD);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user