Files
AX-Copilot-Codex/src/AxCopilot/Services/PerformanceMonitorService.cs
lacvet f7cafe0cfc 런처 Agent Compare 기능 1차 이식 및 현재 런처 구조 연결
- Agent Compare 기준으로 런처 빠른 실행 칩, 검색 히스토리 탐색, 선택 항목 미리보기 패널을 현재 런처에 이식
- 하단 위젯 바, QuickLook(F3), 화면 OCR(F4), 관련 서비스/partial 파일을 현재 LauncherWindow/LauncherViewModel 구조에 연결
- UsageRankingService 상위 항목 조회와 SearchHistoryService를 추가해 실행 상위 경로/검색 기록이 실제 런처 동작에 반영되도록 정리
- README.md, docs/DEVELOPMENT.md에 이식 범위와 검증 결과를 2026-04-05 11:58 (KST) 기준으로 기록

검증 결과
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 경고 0 / 오류 0
2026-04-05 11:51:43 +09:00

134 lines
3.9 KiB
C#

using System.IO;
using System.Runtime.InteropServices;
namespace AxCopilot.Services;
internal sealed class PerformanceMonitorService
{
public static readonly PerformanceMonitorService Instance = new();
[StructLayout(LayoutKind.Sequential)]
private struct FILETIME
{
public uint Low;
public uint High;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetSystemTimes(out FILETIME idle, out FILETIME kernel, out FILETIME user);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
public double CpuPercent { get; private set; }
public double RamPercent { get; private set; }
public string RamText { get; private set; } = "";
public double DiskCPercent { get; private set; }
public string DiskCText { get; private set; } = "";
private System.Threading.Timer? _timer;
private FILETIME _prevIdle;
private FILETIME _prevKernel;
private FILETIME _prevUser;
private bool _hasPrev;
private PerformanceMonitorService() { }
public void StartPolling()
{
if (_timer != null)
return;
_timer = new System.Threading.Timer(_ => Sample(), null, 0, 2000);
}
public void StopPolling()
{
_timer?.Dispose();
_timer = null;
_hasPrev = false;
}
private void Sample()
{
SampleCpu();
SampleRam();
SampleDisk();
}
private void SampleCpu()
{
try
{
if (!GetSystemTimes(out var idle, out var kernel, out var user))
return;
if (_hasPrev)
{
var idleDelta = ToUlong(idle) - ToUlong(_prevIdle);
var kernelDelta = ToUlong(kernel) - ToUlong(_prevKernel);
var userDelta = ToUlong(user) - ToUlong(_prevUser);
var total = kernelDelta + userDelta;
var busy = total - idleDelta;
CpuPercent = total > 0 ? Math.Clamp(100.0 * busy / total, 0, 100) : 0;
}
_prevIdle = idle;
_prevKernel = kernel;
_prevUser = user;
_hasPrev = true;
}
catch { }
}
private void SampleRam()
{
try
{
var mem = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
if (!GlobalMemoryStatusEx(ref mem))
return;
RamPercent = mem.dwMemoryLoad;
var usedGb = (mem.ullTotalPhys - mem.ullAvailPhys) / (1024.0 * 1024 * 1024);
var totalGb = mem.ullTotalPhys / (1024.0 * 1024 * 1024);
RamText = $"{usedGb:F1}/{totalGb:F0}GB";
}
catch { }
}
private void SampleDisk()
{
try
{
var drive = DriveInfo.GetDrives()
.FirstOrDefault(d => d.IsReady && d.Name.StartsWith("C", StringComparison.OrdinalIgnoreCase));
if (drive == null)
return;
var usedBytes = drive.TotalSize - drive.AvailableFreeSpace;
DiskCPercent = 100.0 * usedBytes / drive.TotalSize;
var usedGb = usedBytes / (1024.0 * 1024 * 1024);
var totalGb = drive.TotalSize / (1024.0 * 1024 * 1024);
DiskCText = $"C:{usedGb:F0}/{totalGb:F0}GB";
}
catch { }
}
private static ulong ToUlong(FILETIME ft) => ((ulong)ft.High << 32) | ft.Low;
}