diff --git a/DeviceCommand/Devices/Chroma61800.cs b/DeviceCommand/Devices/Chroma61800.cs new file mode 100644 index 0000000..95d697e --- /dev/null +++ b/DeviceCommand/Devices/Chroma61800.cs @@ -0,0 +1,166 @@ +using Common.Attributes; +using DeviceCommand.Base; +using Model.Models; +using System; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace DeviceCommand.Devices +{ + [ACPCommand] + public class Chroma61800 : Tcp + { + // Chroma SCPI 默认使用 \n 作为结束符 + private const string ScpiDelimiter = "\n"; + + public Chroma61800(TcpConfig config) : base(config) + { + } + + #region 1. 基础系统控制 + + public virtual async Task 清除错误队列(CancellationToken ct = default) + { + await SendAsync($"*CLS{ScpiDelimiter}", ct); + } + + #endregion + + #region 2. LIST 模式初始化 + + /// + /// 初始化 LIST 模式的三相独立编辑环境 + /// + /// 循环次数,0代表无限循环 + public virtual async Task 初始化三相List模式(int loopCount = 0, CancellationToken ct = default) + { + await SendAsync($"INST:PHAS THRE{ScpiDelimiter}", ct); // 选择三相模式 + await SendAsync($"INST:EDIT EACH{ScpiDelimiter}", ct); // 选择编辑电压方式为分别编辑 + await SendAsync($"LIST:COUN {loopCount}{ScpiDelimiter}", ct); // 循环次数设定 + await SendAsync($"LIST:TRIG AUTO{ScpiDelimiter}", ct); // 设定触发方式 + await SendAsync($"LIST:BASE TIME{ScpiDelimiter}", ct); // 选择执行时间类别 + } + + #endregion + + #region 3. LIST 步阶参数配置 + + /// + /// 配置单相的 LIST 序列参数 + /// 传入的数组长度必须保持一致,代表 LIST 的每个 Step(SEQ) + /// + /// 相位 (1: L1, 2: L2, 3: L3) + /// 各步起始角度 (如 [0, 0, 0]) + /// 各步交流起始电压 (如 [230, 11.5, 230]) + /// 各步交流结束电压 (如 [230, 11.5, 230]) + /// 各步直流起始电压 + /// 各步直流结束电压 + /// 各步起始频率 + /// 各步结束频率 + /// 各步执行时间(单位: 毫秒) + public virtual async Task 配置单相List序列( + int phase, + double[] degrees, + double[] vacStart, double[] vacEnd, + double[] vdcStart, double[] vdcEnd, + double[] freqStart, double[] freqEnd, + double[] dwellTimeMs, + CancellationToken ct = default) + { + if (phase < 1 || phase > 3) throw new ArgumentOutOfRangeException(nameof(phase), "相位必须为 1, 2, 或 3"); + + // 选择相位并设置为正弦波 + await SendAsync($"INST:NSEL {phase}{ScpiDelimiter}", ct); + await SendAsync($"FUNC:SHAP:A SINE{ScpiDelimiter}", ct); + + // 转换数组为 SCPI 需要的逗号分隔字符串 + string strDegrees = JoinParameters(degrees); + string strVacStart = JoinParameters(vacStart); + string strVacEnd = JoinParameters(vacEnd); + string strVdcStart = JoinParameters(vdcStart); + string strVdcEnd = JoinParameters(vdcEnd); + string strFreqStart = JoinParameters(freqStart); + string strFreqEnd = JoinParameters(freqEnd); + string strDwell = JoinParameters(dwellTimeMs); + + // 下发序列参数 + // 注意 Chroma 部分指令是 DEGR,部分固件版本是 DEGRee,这里统一使用简写 DEGR,兼容性最好 + await SendAsync($"LIST:DEGR {strDegrees}{ScpiDelimiter}", ct); + await SendAsync($"LIST:VOLT:AC:STAR {strVacStart}{ScpiDelimiter}", ct); + await SendAsync($"LIST:VOLT:AC:END {strVacEnd}{ScpiDelimiter}", ct); + await SendAsync($"LIST:VOLT:DC:STAR {strVdcStart}{ScpiDelimiter}", ct); + await SendAsync($"LIST:VOLT:DC:END {strVdcEnd}{ScpiDelimiter}", ct); + await SendAsync($"LIST:FREQ:STAR {strFreqStart}{ScpiDelimiter}", ct); + await SendAsync($"LIST:FREQ:END {strFreqEnd}{ScpiDelimiter}", ct); + await SendAsync($"LIST:DWEL {strDwell}{ScpiDelimiter}", ct); + } + + /// + /// 辅助方法:将 double 数组转换为逗号分隔的字符串,确保小数点格式正确 + /// + private string JoinParameters(double[] values) + { + return string.Join(",", values.Select(v => v.ToString("0.###", CultureInfo.InvariantCulture))); + } + + #endregion + + #region 4. LIST 触发与状态监控 + + /// + /// 切换到 LIST 模式并触发输出 + /// + public virtual async Task 启动List输出(CancellationToken ct = default) + { + await SendAsync($"OUTP:MODE LIST{ScpiDelimiter}", ct); + await SendAsync($"TRIG ON{ScpiDelimiter}", ct); + } + + /// + /// 查询 LIST 序列是否正在运行 + /// + public virtual async Task 查询List是否运行中(CancellationToken ct = default) + { + string state = await WriteReadAsync($"TRIG:STATE?{ScpiDelimiter}", ScpiDelimiter, ct); + state = state?.Trim().ToUpper(); + return state == "ON" || state == "1"; + } + + /// + /// 阻塞异步等待直到 LIST 序列执行完毕 (带安全超时保护) + /// + /// 最大等待超时时间(建议比实际LIST总时长多5-10秒) + public virtual async Task 等待List执行结束(int timeoutMs = 60000, CancellationToken ct = default) + { + // 给仪器留出响应触发的时间 + await Task.Delay(500, ct); + + Stopwatch sw = Stopwatch.StartNew(); + bool isRunning = true; + + while (isRunning) + { + ct.ThrowIfCancellationRequested(); + + if (sw.ElapsedMilliseconds > timeoutMs) + { + throw new TimeoutException($"等待 LIST 执行结束超时 ({timeoutMs} ms)。"); + } + + // 读取当前状态 + isRunning = await 查询List是否运行中(ct); + + if (isRunning) + { + // 还在运行,适当休眠后继续轮询,避免总线阻塞 + await Task.Delay(500, ct); + } + } + } + + #endregion + } +} \ No newline at end of file