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