添加配置连接窗口已经相关S7与ModbusTCP连接功能
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using NModbus;
|
||||
using NModbus;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
@@ -38,20 +38,48 @@ namespace DeviceCommand.Base
|
||||
await _commLock.WaitAsync(ct);
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 开始连接 - IP: {IPAddress}, 端口: {Port}");
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 超时设置 - 发送: {SendTimeout}ms, 接收: {ReceiveTimeout}ms");
|
||||
|
||||
if (_tcpClient.Connected)
|
||||
{
|
||||
var remoteEndPoint = (IPEndPoint)_tcpClient.Client.RemoteEndPoint!;
|
||||
if (remoteEndPoint.Address.MapToIPv4().ToString() == IPAddress && remoteEndPoint.Port == Port)
|
||||
string currentIp = remoteEndPoint.Address.MapToIPv4().ToString();
|
||||
int currentPort = remoteEndPoint.Port;
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 已有连接: {currentIp}:{currentPort}");
|
||||
|
||||
if (currentIp == IPAddress && currentPort == Port)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 参数匹配,复用现有连接");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 参数不匹配,需要重新连接");
|
||||
}
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 关闭并释放旧连接");
|
||||
_tcpClient.Close();
|
||||
_tcpClient.Dispose();
|
||||
_tcpClient = new TcpClient();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 调用 ConnectAsync({IPAddress}, {Port})");
|
||||
await _tcpClient.ConnectAsync(IPAddress, Port, ct);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 创建ModbusMaster");
|
||||
Modbus = new ModbusFactory().CreateMaster(_tcpClient);
|
||||
return true;
|
||||
|
||||
bool isConnected = _tcpClient.Connected;
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 连接结果: {(isConnected ? "成功" : "失败")}");
|
||||
return isConnected;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 连接异常: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 异常类型: {ex.GetType().Name}");
|
||||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 异常堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using S7.Net;
|
||||
using S7.Net;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
@@ -9,7 +9,6 @@ namespace DeviceCommand.Base
|
||||
{
|
||||
public class S7Device : IS7Device
|
||||
{
|
||||
// 保持和你一致的连接参数命名属性
|
||||
public string IPAddress { get; private set; } = "127.0.0.1";
|
||||
public CpuType CpuType { get; private set; } = CpuType.S71200;
|
||||
public short Rack { get; private set; } = 0;
|
||||
@@ -20,21 +19,15 @@ namespace DeviceCommand.Base
|
||||
private Plc _plc;
|
||||
public Plc PlcContext => _plc;
|
||||
|
||||
// S7.Net 的 Plc.IsConnected 属性内部会通过 Socket 状态进行判断
|
||||
public bool IsConnected => _plc?.IsConnected ?? false;
|
||||
|
||||
// 统一线程锁
|
||||
protected readonly SemaphoreSlim _commLock = new(1, 1);
|
||||
|
||||
public S7Device()
|
||||
{
|
||||
// 初始化默认配置
|
||||
_plc = new Plc(CpuType, IPAddress, Rack, Slot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备参数配置(符合你的命名风格)
|
||||
/// </summary>
|
||||
public void ConfigureDevice(string ipAddress, CpuType cpuType, short rack = 0, short slot = 1, int sendTimeout = 3000, int receiveTimeout = 3000)
|
||||
{
|
||||
IPAddress = ipAddress;
|
||||
@@ -50,32 +43,30 @@ namespace DeviceCommand.Base
|
||||
await _commLock.WaitAsync(ct);
|
||||
try
|
||||
{
|
||||
// 如果已经连接,检查当前的 IP 和 CPU 类型是否一致,一致则直接复用
|
||||
if (_plc != null && _plc.IsConnected)
|
||||
{
|
||||
if (_plc.IP == IPAddress && _plc.CPU == CpuType && _plc.Rack == Rack && _plc.Slot == Slot)
|
||||
return true;
|
||||
}
|
||||
|
||||
// 修复:释放并彻底清空旧连接实例
|
||||
if (_plc != null)
|
||||
{
|
||||
_plc.Close();
|
||||
}
|
||||
|
||||
// 重新实例化 Plc 对象并配置超时
|
||||
_plc = new Plc(CpuType, IPAddress, Rack, Slot)
|
||||
{
|
||||
ReadTimeout = ReceiveTimeout,
|
||||
WriteTimeout = SendTimeout
|
||||
};
|
||||
|
||||
// 部分版本 S7.Net 的 OpenAsync 本身不接受 CancellationToken,我们通过 WaitAsync 实现超时
|
||||
await _plc.OpenAsync().WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
|
||||
await _plc.OpenAsync();
|
||||
return _plc.IsConnected;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[S7Device] 连接异常: IP={IPAddress}, CPU={CpuType}, Error={ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[S7Device] 异常堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
@@ -125,7 +116,22 @@ namespace DeviceCommand.Base
|
||||
public async Task<T> ReadAsync<T>(string address, CancellationToken ct = default)
|
||||
{
|
||||
var result = await ReadAsync(address, ct);
|
||||
return (T)result;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync<T>] 地址={address}, 返回类型={result?.GetType().Name ?? "null"}, 值={result}");
|
||||
|
||||
try
|
||||
{
|
||||
if (result is IConvertible convertible)
|
||||
{
|
||||
return (T)Convert.ChangeType(convertible, typeof(T));
|
||||
}
|
||||
return (T)result;
|
||||
}
|
||||
catch (InvalidCastException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync<T>] 类型转换失败: 目标类型={typeof(T).Name}, 实际类型={result.GetType().Name}, 值={result}, 异常={ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<byte[]> ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count, CancellationToken ct = default)
|
||||
|
||||
@@ -1,49 +1,377 @@
|
||||
using DeviceCommand.Base;
|
||||
using DeviceCommand.Base;
|
||||
using S7.Net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// THC-1100-602A 恒温恒湿试验箱驱动 (S7通讯协议)
|
||||
/// 基于西门子PLC S7-1200/S7-1500系列
|
||||
/// </summary>
|
||||
public class THC1100 : S7Device
|
||||
{
|
||||
// 定义 PLC 地址常量,便于后期维护
|
||||
#region THC设备默认连接参数
|
||||
private const string DefaultIpAddress = "192.168.0.1";
|
||||
private const CpuType DefaultCpuType = CpuType.S71200;
|
||||
private const short DefaultRack = 0;
|
||||
private const short DefaultSlot = 1;
|
||||
private const int DefaultSendTimeout = 3000;
|
||||
private const int DefaultReceiveTimeout = 3000;
|
||||
#endregion
|
||||
|
||||
#region 寄存器地址定义
|
||||
// ========== 主控界面相关 (DB53) ==========
|
||||
private const string RealTimeTemperatureSetPointAddress = "DB53.DBD280";
|
||||
private const string RealTimeHumidityMeasuredValueAddress = "DB53.DBD1092";
|
||||
private const string RealTimeTemperatureMeasuredValueAddress = "DB53.DBD1052";
|
||||
private const string OperationModeAddress = "DB53.DBW2";
|
||||
private const string TestTypeAddress = "DB53.DBW4";
|
||||
private const string CurrentStepAddress = "DB53.DBW6";
|
||||
private const string TotalStepsAddress = "DB53.DBW8";
|
||||
private const string LoopCountAddress = "DB53.DBW10";
|
||||
private const string RunTimeAddress = "DB53.DBD12";
|
||||
private const string StepTimeAddress = "DB53.DBD16";
|
||||
private const string SystemStatusAddress = "DB53.DBW20";
|
||||
|
||||
// ========== 超温保护相关 (DB53) ==========
|
||||
private const string OverTempHighLimitAddress = "DB53.DBD284";
|
||||
private const string OverTempLowLimitAddress = "DB53.DBD288";
|
||||
private const string OverTempEnableAddress = "DB53.DBX292.0";
|
||||
|
||||
// ========== 报警相关 (DB53) ==========
|
||||
private const string AlarmStatusAddress = "DB53.DBW293";
|
||||
private const string AlarmCodeAddress = "DB53.DBW295";
|
||||
private const string AlarmCountAddress = "DB53.DBW297";
|
||||
|
||||
// ========== 控制指令 (DB53) ==========
|
||||
private const string ControlCommandAddress = "DB53.DBW300";
|
||||
|
||||
// ========== 程序步骤参数 (DB54) ==========
|
||||
private const string StepTemperatureBaseAddress = "DB54.DBD";
|
||||
private const string StepTimeBaseAddress = "DB54.DBD";
|
||||
#endregion
|
||||
|
||||
#region 运行模式枚举
|
||||
public enum OperationMode
|
||||
{
|
||||
Stop = 0,
|
||||
Running = 1,
|
||||
Paused = 2
|
||||
}
|
||||
#endregion
|
||||
|
||||
public THC1100() : base()
|
||||
{
|
||||
// 使用THC设备的默认参数配置
|
||||
ConfigureDevice(
|
||||
ipAddress: DefaultIpAddress,
|
||||
cpuType: DefaultCpuType,
|
||||
rack: DefaultRack,
|
||||
slot: DefaultSlot,
|
||||
sendTimeout: DefaultSendTimeout,
|
||||
receiveTimeout: DefaultReceiveTimeout
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取温度实时测量值 (DB53.DBD1052, 浮点型)
|
||||
/// 重写连接方法,确保使用正确的默认参数连接
|
||||
/// </summary>
|
||||
/// <param name="ct">取消令牌</param>
|
||||
/// <returns>温度设定值 (float)</returns>
|
||||
public override async Task<bool> ConnectAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(IPAddress))
|
||||
{
|
||||
ConfigureDevice(
|
||||
ipAddress: DefaultIpAddress,
|
||||
cpuType: DefaultCpuType,
|
||||
rack: DefaultRack,
|
||||
slot: DefaultSlot,
|
||||
sendTimeout: DefaultSendTimeout,
|
||||
receiveTimeout: DefaultReceiveTimeout
|
||||
);
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 连接参数: IP={IPAddress}, CPU={CpuType}, Rack={Rack}, Slot={Slot}");
|
||||
|
||||
bool result = await base.ConnectAsync(ct);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 连接失败: IP={IPAddress}, CPU={CpuType}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region 基础测量方法
|
||||
public async Task<float> GetRealTimeTemperatureSetPointAsync(CancellationToken ct = default)
|
||||
{
|
||||
// S7.Net 读取浮点型时,可以直接将其强转为 float
|
||||
// 内部已通过基类的 _commLock 保证线程安全
|
||||
return await ReadAsync<float>(RealTimeTemperatureSetPointAddress, ct);
|
||||
try
|
||||
{
|
||||
byte[] data = await ReadBytesAsync(DataType.DataBlock, 53, 280, 4, ct);
|
||||
float value = ByteArrayToFloat(data);
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取温度设定值(DB53.DBD280): {value}");
|
||||
return value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取温度设定值失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
try
|
||||
{
|
||||
byte[] data = await ReadBytesAsync(DataType.DataBlock, 53, 1092, 4, ct);
|
||||
float value = ByteArrayToFloat(data);
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取湿度测量值(DB53.DBD1092): {value}");
|
||||
return value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取湿度测量值失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一次性获取温湿度相关的运行数据包(选填,若需要批量展示可以使用此方法)
|
||||
/// </summary>
|
||||
public async Task<(float TemperatureSetPoint, float HumidityMeasuredValue)> GetThcDataPackAsync(CancellationToken ct = default)
|
||||
public async Task<float> GetRealTimeTemperatureMeasuredValueAsync(CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] data = await ReadBytesAsync(DataType.DataBlock, 53, 1052, 4, ct);
|
||||
float value = ByteArrayToFloat(data);
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取温度测量值(DB53.DBD1052): {value}");
|
||||
return value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[THC1100] 读取温度测量值失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private float ByteArrayToFloat(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length < 4)
|
||||
throw new ArgumentException("数据长度不足");
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
byte[] reversed = (byte[])data.Clone();
|
||||
Array.Reverse(reversed);
|
||||
return BitConverter.ToSingle(reversed, 0);
|
||||
}
|
||||
return BitConverter.ToSingle(data, 0);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 运行控制方法
|
||||
public async Task<OperationMode> GetOperationModeAsync(CancellationToken ct = default)
|
||||
{
|
||||
var value = await ReadAsync<ushort>(OperationModeAddress, ct);
|
||||
return (OperationMode)value;
|
||||
}
|
||||
|
||||
public async Task<int> GetTestTypeAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(TestTypeAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetTestTypeAsync(int testType, CancellationToken ct = default)
|
||||
{
|
||||
if (testType < 0 || testType > 65535)
|
||||
throw new ArgumentOutOfRangeException(nameof(testType), "试验类型必须在0-65535范围内");
|
||||
|
||||
await WriteAsync(TestTypeAddress, (ushort)testType, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetCurrentStepAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(CurrentStepAddress, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalStepsAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(TotalStepsAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetTotalStepsAsync(int steps, CancellationToken ct = default)
|
||||
{
|
||||
if (steps < 1 || steps > 999)
|
||||
throw new ArgumentOutOfRangeException(nameof(steps), "步数必须在1-999之间");
|
||||
|
||||
await WriteAsync(TotalStepsAddress, (ushort)steps, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetLoopCountAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(LoopCountAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetLoopCountAsync(int count, CancellationToken ct = default)
|
||||
{
|
||||
if (count < 0 || count > 999)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "循环次数必须在0-999之间");
|
||||
|
||||
await WriteAsync(LoopCountAddress, (ushort)count, ct);
|
||||
}
|
||||
|
||||
public async Task<float> GetRunTimeAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<float>(RunTimeAddress, ct);
|
||||
}
|
||||
|
||||
public async Task<float> GetStepTimeAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<float>(StepTimeAddress, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetSystemStatusAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(SystemStatusAddress, ct);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 超温保护方法
|
||||
public async Task<float> GetOverTempHighLimitAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<float>(OverTempHighLimitAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetOverTempHighLimitAsync(float temperature, CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(OverTempHighLimitAddress, temperature, ct);
|
||||
}
|
||||
|
||||
public async Task<float> GetOverTempLowLimitAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<float>(OverTempLowLimitAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetOverTempLowLimitAsync(float temperature, CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(OverTempLowLimitAddress, temperature, ct);
|
||||
}
|
||||
|
||||
public async Task<bool> GetOverTempEnableAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<bool>(OverTempEnableAddress, ct);
|
||||
}
|
||||
|
||||
public async Task SetOverTempEnableAsync(bool enable, CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(OverTempEnableAddress, enable, ct);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 报警相关方法
|
||||
public async Task<int> GetAlarmStatusAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(AlarmStatusAddress, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetAlarmCodeAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(AlarmCodeAddress, ct);
|
||||
}
|
||||
|
||||
public async Task<int> GetAlarmCountAsync(CancellationToken ct = default)
|
||||
{
|
||||
return await ReadAsync<ushort>(AlarmCountAddress, ct);
|
||||
}
|
||||
|
||||
public async Task ClearAlarmAsync(CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x08, ct);
|
||||
await Task.Delay(100, ct);
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x00, ct);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 控制指令方法
|
||||
public async Task StartDeviceAsync(CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x01, ct);
|
||||
await Task.Delay(100, ct);
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x00, ct);
|
||||
}
|
||||
|
||||
public async Task StopDeviceAsync(CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x02, ct);
|
||||
await Task.Delay(100, ct);
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x00, ct);
|
||||
}
|
||||
|
||||
public async Task ResetDeviceAsync(CancellationToken ct = default)
|
||||
{
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x04, ct);
|
||||
await Task.Delay(100, ct);
|
||||
await WriteAsync(ControlCommandAddress, (ushort)0x00, ct);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 程序步骤方法
|
||||
public async Task SetStepTemperatureAsync(int step, float temperature, CancellationToken ct = default)
|
||||
{
|
||||
if (step < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(step), "步骤编号必须大于0");
|
||||
|
||||
string address = $"{StepTemperatureBaseAddress}{(step - 1) * 12}";
|
||||
await WriteAsync(address, temperature, ct);
|
||||
}
|
||||
|
||||
public async Task<float> GetStepTemperatureAsync(int step, CancellationToken ct = default)
|
||||
{
|
||||
if (step < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(step), "步骤编号必须大于0");
|
||||
|
||||
string address = $"{StepTemperatureBaseAddress}{(step - 1) * 12}";
|
||||
return await ReadAsync<float>(address, ct);
|
||||
}
|
||||
|
||||
public async Task SetStepTimeAsync(int step, float duration, CancellationToken ct = default)
|
||||
{
|
||||
if (step < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(step), "步骤编号必须大于0");
|
||||
if (duration < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(duration), "时间不能为负数");
|
||||
|
||||
string address = $"{StepTimeBaseAddress}{(step - 1) * 12 + 8}";
|
||||
await WriteAsync(address, duration, ct);
|
||||
}
|
||||
|
||||
public async Task<float> GetStepTimeAsync(int step, CancellationToken ct = default)
|
||||
{
|
||||
if (step < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(step), "步骤编号必须大于0");
|
||||
|
||||
string address = $"{StepTimeBaseAddress}{(step - 1) * 12 + 8}";
|
||||
return await ReadAsync<float>(address, ct);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 数据打包方法
|
||||
public async Task<(float TemperatureSetPoint, float TemperatureMeasured, float HumidityMeasured)> GetThcDataPackAsync(CancellationToken ct = default)
|
||||
{
|
||||
var tempSet = await GetRealTimeTemperatureSetPointAsync(ct);
|
||||
var humidMeasure = await GetRealTimeHumidityMeasuredValueAsync(ct);
|
||||
return (tempSet, humidMeasure);
|
||||
var tempMeas = await GetRealTimeTemperatureMeasuredValueAsync(ct);
|
||||
var humidMeas = await GetRealTimeHumidityMeasuredValueAsync(ct);
|
||||
return (tempSet, tempMeas, humidMeas);
|
||||
}
|
||||
|
||||
public async Task<(OperationMode Mode, int CurrentStep, int TotalSteps, int LoopCount, float RunTime)> GetRunStatusPackAsync(CancellationToken ct = default)
|
||||
{
|
||||
var mode = await GetOperationModeAsync(ct);
|
||||
var currentStep = await GetCurrentStepAsync(ct);
|
||||
var totalSteps = await GetTotalStepsAsync(ct);
|
||||
var loopCount = await GetLoopCountAsync(ct);
|
||||
var runTime = await GetRunTimeAsync(ct);
|
||||
return (mode, currentStep, totalSteps, loopCount, runTime);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,15 @@ namespace DeviceCommand.Devices
|
||||
ConfigureDevice(ip, port, sendTimeoutMs, receiveTimeoutMs);
|
||||
}
|
||||
|
||||
public override async Task<bool> ConnectAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return await base.ConnectAsync(ct);
|
||||
}
|
||||
|
||||
// ==================== 读取单个值 ====================
|
||||
|
||||
/// <summary>读取温度 PV(工程值)</summary>
|
||||
|
||||
Reference in New Issue
Block a user