Files
IOT/DeviceCommand/Devices/THC-1100-602A.cs

378 lines
14 KiB
C#
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DeviceCommand.Base;
using S7.Net;
using System;
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
{
#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>
/// 重写连接方法,确保使用正确的默认参数连接
/// </summary>
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)
{
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;
}
}
public async Task<float> GetRealTimeHumidityMeasuredValueAsync(CancellationToken ct = default)
{
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;
}
}
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 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
}
}