Files
ACP/DeviceCommand/Devices/IT6720.cs
T

316 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 低压电源 一拖三
/// </summary>
[ACPCommand]
public class IT6720: Serial_Port // 直接继承您提供的 Serial_Port 基类
{
// 帧格式:同步头 AA + 地址 + 命令字 + 数据[4-25] + 校验和,共 26 字节
private const byte FrameSyncByte = 0xAA;
private const int FrameLength = 26;
private readonly byte _deviceAddress = 0x00; // 电源地址
public IT6720(SerialPortConfig config) : base(config)
{
}
#region 核心协议封装(不变)
/// <summary>
/// 构建 26 字节的发送帧,并计算校验和
/// </summary>
private byte[] BuildFrame(byte command, byte[] dataBytes)
{
List<byte> frame = new List<byte> { FrameSyncByte, _deviceAddress, command };
if (dataBytes != null && dataBytes.Length > 0)
{
frame.AddRange(dataBytes);
}
// 数据区必须占满 23 字节 (索引 3 到 24)
while (frame.Count < 25)
{
frame.Add(0x00);
}
// 计算校验和:前 25 字节之和取低 8 位
byte checkSum = 0;
for (int i = 0; i < 25; i++)
{
checkSum += frame[i];
}
frame.Add(checkSum);
return frame.ToArray();
}
/// <summary>
/// 检查返回的 26 字节响应帧是否正确
/// </summary>
private bool ValidateResponseFrame(byte[] response)
{
if (response == null || response.Length != FrameLength) return false;
if (response[0] != FrameSyncByte) return false;
if (response[1] != _deviceAddress) return false;
byte checkSum = 0;
for (int i = 0; i < 25; i++)
{
checkSum += response[i];
}
return response[25] == checkSum;
}
/// <summary>
/// 将实际电压/电流值转换成协议要求的整型 (乘以 1000 变成整形)
/// </summary>
private int ConvertToProtocolValue(double value)
{
return (int)Math.Round(value * 1000);
}
/// <summary>
/// 将协议返回的 4 字节小端序转换为实际数值 (除以 1000.0)
/// </summary>
private double Convert4BytesToValue(byte b0, byte b1, byte b2, byte b3)
{
uint raw = (uint)(b0 | (b1 << 8) | (b2 << 16) | (b3 << 24));
return raw / 1000.0;
}
/// <summary>
/// 将协议返回的 2 字节小端序转换为实际数值 (除以 1000.0)
/// </summary>
private double Convert2BytesToValue(byte b0, byte b1)
{
ushort raw = (ushort)(b0 | (b1 << 8));
return raw / 1000.0;
}
#endregion
#region 1. 核心二进制收发方法(使用基类新方法)
/// <summary>
/// 发送命令帧并读取仪器返回的 26 字节数据帧
/// </summary>
private async Task<byte[]> SendAndReadFrameAsync(byte command, byte[] dataBytes, CancellationToken ct = default)
{
byte[] sendFrame = BuildFrame(command, dataBytes);
// 使用基类提供的 WriteBytesAsync 发送二进制数据
await WriteBytesAsync(sendFrame, ct).ConfigureAwait(false);
// 使用基类提供的 ReadBytesAsync 读取定长 26 字节二进制数据
// 注意:这里的 ReadTimeout 由基类统一控制
return await ReadBytesAsync(FrameLength, ct).ConfigureAwait(false);
}
#endregion
#region 2. 操作模式与输出控制
/// <summary>
/// 切换电源控制模式:PC 远程控制 / 面板控制
/// </summary>
public virtual async Task 切换远程控制模式(bool 远程控制 = true, CancellationToken ct = default)
{
byte[] data = new byte[] { (byte)(远程控制 ? 0x01 : 0x00) };
byte[] response = await SendAndReadFrameAsync(0x20, data, ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 切换控制模式时通讯校验失败。");
}
/// <summary>
/// 切换为本地(面板)控制模式,与远程控制互斥
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task 切换本地控制模式(CancellationToken ct = default)
{
await 切换远程控制模式(false, ct);
}
/// <summary>
/// 开启或关闭电源输出
/// </summary>
public virtual async Task 设置输出开关(bool 开启 = true, CancellationToken ct = default)
{
byte[] data = new byte[] { (byte)(开启 ? 0x01 : 0x00) };
byte[] response = await SendAndReadFrameAsync(0x21, data, ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 设置输出时通讯校验失败。");
}
/// <summary>
/// 查询电源输出开关状态(由实时测量值的状态字节判断)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>输出开关状态字符串 (ON / OFF)</returns>
public virtual async Task<string> 查询输出开关状态(CancellationToken ct = default)
{
var (_, _, status) = await 查询实时测量值(ct);
return (status & 0x01) != 0 ? "ON" : "OFF";
}
#endregion
#region 3. 电压 / 电流设定
/// <summary>
/// 设置电源的电压上限
/// </summary>
public virtual async Task 设置电压上限(double 电压, CancellationToken ct = default)
{
int raw = ConvertToProtocolValue(电压);
byte[] data = new byte[]
{
(byte)(raw & 0xFF), (byte)((raw >> 8) & 0xFF),
(byte)((raw >> 16) & 0xFF), (byte)((raw >> 24) & 0xFF)
};
byte[] response = await SendAndReadFrameAsync(0x22, data, ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 设置电压上限时通讯校验失败。");
}
/// <summary>
/// 设置电源的输出电压
/// </summary>
public virtual async Task 设置输出电压(double 电压, CancellationToken ct = default)
{
int raw = ConvertToProtocolValue(电压);
byte[] data = new byte[]
{
(byte)(raw & 0xFF), (byte)((raw >> 8) & 0xFF),
(byte)((raw >> 16) & 0xFF), (byte)((raw >> 24) & 0xFF)
};
byte[] response = await SendAndReadFrameAsync(0x23, data, ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 设置输出电压时通讯校验失败。");
}
/// <summary>
/// 设置电源的输出电流限流值
/// </summary>
public virtual async Task 设置输出电流(double 电流, CancellationToken ct = default)
{
int raw = ConvertToProtocolValue(电流);
byte[] data = new byte[]
{
(byte)(raw & 0xFF), (byte)((raw >> 8) & 0xFF)
};
byte[] response = await SendAndReadFrameAsync(0x24, data, ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 设置输出电流时通讯校验失败。");
}
#endregion
#region 4. 读取实时数据与状态(对应 0x26H
/// <summary>
/// 读取电源的实时电流、电压、设定值及电源状态
/// 返回元组:实际电流(A), 实际电压(V), 状态字节(int)
/// </summary>
[Monitorable("IT6720 实时测量数据")]
public virtual async Task<(double ActualCurrent, double ActualVoltage, int StatusByte)> 查询实时测量值(CancellationToken ct = default)
{
byte[] response = await SendAndReadFrameAsync(0x26, new byte[0], ct);
if (!ValidateResponseFrame(response))
throw new Exception("IT6720 读取数据时通讯校验失败。");
double actualCurrent = Convert2BytesToValue(response[4], response[5]);
double actualVoltage = Convert4BytesToValue(response[6], response[7], response[8], response[9]);
int statusByte = response[10];
return (actualCurrent, actualVoltage, statusByte);
}
/// <summary>
/// 查询电源的实时实际输出电压 (MEASure 相关寄存器)
/// </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 相关寄存器)
/// </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) 模式
/// 状态字节位详解:位 2,3 = 1(CV), 2(CC)
/// </summary>
public virtual async Task<string> 查询输出模式(CancellationToken ct = default)
{
var (_, _, status) = await 查询实时测量值(ct);
int mode = (status >> 2) & 0x03;
return mode switch
{
1 => "CV",
2 => "CC",
_ => "UNKNOWN"
};
}
#endregion
#region 5. 读取设备信息(命令字 31H
/// <summary>
/// 读取电源的序列号、型号及软件版本
/// </summary>
public virtual async Task<string> 查询设备信息(CancellationToken ct = default)
{
byte[] response = await SendAndReadFrameAsync(0x31, new byte[0], ct);
if (!ValidateResponseFrame(response)) return "Invalid Response";
string model = System.Text.Encoding.ASCII.GetString(response, 4, 5).Trim('\0');
string swVersion = $"{response[9]:X2}.{response[10]:X2}";
string serialNum = System.Text.Encoding.ASCII.GetString(response, 11, 10).Trim('\0');
return $"Model:{model}, SW:{swVersion}, SN:{serialNum}";
}
#endregion
#region 6. 扩展系统命令(兼容接口)
/// <summary>
/// 基类自带的超时和清空缓冲区已经处理了大部分错误。
/// 如需在面板显示具体错误,需通过状态位自行判断或查看面板 ERROR 灯。
/// </summary>
public virtual async Task<string> 查询错误信息(CancellationToken ct = default)
{
return "IT6720 无标准 SCPI 错误信息查询,请检查前面板 ERROR 指示灯。";
}
#endregion
}
}