457 lines
16 KiB
C#
457 lines
16 KiB
C#
|
|
using System.Collections.ObjectModel;
|
|||
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|||
|
|
using CommunityToolkit.Mvvm.Input;
|
|||
|
|
using Microsoft.Extensions.Options;
|
|||
|
|
using SSPCTester.Devices.Config;
|
|||
|
|
using SSPCTester.Devices.Interfaces;
|
|||
|
|
using SSPCTester.Logic.Calculation;
|
|||
|
|
using SSPCTester.Logic.Models;
|
|||
|
|
using SSPCTester.Logic.Storage;
|
|||
|
|
using SSPCTester.Logic.Testing;
|
|||
|
|
using SSPCTester.UI.Services;
|
|||
|
|
|
|||
|
|
namespace SSPCTester.UI.ViewModels;
|
|||
|
|
|
|||
|
|
/// <summary>曲线测试页 VM。</summary>
|
|||
|
|
public partial class CurveTestViewModel : ObservableObject
|
|||
|
|
{
|
|||
|
|
private readonly ISspc _sspc;
|
|||
|
|
private readonly IDaq _daq;
|
|||
|
|
private readonly DaqOptions _daqOpts;
|
|||
|
|
private readonly UiLogSink _log;
|
|||
|
|
private readonly ProjectStore _store;
|
|||
|
|
private int _loadVersion;
|
|||
|
|
private int _viewWindowSaveVersion;
|
|||
|
|
private bool _syncingViewWindow;
|
|||
|
|
|
|||
|
|
public ObservableCollection<CurveResultRow> Rows { get; } = new();
|
|||
|
|
public ObservableCollection<int> Channels { get; } = new();
|
|||
|
|
|
|||
|
|
[ObservableProperty] private Project? _project;
|
|||
|
|
[ObservableProperty] private int _selectedChannel;
|
|||
|
|
[ObservableProperty] private CurveData? _risingCurve;
|
|||
|
|
[ObservableProperty] private CurveData? _fallingCurve;
|
|||
|
|
[ObservableProperty] private double _vMax = 28.0;
|
|||
|
|
[ObservableProperty] private bool _busy;
|
|||
|
|
[ObservableProperty] private CurveResultRow? _selectedRow;
|
|||
|
|
[ObservableProperty] private double? _risingViewStartMs;
|
|||
|
|
[ObservableProperty] private double? _risingViewEndMs;
|
|||
|
|
[ObservableProperty] private double? _fallingViewStartMs;
|
|||
|
|
[ObservableProperty] private double? _fallingViewEndMs;
|
|||
|
|
|
|||
|
|
public CurveTestViewModel(ISspc sspc, IDaq daq, IOptions<DeviceOptions> opt, UiLogSink log, ProjectStore store)
|
|||
|
|
{
|
|||
|
|
_sspc = sspc; _daq = daq; _daqOpts = opt.Value.Daq; _log = log;
|
|||
|
|
_store = store;
|
|||
|
|
for (int i = 1; i <= _sspc.ChannelCount; i++) Channels.Add(i);
|
|||
|
|
SelectedChannel = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public CurveThresholds Thresholds => Project?.CurveThresholds ?? new CurveThresholds();
|
|||
|
|
|
|||
|
|
public void SetProject(Project p)
|
|||
|
|
{
|
|||
|
|
Project = p;
|
|||
|
|
Rows.Clear();
|
|||
|
|
if (p.CurveResults.Count == 0)
|
|||
|
|
{
|
|||
|
|
for (int i = 1; i <= _sspc.ChannelCount; i++)
|
|||
|
|
p.CurveResults.Add(new CurveResultRow { Channel = i });
|
|||
|
|
}
|
|||
|
|
foreach (var r in p.CurveResults) Rows.Add(r);
|
|||
|
|
SyncSelectedRow();
|
|||
|
|
OnPropertyChanged(nameof(Thresholds));
|
|||
|
|
_ = LoadSelectedChannelCurvesAsync();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void AttachSession(TestSessionViewModel s)
|
|||
|
|
{
|
|||
|
|
s.CurveReady += (ch, rising, falling) =>
|
|||
|
|
{
|
|||
|
|
if (ch == SelectedChannel)
|
|||
|
|
{
|
|||
|
|
RisingCurve = rising;
|
|||
|
|
FallingCurve = falling;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
partial void OnSelectedChannelChanged(int value)
|
|||
|
|
{
|
|||
|
|
SyncSelectedRow();
|
|||
|
|
_ = LoadSelectedChannelCurvesAsync();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
partial void OnRisingViewStartMsChanged(double? value)
|
|||
|
|
{
|
|||
|
|
if (_syncingViewWindow || SelectedRow == null) return;
|
|||
|
|
SelectedRow.RisingViewStartMs = value;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
partial void OnRisingViewEndMsChanged(double? value)
|
|||
|
|
{
|
|||
|
|
if (_syncingViewWindow || SelectedRow == null) return;
|
|||
|
|
SelectedRow.RisingViewEndMs = value;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
partial void OnFallingViewStartMsChanged(double? value)
|
|||
|
|
{
|
|||
|
|
if (_syncingViewWindow || SelectedRow == null) return;
|
|||
|
|
SelectedRow.FallingViewStartMs = value;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
partial void OnFallingViewEndMsChanged(double? value)
|
|||
|
|
{
|
|||
|
|
if (_syncingViewWindow || SelectedRow == null) return;
|
|||
|
|
SelectedRow.FallingViewEndMs = value;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SyncSelectedRow()
|
|||
|
|
{
|
|||
|
|
_syncingViewWindow = true;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
SelectedRow = Rows.FirstOrDefault(x => x.Channel == SelectedChannel)
|
|||
|
|
?? Project?.CurveResults.FirstOrDefault(x => x.Channel == SelectedChannel);
|
|||
|
|
RisingViewStartMs = SelectedRow?.RisingViewStartMs ?? SelectedRow?.ViewStartMs;
|
|||
|
|
RisingViewEndMs = SelectedRow?.RisingViewEndMs ?? SelectedRow?.ViewEndMs;
|
|||
|
|
FallingViewStartMs = SelectedRow?.FallingViewStartMs ?? SelectedRow?.ViewStartMs;
|
|||
|
|
FallingViewEndMs = SelectedRow?.FallingViewEndMs ?? SelectedRow?.ViewEndMs;
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_syncingViewWindow = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[RelayCommand]
|
|||
|
|
private void ResetRisingView()
|
|||
|
|
{
|
|||
|
|
if (SelectedRow == null) return;
|
|||
|
|
RisingViewStartMs = null;
|
|||
|
|
RisingViewEndMs = null;
|
|||
|
|
SelectedRow.RisingViewStartMs = null;
|
|||
|
|
SelectedRow.RisingViewEndMs = null;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[RelayCommand]
|
|||
|
|
private void ResetFallingView()
|
|||
|
|
{
|
|||
|
|
if (SelectedRow == null) return;
|
|||
|
|
FallingViewStartMs = null;
|
|||
|
|
FallingViewEndMs = null;
|
|||
|
|
SelectedRow.FallingViewStartMs = null;
|
|||
|
|
SelectedRow.FallingViewEndMs = null;
|
|||
|
|
SaveProjectQuietly();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SaveProjectQuietly()
|
|||
|
|
{
|
|||
|
|
var project = Project;
|
|||
|
|
if (project == null) return;
|
|||
|
|
int version = Interlocked.Increment(ref _viewWindowSaveVersion);
|
|||
|
|
_ = SaveProjectQuietlyAsync(project, version);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task SaveProjectQuietlyAsync(Project project, int version)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
await Task.Delay(300);
|
|||
|
|
if (version != _viewWindowSaveVersion) return;
|
|||
|
|
await _store.SaveAsync(project);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex) { _log.Add(UiLogLevel.Warning, $"曲线视图窗口保存失败:{ex.Message}"); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task LoadSelectedChannelCurvesAsync()
|
|||
|
|
{
|
|||
|
|
int version = ++_loadVersion;
|
|||
|
|
var project = Project;
|
|||
|
|
if (project == null)
|
|||
|
|
{
|
|||
|
|
RisingCurve = null;
|
|||
|
|
FallingCurve = null;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var row = Rows.FirstOrDefault(x => x.Channel == SelectedChannel)
|
|||
|
|
?? project.CurveResults.FirstOrDefault(x => x.Channel == SelectedChannel);
|
|||
|
|
if (row == null)
|
|||
|
|
{
|
|||
|
|
RisingCurve = null;
|
|||
|
|
FallingCurve = null;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var rising = await WaveformStore.LoadAsync(project, row.RisingWaveformPath);
|
|||
|
|
var falling = await WaveformStore.LoadAsync(project, row.FallingWaveformPath);
|
|||
|
|
if (version != _loadVersion) return;
|
|||
|
|
|
|||
|
|
RisingCurve = rising;
|
|||
|
|
FallingCurve = falling;
|
|||
|
|
if (rising != null)
|
|||
|
|
VMax = CurveMetrics.EstimateVMax(rising.Voltage);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
if (version != _loadVersion) return;
|
|||
|
|
RisingCurve = null;
|
|||
|
|
FallingCurve = null;
|
|||
|
|
_log.Add(UiLogLevel.Warning, $"CH{SelectedChannel} 历史曲线加载失败:{ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>仅用于"浏览历史项目"时的可视化预览:调用 MockDaq 合成一段波形,不操作 SSPC。</summary>
|
|||
|
|
private async Task PreviewSelectedAsync()
|
|||
|
|
{
|
|||
|
|
if (Busy) return;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Busy = true;
|
|||
|
|
var request = new DaqActionCaptureRequest(_daqOpts.PreTriggerMs, _daqOpts.PostTriggerMs, true);
|
|||
|
|
var rising = await _daq.CaptureAroundActionAsync(request, _ => Task.CompletedTask);
|
|||
|
|
var falling = await _daq.CaptureAroundActionAsync(request with { IsRising = false }, _ => Task.CompletedTask);
|
|||
|
|
VMax = CurveMetrics.EstimateVMax(rising.Voltage);
|
|||
|
|
RisingCurve = rising;
|
|||
|
|
FallingCurve = falling;
|
|||
|
|
}
|
|||
|
|
catch { /* 预览失败不影响主流程 */ }
|
|||
|
|
finally { Busy = false; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[RelayCommand]
|
|||
|
|
private void OpenPopup()
|
|||
|
|
{
|
|||
|
|
var win = new Views.CurvePopupWindow(this) { Owner = System.Windows.Application.Current?.MainWindow };
|
|||
|
|
win.Show();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[RelayCommand]
|
|||
|
|
private async Task CaptureRising()
|
|||
|
|
{
|
|||
|
|
if (Project == null) return;
|
|||
|
|
Busy = true;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
int ch = SelectedChannel;
|
|||
|
|
await EnsureCurveCaptureReadyAsync(ch);
|
|||
|
|
await _sspc.TurnOffAsync(ch);
|
|||
|
|
await BasicTestCriteria.WaitForStableOffAsync(_sspc, ch);
|
|||
|
|
|
|||
|
|
_log.Add(UiLogLevel.Info, $"CH{ch} 开始预采集,待触发后发送开启动作。");
|
|||
|
|
var rising = await _daq.CaptureAroundActionAsync(
|
|||
|
|
new DaqActionCaptureRequest(_daqOpts.PreTriggerMs, _daqOpts.PostTriggerMs, true),
|
|||
|
|
async token =>
|
|||
|
|
{
|
|||
|
|
_log.Add(UiLogLevel.Info, $"CH{ch} 触发开启动作命令。");
|
|||
|
|
await _sspc.TurnOnAsync(ch, token);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await ApplyRisingCaptureAsync(Project, ch, rising);
|
|||
|
|
_log.Add(UiLogLevel.Success, $"CH{ch} 开启曲线采集完成。");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_log.Add(UiLogLevel.Error,
|
|||
|
|
$"开启曲线采集失败:{ex.GetType().Name} - {ex.Message}(CH{SelectedChannel},SSPC连接={_sspc.IsConnected},DAQ连接={_daq.IsConnected})");
|
|||
|
|
try { await _sspc.TurnOffAsync(SelectedChannel); } catch { }
|
|||
|
|
}
|
|||
|
|
finally { Busy = false; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[RelayCommand]
|
|||
|
|
private async Task CaptureFalling()
|
|||
|
|
{
|
|||
|
|
if (Project == null) return;
|
|||
|
|
Busy = true;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
int ch = SelectedChannel;
|
|||
|
|
await EnsureCurveCaptureReadyAsync(ch);
|
|||
|
|
_log.Add(UiLogLevel.Info, $"CH{ch} 准备采集关断曲线:先确保通道已开启并稳定。");
|
|||
|
|
await _sspc.TurnOnAsync(ch);
|
|||
|
|
await WaitForStableOnAsync(ch);
|
|||
|
|
|
|||
|
|
_log.Add(UiLogLevel.Info, $"CH{ch} 开始预采集,待触发后发送关断动作。");
|
|||
|
|
var falling = await _daq.CaptureAroundActionAsync(
|
|||
|
|
new DaqActionCaptureRequest(_daqOpts.PreTriggerMs, _daqOpts.PostTriggerMs, false),
|
|||
|
|
async token =>
|
|||
|
|
{
|
|||
|
|
_log.Add(UiLogLevel.Info, $"CH{ch} 触发关断动作命令。");
|
|||
|
|
await _sspc.TurnOffAsync(ch, token);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await ApplyFallingCaptureAsync(Project, ch, falling);
|
|||
|
|
_log.Add(UiLogLevel.Success, $"CH{ch} 关断曲线采集完成。");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_log.Add(UiLogLevel.Error,
|
|||
|
|
$"关断曲线采集失败:{ex.GetType().Name} - {ex.Message}(CH{SelectedChannel},SSPC连接={_sspc.IsConnected},DAQ连接={_daq.IsConnected})");
|
|||
|
|
try { await _sspc.TurnOffAsync(SelectedChannel); } catch { }
|
|||
|
|
}
|
|||
|
|
finally { Busy = false; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task EnsureCurveCaptureReadyAsync(int ch)
|
|||
|
|
{
|
|||
|
|
if (!ValidateCurveCaptureConfig(out var configMessage))
|
|||
|
|
{
|
|||
|
|
_log.Add(UiLogLevel.Error, $"CH{ch} 曲线采集被阻止:{configMessage}");
|
|||
|
|
throw new InvalidOperationException(configMessage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!_sspc.IsConnected) await _sspc.ConnectAsync();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new InvalidOperationException($"Modbus(继电器端)连接失败:{ex.Message}。请在设置中检查串口与连接,否则继电器无法动作。", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!_daq.IsConnected) await _daq.ConnectAsync();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new InvalidOperationException($"高速采集卡连接失败:{ex.Message}。请在设置中检查采集卡。", ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task ApplyRisingCaptureAsync(Project project, int ch, CurveData rising)
|
|||
|
|
{
|
|||
|
|
double vMax = CurveMetrics.EstimateVMax(rising.Voltage);
|
|||
|
|
var row = Rows.First(r => r.Channel == ch);
|
|||
|
|
row.OnTimeMs = CurveMetrics.OnTimeMs(rising, vMax);
|
|||
|
|
row.RiseTimeMs = CurveMetrics.RiseTimeMs(rising, vMax);
|
|||
|
|
row.RisingWaveformPath = await WaveformStore.SaveAsync(project, ch, "on", rising);
|
|||
|
|
RisingCurve = rising;
|
|||
|
|
UpdateVMax(rising, FallingCurve);
|
|||
|
|
RefreshCurveRowStatus(row);
|
|||
|
|
await _store.SaveAsync(project);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task ApplyFallingCaptureAsync(Project project, int ch, CurveData falling)
|
|||
|
|
{
|
|||
|
|
double vMax = CurveMetrics.EstimateVMax(falling.Voltage);
|
|||
|
|
var row = Rows.First(r => r.Channel == ch);
|
|||
|
|
row.OffTimeMs = CurveMetrics.OffTimeMs(falling, vMax);
|
|||
|
|
row.FallTimeMs = CurveMetrics.FallTimeMs(falling, vMax);
|
|||
|
|
row.FallingWaveformPath = await WaveformStore.SaveAsync(project, ch, "off", falling);
|
|||
|
|
|
|||
|
|
FallingCurve = falling;
|
|||
|
|
UpdateVMax(RisingCurve, falling);
|
|||
|
|
RefreshCurveRowStatus(row);
|
|||
|
|
await _store.SaveAsync(project);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void RefreshCurveRowStatus(CurveResultRow row)
|
|||
|
|
{
|
|||
|
|
bool hasRising = !string.IsNullOrWhiteSpace(row.RisingWaveformPath);
|
|||
|
|
bool hasFalling = !string.IsNullOrWhiteSpace(row.FallingWaveformPath);
|
|||
|
|
if (!hasRising && !hasFalling)
|
|||
|
|
{
|
|||
|
|
row.Status = TestStatus.Untested;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!(hasRising && hasFalling))
|
|||
|
|
{
|
|||
|
|
row.Status = TestStatus.Measured;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var th = Thresholds;
|
|||
|
|
bool ok = double.IsFinite(row.OnTimeMs)
|
|||
|
|
&& double.IsFinite(row.RiseTimeMs)
|
|||
|
|
&& double.IsFinite(row.OffTimeMs)
|
|||
|
|
&& double.IsFinite(row.FallTimeMs)
|
|||
|
|
&& !double.IsNaN(row.OnTimeMs)
|
|||
|
|
&& !double.IsNaN(row.RiseTimeMs)
|
|||
|
|
&& !double.IsNaN(row.OffTimeMs)
|
|||
|
|
&& !double.IsNaN(row.FallTimeMs)
|
|||
|
|
&& row.OnTimeMs <= th.MaxOnTimeMs
|
|||
|
|
&& row.RiseTimeMs <= th.MaxRiseTimeMs
|
|||
|
|
&& row.OffTimeMs <= th.MaxOffTimeMs
|
|||
|
|
&& row.FallTimeMs <= th.MaxFallTimeMs;
|
|||
|
|
row.Status = ok ? TestStatus.Pass : TestStatus.Fail;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateVMax(params CurveData?[] curves)
|
|||
|
|
{
|
|||
|
|
var candidates = curves.Where(x => x != null)
|
|||
|
|
.Select(x => CurveMetrics.EstimateVMax(x!.Voltage))
|
|||
|
|
.Where(x => double.IsFinite(x) && !double.IsNaN(x))
|
|||
|
|
.ToArray();
|
|||
|
|
if (candidates.Length > 0)
|
|||
|
|
VMax = candidates.Max();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task WaitForStableOnAsync(int channel, CancellationToken ct = default)
|
|||
|
|
{
|
|||
|
|
await Task.Delay(BasicTestCriteria.SwitchSettleDelayMs, ct);
|
|||
|
|
var measurement = await _sspc.ReadMeasurementAsync(channel, ct);
|
|||
|
|
if (!BasicTestCriteria.IsOn(measurement.Voltage))
|
|||
|
|
throw new InvalidOperationException($"CH{channel} 未能稳定开启,当前电压 {measurement.Voltage:F3} V。");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool ValidateCurveCaptureConfig(out string message)
|
|||
|
|
{
|
|||
|
|
if (_daqOpts.UseMock)
|
|||
|
|
{
|
|||
|
|
message = "Mock 模式,曲线采集配置有效。";
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (_daqOpts.ChannelCount is not (1 or 2 or 4 or 8))
|
|||
|
|
{
|
|||
|
|
message = "DAQ 启用通道数只能为 1、2、4 或 8。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var enabled = _daqOpts.Channels.Where(x => x.PhysicalChannel < _daqOpts.ChannelCount).ToArray();
|
|||
|
|
if (enabled.Length == 0)
|
|||
|
|
{
|
|||
|
|
message = "DAQ 没有可用的启用通道。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var outputVoltage = enabled.Where(x => string.Equals(x.Role, nameof(DaqChannelRole.OutputVoltage), StringComparison.OrdinalIgnoreCase)).ToArray();
|
|||
|
|
if (outputVoltage.Length != 1)
|
|||
|
|
{
|
|||
|
|
message = $"曲线测试必须配置且只能配置一个 OutputVoltage 角色,当前配置了 {outputVoltage.Length} 个。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var voltageChannel = outputVoltage[0];
|
|||
|
|
if (!voltageChannel.IsCalibrated)
|
|||
|
|
{
|
|||
|
|
message = $"CH{voltageChannel.PhysicalChannel} OutputVoltage 尚未勾选已标定。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (!double.IsFinite(voltageChannel.Scale) || voltageChannel.Scale == 0)
|
|||
|
|
{
|
|||
|
|
message = $"CH{voltageChannel.PhysicalChannel} 输出电压通道 Scale 无效。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (!double.IsFinite(voltageChannel.Offset))
|
|||
|
|
{
|
|||
|
|
message = $"CH{voltageChannel.PhysicalChannel} 输出电压通道 Offset 无效。";
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message = "曲线采集配置有效。";
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|