From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Aging.Infrastructure/Services/PowerSupplyService.cs |  543 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 543 insertions(+), 0 deletions(-)

diff --git a/Aging.Infrastructure/Services/PowerSupplyService.cs b/Aging.Infrastructure/Services/PowerSupplyService.cs
new file mode 100644
index 0000000..693a312
--- /dev/null
+++ b/Aging.Infrastructure/Services/PowerSupplyService.cs
@@ -0,0 +1,543 @@
+using System.Diagnostics;
+using Aging.Infrastructure.Contracts;
+using log4net;
+
+namespace Aging.Infrastructure.Services
+{
+    /// <summary>
+    /// 鐢垫簮閰嶇疆鍛戒护
+    /// </summary>
+    public sealed class PowerCommand
+    {
+        /// <summary>
+        /// 鐩爣鐢靛帇鍊硷紙鍙�夛紝鍗曚綅: V锛�
+        /// </summary>
+        public double? Voltage { get; set; }
+
+        /// <summary>
+        /// 鐩爣鐢垫祦闄愬埗鍊硷紙鍙�夛紝鍗曚綅: A锛�
+        /// </summary>
+        public double? CurrentLimit { get; set; }
+
+        /// <summary>
+        /// 杈撳嚭寮�鍏崇姸鎬侊紙true=ON, false=OFF, null=涓嶆敼鍙橈級
+        /// </summary>
+        public bool? Output { get; set; }
+    }
+
+    /// <summary>
+    /// 鐢垫簮鎿嶄綔缁撴灉
+    /// </summary>
+    public sealed class PowerOperationResult
+    {
+        /// <summary>
+        /// 鎿嶄綔鏄惁鎴愬姛
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// 瀹炴祴鐢靛帇鍊硷紙鍗曚綅: V锛�
+        /// </summary>
+        public double? MeasuredVoltage { get; set; }
+
+        /// <summary>
+        /// 瀹炴祴鐢垫祦鍊硷紙鍗曚綅: A锛�
+        /// </summary>
+        public double? MeasuredCurrent { get; set; }
+
+        /// <summary>
+        /// 璁惧鍘熷鍝嶅簲瀛楃涓�
+        /// </summary>
+        public string? RawResponse { get; set; }
+
+        /// <summary>
+        /// 閿欒浠g爜
+        /// </summary>
+        public string? ErrorCode { get; set; }
+
+        /// <summary>
+        /// 閿欒淇℃伅
+        /// </summary>
+        public string? ErrorMessage { get; set; }
+
+        /// <summary>
+        /// 鎿嶄綔寤惰繜锛堟绉掞級
+        /// </summary>
+        public double LatencyMs { get; set; }
+
+        /// <summary>
+        /// 鍒涘缓涓�涓垚鍔熺殑缁撴灉
+        /// </summary>
+        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
+            };
+        }
+
+        /// <summary>
+        /// 鍒涘缓涓�涓け璐ョ殑缁撴灉
+        /// </summary>
+        public static PowerOperationResult Failed(string errorCode, string errorMessage, double latencyMs)
+        {
+            return new PowerOperationResult
+            {
+                Success = false,
+                ErrorCode = errorCode,
+                ErrorMessage = errorMessage,
+                LatencyMs = latencyMs
+            };
+        }
+    }
+
+    /// <summary>
+    /// 鐢垫簮閰嶇疆鏈嶅姟 - 闈㈠悜 Slot 鐨勯珮灞傜數婧愭搷浣溿��
+    /// 閫氳繃 SlotDeviceMappingResolver 瑙f瀽 Slot 鍒板叿浣撹澶囩鐐圭殑鏄犲皠锛�
+    /// 鍗忚皟 ResourceManager 鍜� DeviceManager 瀹屾垚鐢垫簮閰嶇疆銆佹祴閲忓拰鐘舵�佹煡璇€��
+    /// </summary>
+    public sealed class PowerSupplyService
+    {
+        private static readonly ILog Log = LogManager.GetLogger(typeof(PowerSupplyService));
+
+        /// <summary>
+        /// 璧勬簮绠$悊鍣�
+        /// </summary>
+        private readonly IResourceManager _resourceManager;
+
+        /// <summary>
+        /// 璁惧绠$悊鍣�
+        /// </summary>
+        private readonly IDeviceManager _deviceManager;
+
+        /// <summary>
+        /// Slot-璁惧-閫氶亾鏄犲皠瑙f瀽鍣�
+        /// </summary>
+        private readonly SlotDeviceMappingResolver _slotMappingResolver;
+
+        /// <summary>
+        /// 鍒濆鍖栫數婧愰厤缃湇鍔″疄渚�
+        /// </summary>
+        /// <param name="resourceManager">璧勬簮绠$悊鍣�</param>
+        /// <param name="deviceManager">璁惧绠$悊鍣�</param>
+        /// <param name="slotMappingResolver">Slot 鏄犲皠瑙f瀽鍣�</param>
+        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));
+        }
+
+        /// <summary>
+        /// 寮傛閰嶇疆 Slot 瀵瑰簲鐨勭數婧愬弬鏁般��
+        /// 鏍规嵁 SlotId 瑙f瀽鏄犲皠鑾峰彇璁惧绔偣锛岄獙璇佽祫婧愰绾﹀悗鍙戦�� SCPI 鍛戒护閰嶇疆鐢垫簮銆�
+        /// </summary>
+        /// <param name="slotId">Slot 鏍囪瘑绗�</param>
+        /// <param name="command">鐢垫簮閰嶇疆鍛戒护</param>
+        /// <param name="owner">鎵�鏈夎�呬护鐗�</param>
+        /// <param name="ct">鍙栨秷浠ょ墝</param>
+        /// <returns>鐢垫簮鎿嶄綔缁撴灉锛屽寘鍚祴閲忓��</returns>
+        public async Task<PowerOperationResult> 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. 瑙f瀽 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. 瑙f瀽鍥炶鍊�
+                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);
+            }
+        }
+
+        /// <summary>
+        /// 寮傛娴嬮噺 Slot 瀵瑰簲鐢垫簮鐨勮緭鍑哄�硷紙鐢靛帇銆佺數娴侊級銆�
+        /// 鏍规嵁 SlotId 瑙f瀽鏄犲皠鑾峰彇璁惧绔偣锛屾煡璇㈠疄鏃舵祴閲忓�笺��
+        /// </summary>
+        /// <param name="slotId">Slot 鏍囪瘑绗�</param>
+        /// <param name="owner">鎵�鏈夎�呬护鐗�</param>
+        /// <param name="ct">鍙栨秷浠ょ墝</param>
+        /// <returns>鐢垫簮鎿嶄綔缁撴灉锛屽寘鍚祴閲忓��</returns>
+        public async Task<PowerOperationResult> 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. 瑙f瀽 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. 瑙f瀽鍥炶鍊�
+                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);
+            }
+        }
+
+        /// <summary>
+        /// 寮傛鑾峰彇 Slot 瀵瑰簲鐢垫簮鐨勫綋鍓嶇姸鎬侊紙鐢靛帇銆佺數娴併�佽緭鍑哄紑鍏崇姸鎬侊級銆�
+        /// </summary>
+        /// <param name="slotId">Slot 鏍囪瘑绗�</param>
+        /// <param name="owner">鎵�鏈夎�呬护鐗�</param>
+        /// <param name="ct">鍙栨秷浠ょ墝</param>
+        /// <returns>鐢垫簮鎿嶄綔缁撴灉锛屽寘鍚姸鎬佷俊鎭�</returns>
+        public async Task<PowerOperationResult> 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. 瑙f瀽 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. 瑙f瀽娴嬮噺鍊�
+                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);
+            }
+        }
+
+        /// <summary>
+        /// 鏍规嵁鐢垫簮閰嶇疆鍛戒护鏋勫缓 SCPI 鍛戒护鍒楄〃
+        /// </summary>
+        /// <param name="command">鐢垫簮閰嶇疆鍛戒护</param>
+        /// <returns>SCPI 鍛戒护瀛楃涓插垪琛�</returns>
+        private static List<string> BuildScpiCommands(PowerCommand command)
+        {
+            var commands = new List<string>();
+
+            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;
+        }
+
+        /// <summary>
+        /// 瑙f瀽璁惧杩斿洖鐨勬祴閲忓�煎搷搴斿瓧绗︿覆銆�
+        /// 鏀寔鏍煎紡: "voltage,current" 鎴栧崟涓暟鍊笺��
+        /// </summary>
+        /// <param name="rawResponse">鍘熷鍝嶅簲瀛楃涓�</param>
+        /// <param name="voltage">瑙f瀽鍚庣殑鐢靛帇鍊�</param>
+        /// <param name="current">瑙f瀽鍚庣殑鐢垫祦鍊�</param>
+        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)
+            {
+                // 瑙f瀽澶辫触鏃朵繚鎸� null 鍊�
+            }
+        }
+    }
+}

--
Gitblit v1.9.1