- 添加OsDevice模型和OsDevicesController - 实现WindowsScannerService用于网络扫描和WMI查询 - 添加AMT设备UUID查询功能(从CIM_ComputerSystemPackage获取PlatformGUID) - 实现PlatformGUID到标准UUID格式的转换(字节序转换) - 修复HardwareInfoRepository保存UUID的问题 - 前端添加OS设备管理页面和UUID获取/刷新按钮 - 添加数据库迁移脚本
837 lines
33 KiB
C#
837 lines
33 KiB
C#
using AmtScanner.Api.Models;
|
||
using Intel.Management.Wsman;
|
||
using System.Security;
|
||
|
||
namespace AmtScanner.Api.Services;
|
||
|
||
public class AmtHardwareQueryService : IAmtHardwareQueryService
|
||
{
|
||
private readonly ILogger<AmtHardwareQueryService> _logger;
|
||
|
||
public AmtHardwareQueryService(ILogger<AmtHardwareQueryService> logger)
|
||
{
|
||
_logger = logger;
|
||
}
|
||
|
||
public async Task<HardwareInfo> QueryHardwareInfoAsync(
|
||
string ipAddress,
|
||
string username,
|
||
string password,
|
||
List<int> openPorts)
|
||
{
|
||
var hardwareInfo = new HardwareInfo
|
||
{
|
||
CreatedAt = DateTime.UtcNow,
|
||
UpdatedAt = DateTime.UtcNow,
|
||
LastUpdated = DateTime.UtcNow
|
||
};
|
||
|
||
try
|
||
{
|
||
// Determine protocol and port
|
||
string protocol;
|
||
int port;
|
||
|
||
if (openPorts.Contains(16992))
|
||
{
|
||
protocol = "http";
|
||
port = 16992;
|
||
}
|
||
else if (openPorts.Contains(16993))
|
||
{
|
||
protocol = "https";
|
||
port = 16993;
|
||
}
|
||
else
|
||
{
|
||
throw new Exception("No suitable AMT port found");
|
||
}
|
||
|
||
_logger.LogInformation("Querying hardware info from {Protocol}://{Ip}:{Port}", protocol, ipAddress, port);
|
||
|
||
// Create secure password
|
||
var securePassword = new SecureString();
|
||
foreach (char c in password)
|
||
{
|
||
securePassword.AppendChar(c);
|
||
}
|
||
securePassword.MakeReadOnly();
|
||
|
||
// Create WS-Management connection
|
||
var connection = new WsmanConnection();
|
||
connection.Address = $"{protocol}://{ipAddress}:{port}/wsman";
|
||
connection.SetCredentials(username, securePassword);
|
||
|
||
// Accept self-signed certificates for HTTPS
|
||
if (protocol == "https")
|
||
{
|
||
connection.Options.ServerCertificateValidationCallback = (certificate, sslPolicyErrors) =>
|
||
{
|
||
if (certificate.Subject.Equals(certificate.Issuer))
|
||
{
|
||
return true;
|
||
}
|
||
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
}
|
||
|
||
await Task.Run(() =>
|
||
{
|
||
// Query all hardware components sequentially (not in parallel)
|
||
QuerySystemInfo(connection, hardwareInfo);
|
||
QueryProcessorInfo(connection, hardwareInfo);
|
||
QueryMemoryInfo(connection, hardwareInfo);
|
||
QueryStorageInfo(connection, hardwareInfo);
|
||
});
|
||
|
||
_logger.LogInformation("Successfully queried hardware info from {Ip}", ipAddress);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "Error querying hardware info from {Ip}", ipAddress);
|
||
throw;
|
||
}
|
||
|
||
return hardwareInfo;
|
||
}
|
||
|
||
private void QuerySystemInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("Querying system information");
|
||
|
||
var query = connection.ExecQuery("SELECT * FROM CIM_ComputerSystem WHERE Name='ManagedSystem'");
|
||
|
||
int count = 0;
|
||
foreach (IWsmanItem item in query)
|
||
{
|
||
count++;
|
||
_logger.LogDebug("Found CIM_ComputerSystem item {Count}", count);
|
||
|
||
var manufacturer = item.Object.GetProperty("Manufacturer");
|
||
_logger.LogDebug("Manufacturer IsNull: {IsNull}, Value: {Value}", manufacturer.IsNull, manufacturer.IsNull ? "null" : manufacturer.ToString());
|
||
if (!manufacturer.IsNull)
|
||
{
|
||
hardwareInfo.SystemManufacturer = manufacturer.ToString();
|
||
}
|
||
|
||
var model = item.Object.GetProperty("Model");
|
||
_logger.LogDebug("Model IsNull: {IsNull}, Value: {Value}", model.IsNull, model.IsNull ? "null" : model.ToString());
|
||
if (!model.IsNull)
|
||
{
|
||
hardwareInfo.SystemModel = model.ToString();
|
||
}
|
||
|
||
// Try to get serial number (may not be available)
|
||
try
|
||
{
|
||
var serialNumber = item.Object.GetProperty("SerialNumber");
|
||
_logger.LogDebug("SerialNumber IsNull: {IsNull}, Value: {Value}", serialNumber.IsNull, serialNumber.IsNull ? "null" : serialNumber.ToString());
|
||
if (!serialNumber.IsNull)
|
||
{
|
||
hardwareInfo.SystemSerialNumber = serialNumber.ToString();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "Serial number property not available");
|
||
}
|
||
|
||
break; // Only need first result
|
||
}
|
||
|
||
if (count == 0)
|
||
{
|
||
_logger.LogWarning("No CIM_ComputerSystem items found");
|
||
}
|
||
|
||
_logger.LogDebug("System info: {Manufacturer} {Model}",
|
||
hardwareInfo.SystemManufacturer, hardwareInfo.SystemModel);
|
||
|
||
// Query UUID from CIM_ComputerSystemPackage (在同一个 try 块内查询,确保连接有效)
|
||
QuerySystemUuid(connection, hardwareInfo);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to query system information");
|
||
}
|
||
}
|
||
|
||
private void QuerySystemUuid(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation("Querying system UUID");
|
||
|
||
// 通过 CIM_ComputerSystemPackage 获取 PlatformGUID (UUID)
|
||
var query = connection.ExecQuery("SELECT * FROM CIM_ComputerSystemPackage");
|
||
|
||
foreach (IWsmanItem item in query)
|
||
{
|
||
try
|
||
{
|
||
// 尝试获取 PlatformGUID
|
||
var platformGuid = item.Object.GetProperty("PlatformGUID");
|
||
_logger.LogInformation("PlatformGUID IsNull: {IsNull}, Value: {Value}",
|
||
platformGuid.IsNull, platformGuid.IsNull ? "null" : platformGuid.ToString());
|
||
if (!platformGuid.IsNull && !string.IsNullOrWhiteSpace(platformGuid.ToString()))
|
||
{
|
||
var rawGuid = platformGuid.ToString().Trim();
|
||
// 将 PlatformGUID 转换为标准 UUID 格式
|
||
var formattedUuid = FormatPlatformGuidToUuid(rawGuid);
|
||
hardwareInfo.SystemUuid = formattedUuid;
|
||
_logger.LogInformation("Found UUID from CIM_ComputerSystemPackage.PlatformGUID: Raw={Raw}, Formatted={Formatted}",
|
||
rawGuid, formattedUuid);
|
||
return;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogInformation(ex, "PlatformGUID not available from CIM_ComputerSystemPackage");
|
||
}
|
||
|
||
// 备选:尝试从 Antecedent (CIM_Chassis) 获取 UUID
|
||
try
|
||
{
|
||
var antecedent = item.Object.GetProperty("Antecedent");
|
||
_logger.LogInformation("Antecedent IsNull: {IsNull}, IsA CIM_Chassis: {IsChassis}",
|
||
antecedent.IsNull, !antecedent.IsNull && antecedent.IsA("CIM_Chassis"));
|
||
if (!antecedent.IsNull && antecedent.IsA("CIM_Chassis"))
|
||
{
|
||
var chassisObj = antecedent.Ref.Get();
|
||
|
||
var uuid = chassisObj.GetProperty("UUID");
|
||
_logger.LogInformation("CIM_Chassis UUID IsNull: {IsNull}, Value: {Value}",
|
||
uuid.IsNull, uuid.IsNull ? "null" : uuid.ToString());
|
||
if (!uuid.IsNull && !string.IsNullOrWhiteSpace(uuid.ToString()))
|
||
{
|
||
hardwareInfo.SystemUuid = uuid.ToString().Trim();
|
||
_logger.LogInformation("Found UUID from CIM_Chassis: {Uuid}", hardwareInfo.SystemUuid);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogInformation(ex, "UUID not available from CIM_Chassis");
|
||
}
|
||
}
|
||
|
||
// 备选方案:尝试从 CIM_PhysicalPackage 获取
|
||
try
|
||
{
|
||
_logger.LogInformation("Trying CIM_PhysicalPackage for UUID");
|
||
var physicalQuery = connection.ExecQuery("SELECT * FROM CIM_PhysicalPackage");
|
||
foreach (IWsmanItem item in physicalQuery)
|
||
{
|
||
try
|
||
{
|
||
var uuid = item.Object.GetProperty("UUID");
|
||
_logger.LogInformation("CIM_PhysicalPackage UUID IsNull: {IsNull}, Value: {Value}",
|
||
uuid.IsNull, uuid.IsNull ? "null" : uuid.ToString());
|
||
if (!uuid.IsNull && !string.IsNullOrWhiteSpace(uuid.ToString()))
|
||
{
|
||
hardwareInfo.SystemUuid = uuid.ToString().Trim();
|
||
_logger.LogInformation("Found UUID from CIM_PhysicalPackage: {Uuid}", hardwareInfo.SystemUuid);
|
||
return;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogInformation(ex, "UUID property not available from CIM_PhysicalPackage item");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogInformation(ex, "Failed to query CIM_PhysicalPackage for UUID");
|
||
}
|
||
|
||
_logger.LogWarning("Could not find system UUID from any source");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to query system UUID");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将 AMT PlatformGUID 转换为标准 UUID 格式
|
||
/// PlatformGUID 是 32 位十六进制字符串,需要转换为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 格式
|
||
///
|
||
/// 示例:
|
||
/// AMT PlatformGUID: B826D58CD3E2E31187820B47ABD01400
|
||
/// Windows UUID: 8CD526B8-E2D3-11E3-8782-0B47ABD01400
|
||
///
|
||
/// 转换规则:前三组需要按字节反转(每2个十六进制字符为1字节)
|
||
/// </summary>
|
||
private string FormatPlatformGuidToUuid(string platformGuid)
|
||
{
|
||
// 移除可能存在的连字符
|
||
var cleanGuid = platformGuid.Replace("-", "").ToUpperInvariant();
|
||
|
||
if (cleanGuid.Length != 32)
|
||
{
|
||
_logger.LogWarning("Invalid PlatformGUID length: {Length}, expected 32", cleanGuid.Length);
|
||
return platformGuid; // 返回原始值
|
||
}
|
||
|
||
// AMT PlatformGUID 格式(32字符):
|
||
// B826D58C D3E2 E311 8782 0B47ABD01400
|
||
// 位置: 0-7 8-11 12-15 16-19 20-31
|
||
|
||
// 标准 UUID 格式:
|
||
// 8CD526B8-E2D3-11E3-8782-0B47ABD01400
|
||
|
||
// 第一组:4 字节(8字符),按字节反转
|
||
// B826D58C -> 8C D5 26 B8 -> 8CD526B8
|
||
var part1 = cleanGuid.Substring(0, 8);
|
||
var part1Reversed = $"{part1[6]}{part1[7]}{part1[4]}{part1[5]}{part1[2]}{part1[3]}{part1[0]}{part1[1]}";
|
||
|
||
// 第二组:2 字节(4字符),按字节反转
|
||
// D3E2 -> E2 D3 -> E2D3
|
||
var part2 = cleanGuid.Substring(8, 4);
|
||
var part2Reversed = $"{part2[2]}{part2[3]}{part2[0]}{part2[1]}";
|
||
|
||
// 第三组:2 字节(4字符),按字节反转
|
||
// E311 -> 11 E3 -> 11E3
|
||
var part3 = cleanGuid.Substring(12, 4);
|
||
var part3Reversed = $"{part3[2]}{part3[3]}{part3[0]}{part3[1]}";
|
||
|
||
// 第四组:2 字节(4字符),不反转
|
||
var part4 = cleanGuid.Substring(16, 4);
|
||
|
||
// 第五组:6 字节(12字符),不反转
|
||
var part5 = cleanGuid.Substring(20, 12);
|
||
|
||
var result = $"{part1Reversed}-{part2Reversed}-{part3Reversed}-{part4}-{part5}";
|
||
_logger.LogInformation("UUID conversion: {Raw} -> {Formatted}", cleanGuid, result);
|
||
|
||
return result;
|
||
}
|
||
|
||
private void QueryProcessorInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("Querying processor information");
|
||
|
||
var query = connection.ExecQuery("SELECT * FROM CIM_Realizes");
|
||
foreach (IWsmanItem item in query)
|
||
{
|
||
if (item.Object.GetProperty("Dependent").IsA("CIM_Processor"))
|
||
{
|
||
var cpuObj = item.Object.GetProperty("Dependent").Ref.Get();
|
||
var chipObj = item.Object.GetProperty("Antecedent").Ref.Get();
|
||
|
||
// Try to get CPU model from Chip Version (more detailed)
|
||
try
|
||
{
|
||
var version = chipObj.GetProperty("Version");
|
||
if (!version.IsNull && !string.IsNullOrWhiteSpace(version.ToString()))
|
||
{
|
||
hardwareInfo.ProcessorModel = version.ToString().Trim();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Version not available, fall back to Family
|
||
}
|
||
|
||
// If no version, use processor family
|
||
if (string.IsNullOrEmpty(hardwareInfo.ProcessorModel))
|
||
{
|
||
var family = cpuObj.GetProperty("Family");
|
||
if (!family.IsNull)
|
||
{
|
||
hardwareInfo.ProcessorModel = MapProcessorFamily(family.ToString());
|
||
}
|
||
}
|
||
|
||
// Get clock speeds
|
||
var maxClockSpeed = cpuObj.GetProperty("MaxClockSpeed");
|
||
if (!maxClockSpeed.IsNull)
|
||
{
|
||
hardwareInfo.ProcessorMaxClockSpeed = int.Parse(maxClockSpeed.ToString());
|
||
}
|
||
|
||
var currentClockSpeed = cpuObj.GetProperty("CurrentClockSpeed");
|
||
if (!currentClockSpeed.IsNull)
|
||
{
|
||
hardwareInfo.ProcessorCurrentClockSpeed = int.Parse(currentClockSpeed.ToString());
|
||
}
|
||
|
||
// Try to get core count (may not be available on all systems)
|
||
try
|
||
{
|
||
var numberOfCores = cpuObj.GetProperty("NumberOfCores");
|
||
if (!numberOfCores.IsNull)
|
||
{
|
||
hardwareInfo.ProcessorCores = int.Parse(numberOfCores.ToString());
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Core count not available
|
||
}
|
||
|
||
// Try to get thread count
|
||
try
|
||
{
|
||
var numberOfLogicalProcessors = cpuObj.GetProperty("NumberOfLogicalProcessors");
|
||
if (!numberOfLogicalProcessors.IsNull)
|
||
{
|
||
hardwareInfo.ProcessorThreads = int.Parse(numberOfLogicalProcessors.ToString());
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Thread count not available
|
||
}
|
||
|
||
break; // Only need first processor
|
||
}
|
||
}
|
||
|
||
_logger.LogDebug("Processor info: {Model} @ {Speed}MHz",
|
||
hardwareInfo.ProcessorModel, hardwareInfo.ProcessorMaxClockSpeed);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to query processor information");
|
||
}
|
||
}
|
||
|
||
private void QueryMemoryInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("Querying memory information");
|
||
|
||
var query = connection.ExecQuery("SELECT * FROM CIM_PhysicalMemory");
|
||
long totalMemory = 0;
|
||
|
||
foreach (IWsmanItem item in query)
|
||
{
|
||
var module = new MemoryModule();
|
||
|
||
var tag = item.Object.GetProperty("Tag");
|
||
if (!tag.IsNull)
|
||
{
|
||
module.SlotLocation = tag.ToString();
|
||
}
|
||
|
||
var bankLabel = item.Object.GetProperty("BankLabel");
|
||
if (!bankLabel.IsNull && string.IsNullOrEmpty(module.SlotLocation))
|
||
{
|
||
module.SlotLocation = bankLabel.ToString();
|
||
}
|
||
|
||
var capacity = item.Object.GetProperty("Capacity");
|
||
if (!capacity.IsNull)
|
||
{
|
||
module.CapacityBytes = long.Parse(capacity.ToString());
|
||
totalMemory += module.CapacityBytes.Value;
|
||
}
|
||
|
||
// Try ConfiguredMemoryClockSpeed first (this is in MHz, available in newer AMT versions)
|
||
try
|
||
{
|
||
var configuredSpeed = item.Object.GetProperty("ConfiguredMemoryClockSpeed");
|
||
_logger.LogDebug("Memory ConfiguredMemoryClockSpeed: IsNull={IsNull}, Value={Value}",
|
||
configuredSpeed.IsNull, configuredSpeed.IsNull ? "null" : configuredSpeed.ToString());
|
||
if (!configuredSpeed.IsNull)
|
||
{
|
||
var configuredSpeedValue = int.Parse(configuredSpeed.ToString());
|
||
if (configuredSpeedValue > 0)
|
||
{
|
||
module.SpeedMHz = configuredSpeedValue;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "ConfiguredMemoryClockSpeed not available");
|
||
}
|
||
|
||
// If ConfiguredMemoryClockSpeed not available, try MaxMemorySpeed (also in MHz)
|
||
if (module.SpeedMHz == null || module.SpeedMHz == 0)
|
||
{
|
||
try
|
||
{
|
||
var maxSpeed = item.Object.GetProperty("MaxMemorySpeed");
|
||
_logger.LogDebug("Memory MaxMemorySpeed: IsNull={IsNull}, Value={Value}",
|
||
maxSpeed.IsNull, maxSpeed.IsNull ? "null" : maxSpeed.ToString());
|
||
if (!maxSpeed.IsNull)
|
||
{
|
||
var maxSpeedValue = int.Parse(maxSpeed.ToString());
|
||
if (maxSpeedValue > 0)
|
||
{
|
||
module.SpeedMHz = maxSpeedValue;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "MaxMemorySpeed not available");
|
||
}
|
||
}
|
||
|
||
// Speed property is in nanoseconds, convert to MHz if other properties not available
|
||
// Note: Speed in nanoseconds can be converted to MHz: MHz = 1000 / nanoseconds
|
||
if (module.SpeedMHz == null || module.SpeedMHz == 0)
|
||
{
|
||
var speed = item.Object.GetProperty("Speed");
|
||
_logger.LogDebug("Memory Speed (nanoseconds): IsNull={IsNull}, Value={Value}",
|
||
speed.IsNull, speed.IsNull ? "null" : speed.ToString());
|
||
if (!speed.IsNull)
|
||
{
|
||
var speedNs = int.Parse(speed.ToString());
|
||
if (speedNs > 0)
|
||
{
|
||
// Convert nanoseconds to MHz: MHz = 1000 / ns
|
||
// For example: 0.625 ns = 1600 MHz, but AMT typically returns 0 for this
|
||
// If speed is a reasonable nanosecond value (like 1-100), convert it
|
||
if (speedNs <= 100)
|
||
{
|
||
module.SpeedMHz = (int)(1000.0 / speedNs);
|
||
}
|
||
else
|
||
{
|
||
// Some older AMT versions might return MHz directly in Speed field
|
||
module.SpeedMHz = speedNs;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var memoryType = item.Object.GetProperty("MemoryType");
|
||
if (!memoryType.IsNull)
|
||
{
|
||
module.MemoryType = MapMemoryType(memoryType.ToString());
|
||
}
|
||
|
||
// Try to get FormFactor
|
||
try
|
||
{
|
||
var formFactor = item.Object.GetProperty("FormFactor");
|
||
if (!formFactor.IsNull)
|
||
{
|
||
var formFactorStr = MapFormFactor(formFactor.ToString());
|
||
_logger.LogDebug("Memory FormFactor: {FormFactor}", formFactorStr);
|
||
// We could add FormFactor to MemoryModule model if needed
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "FormFactor not available");
|
||
}
|
||
|
||
var manufacturer = item.Object.GetProperty("Manufacturer");
|
||
if (!manufacturer.IsNull)
|
||
{
|
||
module.Manufacturer = manufacturer.ToString();
|
||
}
|
||
|
||
var partNumber = item.Object.GetProperty("PartNumber");
|
||
if (!partNumber.IsNull)
|
||
{
|
||
module.PartNumber = partNumber.ToString();
|
||
}
|
||
|
||
try
|
||
{
|
||
var serialNumber = item.Object.GetProperty("SerialNumber");
|
||
if (!serialNumber.IsNull)
|
||
{
|
||
module.SerialNumber = serialNumber.ToString();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Serial number not available
|
||
}
|
||
|
||
hardwareInfo.MemoryModules.Add(module);
|
||
}
|
||
|
||
hardwareInfo.TotalMemoryBytes = totalMemory;
|
||
|
||
_logger.LogDebug("Memory info: {Count} modules, {TotalGB}GB total",
|
||
hardwareInfo.MemoryModules.Count, totalMemory / 1024 / 1024 / 1024);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to query memory information");
|
||
}
|
||
}
|
||
|
||
private void QueryStorageInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("Querying storage information");
|
||
|
||
var query = connection.ExecQuery("SELECT * FROM CIM_MediaAccessDevice");
|
||
|
||
foreach (IWsmanItem item in query)
|
||
{
|
||
var device = new StorageDevice();
|
||
|
||
var deviceId = item.Object.GetProperty("DeviceID");
|
||
if (!deviceId.IsNull)
|
||
{
|
||
device.DeviceId = deviceId.ToString();
|
||
}
|
||
|
||
// Try to get model from CIM_PhysicalPackage via CIM_Realizes association
|
||
// This is how the SDK gets the model number
|
||
try
|
||
{
|
||
var realizesQuery = connection.ExecQuery("SELECT * FROM CIM_Realizes");
|
||
foreach (IWsmanItem realizesItem in realizesQuery)
|
||
{
|
||
try
|
||
{
|
||
var dependent = realizesItem.Object.GetProperty("Dependent");
|
||
if (!dependent.IsNull && dependent.IsA("CIM_MediaAccessDevice"))
|
||
{
|
||
// Check if this is the same device
|
||
var depObj = dependent.Ref.Get();
|
||
var depDeviceId = depObj.GetProperty("DeviceID");
|
||
if (!depDeviceId.IsNull && depDeviceId.ToString() == device.DeviceId)
|
||
{
|
||
var antecedent = realizesItem.Object.GetProperty("Antecedent");
|
||
if (!antecedent.IsNull && antecedent.IsA("CIM_PhysicalPackage"))
|
||
{
|
||
var packageObj = antecedent.Ref.Get();
|
||
|
||
var model = packageObj.GetProperty("Model");
|
||
if (!model.IsNull && !string.IsNullOrWhiteSpace(model.ToString()))
|
||
{
|
||
device.Model = model.ToString().Trim();
|
||
_logger.LogDebug("Storage Model from CIM_PhysicalPackage: {Model}", device.Model);
|
||
}
|
||
|
||
// Also try to get serial number from package
|
||
try
|
||
{
|
||
var serialNumber = packageObj.GetProperty("SerialNumber");
|
||
if (!serialNumber.IsNull && !string.IsNullOrWhiteSpace(serialNumber.ToString()))
|
||
{
|
||
_logger.LogDebug("Storage SerialNumber from CIM_PhysicalPackage: {SerialNumber}", serialNumber.ToString());
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "Error processing CIM_Realizes item");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogDebug(ex, "Failed to query CIM_Realizes for storage model");
|
||
}
|
||
|
||
// Fallback: try to get model from MediaAccessDevice properties
|
||
if (string.IsNullOrEmpty(device.Model))
|
||
{
|
||
var caption = item.Object.GetProperty("Caption");
|
||
if (!caption.IsNull && !string.IsNullOrWhiteSpace(caption.ToString()))
|
||
{
|
||
device.Model = caption.ToString().Trim();
|
||
}
|
||
}
|
||
|
||
// If Caption is empty, try Name
|
||
if (string.IsNullOrEmpty(device.Model))
|
||
{
|
||
try
|
||
{
|
||
var name = item.Object.GetProperty("Name");
|
||
if (!name.IsNull && !string.IsNullOrWhiteSpace(name.ToString()))
|
||
{
|
||
device.Model = name.ToString().Trim();
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// If still empty, try Description
|
||
if (string.IsNullOrEmpty(device.Model))
|
||
{
|
||
try
|
||
{
|
||
var description = item.Object.GetProperty("Description");
|
||
if (!description.IsNull && !string.IsNullOrWhiteSpace(description.ToString()))
|
||
{
|
||
device.Model = description.ToString().Trim();
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// Try different capacity properties
|
||
try
|
||
{
|
||
var maxMediaSize = item.Object.GetProperty("MaxMediaSize");
|
||
if (!maxMediaSize.IsNull)
|
||
{
|
||
device.CapacityBytes = long.Parse(maxMediaSize.ToString()) * 1024; // Convert KB to bytes
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
try
|
||
{
|
||
var capacity = item.Object.GetProperty("Capacity");
|
||
if (!capacity.IsNull)
|
||
{
|
||
device.CapacityBytes = long.Parse(capacity.ToString());
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Capacity not available
|
||
}
|
||
}
|
||
|
||
// Try to get interface type
|
||
try
|
||
{
|
||
var interfaceType = item.Object.GetProperty("InterfaceType");
|
||
if (!interfaceType.IsNull)
|
||
{
|
||
device.InterfaceType = interfaceType.ToString();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Interface type not available
|
||
}
|
||
|
||
hardwareInfo.StorageDevices.Add(device);
|
||
}
|
||
|
||
_logger.LogDebug("Storage info: {Count} devices", hardwareInfo.StorageDevices.Count);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "Failed to query storage information");
|
||
}
|
||
}
|
||
|
||
private string MapProcessorFamily(string familyCode)
|
||
{
|
||
// Map processor family codes to readable names
|
||
// Based on DMTF SMBIOS Reference Specification
|
||
return familyCode switch
|
||
{
|
||
"2" => "Unknown",
|
||
"3" => "8086",
|
||
"4" => "80286",
|
||
"5" => "80386",
|
||
"6" => "80486",
|
||
"11" => "Pentium",
|
||
"12" => "Pentium Pro",
|
||
"13" => "Pentium II",
|
||
"15" => "Celeron",
|
||
"16" => "Pentium II Xeon",
|
||
"17" => "Pentium III",
|
||
"179" => "Intel Core i3",
|
||
"180" => "Intel Core i5",
|
||
"181" => "Intel Core i7",
|
||
"183" => "Intel Core i9",
|
||
"198" => "Intel Core", // Generic Intel Core processor
|
||
"205" => "Intel Core i9 (12th Gen)",
|
||
"206" => "Intel Core i7 (12th Gen)",
|
||
"207" => "Intel Core i5 (12th Gen)",
|
||
"208" => "Intel Core i3 (12th Gen)",
|
||
_ => $"Processor Family {familyCode}"
|
||
};
|
||
}
|
||
|
||
private string MapMemoryType(string typeCode)
|
||
{
|
||
// Map memory type codes to readable names
|
||
// Based on DMTF SMBIOS Reference Specification
|
||
return typeCode switch
|
||
{
|
||
"0" => "Unknown",
|
||
"1" => "Other",
|
||
"2" => "DRAM",
|
||
"3" => "Synchronous DRAM",
|
||
"4" => "Cache DRAM",
|
||
"5" => "EDO",
|
||
"6" => "EDRAM",
|
||
"7" => "VRAM",
|
||
"8" => "SRAM",
|
||
"9" => "RAM",
|
||
"10" => "ROM",
|
||
"11" => "Flash",
|
||
"12" => "EEPROM",
|
||
"13" => "FEPROM",
|
||
"14" => "EPROM",
|
||
"15" => "CDRAM",
|
||
"16" => "3DRAM",
|
||
"17" => "SDRAM",
|
||
"18" => "SGRAM",
|
||
"19" => "RDRAM",
|
||
"20" => "DDR",
|
||
"21" => "DDR2",
|
||
"22" => "DDR2 FB-DIMM",
|
||
"24" => "DDR3",
|
||
"25" => "FBD2",
|
||
"26" => "DDR4",
|
||
"27" => "LPDDR",
|
||
"28" => "LPDDR2",
|
||
"29" => "LPDDR3",
|
||
"30" => "LPDDR4",
|
||
"31" => "Logical non-volatile device",
|
||
"32" => "HBM (High Bandwidth Memory)",
|
||
"33" => "HBM2 (High Bandwidth Memory Generation 2)",
|
||
"34" => "DDR5",
|
||
"35" => "LPDDR5",
|
||
_ => $"Type {typeCode}"
|
||
};
|
||
}
|
||
|
||
private string MapFormFactor(string formFactorCode)
|
||
{
|
||
// Map form factor codes to readable names
|
||
// Based on DMTF SMBIOS Reference Specification
|
||
return formFactorCode switch
|
||
{
|
||
"0" => "Unknown",
|
||
"1" => "Other",
|
||
"2" => "Unknown",
|
||
"3" => "SIMM",
|
||
"4" => "SIP",
|
||
"5" => "Chip",
|
||
"6" => "DIP",
|
||
"7" => "ZIP",
|
||
"8" => "Proprietary Card",
|
||
"9" => "DIMM",
|
||
"10" => "TSOP",
|
||
"11" => "Row of chips",
|
||
"12" => "RIMM",
|
||
"13" => "SODIMM",
|
||
"14" => "SRIMM",
|
||
"15" => "FB-DIMM",
|
||
_ => $"FormFactor {formFactorCode}"
|
||
};
|
||
}
|
||
}
|