添加配置连接窗口已经相关S7与ModbusTCP连接功能
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
using NModbus;
|
using NModbus;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
@@ -38,20 +38,48 @@ namespace DeviceCommand.Base
|
|||||||
await _commLock.WaitAsync(ct);
|
await _commLock.WaitAsync(ct);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 开始连接 - IP: {IPAddress}, 端口: {Port}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 超时设置 - 发送: {SendTimeout}ms, 接收: {ReceiveTimeout}ms");
|
||||||
|
|
||||||
if (_tcpClient.Connected)
|
if (_tcpClient.Connected)
|
||||||
{
|
{
|
||||||
var remoteEndPoint = (IPEndPoint)_tcpClient.Client.RemoteEndPoint!;
|
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;
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 参数不匹配,需要重新连接");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 关闭并释放旧连接");
|
||||||
_tcpClient.Close();
|
_tcpClient.Close();
|
||||||
_tcpClient.Dispose();
|
_tcpClient.Dispose();
|
||||||
_tcpClient = new TcpClient();
|
_tcpClient = new TcpClient();
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 调用 ConnectAsync({IPAddress}, {Port})");
|
||||||
await _tcpClient.ConnectAsync(IPAddress, Port, ct);
|
await _tcpClient.ConnectAsync(IPAddress, Port, ct);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 创建ModbusMaster");
|
||||||
Modbus = new ModbusFactory().CreateMaster(_tcpClient);
|
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
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using S7.Net;
|
using S7.Net;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -9,7 +9,6 @@ namespace DeviceCommand.Base
|
|||||||
{
|
{
|
||||||
public class S7Device : IS7Device
|
public class S7Device : IS7Device
|
||||||
{
|
{
|
||||||
// 保持和你一致的连接参数命名属性
|
|
||||||
public string IPAddress { get; private set; } = "127.0.0.1";
|
public string IPAddress { get; private set; } = "127.0.0.1";
|
||||||
public CpuType CpuType { get; private set; } = CpuType.S71200;
|
public CpuType CpuType { get; private set; } = CpuType.S71200;
|
||||||
public short Rack { get; private set; } = 0;
|
public short Rack { get; private set; } = 0;
|
||||||
@@ -20,21 +19,15 @@ namespace DeviceCommand.Base
|
|||||||
private Plc _plc;
|
private Plc _plc;
|
||||||
public Plc PlcContext => _plc;
|
public Plc PlcContext => _plc;
|
||||||
|
|
||||||
// S7.Net 的 Plc.IsConnected 属性内部会通过 Socket 状态进行判断
|
|
||||||
public bool IsConnected => _plc?.IsConnected ?? false;
|
public bool IsConnected => _plc?.IsConnected ?? false;
|
||||||
|
|
||||||
// 统一线程锁
|
|
||||||
protected readonly SemaphoreSlim _commLock = new(1, 1);
|
protected readonly SemaphoreSlim _commLock = new(1, 1);
|
||||||
|
|
||||||
public S7Device()
|
public S7Device()
|
||||||
{
|
{
|
||||||
// 初始化默认配置
|
|
||||||
_plc = new Plc(CpuType, IPAddress, Rack, Slot);
|
_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)
|
public void ConfigureDevice(string ipAddress, CpuType cpuType, short rack = 0, short slot = 1, int sendTimeout = 3000, int receiveTimeout = 3000)
|
||||||
{
|
{
|
||||||
IPAddress = ipAddress;
|
IPAddress = ipAddress;
|
||||||
@@ -50,32 +43,30 @@ namespace DeviceCommand.Base
|
|||||||
await _commLock.WaitAsync(ct);
|
await _commLock.WaitAsync(ct);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 如果已经连接,检查当前的 IP 和 CPU 类型是否一致,一致则直接复用
|
|
||||||
if (_plc != null && _plc.IsConnected)
|
if (_plc != null && _plc.IsConnected)
|
||||||
{
|
{
|
||||||
if (_plc.IP == IPAddress && _plc.CPU == CpuType && _plc.Rack == Rack && _plc.Slot == Slot)
|
if (_plc.IP == IPAddress && _plc.CPU == CpuType && _plc.Rack == Rack && _plc.Slot == Slot)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修复:释放并彻底清空旧连接实例
|
|
||||||
if (_plc != null)
|
if (_plc != null)
|
||||||
{
|
{
|
||||||
_plc.Close();
|
_plc.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新实例化 Plc 对象并配置超时
|
|
||||||
_plc = new Plc(CpuType, IPAddress, Rack, Slot)
|
_plc = new Plc(CpuType, IPAddress, Rack, Slot)
|
||||||
{
|
{
|
||||||
ReadTimeout = ReceiveTimeout,
|
ReadTimeout = ReceiveTimeout,
|
||||||
WriteTimeout = SendTimeout
|
WriteTimeout = SendTimeout
|
||||||
};
|
};
|
||||||
|
|
||||||
// 部分版本 S7.Net 的 OpenAsync 本身不接受 CancellationToken,我们通过 WaitAsync 实现超时
|
await _plc.OpenAsync();
|
||||||
await _plc.OpenAsync().WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
|
|
||||||
return _plc.IsConnected;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -125,7 +116,22 @@ namespace DeviceCommand.Base
|
|||||||
public async Task<T> ReadAsync<T>(string address, CancellationToken ct = default)
|
public async Task<T> ReadAsync<T>(string address, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var result = await ReadAsync(address, ct);
|
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)
|
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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DeviceCommand.Devices
|
namespace DeviceCommand.Devices
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// THC-1100-602A 恒温恒湿试验箱驱动 (S7通讯协议)
|
||||||
|
/// 基于西门子PLC S7-1200/S7-1500系列
|
||||||
|
/// </summary>
|
||||||
public class THC1100 : S7Device
|
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 RealTimeTemperatureSetPointAddress = "DB53.DBD280";
|
||||||
private const string RealTimeHumidityMeasuredValueAddress = "DB53.DBD1092";
|
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>
|
||||||
/// 异步获取温度实时测量值 (DB53.DBD1052, 浮点型)
|
/// 重写连接方法,确保使用正确的默认参数连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ct">取消令牌</param>
|
public override async Task<bool> ConnectAsync(CancellationToken ct = default)
|
||||||
/// <returns>温度设定值 (float)</returns>
|
{
|
||||||
|
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)
|
public async Task<float> GetRealTimeTemperatureSetPointAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
// S7.Net 读取浮点型时,可以直接将其强转为 float
|
try
|
||||||
// 内部已通过基类的 _commLock 保证线程安全
|
{
|
||||||
return await ReadAsync<float>(RealTimeTemperatureSetPointAddress, ct);
|
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)
|
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>
|
public async Task<float> GetRealTimeTemperatureMeasuredValueAsync(CancellationToken ct = default)
|
||||||
/// 一次性获取温湿度相关的运行数据包(选填,若需要批量展示可以使用此方法)
|
{
|
||||||
/// </summary>
|
try
|
||||||
public async Task<(float TemperatureSetPoint, float HumidityMeasuredValue)> GetThcDataPackAsync(CancellationToken ct = default)
|
{
|
||||||
|
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 tempSet = await GetRealTimeTemperatureSetPointAsync(ct);
|
||||||
var humidMeasure = await GetRealTimeHumidityMeasuredValueAsync(ct);
|
var tempMeas = await GetRealTimeTemperatureMeasuredValueAsync(ct);
|
||||||
return (tempSet, humidMeasure);
|
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);
|
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>
|
/// <summary>读取温度 PV(工程值)</summary>
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
using LOT.Views;
|
using Notifications.Wpf.Core;
|
||||||
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 System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using UIShare.PubEvent;
|
using UIShare.PubEvent;
|
||||||
|
|||||||
35
MainModule/Events/ConnectionConfigEvent.cs
Normal file
35
MainModule/Events/ConnectionConfigEvent.cs
Normal file
@@ -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<ConnectionConfigEvent.ConnectionConfigData>
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using MainModule.ViewModels;
|
||||||
using MainModule.Views;
|
using MainModule.Views;
|
||||||
|
using Prism.Ioc;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace MainModule
|
namespace MainModule
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
|
||||||
<ProjectReference Include="..\Model\Model.csproj" />
|
<ProjectReference Include="..\Model\Model.csproj" />
|
||||||
<ProjectReference Include="..\Service\Service.csproj" />
|
<ProjectReference Include="..\Service\Service.csproj" />
|
||||||
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
<ProjectReference Include="..\UIShare\UIShare.csproj" />
|
||||||
|
|||||||
237
MainModule/ViewModels/ConnectionConfigViewModel.cs
Normal file
237
MainModule/ViewModels/ConnectionConfigViewModel.cs
Normal file
@@ -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<CpuType> CpuTypes { get; } = new List<CpuType>
|
||||||
|
{
|
||||||
|
CpuType.S71200,
|
||||||
|
CpuType.S71500,
|
||||||
|
CpuType.S7200,
|
||||||
|
CpuType.S7300,
|
||||||
|
CpuType.S7400,
|
||||||
|
CpuType.S7200Smart
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<int> BaudRates { get; } = new List<int>
|
||||||
|
{
|
||||||
|
9600,
|
||||||
|
19200,
|
||||||
|
38400,
|
||||||
|
57600,
|
||||||
|
115200
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<int> DataBitsList { get; } = new List<int> { 7, 8 };
|
||||||
|
|
||||||
|
public List<Parity> ParityList { get; } = new List<Parity>
|
||||||
|
{
|
||||||
|
Parity.None,
|
||||||
|
Parity.Odd,
|
||||||
|
Parity.Even,
|
||||||
|
Parity.Mark,
|
||||||
|
Parity.Space
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<StopBits> StopBitsList { get; } = new List<StopBits>
|
||||||
|
{
|
||||||
|
StopBits.None,
|
||||||
|
StopBits.One,
|
||||||
|
StopBits.OnePointFive,
|
||||||
|
StopBits.Two
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<string> SerialPorts { get; } = new List<string>
|
||||||
|
{
|
||||||
|
"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<Views.ConnectionConfigView>().FirstOrDefault();
|
||||||
|
if (window != null)
|
||||||
|
{
|
||||||
|
window.DialogResult = true;
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteCancel()
|
||||||
|
{
|
||||||
|
ConnectionConfigData = null;
|
||||||
|
var window = Application.Current.Windows.OfType<Views.ConnectionConfigView>().FirstOrDefault();
|
||||||
|
if (window != null)
|
||||||
|
{
|
||||||
|
window.DialogResult = false;
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Commands;
|
||||||
|
using Prism.Events;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
|
using Prism.Navigation;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
using UIShare.ViewModelBase;
|
using UIShare.ViewModelBase;
|
||||||
|
|
||||||
namespace MainModule.ViewModels
|
namespace MainModule.ViewModels
|
||||||
{
|
{
|
||||||
public class MainViewModel : NavigateViewModelBase
|
public class MainViewModel : NavigateViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly THC1100 _thc1100 = new();
|
||||||
|
private readonly UMC1300 _umc1300;
|
||||||
public ObservableCollection<ChamberMonitorItem> Chambers { get; } = new();
|
public ObservableCollection<ChamberMonitorItem> Chambers { get; } = new();
|
||||||
private IContainerProvider _containerProvider;
|
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<ChamberMonitorItem> 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)
|
public MainViewModel(IContainerProvider containerProvider) : base(containerProvider)
|
||||||
{
|
{
|
||||||
_containerProvider = 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<ChamberMonitorItem>(async (item) => await ExecuteConnectDeviceAsync(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 核心配置与数据绑定逻辑
|
/// 当视图被导航到时执行初始化(Prism导航机制)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ConfigureAndBindChambers()
|
public override async void OnNavigatedTo(NavigationContext navigationContext)
|
||||||
{
|
{
|
||||||
// 实例 1:Modbus TCP 环境箱配置
|
base.OnNavigatedTo(navigationContext);
|
||||||
Chambers.Add(new ChamberMonitorItem
|
|
||||||
{
|
|
||||||
Id = 1,
|
|
||||||
Name = "环境箱 01",
|
|
||||||
ProtocolType = "Modbus TCP",
|
|
||||||
IsConnected = false,
|
|
||||||
Temperature = 0.0,
|
|
||||||
Humidity = 0.0
|
|
||||||
});
|
|
||||||
|
|
||||||
// 实例 2:Modbus TCP B+ 环境箱配置
|
// 避免重复初始化
|
||||||
Chambers.Add(new ChamberMonitorItem
|
if (_isInitialized)
|
||||||
{
|
return;
|
||||||
Id = 2,
|
_isInitialized = true;
|
||||||
Name = "环境箱 02",
|
|
||||||
ProtocolType = "Modbus TCP B+",
|
|
||||||
IsConnected = false,
|
|
||||||
Temperature = 0.0,
|
|
||||||
Humidity = 0.0
|
|
||||||
});
|
|
||||||
|
|
||||||
// 实例 3:西门子 S7 环境箱配置
|
await InitializeAsync();
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步初始化设备连接和数据
|
||||||
|
/// </summary>
|
||||||
|
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 配置对话框
|
||||||
|
/// <summary>
|
||||||
|
/// 显示配置连接对话框
|
||||||
|
/// </summary>
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置更新事件处理
|
||||||
|
/// </summary>
|
||||||
|
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 连接命令实现
|
||||||
|
/// <summary>
|
||||||
|
/// 执行连接/断开切换命令
|
||||||
|
/// </summary>
|
||||||
|
private async Task ExecuteConnectAsync()
|
||||||
|
{
|
||||||
|
if (IsConnected)
|
||||||
|
{
|
||||||
|
// 如果已连接,则断开
|
||||||
|
ExecuteDisconnect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果未连接,则连接
|
||||||
|
await ConnectToDeviceAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接到THC设备
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 断开THC设备连接
|
||||||
|
/// </summary>
|
||||||
|
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 定时刷新
|
||||||
|
/// <summary>
|
||||||
|
/// 启动定时刷新
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止定时刷新
|
||||||
|
/// </summary>
|
||||||
|
private void StopPeriodicRefresh()
|
||||||
|
{
|
||||||
|
_refreshCts?.Cancel();
|
||||||
|
_refreshTimer?.Stop();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 数据刷新
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新设备数据
|
||||||
|
/// </summary>
|
||||||
|
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 辅助方法
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取卡片
|
||||||
|
/// </summary>
|
||||||
|
private ChamberMonitorItem GetChamberById(int id)
|
||||||
|
{
|
||||||
|
foreach (var chamber in Chambers)
|
||||||
|
{
|
||||||
|
if (chamber.Id == id)
|
||||||
|
return chamber;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据设备类型连接单个设备
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// S7协议设备连接
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus TCP设备连接
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus RTU设备连接
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> ConnectModbusRtuDeviceAsync(ChamberMonitorItem item)
|
||||||
|
{
|
||||||
|
// 实现Modbus RTU连接逻辑
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Modbus RTU连接: {ConfigSerialPort}, 波特率:{ConfigBaudRate}");
|
||||||
|
await Task.Delay(500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TCP设备连接
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> ConnectTcpDeviceAsync(ChamberMonitorItem item)
|
||||||
|
{
|
||||||
|
// 实现TCP连接逻辑
|
||||||
|
System.Diagnostics.Debug.WriteLine($"TCP连接: {ConfigIpAddress}:{ConfigPort}");
|
||||||
|
await Task.Delay(500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP设备连接
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> ConnectHttpDeviceAsync(ChamberMonitorItem item)
|
||||||
|
{
|
||||||
|
// 实现HTTP连接逻辑
|
||||||
|
System.Diagnostics.Debug.WriteLine($"HTTP连接: {ConfigIpAddress}:{ConfigPort}");
|
||||||
|
await Task.Delay(500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 生命周期管理
|
||||||
|
/// <summary>
|
||||||
|
/// 导航离开时清理资源
|
||||||
|
/// </summary>
|
||||||
|
public override void OnNavigatedFrom(NavigationContext navigationContext)
|
||||||
|
{
|
||||||
|
base.OnNavigatedFrom(navigationContext);
|
||||||
|
|
||||||
|
ExecuteDisconnect();
|
||||||
|
_isInitialized = false;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 环境箱 UI 状态及温湿度数据绑定模型
|
/// 环境箱 UI 状态及温湿度数据绑定模型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChamberMonitorItem :BindableBase
|
public class ChamberMonitorItem : BindableBase
|
||||||
{
|
{
|
||||||
private double _temperature;
|
private double _temperature;
|
||||||
private double _humidity;
|
private double _humidity;
|
||||||
@@ -106,7 +777,18 @@ namespace MainModule.ViewModels
|
|||||||
public bool IsConnected
|
public bool IsConnected
|
||||||
{
|
{
|
||||||
get => _isConnected;
|
get => _isConnected;
|
||||||
set => SetProperty(ref _isConnected, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isConnected, value))
|
||||||
|
{
|
||||||
|
RaisePropertyChanged(nameof(ConnectButtonText));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接按钮文本
|
||||||
|
/// </summary>
|
||||||
|
public string ConnectButtonText => IsConnected ? "断开连接" : "连接设备";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
285
MainModule/Views/ConnectionConfigView.xaml
Normal file
285
MainModule/Views/ConnectionConfigView.xaml
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
<Window x:Class="MainModule.Views.ConnectionConfigView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
|
||||||
|
Title="设备连接配置"
|
||||||
|
Width="520"
|
||||||
|
Height="550"
|
||||||
|
MinWidth="400"
|
||||||
|
MinHeight="450"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ResizeMode="CanResize">
|
||||||
|
<Window.Resources>
|
||||||
|
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||||
|
<Style TargetType="TextBlock" x:Key="LabelStyle">
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="Foreground" Value="#4A5568"/>
|
||||||
|
<Setter Property="Margin" Value="0,6,0,2"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="TextBox" x:Key="InputStyle">
|
||||||
|
<Setter Property="Padding" Value="8"/>
|
||||||
|
<Setter Property="Margin" Value="0,2,0,6"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#E2E8F0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="ComboBox" x:Key="ComboStyle">
|
||||||
|
<Setter Property="Padding" Value="8"/>
|
||||||
|
<Setter Property="Margin" Value="0,2,0,6"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#E2E8F0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid Background="White">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Text="设备连接配置" FontSize="18" FontWeight="Bold" Foreground="#2C3E50" Margin="20,20,20,16"/>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Margin="20">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="选择协议类型" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,2,0,6">
|
||||||
|
<Button Content="S7"
|
||||||
|
Command="{Binding SelectS7Command}"
|
||||||
|
Width="80" Height="36"
|
||||||
|
Margin="0,0,4,0"/>
|
||||||
|
<Button Content="Modbus TCP"
|
||||||
|
Command="{Binding SelectModbusTCPCommand}"
|
||||||
|
Width="100" Height="36"
|
||||||
|
Margin="0,0,4,0"/>
|
||||||
|
<Button Content="Modbus RTU"
|
||||||
|
Command="{Binding SelectModbusRTUCommand}"
|
||||||
|
Width="100" Height="36"
|
||||||
|
Margin="0,0,4,0"/>
|
||||||
|
<Button Content="TCP"
|
||||||
|
Command="{Binding SelectTCPCommand}"
|
||||||
|
Width="80" Height="36"
|
||||||
|
Margin="0,0,4,0"/>
|
||||||
|
<Button Content="HTTP"
|
||||||
|
Command="{Binding SelectHTTPCommand}"
|
||||||
|
Width="80" Height="36"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Border Visibility="{Binding IsS7Protocol, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="S7协议配置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="IP地址" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding IpAddress}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="CPU类型" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding CpuTypes}"
|
||||||
|
SelectedItem="{Binding CpuType}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="Rack" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Rack}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="Slot" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Slot}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Visibility="{Binding IsModbusTCPProtocol, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Modbus TCP配置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="IP地址" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding IpAddress}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="端口号" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Port}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Visibility="{Binding IsModbusRTUProtocol, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Modbus RTU配置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="串口" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding SerialPorts}"
|
||||||
|
SelectedItem="{Binding SerialPort}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="波特率" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding BaudRates}"
|
||||||
|
SelectedItem="{Binding BaudRate}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="数据位" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding DataBitsList}"
|
||||||
|
SelectedItem="{Binding DataBits}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="校验位" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding ParityList}"
|
||||||
|
SelectedItem="{Binding Parity}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="停止位" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<ComboBox Grid.Column="1" ItemsSource="{Binding StopBitsList}"
|
||||||
|
SelectedItem="{Binding StopBits}" Style="{StaticResource ComboStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Visibility="{Binding IsTCPProtocol, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="TCP配置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="IP地址" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding IpAddress}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="端口号" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Port}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Visibility="{Binding IsHTTPProtocol, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="HTTP配置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="IP地址" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding IpAddress}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="端口号" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Port}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border BorderBrush="#E2E8F0" BorderThickness="1" CornerRadius="8" Padding="12" Margin="0,0,0,12">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="超时设置" FontSize="14" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="发送超时(ms)" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding SendTimeout}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="接收超时(ms)" Style="{StaticResource LabelStyle}"/>
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding ReceiveTimeout}" Style="{StaticResource InputStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="20,16,20,20">
|
||||||
|
<Button Content="取消"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Width="80" Height="36"
|
||||||
|
Margin="0,0,8,0"/>
|
||||||
|
<Button Content="确定"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Width="80" Height="36"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
18
MainModule/Views/ConnectionConfigView.xaml.cs
Normal file
18
MainModule/Views/ConnectionConfigView.xaml.cs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<UserControl x:Class="MainModule.Views.MainView"
|
<UserControl x:Class="MainModule.Views.MainView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
@@ -21,7 +21,45 @@
|
|||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Text="环境箱监控状态面板" FontSize="20" FontWeight="Bold" Foreground="#2C3E50" Margin="12,4,0,16"/>
|
<DockPanel Grid.Row="0" Margin="12,4,12,16">
|
||||||
|
<TextBlock Text="环境箱监控状态面板" FontSize="20" FontWeight="Bold" Foreground="#2C3E50" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<!-- 顶部工具栏按钮 -->
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<!-- 配置连接按钮 -->
|
||||||
|
<Button Content="配置连接"
|
||||||
|
Command="{Binding ConfigCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
Foreground="#805AD5"
|
||||||
|
FontSize="13"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
ToolTip="配置设备连接参数"/>
|
||||||
|
|
||||||
|
<!-- 连接/断开按钮 -->
|
||||||
|
<Button Content="{Binding ConnectButtonText}"
|
||||||
|
Command="{Binding ConnectCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
Foreground="#3182CE"
|
||||||
|
FontSize="13"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
ToolTip="点击连接/断开设备">
|
||||||
|
<Button.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="连接设备" Command="{Binding ConnectCommand}"/>
|
||||||
|
<MenuItem Header="断开连接" Command="{Binding DisconnectCommand}"/>
|
||||||
|
</ContextMenu>
|
||||||
|
</Button.ContextMenu>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- 刷新按钮 -->
|
||||||
|
<Button Content="刷新数据"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
Foreground="#38A169"
|
||||||
|
FontSize="13"
|
||||||
|
ToolTip="手动刷新设备数据"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
<ItemsControl Grid.Row="1" ItemsSource="{Binding Chambers}">
|
<ItemsControl Grid.Row="1" ItemsSource="{Binding Chambers}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
@@ -40,6 +78,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<DockPanel Grid.Row="0" LastChildFill="False">
|
<DockPanel Grid.Row="0" LastChildFill="False">
|
||||||
@@ -95,10 +134,30 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
|
|
||||||
|
<!-- 连接按钮 -->
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,16,0,0">
|
||||||
|
<Button Content="{Binding ConnectButtonText}"
|
||||||
|
Command="{Binding DataContext.ConnectDeviceCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
Width="100"
|
||||||
|
Height="36">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button" BasedOn="{StaticResource MaterialDesignFlatButton}">
|
||||||
|
<Setter Property="Foreground" Value="#3182CE"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsConnected}" Value="True">
|
||||||
|
<Setter Property="Foreground" Value="#E53E3E"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
Reference in New Issue
Block a user