diff --git a/AgroBase/AgroBase/Forms/frmInstancial.cs b/AgroBase/AgroBase/Forms/frmInstancial.cs index b66deeacd..c3279b5b6 100644 --- a/AgroBase/AgroBase/Forms/frmInstancial.cs +++ b/AgroBase/AgroBase/Forms/frmInstancial.cs @@ -110,7 +110,7 @@ namespace AgroBase.Forms _appCts?.Dispose(); _appCts = new CancellationTokenSource(); - FuncoesGlobais.AddApplicationToStartup(); + //FuncoesGlobais.AddApplicationToStartup(); /* * Ordem pensada para campo: diff --git a/AgroBase/AgroBase/Models/MapasModel.cs b/AgroBase/AgroBase/Models/MapasModel.cs index 20f253edf..b49207f3c 100644 --- a/AgroBase/AgroBase/Models/MapasModel.cs +++ b/AgroBase/AgroBase/Models/MapasModel.cs @@ -101,6 +101,7 @@ namespace AgroBase.Models return; TipoMapa = tipoSelecionado ?? TipoMapaOperacao.Indefinido; + await AtualizarInteratividadeMapaAsync(true); } catch (Exception ex) { diff --git a/AgroBase/AgroBase/Models/PerformanceModel.cs b/AgroBase/AgroBase/Models/PerformanceModel.cs index b0cdda516..7192250d3 100644 --- a/AgroBase/AgroBase/Models/PerformanceModel.cs +++ b/AgroBase/AgroBase/Models/PerformanceModel.cs @@ -1,45 +1,62 @@ -using OpenHardwareMonitor.Hardware; +using LibreHardwareMonitor.Hardware; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Threading; +using static AgroBase.Models.Enums; namespace AgroBase.Models { /// - /// Snapshot rico de performance do PC. - /// - /// Objetivo: - /// - telemetria em tempo real; - /// - replay de operação; - /// - diagnóstico do health_worker; - /// - correlação com performance dos workers Python. - /// - /// Observação: - /// - Disk_used_space_percent representa espaço usado, não atividade do disco. - /// - GPU_load_percent é um agregado dos principais sensores de load disponíveis. + /// Snapshot de performance do NPC. + /// Windows fornece CPU, RAM e disco; NVML fornece GPU; LibreHardwareMonitor fornece somente sensores físicos da CPU. + /// A falha de uma fonte nunca interrompe as demais nem o loop principal do sensoriamento. /// public class PerformanceModel { private static readonly object ComputerLock = new object(); - private static Computer computer; + private static readonly object NvmlLock = new object(); + private static readonly object CpuTimesLock = new object(); + private static readonly object LogLock = new object(); + private static readonly Dictionary UltimoLogPorOrigem = new Dictionary(); + private static readonly TimeSpan IntervaloRetentativaHardware = TimeSpan.FromMinutes(1); + private static readonly TimeSpan IntervaloLogRepetido = TimeSpan.FromMinutes(5); - private DateTime _ultimaLeituraPerformance = DateTime.MinValue; + private static Computer computer; + private static NvmlProvider nvml; + private static DateTime _proximaTentativaCpuUtc = DateTime.MinValue; + private static DateTime _proximaTentativaNvmlUtc = DateTime.MinValue; + private static ulong _cpuIdleAnterior; + private static ulong _cpuKernelAnterior; + private static ulong _cpuUserAnterior; + private static bool _cpuTimesInicializado; + + private object _coletaLock = new object(); + private object _snapshotLock = new object(); + private DateTime _ultimaLeituraPerformanceUtc = DateTime.MinValue; + private bool _coletaEmAndamento; public PerformanceModel() { - InicializarComputer(); } - // ============================================================ - // Identificação / tempo - // ============================================================ - public DateTime Timestamp { get; set; } = DateTime.Now; public long TimestampUnixMs { get; set; } + public DateTime? UltimaColetaConcluidaUtc { get; set; } + public DateTime? Hardware_ultima_leitura_valida_utc { get; set; } + public DateTime? Hardware_ultima_falha_utc { get; set; } + public bool Hardware_monitor_disponivel { get; set; } + public bool Ultima_coleta_completa { get; set; } + public int Hardware_falhas_consecutivas { get; set; } + public long Quantidade_coletas { get; set; } + public long Quantidade_falhas { get; set; } + public string Ultimo_erro { get; set; } - // ============================================================ - // CPU - // ============================================================ + public bool Hardware_dados_desatualizados => !Hardware_ultima_leitura_valida_utc.HasValue || + DateTime.UtcNow - Hardware_ultima_leitura_valida_utc.Value > TimeSpan.FromSeconds(10); public float? CPU_load_percent { get; set; } public float? CPU_temp_package_c { get; set; } @@ -47,228 +64,69 @@ namespace AgroBase.Models public float? CPU_power_cores_w { get; set; } public float? CPU_power_graphics_w { get; set; } public float? CPU_power_dram_w { get; set; } - public float? CPU_clock_avg_mhz { get; set; } public float? CPU_clock_max_mhz { get; set; } public float? CPU_bus_speed_mhz { get; set; } - - // Compatibilidade conceitual, se você quiser usar nomes antigos depois. public float? CPU_load => CPU_load_percent; public float? CPU_temp => CPU_temp_package_c; - // ============================================================ - // RAM - // ============================================================ - public float? RAM_usage_percent { get; set; } public float? RAM_used_gb { get; set; } public float? RAM_available_gb { get; set; } - public float? RAM_total_gb - { - get - { - if (RAM_used_gb.HasValue && RAM_available_gb.HasValue) - return RAM_used_gb.Value + RAM_available_gb.Value; - - return null; - } - } - - // Compatibilidade conceitual + public float? RAM_total_gb => RAM_used_gb.HasValue && RAM_available_gb.HasValue + ? RAM_used_gb.Value + RAM_available_gb.Value + : (float?)null; public float? RAM_usage => RAM_usage_percent; - // ============================================================ - // GPU geral - // ============================================================ - - /// - /// Agregado dos sensores de carga da GPU. - /// Calculado como o máximo entre core, frame buffer, memory, - /// video engine e bus interface. - /// public float? GPU_load_percent { get; set; } - public float? GPU_core_load_percent { get; set; } public float? GPU_frame_buffer_load_percent { get; set; } public float? GPU_memory_load_percent { get; set; } public float? GPU_video_engine_load_percent { get; set; } public float? GPU_bus_interface_load_percent { get; set; } - public float? GPU_temp_core_c { get; set; } - public float? GPU_power_w { get; set; } - public float? GPU_core_clock_mhz { get; set; } public float? GPU_memory_clock_mhz { get; set; } public float? GPU_shader_clock_mhz { get; set; } - public float? GPU_fan_percent { get; set; } public float? GPU_fan_rpm { get; set; } - public float? GPU_pcie_rx { get; set; } public float? GPU_pcie_tx { get; set; } - - // Compatibilidade conceitual public float? GPU_load => GPU_load_percent; public float? GPU_temp => GPU_temp_core_c; - // ============================================================ - // GPU memória / VRAM - // ============================================================ - public float? GPU_vram_total_mb { get; set; } public float? GPU_vram_used_mb { get; set; } public float? GPU_vram_free_mb { get; set; } + public float? GPU_vram_used_percent => GPU_vram_total_mb.HasValue && GPU_vram_total_mb.Value > 0 && GPU_vram_used_mb.HasValue + ? 100.0f * GPU_vram_used_mb.Value / GPU_vram_total_mb.Value + : GPU_memory_load_percent; - public float? GPU_vram_used_percent - { - get - { - if (GPU_vram_total_mb.HasValue && - GPU_vram_total_mb.Value > 0 && - GPU_vram_used_mb.HasValue) - { - return 100.0f * GPU_vram_used_mb.Value / GPU_vram_total_mb.Value; - } - - return GPU_memory_load_percent; - } - } - - // ============================================================ - // Disco - // ============================================================ - - /// - /// Espaço usado no disco. - /// Isso NÃO é atividade do disco. - /// public float? Disk_used_space_percent { get; set; } - - // Nome antigo, mas agora intencionalmente apontando para espaço usado. public float? HDD_load => Disk_used_space_percent; - // ============================================================ - // Processo atual - // ============================================================ - public int Process_thread_count { get; set; } public long Process_working_set_mb { get; set; } public long Process_private_memory_mb { get; set; } public long Process_virtual_memory_mb { get; set; } public double Process_cpu_time_total_s { get; set; } - // ============================================================ - // ThreadPool - // ============================================================ - public int ThreadPool_available_worker_threads { get; set; } public int ThreadPool_available_completion_port_threads { get; set; } - public int ThreadPool_max_worker_threads { get; set; } public int ThreadPool_max_completion_port_threads { get; set; } + public int ThreadPool_used_worker_threads => Math.Max(0, ThreadPool_max_worker_threads - ThreadPool_available_worker_threads); + public int ThreadPool_used_completion_port_threads => Math.Max(0, ThreadPool_max_completion_port_threads - ThreadPool_available_completion_port_threads); + public float ThreadPool_used_worker_percent => ThreadPool_max_worker_threads <= 0 ? 0 : 100.0f * ThreadPool_used_worker_threads / ThreadPool_max_worker_threads; + public float ThreadPool_used_completion_port_percent => ThreadPool_max_completion_port_threads <= 0 + ? 0 + : 100.0f * ThreadPool_used_completion_port_threads / ThreadPool_max_completion_port_threads; - public int ThreadPool_used_worker_threads - { - get - { - return Math.Max(0, ThreadPool_max_worker_threads - ThreadPool_available_worker_threads); - } - } - - public int ThreadPool_used_completion_port_threads - { - get - { - return Math.Max(0, ThreadPool_max_completion_port_threads - ThreadPool_available_completion_port_threads); - } - } - - public float ThreadPool_used_worker_percent - { - get - { - if (ThreadPool_max_worker_threads <= 0) - return 0; - - return 100.0f * ThreadPool_used_worker_threads / ThreadPool_max_worker_threads; - } - } - - public float ThreadPool_used_completion_port_percent - { - get - { - if (ThreadPool_max_completion_port_threads <= 0) - return 0; - - return 100.0f * ThreadPool_used_completion_port_threads / ThreadPool_max_completion_port_threads; - } - } - - - // ============================================================ - // String resumida para debug rápido - // ============================================================ - - public string PerformanceStr - { - get - { - return - $"CPU={Fmt(CPU_load_percent)}% " + - $"CPU_T={Fmt(CPU_temp_package_c)}C " + - $"RAM={Fmt(RAM_usage_percent)}% " + - $"GPU={Fmt(GPU_load_percent)}% " + - $"GPU_Core={Fmt(GPU_core_load_percent)}% " + - $"GPU_Mem={Fmt(GPU_memory_load_percent)}% " + - $"VRAM={Fmt(GPU_vram_used_mb)}/{Fmt(GPU_vram_total_mb)}MB " + - $"GPU_T={Fmt(GPU_temp_core_c)}C " + - $"GPU_P={Fmt(GPU_power_w)}W"; - } - } - - // ============================================================ - // Inicialização - // ============================================================ - - private static void InicializarComputer() - { - if (!Variaveis.IsAgroBase) - return; - - if (computer != null) - return; - - lock (ComputerLock) - { - if (computer != null) - return; - - computer = new Computer() - { - CPUEnabled = true, - GPUEnabled = true, - RAMEnabled = true, - HDDEnabled = true, - MainboardEnabled = false, - FanControllerEnabled = false, - }; - - try - { - computer.Open(); - } - catch - { - computer = null; - } - } - } - - // ============================================================ - // Atualização principal - // ============================================================ + public string PerformanceStr => $"CPU={Fmt(CPU_load_percent)}% CPU_T={Fmt(CPU_temp_package_c)}C RAM={Fmt(RAM_usage_percent)}% " + + $"GPU={Fmt(GPU_load_percent)}% GPU_Core={Fmt(GPU_core_load_percent)}% GPU_Mem={Fmt(GPU_memory_load_percent)}% " + + $"VRAM={Fmt(GPU_vram_used_mb)}/{Fmt(GPU_vram_total_mb)}MB GPU_T={Fmt(GPU_temp_core_c)}C " + + $"GPU_P={Fmt(GPU_power_w)}W HW={(Hardware_monitor_disponivel ? "OK" : "INDISP")}"; public void AtualizarDadosPerformance() { @@ -277,369 +135,368 @@ namespace AgroBase.Models public void AtualizarDadosPerformance(TimeSpan intervaloMinimo) { - if (DateTime.Now < _ultimaLeituraPerformance.Add(intervaloMinimo)) - return; + if (intervaloMinimo < TimeSpan.Zero) + intervaloMinimo = TimeSpan.Zero; - _ultimaLeituraPerformance = DateTime.Now; - Timestamp = DateTime.Now; - TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + DateTime agoraUtc = DateTime.UtcNow; - if (computer == null || computer.Hardware == null) + lock (_coletaLock) { - InicializarComputer(); - if (computer == null || computer.Hardware == null) + if (_coletaEmAndamento || agoraUtc < _ultimaLeituraPerformanceUtc.Add(intervaloMinimo)) return; + + _coletaEmAndamento = true; + _ultimaLeituraPerformanceUtc = agoraUtc; } - // Variáveis locais, para só atualizar propriedades no final. - float? cpuLoad = null; - float? cpuTempPackage = null; - float? cpuPowerPackage = null; - float? cpuPowerCores = null; - float? cpuPowerGraphics = null; - float? cpuPowerDram = null; - float? cpuClockAvg = null; - float? cpuClockMax = null; - float? cpuBusSpeed = null; - - float? ramUsage = null; - float? ramUsedGb = null; - float? ramAvailableGb = null; - - float? gpuCoreLoad = null; - float? gpuFrameBufferLoad = null; - float? gpuMemoryLoad = null; - float? gpuVideoEngineLoad = null; - float? gpuBusInterfaceLoad = null; - - float? gpuTempCore = null; - float? gpuPower = null; - - float? gpuCoreClock = null; - float? gpuMemoryClock = null; - float? gpuShaderClock = null; - - float? gpuFanPercent = null; - float? gpuFanRpm = null; - - float? gpuVramTotalMb = null; - float? gpuVramUsedMb = null; - float? gpuVramFreeMb = null; - - float? gpuPcieRx = null; - float? gpuPcieTx = null; - - float? diskUsedSpace = null; + string ultimoErro = null; try { + bool processoOk = TentarAtualizar("Processo", AtualizarDadosProcesso, ref ultimoErro); + bool threadPoolOk = TentarAtualizar("ThreadPool", AtualizarDadosThreadPool, ref ultimoErro); + bool windowsOk = TentarAtualizar("Windows nativo", AtualizarDadosWindows, ref ultimoErro); + bool gpuOk = AtualizarDadosNvidia(ref ultimoErro); + bool sensoresCpuOk = AtualizarSensoresCpu(ref ultimoErro); + bool completa = processoOk && threadPoolOk && windowsOk && gpuOk && sensoresCpuOk; + + lock (_snapshotLock) + { + Timestamp = DateTime.Now; + TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + UltimaColetaConcluidaUtc = DateTime.UtcNow; + Ultima_coleta_completa = completa; + Hardware_monitor_disponivel = windowsOk && gpuOk && sensoresCpuOk; + Quantidade_coletas++; + + if (completa) + { + Ultimo_erro = null; + Hardware_falhas_consecutivas = 0; + Hardware_ultima_leitura_valida_utc = DateTime.UtcNow; + } + else + { + Ultimo_erro = ultimoErro; + Hardware_ultima_falha_utc = DateTime.UtcNow; + Hardware_falhas_consecutivas++; + Quantidade_falhas++; + } + } + } + catch (Exception ex) + { + RegistrarFalha("Coleta geral", ex); + + lock (_snapshotLock) + { + Timestamp = DateTime.Now; + TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + UltimaColetaConcluidaUtc = DateTime.UtcNow; + Ultima_coleta_completa = false; + Hardware_monitor_disponivel = false; + Hardware_ultima_falha_utc = DateTime.UtcNow; + Hardware_falhas_consecutivas++; + Ultimo_erro = ResumirErro(ex); + Quantidade_coletas++; + Quantidade_falhas++; + } + } + finally + { + lock (_coletaLock) + { + _coletaEmAndamento = false; + } + } + } + + private void AtualizarDadosWindows() + { + MEMORYSTATUSEX memoria = new MEMORYSTATUSEX(); + if (!GlobalMemoryStatusEx(memoria)) + throw new InvalidOperationException($"GlobalMemoryStatusEx falhou. Win32={Marshal.GetLastWin32Error()}."); + + float? cpuLoad = ObterCargaCpu(); + float? diskUsed = ObterUsoDisco(); + float? clockAvg = null; + float? clockMax = null; + PROCESSOR_POWER_INFORMATION[] processadores = new PROCESSOR_POWER_INFORMATION[Environment.ProcessorCount]; + int tamanho = Marshal.SizeOf() * processadores.Length; + + if (CallNtPowerInformation(11, IntPtr.Zero, 0, processadores, tamanho) == 0 && processadores.Length > 0) + { + clockAvg = (float)processadores.Average(x => (double)x.CurrentMhz); + clockMax = processadores.Max(x => (float)x.MaxMhz); + } + + lock (_snapshotLock) + { + CPU_load_percent = cpuLoad ?? CPU_load_percent; + CPU_clock_avg_mhz = clockAvg ?? CPU_clock_avg_mhz; + CPU_clock_max_mhz = clockMax ?? CPU_clock_max_mhz; + RAM_usage_percent = memoria.dwMemoryLoad; + RAM_used_gb = BytesToGb(memoria.ullTotalPhys - memoria.ullAvailPhys); + RAM_available_gb = BytesToGb(memoria.ullAvailPhys); + Disk_used_space_percent = diskUsed ?? Disk_used_space_percent; + } + } + + private bool AtualizarDadosNvidia(ref string ultimoErro) + { + try + { + if (!GarantirNvmlInicializado()) + return false; + + NvidiaSnapshot leitura; + + lock (NvmlLock) + { + leitura = nvml.Coletar(); + } + + lock (_snapshotLock) + { + GPU_load_percent = leitura.GpuLoad; + GPU_core_load_percent = leitura.GpuLoad; + GPU_frame_buffer_load_percent = leitura.MemoryLoad; + GPU_memory_load_percent = leitura.MemoryLoad; + GPU_temp_core_c = leitura.Temperature ?? GPU_temp_core_c; + GPU_power_w = leitura.Power ?? GPU_power_w; + GPU_core_clock_mhz = leitura.GraphicsClock ?? GPU_core_clock_mhz; + GPU_memory_clock_mhz = leitura.MemoryClock ?? GPU_memory_clock_mhz; + GPU_shader_clock_mhz = leitura.SmClock ?? GPU_shader_clock_mhz; + GPU_fan_percent = leitura.FanPercent ?? GPU_fan_percent; + GPU_vram_total_mb = leitura.VramTotalMb; + GPU_vram_used_mb = leitura.VramUsedMb; + GPU_vram_free_mb = leitura.VramFreeMb; + GPU_pcie_rx = leitura.PcieRxKbS ?? GPU_pcie_rx; + GPU_pcie_tx = leitura.PcieTxKbS ?? GPU_pcie_tx; + } + + return true; + } + catch (Exception ex) + { + ultimoErro = ResumirErro(ex); + RegistrarFalha("NVIDIA NVML", ex); + ReiniciarNvmlAposFalha(); + return false; + } + } + + private bool AtualizarSensoresCpu(ref string ultimoErro) + { + try + { + if (!GarantirComputerInicializado()) + return false; + + CpuSensorSnapshot leitura = new CpuSensorSnapshot(); + lock (ComputerLock) { - foreach (var hardwareItem in computer.Hardware) + foreach (IHardware hardware in computer.Hardware.Where(x => x.HardwareType == HardwareType.Cpu)) { - hardwareItem.Update(); - - switch (hardwareItem.HardwareType) - { - case HardwareType.CPU: - LerCpu(hardwareItem, - ref cpuLoad, - ref cpuTempPackage, - ref cpuPowerPackage, - ref cpuPowerCores, - ref cpuPowerGraphics, - ref cpuPowerDram, - ref cpuClockAvg, - ref cpuClockMax, - ref cpuBusSpeed); - break; - - case HardwareType.RAM: - LerRam(hardwareItem, - ref ramUsage, - ref ramUsedGb, - ref ramAvailableGb); - break; - - case HardwareType.GpuNvidia: - case HardwareType.GpuAti: - LerGpu(hardwareItem, - ref gpuCoreLoad, - ref gpuFrameBufferLoad, - ref gpuMemoryLoad, - ref gpuVideoEngineLoad, - ref gpuBusInterfaceLoad, - ref gpuTempCore, - ref gpuPower, - ref gpuCoreClock, - ref gpuMemoryClock, - ref gpuShaderClock, - ref gpuFanPercent, - ref gpuFanRpm, - ref gpuVramTotalMb, - ref gpuVramUsedMb, - ref gpuVramFreeMb, - ref gpuPcieRx, - ref gpuPcieTx); - break; - - case HardwareType.HDD: - LerDisco(hardwareItem, ref diskUsedSpace); - break; - } + hardware.Update(); + LerSensoresCpu(hardware, leitura); } } - CPU_load_percent = cpuLoad; - CPU_temp_package_c = cpuTempPackage; - CPU_power_package_w = cpuPowerPackage; - CPU_power_cores_w = cpuPowerCores; - CPU_power_graphics_w = cpuPowerGraphics; - CPU_power_dram_w = cpuPowerDram; - CPU_clock_avg_mhz = cpuClockAvg; - CPU_clock_max_mhz = cpuClockMax; - CPU_bus_speed_mhz = cpuBusSpeed; + if (!leitura.Temperature.HasValue && !leitura.PowerPackage.HasValue) + throw new InvalidOperationException("LibreHardwareMonitor não retornou temperatura nem potência da CPU."); - RAM_usage_percent = ramUsage; - RAM_used_gb = ramUsedGb; - RAM_available_gb = ramAvailableGb; - - GPU_core_load_percent = gpuCoreLoad; - GPU_frame_buffer_load_percent = gpuFrameBufferLoad; - GPU_memory_load_percent = gpuMemoryLoad; - GPU_video_engine_load_percent = gpuVideoEngineLoad; - GPU_bus_interface_load_percent = gpuBusInterfaceLoad; - - GPU_load_percent = MaxNullable( - gpuCoreLoad, - gpuFrameBufferLoad, - gpuMemoryLoad, - gpuVideoEngineLoad, - gpuBusInterfaceLoad - ); - - GPU_temp_core_c = gpuTempCore; - GPU_power_w = gpuPower; - - GPU_core_clock_mhz = gpuCoreClock; - GPU_memory_clock_mhz = gpuMemoryClock; - GPU_shader_clock_mhz = gpuShaderClock; - - GPU_fan_percent = gpuFanPercent; - GPU_fan_rpm = gpuFanRpm; - - GPU_vram_total_mb = gpuVramTotalMb; - GPU_vram_used_mb = gpuVramUsedMb; - GPU_vram_free_mb = gpuVramFreeMb; - - GPU_pcie_rx = gpuPcieRx; - GPU_pcie_tx = gpuPcieTx; - - Disk_used_space_percent = diskUsedSpace; - - AtualizarDadosProcesso(); - AtualizarDadosThreadPool(); - } - catch - { - // Mantém último snapshot válido. - // Se quiser, depois podemos adicionar um campo: - // LastError, ErrorCount, LastErrorTimestamp. - } - } - - // ============================================================ - // Leitores por hardware - // ============================================================ - - private static void LerCpu( - IHardware hardwareItem, - ref float? cpuLoad, - ref float? cpuTempPackage, - ref float? cpuPowerPackage, - ref float? cpuPowerCores, - ref float? cpuPowerGraphics, - ref float? cpuPowerDram, - ref float? cpuClockAvg, - ref float? cpuClockMax, - ref float? cpuBusSpeed) - { - float clockSum = 0; - int clockCount = 0; - float? clockMax = null; - - foreach (var sensor in hardwareItem.Sensors) - { - if (!sensor.Value.HasValue) - continue; - - string name = sensor.Name ?? ""; - - if (sensor.SensorType == SensorType.Load && name == "CPU Total") - cpuLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Temperature && name == "CPU Package") - cpuTempPackage = sensor.Value; - - else if (sensor.SensorType == SensorType.Power && name == "CPU Package") - cpuPowerPackage = sensor.Value; - - else if (sensor.SensorType == SensorType.Power && name == "CPU Cores") - cpuPowerCores = sensor.Value; - - else if (sensor.SensorType == SensorType.Power && name == "CPU Graphics") - cpuPowerGraphics = sensor.Value; - - else if (sensor.SensorType == SensorType.Power && name == "CPU DRAM") - cpuPowerDram = sensor.Value; - - else if (sensor.SensorType == SensorType.Clock && name.StartsWith("CPU Core")) + lock (_snapshotLock) { - float v = sensor.Value.Value; - clockSum += v; - clockCount++; - - if (!clockMax.HasValue || v > clockMax.Value) - clockMax = v; + CPU_temp_package_c = leitura.Temperature ?? CPU_temp_package_c; + CPU_power_package_w = leitura.PowerPackage ?? CPU_power_package_w; + CPU_power_cores_w = leitura.PowerCores ?? CPU_power_cores_w; + CPU_power_graphics_w = leitura.PowerGraphics ?? CPU_power_graphics_w; + CPU_power_dram_w = leitura.PowerDram ?? CPU_power_dram_w; + CPU_bus_speed_mhz = leitura.BusSpeed ?? CPU_bus_speed_mhz; } - else if (sensor.SensorType == SensorType.Clock && name == "Bus Speed") - cpuBusSpeed = sensor.Value; + return true; + } + catch (Exception ex) + { + ultimoErro = ResumirErro(ex); + RegistrarFalha("Sensores CPU LibreHardwareMonitor", ex); + ReiniciarComputerAposFalha(); + return false; } - - if (clockCount > 0) - cpuClockAvg = clockSum / clockCount; - - cpuClockMax = clockMax; } - private static void LerRam( - IHardware hardwareItem, - ref float? ramUsage, - ref float? ramUsedGb, - ref float? ramAvailableGb) + private static bool GarantirComputerInicializado() { - foreach (var sensor in hardwareItem.Sensors) + if (!Variaveis.IsAgroBase) + return false; + + if (computer != null) + return true; + + if (DateTime.UtcNow < _proximaTentativaCpuUtc) + return false; + + lock (ComputerLock) + { + if (computer != null) + return true; + + Computer novoComputer = null; + + try + { + novoComputer = new Computer + { + IsCpuEnabled = true, + IsGpuEnabled = false, + IsMemoryEnabled = false, + IsStorageEnabled = false, + IsMotherboardEnabled = false, + IsControllerEnabled = false, + IsNetworkEnabled = false + }; + + novoComputer.Open(); + computer = novoComputer; + _proximaTentativaCpuUtc = DateTime.MinValue; + return true; + } + catch (Exception ex) + { + try + { + novoComputer?.Close(); + } + catch + { + } + + computer = null; + _proximaTentativaCpuUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + RegistrarFalha("Inicialização LibreHardwareMonitor", ex); + return false; + } + } + } + + private static bool GarantirNvmlInicializado() + { + if (!Variaveis.IsAgroBase) + return false; + + if (nvml != null) + return true; + + if (DateTime.UtcNow < _proximaTentativaNvmlUtc) + return false; + + lock (NvmlLock) + { + if (nvml != null) + return true; + + try + { + nvml = new NvmlProvider(); + _proximaTentativaNvmlUtc = DateTime.MinValue; + return true; + } + catch (Exception ex) + { + nvml = null; + _proximaTentativaNvmlUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + RegistrarFalha("Inicialização NVIDIA NVML", ex); + return false; + } + } + } + + private static void ReiniciarComputerAposFalha() + { + lock (ComputerLock) + { + try + { + computer?.Close(); + } + catch (Exception ex) + { + RegistrarFalha("Encerramento LibreHardwareMonitor", ex); + } + finally + { + computer = null; + _proximaTentativaCpuUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + } + } + } + + private static void ReiniciarNvmlAposFalha() + { + lock (NvmlLock) + { + try + { + nvml?.Dispose(); + } + catch (Exception ex) + { + RegistrarFalha("Encerramento NVIDIA NVML", ex); + } + finally + { + nvml = null; + _proximaTentativaNvmlUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + } + } + } + + public static void EncerrarMonitoramento() + { + ReiniciarComputerAposFalha(); + ReiniciarNvmlAposFalha(); + _proximaTentativaCpuUtc = DateTime.MinValue; + _proximaTentativaNvmlUtc = DateTime.MinValue; + } + + private static void LerSensoresCpu(IHardware hardware, CpuSensorSnapshot leitura) + { + foreach (ISensor sensor in hardware.Sensors) { if (!sensor.Value.HasValue) continue; - string name = sensor.Name ?? ""; + string nome = sensor.Name ?? string.Empty; + float valor = sensor.Value.Value; - if (sensor.SensorType == SensorType.Load && name == "Memory") - ramUsage = sensor.Value; - - else if (sensor.SensorType == SensorType.Data && name == "Used Memory") - ramUsedGb = sensor.Value; - - else if (sensor.SensorType == SensorType.Data && name == "Available Memory") - ramAvailableGb = sensor.Value; + if (sensor.SensorType == SensorType.Temperature && + (nome.Equals("CPU Package", StringComparison.OrdinalIgnoreCase) || + nome.IndexOf("Tctl", StringComparison.OrdinalIgnoreCase) >= 0 || + nome.IndexOf("Tdie", StringComparison.OrdinalIgnoreCase) >= 0)) + { + leitura.Temperature = MaxNullable(leitura.Temperature, valor); + } + else if (sensor.SensorType == SensorType.Power && nome.Equals("CPU Package", StringComparison.OrdinalIgnoreCase)) + leitura.PowerPackage = valor; + else if (sensor.SensorType == SensorType.Power && nome.Equals("CPU Cores", StringComparison.OrdinalIgnoreCase)) + leitura.PowerCores = valor; + else if (sensor.SensorType == SensorType.Power && nome.Equals("CPU Graphics", StringComparison.OrdinalIgnoreCase)) + leitura.PowerGraphics = valor; + else if (sensor.SensorType == SensorType.Power && nome.Equals("CPU DRAM", StringComparison.OrdinalIgnoreCase)) + leitura.PowerDram = valor; + else if (sensor.SensorType == SensorType.Clock && nome.Equals("Bus Speed", StringComparison.OrdinalIgnoreCase)) + leitura.BusSpeed = valor; } } - private static void LerGpu( - IHardware hardwareItem, - ref float? gpuCoreLoad, - ref float? gpuFrameBufferLoad, - ref float? gpuMemoryLoad, - ref float? gpuVideoEngineLoad, - ref float? gpuBusInterfaceLoad, - ref float? gpuTempCore, - ref float? gpuPower, - ref float? gpuCoreClock, - ref float? gpuMemoryClock, - ref float? gpuShaderClock, - ref float? gpuFanPercent, - ref float? gpuFanRpm, - ref float? gpuVramTotalMb, - ref float? gpuVramUsedMb, - ref float? gpuVramFreeMb, - ref float? gpuPcieRx, - ref float? gpuPcieTx) - { - foreach (var sensor in hardwareItem.Sensors) - { - if (!sensor.Value.HasValue) - continue; - - string name = sensor.Name ?? ""; - - if (sensor.SensorType == SensorType.Temperature && name == "GPU Core") - gpuTempCore = sensor.Value; - - else if (sensor.SensorType == SensorType.Load && name == "GPU Core") - gpuCoreLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Load && name == "GPU Frame Buffer") - gpuFrameBufferLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Load && name == "GPU Memory") - gpuMemoryLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Load && name == "GPU Video Engine") - gpuVideoEngineLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Load && name == "GPU Bus Interface") - gpuBusInterfaceLoad = sensor.Value; - - else if (sensor.SensorType == SensorType.Clock && name == "GPU Core") - gpuCoreClock = sensor.Value; - - else if (sensor.SensorType == SensorType.Clock && name == "GPU Memory") - gpuMemoryClock = sensor.Value; - - else if (sensor.SensorType == SensorType.Clock && name == "GPU Shader") - gpuShaderClock = sensor.Value; - - else if (sensor.SensorType == SensorType.Control && name == "GPU Fan") - gpuFanPercent = sensor.Value; - - else if (sensor.SensorType == SensorType.Fan && name == "GPU") - gpuFanRpm = sensor.Value; - - else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Total") - gpuVramTotalMb = sensor.Value; - - else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Used") - gpuVramUsedMb = sensor.Value; - - else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Free") - gpuVramFreeMb = sensor.Value; - - else if (sensor.SensorType == SensorType.Power && name == "GPU Power") - gpuPower = sensor.Value; - - else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Rx") - gpuPcieRx = sensor.Value; - - else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Tx") - gpuPcieTx = sensor.Value; - } - } - - private static void LerDisco(IHardware hardwareItem, ref float? diskUsedSpace) - { - foreach (var sensor in hardwareItem.Sensors) - { - if (!sensor.Value.HasValue) - continue; - - string name = sensor.Name ?? ""; - - if (sensor.SensorType == SensorType.Load && name == "Used Space") - diskUsedSpace = sensor.Value; - } - } - - // ============================================================ - // Processo / ThreadPool - // ============================================================ - private void AtualizarDadosProcesso() { - try + using (Process proc = Process.GetCurrentProcess()) { - using (var proc = Process.GetCurrentProcess()) + lock (_snapshotLock) { Process_thread_count = proc.Threads.Count; Process_working_set_mb = BytesToMb(proc.WorkingSet64); @@ -648,120 +505,148 @@ namespace AgroBase.Models Process_cpu_time_total_s = proc.TotalProcessorTime.TotalSeconds; } } - catch - { - // Mantém último valor. - } } private void AtualizarDadosThreadPool() { - try + ThreadPool.GetAvailableThreads(out int availableWorkerThreads, out int availableCompletionPortThreads); + ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads); + + lock (_snapshotLock) { - ThreadPool.GetAvailableThreads( - out int availableWorkerThreads, - out int availableCompletionPortThreads - ); - - ThreadPool.GetMaxThreads( - out int maxWorkerThreads, - out int maxCompletionPortThreads - ); - ThreadPool_available_worker_threads = availableWorkerThreads; ThreadPool_available_completion_port_threads = availableCompletionPortThreads; - ThreadPool_max_worker_threads = maxWorkerThreads; ThreadPool_max_completion_port_threads = maxCompletionPortThreads; } - catch - { - // Mantém último valor. - } } - // ============================================================ - // Clone / Snapshot - // ============================================================ + private bool TentarAtualizar(string origem, Action atualizacao, ref string ultimoErro) + { + try + { + atualizacao(); + return true; + } + catch (Exception ex) + { + ultimoErro = ResumirErro(ex); + RegistrarFalha(origem, ex); + return false; + } + } public PerformanceModel Clone() { - return new PerformanceModel() + lock (_snapshotLock) { - Timestamp = this.Timestamp, - TimestampUnixMs = this.TimestampUnixMs, - - CPU_load_percent = this.CPU_load_percent, - CPU_temp_package_c = this.CPU_temp_package_c, - CPU_power_package_w = this.CPU_power_package_w, - CPU_power_cores_w = this.CPU_power_cores_w, - CPU_power_graphics_w = this.CPU_power_graphics_w, - CPU_power_dram_w = this.CPU_power_dram_w, - CPU_clock_avg_mhz = this.CPU_clock_avg_mhz, - CPU_clock_max_mhz = this.CPU_clock_max_mhz, - CPU_bus_speed_mhz = this.CPU_bus_speed_mhz, - - RAM_usage_percent = this.RAM_usage_percent, - RAM_used_gb = this.RAM_used_gb, - RAM_available_gb = this.RAM_available_gb, - - GPU_load_percent = this.GPU_load_percent, - GPU_core_load_percent = this.GPU_core_load_percent, - GPU_frame_buffer_load_percent = this.GPU_frame_buffer_load_percent, - GPU_memory_load_percent = this.GPU_memory_load_percent, - GPU_video_engine_load_percent = this.GPU_video_engine_load_percent, - GPU_bus_interface_load_percent = this.GPU_bus_interface_load_percent, - - GPU_temp_core_c = this.GPU_temp_core_c, - GPU_power_w = this.GPU_power_w, - - GPU_core_clock_mhz = this.GPU_core_clock_mhz, - GPU_memory_clock_mhz = this.GPU_memory_clock_mhz, - GPU_shader_clock_mhz = this.GPU_shader_clock_mhz, - - GPU_fan_percent = this.GPU_fan_percent, - GPU_fan_rpm = this.GPU_fan_rpm, - - GPU_vram_total_mb = this.GPU_vram_total_mb, - GPU_vram_used_mb = this.GPU_vram_used_mb, - GPU_vram_free_mb = this.GPU_vram_free_mb, - - GPU_pcie_rx = this.GPU_pcie_rx, - GPU_pcie_tx = this.GPU_pcie_tx, - - Disk_used_space_percent = this.Disk_used_space_percent, - - Process_thread_count = this.Process_thread_count, - Process_working_set_mb = this.Process_working_set_mb, - Process_private_memory_mb = this.Process_private_memory_mb, - Process_virtual_memory_mb = this.Process_virtual_memory_mb, - Process_cpu_time_total_s = this.Process_cpu_time_total_s, - - ThreadPool_available_worker_threads = this.ThreadPool_available_worker_threads, - ThreadPool_available_completion_port_threads = this.ThreadPool_available_completion_port_threads, - ThreadPool_max_worker_threads = this.ThreadPool_max_worker_threads, - ThreadPool_max_completion_port_threads = this.ThreadPool_max_completion_port_threads, - }; + PerformanceModel clone = (PerformanceModel)MemberwiseClone(); + clone._coletaLock = new object(); + clone._snapshotLock = new object(); + clone._ultimaLeituraPerformanceUtc = _ultimaLeituraPerformanceUtc; + clone._coletaEmAndamento = false; + return clone; + } } - // ============================================================ - // Helpers - // ============================================================ - - private static float? MaxNullable(params float?[] values) + private static float? ObterCargaCpu() { - float? max = null; + if (!GetSystemTimes(out FILETIME idle, out FILETIME kernel, out FILETIME user)) + throw new InvalidOperationException($"GetSystemTimes falhou. Win32={Marshal.GetLastWin32Error()}."); - foreach (float? value in values) + ulong idleAtual = ToUInt64(idle); + ulong kernelAtual = ToUInt64(kernel); + ulong userAtual = ToUInt64(user); + + lock (CpuTimesLock) { - if (!value.HasValue) - continue; + if (!_cpuTimesInicializado) + { + _cpuIdleAnterior = idleAtual; + _cpuKernelAnterior = kernelAtual; + _cpuUserAnterior = userAtual; + _cpuTimesInicializado = true; + return null; + } - if (!max.HasValue || value.Value > max.Value) - max = value.Value; + ulong idleDelta = idleAtual - _cpuIdleAnterior; + ulong kernelDelta = kernelAtual - _cpuKernelAnterior; + ulong userDelta = userAtual - _cpuUserAnterior; + ulong totalDelta = kernelDelta + userDelta; + _cpuIdleAnterior = idleAtual; + _cpuKernelAnterior = kernelAtual; + _cpuUserAnterior = userAtual; + + if (totalDelta == 0) + return null; + + return (float)FuncoesMatematicas.Clamp(100.0f * (totalDelta - idleDelta) / totalDelta, 0, 100); } + } - return max; + private static float? ObterUsoDisco() + { + string raiz = Path.GetPathRoot(AppContext.BaseDirectory); + DriveInfo drive = DriveInfo.GetDrives().FirstOrDefault(x => + { + try + { + return x.IsReady && string.Equals(x.Name, raiz, StringComparison.OrdinalIgnoreCase); + } + catch + { + return false; + } + }); + + if (drive == null || drive.TotalSize <= 0) + return null; + + return 100.0f * (drive.TotalSize - drive.AvailableFreeSpace) / drive.TotalSize; + } + + private static void RegistrarFalha(string origem, Exception ex) + { + try + { + DateTime agoraUtc = DateTime.UtcNow; + + lock (LogLock) + { + if (UltimoLogPorOrigem.TryGetValue(origem, out DateTime ultimoLogUtc) && + agoraUtc - ultimoLogUtc < IntervaloLogRepetido) + { + return; + } + + UltimoLogPorOrigem[origem] = agoraUtc; + } + + string mensagem = $"Falha na telemetria de performance ({origem}): {ResumirErro(ex)}"; + Variaveis.OperacaoEmAndamento?.InserirEventoOperacao(T_Code.Npc, StatusModulo.Falha, 0, mensagem, null, origem); + } + catch + { + } + } + + private static string ResumirErro(Exception ex) + { + if (ex == null) + return "Erro desconhecido."; + + string mensagem = $"{ex.GetType().Name}: {ex.Message}"; + return mensagem.Length <= 500 ? mensagem : mensagem.Substring(0, 500); + } + + private static ulong ToUInt64(FILETIME value) + { + return ((ulong)value.dwHighDateTime << 32) | value.dwLowDateTime; + } + + private static float BytesToGb(ulong bytes) + { + return bytes / (1024.0f * 1024.0f * 1024.0f); } private static long BytesToMb(long bytes) @@ -769,12 +654,313 @@ namespace AgroBase.Models return bytes / (1024L * 1024L); } + private static float? MaxNullable(params float?[] values) + { + float? max = null; + + foreach (float? value in values) + { + if (value.HasValue && (!max.HasValue || value.Value > max.Value)) + max = value.Value; + } + + return max; + } + private static string Fmt(float? value, int casas = 1) { - if (!value.HasValue) - return "-"; + return value.HasValue ? value.Value.ToString($"F{casas}") : "-"; + } - return value.Value.ToString($"F{casas}"); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool GetSystemTimes(out FILETIME idleTime, out FILETIME kernelTime, out FILETIME userTime); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX buffer); + + [DllImport("powrprof.dll")] + private static extern uint CallNtPowerInformation(int informationLevel, IntPtr inputBuffer, int inputBufferLength, + [Out] PROCESSOR_POWER_INFORMATION[] outputBuffer, int outputBufferLength); + + [StructLayout(LayoutKind.Sequential)] + private struct FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private sealed class MEMORYSTATUSEX + { + public uint dwLength = (uint)Marshal.SizeOf(); + public uint dwMemoryLoad; + public ulong ullTotalPhys; + public ulong ullAvailPhys; + public ulong ullTotalPageFile; + public ulong ullAvailPageFile; + public ulong ullTotalVirtual; + public ulong ullAvailVirtual; + public ulong ullAvailExtendedVirtual; + } + + [StructLayout(LayoutKind.Sequential)] + private struct PROCESSOR_POWER_INFORMATION + { + public uint Number; + public uint MaxMhz; + public uint CurrentMhz; + public uint MhzLimit; + public uint MaxIdleState; + public uint CurrentIdleState; + } + + private sealed class CpuSensorSnapshot + { + public float? Temperature; + public float? PowerPackage; + public float? PowerCores; + public float? PowerGraphics; + public float? PowerDram; + public float? BusSpeed; + } + + private sealed class NvidiaSnapshot + { + public float? GpuLoad; + public float? MemoryLoad; + public float? Temperature; + public float? Power; + public float? GraphicsClock; + public float? MemoryClock; + public float? SmClock; + public float? FanPercent; + public float? VramTotalMb; + public float? VramUsedMb; + public float? VramFreeMb; + public float? PcieRxKbS; + public float? PcieTxKbS; + } + + private sealed class NvmlProvider : IDisposable + { + private const int Success = 0; + private readonly IntPtr _library; + private readonly IntPtr _device; + private readonly NvmlShutdown _shutdown; + private readonly NvmlGetUtilization _getUtilization; + private readonly NvmlGetMemoryInfo _getMemoryInfo; + private readonly NvmlGetTemperature _getTemperature; + private readonly NvmlGetUInt _getPowerUsage; + private readonly NvmlGetClock _getClock; + private readonly NvmlGetUInt _getFanSpeed; + private readonly NvmlGetPcieThroughput _getPcieThroughput; + private bool _disposed; + + public NvmlProvider() + { + string system32 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "System32", "nvml.dll"); + string nvsmi = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "NVIDIA Corporation", "NVSMI", "nvml.dll"); + + _library = CarregarNvml(); + + try + { + NvmlInit init = GetDelegate("nvmlInit_v2"); + _shutdown = GetDelegate("nvmlShutdown"); + NvmlGetDeviceCount getCount = GetDelegate("nvmlDeviceGetCount_v2"); + NvmlGetHandleByIndex getHandle = GetDelegate("nvmlDeviceGetHandleByIndex_v2"); + _getUtilization = GetDelegate("nvmlDeviceGetUtilizationRates"); + _getMemoryInfo = GetDelegate("nvmlDeviceGetMemoryInfo"); + _getTemperature = GetDelegate("nvmlDeviceGetTemperature"); + _getPowerUsage = GetOptionalDelegate("nvmlDeviceGetPowerUsage"); + _getClock = GetOptionalDelegate("nvmlDeviceGetClockInfo"); + _getFanSpeed = GetOptionalDelegate("nvmlDeviceGetFanSpeed"); + _getPcieThroughput = GetOptionalDelegate("nvmlDeviceGetPcieThroughput"); + + Check(init(), "nvmlInit_v2"); + Check(getCount(out uint count), "nvmlDeviceGetCount_v2"); + + if (count == 0) + throw new InvalidOperationException("NVML não encontrou nenhuma GPU NVIDIA."); + + Check(getHandle(0, out _device), "nvmlDeviceGetHandleByIndex_v2"); + } + catch + { + if (_library != IntPtr.Zero) + FreeLibrary(_library); + throw; + } + } + + public NvidiaSnapshot Coletar() + { + Check(_getUtilization(_device, out NvmlUtilization utilization), "nvmlDeviceGetUtilizationRates"); + Check(_getMemoryInfo(_device, out NvmlMemory memory), "nvmlDeviceGetMemoryInfo"); + NvidiaSnapshot snapshot = new NvidiaSnapshot + { + GpuLoad = utilization.gpu, + MemoryLoad = utilization.memory, + VramTotalMb = memory.total / (1024.0f * 1024.0f), + VramUsedMb = memory.used / (1024.0f * 1024.0f), + VramFreeMb = memory.free / (1024.0f * 1024.0f) + }; + + if (_getTemperature(_device, 0, out uint temp) == Success) + snapshot.Temperature = temp; + if (_getPowerUsage != null && _getPowerUsage(_device, out uint powerMw) == Success) + snapshot.Power = powerMw / 1000.0f; + if (_getClock != null && _getClock(_device, 0, out uint graphics) == Success) + snapshot.GraphicsClock = graphics; + if (_getClock != null && _getClock(_device, 1, out uint sm) == Success) + snapshot.SmClock = sm; + if (_getClock != null && _getClock(_device, 2, out uint memoryClock) == Success) + snapshot.MemoryClock = memoryClock; + if (_getFanSpeed != null && _getFanSpeed(_device, out uint fan) == Success) + snapshot.FanPercent = fan; + if (_getPcieThroughput != null && _getPcieThroughput(_device, 0, out uint tx) == Success) + snapshot.PcieTxKbS = tx; + if (_getPcieThroughput != null && _getPcieThroughput(_device, 1, out uint rx) == Success) + snapshot.PcieRxKbS = rx; + + return snapshot; + } + + public void Dispose() + { + if (_disposed) + return; + + _disposed = true; + + try + { + _shutdown?.Invoke(); + } + finally + { + if (_library != IntPtr.Zero) + FreeLibrary(_library); + } + } + + private T GetDelegate(string name) where T : Delegate + { + IntPtr address = GetProcAddress(_library, name); + + if (address == IntPtr.Zero) + { + int error = Marshal.GetLastWin32Error(); + throw new EntryPointNotFoundException( + $"{name} não foi encontrada na nvml.dll. Win32={error}." + ); + } + + return Marshal.GetDelegateForFunctionPointer(address); + } + + private T GetOptionalDelegate(string name) where T : Delegate + { + IntPtr address = GetProcAddress(_library, name); + + return address != IntPtr.Zero + ? Marshal.GetDelegateForFunctionPointer(address) + : null; + } + + private static void Check(int result, string operation) + { + if (result != Success) + throw new InvalidOperationException($"{operation} falhou. NVML={result}."); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlInit(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlShutdown(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetDeviceCount(out uint count); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetHandleByIndex(uint index, out IntPtr device); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetUtilization(IntPtr device, out NvmlUtilization utilization); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetMemoryInfo(IntPtr device, out NvmlMemory memory); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetTemperature(IntPtr device, uint sensorType, out uint temperature); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetUInt(IntPtr device, out uint value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetClock(IntPtr device, uint clockType, out uint clockMhz); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int NvmlGetPcieThroughput(IntPtr device, uint counter, out uint value); + + [StructLayout(LayoutKind.Sequential)] + private struct NvmlUtilization + { + public uint gpu; + public uint memory; + } + + [StructLayout(LayoutKind.Sequential)] + private struct NvmlMemory + { + public ulong total; + public ulong free; + public ulong used; + } + + + + + + + + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern IntPtr LoadLibrary(string lpFileName); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool FreeLibrary(IntPtr hModule); + + private static IntPtr CarregarNvml() + { + string system32 = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.Windows), + "System32", + "nvml.dll" + ); + + string nvsmi = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + "NVIDIA Corporation", + "NVSMI", + "nvml.dll" + ); + + IntPtr library = LoadLibrary(system32); + + if (library == IntPtr.Zero) + library = LoadLibrary(nvsmi); + + if (library == IntPtr.Zero) + library = LoadLibrary("nvml.dll"); + + if (library == IntPtr.Zero) + { + int error = Marshal.GetLastWin32Error(); + throw new DllNotFoundException( + $"nvml.dll não foi encontrada ou não pôde ser carregada. Win32={error}." + ); + } + + return library; + } + } } -} \ No newline at end of file +} diff --git a/AgroBase/AgroBase/Models/PerformanceModel_old.cs b/AgroBase/AgroBase/Models/PerformanceModel_old.cs new file mode 100644 index 000000000..39ca22085 --- /dev/null +++ b/AgroBase/AgroBase/Models/PerformanceModel_old.cs @@ -0,0 +1,823 @@ +//using OpenHardwareMonitor.Hardware; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using static AgroBase.Models.Enums; + +namespace AgroBase.Models +{ + /// + /// Snapshot de performance do NPC. + /// A falha de um sensor nunca deve interromper o loop principal do sensoriamento. + /// Valores sem uma nova leitura válida preservam o último valor conhecido. + /// + public class PerformanceModel_old + { + /* + private static readonly object ComputerLock = new object(); + private static readonly object LogLock = new object(); + private static readonly Dictionary UltimoLogPorOrigem = new Dictionary(); + private static readonly TimeSpan IntervaloRetentativaHardware = TimeSpan.FromMinutes(1); + private static readonly TimeSpan IntervaloLogRepetido = TimeSpan.FromMinutes(5); + + private static Computer computer; + private static DateTime _proximaTentativaHardwareUtc = DateTime.MinValue; + + private readonly object _coletaLock = new object(); + private readonly object _snapshotLock = new object(); + private DateTime _ultimaLeituraPerformanceUtc = DateTime.MinValue; + private bool _coletaEmAndamento; + + public PerformanceModel_old() : this(true) + { + } + + private PerformanceModel_old(bool inicializarHardware) + { + if (inicializarHardware) + GarantirComputerInicializado(); + } + + // ============================================================ + // Identificação / estado da coleta + // ============================================================ + + public DateTime Timestamp { get; set; } = DateTime.Now; + public long TimestampUnixMs { get; set; } + public DateTime? UltimaColetaConcluidaUtc { get; set; } + public DateTime? Hardware_ultima_leitura_valida_utc { get; set; } + public DateTime? Hardware_ultima_falha_utc { get; set; } + public bool Hardware_monitor_disponivel { get; set; } + public bool Ultima_coleta_completa { get; set; } + public int Hardware_falhas_consecutivas { get; set; } + public long Quantidade_coletas { get; set; } + public long Quantidade_falhas { get; set; } + public string Ultimo_erro { get; set; } + + public bool Hardware_dados_desatualizados + { + get + { + return !Hardware_ultima_leitura_valida_utc.HasValue || + DateTime.UtcNow - Hardware_ultima_leitura_valida_utc.Value > TimeSpan.FromSeconds(10); + } + } + + // ============================================================ + // CPU + // ============================================================ + + public float? CPU_load_percent { get; set; } + public float? CPU_temp_package_c { get; set; } + public float? CPU_power_package_w { get; set; } + public float? CPU_power_cores_w { get; set; } + public float? CPU_power_graphics_w { get; set; } + public float? CPU_power_dram_w { get; set; } + public float? CPU_clock_avg_mhz { get; set; } + public float? CPU_clock_max_mhz { get; set; } + public float? CPU_bus_speed_mhz { get; set; } + public float? CPU_load => CPU_load_percent; + public float? CPU_temp => CPU_temp_package_c; + + // ============================================================ + // RAM + // ============================================================ + + public float? RAM_usage_percent { get; set; } + public float? RAM_used_gb { get; set; } + public float? RAM_available_gb { get; set; } + public float? RAM_total_gb => RAM_used_gb.HasValue && RAM_available_gb.HasValue + ? RAM_used_gb.Value + RAM_available_gb.Value + : (float?)null; + public float? RAM_usage => RAM_usage_percent; + + // ============================================================ + // GPU + // ============================================================ + + public float? GPU_load_percent { get; set; } + public float? GPU_core_load_percent { get; set; } + public float? GPU_frame_buffer_load_percent { get; set; } + public float? GPU_memory_load_percent { get; set; } + public float? GPU_video_engine_load_percent { get; set; } + public float? GPU_bus_interface_load_percent { get; set; } + public float? GPU_temp_core_c { get; set; } + public float? GPU_power_w { get; set; } + public float? GPU_core_clock_mhz { get; set; } + public float? GPU_memory_clock_mhz { get; set; } + public float? GPU_shader_clock_mhz { get; set; } + public float? GPU_fan_percent { get; set; } + public float? GPU_fan_rpm { get; set; } + public float? GPU_pcie_rx { get; set; } + public float? GPU_pcie_tx { get; set; } + public float? GPU_load => GPU_load_percent; + public float? GPU_temp => GPU_temp_core_c; + + // ============================================================ + // GPU memória / VRAM + // ============================================================ + + public float? GPU_vram_total_mb { get; set; } + public float? GPU_vram_used_mb { get; set; } + public float? GPU_vram_free_mb { get; set; } + public float? GPU_vram_used_percent => GPU_vram_total_mb.HasValue && GPU_vram_total_mb.Value > 0 && GPU_vram_used_mb.HasValue + ? 100.0f * GPU_vram_used_mb.Value / GPU_vram_total_mb.Value + : GPU_memory_load_percent; + + // ============================================================ + // Disco + // ============================================================ + + public float? Disk_used_space_percent { get; set; } + public float? HDD_load => Disk_used_space_percent; + + // ============================================================ + // Processo atual + // ============================================================ + + public int Process_thread_count { get; set; } + public long Process_working_set_mb { get; set; } + public long Process_private_memory_mb { get; set; } + public long Process_virtual_memory_mb { get; set; } + public double Process_cpu_time_total_s { get; set; } + + // ============================================================ + // ThreadPool + // ============================================================ + + public int ThreadPool_available_worker_threads { get; set; } + public int ThreadPool_available_completion_port_threads { get; set; } + public int ThreadPool_max_worker_threads { get; set; } + public int ThreadPool_max_completion_port_threads { get; set; } + public int ThreadPool_used_worker_threads => Math.Max(0, ThreadPool_max_worker_threads - ThreadPool_available_worker_threads); + public int ThreadPool_used_completion_port_threads => Math.Max(0, ThreadPool_max_completion_port_threads - ThreadPool_available_completion_port_threads); + public float ThreadPool_used_worker_percent => ThreadPool_max_worker_threads <= 0 + ? 0 + : 100.0f * ThreadPool_used_worker_threads / ThreadPool_max_worker_threads; + public float ThreadPool_used_completion_port_percent => ThreadPool_max_completion_port_threads <= 0 + ? 0 + : 100.0f * ThreadPool_used_completion_port_threads / ThreadPool_max_completion_port_threads; + + public string PerformanceStr + { + get + { + return $"CPU={Fmt(CPU_load_percent)}% CPU_T={Fmt(CPU_temp_package_c)}C RAM={Fmt(RAM_usage_percent)}% " + + $"GPU={Fmt(GPU_load_percent)}% GPU_Core={Fmt(GPU_core_load_percent)}% GPU_Mem={Fmt(GPU_memory_load_percent)}% " + + $"VRAM={Fmt(GPU_vram_used_mb)}/{Fmt(GPU_vram_total_mb)}MB GPU_T={Fmt(GPU_temp_core_c)}C " + + $"GPU_P={Fmt(GPU_power_w)}W HW={(Hardware_monitor_disponivel ? "OK" : "INDISP")}"; + } + } + + // ============================================================ + // Atualização principal + // ============================================================ + + public void AtualizarDadosPerformance() + { + AtualizarDadosPerformance(TimeSpan.FromSeconds(1)); + } + + public void AtualizarDadosPerformance(TimeSpan intervaloMinimo) + { + if (intervaloMinimo < TimeSpan.Zero) + intervaloMinimo = TimeSpan.Zero; + + DateTime agoraUtc = DateTime.UtcNow; + + lock (_coletaLock) + { + if (_coletaEmAndamento || agoraUtc < _ultimaLeituraPerformanceUtc.Add(intervaloMinimo)) + return; + + _coletaEmAndamento = true; + _ultimaLeituraPerformanceUtc = agoraUtc; + } + + bool processoOk = false; + bool threadPoolOk = false; + bool hardwareOk = false; + string ultimoErro = null; + + try + { + processoOk = TentarAtualizar("Processo", AtualizarDadosProcesso, ref ultimoErro); + threadPoolOk = TentarAtualizar("ThreadPool", AtualizarDadosThreadPool, ref ultimoErro); + hardwareOk = AtualizarDadosHardware(ref ultimoErro); + + lock (_snapshotLock) + { + Timestamp = DateTime.Now; + TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + UltimaColetaConcluidaUtc = DateTime.UtcNow; + Ultima_coleta_completa = processoOk && threadPoolOk && hardwareOk; + Quantidade_coletas++; + + if (ultimoErro != null) + { + Ultimo_erro = ultimoErro; + Quantidade_falhas++; + } + else + { + Ultimo_erro = null; + } + } + } + catch (Exception ex) + { + RegistrarFalha("Coleta geral", ex); + + lock (_snapshotLock) + { + Timestamp = DateTime.Now; + TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + UltimaColetaConcluidaUtc = DateTime.UtcNow; + Ultima_coleta_completa = false; + Ultimo_erro = ResumirErro(ex); + Quantidade_coletas++; + Quantidade_falhas++; + } + } + finally + { + lock (_coletaLock) + { + _coletaEmAndamento = false; + } + } + } + + private bool AtualizarDadosHardware(ref string ultimoErro) + { + if (!GarantirComputerInicializado()) + { + lock (_snapshotLock) + { + Hardware_monitor_disponivel = false; + } + + return false; + } + + bool algumaLeituraValida = false; + bool houveFalha = false; + HardwareSnapshot leitura = new HardwareSnapshot(); + + try + { + lock (ComputerLock) + { + if (computer == null || computer.Hardware == null) + throw new InvalidOperationException("OpenHardwareMonitor não está inicializado."); + + foreach (IHardware hardwareItem in computer.Hardware) + { + try + { + hardwareItem.Update(); + + switch (hardwareItem.HardwareType) + { + case HardwareType.CPU: + LerCpu(hardwareItem, leitura); + algumaLeituraValida = true; + break; + + case HardwareType.RAM: + LerRam(hardwareItem, leitura); + algumaLeituraValida = true; + break; + + case HardwareType.GpuNvidia: + case HardwareType.GpuAti: + LerGpu(hardwareItem, leitura); + algumaLeituraValida = true; + break; + + case HardwareType.HDD: + LerDisco(hardwareItem, leitura); + algumaLeituraValida = true; + break; + } + } + catch (Exception ex) + { + houveFalha = true; + ultimoErro = ResumirErro(ex); + RegistrarFalha($"Hardware {hardwareItem?.Name ?? "desconhecido"}", ex); + } + } + } + + if (algumaLeituraValida) + AplicarHardwareSnapshot(leitura); + + lock (_snapshotLock) + { + Hardware_monitor_disponivel = algumaLeituraValida; + + if (algumaLeituraValida) + Hardware_ultima_leitura_valida_utc = DateTime.UtcNow; + + if (algumaLeituraValida && !houveFalha) + Hardware_falhas_consecutivas = 0; + else + Hardware_falhas_consecutivas++; + } + + return algumaLeituraValida && !houveFalha; + } + catch (Exception ex) + { + ultimoErro = ResumirErro(ex); + RegistrarFalha("OpenHardwareMonitor", ex); + MarcarFalhaHardware(); + ReiniciarComputerAposFalha(); + return false; + } + } + + private static bool GarantirComputerInicializado() + { + if (!Variaveis.IsAgroBase) + return false; + + if (computer != null) + return true; + + if (DateTime.UtcNow < _proximaTentativaHardwareUtc) + return false; + + lock (ComputerLock) + { + if (computer != null) + return true; + + if (DateTime.UtcNow < _proximaTentativaHardwareUtc) + return false; + + Computer novoComputer = null; + + try + { + novoComputer = new Computer + { + CPUEnabled = true, + GPUEnabled = true, + RAMEnabled = true, + HDDEnabled = true, + MainboardEnabled = false, + FanControllerEnabled = false + }; + + novoComputer.Open(); + computer = novoComputer; + _proximaTentativaHardwareUtc = DateTime.MinValue; + return true; + } + catch (Exception ex) + { + try + { + novoComputer?.Close(); + } + catch + { + } + + computer = null; + _proximaTentativaHardwareUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + RegistrarFalha("Inicialização OpenHardwareMonitor", ex); + return false; + } + } + } + + private void ReiniciarComputerAposFalha() + { + lock (ComputerLock) + { + try + { + computer?.Close(); + } + catch (Exception ex) + { + RegistrarFalha("Encerramento OpenHardwareMonitor", ex); + } + finally + { + computer = null; + _proximaTentativaHardwareUtc = DateTime.UtcNow.Add(IntervaloRetentativaHardware); + } + } + } + + public static void EncerrarMonitoramento() + { + lock (ComputerLock) + { + try + { + computer?.Close(); + } + catch (Exception ex) + { + RegistrarFalha("Encerramento OpenHardwareMonitor", ex); + } + finally + { + computer = null; + _proximaTentativaHardwareUtc = DateTime.MinValue; + } + } + } + + // ============================================================ + // Leitores por hardware + // ============================================================ + + private static void LerCpu(IHardware hardwareItem, HardwareSnapshot leitura) + { + float clockSum = 0; + int clockCount = 0; + + foreach (ISensor sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? string.Empty; + float value = sensor.Value.Value; + + if (sensor.SensorType == SensorType.Load && name == "CPU Total") + leitura.CpuLoad = value; + else if (sensor.SensorType == SensorType.Temperature && name == "CPU Package") + leitura.CpuTempPackage = value; + else if (sensor.SensorType == SensorType.Power && name == "CPU Package") + leitura.CpuPowerPackage = value; + else if (sensor.SensorType == SensorType.Power && name == "CPU Cores") + leitura.CpuPowerCores = value; + else if (sensor.SensorType == SensorType.Power && name == "CPU Graphics") + leitura.CpuPowerGraphics = value; + else if (sensor.SensorType == SensorType.Power && name == "CPU DRAM") + leitura.CpuPowerDram = value; + else if (sensor.SensorType == SensorType.Clock && name.StartsWith("CPU Core", StringComparison.OrdinalIgnoreCase)) + { + clockSum += value; + clockCount++; + leitura.CpuClockMax = MaxNullable(leitura.CpuClockMax, value); + } + else if (sensor.SensorType == SensorType.Clock && name == "Bus Speed") + leitura.CpuBusSpeed = value; + } + + if (clockCount > 0) + leitura.CpuClockAvg = clockSum / clockCount; + } + + private static void LerRam(IHardware hardwareItem, HardwareSnapshot leitura) + { + foreach (ISensor sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? string.Empty; + + if (sensor.SensorType == SensorType.Load && name == "Memory") + leitura.RamUsage = sensor.Value; + else if (sensor.SensorType == SensorType.Data && name == "Used Memory") + leitura.RamUsedGb = sensor.Value; + else if (sensor.SensorType == SensorType.Data && name == "Available Memory") + leitura.RamAvailableGb = sensor.Value; + } + } + + private static void LerGpu(IHardware hardwareItem, HardwareSnapshot leitura) + { + foreach (ISensor sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? string.Empty; + + if (sensor.SensorType == SensorType.Temperature && name == "GPU Core") + leitura.GpuTempCore = sensor.Value; + else if (sensor.SensorType == SensorType.Load && name == "GPU Core") + leitura.GpuCoreLoad = sensor.Value; + else if (sensor.SensorType == SensorType.Load && name == "GPU Frame Buffer") + leitura.GpuFrameBufferLoad = sensor.Value; + else if (sensor.SensorType == SensorType.Load && name == "GPU Memory") + leitura.GpuMemoryLoad = sensor.Value; + else if (sensor.SensorType == SensorType.Load && name == "GPU Video Engine") + leitura.GpuVideoEngineLoad = sensor.Value; + else if (sensor.SensorType == SensorType.Load && name == "GPU Bus Interface") + leitura.GpuBusInterfaceLoad = sensor.Value; + else if (sensor.SensorType == SensorType.Clock && name == "GPU Core") + leitura.GpuCoreClock = sensor.Value; + else if (sensor.SensorType == SensorType.Clock && name == "GPU Memory") + leitura.GpuMemoryClock = sensor.Value; + else if (sensor.SensorType == SensorType.Clock && name == "GPU Shader") + leitura.GpuShaderClock = sensor.Value; + else if (sensor.SensorType == SensorType.Control && name == "GPU Fan") + leitura.GpuFanPercent = sensor.Value; + else if (sensor.SensorType == SensorType.Fan && name == "GPU") + leitura.GpuFanRpm = sensor.Value; + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Total") + leitura.GpuVramTotalMb = sensor.Value; + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Used") + leitura.GpuVramUsedMb = sensor.Value; + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Free") + leitura.GpuVramFreeMb = sensor.Value; + else if (sensor.SensorType == SensorType.Power && name == "GPU Power") + leitura.GpuPower = sensor.Value; + else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Rx") + leitura.GpuPcieRx = sensor.Value; + else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Tx") + leitura.GpuPcieTx = sensor.Value; + } + } + + private static void LerDisco(IHardware hardwareItem, HardwareSnapshot leitura) + { + foreach (ISensor sensor in hardwareItem.Sensors) + { + if (sensor.Value.HasValue && sensor.SensorType == SensorType.Load && sensor.Name == "Used Space") + leitura.DiskUsedSpace = sensor.Value; + } + } + + private void AplicarHardwareSnapshot(HardwareSnapshot leitura) + { + lock (_snapshotLock) + { + CPU_load_percent = leitura.CpuLoad ?? CPU_load_percent; + CPU_temp_package_c = leitura.CpuTempPackage ?? CPU_temp_package_c; + CPU_power_package_w = leitura.CpuPowerPackage ?? CPU_power_package_w; + CPU_power_cores_w = leitura.CpuPowerCores ?? CPU_power_cores_w; + CPU_power_graphics_w = leitura.CpuPowerGraphics ?? CPU_power_graphics_w; + CPU_power_dram_w = leitura.CpuPowerDram ?? CPU_power_dram_w; + CPU_clock_avg_mhz = leitura.CpuClockAvg ?? CPU_clock_avg_mhz; + CPU_clock_max_mhz = leitura.CpuClockMax ?? CPU_clock_max_mhz; + CPU_bus_speed_mhz = leitura.CpuBusSpeed ?? CPU_bus_speed_mhz; + RAM_usage_percent = leitura.RamUsage ?? RAM_usage_percent; + RAM_used_gb = leitura.RamUsedGb ?? RAM_used_gb; + RAM_available_gb = leitura.RamAvailableGb ?? RAM_available_gb; + GPU_core_load_percent = leitura.GpuCoreLoad ?? GPU_core_load_percent; + GPU_frame_buffer_load_percent = leitura.GpuFrameBufferLoad ?? GPU_frame_buffer_load_percent; + GPU_memory_load_percent = leitura.GpuMemoryLoad ?? GPU_memory_load_percent; + GPU_video_engine_load_percent = leitura.GpuVideoEngineLoad ?? GPU_video_engine_load_percent; + GPU_bus_interface_load_percent = leitura.GpuBusInterfaceLoad ?? GPU_bus_interface_load_percent; + + float? gpuLoad = MaxNullable(leitura.GpuCoreLoad, leitura.GpuFrameBufferLoad, leitura.GpuMemoryLoad, + leitura.GpuVideoEngineLoad, leitura.GpuBusInterfaceLoad); + + GPU_load_percent = gpuLoad ?? GPU_load_percent; + GPU_temp_core_c = leitura.GpuTempCore ?? GPU_temp_core_c; + GPU_power_w = leitura.GpuPower ?? GPU_power_w; + GPU_core_clock_mhz = leitura.GpuCoreClock ?? GPU_core_clock_mhz; + GPU_memory_clock_mhz = leitura.GpuMemoryClock ?? GPU_memory_clock_mhz; + GPU_shader_clock_mhz = leitura.GpuShaderClock ?? GPU_shader_clock_mhz; + GPU_fan_percent = leitura.GpuFanPercent ?? GPU_fan_percent; + GPU_fan_rpm = leitura.GpuFanRpm ?? GPU_fan_rpm; + GPU_vram_total_mb = leitura.GpuVramTotalMb ?? GPU_vram_total_mb; + GPU_vram_used_mb = leitura.GpuVramUsedMb ?? GPU_vram_used_mb; + GPU_vram_free_mb = leitura.GpuVramFreeMb ?? GPU_vram_free_mb; + GPU_pcie_rx = leitura.GpuPcieRx ?? GPU_pcie_rx; + GPU_pcie_tx = leitura.GpuPcieTx ?? GPU_pcie_tx; + Disk_used_space_percent = leitura.DiskUsedSpace ?? Disk_used_space_percent; + } + } + + // ============================================================ + // Processo / ThreadPool + // ============================================================ + + private void AtualizarDadosProcesso() + { + using (Process proc = Process.GetCurrentProcess()) + { + int threadCount = proc.Threads.Count; + long workingSetMb = BytesToMb(proc.WorkingSet64); + long privateMemoryMb = BytesToMb(proc.PrivateMemorySize64); + long virtualMemoryMb = BytesToMb(proc.VirtualMemorySize64); + double cpuTimeTotalS = proc.TotalProcessorTime.TotalSeconds; + + lock (_snapshotLock) + { + Process_thread_count = threadCount; + Process_working_set_mb = workingSetMb; + Process_private_memory_mb = privateMemoryMb; + Process_virtual_memory_mb = virtualMemoryMb; + Process_cpu_time_total_s = cpuTimeTotalS; + } + } + } + + private void AtualizarDadosThreadPool() + { + ThreadPool.GetAvailableThreads(out int availableWorkerThreads, out int availableCompletionPortThreads); + ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads); + + lock (_snapshotLock) + { + ThreadPool_available_worker_threads = availableWorkerThreads; + ThreadPool_available_completion_port_threads = availableCompletionPortThreads; + ThreadPool_max_worker_threads = maxWorkerThreads; + ThreadPool_max_completion_port_threads = maxCompletionPortThreads; + } + } + + private bool TentarAtualizar(string origem, Action atualizacao, ref string ultimoErro) + { + try + { + atualizacao(); + return true; + } + catch (Exception ex) + { + ultimoErro = ResumirErro(ex); + RegistrarFalha(origem, ex); + return false; + } + } + + private void MarcarFalhaHardware() + { + lock (_snapshotLock) + { + Hardware_monitor_disponivel = false; + Hardware_ultima_falha_utc = DateTime.UtcNow; + Hardware_falhas_consecutivas++; + } + } + + // ============================================================ + // Clone / Snapshot consistente + // ============================================================ + + public PerformanceModel_old Clone() + { + lock (_snapshotLock) + { + return new PerformanceModel_old(false) + { + Timestamp = Timestamp, + TimestampUnixMs = TimestampUnixMs, + UltimaColetaConcluidaUtc = UltimaColetaConcluidaUtc, + Hardware_ultima_leitura_valida_utc = Hardware_ultima_leitura_valida_utc, + Hardware_ultima_falha_utc = Hardware_ultima_falha_utc, + Hardware_monitor_disponivel = Hardware_monitor_disponivel, + Ultima_coleta_completa = Ultima_coleta_completa, + Hardware_falhas_consecutivas = Hardware_falhas_consecutivas, + Quantidade_coletas = Quantidade_coletas, + Quantidade_falhas = Quantidade_falhas, + Ultimo_erro = Ultimo_erro, + CPU_load_percent = CPU_load_percent, + CPU_temp_package_c = CPU_temp_package_c, + CPU_power_package_w = CPU_power_package_w, + CPU_power_cores_w = CPU_power_cores_w, + CPU_power_graphics_w = CPU_power_graphics_w, + CPU_power_dram_w = CPU_power_dram_w, + CPU_clock_avg_mhz = CPU_clock_avg_mhz, + CPU_clock_max_mhz = CPU_clock_max_mhz, + CPU_bus_speed_mhz = CPU_bus_speed_mhz, + RAM_usage_percent = RAM_usage_percent, + RAM_used_gb = RAM_used_gb, + RAM_available_gb = RAM_available_gb, + GPU_load_percent = GPU_load_percent, + GPU_core_load_percent = GPU_core_load_percent, + GPU_frame_buffer_load_percent = GPU_frame_buffer_load_percent, + GPU_memory_load_percent = GPU_memory_load_percent, + GPU_video_engine_load_percent = GPU_video_engine_load_percent, + GPU_bus_interface_load_percent = GPU_bus_interface_load_percent, + GPU_temp_core_c = GPU_temp_core_c, + GPU_power_w = GPU_power_w, + GPU_core_clock_mhz = GPU_core_clock_mhz, + GPU_memory_clock_mhz = GPU_memory_clock_mhz, + GPU_shader_clock_mhz = GPU_shader_clock_mhz, + GPU_fan_percent = GPU_fan_percent, + GPU_fan_rpm = GPU_fan_rpm, + GPU_vram_total_mb = GPU_vram_total_mb, + GPU_vram_used_mb = GPU_vram_used_mb, + GPU_vram_free_mb = GPU_vram_free_mb, + GPU_pcie_rx = GPU_pcie_rx, + GPU_pcie_tx = GPU_pcie_tx, + Disk_used_space_percent = Disk_used_space_percent, + Process_thread_count = Process_thread_count, + Process_working_set_mb = Process_working_set_mb, + Process_private_memory_mb = Process_private_memory_mb, + Process_virtual_memory_mb = Process_virtual_memory_mb, + Process_cpu_time_total_s = Process_cpu_time_total_s, + ThreadPool_available_worker_threads = ThreadPool_available_worker_threads, + ThreadPool_available_completion_port_threads = ThreadPool_available_completion_port_threads, + ThreadPool_max_worker_threads = ThreadPool_max_worker_threads, + ThreadPool_max_completion_port_threads = ThreadPool_max_completion_port_threads + }; + } + } + + // ============================================================ + // Logs e helpers + // ============================================================ + + private static void RegistrarFalha(string origem, Exception ex) + { + try + { + DateTime agoraUtc = DateTime.UtcNow; + + lock (LogLock) + { + if (UltimoLogPorOrigem.TryGetValue(origem, out DateTime ultimoLogUtc) && + agoraUtc - ultimoLogUtc < IntervaloLogRepetido) + { + return; + } + + UltimoLogPorOrigem[origem] = agoraUtc; + } + + string mensagem = $"Falha na telemetria de performance ({origem}): {ResumirErro(ex)}"; + Variaveis.OperacaoEmAndamento?.InserirEventoOperacao(T_Code.Npc, StatusModulo.Falha, 0, mensagem, null, origem); + } + catch + { + // O registro da falha nunca pode gerar outra falha no loop de sensoriamento. + } + } + + private static string ResumirErro(Exception ex) + { + if (ex == null) + return "Erro desconhecido."; + + string mensagem = $"{ex.GetType().Name}: {ex.Message}"; + return mensagem.Length <= 500 ? mensagem : mensagem.Substring(0, 500); + } + + private static float? MaxNullable(params float?[] values) + { + float? max = null; + + foreach (float? value in values) + { + if (value.HasValue && (!max.HasValue || value.Value > max.Value)) + max = value.Value; + } + + return max; + } + + private static long BytesToMb(long bytes) + { + return bytes / (1024L * 1024L); + } + + private static string Fmt(float? value, int casas = 1) + { + return value.HasValue ? value.Value.ToString($"F{casas}") : "-"; + } + + private sealed class HardwareSnapshot + { + public float? CpuLoad; + public float? CpuTempPackage; + public float? CpuPowerPackage; + public float? CpuPowerCores; + public float? CpuPowerGraphics; + public float? CpuPowerDram; + public float? CpuClockAvg; + public float? CpuClockMax; + public float? CpuBusSpeed; + public float? RamUsage; + public float? RamUsedGb; + public float? RamAvailableGb; + public float? GpuCoreLoad; + public float? GpuFrameBufferLoad; + public float? GpuMemoryLoad; + public float? GpuVideoEngineLoad; + public float? GpuBusInterfaceLoad; + public float? GpuTempCore; + public float? GpuPower; + public float? GpuCoreClock; + public float? GpuMemoryClock; + public float? GpuShaderClock; + public float? GpuFanPercent; + public float? GpuFanRpm; + public float? GpuVramTotalMb; + public float? GpuVramUsedMb; + public float? GpuVramFreeMb; + public float? GpuPcieRx; + public float? GpuPcieTx; + public float? DiskUsedSpace; + } + + */ + } +} diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py index 38259bed2..fd416f966 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py @@ -80,6 +80,7 @@ class ContextoGlobalRedis: socket_connect_timeout=1.0, retry_on_timeout=True, health_check_interval=15, + protocol=2, ) # ============================================================ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/requirements.txt b/AgroBase/AgroBase/bin/x64/Debug/Python/requirements.txt index 71099c89a..565e8760a 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/requirements.txt and b/AgroBase/AgroBase/bin/x64/Debug/Python/requirements.txt differ