diff --git a/DeviceCommand/Base/ModbusTcp.cs b/DeviceCommand/Base/ModbusTcp.cs
index 8db68f3..3f28c10 100644
--- a/DeviceCommand/Base/ModbusTcp.cs
+++ b/DeviceCommand/Base/ModbusTcp.cs
@@ -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
{
diff --git a/DeviceCommand/Base/S7Device.cs b/DeviceCommand/Base/S7Device.cs
index 6a9d672..c59ff58 100644
--- a/DeviceCommand/Base/S7Device.cs
+++ b/DeviceCommand/Base/S7Device.cs
@@ -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);
}
- ///
- /// 设备参数配置(符合你的命名风格)
- ///
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 ReadAsync(string address, CancellationToken ct = default)
{
var result = await ReadAsync(address, ct);
- return (T)result;
+
+ System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync] 地址={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] 类型转换失败: 目标类型={typeof(T).Name}, 实际类型={result.GetType().Name}, 值={result}, 异常={ex.Message}");
+ throw;
+ }
}
public async Task ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count, CancellationToken ct = default)
diff --git a/DeviceCommand/Devices/THC-1100-602A.cs b/DeviceCommand/Devices/THC-1100-602A.cs
index 81b0333..5d1d968 100644
--- a/DeviceCommand/Devices/THC-1100-602A.cs
+++ b/DeviceCommand/Devices/THC-1100-602A.cs
@@ -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
{
+ ///
+ /// THC-1100-602A 恒温恒湿试验箱驱动 (S7通讯协议)
+ /// 基于西门子PLC S7-1200/S7-1500系列
+ ///
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
+ );
+ }
///
- /// 异步获取温度实时测量值 (DB53.DBD1052, 浮点型)
+ /// 重写连接方法,确保使用正确的默认参数连接
///
- /// 取消令牌
- /// 温度设定值 (float)
+ 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)
{
- // S7.Net 读取浮点型时,可以直接将其强转为 float
- // 内部已通过基类的 _commLock 保证线程安全
- return await ReadAsync(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;
+ }
}
- ///
- /// 异步获取湿度实时测量值 (DB53.DBD1092, 浮点型)
- ///
- /// 取消令牌
- /// 湿度测量值 (float)
public async Task GetRealTimeHumidityMeasuredValueAsync(CancellationToken ct = default)
{
- return await ReadAsync(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;
+ }
}
- ///
- /// 一次性获取温湿度相关的运行数据包(选填,若需要批量展示可以使用此方法)
- ///
- public async Task<(float TemperatureSetPoint, float HumidityMeasuredValue)> GetThcDataPackAsync(CancellationToken ct = default)
+ 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 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
}
-}
\ No newline at end of file
+}
diff --git a/DeviceCommand/Devices/UMC1300.cs b/DeviceCommand/Devices/UMC1300.cs
index 292d12b..119ac3c 100644
--- a/DeviceCommand/Devices/UMC1300.cs
+++ b/DeviceCommand/Devices/UMC1300.cs
@@ -35,6 +35,15 @@ namespace DeviceCommand.Devices
ConfigureDevice(ip, port, sendTimeoutMs, receiveTimeoutMs);
}
+ public override async Task ConnectAsync(CancellationToken ct = default)
+ {
+ if (IsConnected)
+ {
+ return true;
+ }
+ return await base.ConnectAsync(ct);
+ }
+
// ==================== 读取单个值 ====================
/// 读取温度 PV(工程值)
diff --git a/LOT/ViewModels/ShellViewModel.cs b/LOT/ViewModels/ShellViewModel.cs
index ce03771..646d21c 100644
--- a/LOT/ViewModels/ShellViewModel.cs
+++ b/LOT/ViewModels/ShellViewModel.cs
@@ -1,14 +1,4 @@
-using LOT.Views;
-using MaterialDesignThemes.Wpf;
-using Notifications.Wpf.Core;
-using Prism.Events;
-using Prism.Ioc;
-using Prism.Modularity;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Notifications.Wpf.Core;
using System.Windows;
using System.Windows.Input;
using UIShare.PubEvent;
diff --git a/MainModule/Events/ConnectionConfigEvent.cs b/MainModule/Events/ConnectionConfigEvent.cs
new file mode 100644
index 0000000..8be73c8
--- /dev/null
+++ b/MainModule/Events/ConnectionConfigEvent.cs
@@ -0,0 +1,35 @@
+using Prism.Events;
+using S7.Net;
+using System.IO.Ports;
+
+namespace MainModule.Events
+{
+ public enum ProtocolType
+ {
+ S7,
+ ModbusTCP,
+ ModbusRTU,
+ TCP,
+ HTTP
+ }
+
+ public class ConnectionConfigEvent : PubSubEvent
+ {
+ public class ConnectionConfigData
+ {
+ public ProtocolType Protocol { get; set; }
+ public string IpAddress { get; set; } = string.Empty;
+ public int Port { get; set; }
+ public string SerialPort { get; set; } = string.Empty;
+ public int BaudRate { get; set; }
+ public Parity Parity { get; set; }
+ public int DataBits { get; set; }
+ public StopBits StopBits { get; set; }
+ public CpuType CpuType { get; set; }
+ public short Rack { get; set; }
+ public short Slot { get; set; }
+ public int SendTimeout { get; set; }
+ public int ReceiveTimeout { get; set; }
+ }
+ }
+}
diff --git a/MainModule/MainModule.cs b/MainModule/MainModule.cs
index 80581dd..8e596cd 100644
--- a/MainModule/MainModule.cs
+++ b/MainModule/MainModule.cs
@@ -1,4 +1,6 @@
+using MainModule.ViewModels;
using MainModule.Views;
+using Prism.Ioc;
using System.Reflection;
namespace MainModule
diff --git a/MainModule/MainModule.csproj b/MainModule/MainModule.csproj
index 9902add..d334cd2 100644
--- a/MainModule/MainModule.csproj
+++ b/MainModule/MainModule.csproj
@@ -8,6 +8,7 @@
+
diff --git a/MainModule/ViewModels/ConnectionConfigViewModel.cs b/MainModule/ViewModels/ConnectionConfigViewModel.cs
new file mode 100644
index 0000000..19d0ca1
--- /dev/null
+++ b/MainModule/ViewModels/ConnectionConfigViewModel.cs
@@ -0,0 +1,237 @@
+using MainModule.Events;
+using Prism.Commands;
+using Prism.Mvvm;
+using S7.Net;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Windows;
+
+namespace MainModule.ViewModels
+{
+ public class ConnectionConfigViewModel : BindableBase
+ {
+ #region 私有字段
+ private Events.ProtocolType _selectedProtocol = Events.ProtocolType.S7;
+ private string _ipAddress = "127.0.0.1";
+ private int _port = 102;
+ private string _serialPort = "COM1";
+ private int _baudRate = 9600;
+ private Parity _parity = Parity.None;
+ private int _dataBits = 8;
+ private StopBits _stopBits = StopBits.One;
+ private CpuType _cpuType = CpuType.S71200;
+ private short _rack = 0;
+ private short _slot = 1;
+ private int _sendTimeout = 3000;
+ private int _receiveTimeout = 5000;
+ #endregion
+
+ #region 公共属性
+ public Events.ProtocolType SelectedProtocol
+ {
+ get => _selectedProtocol;
+ set
+ {
+ if (SetProperty(ref _selectedProtocol, value))
+ {
+ RaisePropertyChanged(nameof(IsS7Protocol));
+ RaisePropertyChanged(nameof(IsModbusTCPProtocol));
+ RaisePropertyChanged(nameof(IsModbusRTUProtocol));
+ RaisePropertyChanged(nameof(IsTCPProtocol));
+ RaisePropertyChanged(nameof(IsHTTPProtocol));
+ }
+ }
+ }
+
+ public string IpAddress
+ {
+ get => _ipAddress;
+ set => SetProperty(ref _ipAddress, value);
+ }
+
+ public int Port
+ {
+ get => _port;
+ set => SetProperty(ref _port, value);
+ }
+
+ public string SerialPort
+ {
+ get => _serialPort;
+ set => SetProperty(ref _serialPort, value);
+ }
+
+ public int BaudRate
+ {
+ get => _baudRate;
+ set => SetProperty(ref _baudRate, value);
+ }
+
+ public Parity Parity
+ {
+ get => _parity;
+ set => SetProperty(ref _parity, value);
+ }
+
+ public int DataBits
+ {
+ get => _dataBits;
+ set => SetProperty(ref _dataBits, value);
+ }
+
+ public StopBits StopBits
+ {
+ get => _stopBits;
+ set => SetProperty(ref _stopBits, value);
+ }
+
+ public CpuType CpuType
+ {
+ get => _cpuType;
+ set => SetProperty(ref _cpuType, value);
+ }
+
+ public short Rack
+ {
+ get => _rack;
+ set => SetProperty(ref _rack, value);
+ }
+
+ public short Slot
+ {
+ get => _slot;
+ set => SetProperty(ref _slot, value);
+ }
+
+ public int SendTimeout
+ {
+ get => _sendTimeout;
+ set => SetProperty(ref _sendTimeout, value);
+ }
+
+ public int ReceiveTimeout
+ {
+ get => _receiveTimeout;
+ set => SetProperty(ref _receiveTimeout, value);
+ }
+ #endregion
+
+ #region 协议类型判断属性
+ public bool IsS7Protocol => SelectedProtocol == Events.ProtocolType.S7;
+ public bool IsModbusTCPProtocol => SelectedProtocol == Events.ProtocolType.ModbusTCP;
+ public bool IsModbusRTUProtocol => SelectedProtocol == Events.ProtocolType.ModbusRTU;
+ public bool IsTCPProtocol => SelectedProtocol == Events.ProtocolType.TCP;
+ public bool IsHTTPProtocol => SelectedProtocol == Events.ProtocolType.HTTP;
+ #endregion
+
+ #region 下拉列表数据源
+ public List CpuTypes { get; } = new List
+ {
+ CpuType.S71200,
+ CpuType.S71500,
+ CpuType.S7200,
+ CpuType.S7300,
+ CpuType.S7400,
+ CpuType.S7200Smart
+ };
+
+ public List BaudRates { get; } = new List
+ {
+ 9600,
+ 19200,
+ 38400,
+ 57600,
+ 115200
+ };
+
+ public List DataBitsList { get; } = new List { 7, 8 };
+
+ public List ParityList { get; } = new List
+ {
+ Parity.None,
+ Parity.Odd,
+ Parity.Even,
+ Parity.Mark,
+ Parity.Space
+ };
+
+ public List StopBitsList { get; } = new List
+ {
+ StopBits.None,
+ StopBits.One,
+ StopBits.OnePointFive,
+ StopBits.Two
+ };
+
+ public List SerialPorts { get; } = new List
+ {
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8"
+ };
+ #endregion
+
+ #region 命令
+ public DelegateCommand ConfirmCommand { get; }
+ public DelegateCommand CancelCommand { get; }
+ public DelegateCommand SelectS7Command { get; }
+ public DelegateCommand SelectModbusTCPCommand { get; }
+ public DelegateCommand SelectModbusRTUCommand { get; }
+ public DelegateCommand SelectTCPCommand { get; }
+ public DelegateCommand SelectHTTPCommand { get; }
+ #endregion
+
+ #region 配置数据(供外部获取)
+ public ConnectionConfigEvent.ConnectionConfigData ConnectionConfigData { get; private set; }
+ #endregion
+
+ public ConnectionConfigViewModel()
+ {
+ ConfirmCommand = new DelegateCommand(ExecuteConfirm);
+ CancelCommand = new DelegateCommand(ExecuteCancel);
+ SelectS7Command = new DelegateCommand(() => SelectedProtocol = Events.ProtocolType.S7);
+ SelectModbusTCPCommand = new DelegateCommand(() => SelectedProtocol = Events.ProtocolType.ModbusTCP);
+ SelectModbusRTUCommand = new DelegateCommand(() => SelectedProtocol = Events.ProtocolType.ModbusRTU);
+ SelectTCPCommand = new DelegateCommand(() => SelectedProtocol = Events.ProtocolType.TCP);
+ SelectHTTPCommand = new DelegateCommand(() => SelectedProtocol = Events.ProtocolType.HTTP);
+ }
+
+ #region 命令执行
+ private void ExecuteConfirm()
+ {
+ ConnectionConfigData = new ConnectionConfigEvent.ConnectionConfigData
+ {
+ Protocol = SelectedProtocol,
+ IpAddress = IpAddress,
+ Port = Port,
+ SerialPort = SerialPort,
+ BaudRate = BaudRate,
+ Parity = Parity,
+ DataBits = DataBits,
+ StopBits = StopBits,
+ CpuType = CpuType,
+ Rack = Rack,
+ Slot = Slot,
+ SendTimeout = SendTimeout,
+ ReceiveTimeout = ReceiveTimeout
+ };
+
+ var window = Application.Current.Windows.OfType().FirstOrDefault();
+ if (window != null)
+ {
+ window.DialogResult = true;
+ window.Close();
+ }
+ }
+
+ private void ExecuteCancel()
+ {
+ ConnectionConfigData = null;
+ var window = Application.Current.Windows.OfType().FirstOrDefault();
+ if (window != null)
+ {
+ window.DialogResult = false;
+ window.Close();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/MainModule/ViewModels/MainViewModel.cs b/MainModule/ViewModels/MainViewModel.cs
index 8b0056e..4524d52 100644
--- a/MainModule/ViewModels/MainViewModel.cs
+++ b/MainModule/ViewModels/MainViewModel.cs
@@ -1,78 +1,749 @@
-using Model.Entity;
+using DeviceCommand.Base;
+using DeviceCommand.Devices;
+using MainModule.Events;
+using MainModule.Views;
+using Model.Entity;
using Prism.Commands;
+using Prism.Events;
using Prism.Ioc;
+using Prism.Navigation;
using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Threading;
using UIShare.ViewModelBase;
namespace MainModule.ViewModels
{
public class MainViewModel : NavigateViewModelBase
{
+ private readonly THC1100 _thc1100 = new();
+ private readonly UMC1300 _umc1300;
public ObservableCollection Chambers { get; } = new();
private IContainerProvider _containerProvider;
+ private CancellationTokenSource _refreshCts;
+ private DispatcherTimer _refreshTimer;
+ private bool _isInitialized = false;
+
+ #region 按钮文本和连接状态
+ private string _connectButtonText = "连接设备";
+ public string ConnectButtonText
+ {
+ get => _connectButtonText;
+ set => SetProperty(ref _connectButtonText, value);
+ }
+
+ private bool _isConnected = false;
+ public bool IsConnected
+ {
+ get => _isConnected;
+ set
+ {
+ if (SetProperty(ref _isConnected, value))
+ {
+ ConnectButtonText = value ? "断开连接" : "连接设备";
+ }
+ }
+ }
+ #endregion
+
+ #region 命令定义
+ public DelegateCommand ConnectCommand { get; }
+ public DelegateCommand DisconnectCommand { get; }
+ public DelegateCommand RefreshCommand { get; }
+ public DelegateCommand ConfigCommand { get; }
+ public DelegateCommand ConnectDeviceCommand { get; }
+ #endregion
+
+ #region 配置信息
+ private Events.ProtocolType _selectedProtocol = Events.ProtocolType.S7;
+ public Events.ProtocolType SelectedProtocol
+ {
+ get => _selectedProtocol;
+ set => SetProperty(ref _selectedProtocol, value);
+ }
+
+ private string _configIpAddress = "127.0.0.1";
+ public string ConfigIpAddress
+ {
+ get => _configIpAddress;
+ set => SetProperty(ref _configIpAddress, value);
+ }
+
+ private int _configPort = 102;
+ public int ConfigPort
+ {
+ get => _configPort;
+ set => SetProperty(ref _configPort, value);
+ }
+
+ private S7.Net.CpuType _configCpuType = S7.Net.CpuType.S71200;
+ public S7.Net.CpuType ConfigCpuType
+ {
+ get => _configCpuType;
+ set => SetProperty(ref _configCpuType, value);
+ }
+
+ private short _configRack = 0;
+ public short ConfigRack
+ {
+ get => _configRack;
+ set => SetProperty(ref _configRack, value);
+ }
+
+ private short _configSlot = 1;
+ public short ConfigSlot
+ {
+ get => _configSlot;
+ set => SetProperty(ref _configSlot, value);
+ }
+
+ private string _configSerialPort = "COM1";
+ public string ConfigSerialPort
+ {
+ get => _configSerialPort;
+ set => SetProperty(ref _configSerialPort, value);
+ }
+
+ private int _configBaudRate = 9600;
+ public int ConfigBaudRate
+ {
+ get => _configBaudRate;
+ set => SetProperty(ref _configBaudRate, value);
+ }
+
+ private int _configSendTimeout = 3000;
+ public int ConfigSendTimeout
+ {
+ get => _configSendTimeout;
+ set => SetProperty(ref _configSendTimeout, value);
+ }
+
+ private int _configReceiveTimeout = 5000;
+ public int ConfigReceiveTimeout
+ {
+ get => _configReceiveTimeout;
+ set => SetProperty(ref _configReceiveTimeout, value);
+ }
+ #endregion
+
public MainViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_containerProvider = containerProvider;
+
+ // 初始化UMC1300设备(默认从站地址1)
+ _umc1300 = new UMC1300(1, "127.0.0.1", 502);
- // 触发配置与绑定初始化
- ConfigureAndBindChambers();
+ // 初始化命令
+ ConnectCommand = new DelegateCommand(async () => await ExecuteConnectAsync());
+ DisconnectCommand = new DelegateCommand(ExecuteDisconnect);
+ RefreshCommand = new DelegateCommand(async () => await RefreshDeviceDataAsync());
+ ConfigCommand = new DelegateCommand(() => ShowConfigDialogAsync());
+ ConnectDeviceCommand = new DelegateCommand(async (item) => await ExecuteConnectDeviceAsync(item));
}
///
- /// 核心配置与数据绑定逻辑
+ /// 当视图被导航到时执行初始化(Prism导航机制)
///
- private void ConfigureAndBindChambers()
+ public override async void OnNavigatedTo(NavigationContext navigationContext)
{
- // 实例 1:Modbus TCP 环境箱配置
- Chambers.Add(new ChamberMonitorItem
- {
- Id = 1,
- Name = "环境箱 01",
- ProtocolType = "Modbus TCP",
- IsConnected = false,
- Temperature = 0.0,
- Humidity = 0.0
- });
+ base.OnNavigatedTo(navigationContext);
- // 实例 2:Modbus TCP B+ 环境箱配置
- Chambers.Add(new ChamberMonitorItem
- {
- Id = 2,
- Name = "环境箱 02",
- ProtocolType = "Modbus TCP B+",
- IsConnected = false,
- Temperature = 0.0,
- Humidity = 0.0
- });
+ // 避免重复初始化
+ if (_isInitialized)
+ return;
+ _isInitialized = true;
- // 实例 3:西门子 S7 环境箱配置
- Chambers.Add(new ChamberMonitorItem
- {
- Id = 3,
- Name = "环境箱 03",
- ProtocolType = "S7",
- IsConnected = false,
- Temperature = 0.0,
- Humidity = 0.0
- });
-
- // 实例 4:HTTP 环境箱配置
- Chambers.Add(new ChamberMonitorItem
- {
- Id = 4,
- Name = "环境箱 04",
- ProtocolType = "HTTP",
- IsConnected = false,
- Temperature = 0.0,
- Humidity = 0.0
- });
+ await InitializeAsync();
}
+
+ ///
+ /// 异步初始化设备连接和数据
+ ///
+ private async Task InitializeAsync()
+ {
+ try
+ {
+ // 先添加所有卡片(显示初始状态)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ Chambers.Clear();
+ Chambers.Add(new ChamberMonitorItem
+ {
+ Id = 1,
+ Name = "环境箱 01",
+ ProtocolType = "Modbus TCP",
+ IsConnected = false,
+ Temperature = 0.0,
+ Humidity = 0.0
+ });
+
+ Chambers.Add(new ChamberMonitorItem
+ {
+ Id = 2,
+ Name = "环境箱 02",
+ ProtocolType = "Modbus TCP B+",
+ IsConnected = false,
+ Temperature = 0.0,
+ Humidity = 0.0
+ });
+
+ Chambers.Add(new ChamberMonitorItem
+ {
+ Id = 3,
+ Name = "环境箱 03",
+ ProtocolType = "S7",
+ IsConnected = false,
+ Temperature = 0.0,
+ Humidity = 0.0
+ });
+
+ Chambers.Add(new ChamberMonitorItem
+ {
+ Id = 4,
+ Name = "环境箱 04",
+ ProtocolType = "HTTP",
+ IsConnected = false,
+ Temperature = 0.0,
+ Humidity = 0.0
+ });
+ });
+
+ System.Diagnostics.Debug.WriteLine("设备卡片初始化完成");
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"初始化异常: {ex.Message}");
+ }
+ }
+
+ #region 配置对话框
+ ///
+ /// 显示配置连接对话框
+ ///
+ private void ShowConfigDialogAsync()
+ {
+ try
+ {
+ // 手动创建 ViewModel
+ var viewModel = new ConnectionConfigViewModel();
+
+ // 设置初始值
+ viewModel.SelectedProtocol = SelectedProtocol;
+ viewModel.IpAddress = ConfigIpAddress;
+ viewModel.Port = ConfigPort;
+ viewModel.CpuType = ConfigCpuType;
+ viewModel.Rack = ConfigRack;
+ viewModel.Slot = ConfigSlot;
+ viewModel.SerialPort = ConfigSerialPort;
+ viewModel.BaudRate = ConfigBaudRate;
+ viewModel.SendTimeout = ConfigSendTimeout;
+ viewModel.ReceiveTimeout = ConfigReceiveTimeout;
+
+ // 创建窗口
+ var configWindow = new ConnectionConfigView(viewModel);
+
+ // 设置父窗口
+ configWindow.Owner = Application.Current.MainWindow;
+
+ // 显示模态对话框
+ bool? result = configWindow.ShowDialog();
+
+ // 如果用户点击确定,获取配置数据
+ if (result == true && viewModel.ConnectionConfigData != null)
+ {
+ // 直接更新配置参数
+ SelectedProtocol = viewModel.ConnectionConfigData.Protocol;
+ ConfigIpAddress = viewModel.ConnectionConfigData.IpAddress;
+ ConfigPort = viewModel.ConnectionConfigData.Port;
+ ConfigCpuType = viewModel.ConnectionConfigData.CpuType;
+ ConfigRack = viewModel.ConnectionConfigData.Rack;
+ ConfigSlot = viewModel.ConnectionConfigData.Slot;
+ ConfigSerialPort = viewModel.ConnectionConfigData.SerialPort;
+ ConfigBaudRate = viewModel.ConnectionConfigData.BaudRate;
+ ConfigSendTimeout = viewModel.ConnectionConfigData.SendTimeout;
+ ConfigReceiveTimeout = viewModel.ConnectionConfigData.ReceiveTimeout;
+
+ System.Diagnostics.Debug.WriteLine($"配置更新成功: 协议={SelectedProtocol}, IP={ConfigIpAddress}, 端口={ConfigPort}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"打开配置对话框异常: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 配置更新事件处理
+ ///
+ private void OnConfigUpdated(ConnectionConfigEvent.ConnectionConfigData config)
+ {
+ SelectedProtocol = config.Protocol;
+ ConfigIpAddress = config.IpAddress;
+ ConfigPort = config.Port;
+ ConfigCpuType = config.CpuType;
+ ConfigRack = config.Rack;
+ ConfigSlot = config.Slot;
+ ConfigSerialPort = config.SerialPort;
+ ConfigBaudRate = config.BaudRate;
+ ConfigSendTimeout = config.SendTimeout;
+ ConfigReceiveTimeout = config.ReceiveTimeout;
+
+ System.Diagnostics.Debug.WriteLine($"配置更新: 协议={SelectedProtocol}, IP={ConfigIpAddress}, 端口={ConfigPort}");
+ }
+ #endregion
+
+ #region 连接命令实现
+ ///
+ /// 执行连接/断开切换命令
+ ///
+ private async Task ExecuteConnectAsync()
+ {
+ if (IsConnected)
+ {
+ // 如果已连接,则断开
+ ExecuteDisconnect();
+ }
+ else
+ {
+ // 如果未连接,则连接
+ await ConnectToDeviceAsync();
+ }
+ }
+
+ ///
+ /// 连接到THC设备
+ ///
+ private async Task ConnectToDeviceAsync()
+ {
+ try
+ {
+ System.Diagnostics.Debug.WriteLine("正在连接THC设备...");
+
+ // 使用配置对话框中设置的参数配置设备
+ _thc1100.ConfigureDevice(
+ ipAddress: ConfigIpAddress,
+ cpuType: ConfigCpuType,
+ rack: ConfigRack,
+ slot: ConfigSlot
+ );
+ System.Diagnostics.Debug.WriteLine($"配置参数: 协议={SelectedProtocol}, IP={_thc1100.IPAddress}, CPU={_thc1100.CpuType}, Rack={_thc1100.Rack}, Slot={_thc1100.Slot}");
+
+ // 连接THC设备
+ bool connected = await _thc1100.ConnectAsync();
+
+ if (connected)
+ {
+ IsConnected = true;
+ System.Windows.MessageBox.Show("THC设备连接成功", "连接成功", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information);
+
+ // 更新环境箱03的连接状态
+ var chamber3 = GetChamberById(3);
+ if (chamber3 != null)
+ {
+ chamber3.IsConnected = true;
+ }
+
+ // 读取初始数据
+ await RefreshDeviceDataAsync();
+
+ // 启动定时刷新(每1秒)
+ StartPeriodicRefresh();
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("THC设备连接失败");
+ MessageBox.Show("设备连接失败,请检查网络和设备状态!", "连接失败",
+ MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"连接异常: {ex.Message}");
+ MessageBox.Show($"连接异常: {ex.Message}", "错误",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ ///
+ /// 断开THC设备连接
+ ///
+ private void ExecuteDisconnect()
+ {
+ try
+ {
+ // 停止定时器
+ _refreshCts?.Cancel();
+ _refreshTimer?.Stop();
+
+ // 关闭设备连接
+ _thc1100.Close();
+
+ // 更新连接状态
+ IsConnected = false;
+
+ // 更新环境箱03的连接状态
+ var chamber3 = GetChamberById(3);
+ if (chamber3 != null)
+ {
+ chamber3.IsConnected = false;
+ }
+
+ System.Diagnostics.Debug.WriteLine("THC设备已断开连接");
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"断开连接异常: {ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 定时刷新
+ ///
+ /// 启动定时刷新
+ ///
+ private void StartPeriodicRefresh()
+ {
+ _refreshCts?.Cancel();
+ _refreshCts = new CancellationTokenSource();
+
+ _refreshTimer?.Stop();
+ _refreshTimer = new DispatcherTimer
+ {
+ Interval = TimeSpan.FromSeconds(1)
+ };
+
+ _refreshTimer.Tick += async (s, e) =>
+ {
+ try
+ {
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] Tick触发,当前时间: {DateTime.Now:HH:mm:ss.fff}");
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] THC1100.IsConnected = {_thc1100.IsConnected}");
+
+ // 检查THC1100连接状态
+ if (_thc1100.IsConnected)
+ {
+ var chamber3 = GetChamberById(3);
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] chamber3 = {chamber3}, IsConnected = {chamber3?.IsConnected}");
+
+ if (chamber3 != null)
+ {
+ System.Diagnostics.Debug.WriteLine("[定时刷新] 开始读取温湿度数据...");
+ try
+ {
+ var temp = await _thc1100.GetRealTimeTemperatureSetPointAsync();
+ var humidity = await _thc1100.GetRealTimeHumidityMeasuredValueAsync();
+
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] 读取完成 - 温度(DB53.DBD280): {temp}, 湿度(DB53.DBD1092): {humidity}");
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ chamber3.Temperature = temp;
+ chamber3.Humidity = humidity;
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] UI已更新 - Temperature={chamber3.Temperature}, Humidity={chamber3.Humidity}");
+ });
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] 读取温湿度数据异常: {ex.Message}");
+ }
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("[定时刷新] THC1100未连接,跳过数据读取");
+ }
+
+ // 检查UMC1300连接状态
+ if (_umc1300.IsConnected)
+ {
+ var chamber1 = GetChamberById(1);
+ if (chamber1 != null)
+ {
+ try
+ {
+ var temp = await _umc1300.ReadTemperaturePVAsync();
+ var humidity = await _umc1300.ReadHumidityPVAsync();
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ chamber1.Temperature = temp;
+ chamber1.Humidity = humidity;
+ });
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] 读取UMC1300数据异常: {ex.Message}");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[定时刷新] 定时器异常: {ex.Message}");
+ }
+ };
+
+ _refreshTimer.Start();
+ }
+
+ ///
+ /// 停止定时刷新
+ ///
+ private void StopPeriodicRefresh()
+ {
+ _refreshCts?.Cancel();
+ _refreshTimer?.Stop();
+ }
+ #endregion
+
+ #region 数据刷新
+ ///
+ /// 刷新设备数据
+ ///
+ private async Task RefreshDeviceDataAsync()
+ {
+ try
+ {
+ // 如果未连接,不刷新数据
+ if (!_thc1100.IsConnected)
+ return;
+
+ var chamber3 = GetChamberById(3);
+ if (chamber3 == null) return;
+
+ // 读取温湿度数据(使用用户指定的寄存器地址)
+ // 温度: DB53.DBD280, 湿度: DB53.DBD1092
+ var temp = await _thc1100.GetRealTimeTemperatureSetPointAsync();
+ var humidity = await _thc1100.GetRealTimeHumidityMeasuredValueAsync();
+
+ System.Diagnostics.Debug.WriteLine($"[初始刷新] 环境箱3 - 温度(DB53.DBD280): {temp}, 湿度(DB53.DBD1092): {humidity}");
+
+ // 更新UI(在UI线程执行)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ chamber3.Temperature = temp;
+ chamber3.Humidity = humidity;
+ });
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"刷新数据异常: {ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 辅助方法
+ ///
+ /// 根据ID获取卡片
+ ///
+ private ChamberMonitorItem GetChamberById(int id)
+ {
+ foreach (var chamber in Chambers)
+ {
+ if (chamber.Id == id)
+ return chamber;
+ }
+ return null;
+ }
+
+ ///
+ /// 根据设备类型连接单个设备
+ ///
+ private async Task ExecuteConnectDeviceAsync(ChamberMonitorItem item)
+ {
+ if (item == null) return;
+
+ try
+ {
+ if (item.IsConnected)
+ {
+ // 断开连接
+ System.Diagnostics.Debug.WriteLine($"[连接] 正在断开设备 {item.Name}");
+
+ // 根据设备自己的协议类型调用对应设备的Close方法
+ switch (item.ProtocolType)
+ {
+ case "S7":
+ _thc1100.Close();
+ break;
+ case "Modbus TCP":
+ _umc1300.Close();
+ break;
+ }
+
+ // 停止数据刷新
+ StopPeriodicRefresh();
+
+ item.IsConnected = false;
+ System.Diagnostics.Debug.WriteLine($"[连接] 设备 {item.Name} 已断开连接");
+ }
+ else
+ {
+ // 输出当前配置参数
+ System.Diagnostics.Debug.WriteLine($"[连接] ========== 开始连接设备 {item.Name} ==========");
+ System.Diagnostics.Debug.WriteLine($"[连接] 当前协议类型: {SelectedProtocol}");
+ System.Diagnostics.Debug.WriteLine($"[连接] IP地址: {ConfigIpAddress}");
+ System.Diagnostics.Debug.WriteLine($"[连接] 端口: {ConfigPort}");
+ System.Diagnostics.Debug.WriteLine($"[连接] CPU类型: {ConfigCpuType}");
+ System.Diagnostics.Debug.WriteLine($"[连接] Rack: {ConfigRack}, Slot: {ConfigSlot}");
+ System.Diagnostics.Debug.WriteLine($"[连接] 串口: {ConfigSerialPort}, 波特率: {ConfigBaudRate}");
+ System.Diagnostics.Debug.WriteLine($"[连接] 发送超时: {ConfigSendTimeout}ms, 接收超时: {ConfigReceiveTimeout}ms");
+
+ // 根据设备自己的协议类型连接设备
+ bool connected = false;
+ System.Diagnostics.Debug.WriteLine($"[连接] 设备协议类型: {item.ProtocolType}");
+
+ switch (item.ProtocolType)
+ {
+ case "S7":
+ // S7协议连接
+ System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的S7协议");
+ connected = await ConnectS7DeviceAsync(item);
+ break;
+ case "Modbus TCP":
+ // Modbus TCP连接
+ System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的Modbus TCP协议");
+ connected = await ConnectModbusTcpDeviceAsync(item);
+ break;
+ case "Modbus TCP B+":
+ // Modbus TCP B+连接
+ System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的Modbus TCP B+协议");
+ connected = await ConnectModbusTcpDeviceAsync(item);
+ break;
+ case "HTTP":
+ // HTTP连接
+ System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的HTTP协议");
+ connected = await ConnectHttpDeviceAsync(item);
+ break;
+ default:
+ System.Diagnostics.Debug.WriteLine($"[连接] 未知协议类型: {item.ProtocolType}");
+ break;
+ }
+
+ item.IsConnected = connected;
+ System.Diagnostics.Debug.WriteLine($"[连接] 设备 {item.Name} 连接结果: {(connected ? "成功" : "失败")}");
+ System.Diagnostics.Debug.WriteLine($"[连接] ========== 连接尝试结束 ==========");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[连接] 设备 {item.Name} 连接异常: {ex.Message}");
+ System.Diagnostics.Debug.WriteLine($"[连接] 异常堆栈: {ex.StackTrace}");
+ item.IsConnected = false;
+ }
+ }
+
+ ///
+ /// S7协议设备连接
+ ///
+ private async Task ConnectS7DeviceAsync(ChamberMonitorItem item)
+ {
+ try
+ {
+ System.Diagnostics.Debug.WriteLine($"[ConnectS7DeviceAsync] 开始配置设备: IP={ConfigIpAddress}, CPU={ConfigCpuType}, Rack={ConfigRack}, Slot={ConfigSlot}");
+
+ _thc1100.ConfigureDevice(ConfigIpAddress, ConfigCpuType, ConfigRack, ConfigSlot);
+ bool result = await _thc1100.ConnectAsync();
+
+ System.Diagnostics.Debug.WriteLine($"[ConnectS7DeviceAsync] 连接结果: {result}, IsConnected属性值: {_thc1100.IsConnected}");
+
+ if (result)
+ {
+ System.Diagnostics.Debug.WriteLine("[ConnectS7DeviceAsync] 连接成功,启动定时刷新");
+ StartPeriodicRefresh();
+ }
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[ConnectS7DeviceAsync] 异常: {ex.Message}, 堆栈: {ex.StackTrace}");
+ return false;
+ }
+ }
+
+ ///
+ /// Modbus TCP设备连接
+ ///
+ private async Task ConnectModbusTcpDeviceAsync(ChamberMonitorItem item)
+ {
+ try
+ {
+ // 配置UMC1300设备
+ _umc1300.ConfigureDevice(ConfigIpAddress, ConfigPort, ConfigSendTimeout, ConfigReceiveTimeout);
+ bool result = await _umc1300.ConnectAsync();
+
+ if (result)
+ {
+ // 启动数据刷新
+ StartPeriodicRefresh();
+ }
+
+ System.Diagnostics.Debug.WriteLine($"UMC1300 Modbus TCP连接: {ConfigIpAddress}:{ConfigPort}, 结果: {result}");
+ return result;
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"UMC1300连接异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Modbus RTU设备连接
+ ///
+ private async Task ConnectModbusRtuDeviceAsync(ChamberMonitorItem item)
+ {
+ // 实现Modbus RTU连接逻辑
+ System.Diagnostics.Debug.WriteLine($"Modbus RTU连接: {ConfigSerialPort}, 波特率:{ConfigBaudRate}");
+ await Task.Delay(500);
+ return true;
+ }
+
+ ///
+ /// TCP设备连接
+ ///
+ private async Task ConnectTcpDeviceAsync(ChamberMonitorItem item)
+ {
+ // 实现TCP连接逻辑
+ System.Diagnostics.Debug.WriteLine($"TCP连接: {ConfigIpAddress}:{ConfigPort}");
+ await Task.Delay(500);
+ return true;
+ }
+
+ ///
+ /// HTTP设备连接
+ ///
+ private async Task ConnectHttpDeviceAsync(ChamberMonitorItem item)
+ {
+ // 实现HTTP连接逻辑
+ System.Diagnostics.Debug.WriteLine($"HTTP连接: {ConfigIpAddress}:{ConfigPort}");
+ await Task.Delay(500);
+ return true;
+ }
+ #endregion
+
+ #region 生命周期管理
+ ///
+ /// 导航离开时清理资源
+ ///
+ public override void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ base.OnNavigatedFrom(navigationContext);
+
+ ExecuteDisconnect();
+ _isInitialized = false;
+ }
+ #endregion
}
///
/// 环境箱 UI 状态及温湿度数据绑定模型
///
- public class ChamberMonitorItem :BindableBase
+ public class ChamberMonitorItem : BindableBase
{
private double _temperature;
private double _humidity;
@@ -106,7 +777,18 @@ namespace MainModule.ViewModels
public bool IsConnected
{
get => _isConnected;
- set => SetProperty(ref _isConnected, value);
+ set
+ {
+ if (SetProperty(ref _isConnected, value))
+ {
+ RaisePropertyChanged(nameof(ConnectButtonText));
+ }
+ }
}
+
+ ///
+ /// 连接按钮文本
+ ///
+ public string ConnectButtonText => IsConnected ? "断开连接" : "连接设备";
}
-}
\ No newline at end of file
+}
diff --git a/MainModule/Views/ConnectionConfigView.xaml b/MainModule/Views/ConnectionConfigView.xaml
new file mode 100644
index 0000000..f3e896c
--- /dev/null
+++ b/MainModule/Views/ConnectionConfigView.xaml
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MainModule/Views/ConnectionConfigView.xaml.cs b/MainModule/Views/ConnectionConfigView.xaml.cs
new file mode 100644
index 0000000..aed0308
--- /dev/null
+++ b/MainModule/Views/ConnectionConfigView.xaml.cs
@@ -0,0 +1,18 @@
+using MainModule.ViewModels;
+using System.Windows;
+
+namespace MainModule.Views
+{
+ public partial class ConnectionConfigView : Window
+ {
+ public ConnectionConfigView()
+ {
+ InitializeComponent();
+ }
+
+ public ConnectionConfigView(ConnectionConfigViewModel viewModel) : this()
+ {
+ DataContext = viewModel;
+ }
+ }
+}
diff --git a/MainModule/Views/MainView.xaml b/MainModule/Views/MainView.xaml
index 412d16d..2a5fa88 100644
--- a/MainModule/Views/MainView.xaml
+++ b/MainModule/Views/MainView.xaml
@@ -1,4 +1,4 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -40,6 +78,7 @@
+
@@ -95,10 +134,30 @@
+
+
+
+
+
-
\ No newline at end of file
+