添加冷水机驱动
This commit is contained in:
@@ -1,20 +1,337 @@
|
||||
using DeviceCommand.Base;
|
||||
using Common.Attributes;
|
||||
using DeviceCommand.Base;
|
||||
using Model.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// 水冷机 一拖三
|
||||
/// 水冷机 Modbus TCP 驱动 (支持3回路独立控制)
|
||||
/// 通讯协议: MCx ModbusTCP
|
||||
/// </summary>
|
||||
public class MCc30W:ModbusTcp
|
||||
[ACPCommand]
|
||||
public class 冷水机 : ModbusTcp
|
||||
{
|
||||
public MCc30W(TcpConfig config) : base(config)
|
||||
// 默认配置 (可从 config 或 TcpConfig 注入)
|
||||
private readonly byte _slaveId = 0x01; // 单元标识符
|
||||
|
||||
public 冷水机(TcpConfig config) : base(config)
|
||||
{
|
||||
}
|
||||
|
||||
#region 1. 底层连接与字节序转换辅助
|
||||
|
||||
/// <summary>
|
||||
/// 将单精度浮点数转换为 Modbus 大端序 2个16位寄存器
|
||||
/// </summary>
|
||||
private ushort[] FloatToRegisters(float value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return new ushort[]
|
||||
{
|
||||
BitConverter.ToUInt16(bytes, 0),
|
||||
BitConverter.ToUInt16(bytes, 2)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Modbus 大端序 2个16位寄存器转换回单精度浮点数
|
||||
/// </summary>
|
||||
private float RegistersToFloat(ushort[] registers)
|
||||
{
|
||||
if (registers.Length < 2) return 0f;
|
||||
byte[] bytes = new byte[4];
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(registers[0]), 0, bytes, 0, 2);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(registers[1]), 0, bytes, 2, 2);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 32位无符号整数转换为 Modbus 大端序 2个16位寄存器
|
||||
/// </summary>
|
||||
private ushort[] UInt32ToRegisters(uint value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return new ushort[]
|
||||
{
|
||||
BitConverter.ToUInt16(bytes, 0),
|
||||
BitConverter.ToUInt16(bytes, 2)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Modbus 大端序 2个16位寄存器转换回 32位无符号整数
|
||||
/// </summary>
|
||||
private uint RegistersToUInt32(ushort[] registers)
|
||||
{
|
||||
byte[] bytes = new byte[4];
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(registers[0]), 0, bytes, 0, 2);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(registers[1]), 0, bytes, 2, 2);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2. 模拟量读写 API (支持 3 通道)
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定回路的出液目标温度 (℃)
|
||||
/// </summary>
|
||||
public virtual async Task 设置出液目标温度Async(int loop, float temperature, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0000 + (loop - 1) * 2);
|
||||
ushort[] data = FloatToRegisters(temperature);
|
||||
await WriteMultipleRegistersAsync(_slaveId, address, data, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定回路的出液目标流量 (L/min)
|
||||
/// </summary>
|
||||
public virtual async Task 设置出液目标流量Async(int loop, float flow, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0008 + (loop - 1) * 2);
|
||||
ushort[] data = FloatToRegisters(flow);
|
||||
await WriteMultipleRegistersAsync(_slaveId, address, data, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定回路的出液目标压力 (kPa)
|
||||
/// </summary>
|
||||
public virtual async Task 设置出液目标压力Async(int loop, float pressure, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x000E + (loop - 1) * 2);
|
||||
ushort[] data = FloatToRegisters(pressure);
|
||||
await WriteMultipleRegistersAsync(_slaveId, address, data, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的当前出液温度 (℃)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机出液当前温度")]
|
||||
public virtual async Task<float> 读取出液当前温度Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0018 + (loop - 1) * 2);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的当前出液流量 (L/min)
|
||||
/// 注意:协议中三通道流量地址间隔为 0x08
|
||||
/// </summary>
|
||||
[Monitorable("水冷机出液当前流量")]
|
||||
public virtual async Task<float> 读取出液当前流量Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x001E + (loop - 1) * 8);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的当前出液压力 (kPa)
|
||||
/// 注意:协议中三通道压力地址间隔为 0x08
|
||||
/// </summary>
|
||||
[Monitorable("水冷机出液当前压力")]
|
||||
public virtual async Task<float> 读取出液当前压力Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0020 + (loop - 1) * 8);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的当前回液压力 (kPa) (0x0022 + (loop-1)*8)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机回液当前压力")]
|
||||
public virtual async Task<float> 读取回液当前压力Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0022 + (loop - 1) * 8);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的当前回液温度 (℃) (0x0024 + (loop-1)*8)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机回液当前温度")]
|
||||
public virtual async Task<float> 读取回液当前温度Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0024 + (loop - 1) * 8);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的外部温度反馈 (℃) (0x0036 + (loop-1)*2)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机外部温度反馈")]
|
||||
public virtual async Task<float> 读取外部温度反馈Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0036 + (loop - 1) * 2);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定回路的温变速率设定 (℃/min) (0x0006 + (loop-1)*2)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机温变速率")]
|
||||
public virtual async Task<float> 读取温变速率Async(int loop, CancellationToken ct = default)
|
||||
{
|
||||
ushort address = (ushort)(0x0006 + (loop - 1) * 2);
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取回抽2流量目标值 (L/min) (0x0016)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机回抽2流量目标值")]
|
||||
public virtual async Task<float> 读取回抽2流量目标值Async(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x0016, 2, ct);
|
||||
return RegistersToFloat(regs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3. 系统状态与位控制 API (支持 3 通道)
|
||||
|
||||
/// <summary>
|
||||
/// 核心位操作方法:读取 -> 置位/复位 -> 写入系统状态控制寄存器 (0x0014)
|
||||
/// </summary>
|
||||
private async Task SetControlBit(int bitIndex, bool isSet, CancellationToken ct = default)
|
||||
{
|
||||
// 1. 读取当前状态
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x0014, 1, ct);
|
||||
ushort currentState = regs[0];
|
||||
|
||||
// 2. 修改位
|
||||
if (isSet)
|
||||
currentState |= (ushort)(1 << bitIndex);
|
||||
else
|
||||
currentState &= (ushort)~(1 << bitIndex);
|
||||
|
||||
// 3. 写回
|
||||
await WriteSingleRegisterAsync(_slaveId, 0x0014, currentState, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动/停止 指定回路 (Bit 01, 02, 03)
|
||||
/// </summary>
|
||||
public virtual async Task 设置回路运行Async(int loop, bool enable, CancellationToken ct = default)
|
||||
{
|
||||
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
|
||||
await SetControlBit(loop, enable, ct); // 回路1->Bit1, 回路2->Bit2, 回路3->Bit3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动/停止 指定回路 预控温 (Bit 10, 11, 12)
|
||||
/// </summary>
|
||||
public virtual async Task 设置回路预控温Async(int loop, bool enable, CancellationToken ct = default)
|
||||
{
|
||||
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
|
||||
await SetControlBit(9 + loop, enable, ct); // 回路1->Bit10, 回路2->Bit11, 回路3->Bit12
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定回路 流量/压力 控制模式 (Bit 04, 05, 06)
|
||||
/// true=控压, false=控流
|
||||
/// </summary>
|
||||
public virtual async Task 设置回路压力控制Async(int loop, bool isPressureControl, CancellationToken ct = default)
|
||||
{
|
||||
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
|
||||
await SetControlBit(3 + loop, isPressureControl, ct); // 回路1->Bit4, 回路2->Bit5, 回路3->Bit6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 报警复位 (Bit 08)
|
||||
/// </summary>
|
||||
public virtual async Task 报警复位Async(bool enable, CancellationToken ct = default)
|
||||
{
|
||||
await SetControlBit(8, enable, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 报警消音 (Bit 09)
|
||||
/// </summary>
|
||||
public virtual async Task 报警消音Async(bool enable, CancellationToken ct = default)
|
||||
{
|
||||
await SetControlBit(9, enable, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置指定回路 自动回液开关 (寄存器 0x0015, 低位部分)
|
||||
/// 此方法操作的是 0x0015 寄存器的低 16 位
|
||||
/// </summary>
|
||||
public virtual async Task 设置回路自动回液Async(int loop, bool enable, CancellationToken ct = default)
|
||||
{
|
||||
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
|
||||
// 协议 Sheet2 中 0x0015 寄存器位定义:
|
||||
// Bit 13 (Loop1), Bit 14 (Loop2), Bit 15 (Loop3)
|
||||
int bitIndex = 12 + loop;
|
||||
// 0x0015 控制寄存器逻辑
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x0015, 1, ct);
|
||||
ushort currentState = regs[0];
|
||||
|
||||
if (enable)
|
||||
currentState |= (ushort)(1 << bitIndex);
|
||||
else
|
||||
currentState &= (ushort)~(1 << bitIndex);
|
||||
|
||||
await WriteSingleRegisterAsync(_slaveId, 0x0015, currentState, ct);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 4. 状态回读 (报警、心跳、运行状态)
|
||||
|
||||
/// <summary>
|
||||
/// 读取心跳帧 (0x0044),用于判断通讯是否正常
|
||||
/// </summary>
|
||||
[Monitorable("水冷机心跳帧")]
|
||||
public virtual async Task<uint> 读心跳帧Async(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x0044, 2, ct);
|
||||
return RegistersToUInt32(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取运行状态反馈 (0x0046),含各回路完成状态 (Sheet5)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机运行状态反馈")]
|
||||
public virtual async Task<uint> 读运行状态反馈Async(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x0046, 2, ct);
|
||||
return RegistersToUInt32(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取报警信息1 (0x003C)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机报警信息1")]
|
||||
public virtual async Task<uint> 读报警信息1Async(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x003C, 2, ct);
|
||||
return RegistersToUInt32(regs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取报警信息2 (0x003E)
|
||||
/// </summary>
|
||||
[Monitorable("水冷机报警信息2")]
|
||||
public virtual async Task<uint> 读报警信息2Async(CancellationToken ct = default)
|
||||
{
|
||||
ushort[] regs = await ReadHoldingRegistersAsync(_slaveId, 0x003E, 2, ct);
|
||||
return RegistersToUInt32(regs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user