设备驱动修改

This commit is contained in:
“hsc”
2026-06-10 10:45:51 +08:00
parent b0a7742b8f
commit 5d14afcb66
19 changed files with 764 additions and 122 deletions

View File

@@ -3,15 +3,80 @@ using Model.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// CTS3-6-300-8IS0 设备:通过 EnovaDataController 接收下位机 POST 上报的通道数据
/// </summary>
public class CTS3 : EnovaDataReporter
{
/// <summary>
/// 最近一次收到的通道数据快照key = ChannelCode
/// </summary>
public IReadOnlyDictionary<string, EnovaChannelData> LatestChannelData => _latestChannelData;
private readonly Dictionary<string, EnovaChannelData> _latestChannelData = new Dictionary<string, EnovaChannelData>();
private readonly object _dataLock = new object();
/// <summary>
/// 业务侧可订阅此事件以获得更友好的回调(仅本设备数据)
/// </summary>
public event EventHandler<EnovaChannelDataReceivedEventArgs>? OnDataUpdated;
public CTS3(HttpClient httpClient) : base(httpClient)
{
// 订阅基类事件:当 Controller 调用 HandleIncomingChannelData 时会触发
ChannelDataReceived += OnChannelDataReceivedInternal;
}
/// <summary>
/// 指定 DeviceCode 的便捷构造,便于多台 CTS3 共存时按 deviceCode 精确匹配
/// </summary>
public CTS3(HttpClient httpClient, string deviceCode) : this(httpClient)
{
DeviceCode = deviceCode ?? string.Empty;
}
private void OnChannelDataReceivedInternal(object? sender, EnovaChannelDataReceivedEventArgs e)
{
if (e?.DataList == null || e.DataList.Count == 0)
{
return;
}
// 1. 缓存最近一次的通道快照
lock (_dataLock)
{
foreach (var data in e.DataList)
{
if (data == null) continue;
string key = data.ChannelCode ?? string.Empty;
_latestChannelData[key] = data;
}
}
// 2. 业务异常拦截示例:通道错误状态
foreach (var data in e.DataList)
{
if (data == null) continue;
if (data.ChannelState == "错误" || data.ChannelState == "0x04")
{
// TODO: 触发本地报警逻辑
// Logger.LoggerHelper.WarnWithNotify($"通道 {data.ChannelCode} 故障");
}
}
// 3. 转发给业务订阅者
OnDataUpdated?.Invoke(this, e);
}
public override void Unregister()
{
ChannelDataReceived -= OnChannelDataReceivedInternal;
base.Unregister();
}
}
}

View File

@@ -0,0 +1,108 @@
using DeviceCommand.Base;
public class Others : ModbusTcp
{
private readonly byte _slaveId;
/// <summary>
/// 通道1PV
/// </summary>
private const ushort CH1_PV = 100;
/// <summary>
/// 通道2PV
/// </summary>
private const ushort CH2_PV = 102;
/// <summary>
/// 通道3PV
/// </summary>
private const ushort CH3_PV = 104;
/// <summary>
/// 通道4PV
/// </summary>
private const ushort CH4_PV = 106;
public Others(
byte slaveId,
string ip,
int port = 508,
int sendTimeoutMs = 3000,
int receiveTimeoutMs = 3000)
: base()
{
_slaveId = slaveId;
ConfigureDevice(ip, port, sendTimeoutMs, receiveTimeoutMs);
}
/// <summary>
/// 读取通道1PV
/// </summary>
public async Task<float> ReadCH1Async(CancellationToken ct = default)
{
return await ReadRealAsync(CH1_PV, ct);
}
/// <summary>
/// 读取通道2PV
/// </summary>
public async Task<float> ReadCH2Async(CancellationToken ct = default)
{
return await ReadRealAsync(CH2_PV, ct);
}
/// <summary>
/// 读取通道3PV
/// </summary>
public async Task<float> ReadCH3Async(CancellationToken ct = default)
{
return await ReadRealAsync(CH3_PV, ct);
}
/// <summary>
/// 读取通道4PV
/// </summary>
public async Task<float> ReadCH4Async(CancellationToken ct = default)
{
return await ReadRealAsync(CH4_PV, ct);
}
/// <summary>
/// 一次读取4个通道PV
/// </summary>
public async Task<(float ch1, float ch2, float ch3, float ch4)> ReadAllAsync(CancellationToken ct = default)
{
ushort[] raw = await ReadHoldingRegistersAsync(_slaveId, 100, 8, ct);
return
(
ToFloat(raw[0], raw[1]),
ToFloat(raw[2], raw[3]),
ToFloat(raw[4], raw[5]),
ToFloat(raw[6], raw[7])
);
}
private async Task<float> ReadRealAsync(ushort address, CancellationToken ct)
{
ushort[] raw = await ReadHoldingRegistersAsync(_slaveId, address, 2, ct);
return ToFloat(raw[0], raw[1]);
}
private static float ToFloat(ushort high, ushort low)
{
byte[] bytes =
{
(byte)(high >> 8),
(byte)high,
(byte)(low >> 8),
(byte)low
};
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
}

View File

@@ -1,13 +0,0 @@
using DeviceCommand.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
public class SDE720SH_A : ModbusTcp
{
}
}

View File

@@ -1,13 +0,0 @@
using DeviceCommand.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
public class SDL710FH:ModbusTcp
{
}
}

