Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cc0e99149 | |||
| a42c5753c9 | |||
| 78076a984b | |||
| 77f6c5abe1 |
@@ -9,15 +9,6 @@ public static class NvmlWrapper
|
|||||||
[DllImport("nvml.dll", EntryPoint = "nvmlShutdown")]
|
[DllImport("nvml.dll", EntryPoint = "nvmlShutdown")]
|
||||||
public static extern int NvmlShutdown();
|
public static extern int NvmlShutdown();
|
||||||
|
|
||||||
// Get device count
|
|
||||||
/* [DllImport("nvml.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern int nvmlDeviceGetCount_v2(ref uint deviceCount); */
|
|
||||||
|
|
||||||
/* public static int NvmlDeviceGetCount(ref uint deviceCount)
|
|
||||||
{
|
|
||||||
return nvmlDeviceGetCount_v2(ref deviceCount);
|
|
||||||
} */
|
|
||||||
|
|
||||||
[DllImport("nvml.dll", EntryPoint = "nvmlDeviceGetHandleByIndex_v2")]
|
[DllImport("nvml.dll", EntryPoint = "nvmlDeviceGetHandleByIndex_v2")]
|
||||||
public static extern int NvmlDeviceGetHandleByIndex(int index, out IntPtr device);
|
public static extern int NvmlDeviceGetHandleByIndex(int index, out IntPtr device);
|
||||||
|
|
||||||
@@ -99,21 +99,13 @@ This file provides a C# wrapper for the NVIDIA Management Library (NVML) functio
|
|||||||
Feel free to contribute by opening issues or submitting pull requests. Make sure to follow the project's coding style and best practices.
|
Feel free to contribute by opening issues or submitting pull requests. Make sure to follow the project's coding style and best practices.
|
||||||
|
|
||||||
|
|
||||||
# devnote
|
## devnote
|
||||||
dotnet run
|
- **Commonlly use command**:
|
||||||
git add .
|
- dotnet run
|
||||||
git commit -m "Add steam running games"
|
- git add .
|
||||||
git push origin master
|
- git commit -m "Add steam running games"
|
||||||
dotnet publish -c Release -o ./publish
|
- git push origin master
|
||||||
|
- dotnet publish -c Release -o ./publish
|
||||||
|
|
||||||
# devtest
|
- **Using git bash**:
|
||||||
Invoke-WebRequest -Uri "http://localhost:5000/api/kill-process" -Method POST -Body "1234" -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" }
|
- curl -X POST "http://localhost:5000/api/kill-process" -H "Content-Type: application/json" -d '3333333'
|
||||||
|
|
||||||
Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/resource-usage" -Method GET -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" }
|
|
||||||
|
|
||||||
Use 'shutdown', 'restart', or 'cancel'.
|
|
||||||
Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/force-shutdown" `
|
|
||||||
-Method POST `
|
|
||||||
-Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } `
|
|
||||||
-Body '{"Action": "shutdown", "DelaySeconds": 120}' `
|
|
||||||
-ContentType "application/json"
|
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace ResourceMonitorService
|
||||||
|
{
|
||||||
|
public class Worker : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
|
|
||||||
|
public Worker(IHostApplicationLifetime lifetime)
|
||||||
|
{
|
||||||
|
_lifetime = lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder();
|
||||||
|
/* builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
{
|
||||||
|
options.ListenAnyIP(5000); // Replace 5000 with your desired port
|
||||||
|
}); */
|
||||||
|
/* builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
{
|
||||||
|
var url = builder.Configuration.GetValue<string>("Kestrel:Endpoints:Http:Url");
|
||||||
|
var port = url.Split(':').Last();
|
||||||
|
options.ListenAnyIP(int.Parse(port));
|
||||||
|
}); */
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowAllOrigins",
|
||||||
|
builder => builder.AllowAnyOrigin()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod());
|
||||||
|
});
|
||||||
|
builder.Services.AddControllers().AddNewtonsoftJson();
|
||||||
|
var app = builder.Build();
|
||||||
|
// Apply CORS policy to allow all origins
|
||||||
|
app.UseCors("AllowAllOrigins");
|
||||||
|
|
||||||
|
app.MapGet("/api/resource-usage", async context =>
|
||||||
|
{
|
||||||
|
var currentTime = GetCurrentTime();
|
||||||
|
|
||||||
|
var computerInfo = GetComputerInfo();
|
||||||
|
var cpuUsage = GetCpuUsage();
|
||||||
|
var ramUsage = GetRamUsage();
|
||||||
|
var gpuUsage = GetGpuUsage();
|
||||||
|
var runningGame = GetCurrentlyRunningGame();
|
||||||
|
|
||||||
|
var resourceUsage = new
|
||||||
|
{
|
||||||
|
CurrentTime = currentTime,
|
||||||
|
ComputerInfo = computerInfo,
|
||||||
|
CPU = cpuUsage,
|
||||||
|
RAM = ramUsage,
|
||||||
|
GPU = gpuUsage,
|
||||||
|
CurrentlyRunningGame = runningGame
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(resourceUsage);
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
await context.Response.WriteAsync(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapPost("/api/kill-process", async context =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var idStr = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
||||||
|
int processId = Convert.ToInt32(idStr);
|
||||||
|
|
||||||
|
Process[] processes = Process.GetProcesses().Where(p => p.Id == processId).ToArray();
|
||||||
|
|
||||||
|
if (processes.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
await context.Response.WriteAsync($"Process with ID {processId} has been killed.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync($"Error killing process with ID {processId}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync($"No process found with ID {processId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync($"An error occurred: {ex.Message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.RunAsync(stoppingToken);
|
||||||
|
|
||||||
|
await Task.Delay(Timeout.Infinite, stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetComputerInfo()
|
||||||
|
{
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
MachineName = Environment.MachineName,
|
||||||
|
OSVersion = RuntimeInformation.OSDescription,
|
||||||
|
OSArchitecture = RuntimeInformation.OSArchitecture.ToString(),
|
||||||
|
ProcessorCount = Environment.ProcessorCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetCpuUsage()
|
||||||
|
{
|
||||||
|
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
||||||
|
cpuCounter.NextValue();
|
||||||
|
Thread.Sleep(1000); // Wait a second to get a valid reading
|
||||||
|
|
||||||
|
var usage = cpuCounter.NextValue();
|
||||||
|
if (usage > 80)
|
||||||
|
{
|
||||||
|
// Get the current processes and sort them by CPU usage in descending order
|
||||||
|
var processes = Process.GetProcesses().OrderByDescending(p => p.TotalProcessorTime);
|
||||||
|
|
||||||
|
// Create a new anonymous type containing the CPU usage, RAM usage, and the top 3 highest CPU-using processes
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Usage = usage,
|
||||||
|
Process1 = new
|
||||||
|
{
|
||||||
|
Id = processes.ElementAt(0).Id,
|
||||||
|
Name = processes.ElementAt(0).ProcessName,
|
||||||
|
TotalProcessorTime = processes.ElementAt(0).TotalProcessorTime,
|
||||||
|
WorkingSet64 = processes.ElementAt(0).WorkingSet64 / (1024 * 1024) // Convert to MB
|
||||||
|
},
|
||||||
|
Process2 = new
|
||||||
|
{
|
||||||
|
Id = processes.ElementAt(1).Id,
|
||||||
|
Name = processes.ElementAt(1).ProcessName,
|
||||||
|
TotalProcessorTime = processes.ElementAt(1).TotalProcessorTime,
|
||||||
|
WorkingSet64 = processes.ElementAt(1).WorkingSet64 / (1024 * 1024) // Convert to MB
|
||||||
|
},
|
||||||
|
Process3 = new
|
||||||
|
{
|
||||||
|
Id = processes.ElementAt(2).Id,
|
||||||
|
Name = processes.ElementAt(2).ProcessName,
|
||||||
|
TotalProcessorTime = processes.ElementAt(2).TotalProcessorTime,
|
||||||
|
WorkingSet64 = processes.ElementAt(2).WorkingSet64 / (1024 * 1024) // Convert to MB
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Usage = usage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetRamUsage()
|
||||||
|
{
|
||||||
|
var ramCounter = new PerformanceCounter("Memory", "Available MBytes");
|
||||||
|
var totalMemory = GetTotalPhysicalMemory();
|
||||||
|
var availableMemory = ramCounter.NextValue() * 1024 * 1024;
|
||||||
|
return (float)(totalMemory - availableMemory) / totalMemory * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong GetTotalPhysicalMemory()
|
||||||
|
{
|
||||||
|
ulong totalMemory = 0;
|
||||||
|
var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem");
|
||||||
|
foreach (var obj in searcher.Get())
|
||||||
|
{
|
||||||
|
totalMemory = (ulong)obj["TotalPhysicalMemory"];
|
||||||
|
}
|
||||||
|
return totalMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetGpuUsage()
|
||||||
|
{
|
||||||
|
NvmlWrapper.NvmlInit();
|
||||||
|
IntPtr device;
|
||||||
|
NvmlWrapper.NvmlDeviceGetHandleByIndex(0, out device);
|
||||||
|
NvmlWrapper.NvmlUtilization utilization;
|
||||||
|
NvmlWrapper.NvmlDeviceGetUtilizationRates(device, out utilization);
|
||||||
|
|
||||||
|
uint temperature;
|
||||||
|
NvmlWrapper.NvmlDeviceGetTemperature(device, 0, out temperature);
|
||||||
|
|
||||||
|
uint fanSpeed;
|
||||||
|
NvmlWrapper.NvmlDeviceGetFanSpeed(device, out fanSpeed);
|
||||||
|
|
||||||
|
NvmlWrapper.NvmlShutdown();
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Usage = utilization.Gpu,
|
||||||
|
Temperature = temperature,
|
||||||
|
FanSpeed = fanSpeed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetCurrentlyRunningGame()
|
||||||
|
{
|
||||||
|
var processes = Process.GetProcesses();
|
||||||
|
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filePath = process.MainModule.FileName;
|
||||||
|
if (filePath.Contains(@"\steamapps\common\"))
|
||||||
|
{
|
||||||
|
// Extract the game directory name
|
||||||
|
var parts = filePath.Split(new[] { @"\steamapps\common\" }, StringSplitOptions.None);
|
||||||
|
if (parts.Length > 1)
|
||||||
|
{
|
||||||
|
var gamePath = parts[1];
|
||||||
|
var gameName = gamePath.Split(Path.DirectorySeparatorChar)[0];
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
GameName = gameName,
|
||||||
|
ExecutableName = Path.GetFileName(filePath),
|
||||||
|
FullPath = filePath,
|
||||||
|
ProcessId = process.Id,
|
||||||
|
MemoryUsage = process.WorkingSet64 / (1024 * 1024) + " MB", // Memory usage in MB
|
||||||
|
CpuTime = process.TotalProcessorTime.ToString(),
|
||||||
|
StartTime = process.StartTime.ToString("G"), // General date/time pattern
|
||||||
|
UserName = Environment.UserName // The user running the process
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Handle access exceptions or continue if not important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "No Steam game is currently running.";
|
||||||
|
}
|
||||||
|
private string GetCurrentTime()
|
||||||
|
{
|
||||||
|
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,12 @@
|
|||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RunAsWindowsService": true,
|
"RunAsWindowsService": false,
|
||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
"Http": {
|
"Http": {
|
||||||
"Url": "http://*:5000"
|
"Url": "http://*:5000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ApiSettings": {
|
|
||||||
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,5 @@
|
|||||||
"Url": "http://*:5000"
|
"Url": "http://*:5000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ApiSettings": {
|
|
||||||
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# PowerShell script to create a new inbound rule in Windows Firewall
|
# PowerShell script to create a new inbound rule in Windows Firewall
|
||||||
$port = 5000
|
$port = 5000
|
||||||
$ruleName = "ResourceMonitorServicePublish"
|
$ruleName = "ResourceMonitorService"
|
||||||
if (Get-NetFirewallRule -DisplayName $ruleName) {
|
if (Get-NetFirewallRule -DisplayName $ruleName) {
|
||||||
Write-Host "Rule already exists, not creating a new one"
|
Write-Host "Rule already exists, not creating a new one"
|
||||||
} else {
|
} else {
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
sc create ResourceMonitorServicerrr binPath= "%~dp0ResourceMonitorService.exe --windows-service" start= auto
|
||||||
|
sc description ResourceMonitorServicerrr "A service that monitors system resource usage and exposes it via a web API."
|
||||||
|
|
||||||
|
|
||||||
@@ -1,445 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Management;
|
|
||||||
|
|
||||||
namespace ResourceMonitorService
|
|
||||||
{
|
|
||||||
public class Worker : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
|
||||||
|
|
||||||
public Worker(IHostApplicationLifetime lifetime)
|
|
||||||
{
|
|
||||||
_lifetime = lifetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
var builder = WebApplication.CreateBuilder();
|
|
||||||
builder.Services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy("AllowAllOrigins",
|
|
||||||
builder => builder
|
|
||||||
.WithOrigins("http://localhost:4200","http://192.168.50.52:4200","http://vmwin11:4200")
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowAnyMethod());
|
|
||||||
});
|
|
||||||
builder.Services.AddControllers().AddNewtonsoftJson();
|
|
||||||
|
|
||||||
// Read the API key from appsettings.json
|
|
||||||
var configuration = builder.Configuration;
|
|
||||||
var apiKey = configuration["ApiSettings:ApiKey"];
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Middleware to validate API key
|
|
||||||
// This middleware checks for the presence of the API key in the request headers
|
|
||||||
// and compares it with the expected API key from appsettings.json.
|
|
||||||
// If the API key is missing or invalid, it returns a 401 Unauthorized response.
|
|
||||||
//
|
|
||||||
/* app.Use(async (context, next) =>
|
|
||||||
{
|
|
||||||
if (!context.Request.Headers.TryGetValue("X-API-KEY", out var extractedApiKey) || extractedApiKey != apiKey)
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|
||||||
await context.Response.WriteAsync("Unauthorized: Invalid API Key");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await next();
|
|
||||||
}); */
|
|
||||||
// Apply CORS policy to allow all origins
|
|
||||||
app.UseCors("AllowAllOrigins");
|
|
||||||
|
|
||||||
app.MapGet("/api/resource-usage", async context =>
|
|
||||||
{
|
|
||||||
var currentTime = GetCurrentTime();
|
|
||||||
|
|
||||||
var computerInfo = GetComputerInfo();
|
|
||||||
var cpuUsage = GetCpuUsage();
|
|
||||||
var ramUsage = GetRamUsage();
|
|
||||||
var gpuUsage = GetGpuUsage();
|
|
||||||
var runningGame = GetCurrentlyRunningGame();
|
|
||||||
|
|
||||||
var resourceUsage = new
|
|
||||||
{
|
|
||||||
CurrentTime = currentTime,
|
|
||||||
ComputerInfo = computerInfo,
|
|
||||||
CPU = cpuUsage,
|
|
||||||
RAM = ramUsage,
|
|
||||||
GPU = gpuUsage,
|
|
||||||
CurrentlyRunningGame = runningGame
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(resourceUsage);
|
|
||||||
context.Response.ContentType = "application/json";
|
|
||||||
await context.Response.WriteAsync(json);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapPost("/api/kill-process", async context =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var idStr = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
|
||||||
int processId = Convert.ToInt32(idStr);
|
|
||||||
|
|
||||||
Process[] processes = Process.GetProcesses().Where(p => p.Id == processId).ToArray();
|
|
||||||
|
|
||||||
if (processes.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var process in processes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
process.Kill();
|
|
||||||
await context.Response.WriteAsync($"Process with ID {processId} has been killed.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync($"Error killing process with ID {processId}: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync($"No process found with ID {processId}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync($"An error occurred: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* curl -X POST http://localhost:5000/api/force-shutdown -d "5000" */
|
|
||||||
/* Invoke-WebRequest -Uri "http://localhost:5000/api/force-shutdown" -Method POST -Body "50000" -ContentType "text/plain" */
|
|
||||||
app.MapPost("/api/force-shutdown", async context =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
|
||||||
var parameters = JsonConvert.DeserializeObject<dynamic>(requestBody);
|
|
||||||
|
|
||||||
string? action = parameters?.Action?.ToString()?.ToLower(); // "shutdown" or "restart"
|
|
||||||
int delaySeconds = parameters?.DelaySeconds ?? 0;
|
|
||||||
|
|
||||||
// Validate action input
|
|
||||||
if (action != "shutdown" && action != "restart" && action != "cancel")
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync("Invalid action. Use 'shutdown', 'restart', or 'cancel'.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if action is stop, then cancel the shutdown
|
|
||||||
if (action == "cancel")
|
|
||||||
{
|
|
||||||
var processStartInfoCancel = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "shutdown",
|
|
||||||
Arguments = "/a",
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = false
|
|
||||||
};
|
|
||||||
|
|
||||||
Process.Start(processStartInfoCancel);
|
|
||||||
await context.Response.WriteAsync("Shutdown cancelled.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate delay input
|
|
||||||
if (delaySeconds < 0)
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync("Delay must be a non-negative integer.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the shutdown command
|
|
||||||
string shutdownCommand = action == "shutdown" ? $"/s /f /t {delaySeconds}" : $"/r /f /t {delaySeconds}";
|
|
||||||
|
|
||||||
var processStartInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "shutdown",
|
|
||||||
Arguments = shutdownCommand,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = false
|
|
||||||
};
|
|
||||||
|
|
||||||
Process.Start(processStartInfo);
|
|
||||||
|
|
||||||
await context.Response.WriteAsync($"{action.ToUpper()} command executed with a delay of {delaySeconds} seconds.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync($"An error occurred: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapGet("/api/stop", async context =>
|
|
||||||
{
|
|
||||||
await context.Response.WriteAsync("Stopping the service...");
|
|
||||||
_lifetime.StopApplication();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapGet("/", () => "Resource Monitor Service is running.");
|
|
||||||
app.MapGet("/api/current-time", () => Results.Ok(GetCurrentTime()));
|
|
||||||
app.MapGet("/api/computer-info", () => Results.Ok(GetComputerInfo()));
|
|
||||||
app.MapGet("/api/cpu-usage", () => Results.Ok(GetCpuUsage()));
|
|
||||||
app.MapGet("/api/ram-usage", () => Results.Ok(GetRamUsage()));
|
|
||||||
app.MapGet("/api/gpu-usage", () => Results.Ok(GetGpuUsage()));
|
|
||||||
app.MapGet("/api/running-game", () => Results.Ok(GetCurrentlyRunningGame()));
|
|
||||||
app.MapGet("/api/total-physical-memory", () => Results.Ok(GetTotalPhysicalMemory()));
|
|
||||||
app.MapGet("/api/total-available-memory", () => Results.Ok(new { TotalAvailableMemory = Environment.WorkingSet }));
|
|
||||||
|
|
||||||
app.MapGet("/health", () => Results.Ok("Service is healthy."));
|
|
||||||
|
|
||||||
|
|
||||||
_ = app.RunAsync(stoppingToken);
|
|
||||||
|
|
||||||
await Task.Delay(Timeout.Infinite, stoppingToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetComputerInfo()
|
|
||||||
{
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
MachineName = Environment.MachineName,
|
|
||||||
OSVersion = RuntimeInformation.OSDescription,
|
|
||||||
OSArchitecture = RuntimeInformation.OSArchitecture.ToString(),
|
|
||||||
ProcessorCount = Environment.ProcessorCount
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetCpuUsage()
|
|
||||||
{
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
cpuCounter.NextValue();
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
Thread.Sleep(1000); // Wait a second to get a valid reading
|
|
||||||
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
var usage = cpuCounter.NextValue();
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
if (usage > 80)
|
|
||||||
{
|
|
||||||
// Get the current processes and sort them by CPU usage in descending order
|
|
||||||
var processes = Process.GetProcesses()
|
|
||||||
.Select(p =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Process = p,
|
|
||||||
TotalProcessorTime = p.TotalProcessorTime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null; // Skip processes that throw exceptions
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(p => p != null)
|
|
||||||
.OrderByDescending(p => p!.TotalProcessorTime)
|
|
||||||
.Select(p => p!.Process)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Create a new anonymous type containing the CPU usage, RAM usage, and the top 3 highest CPU-using processes
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Usage = usage,
|
|
||||||
Process1 = processes.Count > 0 ? new
|
|
||||||
{
|
|
||||||
Name = processes[0].ProcessName,
|
|
||||||
TotalProcessorTime = processes[0].TotalProcessorTime,
|
|
||||||
WorkingSet64 = processes[0].WorkingSet64 / (1024 * 1024) // Convert to MB
|
|
||||||
} : null,
|
|
||||||
Process2 = processes.Count > 1 ? new
|
|
||||||
{
|
|
||||||
Name = processes[1].ProcessName,
|
|
||||||
TotalProcessorTime = processes[1].TotalProcessorTime,
|
|
||||||
WorkingSet64 = processes[1].WorkingSet64 / (1024 * 1024) // Convert to MB
|
|
||||||
} : null,
|
|
||||||
Process3 = processes.Count > 2 ? new
|
|
||||||
{
|
|
||||||
Name = processes[2].ProcessName,
|
|
||||||
TotalProcessorTime = processes[2].TotalProcessorTime,
|
|
||||||
WorkingSet64 = processes[2].WorkingSet64 / (1024 * 1024) // Convert to MB
|
|
||||||
} : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Usage = usage
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetRamUsage()
|
|
||||||
{
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
var ramCounter = new PerformanceCounter("Memory", "Available MBytes");
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
var totalMemory = GetTotalPhysicalMemory();
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
var availableMemory = ramCounter.NextValue() * 1024 * 1024;
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
return (float)(totalMemory - availableMemory) / totalMemory * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong GetTotalPhysicalMemory()
|
|
||||||
{
|
|
||||||
ulong totalMemory = 0;
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem");
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
foreach (var obj in searcher.Get())
|
|
||||||
{
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
totalMemory = (ulong)obj["TotalPhysicalMemory"];
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
}
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
return totalMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetGpuUsage()
|
|
||||||
{
|
|
||||||
/* if (!IsNvidiaGpuPresent())
|
|
||||||
{
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Usage = 0,
|
|
||||||
Temperature = 0,
|
|
||||||
FanSpeed = 0,
|
|
||||||
IsAvailable = false,
|
|
||||||
Message = "No NVIDIA GPU detected"
|
|
||||||
};
|
|
||||||
} */
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
NvmlWrapper.NvmlInit();
|
|
||||||
IntPtr device;
|
|
||||||
NvmlWrapper.NvmlDeviceGetHandleByIndex(0, out device);
|
|
||||||
NvmlWrapper.NvmlUtilization utilization;
|
|
||||||
NvmlWrapper.NvmlDeviceGetUtilizationRates(device, out utilization);
|
|
||||||
|
|
||||||
uint temperature;
|
|
||||||
NvmlWrapper.NvmlDeviceGetTemperature(device, 0, out temperature);
|
|
||||||
|
|
||||||
uint fanSpeed;
|
|
||||||
NvmlWrapper.NvmlDeviceGetFanSpeed(device, out fanSpeed);
|
|
||||||
|
|
||||||
NvmlWrapper.NvmlShutdown();
|
|
||||||
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Usage = utilization.Gpu,
|
|
||||||
Temperature = temperature,
|
|
||||||
FanSpeed = fanSpeed,
|
|
||||||
IsAvailable = false,
|
|
||||||
Error = ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Usage = 0,
|
|
||||||
Temperature = 0,
|
|
||||||
FanSpeed = 0,
|
|
||||||
IsAvailable = false,
|
|
||||||
Error = ex.Message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* private bool IsNvidiaGpuPresent()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Method 1: Try to initialize NVML
|
|
||||||
NvmlWrapper.NvmlInit();
|
|
||||||
uint deviceCount = 0;
|
|
||||||
NvmlWrapper.NvmlDeviceGetCount(ref deviceCount);
|
|
||||||
NvmlWrapper.NvmlShutdown();
|
|
||||||
|
|
||||||
return deviceCount > 0;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Method 2: Fallback to checking using WMI
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController WHERE Name LIKE '%NVIDIA%'"))
|
|
||||||
{
|
|
||||||
var collection = searcher.Get();
|
|
||||||
return collection.Count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
private object GetCurrentlyRunningGame()
|
|
||||||
{
|
|
||||||
var processes = Process.GetProcesses();
|
|
||||||
|
|
||||||
foreach (var process in processes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
|
||||||
var filePath = process.MainModule.FileName;
|
|
||||||
#pragma warning restore CS8602 // Dereference of a possibly null reference.
|
|
||||||
if (filePath.Contains(@"\steamapps\common\"))
|
|
||||||
{
|
|
||||||
// Extract the game directory name
|
|
||||||
var parts = filePath.Split(new[] { @"\steamapps\common\" }, StringSplitOptions.None);
|
|
||||||
if (parts.Length > 1)
|
|
||||||
{
|
|
||||||
var gamePath = parts[1];
|
|
||||||
var gameName = gamePath.Split(Path.DirectorySeparatorChar)[0];
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
GameName = gameName,
|
|
||||||
ExecutableName = Path.GetFileName(filePath),
|
|
||||||
FullPath = filePath,
|
|
||||||
ProcessId = process.Id,
|
|
||||||
MemoryUsage = process.WorkingSet64 / (1024 * 1024) + " MB", // Memory usage in MB
|
|
||||||
CpuTime = process.TotalProcessorTime.ToString(),
|
|
||||||
StartTime = process.StartTime.ToString("G"), // General date/time pattern
|
|
||||||
UserName = Environment.UserName // The user running the process
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Handle access exceptions or continue if not important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "No Steam game is currently running.";
|
|
||||||
}
|
|
||||||
private string GetCurrentTime()
|
|
||||||
{
|
|
||||||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RunAsWindowsService": true,
|
|
||||||
"Kestrel": {
|
|
||||||
"Endpoints": {
|
|
||||||
"Http": {
|
|
||||||
"Url": "http://*:5000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ApiSettings": {
|
|
||||||
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
sc create ResourceMonitorService binPath="%~dp0ResourceMonitorService.exe --windows-service" start= auto
|
|
||||||
sc description ResourceMonitorService "A service that monitors system resource usage and exposes it via a web API."
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RunAsWindowsService": true,
|
|
||||||
"Kestrel": {
|
|
||||||
"Endpoints": {
|
|
||||||
"Http": {
|
|
||||||
"Url": "http://*:5000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ApiSettings": {
|
|
||||||
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
// other components
|
||||||
|
resourceUsage: dynamic(() => import('./resourceUsage/component')),
|
||||||
|
yourwidget: dynamic(() => import("./yourwidget/component"))
|
||||||
|
};
|
||||||
|
|
||||||
|
export default components;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import resourceUsage from "./resourceUsage/widget";
|
||||||
|
import yourwidget from "./yourwidget/widget";
|
||||||
|
const widgets = {
|
||||||
|
// other widgets
|
||||||
|
resourceUsage: resourceUsage,
|
||||||
|
yourwidget: yourwidget
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widgets;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
import Container from "components/services/widget/container";
|
||||||
|
import Block from "components/services/widget/block";
|
||||||
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { widget } = service;
|
||||||
|
const { data, error } = useWidgetAPI(widget, "info");
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Container service={service} error={error} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="Loading..." />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="Machine Name" value={data.ComputerInfo.MachineName} />
|
||||||
|
<Block label="OS Version" value={data.ComputerInfo.OSVersion} />
|
||||||
|
<Block label="OS Architecture" value={data.ComputerInfo.OSArchitecture} />
|
||||||
|
<Block label="Processor Count" value={data.ComputerInfo.ProcessorCount} />
|
||||||
|
<Block label="CPU Usage" value={data.CPU} />
|
||||||
|
<Block label="RAM Usage" value={data.RAM} />
|
||||||
|
<Block label="GPU Usage" value={data.GPU.Usage} />
|
||||||
|
<Block label="GPU Temperature" value={data.GPU.Temperature} />
|
||||||
|
<Block label="GPU Fan Speed" value={data.GPU.FanSpeed} />
|
||||||
|
<Block label="Currently Running Game" value={data.CurrentlyRunningGame} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "http://192.168.50.201:5000/api/resource-usage",
|
||||||
|
proxyHandler: genericProxyHandler,
|
||||||
|
mappings: {
|
||||||
|
info: {
|
||||||
|
endpoint: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
|
import Container from "components/services/widget/container";
|
||||||
|
import Block from "components/services/widget/block";
|
||||||
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { widget } = service;
|
||||||
|
const { data, error } = useWidgetAPI(widget, "info");
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Container service={service} error={error} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="yourwidget.key1" />
|
||||||
|
<Block label="yourwidget.key2" />
|
||||||
|
<Block label="yourwidget.key3" />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="yourwidget.key1" value={t("common.number", { value: data.key1 })} />
|
||||||
|
<Block label="yourwidget.key2" value={t("common.number", { value: data.key2 })} />
|
||||||
|
<Block label="yourwidget.key3" value={t("common.number", { value: data.key3 })} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "{url}/{endpoint}" ,
|
||||||
|
proxyHandler: genericProxyHandler ,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
info: {
|
||||||
|
endpoint: "v1/info" ,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
||||||
Reference in New Issue
Block a user