BDU/DeviceCommand/Device/RTM3004.cs

810 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Common.Attributes;
using DeviceCommand.Base;
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows.Shapes;
using static Common.Attributes.ATSCommandAttribute;
using static System.Formats.Asn1.AsnWriter;
namespace DeviceCommand.Device
{
/// <summary>
/// 示波器型号RTM3004
/// </summary>
[ATSCommand]
[DeviceCategory("示波器")]
public class RTM3004 : Tcp
{
/// <summary>
/// 信号量锁
/// </summary>
public SemaphoreSlim semaphoreSlimLock { get; set; } = new(1, 1);
#region
/// <summary>
/// 单个通道名称枚举
/// </summary>
public enum ChannelName_Single_Enum
{
/// <summary>
/// 通道1
/// </summary>
CH1 = 1,
/// <summary>
/// 通道2
/// </summary>
CH2 = 2,
/// <summary>
/// 通道3
/// </summary>
CH3 = 3,
/// <summary>
/// 通道4
/// </summary>
CH4 = 4
}
/// <summary>
/// 测量项枚举RTM3004支持8个独立的测量项
/// </summary>
public enum Measurement_Enum
{
/// <summary>测量项1</summary>
MEAS1 = 1,
/// <summary>测量项2</summary>
MEAS2 = 2,
/// <summary>测量项3</summary>
MEAS3 = 3,
/// <summary>测量项4</summary>
MEAS4 = 4,
/// <summary>测量项5</summary>
MEAS5 = 5,
/// <summary>测量项6</summary>
MEAS6 = 6,
/// <summary>测量项7</summary>
MEAS7 = 7,
/// <summary>测量项8</summary>
MEAS8 = 8
}
/// <summary>
/// 通道耦合模式枚举
/// </summary>
public enum CouplingMode
{
/// <summary>直流限制耦合</summary>
DCLimit,
/// <summary>交流限制耦合</summary>
ACLimit,
/// <summary>接地耦合,用于参考电平</summary>
GND,
/// <summary>直流耦合</summary>
DC
}
/// <summary>
/// 采集状态(手册 ACQuire:STATE 支持值)
/// </summary>
public enum AcquisitionState
{
/// <summary>运行中,持续采集波形</summary>
RUN,
/// <summary>采集完成后停止采集</summary>
STOPping,
/// <summary>采集完成(设置:不可用)</summary>
COMPlete,
/// <summary>立即中断当前采集</summary>
BREak
}
/// <summary>
/// 带宽限制(手册 CH<x>:BANdwidth 支持值)
/// </summary>
public enum BandwidthLimit
{
/// <summary>使用全部带宽,无限制</summary>
FULL,
/// <summary>频率限制为20 MHz高频信号被移除以降低噪声</summary>
B20
}
/// <summary>
/// 触发模式(手册 TRIGger:A:MODe 支持值)
/// </summary>
public enum TriggerMode
{
/// <summary>自动模式,无触发时自动扫描</summary>
AUTO,
/// <summary>正常模式,仅在触发时采集</summary>
NORMal,
}
/// <summary>
/// 触发斜率/边沿枚举
/// </summary>
public enum Slope_Enum
{
/// <summary>上升沿触发,检测电压正向变化</summary>
POSitive,
/// <summary>下降沿触发,检测电压负向变化</summary>
NEGative,
/// <summary>上升和下降沿都触发</summary>
EITHer
}
/// <summary>
/// 参考电平百分比模式枚举
/// 用于设置上升/下降时间测量的上下参考电平
/// </summary>
public enum Relative_Mode
{
/// <summary>10%、50%和90%参考电平</summary>
TEN,
/// <summary>20%、50%和80%参考电平</summary>
TWENty,
/// <summary>5%、50%和95%参考电平</summary>
FIVE,
/// <summary>用户自定义参考电平需配合REFLevel参数使用</summary>
USER
}
#endregion
#region
//罗德与施瓦茨RTM3000示波器没有专门的"切换本地/远程"SCPI命令,自动切换
/// <summary>
/// 指定电源应用程序的电流源
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_CurrentSource(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"POWer:SOURce:CURRent {channelName}\n", ct); // 手册 POWer 命令规范
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 指定电源应用程序的电压源
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_VoltageSource(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"POWer:SOURce:VOLTage {channelName}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 用于设置上升/下降时间测量的上下参考电平
/// </summary>
/// <param name="client">设备</param>
/// <param name="relativemode">参考电平的百分比</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Stop_QuickMeasurement(Tcp client, Relative_Mode relativemode, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"REFLevel:RELative:MODE{relativemode}:AOFF\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 启动快速测量(AUTO/NORMal)
/// </summary>
/// <param name="client">设备</param>
/// <param name="measurement">测试项</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Start_QuickMeasurement(Tcp client, Measurement_Enum measurement, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"MEASurement{measurement}:AON\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 停止快速测量
/// </summary>
/// <param name="client">设备</param>
/// <param name="measurement">测试项</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Stop_QuickMeasurement(Tcp client, Measurement_Enum measurement, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"MEASurement{measurement}:AOFF\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 启动自动测量(AUTO/NORMal)
/// </summary>
/// <param name="client">设备</param>
/// <param name="measurement">测试项</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Start_AutoMeasurement(Tcp client, Measurement_Enum measurement, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"MEASurement{measurement}:ON\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 停止自动测量
/// </summary>
/// <param name="client">设备</param>
/// <param name="measurement">测试项</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Stop_AutoMeasurement(Tcp client, Measurement_Enum measurement, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"MEASurement{measurement}:OFF\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 指定测量的通道
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道</param>
/// <param name="measurement">测试项</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_Measurement_Source(Tcp client, ChannelName_Single_Enum channelName, Measurement_Enum measurement, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"MEASurement{measurement}:SOURce CH{channelName}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 指定A触发的通道 (AUTO/NORMal)
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ATrigger_SOURce(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"TRIGger:A:SOURce CH{channelName}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置A触发的模式 (AUTO/NORMal)
/// </summary>
/// <param name="client">设备</param>
/// <param name="mode">触发模式</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ATrigger_Mode(Tcp client, TriggerMode mode, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string modeStr = mode.ToString().ToUpper();
await SendAsync(client, $"TRIGger:A:MODE {modeStr}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置A触发的触发斜率(沿)(AUTO/NORMal)
/// </summary>
/// <param name="client">设备</param>
/// <param name="slope">触发斜率(沿)</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ATrigger_Slope(Tcp client, Slope_Enum slope, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string modeStr = slope.ToString().ToUpper();
await SendAsync(client, $"TRIGger:A:EDGE:SLOPe {slope}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置A触发的阈值电压(取决于垂直标度)
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道</param>
/// <param name="level">阈值电压/param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ATrigger_Level(Tcp client, ChannelName_Single_Enum channelName, float level, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string modeStr = level.ToString().ToUpper();
await SendAsync(client, $"TRIGger:A:LEVel {channelName} {level}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置指定通道的输入耦合模式
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="couplingMode">耦合模式</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelCoupling(Tcp client, ChannelName_Single_Enum channelName, CouplingMode couplingMode, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string modeStr = couplingMode.ToString().ToUpper();
await SendAsync(client, $"CHANnel {channelName}:COUPling{couplingMode}\n", ct); // 直接使用通道枚举值(如 CHANnel 1:COUPLING AC
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置指定通道的输入耦合为DCLimit
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelCoupling_DCLimit(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await Set_ChannelCoupling(client, channelName, CouplingMode.DCLimit, ct);
}
/// <summary>
/// 设置指定通道的输入耦合为ACLimit
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelCoupling_ACLimit(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await Set_ChannelCoupling(client, channelName, CouplingMode.ACLimit, ct);
}
/// <summary>
/// 设置指定通道的输入耦合为GND
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelCoupling_GND(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await Set_ChannelCoupling(client, channelName, CouplingMode.GND, ct);
}
/// <summary>
/// 设置指定通道的输入耦合为DC
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelCoupling_DC(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await Set_ChannelCoupling(client, channelName, CouplingMode.DC, ct);
}
/// <summary>
/// 设置指定通道的硬件带宽限制
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="bandwidth">带宽限制</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelBandwidth(Tcp client, ChannelName_Single_Enum channelName, BandwidthLimit bandwidth, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string bwStr = $"{(int)bandwidth}E6";
await SendAsync(client, $"CHANnel{channelName}:BANDwidth{bwStr}\n", ct); // 如 CHANnel 1:BANDwidth FULL
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 启用指定通道
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Start_Channel(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"CHANnel:{channelName}:STATe ON\n", ct); // 手册标准通道启用命令
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 关闭指定通道
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Stop_Channel(Tcp client, ChannelName_Single_Enum channelName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"CHANnel:{channelName}:STATe OFF\n", ct); // 手册标准通道启用命令
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置指定通道的垂直标度 (Volts/Div)
/// </summary>
/// <param name="client">设备</param>
/// <param name="channelName">通道名称</param>
/// <param name="scale">标度值单位V/div</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_ChannelScale(Tcp client, ChannelName_Single_Enum channelName, float scale, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"CHANnel{channelName}:SCALe{scale}\n", ct); // 如 CH1:SCALE 1.0(单位 V/div
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置所有通道及数学波形的水平比例尺(每格时间,秒/格)
/// </summary>
/// <param name="client">设备</param>
/// <param name="scale">时基标度单位s/div</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_Timebase_Scale(Tcp client, double scale, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"TIMebase:SCALe {scale}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置水平触发位置 (相对于屏幕中心的偏移,百分比)
/// </summary>
/// <param name="client">设备</param>
/// <param name="position">位置百分比</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_Timebase_Position(Tcp client, double position, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"TIMebase:POSition {position}\n", ct); // 位置单位为百分比
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置示波器的采集状态 (RUN/STOP)
/// </summary>
/// <param name="client">设备</param>
/// <param name="state">采集状态</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Set_Acquisition_State(Tcp client, AcquisitionState state, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
string stateStr = state.ToString().ToUpper();
await SendAsync(client, $"ACQuire:STATe {stateStr}\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// Trigger 启动采集替代原电源ON手册无OUTPUT命令
/// </summary>
/// <param name="client">设备</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Start_Acquisition(Tcp client, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"ACQuire:STATe RUN\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 采集完停止采集
/// </summary>
/// <param name="client">设备</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Stopping_Acquisition(Tcp client, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"ACQuire:STATe STOPping\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 设置采集不可用
/// </summary>
/// <param name="client">设备</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Complete_Acquisition(Tcp client, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"ACQuire:STATe COMPlete\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 立即停止采集
/// </summary>
/// <param name="client">设备</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Break_Acquisition(Tcp client, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
await SendAsync(client, $"ACQuire:STATe BREak\n", ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 保存波形数据保存全通道到CSV文件
/// </summary>
/// <param name="client">设备</param>_当前时间.CSV
/// <param name="channelName">指定通道默认D:/BDUWAVE</param>
/// <param name="filePath">保存路径默认D:/BDUWAVE</param>
/// <param name="fileName">保存波形文件名默认WAVE_当前时间.PNG)</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Save_Waveform(Tcp client, ChannelName_Single_Enum channelName, string filePath, string fileName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
if (!Directory.Exists(filePath))
{
filePath = $"D:/WAVE";
Directory.CreateDirectory(filePath);
}
if (string.IsNullOrEmpty(fileName))
{
fileName = $"WAVE_{DateTime.Now.ToString()}";
}
await SendAsync(client, $"EXPort:WAVeform:SOURce{channelName}\n", ct); // 设置待导出的波形源
await SendAsync(client, $"EXPort:WFMSave:DESTination{filePath}\n", ct); // 设置待导出的目录
await SendAsync(client, "FORMAT CSV\n", ct); // 设置文件格式
await SendAsync(client, $"EXPort:WAVeform:NAME\"{fileName}\"\n", ct); //设置文件名字
await SendAsync(client, $"EXPort:WAVeform:SAVE\n", ct);// 执行保存操作
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 保存屏幕截图指定格式为PNG
/// </summary>
/// <param name="client">设备</param>
/// <param name="filePath">保存路径默认D:/BDUSCREEN</param>
/// <param name="fileName">保存波形文件名默认SCREEN_当前时间.PNG)</param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task Save_Screen_Image(Tcp client, string filePath, string fileName, CancellationToken ct = default)
{
await semaphoreSlimLock.WaitAsync(ct);
try
{
if (!Directory.Exists(filePath))
{
filePath = $"D:/WAVE";
Directory.CreateDirectory(filePath);
}
if (string.IsNullOrEmpty(fileName))
{
fileName = $"WAVE_{DateTime.Now.ToString()}";
}
await SendAsync(client, $"EXPort:SCRSave:DESTination{filePath}\n", ct); // 设置待导出的目录
await SendAsync(client, "FORMAT PNG\n", ct); // 设置文件格式
await SendAsync(client, $"MMEMory:NAME\"{fileName}\"\n", ct); //设置文件名字
await SendAsync(client, $"HCOPy:IMMediate\n", ct);// 执行保存操作
}
finally
{
semaphoreSlimLock.Release();
}
}
/// <summary>
/// 发送自定义SCPI命令
/// </summary>
/// <param name="client">设备</param>
/// <param name="command">SCPI命令字符串</param>
/// <param name="ct"></param>
/// <returns>命令执行结果</returns>
public async Task<string> Send_CustomCommand(Tcp client, string command, CancellationToken ct = default)
{
if (string.IsNullOrEmpty(command))
throw new ArgumentNullException(nameof(command));
await semaphoreSlimLock.WaitAsync(ct);
try
{
return await WriteRead(client, $"{command}\n", "\n", ct: ct);
}
finally
{
semaphoreSlimLock.Release();
}
}
#endregion
}
}