using System.Diagnostics;
using Aging.Infrastructure.Contracts;
using log4net;
namespace Aging.Infrastructure.Services
{
///
/// 电源配置命令
///
public sealed class PowerCommand
{
///
/// 目标电压值(可选,单位: V)
///
public double? Voltage { get; set; }
///
/// 目标电流限制值(可选,单位: A)
///
public double? CurrentLimit { get; set; }
///
/// 输出开关状态(true=ON, false=OFF, null=不改变)
///
public bool? Output { get; set; }
}
///
/// 电源操作结果
///
public sealed class PowerOperationResult
{
///
/// 操作是否成功
///
public bool Success { get; set; }
///
/// 实测电压值(单位: V)
///
public double? MeasuredVoltage { get; set; }
///
/// 实测电流值(单位: A)
///
public double? MeasuredCurrent { get; set; }
///
/// 设备原始响应字符串
///
public string? RawResponse { get; set; }
///
/// 错误代码
///
public string? ErrorCode { get; set; }
///
/// 错误信息
///
public string? ErrorMessage { get; set; }
///
/// 操作延迟(毫秒)
///
public double LatencyMs { get; set; }
///
/// 创建一个成功的结果
///
public static PowerOperationResult Succeed(double? voltage, double? current, string? rawResponse, double latencyMs)
{
return new PowerOperationResult
{
Success = true,
MeasuredVoltage = voltage,
MeasuredCurrent = current,
RawResponse = rawResponse,
LatencyMs = latencyMs
};
}
///
/// 创建一个失败的结果
///
public static PowerOperationResult Failed(string errorCode, string errorMessage, double latencyMs)
{
return new PowerOperationResult
{
Success = false,
ErrorCode = errorCode,
ErrorMessage = errorMessage,
LatencyMs = latencyMs
};
}
}
///
/// 电源配置服务 - 面向 Slot 的高层电源操作。
/// 通过 SlotDeviceMappingResolver 解析 Slot 到具体设备端点的映射,
/// 协调 ResourceManager 和 DeviceManager 完成电源配置、测量和状态查询。
///
public sealed class PowerSupplyService
{
private static readonly ILog Log = LogManager.GetLogger(typeof(PowerSupplyService));
///
/// 资源管理器
///
private readonly IResourceManager _resourceManager;
///
/// 设备管理器
///
private readonly IDeviceManager _deviceManager;
///
/// Slot-设备-通道映射解析器
///
private readonly SlotDeviceMappingResolver _slotMappingResolver;
///
/// 初始化电源配置服务实例
///
/// 资源管理器
/// 设备管理器
/// Slot 映射解析器
public PowerSupplyService(
IResourceManager resourceManager,
IDeviceManager deviceManager,
SlotDeviceMappingResolver slotMappingResolver)
{
_resourceManager = resourceManager ?? throw new ArgumentNullException(nameof(resourceManager));
_deviceManager = deviceManager ?? throw new ArgumentNullException(nameof(deviceManager));
_slotMappingResolver = slotMappingResolver ?? throw new ArgumentNullException(nameof(slotMappingResolver));
}
///
/// 异步配置 Slot 对应的电源参数。
/// 根据 SlotId 解析映射获取设备端点,验证资源预约后发送 SCPI 命令配置电源。
///
/// Slot 标识符
/// 电源配置命令
/// 所有者令牌
/// 取消令牌
/// 电源操作结果,包含测量值
public async Task ConfigureAsync(
string slotId, PowerCommand command, ResourceOwnerToken owner, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(slotId))
throw new ArgumentException("SlotId 不能为空", nameof(slotId));
if (command == null)
throw new ArgumentNullException(nameof(command));
if (owner == null)
throw new ArgumentNullException(nameof(owner));
var stopwatch = Stopwatch.StartNew();
try
{
Log.Info($"[PowerSupplyService] 配置电源: Slot={slotId}, Owner={owner.OwnerId}");
// 1. 解析 Slot 映射
var endpoint = _slotMappingResolver.ResolveEndpoint(slotId, "PowerSupply");
if (endpoint == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] Slot {slotId} 未找到 PowerSupply 映射");
return PowerOperationResult.Failed("MAPPING_NOT_FOUND",
$"Slot {slotId} 未找到 PowerSupply 类型的设备映射", stopwatch.Elapsed.TotalMilliseconds);
}
var resourceKey = endpoint.ToResourceKey();
// 2. 验证预约
if (!_resourceManager.ValidateOwner(owner, resourceKey))
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 权限验证失败: Slot={slotId}, Device={endpoint.DeviceId}, Owner={owner.OwnerId}");
return PowerOperationResult.Failed("PERMISSION_DENIED",
$"所有者 {owner.OwnerId} 未预约 Slot {slotId} 对应的设备 {endpoint.DeviceId} 或令牌已过期",
stopwatch.Elapsed.TotalMilliseconds);
}
// 3. 获取租约
using var lease = await _resourceManager.AcquireOperationAsync(owner, resourceKey, ct).ConfigureAwait(false);
// 4. 获取设备驱动
var driver = _deviceManager.GetDriver(endpoint.DeviceId);
if (driver == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 设备 {endpoint.DeviceId} 驱动不存在");
return PowerOperationResult.Failed("DRIVER_NOT_FOUND",
$"设备 {endpoint.DeviceId} 的驱动未注册", stopwatch.Elapsed.TotalMilliseconds);
}
// 5. 构建并发送 SCPI 命令
var scpiCommands = BuildScpiCommands(command);
foreach (var scpiCommand in scpiCommands)
{
Log.Info($"[PowerSupplyService] 发送 SCPI 命令: Device={endpoint.DeviceId}, Command={scpiCommand}");
await driver.SendCommandAsync(scpiCommand, ct).ConfigureAwait(false);
}
// 6. 回读测量值
string rawResponse;
try
{
// 优先尝试 MEAS:ALL? 一次性获取所有测量值
rawResponse = await driver.QueryAsync("MEAS:ALL?", ct).ConfigureAwait(false);
Log.Info($"[PowerSupplyService] MEAS:ALL? 响应: {rawResponse}");
}
catch
{
// 如果 MEAS:ALL? 失败,分别查询电压和电流
Log.Info($"[PowerSupplyService] MEAS:ALL? 失败,尝试分别查询 VOLT? 和 CURR?");
var voltageResponse = await driver.QueryAsync("VOLT?", ct).ConfigureAwait(false);
var currentResponse = await driver.QueryAsync("CURR?", ct).ConfigureAwait(false);
rawResponse = $"{voltageResponse},{currentResponse}";
}
// 7. 解析回读值
ParseMeasurementResponse(rawResponse, out var measuredVoltage, out var measuredCurrent);
stopwatch.Stop();
Log.Info($"[PowerSupplyService] 电源配置完成: Slot={slotId}, Device={endpoint.DeviceId}, " +
$"V={measuredVoltage}, I={measuredCurrent}, 延迟={stopwatch.Elapsed.TotalMilliseconds:F2}ms");
return PowerOperationResult.Succeed(measuredVoltage, measuredCurrent, rawResponse,
stopwatch.Elapsed.TotalMilliseconds);
}
catch (ResourceConflictException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 资源冲突: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("RESOURCE_CONFLICT", ex.Message, stopwatch.Elapsed.TotalMilliseconds);
}
catch (OperationCanceledException)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 电源配置被取消: Slot={slotId}");
return PowerOperationResult.Failed("OPERATION_CANCELLED", "操作已被取消",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (TimeoutException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 电源配置超时: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("TIMEOUT", $"电源配置超时: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (Exception ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 电源配置异常: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("UNKNOWN_ERROR", $"电源配置失败: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
}
///
/// 异步测量 Slot 对应电源的输出值(电压、电流)。
/// 根据 SlotId 解析映射获取设备端点,查询实时测量值。
///
/// Slot 标识符
/// 所有者令牌
/// 取消令牌
/// 电源操作结果,包含测量值
public async Task MeasureAsync(
string slotId, ResourceOwnerToken owner, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(slotId))
throw new ArgumentException("SlotId 不能为空", nameof(slotId));
if (owner == null)
throw new ArgumentNullException(nameof(owner));
var stopwatch = Stopwatch.StartNew();
try
{
Log.Info($"[PowerSupplyService] 测量电源: Slot={slotId}, Owner={owner.OwnerId}");
// 1. 解析 Slot 映射
var endpoint = _slotMappingResolver.ResolveEndpoint(slotId, "PowerSupply");
if (endpoint == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] Slot {slotId} 未找到 PowerSupply 映射");
return PowerOperationResult.Failed("MAPPING_NOT_FOUND",
$"Slot {slotId} 未找到 PowerSupply 类型的设备映射", stopwatch.Elapsed.TotalMilliseconds);
}
var resourceKey = endpoint.ToResourceKey();
// 2. 验证预约
if (!_resourceManager.ValidateOwner(owner, resourceKey))
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 权限验证失败: Slot={slotId}, Device={endpoint.DeviceId}, Owner={owner.OwnerId}");
return PowerOperationResult.Failed("PERMISSION_DENIED",
$"所有者 {owner.OwnerId} 未预约 Slot {slotId} 对应的设备 {endpoint.DeviceId} 或令牌已过期",
stopwatch.Elapsed.TotalMilliseconds);
}
// 3. 获取租约
using var lease = await _resourceManager.AcquireOperationAsync(owner, resourceKey, ct).ConfigureAwait(false);
// 4. 获取设备驱动
var driver = _deviceManager.GetDriver(endpoint.DeviceId);
if (driver == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 设备 {endpoint.DeviceId} 驱动不存在");
return PowerOperationResult.Failed("DRIVER_NOT_FOUND",
$"设备 {endpoint.DeviceId} 的驱动未注册", stopwatch.Elapsed.TotalMilliseconds);
}
// 5. 查询测量值
string rawResponse;
try
{
rawResponse = await driver.QueryAsync("MEAS:ALL?", ct).ConfigureAwait(false);
}
catch
{
var voltageResponse = await driver.QueryAsync("VOLT?", ct).ConfigureAwait(false);
var currentResponse = await driver.QueryAsync("CURR?", ct).ConfigureAwait(false);
rawResponse = $"{voltageResponse},{currentResponse}";
}
// 6. 解析回读值
ParseMeasurementResponse(rawResponse, out var measuredVoltage, out var measuredCurrent);
stopwatch.Stop();
Log.Info($"[PowerSupplyService] 测量完成: Slot={slotId}, Device={endpoint.DeviceId}, " +
$"V={measuredVoltage}, I={measuredCurrent}, 延迟={stopwatch.Elapsed.TotalMilliseconds:F2}ms");
return PowerOperationResult.Succeed(measuredVoltage, measuredCurrent, rawResponse,
stopwatch.Elapsed.TotalMilliseconds);
}
catch (ResourceConflictException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 资源冲突: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("RESOURCE_CONFLICT", ex.Message, stopwatch.Elapsed.TotalMilliseconds);
}
catch (OperationCanceledException)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 测量操作被取消: Slot={slotId}");
return PowerOperationResult.Failed("OPERATION_CANCELLED", "操作已被取消",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (TimeoutException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 测量操作超时: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("TIMEOUT", $"测量操作超时: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (Exception ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 测量操作异常: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("UNKNOWN_ERROR", $"测量操作失败: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
}
///
/// 异步获取 Slot 对应电源的当前状态(电压、电流、输出开关状态)。
///
/// Slot 标识符
/// 所有者令牌
/// 取消令牌
/// 电源操作结果,包含状态信息
public async Task GetPowerStatusAsync(
string slotId, ResourceOwnerToken owner, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(slotId))
throw new ArgumentException("SlotId 不能为空", nameof(slotId));
if (owner == null)
throw new ArgumentNullException(nameof(owner));
var stopwatch = Stopwatch.StartNew();
try
{
Log.Info($"[PowerSupplyService] 查询电源状态: Slot={slotId}, Owner={owner.OwnerId}");
// 1. 解析 Slot 映射
var endpoint = _slotMappingResolver.ResolveEndpoint(slotId, "PowerSupply");
if (endpoint == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] Slot {slotId} 未找到 PowerSupply 映射");
return PowerOperationResult.Failed("MAPPING_NOT_FOUND",
$"Slot {slotId} 未找到 PowerSupply 类型的设备映射", stopwatch.Elapsed.TotalMilliseconds);
}
var resourceKey = endpoint.ToResourceKey();
// 2. 验证预约
if (!_resourceManager.ValidateOwner(owner, resourceKey))
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 权限验证失败: Slot={slotId}, Device={endpoint.DeviceId}, Owner={owner.OwnerId}");
return PowerOperationResult.Failed("PERMISSION_DENIED",
$"所有者 {owner.OwnerId} 未预约 Slot {slotId} 对应的设备 {endpoint.DeviceId} 或令牌已过期",
stopwatch.Elapsed.TotalMilliseconds);
}
// 3. 获取租约
using var lease = await _resourceManager.AcquireOperationAsync(owner, resourceKey, ct).ConfigureAwait(false);
// 4. 获取设备驱动
var driver = _deviceManager.GetDriver(endpoint.DeviceId);
if (driver == null)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 设备 {endpoint.DeviceId} 驱动不存在");
return PowerOperationResult.Failed("DRIVER_NOT_FOUND",
$"设备 {endpoint.DeviceId} 的驱动未注册", stopwatch.Elapsed.TotalMilliseconds);
}
// 5. 查询电压、电流和输出状态
var voltageResponse = await driver.QueryAsync("VOLT?", ct).ConfigureAwait(false);
var currentResponse = await driver.QueryAsync("CURR?", ct).ConfigureAwait(false);
var outputResponse = await driver.QueryAsync("OUTP?", ct).ConfigureAwait(false);
var rawResponse = $"VOLT={voltageResponse},CURR={currentResponse},OUTP={outputResponse}";
// 6. 解析测量值
ParseMeasurementResponse($"{voltageResponse},{currentResponse}",
out var measuredVoltage, out var measuredCurrent);
stopwatch.Stop();
Log.Info($"[PowerSupplyService] 电源状态查询完成: Slot={slotId}, Device={endpoint.DeviceId}, " +
$"V={measuredVoltage}, I={measuredCurrent}, OUTP={outputResponse}, " +
$"延迟={stopwatch.Elapsed.TotalMilliseconds:F2}ms");
return new PowerOperationResult
{
Success = true,
MeasuredVoltage = measuredVoltage,
MeasuredCurrent = measuredCurrent,
RawResponse = rawResponse,
LatencyMs = stopwatch.Elapsed.TotalMilliseconds
};
}
catch (ResourceConflictException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 资源冲突: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("RESOURCE_CONFLICT", ex.Message, stopwatch.Elapsed.TotalMilliseconds);
}
catch (OperationCanceledException)
{
stopwatch.Stop();
Log.Warn($"[PowerSupplyService] 状态查询被取消: Slot={slotId}");
return PowerOperationResult.Failed("OPERATION_CANCELLED", "操作已被取消",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (TimeoutException ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 状态查询超时: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("TIMEOUT", $"状态查询超时: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
catch (Exception ex)
{
stopwatch.Stop();
Log.Error($"[PowerSupplyService] 状态查询异常: Slot={slotId}, 错误: {ex.Message}", ex);
return PowerOperationResult.Failed("UNKNOWN_ERROR", $"状态查询失败: {ex.Message}",
stopwatch.Elapsed.TotalMilliseconds);
}
}
///
/// 根据电源配置命令构建 SCPI 命令列表
///
/// 电源配置命令
/// SCPI 命令字符串列表
private static List BuildScpiCommands(PowerCommand command)
{
var commands = new List();
if (command.Voltage.HasValue)
{
commands.Add($"VOLT {command.Voltage.Value:G}");
}
if (command.CurrentLimit.HasValue)
{
commands.Add($"CURR {command.CurrentLimit.Value:G}");
}
if (command.Output.HasValue)
{
commands.Add(command.Output.Value ? "OUTP ON" : "OUTP OFF");
}
return commands;
}
///
/// 解析设备返回的测量值响应字符串。
/// 支持格式: "voltage,current" 或单个数值。
///
/// 原始响应字符串
/// 解析后的电压值
/// 解析后的电流值
private static void ParseMeasurementResponse(string rawResponse, out double? voltage, out double? current)
{
voltage = null;
current = null;
if (string.IsNullOrWhiteSpace(rawResponse))
return;
try
{
var parts = rawResponse.Split(',');
if (parts.Length >= 1 && double.TryParse(parts[0].Trim(), out var v))
{
voltage = v;
}
if (parts.Length >= 2 && double.TryParse(parts[1].Trim(), out var c))
{
current = c;
}
}
catch (Exception)
{
// 解析失败时保持 null 值
}
}
}
}