wenxiangchao
2026-11-02 9d1660270272180cd71147d6ddd53d1382643d2e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
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>
        /// 错误代码
        /// </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 解析 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-设备-通道映射解析器
        /// </summary>
        private readonly SlotDeviceMappingResolver _slotMappingResolver;
 
        /// <summary>
        /// 初始化电源配置服务实例
        /// </summary>
        /// <param name="resourceManager">资源管理器</param>
        /// <param name="deviceManager">设备管理器</param>
        /// <param name="slotMappingResolver">Slot 映射解析器</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 解析映射获取设备端点,验证资源预约后发送 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. 解析 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);
            }
        }
 
        /// <summary>
        /// 异步测量 Slot 对应电源的输出值(电压、电流)。
        /// 根据 SlotId 解析映射获取设备端点,查询实时测量值。
        /// </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. 解析 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);
            }
        }
 
        /// <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. 解析 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);
            }
        }
 
        /// <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>
        /// 解析设备返回的测量值响应字符串。
        /// 支持格式: "voltage,current" 或单个数值。
        /// </summary>
        /// <param name="rawResponse">原始响应字符串</param>
        /// <param name="voltage">解析后的电压值</param>
        /// <param name="current">解析后的电流值</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)
            {
                // 解析失败时保持 null 值
            }
        }
    }
}