182 lines
7.8 KiB
C#
182 lines
7.8 KiB
C#
using Common.Attributes;
|
||
using DeviceCommand.Base;
|
||
using Model.Models;
|
||
using System;
|
||
using System.Globalization;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace DeviceCommand.Devices
|
||
{
|
||
/// <summary>
|
||
/// 低压电源 一拖三(ITECH IT67xx 系列,标准 SCPI 指令集)。
|
||
/// <para>传输层为 VISA(USB VISA 资源字符串寻址,如 USB0::VID::PID::SN::INSTR)。</para>
|
||
/// <para>SCPI 命令语法参照同家族已在实机验证的实现 BOB/DeviceCommand/Devices/IT6724C.cs:
|
||
/// 设定类命令只发不读(SCPI 设置命令无应答,避免空等超时),查询类命令走 WriteReadAsync 读取一行响应。</para>
|
||
/// </summary>
|
||
[ACPCommand]
|
||
public class IT6720 : Visa
|
||
{
|
||
// SCPI 指令结束符(VISA 消息终止符使用 LF)
|
||
private const string ScpiDelimiter = "\n";
|
||
|
||
// 最近一次设定的限流值(A):IT67xx SCPI 无直接 CV/CC 查询,用"实测电流是否顶到限流"启发式判断输出模式
|
||
private double _setCurrentLimit;
|
||
|
||
public IT6720(VisaConfig config) : base(config)
|
||
{
|
||
}
|
||
|
||
#region SCPI 收发辅助
|
||
|
||
/// <summary>以不变文化格式化数值,避免小数分隔符随系统区域变化。</summary>
|
||
private static string Fmt(double value) => value.ToString(CultureInfo.InvariantCulture);
|
||
|
||
/// <summary>宽松解析仪器回传的数值字符串,失败返回 0。</summary>
|
||
private static double ParseDouble(string raw)
|
||
=> double.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out double v) ? v : 0;
|
||
|
||
/// <summary>发送设定类 SCPI 命令(只发不读:SCPI 设置命令无应答,读取会空等超时)。</summary>
|
||
private async Task SetAsync(string command, CancellationToken ct)
|
||
=> await SendAsync($"{command}{ScpiDelimiter}", ct).ConfigureAwait(false);
|
||
|
||
/// <summary>发送查询类 SCPI 命令并读取一行响应。</summary>
|
||
private async Task<string> QueryAsync(string query, CancellationToken ct)
|
||
=> await WriteReadAsync($"{query}{ScpiDelimiter}", ScpiDelimiter, ct).ConfigureAwait(false);
|
||
|
||
#endregion
|
||
|
||
#region 1. 操作模式与输出控制
|
||
|
||
/// <summary>
|
||
/// 切换电源控制模式:PC 远程控制 (SYSTem:REMote) / 面板本地控制 (SYSTem:LOCal)
|
||
/// </summary>
|
||
public virtual async Task 切换远程控制模式(bool 远程控制 = true, CancellationToken ct = default)
|
||
=> await SetAsync(远程控制 ? "SYSTem:REMote" : "SYSTem:LOCal", ct);
|
||
|
||
/// <summary>
|
||
/// 切换为本地(面板)控制模式,与远程控制互斥
|
||
/// </summary>
|
||
/// <param name="ct">取消令牌</param>
|
||
public virtual async Task 切换本地控制模式(CancellationToken ct = default)
|
||
=> await 切换远程控制模式(false, ct);
|
||
|
||
/// <summary>
|
||
/// 开启或关闭电源输出 (OUTPut 1/0)
|
||
/// </summary>
|
||
public virtual async Task 设置输出开关(bool 开启 = true, CancellationToken ct = default)
|
||
=> await SetAsync($"OUTPut {(开启 ? 1 : 0)}", ct);
|
||
|
||
/// <summary>
|
||
/// 查询电源输出开关状态 (OUTPut?)
|
||
/// </summary>
|
||
/// <param name="ct">取消令牌</param>
|
||
/// <returns>输出开关状态字符串 (ON / OFF)</returns>
|
||
public virtual async Task<string> 查询输出开关状态(CancellationToken ct = default)
|
||
{
|
||
string r = (await QueryAsync("OUTPut?", ct)).Trim();
|
||
return (r == "1" || r.StartsWith("ON", StringComparison.OrdinalIgnoreCase)) ? "ON" : "OFF";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 2. 电压 / 电流设定
|
||
|
||
/// <summary>
|
||
/// 设置电源的电压上限 / 过压保护阈值 (VOLT:PROTection)
|
||
/// </summary>
|
||
public virtual async Task 设置电压上限(double 电压, CancellationToken ct = default)
|
||
=> await SetAsync($"VOLT:PROTection {Fmt(电压)}", ct);
|
||
|
||
/// <summary>
|
||
/// 设置电源的输出电压 (VOLTage)
|
||
/// </summary>
|
||
public virtual async Task 设置输出电压(double 电压, CancellationToken ct = default)
|
||
=> await SetAsync($"VOLTage {Fmt(电压)}", ct);
|
||
|
||
/// <summary>
|
||
/// 设置电源的输出电流限流值 (CURRent)
|
||
/// </summary>
|
||
public virtual async Task 设置输出电流(double 电流, CancellationToken ct = default)
|
||
{
|
||
_setCurrentLimit = 电流;
|
||
await SetAsync($"CURRent {Fmt(电流)}", ct);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 3. 读取实时数据与状态
|
||
|
||
/// <summary>
|
||
/// 读取电源的实时电流、电压及输出开关状态
|
||
/// 返回元组:实际电流(A, MEASure:CURRent?), 实际电压(V, MEASure:VOLTage?), 状态字节(int, 位0=输出开关)
|
||
/// </summary>
|
||
[Monitorable("IT6720 实时测量数据")]
|
||
public virtual async Task<(double ActualCurrent, double ActualVoltage, int StatusByte)> 查询实时测量值(CancellationToken ct = default)
|
||
{
|
||
double current = ParseDouble(await QueryAsync("MEASure:CURRent?", ct));
|
||
double voltage = ParseDouble(await QueryAsync("MEASure:VOLTage?", ct));
|
||
string on = (await QueryAsync("OUTPut?", ct)).Trim();
|
||
int statusByte = (on == "1" || on.StartsWith("ON", StringComparison.OrdinalIgnoreCase)) ? 0x01 : 0x00;
|
||
return (current, voltage, statusByte);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询电源的实时实际输出电压 (MEASure:VOLTage?)
|
||
/// </summary>
|
||
/// <param name="ct">取消令牌</param>
|
||
/// <returns>实时电压值 (单位: V,保留两位小数)</returns>
|
||
[Monitorable("IT6720 实际电压")]
|
||
public virtual async Task<double> 查询实际电压(CancellationToken ct = default)
|
||
{
|
||
var (_, voltage, _) = await 查询实时测量值(ct);
|
||
return Math.Round(voltage, 2);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询电源的实时实际输出电流 (MEASure:CURRent?)
|
||
/// </summary>
|
||
/// <param name="ct">取消令牌</param>
|
||
/// <returns>实时电流值 (单位: A,保留两位小数)</returns>
|
||
[Monitorable("IT6720 实际电流")]
|
||
public virtual async Task<double> 查询实际电流(CancellationToken ct = default)
|
||
{
|
||
var (current, _, _) = await 查询实时测量值(ct);
|
||
return Math.Round(current, 2);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断输出处于恒压 (CV) 还是恒流 (CC) 模式。
|
||
/// IT67xx SCPI 无直接 CV/CC 查询命令,此处以"实测电流是否顶到限流设定值"启发式判断:
|
||
/// 输出关闭返回 OFF;实测电流达到限流值(≥98%)判为 CC,否则 CV。
|
||
/// </summary>
|
||
public virtual async Task<string> 查询输出模式(CancellationToken ct = default)
|
||
{
|
||
var (current, _, status) = await 查询实时测量值(ct);
|
||
if ((status & 0x01) == 0) return "OFF";
|
||
if (_setCurrentLimit > 0 && current >= _setCurrentLimit * 0.98) return "CC";
|
||
return "CV";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 4. 设备信息与错误
|
||
|
||
/// <summary>
|
||
/// 读取电源的标识信息 (*IDN?:制造商, 型号, 序列号, 固件版本)
|
||
/// </summary>
|
||
public virtual async Task<string> 查询设备信息(CancellationToken ct = default)
|
||
=> await QueryAsync("*IDN?", ct);
|
||
|
||
/// <summary>
|
||
/// IT67xx 未提供标准错误队列查询,返回面板提示说明
|
||
/// </summary>
|
||
public virtual async Task<string> 查询错误信息(CancellationToken ct = default)
|
||
{
|
||
return "IT6720 无标准 SCPI 错误信息查询,请检查前面板 ERROR 指示灯。";
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|