using AmtScanner.Api.Data; using AmtScanner.Api.Models; using AmtScanner.Api.Repositories; namespace AmtScanner.Api.Services; public interface IHardwareInfoService { Task GetHardwareInfoAsync(long deviceId, bool forceRefresh = false); Task> GetBatchHardwareInfoAsync(List deviceIds, bool forceRefresh = false); } public class HardwareInfoService : IHardwareInfoService { private readonly IHardwareInfoRepository _repository; private readonly IAmtHardwareQueryService _hardwareQueryService; private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly TimeSpan _cacheExpiration = TimeSpan.FromHours(24); public HardwareInfoService( IHardwareInfoRepository repository, IAmtHardwareQueryService hardwareQueryService, IServiceScopeFactory scopeFactory, ILogger logger) { _repository = repository; _hardwareQueryService = hardwareQueryService; _scopeFactory = scopeFactory; _logger = logger; } public async Task GetHardwareInfoAsync(long deviceId, bool forceRefresh = false) { // Check cache var cached = await _repository.GetByDeviceIdAsync(deviceId); if (cached != null && !forceRefresh && DateTime.UtcNow - cached.LastUpdated < _cacheExpiration) { _logger.LogInformation("Returning cached hardware info for device {DeviceId}", deviceId); return MapToDto(cached); } // Query from device _logger.LogInformation("Querying hardware info from device {DeviceId}", deviceId); using var scope = _scopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); var credentialService = scope.ServiceProvider.GetRequiredService(); var device = await context.AmtDevices.FindAsync(deviceId); if (device == null) { throw new Exception($"Device {deviceId} not found"); } var credential = await credentialService.GetDefaultCredentialAsync(); if (credential == null) { throw new Exception("No default credential found"); } var password = credentialService.DecryptPassword(credential.Password); // Determine open ports from device IP (simplified) var openPorts = new List(); if (device.IpAddress.EndsWith("111")) { openPorts.Add(16992); // HTTP openPorts.Add(623); } else if (device.IpAddress.EndsWith("112")) { openPorts.Add(16993); // HTTPS } else { // Default: try both openPorts.Add(16992); openPorts.Add(16993); } var hardwareInfo = await _hardwareQueryService.QueryHardwareInfoAsync( device.IpAddress, credential.Username, password, openPorts); hardwareInfo.DeviceId = deviceId; // Save to cache await _repository.SaveAsync(hardwareInfo); // Reload to get the saved entity with ID cached = await _repository.GetByDeviceIdAsync(deviceId); return MapToDto(cached!); } public async Task> GetBatchHardwareInfoAsync( List deviceIds, bool forceRefresh = false) { var results = new List(); var semaphore = new SemaphoreSlim(10); // Max 10 concurrent queries var tasks = deviceIds.Select(async deviceId => { await semaphore.WaitAsync(); try { var data = await GetHardwareInfoAsync(deviceId, forceRefresh); return new BatchHardwareInfoResult { DeviceId = deviceId, Success = true, Data = data }; } catch (Exception ex) { _logger.LogError(ex, "Failed to get hardware info for device {DeviceId}", deviceId); return new BatchHardwareInfoResult { DeviceId = deviceId, Success = false, Error = ex.Message }; } finally { semaphore.Release(); } }); results = (await Task.WhenAll(tasks)).ToList(); return results; } private HardwareInfoDto MapToDto(HardwareInfo hardwareInfo) { return new HardwareInfoDto { DeviceId = hardwareInfo.DeviceId, IpAddress = hardwareInfo.Device.IpAddress, LastUpdated = hardwareInfo.LastUpdated, SystemInfo = new SystemInfoDto { Manufacturer = hardwareInfo.SystemManufacturer, Model = hardwareInfo.SystemModel, SerialNumber = hardwareInfo.SystemSerialNumber }, Processor = new ProcessorInfoDto { Model = hardwareInfo.ProcessorModel, Cores = hardwareInfo.ProcessorCores, Threads = hardwareInfo.ProcessorThreads, MaxClockSpeed = hardwareInfo.ProcessorMaxClockSpeed, CurrentClockSpeed = hardwareInfo.ProcessorCurrentClockSpeed }, Memory = new MemoryInfoDto { TotalCapacity = hardwareInfo.TotalMemoryBytes, TotalCapacityGB = hardwareInfo.TotalMemoryBytes.HasValue ? (int)(hardwareInfo.TotalMemoryBytes.Value / 1024 / 1024 / 1024) : null, Modules = hardwareInfo.MemoryModules.Select(m => new MemoryModuleDto { Slot = m.SlotLocation, Capacity = m.CapacityBytes, CapacityGB = m.CapacityBytes.HasValue ? (int)(m.CapacityBytes.Value / 1024 / 1024 / 1024) : null, Speed = m.SpeedMHz, Type = m.MemoryType, Manufacturer = m.Manufacturer, PartNumber = m.PartNumber, SerialNumber = m.SerialNumber }).ToList() }, Storage = new StorageInfoDto { Devices = hardwareInfo.StorageDevices.Select(d => new StorageDeviceDto { DeviceId = d.DeviceId, Model = d.Model, Capacity = d.CapacityBytes, CapacityGB = d.CapacityBytes.HasValue ? (int)(d.CapacityBytes.Value / 1024 / 1024 / 1024) : null, InterfaceType = d.InterfaceType }).ToList() } }; } }