using Common.Attributes; using DeviceCommand.Base; using System; using System.ComponentModel; using System.Globalization; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Device { [ADPCommand] public class N36600 : Tcp { private CancellationTokenSource? _heartbeatCts; private Task? _heartbeatTask; private const int HeartbeatInterval = 3000; // 心跳间隔 3 秒 public bool IsActive { get; private set; } = false; public int ReConnectionAttempts { get; private set; } = 0; public const int MaxReconnectAttempts = 10; // 手册第 4 页明确规定:每条命令后面都要加结束符 0x0A (\n) private const string SCPIDelimiter = "\n"; public N36600(string ipAddress, int port, int sendTimeout, int receiveTimeout) { ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } /// /// 建立 TCP 连接,成功后自动激活心跳 /// public new async Task ConnectAsync(CancellationToken ct = default) { bool isConnected = await base.ConnectAsync(ct); if (isConnected) { IsActive = true; ReConnectionAttempts = 0; StartHeartbeat(); } return isConnected; } public override void Close() { StopHeartbeat(); base.Close(); } #region 心跳与断线重连逻辑 [Browsable(false)] public void StartHeartbeat() { if (_heartbeatTask != null && !_heartbeatTask.IsCompleted) return; _heartbeatCts?.Cancel(); _heartbeatCts?.Dispose(); _heartbeatCts = new CancellationTokenSource(); _heartbeatTask = Task.Run(() => HeartbeatLoop(_heartbeatCts.Token)); } [Browsable(false)] public void StopHeartbeat() { IsActive = false; if (_heartbeatCts != null && !_heartbeatCts.IsCancellationRequested) { _heartbeatCts.Cancel(); } _heartbeatTask = null; } private async Task HeartbeatLoop(CancellationToken ct) { while (!ct.IsCancellationRequested) { try { await Task.Delay(HeartbeatInterval, ct); if (ct.IsCancellationRequested) break; // 使用公共查询命令发送心跳,确保通道连接正常,且不破坏远程或本地锁定状态 await SendAsync($"*IDN?{SCPIDelimiter}", ct); IsActive = true; ReConnectionAttempts = 0; } catch (Exception) { IsActive = false; ReConnectionAttempts++; if (ReConnectionAttempts > MaxReconnectAttempts) { StopHeartbeat(); base.Close(); return; } await ReconnectDeviceAsync(ct); } } } private async Task ReconnectDeviceAsync(CancellationToken ct) { try { await ConnectAsync(ct); } catch (Exception) { // 静默处理,等待下一轮心跳重试 } } #endregion #region 3.1 IEEE 488.2 公用命令 /// /// 3.1.1 读取电源的相关信息(制造商, 产品标号, 产品序列号, 软件版本号) /// public virtual async Task 查询设备标识(CancellationToken ct = default) { return await WriteReadAsync($"*IDN?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.1.2 从指定的单位中恢复 *SAV 命令保存的设定值 (参数:1~99) /// public virtual async Task 调用存储状态(int 组别, CancellationToken ct = default) { if (组别 < 1 || 组别 > 99) throw new ArgumentOutOfRangeException(nameof(组别), "组别有效范围为 1~99"); await SendAsync($"*RCL {组别}{SCPIDelimiter}", ct); } /// /// 3.1.3 将仪器当前系统状态保存到非易失性内存中 (参数:1~99) /// public virtual async Task 保存当前状态(int 组别, CancellationToken ct = default) { if (组别 < 1 || 组别 > 99) throw new ArgumentOutOfRangeException(nameof(组别), "组别有效范围为 1~99"); await SendAsync($"*SAV {组别}{SCPIDelimiter}", ct); } /// /// 3.1.4 远程控制触发一次 /// public virtual async Task 远程触发(CancellationToken ct = default) { await SendAsync($"*TRG{SCPIDelimiter}", ct); } /// /// 3.1.5 返回状态、采集电压、采集电流 (格式: 状态码,电压值,电流值) /// public virtual async Task 查询全部状态及采样(CancellationToken ct = default) { return await WriteReadAsync($"*ALL?{SCPIDelimiter}", SCPIDelimiter, ct); } #endregion #region 3.2 APPLy 命令子系统 /// /// 3.2.1 设置输出电压和输出电流值 /// public virtual async Task 快捷设置电压电流(double 电压, double 电流, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":APPLY {0:F3},{1:F3}{2}", 电压, 电流, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.2.1 查询当前设定的输出电压和电流值 /// public virtual async Task 查询快捷电压电流设定(CancellationToken ct = default) { return await WriteReadAsync($":APPLY?{SCPIDelimiter}", SCPIDelimiter, ct); } #endregion #region 3.5 MEASure 命令子系统 /// /// 3.5.1 查询通道输出端子上测得的直流电流值 (A) /// public virtual async Task 查询实际电流(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:CURRent?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.2 查询通道输出端子上测得的直流功率值 (W) /// public virtual async Task 查询实际功率(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:POWer?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.3 查询通道输出端子上测得的直流电压值 (V) /// public virtual async Task 查询实际电压(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:VOLTage?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.4 查询通道输出端子上测得的电压、电流和功率的组合值 /// public virtual async Task 查询电压电流功率数组(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:VAP?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.5 查询通道输出端子上测得的电池容量 /// public virtual async Task 查询测得电池容量(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:CAPAcity?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.6 查询通道输出端子上测得的恒流输出时间长度 (单位: ms) /// public virtual async Task 查询恒流输出时间长度(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:TIMEr:CC?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.7 查询通道输出端子上测得的输出时间长度 /// public virtual async Task 查询总输出时间长度(CancellationToken ct = default) { return await WriteReadAsync($":MEASure:TIME:OUTPut?{SCPIDelimiter}", SCPIDelimiter, ct); } #endregion #region 3.6 OUTPut 命令子系统 /// /// 3.6.1 设置电源输出开关 /// public virtual async Task 设置DC输出(bool 开启, CancellationToken ct = default) { string 状态 = 开启 ? "ON" : "OFF"; await SendAsync($":OUTPut {状态}{SCPIDelimiter}", ct); } /// /// 3.6.1 查询通道输出状态 (返回 ON 或 OFF) /// public virtual async Task 查询DC输出状态(CancellationToken ct = default) { return await WriteReadAsync($":OUTPut?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.6.2 控制电源输出定时器的开关状态 /// public virtual async Task 设置定时器状态(bool 开启, CancellationToken ct = default) { string 状态 = 开启 ? "ON" : "OFF"; await SendAsync($":OUTPut:TIMEr {状态}{SCPIDelimiter}", ct); } /// /// 3.6.3 设定电源输出定时器的时间 (单位: s, 范围: 0.1 ~ 999999.9) /// public virtual async Task 设置定时器时间(double 秒数, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":OUTPut:TIMEr:DATA {0:F1}{1}", 秒数, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.6.4 设定电源输出的模式优先权 (CV优先 或 CC优先) /// public virtual async Task 设置输出模式优先(bool 是CV优先, CancellationToken ct = default) { string 模式 = 是CV优先 ? "CV" : "CC"; await SendAsync($":OUTPut:MODE {模式}{SCPIDelimiter}", ct); } #endregion #region 3.7 SOURce 命令子系统 /// /// 3.7.1.1 清除输出电流保护(OCP)电路状态 /// public virtual async Task 清除电流保护状态(CancellationToken ct = default) { await SendAsync($":CURRent:PROTection:CLEar{SCPIDelimiter}", ct); } /// /// 3.7.1.2 使能或禁止当前输出电流保护(OCP)电路 /// public virtual async Task 设置电流保护使能(bool 启用, CancellationToken ct = default) { string 状态 = 启用 ? "ON" : "OFF"; await SendAsync($":CURRent:PROTection:STATE {状态}{SCPIDelimiter}", ct); } /// /// 3.7.1.4 设置输出电流保护(OCP)阀值 (A) /// public virtual async Task 设置过流保护值(double 电流, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":CURRent:PROTection {0:F3}{1}", 电流, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.1.7 设置电源的输出电流值 (A) /// public virtual async Task 设置电流(double 电流, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":CURRent {0:F3}{1}", 电流, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.2.1 清除输出电压保护(OVP)电路状态 /// public virtual async Task 清除电压保护状态(CancellationToken ct = default) { await SendAsync($":VOLTage:PROTection:CLEar{SCPIDelimiter}", ct); } /// /// 3.7.2.2 使能或禁止当前输出电压保护(OVP)电路 /// public virtual async Task 设置电压保护使能(bool 启用, CancellationToken ct = default) { string 状态 = 启用 ? "ON" : "OFF"; await SendAsync($":VOLTage:PROTection:STATE {状态}{SCPIDelimiter}", ct); } /// /// 3.7.2.4 设置输出电压保护(OVP)阀值 (V) /// public virtual async Task 设置过压保护值(double 电压, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage:PROTection {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.2.7 设置电源的输出电压值 (V) /// public virtual async Task 设置电压(double 电压, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.2.8 设定电压输出范围的上限电压值 (V) /// public virtual async Task 设置电压上限限制(double 电压, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage:LIMIT {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.3.1 设置输出功率值 (W) /// public virtual async Task 设置功率(double 功率, CancellationToken ct = default) { string cmd = string.Format(CultureInfo.InvariantCulture, ":POWer {0:F3}{1}", 功率, SCPIDelimiter); await SendAsync(cmd, ct); } /// /// 3.7.3.2 禁止或使能当前恒功率(CP)输出 /// public virtual async Task 设置恒功率输出使能(bool 启用, CancellationToken ct = default) { string 状态 = 启用 ? "ON" : "OFF"; await SendAsync($":POWer:STATE {状态}{SCPIDelimiter}", ct); } #endregion #region 3.9 SYSTem 命令子系统 /// /// 3.9.1 控制蜂鸣器开关 /// public virtual async Task 设置蜂鸣器状态(bool 开启, CancellationToken ct = default) { string 状态 = 开启 ? "ON" : "OFF"; await SendAsync($":SYSTem:BEEPer:STATE {状态}{SCPIDelimiter}", ct); } /// /// 3.9.2 使蜂鸣器强制鸣叫一声 /// public virtual async Task 蜂鸣器鸣叫(CancellationToken ct = default) { await SendAsync($":SYSTem:BEEPer{SCPIDelimiter}", ct); } /// /// 3.9.4 查询仪器当前出错记录数量 (最大 18 组) /// public virtual async Task 查询错误记录数量(CancellationToken ct = default) { return await WriteReadAsync($":SYSTem:ERRor:COUNT?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.9.5 读取仪器的出错信息 (成功返回 0,"No error") /// public virtual async Task 查询错误信息(CancellationToken ct = default) { return await WriteReadAsync($":SYSTem:ERRor?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.9.7 设置电源为面板控制模式 (本地 Local 状态,前面板按键可用) /// public virtual async Task 切换本地控制模式(CancellationToken ct = default) { await SendAsync($":SYSTem:LOCal{SCPIDelimiter}", ct); } /// /// 3.9.8 设置电源为远程控制模式 (Remote 状态) /// public virtual async Task 切换远程控制模式(CancellationToken ct = default) { await SendAsync($":SYSTem:REMote{SCPIDelimiter}", ct); } /// /// 3.9.9 通过通信接口设置电源为远程控制锁定模式 /// public virtual async Task 远程控制模式锁定(CancellationToken ct = default) { await SendAsync($":SYSTem:RWLock{SCPIDelimiter}", ct); } #endregion } }