Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a65e72c49 | |||
| f8bbd1199d | |||
| 294438145a | |||
| 413360ece2 | |||
| a0b9f05ae3 | |||
| caa7436d51 | |||
| 5eec358b68 | |||
| 7c1cbb44f8 | |||
| 06ea991a6c |
@@ -1,7 +0,0 @@
|
||||
# dotnet
|
||||
|
||||
dotnet run
|
||||
git add .
|
||||
git commit -m "Add steam running games"
|
||||
git push origin master
|
||||
dotnet publish -c Release -o ./publish
|
||||
@@ -1,227 +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.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.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().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
|
||||
{
|
||||
Name = processes.ElementAt(0).ProcessName,
|
||||
TotalProcessorTime = processes.ElementAt(0).TotalProcessorTime,
|
||||
WorkingSet64 = processes.ElementAt(0).WorkingSet64 / (1024 * 1024) // Convert to MB
|
||||
},
|
||||
Process2 = new
|
||||
{
|
||||
Name = processes.ElementAt(1).ProcessName,
|
||||
TotalProcessorTime = processes.ElementAt(1).TotalProcessorTime,
|
||||
WorkingSet64 = processes.ElementAt(1).WorkingSet64 / (1024 * 1024) // Convert to MB
|
||||
},
|
||||
Process3 = new
|
||||
{
|
||||
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()
|
||||
{
|
||||
#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()
|
||||
{
|
||||
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
|
||||
{
|
||||
#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,4 +0,0 @@
|
||||
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."
|
||||
|
||||
|
||||
@@ -9,6 +9,15 @@ public static class NvmlWrapper
|
||||
[DllImport("nvml.dll", EntryPoint = "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")]
|
||||
public static extern int NvmlDeviceGetHandleByIndex(int index, out IntPtr device);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# Resource Usage API
|
||||
|
||||
This project is a background service developed using ASP.NET Core that monitors system resource usage, such as CPU, RAM, GPU, and running games. The service provides APIs to fetch the current resource usage and kill specified processes.
|
||||
|
||||
## Features
|
||||
|
||||
- **Resource Monitoring**: Fetches detailed information about the system's resources, including:
|
||||
- Current Time
|
||||
- Computer Information (Machine Name, OS Version, Architecture, Processor Count)
|
||||
- CPU Usage
|
||||
- RAM Usage
|
||||
- GPU Usage
|
||||
- Currently Running Steam Games (if any)
|
||||
|
||||
- **Process Management**: Provides an API to kill processes by their process ID.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
ResourceUsageAPI/
|
||||
├── Worker.cs
|
||||
├── Program.cs
|
||||
├── Startup.cs
|
||||
└── NvmlWrapper.cs
|
||||
```
|
||||
|
||||
## Code Analysis
|
||||
|
||||
### Worker.cs
|
||||
|
||||
This file contains the main logic for monitoring system resources and exposing APIs.
|
||||
|
||||
- **Dependencies**: Uses `System.Diagnostics`, `Microsoft.AspNetCore.Builder`, `Newtonsoft.Json` among others.
|
||||
- **Methods**:
|
||||
- `ExecuteAsync`: Sets up the ASP.NET Core web application, defines routes for resource usage and process management, and runs the server.
|
||||
- `GetComputerInfo`: Retrieves basic system information.
|
||||
- `GetCpuUsage`: Fetches CPU usage and lists top three processes by CPU usage if usage is over 80%.
|
||||
- `GetRamUsage`: Calculates RAM usage percentage.
|
||||
- `GetTotalPhysicalMemory`: Retrieves total physical memory size.
|
||||
- `GetGpuUsage`: Uses NVIDIA Management Library (NVML) to fetch GPU usage, temperature, and fan speed.
|
||||
- `GetCurrentlyRunningGame`: Detects if a Steam game is running by checking process paths.
|
||||
- `GetCurrentTime`: Returns the current time.
|
||||
|
||||
### Program.cs
|
||||
|
||||
This file sets up the hosting environment for the application.
|
||||
|
||||
- **Dependencies**: Uses `Microsoft.Extensions.DependencyInjection` and `Microsoft.Extensions.Hosting`.
|
||||
- **Methods**:
|
||||
- `Main`: Entry point of the application, builds and runs the host.
|
||||
- `CreateHostBuilder`: Configures services and determines if the application should run as a Windows service based on command-line arguments or environment variables.
|
||||
|
||||
### Startup.cs
|
||||
|
||||
This file is not used in the current implementation since all routing and configuration are done within `Worker.cs`.
|
||||
|
||||
- **Dependencies**: Uses `Microsoft.AspNetCore.Builder` and `Microsoft.AspNetCore.Hosting`.
|
||||
- **Methods**:
|
||||
- `ConfigureServices`: Placeholder method for adding services.
|
||||
- `Configure`: Placeholder method for configuring application HTTP requests pipeline.
|
||||
|
||||
### NvmlWrapper.cs
|
||||
|
||||
This file provides a C# wrapper for the NVIDIA Management Library (NVML) functions.
|
||||
|
||||
- **Dependencies**: Uses `System` and `System.Runtime.InteropServices`.
|
||||
- **Methods**:
|
||||
- Importing NVML DLL functions to interact with GPU hardware.
|
||||
- Structures like `NvmlUtilization` are defined for handling utilization rates returned by NVML.
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Build the Project**: Use your preferred .NET build tool (e.g., `dotnet build`) to compile the project.
|
||||
2. **Run the Application**:
|
||||
- To run as a console application, execute the compiled binary directly.
|
||||
- To run as a Windows service, use the command-line argument `--windows-service` or set the environment variable `RUN_AS_SERVICE` to `"true"`.
|
||||
|
||||
## APIs
|
||||
|
||||
- **Get Resource Usage**:
|
||||
- URL: `/api/resource-usage`
|
||||
- Method: GET
|
||||
- Description: Retrieves current system resource usage.
|
||||
|
||||
- **Kill Process**:
|
||||
- URL: `/api/kill-process`
|
||||
- Method: POST
|
||||
- Body: JSON with the process ID (`{"id": "1234"}`)
|
||||
- Description: Kills the specified process.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Ensure that the NVIDIA Management Library (NVML) is installed on the system for GPU monitoring to work.
|
||||
- The application allows CORS from all origins, which should be configured securely in production environments.
|
||||
- Error handling and logging are minimal; consider adding robust error handling and logging mechanisms for a production-ready solution.
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to contribute by opening issues or submitting pull requests. Make sure to follow the project's coding style and best practices.
|
||||
|
||||
|
||||
# devnote
|
||||
dotnet run
|
||||
git add .
|
||||
git commit -m "Add steam running games"
|
||||
git push origin master
|
||||
dotnet publish -c Release -o ./publish
|
||||
|
||||
# devtest
|
||||
Invoke-WebRequest -Uri "http://localhost:5000/api/kill-process" -Method POST -Body "1234" -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" }
|
||||
|
||||
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,445 @@
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,5 +12,8 @@
|
||||
"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
|
||||
$port = 5000
|
||||
$ruleName = "ResourceMonitorService"
|
||||
$ruleName = "ResourceMonitorServicePublish"
|
||||
if (Get-NetFirewallRule -DisplayName $ruleName) {
|
||||
Write-Host "Rule already exists, not creating a new one"
|
||||
} else {
|
||||
@@ -0,0 +1,4 @@
|
||||
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."
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"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,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"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,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"RunAsWindowsService": true,
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"Http": {
|
||||
"Url": "http://*:5000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiSettings": {
|
||||
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const components = {
|
||||
// other components
|
||||
resourceUsage: dynamic(() => import('./resourceUsage/component')),
|
||||
yourwidget: dynamic(() => import("./yourwidget/component"))
|
||||
};
|
||||
|
||||
export default components;
|
||||
@@ -1,9 +0,0 @@
|
||||
import resourceUsage from "./resourceUsage/widget";
|
||||
import yourwidget from "./yourwidget/widget";
|
||||
const widgets = {
|
||||
// other widgets
|
||||
resourceUsage: resourceUsage,
|
||||
yourwidget: yourwidget
|
||||
};
|
||||
|
||||
export default widgets;
|
||||
@@ -1,37 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
@@ -1,33 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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