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 值 } } } }