命令树优化,测试编辑区域优化
This commit is contained in:
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -37,22 +38,48 @@ namespace DeviceCommand.Devices
|
||||
/// </summary>
|
||||
public enum TekMeasurementType
|
||||
{
|
||||
/// <summary>幅度</summary>
|
||||
AMPLitude,
|
||||
/// <summary>频率</summary>
|
||||
FREQuency,
|
||||
/// <summary>平均值</summary>
|
||||
MEAN,
|
||||
/// <summary>峰峰值</summary>
|
||||
PK2PK,
|
||||
/// <summary>最大值</summary>
|
||||
MAXimum,
|
||||
/// <summary>最小值</summary>
|
||||
MINimum
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Tektronix 示波器 (MSO 4/5/6 系列),支持采集控制、通道设置、自动测量与截图导出
|
||||
/// </summary>
|
||||
[ACPCommand]
|
||||
public class TektronixMSO : Tcp
|
||||
{
|
||||
// SCPI 指令结束符 (消息终止符需使用 LF)
|
||||
private const string ScpiDelimiter = "\n";
|
||||
|
||||
/// <summary>
|
||||
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
|
||||
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
|
||||
/// </summary>
|
||||
/// <param name="raw">设备返回的原始字符串</param>
|
||||
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
|
||||
private static double ExtractDouble(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return 0;
|
||||
|
||||
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
|
||||
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
|
||||
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
|
||||
? Math.Round(val, 2)
|
||||
: 0;
|
||||
}
|
||||
|
||||
#region 兼容旧 MSO44B 枚举(供 ACP 测试项反序列化)
|
||||
|
||||
public enum AcquireMode { Sample = 0, PeakDetect = 1, HiRes = 2, Average = 3, Envelope = 4 }
|
||||
@@ -81,21 +108,32 @@ namespace DeviceCommand.Devices
|
||||
|
||||
#region 1. 公共命令与状态查询
|
||||
|
||||
/// <summary>
|
||||
/// 查询仪器识别码 (*IDN? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>设备标识字符串 (制造商, 型号, 序列号, 固件版本)</returns>
|
||||
public virtual async Task<string> 查询设备标识(CancellationToken ct = default)
|
||||
{
|
||||
// 返回仪器的标识代码[cite: 2]
|
||||
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询仪器是否处于忙碌状态,常用于同步操作 (BUSY? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>忙碌状态字符串 (0: 空闲, 1: 忙碌)</returns>
|
||||
public virtual async Task<string> 查询忙碌状态(CancellationToken ct = default)
|
||||
{
|
||||
// 查询仪器是否处于忙碌状态,常用于同步操作[cite: 2]
|
||||
return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指示仪器执行信号路径校准 (SPC) (*CAL? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 执行自校准(CancellationToken ct = default)
|
||||
{
|
||||
// 指示仪器执行信号路径校准 (SPC)[cite: 2]
|
||||
await SendAsync($"*CAL?{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
@@ -104,24 +142,33 @@ namespace DeviceCommand.Devices
|
||||
#region 2. 采集系统控制 (Acquisition)
|
||||
|
||||
/// <summary>
|
||||
/// 启动或停止采集
|
||||
/// 启动或停止采集 (ACQuire:STATE 指令)
|
||||
/// </summary>
|
||||
/// <param name="运行">true: 启动采集 (RUN), false: 停止采集 (STOP)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置采集状态(bool 运行, CancellationToken ct = default)
|
||||
{
|
||||
// 启动、停止或返回采集状态[cite: 2]
|
||||
string 参数 = 运行 ? "RUN" : "STOP";
|
||||
await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询当前采集状态 (ACQuire:STATE? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>采集状态字符串 (RUN / STOP)</returns>
|
||||
public virtual async Task<string> 查询采集状态(CancellationToken ct = default)
|
||||
{
|
||||
// 启动、停止或返回采集状态[cite: 2]
|
||||
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置采集模式 (ACQuire:MODe 指令)
|
||||
/// </summary>
|
||||
/// <param name="模式">采集模式枚举 (采样/峰值检测/高分辨率/平均/包络)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置采集模式(TekAcquisitionMode 模式, CancellationToken ct = default)
|
||||
{
|
||||
// 设置或查询采集模式[cite: 2]
|
||||
await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
@@ -129,23 +176,36 @@ namespace DeviceCommand.Devices
|
||||
|
||||
#region 3. 水平系统控制 (Horizontal)
|
||||
|
||||
/// <summary>
|
||||
/// 设置水平刻度 (Scale),即每格代表的时间 (HORizontal:SCAle 指令)
|
||||
/// </summary>
|
||||
/// <param name="秒每格">水平刻度值 (单位: s/Div)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置水平刻度(double 秒每格, CancellationToken ct = default)
|
||||
{
|
||||
// 设置或查询水平刻度 (Scale)[cite: 2]
|
||||
string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", 秒每格, ScpiDelimiter);
|
||||
await SendAsync(cmd, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置水平记录长度 (HORizontal:RECOrdlength 指令)
|
||||
/// </summary>
|
||||
/// <param name="长度">记录长度(采样点数)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置记录长度(long 长度, CancellationToken ct = default)
|
||||
{
|
||||
// 设置或查询水平记录长度[cite: 2]
|
||||
await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
public virtual async Task<string> 查询采样率(CancellationToken ct = default)
|
||||
/// <summary>
|
||||
/// 查询水平采样率 (HORizontal:SAMPLERate? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>水平采样率 (单位: Sa/s,保留两位小数)</returns>
|
||||
public virtual async Task<double> 查询采样率(CancellationToken ct = default)
|
||||
{
|
||||
// 查询水平采样率[cite: 2]
|
||||
return await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
string resp = await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
return ExtractDouble(resp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -153,18 +213,23 @@ namespace DeviceCommand.Devices
|
||||
#region 4. 通道与显示控制 (Vertical)
|
||||
|
||||
/// <summary>
|
||||
/// 全局启用或关闭某通道的显示
|
||||
/// 全局启用或关闭某通道的显示 (DISplay:GLObal:CH:STATE 指令)
|
||||
/// </summary>
|
||||
/// <param name="通道号">通道编号 (1 - 4)</param>
|
||||
/// <param name="开启">true: 开启通道显示, false: 关闭通道显示</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置通道显示开关(int 通道号, bool 开启, CancellationToken ct = default)
|
||||
{
|
||||
// 4/5/6 Series MSO 标准通道显示指令[cite: 2]
|
||||
string 参数 = 开启 ? "ON" : "OFF";
|
||||
await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定通道的垂直缩放比例 (Volts/Div)
|
||||
/// 设置指定通道的垂直缩放比例 (CH:SCAle 指令)
|
||||
/// </summary>
|
||||
/// <param name="通道号">通道编号 (1 - 4)</param>
|
||||
/// <param name="伏特每格">垂直刻度值 (单位: V/Div)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置通道垂直刻度(int 通道号, double 伏特每格, CancellationToken ct = default)
|
||||
{
|
||||
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:SCAle {1:E}{2}", 通道号, 伏特每格, ScpiDelimiter);
|
||||
@@ -172,8 +237,11 @@ namespace DeviceCommand.Devices
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定通道的垂直位置 (Divisions)
|
||||
/// 设置指定通道的垂直位置 (CH:POSition 指令)
|
||||
/// </summary>
|
||||
/// <param name="通道号">通道编号 (1 - 4)</param>
|
||||
/// <param name="格数">垂直位置偏移 (单位: Div)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置通道垂直位置(int 通道号, double 格数, CancellationToken ct = default)
|
||||
{
|
||||
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:POSition {1:F2}{2}", 通道号, 格数, ScpiDelimiter);
|
||||
@@ -185,43 +253,73 @@ namespace DeviceCommand.Devices
|
||||
#region 5. 自动测量 (Measurement)
|
||||
|
||||
/// <summary>
|
||||
/// 动态添加并开启一个测量项
|
||||
/// 动态添加并开启一个测量项 (MEASUrement:ADDNew 指令)。
|
||||
/// 4/5/6 Series MSO 必须先动态创建测量项
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 开启测量项(int 测量项编号, CancellationToken ct = default)
|
||||
{
|
||||
// 4/5/6 Series MSO 必须先动态创建测量项[cite: 2]
|
||||
await SendAsync($"MEASUrement:ADDNew \"MEAS{测量项编号}\"{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置测量项的测量类型 (MEASUrement:TYPe 指令)
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="类型">测量类型枚举 (幅度/频率/平均值/峰峰值等)</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置测量类型(int 测量项编号, TekMeasurementType 类型, CancellationToken ct = default)
|
||||
{
|
||||
// 设置或查询测量类型[cite: 2]
|
||||
await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置测量项的测量源 (MEASUrement:SOUrce1 指令)
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="源名称">测量源名称 (如 "CH1", "CH2")</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 设置测量源(int 测量项编号, string 源名称, CancellationToken ct = default)
|
||||
{
|
||||
// 4/5/6 Series 中设置测量源[cite: 2]
|
||||
await SendAsync($"MEASUrement:MEAS{测量项编号}:SOUrce1 {源名称}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询测量项的当前采集平均值 (MEASUrement:RESUlts:CURRentacq:MEAN? 指令)
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>测量项当前采集的平均值 (保留两位小数)</returns>
|
||||
[Monitorable("测量项的当前采集平均值")]
|
||||
public virtual async Task<string> 查询测量项当前值(int 测量项编号, CancellationToken ct = default)
|
||||
public virtual async Task<double> 查询测量项当前值(int 测量项编号, CancellationToken ct = default)
|
||||
{
|
||||
// 4/5/6 Series 获取当前采集值的标准方式,查询平均值[cite: 2]
|
||||
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
return ExtractDouble(resp);
|
||||
}
|
||||
|
||||
public virtual async Task<string> 查询测量项全局平均值(int 测量项编号, CancellationToken ct = default)
|
||||
/// <summary>
|
||||
/// 查询测量项的全局历史累积平均值 (MEASUrement:RESUlts:ALLAcqs:MEAN? 指令)
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>测量项全局平均值 (保留两位小数)</returns>
|
||||
public virtual async Task<double> 查询测量项全局平均值(int 测量项编号, CancellationToken ct = default)
|
||||
{
|
||||
// 获取所有采集(AllAcqs)历史累积的平均值[cite: 2]
|
||||
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
return ExtractDouble(resp);
|
||||
}
|
||||
|
||||
public virtual async Task<string> 查询测量项全局最大值(int 测量项编号, CancellationToken ct = default)
|
||||
/// <summary>
|
||||
/// 查询测量项的全局历史累积最大值 (MEASUrement:RESUlts:ALLAcqs:MAXimum? 指令)
|
||||
/// </summary>
|
||||
/// <param name="测量项编号">测量项编号</param>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>测量项全局最大值 (保留两位小数)</returns>
|
||||
public virtual async Task<double> 查询测量项全局最大值(int 测量项编号, CancellationToken ct = default)
|
||||
{
|
||||
// 获取所有采集(AllAcqs)历史累积的最大值[cite: 2]
|
||||
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
return ExtractDouble(resp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -237,13 +335,13 @@ namespace DeviceCommand.Devices
|
||||
{
|
||||
string 临时文件 = "E:/temp1.png";
|
||||
|
||||
// 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)[cite: 2]
|
||||
// 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)
|
||||
await SendAsync($"SAVe:IMAGe \"{临时文件}\"{ScpiDelimiter}", ct);
|
||||
|
||||
// 2. 发送 *OPC? 并等待返回 1,确保图像文件已经完全写入磁盘[cite: 2]
|
||||
// 2. 发送 *OPC? 并等待返回 1,确保图像文件已经完全写入磁盘
|
||||
await WriteReadAsync($"*OPC?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
|
||||
// 3. 将文件内容读取到当前通信接口[cite: 2]
|
||||
// 3. 将文件内容读取到当前通信接口
|
||||
await SendAsync($"FILESystem:READFile \"{临时文件}\"{ScpiDelimiter}", ct);
|
||||
|
||||
// 4. 提取底层的二进制块 (Block Data)
|
||||
@@ -252,7 +350,7 @@ namespace DeviceCommand.Devices
|
||||
// 5. 将提取的二进制字节流写入上位机本地磁盘
|
||||
await File.WriteAllBytesAsync(上位机文件路径, imageBytes, ct);
|
||||
|
||||
// 6. 删除示波器上的临时文件,保持设备存储干净[cite: 2]
|
||||
// 6. 删除示波器上的临时文件,保持设备存储干净
|
||||
await SendAsync($"FILESystem:DELEte \"{临时文件}\"{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
@@ -331,41 +429,44 @@ namespace DeviceCommand.Devices
|
||||
#region 7. 状态与错误队列 (Status and Error)
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有的状态寄存器和错误/事件队列
|
||||
/// 清除所有的状态寄存器和错误/事件队列 (*CLS 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
public virtual async Task 清除错误队列Async(CancellationToken ct = default)
|
||||
{
|
||||
// 发送 *CLS 指令清空状态[cite: 2]
|
||||
await SendAsync($"*CLS{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询错误队列中当前的事件/错误数量
|
||||
/// 查询错误队列中当前的事件/错误数量 (EVQty? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>事件队列中的事件数量字符串</returns>
|
||||
public virtual async Task<string> 查询错误数量Async(CancellationToken ct = default)
|
||||
{
|
||||
// 返回事件队列中的事件数量
|
||||
return await WriteReadAsync($"EVQty?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从错误队列中读取最早的一个事件/错误(包含代码和详细消息),并将其从队列中弹出
|
||||
/// 从错误队列中读取最早的一个事件/错误(包含代码和详细消息),并将其从队列中弹出 (EVMsg? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>事件代码和消息字符串</returns>
|
||||
public virtual async Task<string> 读取单条错误消息Async(CancellationToken ct = default)
|
||||
{
|
||||
// 返回事件队列中的事件代码和消息
|
||||
return await WriteReadAsync($"EVMsg?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取并清空队列中的所有错误和事件消息
|
||||
/// 读取并清空队列中的所有错误和事件消息 (ALLEv? 指令)
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>所有事件及其消息字符串</returns>
|
||||
public virtual async Task<string> 读取所有错误消息Async(CancellationToken ct = default)
|
||||
{
|
||||
// 返回所有事件及其消息
|
||||
return await WriteReadAsync($"ALLEv?{ScpiDelimiter}", ScpiDelimiter, ct);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user