Files
ACP/DeviceCommand/Devices/IT6720.cs
2026-08-03 11:05:26 +08:00

297 lines
11 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 切换控制模式时通讯校验失败。");
}
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 设置输出时通讯校验失败。");
}
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);
}
[Monitorable("IT6720 实际电压")]
public virtual async Task<string> (CancellationToken ct = default)
{
var (_, voltage, _) = await (ct);
return voltage.ToString("F2", CultureInfo.InvariantCulture);
}
[Monitorable("IT6720 实际电流")]
public virtual async Task<string> (CancellationToken ct = default)
{
var (current, _, _) = await (ct);
return current.ToString("F3", CultureInfo.InvariantCulture);
}
/// <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
}
}