207 lines
7.3 KiB
C#
207 lines
7.3 KiB
C#
using System.Collections.ObjectModel;
|
||
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using SSPCTester.Devices.Config;
|
||
using SSPCTester.Devices.Interfaces;
|
||
using SSPCTester.Logic.Models;
|
||
using SSPCTester.UI.Services;
|
||
|
||
namespace SSPCTester.UI.ViewModels;
|
||
|
||
/// <summary>功率损耗页 VM。</summary>
|
||
public partial class PowerTestViewModel : ObservableObject
|
||
{
|
||
private readonly ISspc _sspc;
|
||
private readonly IPowerSupply _power;
|
||
private readonly ILoad _load;
|
||
private readonly UiLogSink _log;
|
||
|
||
public ObservableCollection<PowerLossRow> Rows { get; } = new();
|
||
public ObservableCollection<int> Channels { get; } = new();
|
||
[ObservableProperty] private Project? _project;
|
||
[ObservableProperty] private int _selectedChannel;
|
||
[ObservableProperty] private int _samplesPerChannel = 5;
|
||
[ObservableProperty] private int _intervalMs = 100;
|
||
[ObservableProperty] private PowerLossRow? _current;
|
||
[ObservableProperty] private bool _busy;
|
||
|
||
public bool CanOperate => !Busy;
|
||
|
||
public PowerTestViewModel(ISspc sspc, IPowerSupply power, ILoad load, UiLogSink log)
|
||
{
|
||
_sspc = sspc;
|
||
_power = power;
|
||
_load = load;
|
||
_log = log;
|
||
for (int i = 1; i <= sspc.ChannelCount; i++) Channels.Add(i);
|
||
SelectedChannel = 1;
|
||
}
|
||
|
||
partial void OnBusyChanged(bool value) => OnPropertyChanged(nameof(CanOperate));
|
||
|
||
public void SetProject(Project p)
|
||
{
|
||
Project = p;
|
||
Rows.Clear();
|
||
if (p.PowerResults.Count == 0)
|
||
{
|
||
for (int i = 1; i <= Channels.Count; i++)
|
||
p.PowerResults.Add(new PowerLossRow { Channel = i });
|
||
}
|
||
foreach (var r in p.PowerResults) Rows.Add(r);
|
||
Current = Rows.FirstOrDefault();
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task TurnOnCurrentChannel()
|
||
{
|
||
Busy = true;
|
||
int ch = SelectedChannel;
|
||
try
|
||
{
|
||
if (!_sspc.IsConnected) await _sspc.ConnectAsync();
|
||
await _sspc.TurnOnAsync(ch);
|
||
_log.Add(UiLogLevel.Info, $"CH{ch} 已开启。");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_log.Add(UiLogLevel.Error, $"CH{ch} 开启失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
Busy = false;
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task TurnOffCurrentChannel()
|
||
{
|
||
Busy = true;
|
||
int ch = SelectedChannel;
|
||
try
|
||
{
|
||
if (!_sspc.IsConnected) await _sspc.ConnectAsync();
|
||
await _sspc.TurnOffAsync(ch);
|
||
_log.Add(UiLogLevel.Info, $"CH{ch} 已关断。");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_log.Add(UiLogLevel.Error, $"CH{ch} 关断失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
Busy = false;
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task MeasureCurrent()
|
||
{
|
||
if (Project == null) return;
|
||
Busy = true;
|
||
int ch = SelectedChannel;
|
||
var row = Rows.First(r => r.Channel == ch);
|
||
try
|
||
{
|
||
// 功率损耗输入端来自 UDP5080-100 电源实时测量。
|
||
|
||
try
|
||
{
|
||
await Task.CompletedTask;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"Modbus(继电器端)连接失败:{ex.Message}。请在设置中检查串口与连接。", ex);
|
||
}
|
||
try
|
||
{
|
||
if (!_power.IsConnected) await _power.ConnectAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"高速采集卡连接失败:{ex.Message}。请在设置中检查采集卡。", ex);
|
||
}
|
||
|
||
try
|
||
{
|
||
if (!_load.IsConnected) await _load.ConnectAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"IT8702P connection failed: {ex.Message}", ex);
|
||
}
|
||
|
||
await _power.OutputAsync(true);
|
||
await _load.InputAsync(true);
|
||
|
||
double sVin = 0, sIin = 0, sVout = 0, sIout = 0, sPout = 0;
|
||
for (int s = 0; s < SamplesPerChannel; s++)
|
||
{
|
||
/*
|
||
// 输出端:Modbus 继电器端电压 + 通流电流(SSPC 为串联开关,Iin = Iout)。
|
||
var measurement = await _load.ReadPowerAsync();
|
||
// 输入端:高速采集卡电压。
|
||
var daqValues = await _daq.ReadInstantAsync(new[] { daqVoltageChannel });
|
||
*/
|
||
var input = await _power.ReadAsync();
|
||
var measurement = await _load.ReadPowerAsync();
|
||
double vin = input.volts;
|
||
double iin = Math.Abs(input.amps);
|
||
double vout = measurement.Volts;
|
||
double iout = Math.Abs(measurement.Amps);
|
||
double pout = measurement.Watts;
|
||
if (!double.IsFinite(vin) || !double.IsFinite(iin) || !double.IsFinite(vout) || !double.IsFinite(iout) || !double.IsFinite(pout))
|
||
throw new InvalidOperationException("功率损耗采样无效,请检查采集卡电压通道标定与 Modbus 连接。");
|
||
|
||
sVin += vin;
|
||
sIin += iin;
|
||
sVout += vout;
|
||
sIout += iout;
|
||
sPout += pout;
|
||
await Task.Delay(IntervalMs);
|
||
}
|
||
|
||
row.Vin = sVin / SamplesPerChannel;
|
||
row.Iin = sIin / SamplesPerChannel;
|
||
row.Vout = sVout / SamplesPerChannel;
|
||
row.Iout = sIout / SamplesPerChannel;
|
||
row.MeasuredPowerOut = sPout / SamplesPerChannel;
|
||
row.Status = TestStatus.Measured;
|
||
Current = row;
|
||
_log.Add(UiLogLevel.Success, $"CH{ch} 功率损耗 {row.PowerLossW:F3} W,效率 {row.EfficiencyPct:F1}%");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
row.Status = TestStatus.Fail;
|
||
_log.Add(UiLogLevel.Error, $"CH{ch} 功率损耗读取失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
Busy = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>解析用于输入端电压的高速采集卡通道(取已标定的电压通道,优先 OutputVoltage)。</summary>
|
||
private static int ResolveDaqVoltageChannel(DaqOptions options)
|
||
{
|
||
if (options.Channels == null || options.Channels.Count == 0)
|
||
throw new InvalidOperationException("未配置采集卡通道,无法执行功率损耗测试。");
|
||
|
||
var candidates = options.Channels
|
||
.Where(x => x.PhysicalChannel >= 0 && x.PhysicalChannel < options.ChannelCount)
|
||
.Where(x => x.IsCalibrated)
|
||
.Where(x => IsVoltageRole(x.Role))
|
||
.ToArray();
|
||
|
||
var pick = candidates.FirstOrDefault(x => string.Equals(x.Role, nameof(DaqChannelRole.OutputVoltage), StringComparison.OrdinalIgnoreCase))
|
||
?? candidates.FirstOrDefault();
|
||
if (pick == null)
|
||
throw new InvalidOperationException("功率损耗测试需要一个已标定的采集卡电压通道(输入端电压),请在设置中配置并标定。");
|
||
return pick.PhysicalChannel;
|
||
}
|
||
|
||
private static bool IsVoltageRole(string? role) =>
|
||
Enum.TryParse<DaqChannelRole>(role, true, out var parsed)
|
||
&& parsed is DaqChannelRole.OutputVoltage or DaqChannelRole.InputVoltage;
|
||
}
|