2 Commits

Author SHA1 Message Date
king 3d1c55468b Enhance monitoring features and UI:
- Add detailed CPU core monitoring option for better performance control
- Update monitoring settings in app configuration
- Improve parallel task execution for resource usage monitoring
- Modify Telegram notification service to skip alerts from svchost processes
- Add "Memory %" column to process table in HTML and update related JavaScript
- Create performance test scripts for API response time evaluation
2025-08-08 16:19:45 +08:00
king bb7c4c3d0e Refactor code structure for improved readability and maintainability 2025-08-08 12:26:54 +08:00
19 changed files with 313 additions and 74 deletions
+1
View File
@@ -8,6 +8,7 @@ namespace ResourceMonitorService.Configuration
public bool EnableDiskMonitoring { get; set; } = true; public bool EnableDiskMonitoring { get; set; } = true;
public bool EnableTemperatureMonitoring { get; set; } = true; public bool EnableTemperatureMonitoring { get; set; } = true;
public bool EnableProcessMonitoring { get; set; } = true; public bool EnableProcessMonitoring { get; set; } = true;
public bool EnableDetailedCpuCoreMonitoring { get; set; } = false; // Disable by default for better performance
public bool EnableGameDetection { get; set; } = true; public bool EnableGameDetection { get; set; } = true;
public bool EnableAlerts { get; set; } = true; public bool EnableAlerts { get; set; } = true;
public int MaxProcessesToTrack { get; set; } = 10; public int MaxProcessesToTrack { get; set; } = 10;
+1 -1
View File
@@ -41,7 +41,7 @@ namespace ResourceMonitorService
.ConfigureWebHostDefaults(webBuilder => .ConfigureWebHostDefaults(webBuilder =>
{ {
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://localhost:5000", "https://localhost:5001"); // URLs are now configured via appsettings.{Environment}.json files
}) })
.ConfigureServices((hostContext, services) => .ConfigureServices((hostContext, services) =>
{ {
-1
View File
@@ -4,7 +4,6 @@
"ResourceMonitorService": { "ResourceMonitorService": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"applicationUrl": "http://localhost:5000;https://localhost:5001",
"environmentVariables": { "environmentVariables": {
"DOTNET_ENVIRONMENT": "Development" "DOTNET_ENVIRONMENT": "Development"
} }
+34 -12
View File
@@ -4,7 +4,11 @@ A comprehensive system monitoring service with a modern web dashboard for real-t
## 🌟 New: Web Dashboard ## 🌟 New: Web Dashboard
Access the interactive web dashboard at `http://localhost:5000` featuring: Access the interactive web dashboard:
- **Development**: `http://localhost:5000`
- **Release/Production**: `http://localhost:24142`
Features:
- **Real-time Monitoring**: Live updates every 15 seconds via SignalR - **Real-time Monitoring**: Live updates every 15 seconds via SignalR
- **Responsive Design**: Mobile-friendly interface built with Tailwind CSS - **Responsive Design**: Mobile-friendly interface built with Tailwind CSS
@@ -90,14 +94,22 @@ For security reasons, the system control feature is hidden by default. To access
## 📡 API Endpoints ## 📡 API Endpoints
The service runs a web server on `http://localhost:5000` providing: The service runs a web server providing:
- **Development**: `http://localhost:5000`
- **Release/Production**: `http://localhost:24142`
Both environments provide:
### Web Interface ### Web Interface
- `GET /` - **Main Dashboard** - Interactive web interface for monitoring - `GET /` - **Main Dashboard** - Interactive web interface for monitoring
- `GET /swagger` - **API Documentation** - Interactive API explorer and documentation - `GET /swagger` - **API Documentation** - Interactive API explorer and documentation
### REST API ### REST API
All API endpoints are available at `http://localhost:5000/api/[endpoint]`: All API endpoints are available at:
- **Development**: `http://localhost:5000/api/[endpoint]`
- **Release/Production**: `http://localhost:24142/api/[endpoint]`
Endpoints:
### System Information ### System Information
- `GET /api/resource/usage` - **Complete resource overview** - All monitoring data in one call - `GET /api/resource/usage` - **Complete resource overview** - All monitoring data in one call
@@ -149,7 +161,11 @@ All API endpoints are available at `http://localhost:5000/api/[endpoint]`:
cd C:\Work\DEV\ResourceUsageAPI cd C:\Work\DEV\ResourceUsageAPI
dotnet run --configuration Release dotnet run --configuration Release
``` ```
Then open your browser to `http://localhost:5000` for the interactive dashboard. Then open your browser to:
- Development: `http://localhost:5000`
- Release/Production: `http://localhost:24142`
for the interactive dashboard.
### Option 2: Windows Service (Production) ### Option 2: Windows Service (Production)
```powershell ```powershell
@@ -221,8 +237,10 @@ For development and testing:
# Run in development mode with hot reload # Run in development mode with hot reload
dotnet run --environment Development dotnet run --environment Development
# Access the dashboard at http://localhost:5000 # Access the dashboard at:
# Access Swagger API documentation at http://localhost:5000/swagger # - Development: http://localhost:5000
# - Release/Production: http://localhost:24142
# Access Swagger API documentation at the same URL + /swagger
``` ```
### Troubleshooting ### Troubleshooting
@@ -230,7 +248,7 @@ dotnet run --environment Development
- **Service won't start**: Check the logs in the `logs/` directory - **Service won't start**: Check the logs in the `logs/` directory
- **No GPU data**: Make sure you have an NVIDIA GPU and drivers installed - **No GPU data**: Make sure you have an NVIDIA GPU and drivers installed
- **High CPU usage**: Adjust monitoring intervals in `appsettings.json` - **High CPU usage**: Adjust monitoring intervals in `appsettings.json`
- **Web dashboard not accessible**: Verify firewall settings and ensure port 5000 is available - **Web dashboard not accessible**: Verify firewall settings and ensure the appropriate port is available (5000 for development, 24142 for release/production)
- **Game detection issues**: Check if games are running from standard installation directories - **Game detection issues**: Check if games are running from standard installation directories
- **API errors**: Verify endpoints using Swagger documentation at `/swagger` - **API errors**: Verify endpoints using Swagger documentation at `/swagger`
- **Performance issues**: Consider increasing `UpdateIntervalMs` in configuration - **Performance issues**: Consider increasing `UpdateIntervalMs` in configuration
@@ -468,23 +486,27 @@ The service supports real-time alert notifications via Telegram bot. To set up T
## 🔧 PowerShell Usage Examples ## 🔧 PowerShell Usage Examples
```powershell ```powershell
# Access the web dashboard # Access the web dashboard (adjust port based on environment)
# Development:
Start-Process "http://localhost:5000" Start-Process "http://localhost:5000"
# Release/Production:
Start-Process "http://localhost:24142"
# Get complete resource overview # Get complete resource overview (adjust URL as needed)
$resources = Invoke-RestMethod -Uri "http://localhost:5000/api/resource/usage" $baseUrl = "http://localhost:24142" # Use 5000 for development
$resources = Invoke-RestMethod -Uri "$baseUrl/api/resource/usage"
Write-Host "CPU: $($resources.cpu.usage.ToString('F1'))%" Write-Host "CPU: $($resources.cpu.usage.ToString('F1'))%"
Write-Host "Memory: $($resources.memory.usagePercentage.ToString('F1'))%" Write-Host "Memory: $($resources.memory.usagePercentage.ToString('F1'))%"
Write-Host "GPU: $($resources.gpu.usage)%" Write-Host "GPU: $($resources.gpu.usage)%"
# Get system information # Get system information
$systemInfo = Invoke-RestMethod -Uri "http://localhost:5000/api/resource/system-info" $systemInfo = Invoke-RestMethod -Uri "$baseUrl/api/resource/system-info"
Write-Host "Machine: $($systemInfo.machineName)" Write-Host "Machine: $($systemInfo.machineName)"
Write-Host "OS: $($systemInfo.osVersion)" Write-Host "OS: $($systemInfo.osVersion)"
Write-Host "CPU: $($systemInfo.cpuName)" Write-Host "CPU: $($systemInfo.cpuName)"
# Get disk usage # Get disk usage
$disks = Invoke-RestMethod -Uri "http://localhost:5000/api/resource/disks" $disks = Invoke-RestMethod -Uri "$baseUrl/api/resource/disks"
foreach ($disk in $disks) { foreach ($disk in $disks) {
$freeGB = [math]::Round($disk.freeSpace / 1GB, 1) $freeGB = [math]::Round($disk.freeSpace / 1GB, 1)
$totalGB = [math]::Round($disk.totalSize / 1GB, 1) $totalGB = [math]::Round($disk.totalSize / 1GB, 1)
+29 -32
View File
@@ -96,32 +96,26 @@ namespace ResourceMonitorService.Services
{ {
var timestamp = DateTime.Now; var timestamp = DateTime.Now;
var tasks = new List<Task> // Execute all monitoring tasks in parallel and capture results
{ var cpuTask = GetCpuUsageAsync();
Task.Run(async () => await GetCpuUsageAsync()), var memoryTask = GetMemoryUsageAsync();
Task.Run(async () => await GetMemoryUsageAsync()) var gpuTask = _settings.EnableGpuMonitoring ? GetGpuUsageAsync() : Task.FromResult(new GpuUsage());
}; var diskTask = _settings.EnableDiskMonitoring ? GetDiskUsageAsync() : Task.FromResult(new List<DiskUsage>());
var processTask = _settings.EnableProcessMonitoring ? GetTopProcessesAsync(_settings.MaxProcessesToTrack) : Task.FromResult(new List<ProcessInfo>());
var temperatureTask = _settings.EnableTemperatureMonitoring ? GetTemperatureInfoAsync() : Task.FromResult(new TemperatureInfo());
if (_settings.EnableGpuMonitoring) // Wait for all tasks to complete
tasks.Add(Task.Run(async () => await GetGpuUsageAsync())); await Task.WhenAll(cpuTask, memoryTask, gpuTask, diskTask, processTask, temperatureTask);
if (_settings.EnableDiskMonitoring)
tasks.Add(Task.Run(async () => await GetDiskUsageAsync()));
if (_settings.EnableProcessMonitoring)
tasks.Add(Task.Run(async () => await GetTopProcessesAsync(_settings.MaxProcessesToTrack)));
await Task.WhenAll(tasks);
return new ResourceUsage return new ResourceUsage
{ {
Timestamp = timestamp, Timestamp = timestamp,
CPU = await GetCpuUsageAsync(), CPU = await cpuTask,
Memory = await GetMemoryUsageAsync(), Memory = await memoryTask,
GPU = _settings.EnableGpuMonitoring ? await GetGpuUsageAsync() : new GpuUsage(), GPU = await gpuTask,
Disks = _settings.EnableDiskMonitoring ? await GetDiskUsageAsync() : new List<DiskUsage>(), Disks = await diskTask,
TopProcesses = _settings.EnableProcessMonitoring ? await GetTopProcessesAsync(_settings.MaxProcessesToTrack) : new List<ProcessInfo>(), TopProcesses = await processTask,
Temperature = _settings.EnableTemperatureMonitoring ? await GetTemperatureInfoAsync() : new TemperatureInfo() Temperature = await temperatureTask
}; };
} }
@@ -136,20 +130,23 @@ namespace ResourceMonitorService.Services
#pragma warning disable CA1416 // Validate platform compatibility #pragma warning disable CA1416 // Validate platform compatibility
var usage = _counters.TryGetValue("cpu", out var cpuCounter) ? cpuCounter.NextValue() : 0f; var usage = _counters.TryGetValue("cpu", out var cpuCounter) ? cpuCounter.NextValue() : 0f;
// Get per-core usage // Get per-core usage (only if enabled for performance)
var coreUsages = new List<float>(); var coreUsages = new List<float>();
for (int i = 0; i < Environment.ProcessorCount; i++) if (_settings.EnableDetailedCpuCoreMonitoring)
{ {
try for (int i = 0; i < Environment.ProcessorCount; i++)
{ {
using var coreCounter = new PerformanceCounter("Processor", "% Processor Time", i.ToString()); try
coreCounter.NextValue(); {
Thread.Sleep(100); // Small delay for accurate reading using var coreCounter = new PerformanceCounter("Processor", "% Processor Time", i.ToString());
coreUsages.Add(coreCounter.NextValue()); coreCounter.NextValue();
} Thread.Sleep(50); // Reduced delay for faster reading while maintaining accuracy
catch coreUsages.Add(coreCounter.NextValue());
{ }
coreUsages.Add(0f); catch
{
coreUsages.Add(0f);
}
} }
} }
+18
View File
@@ -77,6 +77,15 @@ namespace ResourceMonitorService.Services
return; return;
} }
// Ignore alerts from svchost processes
if (alert.Component.Contains("svchost", StringComparison.OrdinalIgnoreCase) ||
alert.Message.Contains("svchost", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation("Skipping Telegram alert for svchost process: {Component} - {Message}",
alert.Component, alert.Message);
return;
}
var message = FormatAlertMessage(alert, _telegramSettings.MessageTemplate); var message = FormatAlertMessage(alert, _telegramSettings.MessageTemplate);
foreach (var chatId in _telegramSettings.ChatIds) foreach (var chatId in _telegramSettings.ChatIds)
@@ -110,6 +119,15 @@ namespace ResourceMonitorService.Services
if (_botClient == null || !_telegramSettings.IsEnabled || !_telegramSettings.SendResolutionNotifications) if (_botClient == null || !_telegramSettings.IsEnabled || !_telegramSettings.SendResolutionNotifications)
return; return;
// Ignore alerts from svchost processes
if (alert.Component.Contains("svchost", StringComparison.OrdinalIgnoreCase) ||
alert.Message.Contains("svchost", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation("Skipping Telegram resolution notification for svchost process: {Component} - {Message}",
alert.Component, alert.Message);
return;
}
var message = FormatAlertMessage(alert, _telegramSettings.ResolutionTemplate); var message = FormatAlertMessage(alert, _telegramSettings.ResolutionTemplate);
foreach (var chatId in _telegramSettings.ChatIds) foreach (var chatId in _telegramSettings.ChatIds)
+10
View File
@@ -4,5 +4,15 @@
"Default": "Information", "Default": "Information",
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
},
"Https": {
"Url": "https://*:5001"
}
}
} }
} }
+15
View File
@@ -0,0 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:24142"
}
}
}
}
+2 -11
View File
@@ -9,16 +9,6 @@
} }
}, },
"RunAsWindowsService": true, "RunAsWindowsService": true,
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
},
"Https": {
"Url": "https://*:5001"
}
}
},
"ApiSettings": { "ApiSettings": {
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a", "ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a",
"RequireApiKey": false, "RequireApiKey": false,
@@ -32,13 +22,14 @@
"BasePath": "/api" "BasePath": "/api"
}, },
"MonitoringSettings": { "MonitoringSettings": {
"UpdateIntervalMs": 120000, "UpdateIntervalMs": 60000,
"DataRetentionDays": 7, "DataRetentionDays": 7,
"EnableGpuMonitoring": true, "EnableGpuMonitoring": true,
"EnableDiskMonitoring": true, "EnableDiskMonitoring": true,
"EnableNetworkMonitoring": true, "EnableNetworkMonitoring": true,
"EnableTemperatureMonitoring": true, "EnableTemperatureMonitoring": true,
"EnableProcessMonitoring": true, "EnableProcessMonitoring": true,
"EnableDetailedCpuCoreMonitoring": false,
"EnableGameDetection": true, "EnableGameDetection": true,
"EnableAlerts": true, "EnableAlerts": true,
"MaxProcessesToTrack": 10, "MaxProcessesToTrack": 10,
+3 -2
View File
@@ -23,6 +23,7 @@ Start-Sleep -Seconds 2
Copy-Item "install-service.ps1" $TEMP_PATH Copy-Item "install-service.ps1" $TEMP_PATH
Copy-Item "start-service.bat" $TEMP_PATH Copy-Item "start-service.bat" $TEMP_PATH
Copy-Item "appsettings.json" $TEMP_PATH Copy-Item "appsettings.json" $TEMP_PATH
Copy-Item "appsettings.Production.json" $TEMP_PATH -ErrorAction SilentlyContinue
Copy-Item "README.md" $TEMP_PATH -ErrorAction SilentlyContinue Copy-Item "README.md" $TEMP_PATH -ErrorAction SilentlyContinue
# Create deployment readme # Create deployment readme
@@ -35,8 +36,8 @@ Copy-Item "README.md" $TEMP_PATH -ErrorAction SilentlyContinue
3. Execute: .\install-service.ps1 3. Execute: .\install-service.ps1
## Access ## Access
- Web Dashboard: http://localhost:5000 - Web Dashboard: http://localhost:24142
- API Health: http://localhost:5000/api/health - API Health: http://localhost:24142/api/health
Generated: $(Get-Date) Generated: $(Get-Date)
"@ | Out-File "$TEMP_PATH\DEPLOYMENT.txt" -Encoding UTF8 "@ | Out-File "$TEMP_PATH\DEPLOYMENT.txt" -Encoding UTF8
+30 -11
View File
@@ -77,6 +77,25 @@ try {
exit 1 exit 1
} }
# Check if service is currently running and stop it before building
Write-Host "Checking for existing service..."
try {
$existingService = Get-Service -Name $SERVICE_NAME -ErrorAction SilentlyContinue
if ($existingService) {
Write-Host "Found existing service: $($existingService.Status)" -ForegroundColor Yellow
if ($existingService.Status -eq "Running") {
Write-Host "Stopping running service before build..."
Stop-Service -Name $SERVICE_NAME -Force -ErrorAction Stop
Write-Host "Service stopped successfully" -ForegroundColor Green
Start-Sleep -Seconds 2 # Give it a moment to fully stop
}
} else {
Write-Host "No existing service found" -ForegroundColor Gray
}
} catch {
Write-Host "Warning: Could not check existing service status: $($_.Exception.Message)" -ForegroundColor Yellow
}
# Build the service in release mode # Build the service in release mode
Write-Host "Building service..." Write-Host "Building service..."
try { try {
@@ -141,12 +160,12 @@ try {
# Remove old rule if it exists # Remove old rule if it exists
Remove-NetFirewallRule -DisplayName "Resource Monitor Service" -ErrorAction SilentlyContinue Remove-NetFirewallRule -DisplayName "Resource Monitor Service" -ErrorAction SilentlyContinue
# Create new rule for port 5000 (web dashboard) # Create new rule for port 24142 (web dashboard)
New-NetFirewallRule -DisplayName "Resource Monitor Service" -Direction Inbound -Protocol TCP -LocalPort 5000 -Action Allow -Profile Any -ErrorAction Stop New-NetFirewallRule -DisplayName "Resource Monitor Service" -Direction Inbound -Protocol TCP -LocalPort 24142 -Action Allow -Profile Any -ErrorAction Stop
Write-Host "Firewall rule created for web dashboard (port 5000)" -ForegroundColor Green Write-Host "Firewall rule created for web dashboard (port 24142)" -ForegroundColor Green
} catch { } catch {
Write-Host "WARNING: Failed to create firewall rule. You may need to configure manually." -ForegroundColor Yellow Write-Host "WARNING: Failed to create firewall rule. You may need to configure manually." -ForegroundColor Yellow
Write-Host "Manual command: New-NetFirewallRule -DisplayName 'Resource Monitor Service' -Direction Inbound -Protocol TCP -LocalPort 5000 -Action Allow" -ForegroundColor Gray Write-Host "Manual command: New-NetFirewallRule -DisplayName 'Resource Monitor Service' -Direction Inbound -Protocol TCP -LocalPort 24142 -Action Allow" -ForegroundColor Gray
} }
# Start the service # Start the service
@@ -169,9 +188,9 @@ Write-Host
Write-Host "=== Installation Complete ===" -ForegroundColor Cyan Write-Host "=== Installation Complete ===" -ForegroundColor Cyan
Write-Host "Service Name: $SERVICE_NAME" -ForegroundColor White Write-Host "Service Name: $SERVICE_NAME" -ForegroundColor White
Write-Host "Installation Path: $INSTALL_PATH" -ForegroundColor White Write-Host "Installation Path: $INSTALL_PATH" -ForegroundColor White
Write-Host "Web Dashboard: http://localhost:5000" -ForegroundColor Yellow Write-Host "Web Dashboard: http://localhost:24142" -ForegroundColor Yellow
Write-Host "API Documentation: http://localhost:5000/swagger" -ForegroundColor Yellow Write-Host "API Documentation: http://localhost:24142/swagger" -ForegroundColor Yellow
Write-Host "API Health Check: http://localhost:5000/api/health" -ForegroundColor White Write-Host "API Health Check: http://localhost:24142/api/health" -ForegroundColor White
Write-Host Write-Host
Write-Host "The service is now running and will start automatically with Windows." -ForegroundColor Green Write-Host "The service is now running and will start automatically with Windows." -ForegroundColor Green
Write-Host "You can manage it through Services.msc or using PowerShell commands:" -ForegroundColor White Write-Host "You can manage it through Services.msc or using PowerShell commands:" -ForegroundColor White
@@ -187,15 +206,15 @@ Write-Host
Write-Host "Testing web dashboard..." -ForegroundColor Yellow Write-Host "Testing web dashboard..." -ForegroundColor Yellow
Start-Sleep -Seconds 5 Start-Sleep -Seconds 5
try { try {
$response = Invoke-RestMethod -Uri "http://localhost:5000/api/health" -TimeoutSec 10 $response = Invoke-RestMethod -Uri "http://localhost:24142/api/health" -TimeoutSec 10
Write-Host "Web Dashboard Test: SUCCESS" -ForegroundColor Green Write-Host "Web Dashboard Test: SUCCESS" -ForegroundColor Green
Write-Host "Service Status: $($response.status)" -ForegroundColor White Write-Host "Service Status: $($response.status)" -ForegroundColor White
Write-Host "Service Uptime: $($response.uptime)" -ForegroundColor White Write-Host "Service Uptime: $($response.uptime)" -ForegroundColor White
Write-Host Write-Host
Write-Host "🎉 Web Dashboard is ready at: http://localhost:5000" -ForegroundColor Green Write-Host "Web Dashboard is ready at: http://localhost:24142" -ForegroundColor Green
Write-Host "📖 API Documentation at: http://localhost:5000/swagger" -ForegroundColor Green Write-Host "API Documentation at: http://localhost:24142/swagger" -ForegroundColor Green
} catch { } catch {
Write-Host "Web Dashboard Test: FAILED" -ForegroundColor Red Write-Host "Web Dashboard Test: FAILED" -ForegroundColor Red
Write-Host "The service may still be starting up. Wait a few minutes and try accessing:" -ForegroundColor Yellow Write-Host "The service may still be starting up. Wait a few minutes and try accessing:" -ForegroundColor Yellow
Write-Host "http://localhost:5000" -ForegroundColor White Write-Host "http://localhost:24142" -ForegroundColor White
} }
+27
View File
@@ -0,0 +1,27 @@
# Quick API Test Command
# Run this after starting the service with: dotnet run
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try {
$response = Invoke-WebRequest -Uri "http://localhost:5000/api/resource/usage" -UseBasicParsing
$stopwatch.Stop()
$content = $response.Content | ConvertFrom-Json
Write-Host "✅ API Response Time: $($stopwatch.ElapsedMilliseconds)ms" -ForegroundColor Green
Write-Host " Status: $($response.StatusCode)" -ForegroundColor Cyan
Write-Host " CPU Usage: $($content.CPU.Usage)%" -ForegroundColor White
Write-Host " Memory Usage: $($content.Memory.UsagePercentage)%" -ForegroundColor White
Write-Host " Core Count: $($content.CPU.CoreUsages.Length)" -ForegroundColor White
if ($stopwatch.ElapsedMilliseconds -lt 1000) {
Write-Host "🚀 Excellent performance!" -ForegroundColor Green
} elseif ($stopwatch.ElapsedMilliseconds -lt 2000) {
Write-Host "✅ Good performance" -ForegroundColor Yellow
} else {
Write-Host "⚠️ Could be faster" -ForegroundColor Red
}
}
catch {
$stopwatch.Stop()
Write-Host "❌ Error after $($stopwatch.ElapsedMilliseconds)ms: $($_.Exception.Message)" -ForegroundColor Red
}
+3 -3
View File
@@ -6,8 +6,8 @@ echo.
echo This service will monitor your VM's resources and provide a REST API echo This service will monitor your VM's resources and provide a REST API
echo for remote monitoring from your Unraid server. echo for remote monitoring from your Unraid server.
echo. echo.
echo Service will be available at: http://localhost:5000 echo Service will be available at: http://localhost:24142
echo API Documentation: http://localhost:5000/api/health echo API Documentation: http://localhost:24142/api/health
echo. echo.
echo Press Ctrl+C to stop the service echo Press Ctrl+C to stop the service
echo. echo.
@@ -34,7 +34,7 @@ if errorlevel 1 (
) )
echo. echo.
echo Starting service on http://localhost:5000 echo Starting service on http://localhost:24142
echo. echo.
dotnet run --configuration Release dotnet run --configuration Release
+56
View File
@@ -0,0 +1,56 @@
# Performance test script for ResourceUsage API
Write-Host "Waiting for service to start..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
Write-Host "Testing API response time..." -ForegroundColor Green
$times = @()
$errors = 0
for ($i = 1; $i -le 5; $i++) {
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try {
$response = Invoke-WebRequest -Uri "http://localhost:5000/api/resource/usage" -UseBasicParsing -TimeoutSec 30
$stopwatch.Stop()
$time = $stopwatch.ElapsedMilliseconds
$times += $time
Write-Host "Test $i - Response time: ${time}ms - Status: $($response.StatusCode)" -ForegroundColor Cyan
# Show first response content for verification
if ($i -eq 1) {
$jsonContent = $response.Content | ConvertFrom-Json
Write-Host "Sample response - CPU Usage: $($jsonContent.CPU.Usage)%" -ForegroundColor White
}
}
catch {
$stopwatch.Stop()
$errors++
Write-Host "Test $i - Error after $($stopwatch.ElapsedMilliseconds)ms: $($_.Exception.Message)" -ForegroundColor Red
}
if ($i -lt 5) {
Start-Sleep -Seconds 2
}
}
if ($times.Count -gt 0) {
$avgTime = ($times | Measure-Object -Average).Average
$minTime = ($times | Measure-Object -Minimum).Minimum
$maxTime = ($times | Measure-Object -Maximum).Maximum
Write-Host "`nPerformance Results:" -ForegroundColor Green
Write-Host " Average response time: $([math]::Round($avgTime, 2))ms" -ForegroundColor White
Write-Host " Minimum response time: ${minTime}ms" -ForegroundColor White
Write-Host " Maximum response time: ${maxTime}ms" -ForegroundColor White
Write-Host " Successful requests: $($times.Count)/5" -ForegroundColor White
Write-Host " Failed requests: $errors/5" -ForegroundColor White
if ($avgTime -lt 1000) {
Write-Host "✅ Performance looks good!" -ForegroundColor Green
} elseif ($avgTime -lt 3000) {
Write-Host "⚠️ Performance is acceptable but could be better" -ForegroundColor Yellow
} else {
Write-Host "❌ Performance needs improvement" -ForegroundColor Red
}
} else {
Write-Host "❌ All requests failed!" -ForegroundColor Red
}
+1
View File
@@ -140,6 +140,7 @@
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Process</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Process</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">CPU %</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">CPU %</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Memory</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Memory</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Memory %</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
</tr> </tr>
</thead> </thead>
+5 -1
View File
@@ -428,7 +428,7 @@ class ResourceDashboard {
if (!processes || !Array.isArray(processes)) { if (!processes || !Array.isArray(processes)) {
const row = document.createElement('tr'); const row = document.createElement('tr');
row.innerHTML = '<td colspan="4" class="px-4 py-4 text-center text-gray-500">No process data available</td>'; row.innerHTML = '<td colspan="5" class="px-4 py-4 text-center text-gray-500">No process data available</td>';
tableBody.appendChild(row); tableBody.appendChild(row);
return; return;
} }
@@ -448,6 +448,7 @@ class ResourceDashboard {
const processId = process.processId || 0; const processId = process.processId || 0;
const cpuUsage = process.cpuUsage || 0; const cpuUsage = process.cpuUsage || 0;
const memoryUsage = process.memoryUsage || 0; const memoryUsage = process.memoryUsage || 0;
const memoryUsagePercentage = process.memoryUsagePercentage || 0;
row.innerHTML = ` row.innerHTML = `
<td class="px-4 py-4 whitespace-nowrap"> <td class="px-4 py-4 whitespace-nowrap">
@@ -462,6 +463,9 @@ class ResourceDashboard {
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900"> <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
${(memoryUsage / 1024 / 1024).toFixed(1)} MB ${(memoryUsage / 1024 / 1024).toFixed(1)} MB
</td> </td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
${memoryUsagePercentage.toFixed(1)}%
</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900"> <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
<button onclick="dashboard.killProcess(${processId}, '${processName}')" <button onclick="dashboard.killProcess(${processId}, '${processName}')"
class="${killButtonClass}" ${killButtonDisabled}> class="${killButtonClass}" ${killButtonDisabled}>
+78
View File
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Process Table Test</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-8">
<div class="max-w-4xl mx-auto">
<h1 class="text-2xl font-bold mb-6">Process Table with Memory Percentage</h1>
<div class="bg-white rounded-lg shadow-lg p-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">
<i class="fas fa-list mr-2"></i>Top Processes
</h2>
<div class="overflow-x-auto">
<table class="min-w-full table-auto">
<thead>
<tr class="bg-gray-50">
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Process</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">CPU %</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Memory</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Memory %</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-gray-50">
<td class="px-4 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="text-sm font-medium text-gray-900">chrome.exe</div>
<div class="text-sm text-gray-500 ml-2">PID: 1234</div>
</div>
</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">15.2%</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">512.3 MB</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">3.2%</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
<button class="bg-red-500 hover:bg-red-700 text-white px-2 py-1 rounded text-xs">
Kill
</button>
</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-4 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="text-sm font-medium text-gray-900">notepad.exe</div>
<div class="text-sm text-gray-500 ml-2">PID: 5678</div>
</div>
</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">2.1%</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">25.6 MB</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">0.2%</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
<button class="bg-red-500 hover:bg-red-700 text-white px-2 py-1 rounded text-xs">
Kill
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mt-6 p-4 bg-green-100 border border-green-200 rounded-lg">
<h3 class="font-bold text-green-800">✅ Changes Made:</h3>
<ul class="list-disc list-inside text-green-700 mt-2">
<li>Added "Memory %" column to the process table</li>
<li>Updated HTML table header to include the new column</li>
<li>Modified JavaScript to display memory percentage from backend data</li>
<li>Backend already calculates MemoryUsagePercentage based on total system memory</li>
<li>Updated colspan for "No data" message from 4 to 5 columns</li>
</ul>
</div>
</div>
</body>
</html>