View File

@@ -3,12 +3,47 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
public class THC1100:S7Device
public class THC1100 : S7Device
{
// 定义 PLC 地址常量,便于后期维护
private const string RealTimeTemperatureSetPointAddress = "DB53.DBD280";
private const string RealTimeHumidityMeasuredValueAddress = "DB53.DBD1092";
/// <summary>
/// 异步获取温度实时测量值 (DB53.DBD1052, 浮点型)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>温度设定值 (float)</returns>
public async Task<float> GetRealTimeTemperatureSetPointAsync(CancellationToken ct = default)
{
// S7.Net 读取浮点型时,可以直接将其强转为 float
// 内部已通过基类的 _commLock 保证线程安全
return await ReadAsync<float>(RealTimeTemperatureSetPointAddress, ct);
}
/// <summary>
/// 异步获取湿度实时测量值 (DB53.DBD1092, 浮点型)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>湿度测量值 (float)</returns>
public async Task<float> GetRealTimeHumidityMeasuredValueAsync(CancellationToken ct = default)
{
return await ReadAsync<float>(RealTimeHumidityMeasuredValueAddress, ct);
}
/// <summary>
/// 一次性获取温湿度相关的运行数据包(选填,若需要批量展示可以使用此方法)
/// </summary>
public async Task<(float TemperatureSetPoint, float HumidityMeasuredValue)> GetThcDataPackAsync(CancellationToken ct = default)
{
var tempSet = await GetRealTimeTemperatureSetPointAsync(ct);
var humidMeasure = await GetRealTimeHumidityMeasuredValueAsync(ct);
return (tempSet, humidMeasure);
}
}
}
}

View File

@@ -0,0 +1,123 @@
using DeviceCommand.Base;
using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// UMC1000 温湿度控制器(RS485)
/// </summary>
public class UMC1000Rtu : ModbusRtu
{
private readonly byte _slaveId;
/// <summary>
/// 温度测量值 REAL
/// </summary>
private const ushort TemperatureAddress = 150;
/// <summary>
/// 湿度测量值 REAL
/// </summary>
private const ushort HumidityAddress = 152;
public UMC1000Rtu(
byte slaveId,
string portName,
int baudRate = 9600,
int dataBits = 8,
StopBits stopBits = StopBits.One,
Parity parity = Parity.None,
int readTimeout = 3000,
int writeTimeout = 3000)
{
_slaveId = slaveId;
ConfigureDevice(
portName,
baudRate,
dataBits,
stopBits,
parity,
readTimeout,
writeTimeout);
}
/// <summary>
/// 读取温度
/// </summary>
public async Task<float> ReadTemperatureAsync(
CancellationToken ct = default)
{
return await ReadFloatAsync(
TemperatureAddress,
ct);
}
/// <summary>
/// 读取湿度
/// </summary>
public async Task<float> ReadHumidityAsync(
CancellationToken ct = default)
{
return await ReadFloatAsync(
HumidityAddress,
ct);
}
/// <summary>
/// 一次读取温湿度
/// </summary>
public async Task<(float Temperature, float Humidity)> ReadAllAsync(
CancellationToken ct = default)
{
ushort[] regs = await ReadHoldingRegistersAsync(
_slaveId,
TemperatureAddress,
4,
ct);
return
(
ToFloat(regs[0], regs[1]),
ToFloat(regs[2], regs[3])
);
}
private async Task<float> ReadFloatAsync(
ushort address,
CancellationToken ct)
{
ushort[] regs = await ReadHoldingRegistersAsync(
_slaveId,
address,
2,
ct);
return ToFloat(regs[0], regs[1]);
}
/// <summary>
/// 两个寄存器转Float
/// </summary>
private static float ToFloat(
ushort highWord,
ushort lowWord)
{
byte[] bytes =
{
(byte)(highWord >> 8),
(byte)highWord,
(byte)(lowWord >> 8),
(byte)lowWord
};
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
}
}

View File

@@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
public class SDE710FH_A : ModbusTcp
public class UMC1300 : ModbusTcp
{
// 从站地址(设备 ID
private readonly byte _slaveId;
@@ -27,7 +27,7 @@ namespace DeviceCommand.Devices
/// <param name="port">端口,默认 502</param>
/// <param name="sendTimeoutMs">发送超时(毫秒)</param>
/// <param name="receiveTimeoutMs">接收超时(毫秒)</param>
public SDE710FH_A(byte slaveId, string ip, int port = 502,
public UMC1300(byte slaveId, string ip, int port = 502,
int sendTimeoutMs = 3000, int receiveTimeoutMs = 3000)
: base()
{
@@ -82,21 +82,5 @@ namespace DeviceCommand.Devices
raw[3] * SCALE // 湿度 MV
);
}
// ==================== 写入 ====================
/// <summary>设定温度 MV工程值</summary>
public async Task WriteTemperatureMVAsync(float value, CancellationToken ct = default)
{
ushort raw = (ushort)(value / SCALE);
await WriteSingleRegisterAsync(_slaveId, ADDR_TEMP_MV, raw, ct);
}
/// <summary>设定湿度 MV工程值</summary>
public async Task WriteHumidityMVAsync(float value, CancellationToken ct = default)
{
ushort raw = (ushort)(value / SCALE);
await WriteSingleRegisterAsync(_slaveId, ADDR_HUMID_MV, raw, ct);
}
}
}