设备类优化

This commit is contained in:
“hsc”
2026-08-03 09:31:14 +08:00
parent 5cfb8af9e4
commit 2e74627b31
15 changed files with 148 additions and 949 deletions

View File

@@ -0,0 +1,297 @@
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
}
}