Unificação do arquivo do registro de operações e maior detalhanmento em caso de update.
This commit is contained in:
parent
817d542631
commit
e4c398eef0
@ -30,10 +30,49 @@ namespace Application
|
||||
_rateLimiter?.Dispose();
|
||||
}
|
||||
|
||||
private enum LogType { Info, Error, Operation, UpdateMeasurement }
|
||||
|
||||
private record LogItem(
|
||||
LogType Tipo,
|
||||
string Perfil,
|
||||
string Ponto,
|
||||
double DiaNum,
|
||||
int Minuto,
|
||||
string Status,
|
||||
string Mensagem,
|
||||
int Inseridos,
|
||||
int Atualizados,
|
||||
Medicao? Antes,
|
||||
Medicao? Depois,
|
||||
DateTime Timestamp)
|
||||
{
|
||||
public string ToCsvLine()
|
||||
{
|
||||
static string Esc(string? s) => (s ?? "").Replace(";", ",").Replace("\r", " ").Replace("\n", " ");
|
||||
static string FMed(Medicao? m) =>
|
||||
m is null ? "" : $"{Esc(m.Ponto)}|{m.DiaNum}|{m.Minuto}|{Esc(m.Origem)}|{m.AtivaConsumo?.ToString(CultureInfo.InvariantCulture) ?? ""}|{m.AtivaGeracao?.ToString(CultureInfo.InvariantCulture) ?? ""}|{m.ReativaConsumo?.ToString(CultureInfo.InvariantCulture) ?? ""}|{m.ReativaGeracao?.ToString(CultureInfo.InvariantCulture) ?? ""}";
|
||||
|
||||
return string.Join(";", new[]
|
||||
{
|
||||
Tipo.ToString(),
|
||||
Esc(Perfil),
|
||||
Esc(Ponto),
|
||||
DiaNum.ToString(CultureInfo.InvariantCulture),
|
||||
Minuto.ToString(),
|
||||
Esc(Status),
|
||||
Esc(Mensagem),
|
||||
Inseridos.ToString(),
|
||||
Atualizados.ToString(),
|
||||
FMed(Antes),
|
||||
FMed(Depois),
|
||||
Timestamp.ToString("o")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(DateTime dataIni, DateTime dataFim, string caminhoLog, CancellationToken ct)
|
||||
{
|
||||
var errosPersistentes = new ConcurrentBag<string>();
|
||||
var operacoesLog = new ConcurrentBag<string>();
|
||||
var logs = new ConcurrentBag<LogItem>();
|
||||
var perfis = (await _accessRepository.ObterPerfisAsync(ct)).ToList();
|
||||
|
||||
var endpoint = new Uri("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2");
|
||||
@ -41,42 +80,39 @@ namespace Application
|
||||
|
||||
await Parallel.ForEachAsync(perfis, async (perfil, ctPerfil) =>
|
||||
{
|
||||
//Console.WriteLine($"{DateTime.Now}: Iniciado ponto {perfil.CodigoSCDE}");
|
||||
if (perfil.Codigo5Minutos == "0" || perfil.Codigo5Minutos == string.Empty || perfil.Codigo5Minutos == null)
|
||||
{
|
||||
Console.WriteLine($"Pular {perfil.CodigoSCDE} - (cod 5 min pendente)");
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};{dataIni.ToOADate()};ERRO;cod_5min pendente");
|
||||
logs.Add(new LogItem(LogType.Error, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dataIni.ToOADate(), 0, "ERRO", "cod_5min pendente", 0, 0, null, null, DateTime.UtcNow));
|
||||
return;
|
||||
}
|
||||
|
||||
var existentes = await ObterMedicoesComRetry(perfil.CodigoSCDE, dataIni, dataFim, ctPerfil, errosPersistentes);
|
||||
var existentes = await ObterMedicoesComRetry(perfil.CodigoSCDE, dataIni, dataFim, ctPerfil, logs);
|
||||
|
||||
// Paraleliza os dias deste perfil; o semáforo limita as requisições simultâneas
|
||||
await Parallel.ForEachAsync(datas, ctPerfil, async (dia, ctDia) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, operacoesLog, ctDia);
|
||||
await ProcessarDiaAsync(perfil, dia, existentes, endpoint, logs, ctDia);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
operacoesLog.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};{dia.ToOADate()};ERRO;{ex.Message.Replace("\n", "-n-")}");
|
||||
logs.Add(new LogItem(LogType.Error, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dia.ToOADate(), 0, "ERRO", ex.Message.Replace("\n", "-n-"), 0, 0, null, null, DateTime.UtcNow));
|
||||
}
|
||||
});
|
||||
|
||||
//Console.WriteLine($"{DateTime.Now}: Finalizado ponto {perfil.CodigoSCDE}");
|
||||
});
|
||||
|
||||
// Cabeçalho do log
|
||||
var linhasLog = new List<string> { "Perfil;Ponto;DiaNum;Status;Mensagem;Inseridos;Atualizados" };
|
||||
linhasLog.AddRange(operacoesLog);
|
||||
linhasLog.AddRange(errosPersistentes);
|
||||
var linhas = new List<string>
|
||||
{
|
||||
"Tipo;Perfil;Ponto;DiaNum;Minuto;Status;Mensagem;Inseridos;Atualizados;Antes;Depois;Timestamp"
|
||||
};
|
||||
linhas.AddRange(logs.Select(l => l.ToCsvLine()));
|
||||
|
||||
File.WriteAllLines(caminhoLog, linhasLog);
|
||||
File.WriteAllLines(caminhoLog, linhas);
|
||||
}
|
||||
|
||||
private async Task<IDictionary<(string, double, int), Medicao>> ObterMedicoesComRetry(
|
||||
string codigoSCDE, DateTime dataIni, DateTime dataFim, CancellationToken ct, ConcurrentBag<string> errosPersistentes)
|
||||
string codigoSCDE, DateTime dataIni, DateTime dataFim, CancellationToken ct, ConcurrentBag<LogItem> logs)
|
||||
{
|
||||
int tentativas = 0;
|
||||
while (tentativas < 3)
|
||||
@ -90,7 +126,7 @@ namespace Application
|
||||
tentativas++;
|
||||
if (tentativas >= 3)
|
||||
{
|
||||
errosPersistentes.Add($";{codigoSCDE};{dataIni.ToOADate()};Erro;{ex.Message.Replace("\n", "-n-")}");
|
||||
logs.Add(new LogItem(LogType.Error, "", codigoSCDE, dataIni.ToOADate(), 0, "Erro", ex.Message.Replace("\n", "-n-"), 0, 0, null, null, DateTime.UtcNow));
|
||||
throw;
|
||||
}
|
||||
int backoff = (int)Math.Pow(2, tentativas) * 1000;
|
||||
@ -104,21 +140,18 @@ namespace Application
|
||||
private async Task ProcessarDiaAsync(
|
||||
Perfil perfil,
|
||||
DateTime dia,
|
||||
IDictionary<(string, double, int),
|
||||
Medicao> existentes,
|
||||
IDictionary<(string, double, int), Medicao> existentes,
|
||||
Uri endpoint,
|
||||
ConcurrentBag<string> errosPersistentes,
|
||||
ConcurrentBag<string> operacoesLog,
|
||||
ConcurrentBag<LogItem> logs,
|
||||
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}");
|
||||
logs.Add(new LogItem(LogType.Info, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dia.ToOADate(), 0, "Fora da data de migração", $"Data de migração {perfil.DataDeMigracao} x {dia}", 0, 0, null, null, DateTime.UtcNow));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Acumulador de medidas (todas as páginas)
|
||||
var acumulador = new List<XElement>();
|
||||
int paginaAtual = 1;
|
||||
@ -168,7 +201,7 @@ namespace Application
|
||||
if (ex.ErrorCode == "4001" || ex.ErrorCode == "2001")
|
||||
{
|
||||
// erro persistente, registra e interrompe processamento deste dia/ponto
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};{dia.ToOADate()};SOAP Fault: {ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}");
|
||||
logs.Add(new LogItem(LogType.Error, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dia.ToOADate(), 0, "SOAP Fault", $"{ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}", 0, 0, null, null, DateTime.UtcNow));
|
||||
return;
|
||||
}
|
||||
throw;
|
||||
@ -199,7 +232,7 @@ namespace Application
|
||||
tentativas++;
|
||||
if (tentativas >= 5)
|
||||
{
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};{dia.ToOADate()};Erro;{ex.Message.Replace("\n", "-n-")}");
|
||||
logs.Add(new LogItem(LogType.Error, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dia.ToOADate(), 0, "Erro", ex.Message.Replace("\n", "-n-"), 0, 0, null, null, DateTime.UtcNow));
|
||||
// aborta o processamento do dia após falhas repetidas na mesma página
|
||||
return;
|
||||
}
|
||||
@ -216,11 +249,11 @@ namespace Application
|
||||
// ao final de todas as páginas, processa o XML acumulado
|
||||
try
|
||||
{
|
||||
await ProcessarXMLAsync(acumulador, dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, existentes, ct, operacoesLog);
|
||||
await ProcessarXMLAsync(acumulador, dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, existentes, ct, logs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};{dia.ToOADate()};Erro;{ex.Message.Replace("\n", "-n-")}");
|
||||
logs.Add(new LogItem(LogType.Error, perfil.Codigo5Minutos ?? "", perfil.CodigoSCDE ?? "", dia.ToOADate(), 0, "Erro", ex.Message.Replace("\n", "-n-"), 0, 0, null, null, DateTime.UtcNow));
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,9 +264,10 @@ namespace Application
|
||||
string ponto,
|
||||
IDictionary<(string, double, int), Medicao> existentes,
|
||||
CancellationToken ct,
|
||||
ConcurrentBag<string>? operacoesLog = null)
|
||||
ConcurrentBag<LogItem>? logs = null)
|
||||
{
|
||||
// Processa as medidas já acumuladas (antes chamadas faziam paginação)
|
||||
logs ??= new ConcurrentBag<LogItem>();
|
||||
|
||||
XNamespace ns = "http://xmlns.energia.org.br/BO/v2";
|
||||
|
||||
var medidasProcessadas = acumulador
|
||||
@ -305,7 +339,7 @@ namespace Application
|
||||
{
|
||||
if (faltantes.Count > 3)
|
||||
{
|
||||
// Se mais de 3 faltantes na hora, n<EFBFBD>o faz estimativa
|
||||
// Se mais de 3 faltantes na hora, não faz estimativa
|
||||
var estimada = new Medicao(
|
||||
ponto + "P",
|
||||
(dia.ToOADate() - dia.ToOADate() % 1),
|
||||
@ -365,6 +399,20 @@ namespace Application
|
||||
Math.Round(existente.ReativaGeracao ?? 0, 10) != Math.Round(m.ReativaGeracao ?? 0, 10))
|
||||
{
|
||||
alterados.Add(m);
|
||||
// log detalhado por medição alterada
|
||||
logs.Add(new LogItem(
|
||||
LogType.UpdateMeasurement,
|
||||
perfil,
|
||||
m.Ponto,
|
||||
m.DiaNum,
|
||||
m.Minuto,
|
||||
"ALTERADO",
|
||||
"Medição alterada (antes x depois)",
|
||||
0,
|
||||
1,
|
||||
existente,
|
||||
m,
|
||||
DateTime.UtcNow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,19 +421,19 @@ namespace Application
|
||||
{
|
||||
await _postgresRepository.InserirMedicoesAsync(novos, ct);
|
||||
Console.WriteLine($"Ponto {ponto}. Dia {dia:dd/MM/yyyy}. {novos.Count:D3} registros inseridos.");
|
||||
operacoesLog?.Add($"{perfil};{ponto};{dia.ToOADate()};OK;Novos;{novos.Count};0");
|
||||
logs.Add(new LogItem(LogType.Operation, perfil, ponto, dia.ToOADate(), 0, "OK", "Novos", novos.Count, 0, null, null, DateTime.UtcNow));
|
||||
}
|
||||
|
||||
if (alterados.Any())
|
||||
{
|
||||
await _postgresRepository.AtualizarMedicoesAsync(alterados, ct);
|
||||
Console.WriteLine($"Ponto {ponto}. Dia {dia:dd/MM/yyyy}. {alterados.Count:D3} registros atualizados.");
|
||||
operacoesLog?.Add($"{perfil};{ponto};{dia.ToOADate()};OK;Atualizados;0;{alterados.Count}");
|
||||
// logs.Add(new LogItem(LogType.Operation, perfil, ponto, dia.ToOADate(), 0, "OK", "Atualizados", 0, alterados.Count, null, null, DateTime.UtcNow));
|
||||
}
|
||||
if (!novos.Any() && !alterados.Any())
|
||||
{
|
||||
Console.WriteLine($"Ponto {ponto}. Dia {dia:dd/MM/yyyy}. 000 registros alterados.");
|
||||
operacoesLog?.Add($"{perfil};{ponto};{dia.ToOADate()};OK;Sem alterações;0;0");
|
||||
logs.Add(new LogItem(LogType.Info, perfil, ponto, dia.ToOADate(), 0, "OK", "Sem alterações", 0, 0, null, null, DateTime.UtcNow));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,9 +12,9 @@ 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);
|
||||
//junho finalizado
|
||||
DateTime dataIni = new DateTime(inicio.Year, 11, 01);
|
||||
DateTime dataFim = new DateTime(inicio.Year, 11, 14);
|
||||
//agosto finalizado
|
||||
DateTime dataIni = new DateTime(inicio.Year, 09, 01);
|
||||
DateTime dataFim = new DateTime(inicio.Year, 10, 01);
|
||||
|
||||
// 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