using DeviceCommand.Base; using S7.Net; using System; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Devices { /// /// THC-1100-602A 恒温恒湿试验箱驱动 (S7通讯协议) /// 基于西门子PLC S7-1200/S7-1500系列 /// 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 ); } /// /// 重写连接方法,确保使用正确的默认参数连接 /// public override async Task 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 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 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 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 GetOperationModeAsync(CancellationToken ct = default) { var value = await ReadAsync(OperationModeAddress, ct); return (OperationMode)value; } public async Task GetTestTypeAsync(CancellationToken ct = default) { return await ReadAsync(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 GetCurrentStepAsync(CancellationToken ct = default) { return await ReadAsync(CurrentStepAddress, ct); } public async Task GetTotalStepsAsync(CancellationToken ct = default) { return await ReadAsync(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 GetLoopCountAsync(CancellationToken ct = default) { return await ReadAsync(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 GetRunTimeAsync(CancellationToken ct = default) { return await ReadAsync(RunTimeAddress, ct); } public async Task GetStepTimeAsync(CancellationToken ct = default) { return await ReadAsync(StepTimeAddress, ct); } public async Task GetSystemStatusAsync(CancellationToken ct = default) { return await ReadAsync(SystemStatusAddress, ct); } #endregion #region 超温保护方法 public async Task GetOverTempHighLimitAsync(CancellationToken ct = default) { return await ReadAsync(OverTempHighLimitAddress, ct); } public async Task SetOverTempHighLimitAsync(float temperature, CancellationToken ct = default) { await WriteAsync(OverTempHighLimitAddress, temperature, ct); } public async Task GetOverTempLowLimitAsync(CancellationToken ct = default) { return await ReadAsync(OverTempLowLimitAddress, ct); } public async Task SetOverTempLowLimitAsync(float temperature, CancellationToken ct = default) { await WriteAsync(OverTempLowLimitAddress, temperature, ct); } public async Task GetOverTempEnableAsync(CancellationToken ct = default) { return await ReadAsync(OverTempEnableAddress, ct); } public async Task SetOverTempEnableAsync(bool enable, CancellationToken ct = default) { await WriteAsync(OverTempEnableAddress, enable, ct); } #endregion #region 报警相关方法 public async Task GetAlarmStatusAsync(CancellationToken ct = default) { return await ReadAsync(AlarmStatusAddress, ct); } public async Task GetAlarmCodeAsync(CancellationToken ct = default) { return await ReadAsync(AlarmCodeAddress, ct); } public async Task GetAlarmCountAsync(CancellationToken ct = default) { return await ReadAsync(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 GetStepTemperatureAsync(int step, CancellationToken ct = default) { if (step < 1) throw new ArgumentOutOfRangeException(nameof(step), "步骤编号必须大于0"); string address = $"{StepTemperatureBaseAddress}{(step - 1) * 12}"; return await ReadAsync(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 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(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 } }