驱动更新

This commit is contained in:
“hsc”
2026-08-04 15:10:37 +08:00
parent 7250080a24
commit 66bb6d36ba
12 changed files with 283 additions and 1193 deletions

View File

@@ -11,12 +11,12 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 示波器 MDO/MSO/DPO 系列
/// 示波器 MSO 4/5/6 系列
/// </summary>
#region
/// <summary>
/// Tektronix MSO/MDO 采集模式
/// Tektronix MSO 采集模式
/// </summary>
public enum TekAcquisitionMode
{
@@ -33,7 +33,7 @@ namespace DeviceCommand.Devices
}
/// <summary>
/// Tektronix MSO/MDO 测量类型 (部分常用)
/// Tektronix MSO 测量类型 (部分常用)
/// </summary>
public enum TekMeasurementType
{
@@ -50,7 +50,7 @@ namespace DeviceCommand.Devices
[ACPCommand]
public class TektronixMSO : Tcp
{
// SCPI 指令结束符 (文档中提到消息终止符需使用 LF)[cite: 1]
// SCPI 指令结束符 (消息终止符需使用 LF)
private const string ScpiDelimiter = "\n";
public TektronixMSO(TcpConfig config) : base(config)
@@ -61,19 +61,19 @@ namespace DeviceCommand.Devices
public virtual async Task<string> (CancellationToken ct = default)
{
// 返回仪器的标识代码[cite: 1]
// 返回仪器的标识代码[cite: 2]
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
// 查询仪器是否处于忙碌状态,常用于同步操作[cite: 1]
// 查询仪器是否处于忙碌状态,常用于同步操作[cite: 2]
return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task (CancellationToken ct = default)
{
// 指示仪器执行信号路径校准 (SPC)[cite: 1]
// 指示仪器执行信号路径校准 (SPC)[cite: 2]
await SendAsync($"*CAL?{ScpiDelimiter}", ct);
}
@@ -86,19 +86,20 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
// 启动、停止或返回采集状态[cite: 1]
// 启动、停止或返回采集状态[cite: 2]
string = ? "RUN" : "STOP";
await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
// 启动、停止或返回采集状态[cite: 2]
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task (TekAcquisitionMode , CancellationToken ct = default)
{
// 设置或查询采集模式[cite: 1]
// 设置或查询采集模式[cite: 2]
await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct);
}
@@ -108,20 +109,20 @@ namespace DeviceCommand.Devices
public virtual async Task (double , CancellationToken ct = default)
{
// 设置或查询水平刻度 (Scale)[cite: 1]
// 设置或查询水平刻度 (Scale)[cite: 2]
string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", , ScpiDelimiter);
await SendAsync(cmd, ct);
}
public virtual async Task (long , CancellationToken ct = default)
{
// 设置或查询记录长度[cite: 1]
// 设置或查询水平记录长度[cite: 2]
await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
// 查询水平采样率。注:此命令在当前系列设备中为只读[cite: 1]
// 查询水平采样率[cite: 2]
return await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct);
}
@@ -134,9 +135,9 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task (int , bool , CancellationToken ct = default)
{
// 开启或关闭指定通道的波形显示[cite: 1]
// 4/5/6 Series MSO 标准通道显示指令[cite: 2]
string = ? "ON" : "OFF";
await SendAsync($"SELect:CH{通道号} {参数}{ScpiDelimiter}", ct);
await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct);
}
/// <summary>
@@ -144,7 +145,6 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task (int , double , CancellationToken ct = default)
{
// 设置指定通道的垂直刻度[cite: 1]
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:SCAle {1:E}{2}", , , ScpiDelimiter);
await SendAsync(cmd, ct);
}
@@ -154,7 +154,6 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task (int , double , CancellationToken ct = default)
{
// 设置指定通道的通道垂直位置[cite: 1]
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:POSition {1:F2}{2}", , , ScpiDelimiter);
await SendAsync(cmd, ct);
}
@@ -164,43 +163,43 @@ namespace DeviceCommand.Devices
#region 5. (Measurement)
/// <summary>
/// 开启指定的测量项槽位 (1-8)
/// 动态添加并开启一个测量项
/// </summary>
public virtual async Task (int , CancellationToken ct = default)
{
// 指定该测量槽位是否计算并在屏幕显示[cite: 1]
await SendAsync($"MEASUrement:MEAS{测量项编号}:STATE ON{ScpiDelimiter}", ct);
// 4/5/6 Series MSO 必须先动态创建测量项[cite: 2]
await SendAsync($"MEASUrement:ADDNew \"MEAS{}\"{ScpiDelimiter}", ct);
}
public virtual async Task (int , TekMeasurementType , CancellationToken ct = default)
{
// 设置或查询指定测量槽位的测量类型[cite: 1]
// 设置或查询测量类型[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct);
}
public virtual async Task (int , string , CancellationToken ct = default)
{
// 设置单通道测量的 "from" 源信号[cite: 1]
// 4/5/6 Series 中设置测量源[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:SOUrce1 {源名称}{ScpiDelimiter}", ct);
}
[Monitorable("测量项的当前值")]
[Monitorable("测量项的当前采集平均值")]
public virtual async Task<string> (int , CancellationToken ct = default)
{
// 返回指定的测量项当前值[cite: 1]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:VALue?{ScpiDelimiter}", ScpiDelimiter, ct);
// 4/5/6 Series 获取当前采集值的标准方式,查询平均值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task<string> (int , CancellationToken ct = default)
public virtual async Task<string> (int , CancellationToken ct = default)
{
// 返回自上次统计重置以来累积的平均值[cite: 1]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
// 获取所有采集(AllAcqs)历史累积的平均值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task<string> (int , CancellationToken ct = default)
public virtual async Task<string> (int , CancellationToken ct = default)
{
// 返回自上次统计重置以来找到的最大值[cite: 1]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
// 获取所有采集(AllAcqs)历史累积的最大值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
}
#endregion
@@ -214,28 +213,24 @@ namespace DeviceCommand.Devices
/// <param name="ct">异步取消令牌</param>
public virtual async Task (string , CancellationToken ct = default)
{
// 根据之前的排查,您的设备使用 E: 作为 U 盘挂载点
string = "E:/temp1.png";
// 1. 设置保存图像的格式为 PNG[cite: 1]
await SendAsync($"SAVE:IMAGE:FILEFormat PNG{ScpiDelimiter}", ct);
// 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)[cite: 2]
await SendAsync($"SAVe:IMAGe \"{}\"{ScpiDelimiter}", ct);
// 2. 捕获屏幕并保存到示波器的 U 盘中[cite: 1]
await SendAsync($"SAVE:IMAGE \"{}\"{ScpiDelimiter}", ct);
// 3. 发送 *OPC? 并等待返回 1确保图像文件已经完全写入磁盘[cite: 1]
// 2. 发送 *OPC? 并等待返回 1确保图像文件已经完全写入磁盘[cite: 2]
await WriteReadAsync($"*OPC?{ScpiDelimiter}", ScpiDelimiter, ct);
// 4. 将文件内容读取到当前通信接口[cite: 1]
// 3. 将文件内容读取到当前通信接口[cite: 2]
await SendAsync($"FILESystem:READFile \"{}\"{ScpiDelimiter}", ct);
// 5. 提取底层的二进制块 (Block Data)[cite: 1]
// 4. 提取底层的二进制块 (Block Data)
byte[] imageBytes = await ReadBinaryBlockAsync(ct);
// 6. 将提取的二进制字节流写入上位机本地磁盘
// 5. 将提取的二进制字节流写入上位机本地磁盘
await File.WriteAllBytesAsync(, imageBytes, ct);
// 7. 删除示波器上的临时文件,保持设备存储干净[cite: 1]
// 6. 删除示波器上的临时文件,保持设备存储干净[cite: 2]
await SendAsync($"FILESystem:DELEte \"{}\"{ScpiDelimiter}", ct);
}
@@ -258,14 +253,13 @@ namespace DeviceCommand.Devices
/// <summary>
/// 辅助方法:读取 IEEE 488.2 标准的二进制块数据 (Block Data)
/// 手册规范:<Block> ::= {#<NZDig><Dig>[<Dig>...][<DChar>...]}[cite: 1]
/// </summary>
protected virtual async Task<byte[]> ReadBinaryBlockAsync(CancellationToken ct = default)
{
var stream = GetNetworkStream();
byte[] singleByte = new byte[1];
// 1. 逐字节读取,直到读到起始符 '#'[cite: 1]
// 1. 逐字节读取,直到读到起始符 '#'
while (true)
{
int read = await stream.ReadAsync(singleByte, 0, 1, ct);
@@ -273,13 +267,13 @@ namespace DeviceCommand.Devices
if (singleByte[0] == (byte)'#') break;
}
// 2. 读取 1 个字符 <NZDig>,它代表接下来有多少个字符表示数据长度[cite: 1]
// 2. 读取 1 个字符 <NZDig>,它代表接下来有多少个字符表示数据长度
await stream.ReadAsync(singleByte, 0, 1, ct);
char nzDigChar = (char)singleByte[0];
if (!char.IsDigit(nzDigChar)) throw new InvalidDataException($"无效的块头标识: {nzDigChar}");
int lengthBytesCount = int.Parse(nzDigChar.ToString());
// 3. 读取 <NZDig> 所指示的长度字符,将其解析为整数 N[cite: 1]
// 3. 读取 <NZDig> 所指示的长度字符,将其解析为整数 N
byte[] lengthBytes = new byte[lengthBytesCount];
int currentRead = 0;
while (currentRead < lengthBytesCount)
@@ -290,7 +284,7 @@ namespace DeviceCommand.Devices
}
int dataLength = int.Parse(Encoding.ASCII.GetString(lengthBytes));
// 4. 从流中精确读取 N 个字节 <DChar>...,这就是完整的图像数据[cite: 1]
// 4. 从流中精确读取 N 个字节 <DChar>...,这就是完整的图像数据
byte[] imageData = new byte[dataLength];
int totalBytesRead = 0;
while (totalBytesRead < dataLength)
@@ -300,7 +294,7 @@ namespace DeviceCommand.Devices
totalBytesRead += read;
}
// 5. 结尾可能包含 <EOM> (例如 LF \n),将其读取并丢弃以清空缓冲区[cite: 1]
// 5. 结尾可能包含 <EOM> (例如 LF \n),将其读取并丢弃以清空缓冲区
try
{
await stream.ReadAsync(singleByte, 0, 1, ct);
@@ -319,7 +313,7 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task Async(CancellationToken ct = default)
{
// 发送 *CLS 指令清空状态[cite: 1]
// 发送 *CLS 指令清空状态[cite: 2]
await SendAsync($"*CLS{ScpiDelimiter}", ct);
}
@@ -328,7 +322,7 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回事件队列中的事件数量[cite: 1]
// 返回事件队列中的事件数量
return await WriteReadAsync($"EVQty?{ScpiDelimiter}", ScpiDelimiter, ct);
}
@@ -337,7 +331,7 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回事件队列中的事件代码和消息[cite: 1]
// 返回事件队列中的事件代码和消息
return await WriteReadAsync($"EVMsg?{ScpiDelimiter}", ScpiDelimiter, ct);
}
@@ -346,7 +340,7 @@ namespace DeviceCommand.Devices
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回所有事件及其消息[cite: 1]
// 返回所有事件及其消息
return await WriteReadAsync($"ALLEv?{ScpiDelimiter}", ScpiDelimiter, ct);
}