Código funcional.
Separação em camadas. Utilização de estimativa para horas faltantes (entre 9 e 11 registros).
This commit is contained in:
parent
1e814e9c34
commit
460598c6b5
@ -1,10 +0,0 @@
|
|||||||
namespace App
|
|
||||||
{
|
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Hello, World!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,4 +6,9 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
367
Application/ProcessarMedicoesUseCase.cs
Normal file
367
Application/ProcessarMedicoesUseCase.cs
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Domain;
|
||||||
|
using Infrastructure;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Application
|
||||||
|
{
|
||||||
|
public class ProcessarMedicoesUseCase
|
||||||
|
{
|
||||||
|
private readonly IPostgresRepository _postgresRepository;
|
||||||
|
private readonly IAccessRepository _accessRepository;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly RateLimiter _rateLimiter;
|
||||||
|
|
||||||
|
public ProcessarMedicoesUseCase(
|
||||||
|
IPostgresRepository postgresRepository,
|
||||||
|
IAccessRepository accessRepository,
|
||||||
|
HttpClient httpClient,
|
||||||
|
RateLimiter rateLimiter)
|
||||||
|
{
|
||||||
|
_postgresRepository = postgresRepository;
|
||||||
|
_accessRepository = accessRepository;
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_rateLimiter = rateLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(DateTime dataIni, DateTime dataFim, string caminhoLog, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var errosPersistentes = new ConcurrentBag<string>();
|
||||||
|
var perfis = (await _accessRepository.ObterPerfisAsync(ct)).ToList();
|
||||||
|
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("SOAPAction", "listarMedidaCincoMinutos");
|
||||||
|
var endpoint = new Uri("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Pular {perfil.CodigoSCDE} - (cod 5 min pendente)");
|
||||||
|
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE}; cod_5min pendente");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existentes = (await _postgresRepository.ObterMedicoesAsync(perfil.CodigoSCDE, dataIni, dataFim, ct));
|
||||||
|
|
||||||
|
foreach (DateTime dia in datas)
|
||||||
|
{
|
||||||
|
int tentativas = 0;
|
||||||
|
bool sucesso = false;
|
||||||
|
|
||||||
|
while (tentativas < 5 && !sucesso)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string payload = Xml_requisicao(dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, 1);
|
||||||
|
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
|
||||||
|
|
||||||
|
await _rateLimiter.WaitAsync(ct);
|
||||||
|
using var response = await _httpClient.PostAsync(endpoint, conteudo, ct);
|
||||||
|
string resposta = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if ((int)response.StatusCode >= 400)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SoapHelper.VerificarRespostaSOAP(resposta);
|
||||||
|
}
|
||||||
|
catch (SoapFaultException ex)
|
||||||
|
{
|
||||||
|
if (ex.ErrorCode == "2003") // limite de requisições
|
||||||
|
{
|
||||||
|
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
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ex.ErrorCode == "4001") // Dados não encontrados
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
tentativas++;
|
||||||
|
if (tentativas >= 5)
|
||||||
|
{
|
||||||
|
errosPersistentes.Add($"{perfil.Codigo5Minutos};{perfil.CodigoSCDE};Erro;{ex.Message.Replace("\n", "-n-")}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int backoff = (int)Math.Pow(2.4, tentativas) * 1000; // exponencial
|
||||||
|
Console.WriteLine($"Erro na requisição ({ex.Message}), tentativa {tentativas}. Aguardando {backoff / 1000}s...");
|
||||||
|
await Task.Delay(backoff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
string perfil,
|
||||||
|
string ponto,
|
||||||
|
IDictionary<(string, double, int), Medicao> existentes,
|
||||||
|
CancellationToken ct,
|
||||||
|
int paginaAtual = 1,
|
||||||
|
List<XElement>? acumulador = null,
|
||||||
|
int totalPaginas = 1)
|
||||||
|
{
|
||||||
|
var doc = XDocument.Parse(xml);
|
||||||
|
XNamespace ns = "http://xmlns.energia.org.br/BO/v2";
|
||||||
|
|
||||||
|
int.TryParse(doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "totalPaginas")?.Value, out totalPaginas);
|
||||||
|
int.TryParse(doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "numero")?.Value, out paginaAtual);
|
||||||
|
|
||||||
|
var medidas = doc.Descendants(ns + "medida")
|
||||||
|
.Where(x => (string)x.Element(ns + "tipoEnergia") == "L");
|
||||||
|
|
||||||
|
acumulador ??= new List<XElement>();
|
||||||
|
acumulador.AddRange(medidas);
|
||||||
|
|
||||||
|
if (paginaAtual < totalPaginas)
|
||||||
|
{
|
||||||
|
// Requisita próxima página
|
||||||
|
string payload = Xml_requisicao(dia, perfil, ponto, paginaAtual + 1);
|
||||||
|
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
|
||||||
|
await _rateLimiter.WaitAsync(ct);
|
||||||
|
using var resp = await _httpClient.PostAsync("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2", conteudo, ct);
|
||||||
|
string proxXml = await resp.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
await ProcessarXMLAsync(proxXml, dia, perfil, ponto, existentes, ct, paginaAtual + 1, acumulador, totalPaginas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var medidasProcessadas = acumulador
|
||||||
|
.Select(m =>
|
||||||
|
{
|
||||||
|
string origem = m.Element(ns + "coletaMedicao")?.Element(ns + "tipo")?.Element(ns + "nome")?.Value ?? "";
|
||||||
|
string pontoMed = m.Element(ns + "medidor")?.Element(ns + "codigo")?.Value ?? "";
|
||||||
|
DateTime data = DateTime.Parse(m.Element(ns + "data")?.Value ?? "");
|
||||||
|
double diaNum = (data.ToOADate() - data.ToOADate() % 1);
|
||||||
|
int minuto = data.Hour * 60 + data.Minute;
|
||||||
|
if (minuto == 0) { minuto = 1440; diaNum--; }
|
||||||
|
|
||||||
|
double.TryParse(m.Element(ns + "energiaAtiva")?.Element(ns + "consumo")?.Element(ns + "valor")?.Value,
|
||||||
|
NumberStyles.Any, CultureInfo.InvariantCulture, out double ativa_c);
|
||||||
|
double.TryParse(m.Element(ns + "energiaAtiva")?.Element(ns + "geracao")?.Element(ns + "valor")?.Value,
|
||||||
|
NumberStyles.Any, CultureInfo.InvariantCulture, out double ativa_g);
|
||||||
|
double.TryParse(m.Element(ns + "energiaReativa")?.Element(ns + "consumo")?.Element(ns + "valor")?.Value,
|
||||||
|
NumberStyles.Any, CultureInfo.InvariantCulture, out double reat_c);
|
||||||
|
double.TryParse(m.Element(ns + "energiaReativa")?.Element(ns + "geracao")?.Element(ns + "valor")?.Value,
|
||||||
|
NumberStyles.Any, CultureInfo.InvariantCulture, out double reat_g);
|
||||||
|
|
||||||
|
return new Medicao(
|
||||||
|
pontoMed,
|
||||||
|
diaNum,
|
||||||
|
minuto,
|
||||||
|
origem,
|
||||||
|
ativa_c,
|
||||||
|
ativa_g,
|
||||||
|
reat_c,
|
||||||
|
reat_g
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.GroupBy(x => new { x.Ponto, x.DiaNum, x.Minuto })
|
||||||
|
.Select(g =>
|
||||||
|
{
|
||||||
|
var logica = g.FirstOrDefault(x => x.Origem == "Inspeção Lógica");
|
||||||
|
return logica ?? g.First();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var minutosEsperados = new[] { 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 };
|
||||||
|
|
||||||
|
var medidasPorHora = medidasProcessadas
|
||||||
|
.GroupBy(m => new { m.Ponto, m.DiaNum, Hora = (m.Minuto - 5) / 60 })
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var medidasComEstimativa = new List<Medicao>();
|
||||||
|
|
||||||
|
foreach (var grupoHora in medidasPorHora)
|
||||||
|
{
|
||||||
|
var lista = grupoHora.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 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)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
// Busca posterior real
|
||||||
|
var posterior = reais.Values.Where(m => m.Minuto > minFim).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 estimada = new Medicao(
|
||||||
|
grupoHora.Key.Ponto,
|
||||||
|
grupoHora.Key.DiaNum,
|
||||||
|
minFaltante,
|
||||||
|
"Estimado",
|
||||||
|
ativaConsumo,
|
||||||
|
ativaGeracao,
|
||||||
|
reativaConsumo,
|
||||||
|
reativaGeracao
|
||||||
|
);
|
||||||
|
estimadas.Add(estimada);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adiciona todos (originais + estimados) ao resultado final
|
||||||
|
medidasComEstimativa.AddRange(lista);
|
||||||
|
medidasComEstimativa.AddRange(estimadas);
|
||||||
|
}
|
||||||
|
|
||||||
|
var novos = new List<Medicao>();
|
||||||
|
var alterados = new List<Medicao>();
|
||||||
|
|
||||||
|
foreach (var m in medidasComEstimativa)
|
||||||
|
{
|
||||||
|
var chave = (m.Ponto, m.DiaNum, m.Minuto);
|
||||||
|
|
||||||
|
if (!existentes.TryGetValue(chave, out var existente))
|
||||||
|
{
|
||||||
|
novos.Add(m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (existente.Origem != m.Origem ||
|
||||||
|
existente.AtivaConsumo != m.AtivaConsumo ||
|
||||||
|
existente.AtivaGeracao != m.AtivaGeracao ||
|
||||||
|
existente.ReativaConsumo != m.ReativaConsumo ||
|
||||||
|
existente.ReativaGeracao != m.ReativaGeracao)
|
||||||
|
{
|
||||||
|
alterados.Add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (novos.Any())
|
||||||
|
{
|
||||||
|
await _postgresRepository.InserirMedicoesAsync(novos, ct);
|
||||||
|
Console.WriteLine($"Inserido {novos.Count} registros. Ponto {ponto}. Dia {dia}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alterados.Any())
|
||||||
|
{
|
||||||
|
await _postgresRepository.AtualizarMedicoesAsync(alterados, ct);
|
||||||
|
Console.WriteLine($"Atualizado {alterados.Count} registros. Ponto {ponto}. Dia {dia}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static string Xml_requisicao(DateTime data_req, string perfil, string cod_ponto, int pagina)
|
||||||
|
{
|
||||||
|
string cam_ent, tex_req, sdat_req;
|
||||||
|
cam_ent = @"X:\Back\Plataforma de Integração CCEE\RequestPaginate.txt";
|
||||||
|
cod_ponto += "P";
|
||||||
|
sdat_req = data_req.ToString("yyyy-MM-ddT00:00:00");
|
||||||
|
tex_req = File.ReadAllText(cam_ent);
|
||||||
|
tex_req = tex_req.Replace("DATAALTERADA", sdat_req);
|
||||||
|
tex_req = tex_req.Replace("PONTOMEDICAO", cod_ponto);
|
||||||
|
tex_req = tex_req.Replace("CODPERFIL", perfil);
|
||||||
|
tex_req = tex_req.Replace("PAGNUM", pagina.ToString());
|
||||||
|
return tex_req;
|
||||||
|
}
|
||||||
|
private static double? Interpolar(
|
||||||
|
double? xAnterior, double? yAnterior,
|
||||||
|
double? xPosterior, double? yPosterior,
|
||||||
|
double xProcurado)
|
||||||
|
{
|
||||||
|
if (xAnterior.HasValue && yAnterior.HasValue &&
|
||||||
|
(!xPosterior.HasValue || !yPosterior.HasValue))
|
||||||
|
{
|
||||||
|
return yAnterior.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xPosterior.HasValue && yPosterior.HasValue &&
|
||||||
|
(!xAnterior.HasValue || !yAnterior.HasValue))
|
||||||
|
{
|
||||||
|
return yPosterior.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xAnterior.HasValue && yAnterior.HasValue &&
|
||||||
|
xPosterior.HasValue && yPosterior.HasValue)
|
||||||
|
{
|
||||||
|
if (xPosterior.Value == xAnterior.Value)
|
||||||
|
throw new ArgumentException("xAnterior e xPosterior não podem ser iguais (divisão por zero).");
|
||||||
|
|
||||||
|
double yProcurado = yAnterior.Value +
|
||||||
|
((yPosterior.Value - yAnterior.Value) / (xPosterior.Value - xAnterior.Value)) *
|
||||||
|
(xProcurado - xAnterior.Value);
|
||||||
|
|
||||||
|
return yProcurado;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Data
|
|
||||||
{
|
|
||||||
internal class AccessRepository
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Data
|
|
||||||
{
|
|
||||||
internal class PostgresRepository
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
Domain/IAccessRepository.cs
Normal file
7
Domain/IAccessRepository.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Domain
|
||||||
|
{
|
||||||
|
public interface IAccessRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<Perfil>> ObterPerfisAsync(CancellationToken ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Domain/IPostgresRepository.cs
Normal file
12
Domain/IPostgresRepository.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Domain
|
||||||
|
{
|
||||||
|
public interface IPostgresRepository
|
||||||
|
{
|
||||||
|
Task<IDictionary<(string Ponto, double DiaNum, int Minuto), Medicao>>
|
||||||
|
ObterMedicoesAsync(string codigoSCDE, DateTime dataIni, DateTime dataFim, CancellationToken ct);
|
||||||
|
|
||||||
|
Task InserirMedicoesAsync(IEnumerable<Medicao> medicoes, CancellationToken ct);
|
||||||
|
|
||||||
|
Task AtualizarMedicoesAsync(IEnumerable<Medicao> medicoes, CancellationToken ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
using System;
|
namespace Domain
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Domain
|
|
||||||
{
|
{
|
||||||
internal class Medicao
|
public record Medicao(
|
||||||
{
|
string Ponto,
|
||||||
}
|
double DiaNum,
|
||||||
|
int Minuto,
|
||||||
|
string Origem,
|
||||||
|
double AtivaConsumo,
|
||||||
|
double AtivaGeracao,
|
||||||
|
double ReativaConsumo,
|
||||||
|
double ReativaGeracao
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,4 @@
|
|||||||
using System;
|
namespace Domain
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Domain
|
|
||||||
{
|
{
|
||||||
internal class Perfil
|
public record Perfil(string? Codigo5Minutos, string? CodigoSCDE, DateTime? DataDeMigracao);
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
41
Infrastructure/AccessRepository.cs
Normal file
41
Infrastructure/AccessRepository.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System.Data.OleDb;
|
||||||
|
using Domain;
|
||||||
|
|
||||||
|
namespace Infrastructure
|
||||||
|
{
|
||||||
|
public class AccessRepository : IAccessRepository
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
|
public AccessRepository(string connectionString)
|
||||||
|
{
|
||||||
|
_connectionString = connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Perfil>> ObterPerfisAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
var perfis = new List<Perfil>();
|
||||||
|
|
||||||
|
using var connection = new OleDbConnection(_connectionString);
|
||||||
|
await connection.OpenAsync(ct);
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
using var command = new OleDbCommand(sql, connection);
|
||||||
|
using var reader = await command.ExecuteReaderAsync(ct);
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(ct))
|
||||||
|
{
|
||||||
|
var cod5min = reader.IsDBNull(0) ? null : reader.GetString(0);
|
||||||
|
var codigoSCDE = reader.IsDBNull(1) ? null : reader.GetString(1);
|
||||||
|
var dataMigracao = reader.IsDBNull(2) ? (DateTime?)null : reader.GetDateTime(2);
|
||||||
|
perfis.Add(new Perfil(cod5min, codigoSCDE, dataMigracao));
|
||||||
|
}
|
||||||
|
|
||||||
|
return perfis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Infrastructure/Infrastructure.csproj
Normal file
18
Infrastructure/Infrastructure.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||||
|
<PackageReference Include="System.Data.OleDb" Version="9.0.9" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
105
Infrastructure/PostgresRepository.cs
Normal file
105
Infrastructure/PostgresRepository.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
using Domain;
|
||||||
|
using Npgsql;
|
||||||
|
using NpgsqlTypes;
|
||||||
|
|
||||||
|
namespace Infrastructure
|
||||||
|
{
|
||||||
|
public class PostgresRepository : IPostgresRepository
|
||||||
|
{
|
||||||
|
private readonly NpgsqlDataSource _dataSource;
|
||||||
|
|
||||||
|
public PostgresRepository(string connectionString)
|
||||||
|
{
|
||||||
|
_dataSource = NpgsqlDataSource.Create(connectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IDictionary<(string, double, int), Medicao>>
|
||||||
|
ObterMedicoesAsync(string codigoSCDE, DateTime dataIni, DateTime dataFim, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var existentes = new Dictionary<(string, double, int), Medicao>();
|
||||||
|
|
||||||
|
string sql = @"
|
||||||
|
SELECT ponto, dia_num, minuto, origem, ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao
|
||||||
|
FROM med_5min
|
||||||
|
WHERE ponto = @ponto AND dia_num >= @data_ini AND dia_num <= @data_fim";
|
||||||
|
|
||||||
|
await using var command = _dataSource.CreateCommand(sql);
|
||||||
|
command.Parameters.AddWithValue("ponto", codigoSCDE + "P");
|
||||||
|
command.Parameters.AddWithValue("data_ini", dataIni.ToOADate());
|
||||||
|
command.Parameters.AddWithValue("data_fim", dataFim.ToOADate());
|
||||||
|
|
||||||
|
await using var reader = await command.ExecuteReaderAsync(ct);
|
||||||
|
while (await reader.ReadAsync(ct))
|
||||||
|
{
|
||||||
|
var medicao = new Medicao(
|
||||||
|
reader.GetString(0),
|
||||||
|
reader.GetDouble(1),
|
||||||
|
reader.GetInt32(2),
|
||||||
|
reader.GetString(3),
|
||||||
|
reader.GetDouble(4),
|
||||||
|
reader.GetDouble(5),
|
||||||
|
reader.GetDouble(6),
|
||||||
|
reader.GetDouble(7)
|
||||||
|
);
|
||||||
|
|
||||||
|
existentes[(medicao.Ponto, medicao.DiaNum, medicao.Minuto)] = medicao;
|
||||||
|
}
|
||||||
|
|
||||||
|
return existentes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InserirMedicoesAsync(IEnumerable<Medicao> medicoes, CancellationToken ct)
|
||||||
|
{
|
||||||
|
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||||
|
using var writer = connection.BeginBinaryImport(
|
||||||
|
"COPY med_5min (origem, dia_num, minuto, ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao, ponto) FROM STDIN (FORMAT BINARY)");
|
||||||
|
|
||||||
|
foreach (var m in medicoes)
|
||||||
|
{
|
||||||
|
writer.StartRow();
|
||||||
|
writer.Write(m.Origem);
|
||||||
|
writer.Write(m.DiaNum, NpgsqlDbType.Numeric);
|
||||||
|
writer.Write(m.Minuto, NpgsqlDbType.Integer);
|
||||||
|
writer.Write(m.AtivaConsumo, NpgsqlDbType.Numeric);
|
||||||
|
writer.Write(m.AtivaGeracao, NpgsqlDbType.Numeric);
|
||||||
|
writer.Write(m.ReativaConsumo, NpgsqlDbType.Numeric);
|
||||||
|
writer.Write(m.ReativaGeracao, NpgsqlDbType.Numeric);
|
||||||
|
writer.Write(m.Ponto);
|
||||||
|
}
|
||||||
|
|
||||||
|
await writer.CompleteAsync();
|
||||||
|
}
|
||||||
|
public async Task AtualizarMedicoesAsync(IEnumerable<Medicao> medicoes, CancellationToken ct)
|
||||||
|
{
|
||||||
|
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||||
|
using var batch = new NpgsqlBatch(connection);
|
||||||
|
|
||||||
|
foreach (var m in medicoes)
|
||||||
|
{
|
||||||
|
var cmd = new NpgsqlBatchCommand(@"
|
||||||
|
UPDATE med_5min
|
||||||
|
SET origem = @origem,
|
||||||
|
ativa_consumo = @ativa_consumo,
|
||||||
|
ativa_geracao = @ativa_geracao,
|
||||||
|
reativa_consumo = @reativa_consumo,
|
||||||
|
reativa_geracao = @reativa_geracao
|
||||||
|
WHERE ponto = @ponto
|
||||||
|
AND dia_num = @dia_num
|
||||||
|
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("ponto", m.Ponto);
|
||||||
|
cmd.Parameters.AddWithValue("dia_num", m.DiaNum);
|
||||||
|
cmd.Parameters.AddWithValue("minuto", m.Minuto);
|
||||||
|
|
||||||
|
batch.BatchCommands.Add(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
await batch.ExecuteNonQueryAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace Infrastructure
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Infra
|
|
||||||
{
|
{
|
||||||
public class RateLimiter
|
public class RateLimiter
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,4 @@
|
|||||||
using System;
|
namespace Infrastructure
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Infra;
|
|
||||||
|
|
||||||
namespace Infra
|
|
||||||
{
|
{
|
||||||
public class SoapFaultException : Exception
|
public class SoapFaultException : Exception
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
using System;
|
using System.Xml.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Infra
|
namespace Infrastructure
|
||||||
{
|
{
|
||||||
public static class SoapHelper
|
public static class SoapHelper
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,15 +5,13 @@ VisualStudioVersion = 17.1.32319.34
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App_old", "App_old.csproj", "{48DFCC70-0352-4394-9821-2B243EB389AB}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App_old", "App_old.csproj", "{48DFCC70-0352-4394-9821-2B243EB389AB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infra", "Infrastructure\Infra.csproj", "{0910ED06-C154-41FF-B556-ADBA53A10427}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{0910ED06-C154-41FF-B556-ADBA53A10427}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data", "Data\Data.csproj", "{4B7EE8CE-E452-4B31-9B21-64113B6C48C8}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{2B2323E4-C409-4432-AA45-54C1642FDA88}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{8B6C1411-58F1-4701-9736-D8A87F2FBB64}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{2B43C133-911B-4F3D-9DAB-1DA342D3822B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Presentation", "Presentation\Presentation.csproj", "{979738F9-0985-45D4-8E9C-2DE972084FE9}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -29,22 +27,18 @@ Global
|
|||||||
{0910ED06-C154-41FF-B556-ADBA53A10427}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0910ED06-C154-41FF-B556-ADBA53A10427}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0910ED06-C154-41FF-B556-ADBA53A10427}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0910ED06-C154-41FF-B556-ADBA53A10427}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0910ED06-C154-41FF-B556-ADBA53A10427}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0910ED06-C154-41FF-B556-ADBA53A10427}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4B7EE8CE-E452-4B31-9B21-64113B6C48C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{4B7EE8CE-E452-4B31-9B21-64113B6C48C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{4B7EE8CE-E452-4B31-9B21-64113B6C48C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{4B7EE8CE-E452-4B31-9B21-64113B6C48C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
{38CE64C7-8F03-4622-8DD5-5BA19F66E7AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{2B2323E4-C409-4432-AA45-54C1642FDA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{8B6C1411-58F1-4701-9736-D8A87F2FBB64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2B2323E4-C409-4432-AA45-54C1642FDA88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8B6C1411-58F1-4701-9736-D8A87F2FBB64}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2B2323E4-C409-4432-AA45-54C1642FDA88}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8B6C1411-58F1-4701-9736-D8A87F2FBB64}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2B2323E4-C409-4432-AA45-54C1642FDA88}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8B6C1411-58F1-4701-9736-D8A87F2FBB64}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{2B43C133-911B-4F3D-9DAB-1DA342D3822B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{979738F9-0985-45D4-8E9C-2DE972084FE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2B43C133-911B-4F3D-9DAB-1DA342D3822B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{979738F9-0985-45D4-8E9C-2DE972084FE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2B43C133-911B-4F3D-9DAB-1DA342D3822B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{979738F9-0985-45D4-8E9C-2DE972084FE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2B43C133-911B-4F3D-9DAB-1DA342D3822B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{979738F9-0985-45D4-8E9C-2DE972084FE9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Application\Application.csproj" />
|
||||||
|
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
36
Presentation/Program.cs
Normal file
36
Presentation/Program.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Data.OleDb;
|
||||||
|
using Application;
|
||||||
|
using Infrastructure;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static async Task Main()
|
||||||
|
{
|
||||||
|
DateTime inicio = DateTime.Now;
|
||||||
|
string PG_CONN_STRING_PROD = "Server = smart-energia-dev-pgsql.cykff7tj7mik.us-east-1.rds.amazonaws.com; Port = 5432; Database = smartenergiaprod; Username = postgres; Password = VfHml#Z78!%kvvNM; Timeout = 60; CommandTimeout = 60; ApplicationName = new_med_5_min; Connection Lifetime = 120; Minimum Pool Size = 2; Maximum Pool Size = 2;";
|
||||||
|
string ACCESS_CONN_STRING = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\srv-dados\documentos\Middle\Informativo Setorial\Modelo Word\BD1_dados cadastrais e faturas.accdb;Jet OLEDB:Database Password=gds21";
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Configuração de dependências (pode usar um container DI depois)
|
||||||
|
var postgresRepo = new PostgresRepository(PG_CONN_STRING_PROD);
|
||||||
|
var accessRepo = new AccessRepository(ACCESS_CONN_STRING);
|
||||||
|
var httpClient = new HttpClient(new HttpClientHandler
|
||||||
|
{
|
||||||
|
ClientCertificateOptions = ClientCertificateOption.Automatic,
|
||||||
|
//Proxy = new WebProxy("127.0.0.1", 8888),
|
||||||
|
//UseProxy = true,
|
||||||
|
//ServerCertificateCustomValidationCallback = (HttpRequestMessage req, X509Certificate2? cert, X509Chain? chain, SslPolicyErrors errors) => true
|
||||||
|
});
|
||||||
|
var rateLimiter = new RateLimiter(400, TimeSpan.FromMinutes(1));
|
||||||
|
var useCase = new ProcessarMedicoesUseCase(postgresRepo, accessRepo, httpClient, rateLimiter);
|
||||||
|
|
||||||
|
await useCase.ExecuteAsync(dataIni, dataFim, caminhoLog, CancellationToken.None);
|
||||||
|
|
||||||
|
Console.WriteLine($"Concluído. Tempo total: {DateTime.Now - inicio}");
|
||||||
|
Console.ReadKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
385
Program.cs
385
Program.cs
@ -8,393 +8,8 @@ using Npgsql;
|
|||||||
|
|
||||||
internal class Plat_integ
|
internal class Plat_integ
|
||||||
{
|
{
|
||||||
static string PG_CONN_STRING_PROD = "Server = smart-energia-dev-pgsql.cykff7tj7mik.us-east-1.rds.amazonaws.com; Port = 5432; Database = smartenergiaprod; Username = postgres; Password = VfHml#Z78!%kvvNM; Timeout = 60; CommandTimeout = 60; ApplicationName = new_med_5_min; Connection Lifetime = 120; Minimum Pool Size = 2; Maximum Pool Size = 2;";
|
|
||||||
static readonly HttpClient client = new HttpClient(new HttpClientHandler
|
|
||||||
{
|
|
||||||
ClientCertificateOptions = ClientCertificateOption.Automatic,
|
|
||||||
//Proxy = new WebProxy("127.0.0.1", 8888),
|
|
||||||
//UseProxy = true,
|
|
||||||
//ServerCertificateCustomValidationCallback = (HttpRequestMessage req, X509Certificate2? cert, X509Chain? chain, SslPolicyErrors errors) => true
|
|
||||||
});
|
|
||||||
|
|
||||||
static async Task Main()
|
static async Task Main()
|
||||||
{
|
{
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
|
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
|
||||||
DateTime inicio = DateTime.Now;
|
|
||||||
|
|
||||||
string caminhoBD = @"\\srv-dados\documentos\Middle\Informativo Setorial\Modelo Word\BD1_dados cadastrais e faturas.accdb";
|
|
||||||
string caminhoLog = $@"\\srv-dados\documentos\Back\Carteira x.x\Codigo\Erros\log_erros_{DateTime.Now:MM_dd_HH_mm}.csv";
|
|
||||||
|
|
||||||
DateTime now = DateTime.Now;
|
|
||||||
//DateTime dataIni = new DateTime(now.Year, now.Month, 1);
|
|
||||||
//DateTime dataFim = new DateTime(now.Year, now.Month, now.Day);
|
|
||||||
DateTime dataIni = new DateTime(now.Year, 7, 1);
|
|
||||||
DateTime dataFim = new DateTime(now.Year, 8, 1);
|
|
||||||
|
|
||||||
var perfis = Busca_dad_BD(caminhoBD, dataIni);
|
|
||||||
|
|
||||||
await ProcessarMedicoesAsync(dataIni, dataFim, perfis, caminhoLog);
|
|
||||||
|
|
||||||
Console.WriteLine($"Concluído. Tempo total: {DateTime.Now - inicio}");
|
|
||||||
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
public static List<perfil> Busca_dad_BD(string caminho_BD, DateTime dataIni)
|
|
||||||
{
|
|
||||||
var lista = new List<perfil>();
|
|
||||||
|
|
||||||
string query = $"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 query = "SELECT Cod_5min, Codigo_SCDE 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 query = "SELECT Cod_5min, Codigo_SCDE FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'ALMAVIVA' and Unidade = 'GUARULHOS' AND unidadade_gerenciada ORDER BY PerfilCCEE";
|
|
||||||
//string query = "SELECT Cod_5min, Codigo_SCDE FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'bramagran' AND Unidade_gerenciada ORDER BY PerfilCCEE";
|
|
||||||
using (var connection = new OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={caminho_BD};Jet OLEDB:Database Password=gds21"))
|
|
||||||
{
|
|
||||||
using var cmd = new OleDbCommand(query, connection);
|
|
||||||
connection.Open();
|
|
||||||
using var reader = cmd.ExecuteReader();
|
|
||||||
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
|
||||||
lista.Add(
|
|
||||||
new perfil(
|
|
||||||
reader["Cod_5min"].ToString(),
|
|
||||||
reader["Codigo_SCDE"].ToString(),
|
|
||||||
DateTime.Parse(reader["Data_de_Migracao"].ToString())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lista;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ProcessarMedicoesAsync(DateTime dataIni, DateTime dataFim, List<perfil> perfis, string caminhoLog)
|
|
||||||
{
|
|
||||||
var limiter = new RateLimiter(400, TimeSpan.FromMinutes(1));
|
|
||||||
|
|
||||||
var errosPersistentes = new ConcurrentBag<string>();
|
|
||||||
var atualizados = new ConcurrentBag<string>();
|
|
||||||
var inseridos = new ConcurrentBag<string>();
|
|
||||||
|
|
||||||
await using var dataSource = NpgsqlDataSource.Create(PG_CONN_STRING_PROD);
|
|
||||||
|
|
||||||
client.DefaultRequestHeaders.Add("SOAPAction", "listarMedidaCincoMinutos");
|
|
||||||
var endpoint = new Uri("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2");
|
|
||||||
|
|
||||||
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._Codigo_SCDE}");
|
|
||||||
if (perfil._Cod_5min == "0" || perfil._Cod_5min == string.Empty)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Pular {perfil._Codigo_SCDE} - (cod 5 min pendente)");
|
|
||||||
errosPersistentes.Add($"{perfil._Cod_5min};{perfil._Codigo_SCDE}; cod_5min pendente");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
string sqlSelect = @"
|
|
||||||
SELECT ponto, dia_num, minuto, origem,
|
|
||||||
ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao
|
|
||||||
FROM med_5min
|
|
||||||
WHERE ponto = @ponto AND dia_num >= @data_ini AND dia_num < @data_fim;
|
|
||||||
";
|
|
||||||
|
|
||||||
var existentes = new Dictionary<(string, double, int), dynamic>();
|
|
||||||
|
|
||||||
await using (var command = dataSource.CreateCommand(sqlSelect))
|
|
||||||
{
|
|
||||||
|
|
||||||
command.Parameters.AddWithValue("ponto", perfil._Codigo_SCDE + "P");
|
|
||||||
command.Parameters.AddWithValue("data_ini", dataIni.ToOADate());
|
|
||||||
command.Parameters.AddWithValue("data_fim", dataFim.ToOADate());
|
|
||||||
|
|
||||||
await using (var reader = await command.ExecuteReaderAsync(ct))
|
|
||||||
{
|
|
||||||
while (await reader.ReadAsync())
|
|
||||||
{
|
|
||||||
existentes[(reader.GetString(0), reader.GetDouble(1), reader.GetInt32(2))] = new
|
|
||||||
{
|
|
||||||
Origem = reader.GetString(3),
|
|
||||||
AtivaC = reader.GetDouble(4),
|
|
||||||
AtivaG = reader.GetDouble(5),
|
|
||||||
ReatC = reader.GetDouble(6),
|
|
||||||
ReatG = reader.GetDouble(7)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (DateTime dia in datas)
|
|
||||||
{
|
|
||||||
int tentativas = 0;
|
|
||||||
bool sucesso = false;
|
|
||||||
|
|
||||||
while (tentativas < 5 && !sucesso)
|
|
||||||
{
|
|
||||||
if (perfil._Data_de_Migracao > dia)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Pular {perfil._Codigo_SCDE} - {dia.ToShortDateString()} (antes da migração)");
|
|
||||||
errosPersistentes.Add($"{perfil._Cod_5min};{perfil._Codigo_SCDE};Fora da data de migração {perfil._Data_de_Migracao} x {dia}");
|
|
||||||
break; // não tentar antes da data de migração
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string payload = Xml_requisicao(dia, perfil._Cod_5min, perfil._Codigo_SCDE, 1);
|
|
||||||
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
|
|
||||||
|
|
||||||
await limiter.WaitAsync(ct);
|
|
||||||
using var response = await client.PostAsync(endpoint, conteudo, ct);
|
|
||||||
string resposta = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
if ((int)response.StatusCode >= 400)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VerificarRespostaSOAP(resposta);
|
|
||||||
}
|
|
||||||
catch (SoapFaultException ex)
|
|
||||||
{
|
|
||||||
if (ex.ErrorCode == "2003") // limite de requisições
|
|
||||||
{
|
|
||||||
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
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ex.ErrorCode == "4001") // Dados não encontrados
|
|
||||||
{
|
|
||||||
errosPersistentes.Add($"{perfil._Cod_5min};{perfil._Codigo_SCDE};SOAP Fault: {ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ex.ErrorCode == "2001") // Sem acesso
|
|
||||||
{
|
|
||||||
errosPersistentes.Add($"{perfil._Cod_5min};{perfil._Codigo_SCDE};SOAP Fault: {ex.ErrorCode};{ex.ErrorMessage.Replace("\n", "-n-")}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await ProcessarXMLAsync(resposta, dataSource, dia, perfil._Cod_5min, perfil._Codigo_SCDE, existentes, limiter, ct, 1);
|
|
||||||
|
|
||||||
sucesso = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
tentativas++;
|
|
||||||
if (tentativas >= 5)
|
|
||||||
{
|
|
||||||
errosPersistentes.Add($"{perfil._Cod_5min};{perfil._Codigo_SCDE};Erro;{ex.Message.Replace("\n", "-n-")}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int backoff = (int)Math.Pow(2.4, tentativas) * 1000; // exponencial
|
|
||||||
Console.WriteLine($"Erro na requisição ({ex.Message}), tentativa {tentativas}. Aguardando {backoff / 1000}s...");
|
|
||||||
await Task.Delay(backoff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Console.WriteLine($"{DateTime.Now}: Finalizado ponto {perfil._Codigo_SCDE}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errosPersistentes.Count > 0)
|
|
||||||
{
|
|
||||||
File.WriteAllLines(caminhoLog, new[] { "Perfil;Ponto;Status;Message" }.Concat(errosPersistentes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static string Xml_requisicao(DateTime data_req, string perfil, string cod_ponto, int pagina)
|
|
||||||
{
|
|
||||||
string cam_ent, tex_req, sdat_req;
|
|
||||||
cam_ent = @"X:\Back\Plataforma de Integração CCEE\RequestPaginate.txt";
|
|
||||||
cod_ponto += "P";
|
|
||||||
sdat_req = data_req.ToString("yyyy-MM-ddT00:00:00");
|
|
||||||
tex_req = File.ReadAllText(cam_ent);
|
|
||||||
tex_req = tex_req.Replace("DATAALTERADA", sdat_req);
|
|
||||||
tex_req = tex_req.Replace("PONTOMEDICAO", cod_ponto);
|
|
||||||
tex_req = tex_req.Replace("CODPERFIL", perfil);
|
|
||||||
tex_req = tex_req.Replace("PAGNUM", pagina.ToString());
|
|
||||||
return tex_req;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ProcessarXMLAsync(string xml, NpgsqlDataSource dataSource, DateTime dia, string perfil, string ponto, Dictionary<(string, double, int), dynamic> existentes, RateLimiter limiter, CancellationToken ct, int paginaAtual = 1, List<XElement> acumulador = null, int totalPaginas = 1)
|
|
||||||
{
|
|
||||||
var doc = XDocument.Parse(xml);
|
|
||||||
XNamespace ns = "http://xmlns.energia.org.br/BO/v2";
|
|
||||||
|
|
||||||
int.TryParse(doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "totalPaginas")?.Value, out totalPaginas);
|
|
||||||
int.TryParse(doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "numero")?.Value, out paginaAtual);
|
|
||||||
|
|
||||||
|
|
||||||
var medidas = doc.Descendants(ns + "medida")
|
|
||||||
.Where(x => (string)x.Element(ns + "tipoEnergia") == "L");
|
|
||||||
|
|
||||||
acumulador ??= new List<XElement>();
|
|
||||||
acumulador.AddRange(medidas);
|
|
||||||
|
|
||||||
if (paginaAtual < totalPaginas)
|
|
||||||
{
|
|
||||||
// Requisita próxima página
|
|
||||||
string payload = Xml_requisicao(dia, perfil, ponto, paginaAtual + 1);
|
|
||||||
|
|
||||||
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
|
|
||||||
await limiter.WaitAsync(ct);
|
|
||||||
using var resp = await client.PostAsync("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2", conteudo);
|
|
||||||
|
|
||||||
string proxXml = await resp.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
await ProcessarXMLAsync(proxXml, dataSource, dia, perfil, ponto, existentes, limiter, ct, paginaAtual + 1, acumulador, totalPaginas);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var medidasProcessadas = acumulador
|
|
||||||
.Select(m =>
|
|
||||||
{
|
|
||||||
string origem = m.Element(ns + "coletaMedicao")?.Element(ns + "tipo")?.Element(ns + "nome")?.Value ?? "";
|
|
||||||
string ponto = m.Element(ns + "medidor")?.Element(ns + "codigo")?.Value ?? "";
|
|
||||||
DateTime data = DateTime.Parse(m.Element(ns + "data")?.Value ?? "");
|
|
||||||
double diaNum = (data.ToOADate() - data.ToOADate() % 1);
|
|
||||||
int minuto = data.Hour * 60 + data.Minute;
|
|
||||||
if (minuto == 0) { minuto = 1440; diaNum--; }
|
|
||||||
|
|
||||||
double.TryParse(m.Element(ns + "energiaAtiva")?.Element(ns + "consumo")?.Element(ns + "valor")?.Value, out double ativa_c);
|
|
||||||
double.TryParse(m.Element(ns + "energiaAtiva")?.Element(ns + "geracao")?.Element(ns + "valor")?.Value, out double ativa_g);
|
|
||||||
double.TryParse(m.Element(ns + "energiaReativa")?.Element(ns + "consumo")?.Element(ns + "valor")?.Value, out double reat_c);
|
|
||||||
double.TryParse(m.Element(ns + "energiaReativa")?.Element(ns + "geracao")?.Element(ns + "valor")?.Value, out double reat_g);
|
|
||||||
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Origem = origem,
|
|
||||||
Ponto = ponto,
|
|
||||||
DiaNum = diaNum,
|
|
||||||
Minuto = minuto,
|
|
||||||
AtivaC = ativa_c,
|
|
||||||
AtivaG = ativa_g,
|
|
||||||
ReatC = reat_c,
|
|
||||||
ReatG = reat_g
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.GroupBy(x => new { x.Ponto, x.DiaNum, x.Minuto })
|
|
||||||
.Select(g =>
|
|
||||||
{
|
|
||||||
// Se houver alguma Inspeção Lógica → prioriza
|
|
||||||
var logica = g.FirstOrDefault(x => x.Origem == "Inspeção Lógica");
|
|
||||||
return logica ?? g.First(); // se não tiver lógica, pega qualquer (coleta diária)
|
|
||||||
});
|
|
||||||
|
|
||||||
var novos = new List<dynamic>();
|
|
||||||
var alterados = new List<dynamic>();
|
|
||||||
|
|
||||||
foreach (var m in medidasProcessadas)
|
|
||||||
{
|
|
||||||
var chave = (m.Ponto, m.DiaNum, m.Minuto);
|
|
||||||
|
|
||||||
if (!existentes.TryGetValue(chave, out var existente))
|
|
||||||
{
|
|
||||||
// não existe → inserir
|
|
||||||
novos.Add(m);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// existe mas mudou → atualizar
|
|
||||||
if (existente.Origem != m.Origem ||
|
|
||||||
existente.AtivaC != m.AtivaC ||
|
|
||||||
existente.AtivaG != m.AtivaG ||
|
|
||||||
existente.ReatC != m.ReatC ||
|
|
||||||
existente.ReatG != m.ReatG)
|
|
||||||
{
|
|
||||||
alterados.Add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (novos.Any())
|
|
||||||
{
|
|
||||||
await using (var connection = await dataSource.OpenConnectionAsync(ct))
|
|
||||||
{
|
|
||||||
using var writer = connection.BeginBinaryImport("COPY med_5min (origem, dia_num, minuto, ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao, ponto) FROM STDIN (FORMAT BINARY)");
|
|
||||||
|
|
||||||
foreach (var m in novos)
|
|
||||||
{
|
|
||||||
writer.StartRow();
|
|
||||||
writer.Write(m.Origem);
|
|
||||||
writer.Write(m.DiaNum, NpgsqlTypes.NpgsqlDbType.Numeric);
|
|
||||||
writer.Write(m.Minuto, NpgsqlTypes.NpgsqlDbType.Integer);
|
|
||||||
writer.Write(m.AtivaC, NpgsqlTypes.NpgsqlDbType.Numeric);
|
|
||||||
writer.Write(m.AtivaG, NpgsqlTypes.NpgsqlDbType.Numeric);
|
|
||||||
writer.Write(m.ReatC, NpgsqlTypes.NpgsqlDbType.Numeric);
|
|
||||||
writer.Write(m.ReatG, NpgsqlTypes.NpgsqlDbType.Numeric);
|
|
||||||
writer.Write(m.Ponto);
|
|
||||||
}
|
|
||||||
|
|
||||||
await writer.CompleteAsync();
|
|
||||||
}
|
|
||||||
Console.WriteLine($"Inserido {novos.Count} registros. Ponto {ponto}. Dia {dia}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alterados.Any())
|
|
||||||
{
|
|
||||||
await using (var connection = await dataSource.OpenConnectionAsync(ct))
|
|
||||||
{
|
|
||||||
using (var batch = new NpgsqlBatch(connection))
|
|
||||||
{
|
|
||||||
foreach (var m in alterados)
|
|
||||||
{
|
|
||||||
var cmd = new NpgsqlBatchCommand(@"
|
|
||||||
UPDATE med_5min
|
|
||||||
SET origem = @origem,
|
|
||||||
ativa_consumo = @ativa_consumo,
|
|
||||||
ativa_geracao = @ativa_geracao,
|
|
||||||
reativa_consumo = @reativa_consumo,
|
|
||||||
reativa_geracao = @reativa_geracao
|
|
||||||
WHERE ponto = @ponto
|
|
||||||
AND dia_num = @dia_num
|
|
||||||
AND minuto = @minuto;");
|
|
||||||
|
|
||||||
// Adiciona os parâmetros de forma segura
|
|
||||||
cmd.Parameters.AddWithValue("origem", m.Origem);
|
|
||||||
cmd.Parameters.AddWithValue("ativa_consumo", m.AtivaC);
|
|
||||||
cmd.Parameters.AddWithValue("ativa_geracao", m.AtivaG);
|
|
||||||
cmd.Parameters.AddWithValue("reativa_consumo", m.ReatC);
|
|
||||||
cmd.Parameters.AddWithValue("reativa_geracao", m.ReatG);
|
|
||||||
cmd.Parameters.AddWithValue("ponto", m.Ponto);
|
|
||||||
cmd.Parameters.AddWithValue("dia_num", m.DiaNum);
|
|
||||||
cmd.Parameters.AddWithValue("minuto", m.Minuto);
|
|
||||||
|
|
||||||
batch.BatchCommands.Add(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Executa o lote de comandos de uma vez
|
|
||||||
await batch.ExecuteNonQueryAsync();
|
|
||||||
Console.WriteLine($"Atualizado {alterados.Count} registros. Ponto {ponto}. Dia {dia}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class perfil
|
|
||||||
{
|
|
||||||
public string _Cod_5min { get; set; }
|
|
||||||
public string _Codigo_SCDE { get; set; }
|
|
||||||
public DateTime _Data_de_Migracao { get; set; }
|
|
||||||
|
|
||||||
public perfil(string cod_5min, string codigo_scde, DateTime data_de_migracao)
|
|
||||||
{
|
|
||||||
_Cod_5min = cod_5min;
|
|
||||||
_Codigo_SCDE = codigo_scde;
|
|
||||||
_Data_de_Migracao = data_de_migracao;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Services
|
|
||||||
{
|
|
||||||
internal class MedicaoService
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Services
|
|
||||||
{
|
|
||||||
internal class SoapService
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user