示波器修改:visa连接方式+截图保存
This commit is contained in:
@@ -124,6 +124,11 @@ namespace UIShare.GlobalVariable
|
||||
{
|
||||
alive = await ProbeTcpDeviceAsync(tcpDevice);
|
||||
}
|
||||
// VISA 设备同理:IsConnected 仅表示会话对象存在,主动探活以检测死会话
|
||||
else if (alive && device is Visa visaDevice)
|
||||
{
|
||||
alive = await ProbeVisaDeviceAsync(visaDevice);
|
||||
}
|
||||
|
||||
if (alive)
|
||||
{
|
||||
@@ -176,6 +181,26 @@ namespace UIShare.GlobalVariable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对 VISA 设备执行轻量级主动探活(SCPI *IDN?)。
|
||||
/// <para>通过 WriteReadAsync 内部获取 _commLock,与监控采样天然串行化;
|
||||
/// 死会话将在 VISA 自身的 IOTimeout 内抛出异常,返回 false 触发重连。</para>
|
||||
/// </summary>
|
||||
/// <returns>true: 探活成功(会话确实存活); false: 探活失败(死会话)</returns>
|
||||
private async Task<bool> ProbeVisaDeviceAsync(Visa visaDevice)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var probeCts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||
string resp = await visaDevice.WriteReadAsync("*IDN?\n", "\n", probeCts.Token);
|
||||
return !string.IsNullOrWhiteSpace(resp);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重连单个设备。
|
||||
/// <para>
|
||||
|
||||
@@ -68,6 +68,12 @@ namespace UIShare.GlobalVariable
|
||||
return $"Tcp:{config.TcpConfig.IPAddress}:{config.TcpConfig.Port}";
|
||||
}
|
||||
|
||||
if (string.Equals(config.ConnectionType, "Visa", StringComparison.OrdinalIgnoreCase)
|
||||
&& config.VisaConfig != null)
|
||||
{
|
||||
return $"Visa:{config.VisaConfig.ResourceName}";
|
||||
}
|
||||
|
||||
if (string.Equals(config.ConnectionType, "Serial", StringComparison.OrdinalIgnoreCase)
|
||||
&& config.SerialPortConfig != null)
|
||||
{
|
||||
@@ -129,6 +135,7 @@ namespace UIShare.GlobalVariable
|
||||
return config.ConnectionType switch
|
||||
{
|
||||
"Tcp" => CreateTcpDevice(deviceType, config.TcpConfig)!,
|
||||
"Visa" => CreateVisaDevice(deviceType, config.VisaConfig)!,
|
||||
"Serial" => CreateSerialDevice(deviceType, config.SerialPortConfig)!,
|
||||
_ => null!
|
||||
};
|
||||
@@ -379,6 +386,7 @@ namespace UIShare.GlobalVariable
|
||||
bool ok = conn switch
|
||||
{
|
||||
"Tcp" => await ConnectTcpAsync(name, device, ct),
|
||||
"Visa" => await ConnectVisaAsync(name, device, ct),
|
||||
"Serial" => await ConnectSerialAsync(name, device, ct),
|
||||
_ => false
|
||||
};
|
||||
@@ -417,6 +425,16 @@ namespace UIShare.GlobalVariable
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<bool> ConnectVisaAsync(string name, IBaseInterface device, CancellationToken ct)
|
||||
{
|
||||
if (device is IVisa visa)
|
||||
{
|
||||
return await visa.ConnectAsync(ct);
|
||||
}
|
||||
LoggerHelper.Warn($"设备 [{name}] 配置为 Visa 但未实现 IVisa,实际类型为 {device.GetType().Name}。");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<bool> ConnectSerialAsync(string name, IBaseInterface device, CancellationToken ct)
|
||||
{
|
||||
if (device is not ISerialPort sp)
|
||||
@@ -533,7 +551,34 @@ namespace UIShare.GlobalVariable
|
||||
SendTimeout = vm.SendTimeout,
|
||||
ReceiveTimeout = vm.ReceiveTimeout
|
||||
};
|
||||
return Activator.CreateInstance(type, cfg) as IBaseInterface;
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type, cfg) as IBaseInterface;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Warn($"实例化 Tcp 设备 [{type.Name}] 失败(连接配置与设备构造函数不匹配?):{ex.InnerException?.Message ?? ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static IBaseInterface? CreateVisaDevice(Type type, VisaConfigVM? vm)
|
||||
{
|
||||
vm ??= new VisaConfigVM();
|
||||
var cfg = new VisaConfig
|
||||
{
|
||||
ResourceName = vm.ResourceName,
|
||||
OpenTimeout = vm.OpenTimeout,
|
||||
IOTimeout = vm.IOTimeout
|
||||
};
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type, cfg) as IBaseInterface;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Warn($"实例化 Visa 设备 [{type.Name}] 失败(连接配置与设备构造函数不匹配?):{ex.InnerException?.Message ?? ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static IBaseInterface? CreateSerialDevice(Type type, SerialPortConfigVM? vm)
|
||||
{
|
||||
@@ -548,7 +593,15 @@ namespace UIShare.GlobalVariable
|
||||
ReadTimeout = vm.ReadTimeout,
|
||||
WriteTimeout = vm.WriteTimeout
|
||||
};
|
||||
return Activator.CreateInstance(type, cfg) as IBaseInterface;
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type, cfg) as IBaseInterface;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Warn($"实例化 Serial 设备 [{type.Name}] 失败(连接配置与设备构造函数不匹配?):{ex.InnerException?.Message ?? ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -61,6 +61,14 @@ namespace UIShare.UIViewModel
|
||||
set => SetProperty(ref _tcpConfig, value);
|
||||
}
|
||||
|
||||
/// <summary>VISA 连接参数(首次访问时自动初始化,便于 XAML 直接绑定)。</summary>
|
||||
private VisaConfigVM _visaConfig = new();
|
||||
public VisaConfigVM VisaConfig
|
||||
{
|
||||
get => _visaConfig;
|
||||
set => SetProperty(ref _visaConfig, value);
|
||||
}
|
||||
|
||||
/// <summary>串口连接参数(首次访问时自动初始化,便于 XAML 直接绑定)。</summary>
|
||||
private SerialPortConfigVM _serialPortConfig = new();
|
||||
public SerialPortConfigVM SerialPortConfig
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using Prism.Mvvm;
|
||||
|
||||
namespace UIShare.UIViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// VISA 连接配置(与 DeviceCommand.Base.Visa 保持字段一致)。
|
||||
/// <para>VISA 通过统一资源字符串寻址,无需区分 IP/端口,例如
|
||||
/// TCPIP0::192.168.20.125::inst0::INSTR、USB0::0x0699::0x0408::C012345::INSTR。</para>
|
||||
/// </summary>
|
||||
public class VisaConfigVM : BindableBase
|
||||
{
|
||||
private string _resourceName = "TCPIP0::127.0.0.1::inst0::INSTR";
|
||||
/// <summary>VISA 资源字符串(TCPIP / USB / GPIB / ASRL 等统一寻址)。</summary>
|
||||
public string ResourceName
|
||||
{
|
||||
get => _resourceName;
|
||||
set => SetProperty(ref _resourceName, value);
|
||||
}
|
||||
|
||||
private int _openTimeout = 3000;
|
||||
/// <summary>打开会话超时(毫秒)。</summary>
|
||||
public int OpenTimeout
|
||||
{
|
||||
get => _openTimeout;
|
||||
set => SetProperty(ref _openTimeout, value);
|
||||
}
|
||||
|
||||
private int _ioTimeout = 3000;
|
||||
/// <summary>读写 I/O 超时(毫秒)。</summary>
|
||||
public int IOTimeout
|
||||
{
|
||||
get => _ioTimeout;
|
||||
set => SetProperty(ref _ioTimeout, value);
|
||||
}
|
||||
|
||||
public VisaConfigVM() { }
|
||||
|
||||
/// <summary>拷贝构造,用于对话框编辑副本。</summary>
|
||||
public VisaConfigVM(VisaConfigVM? src)
|
||||
{
|
||||
if (src == null) return;
|
||||
ResourceName = src.ResourceName;
|
||||
OpenTimeout = src.OpenTimeout;
|
||||
IOTimeout = src.IOTimeout;
|
||||
}
|
||||
|
||||
/// <summary>把字段拷回目标对象(保存时用)。</summary>
|
||||
public void CopyTo(VisaConfigVM? dst)
|
||||
{
|
||||
if (dst == null) return;
|
||||
dst.ResourceName = ResourceName;
|
||||
dst.OpenTimeout = OpenTimeout;
|
||||
dst.IOTimeout = IOTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user