diff --git a/SunlightCentralizedControlManagement_SCCM_.csproj b/SunlightCentralizedControlManagement_SCCM_.csproj
index 8a818e7..bde8f8a 100644
--- a/SunlightCentralizedControlManagement_SCCM_.csproj
+++ b/SunlightCentralizedControlManagement_SCCM_.csproj
@@ -119,6 +119,7 @@
Login.xaml
+
@@ -133,6 +134,7 @@
+
diff --git a/UserClass/AsyncSerialPortClient.cs b/UserClass/AsyncSerialPortClient.cs
new file mode 100644
index 0000000..fa478c0
--- /dev/null
+++ b/UserClass/AsyncSerialPortClient.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TouchSocket.Core;
+using TouchSocket.SerialPorts;
+using TouchSocket.Sockets;
+
+namespace SunlightCentralizedControlManagement_SCCM_.UserClass
+{
+ public class AsyncSerialPortClient
+ {
+ public static async Task PortClient(SerialPortClient portclient, string com, int BAUD)
+ {
+ //var client = new SerialPortClient();
+ portclient.Connecting = (client, e) => { return EasyTask.CompletedTask; };//即将连接到端口
+ portclient.Connected = (client, e) => { return EasyTask.CompletedTask; };//成功连接到端口
+ portclient.Closing = (client, e) => { return EasyTask.CompletedTask; };//即将从端口断开连接。此处仅主动断开才有效。
+ portclient.Closed = (client, e) => { return EasyTask.CompletedTask; };//从端口断开连接,当连接不成功时不会触发。
+ portclient.Received = async (c, e) =>
+ {
+
+ await Console.Out.WriteLineAsync(e.ByteBlock.Span.ToString(Encoding.UTF8));
+ };
+
+ await portclient.SetupAsync(new TouchSocketConfig()
+ .SetSerialPortOption(new SerialPortOption()
+ {
+ BaudRate = BAUD,//波特率
+ DataBits = 8,//数据位
+ Parity = System.IO.Ports.Parity.None,//校验位
+ PortName = com,//COM
+ StopBits = System.IO.Ports.StopBits.One,//停止位
+ })
+ .SetSerialDataHandlingAdapter(() => new PeriodPackageAdapter() { CacheTimeout = TimeSpan.FromMilliseconds(100) })
+ .ConfigurePlugins(a =>
+ {
+ //a.Add();
+ }));
+
+ await portclient.ConnectAsync();
+ }
+ }
+}
diff --git a/UserClass/RS485MasterStation.cs b/UserClass/RS485MasterStation.cs
new file mode 100644
index 0000000..984dea2
--- /dev/null
+++ b/UserClass/RS485MasterStation.cs
@@ -0,0 +1,617 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TouchSocket.Core;
+using TouchSocket.SerialPorts;
+using TouchSocket.Sockets;
+
+namespace SunlightCentralizedControlManagement_SCCM_.UserClass
+{
+ #region 基础数据结构
+ ///
+ /// 485 设备地址
+ ///
+ public struct DeviceAddress
+ {
+ public byte Value { get; }
+
+ public DeviceAddress(byte address)
+ {
+ if (address == 0) throw new ArgumentException("设备地址不能为0(广播地址)");
+ Value = address;
+ }
+
+ public static implicit operator byte(DeviceAddress address) => address.Value;
+ public static implicit operator DeviceAddress(byte address) => new DeviceAddress(address);
+
+ public override string ToString() => $"0x{Value:X2}";
+ }
+
+ ///
+ /// 485 命令帧
+ ///
+ public class CommandFrame
+ {
+ public DeviceAddress DeviceAddress { get; set; }
+ public byte FunctionCode { get; set; }
+ public byte[] Data { get; set; }
+ public ushort CRC { get; set; }
+
+ public byte[] ToBytes()
+ {
+ // 实现帧序列化逻辑,包括CRC计算
+ var dataLength = Data?.Length ?? 0;
+ var frame = new byte[3 + dataLength + 2]; // 地址+功能码+数据+CRC
+
+ frame[0] = DeviceAddress;
+ frame[1] = FunctionCode;
+
+ if (dataLength > 0)
+ Array.Copy(Data, 0, frame, 2, dataLength);
+
+ // 计算CRC (这里使用Modbus CRC算法示例)
+ CRC = CalculateCRC(frame, 0, frame.Length - 2);
+ frame[frame.Length - 2] = (byte)(CRC & 0xFF);
+ frame[frame.Length - 1] = (byte)(CRC >> 8);
+
+ return frame;
+ }
+
+ private ushort CalculateCRC(byte[] data, int offset, int length)
+ {
+ // 简化的CRC计算示例,实际应根据协议实现
+ ushort crc = 0xFFFF;
+ for (int i = offset; i < offset + length; i++)
+ {
+ crc ^= data[i];
+ for (int j = 0; j < 8; j++)
+ {
+ if ((crc & 0x0001) != 0)
+ {
+ crc >>= 1;
+ crc ^= 0xA001;
+ }
+ else
+ {
+ crc >>= 1;
+ }
+ }
+ }
+ return crc;
+ }
+ }
+
+ ///
+ /// 响应帧
+ ///
+ public class ResponseFrame
+ {
+ public DeviceAddress DeviceAddress { get; set; }
+ public byte FunctionCode { get; set; }
+ public byte[] Data { get; set; }
+ public ushort CRC { get; set; }
+ public bool IsValid { get; set; }
+ public string Error { get; set; }
+
+ public static ResponseFrame FromBytes(byte[] data)
+ {
+ // 实现帧解析逻辑,包括CRC验证
+ var frame = new ResponseFrame();
+
+ if (data.Length < 4) // 最小帧长度
+ {
+ frame.IsValid = false;
+ frame.Error = "帧长度不足";
+ return frame;
+ }
+
+ frame.DeviceAddress = data[0];
+ frame.FunctionCode = data[1];
+
+ // 检查CRC
+ ushort receivedCRC = (ushort)(data[data.Length - 1] << 8 | data[data.Length - 2]);
+ ushort calculatedCRC = CalculateCRC(data, 0, data.Length - 2);
+
+ if (receivedCRC != calculatedCRC)
+ {
+ frame.IsValid = false;
+ frame.Error = "CRC校验失败";
+ return frame;
+ }
+
+ // 提取数据部分
+ if (data.Length > 4)
+ {
+ frame.Data = new byte[data.Length - 4];
+ Array.Copy(data, 2, frame.Data, 0, frame.Data.Length);
+ }
+
+ frame.IsValid = true;
+ return frame;
+ }
+
+ private static ushort CalculateCRC(byte[] data, int offset, int length)
+ {
+ // 与CommandFrame相同的CRC算法
+ ushort crc = 0xFFFF;
+ for (int i = offset; i < offset + length; i++)
+ {
+ crc ^= data[i];
+ for (int j = 0; j < 8; j++)
+ {
+ if ((crc & 0x0001) != 0)
+ {
+ crc >>= 1;
+ crc ^= 0xA001;
+ }
+ else
+ {
+ crc >>= 1;
+ }
+ }
+ }
+ return crc;
+ }
+ }
+
+ ///
+ /// 通信任务
+ ///
+ public class CommunicationTask
+ {
+ public DeviceAddress DeviceAddress { get; set; }
+ public CommandFrame Command { get; set; }
+ public TaskCompletionSource CompletionSource { get; set; }
+ public int TimeoutMs { get; set; } = 1000;
+ public int RetryCount { get; set; } = 3;
+ }
+#endregion
+
+ #region 异常类
+ public class RS485CommunicationException : Exception
+ {
+ public DeviceAddress DeviceAddress { get; }
+ public byte FunctionCode { get; }
+
+ public RS485CommunicationException(DeviceAddress address, byte functionCode, string message)
+ : base($"设备 {address} 功能码 0x{functionCode:X2} 通信失败: {message}")
+ {
+ DeviceAddress = address;
+ FunctionCode = functionCode;
+ }
+ }
+
+ public class RS485TimeoutException : RS485CommunicationException
+ {
+ public RS485TimeoutException(DeviceAddress address, byte functionCode)
+ : base(address, functionCode, "响应超时")
+ {
+ }
+ }
+
+ public class RS485CRCException : RS485CommunicationException
+ {
+ public RS485CRCException(DeviceAddress address, byte functionCode)
+ : base(address, functionCode, "CRC校验失败")
+ {
+ }
+ }
+ #endregion
+
+ #region 主站实现
+ ///
+ /// 485 主站实现
+ ///
+ public class RS485MasterStation : IDisposable
+ {
+ private readonly SerialPortClient _serialPortClient;
+ private readonly ConcurrentDictionary _deviceStates;
+ private readonly ConcurrentQueue _taskQueue;
+ private readonly CancellationTokenSource _cancellationTokenSource;
+ private Task _processingTask;
+ private bool _isDisposed = false;
+
+ // 响应超时时间(毫秒)
+ public int ResponseTimeout { get; set; } = 1000;
+
+ // 帧间延迟(毫秒)
+ public int InterFrameDelay { get; set; } = 10;
+
+ // 最大重试次数
+ public int MaxRetries { get; set; } = 3;
+
+ ///
+ /// 设备状态
+ ///
+ private class DeviceState
+ {
+ public DeviceAddress Address { get; set; }
+ public DateTime LastCommunication { get; set; }
+ public bool IsOnline { get; set; }
+ public int TimeoutCount { get; set; }
+ }
+
+ public RS485MasterStation(string portName, int baudRate = 9600)
+ {
+ _serialPortClient = new SerialPortClient();
+ _deviceStates = new ConcurrentDictionary();
+ _taskQueue = new ConcurrentQueue();
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ // 配置串口
+ _serialPortClient.Setup(new TouchSocketConfig()
+ .SetSerialPortOption(new SerialPortOption()
+ {
+ PortName = portName,
+ BaudRate = baudRate,
+ DataBits = 8,
+ Parity = Parity.None,
+ StopBits = StopBits.One
+ }));
+
+ // 注册数据接收事件
+ _serialPortClient.Received = OnDataReceived;
+ }
+
+ ///
+ /// 启动主站
+ ///
+ public void Start()
+ {
+ _serialPortClient.Connect();
+ _processingTask = Task.Run(ProcessQueue, _cancellationTokenSource.Token);
+ Console.WriteLine("485主站已启动");
+ }
+
+ ///
+ /// 停止主站
+ ///
+ public void Stop()
+ {
+ _cancellationTokenSource.Cancel();
+ _serialPortClient.Close();
+ Console.WriteLine("485主站已停止");
+ }
+
+ ///
+ /// 添加设备到监控列表
+ ///
+ public void AddDevice(DeviceAddress address)
+ {
+ _deviceStates[address] = new DeviceState
+ {
+ Address = address,
+ LastCommunication = DateTime.MinValue,
+ IsOnline = false,
+ TimeoutCount = 0
+ };
+ Console.WriteLine($"已添加设备: {address}");
+ }
+
+ ///
+ /// 从监控列表移除设备
+ ///
+ public void RemoveDevice(DeviceAddress address)
+ {
+ _deviceStates.TryRemove(address, out _);
+ Console.WriteLine($"已移除设备: {address}");
+ }
+
+ ///
+ /// 发送命令到设备
+ ///
+ public Task SendCommandAsync(DeviceAddress address, byte functionCode, byte[] data = null, int timeoutMs = 1000, int retryCount = 3)
+ {
+ var command = new CommandFrame
+ {
+ DeviceAddress = address,
+ FunctionCode = functionCode,
+ Data = data ?? new byte[0]
+ };
+
+ var task = new CommunicationTask
+ {
+ DeviceAddress = address,
+ Command = command,
+ CompletionSource = new TaskCompletionSource(),
+ TimeoutMs = timeoutMs,
+ RetryCount = retryCount
+ };
+
+ _taskQueue.Enqueue(task);
+ return task.CompletionSource.Task;
+ }
+
+ ///
+ /// 读取保持寄存器
+ ///
+ public async Task ReadHoldingRegistersAsync(DeviceAddress address, ushort startAddress, ushort numberOfRegisters)
+ {
+ var data = new byte[4];
+ data[0] = (byte)(startAddress >> 8); // 起始地址高字节
+ data[1] = (byte)(startAddress & 0xFF); // 起始地址低字节
+ data[2] = (byte)(numberOfRegisters >> 8); // 寄存器数量高字节
+ data[3] = (byte)(numberOfRegisters & 0xFF); // 寄存器数量低字节
+
+ var response = await SendCommandAsync(address, 0x03, data);
+
+ if (!response.IsValid)
+ throw new RS485CommunicationException(address, 0x03, response.Error);
+
+ return response.Data;
+ }
+
+ ///
+ /// 写入单个寄存器
+ ///
+ public async Task WriteSingleRegisterAsync(DeviceAddress address, ushort registerAddress, ushort value)
+ {
+ var data = new byte[4];
+ data[0] = (byte)(registerAddress >> 8); // 寄存器地址高字节
+ data[1] = (byte)(registerAddress & 0xFF); // 寄存器地址低字节
+ data[2] = (byte)(value >> 8); // 值高字节
+ data[3] = (byte)(value & 0xFF); // 值低字节
+
+ var response = await SendCommandAsync(address, 0x06, data);
+
+ if (!response.IsValid)
+ throw new RS485CommunicationException(address, 0x06, response.Error);
+ }
+
+ ///
+ /// 处理任务队列
+ ///
+ private async Task ProcessQueue()
+ {
+ while (!_cancellationTokenSource.Token.IsCancellationRequested)
+ {
+ if (_taskQueue.TryDequeue(out var task))
+ {
+ await ProcessTask(task);
+ }
+ else
+ {
+ // 队列为空,执行设备轮询
+ await PollDevices();
+ await Task.Delay(100, _cancellationTokenSource.Token); // 短暂休眠
+ }
+ }
+ }
+
+ ///
+ /// 处理单个通信任务
+ ///
+ private async Task ProcessTask(CommunicationTask task)
+ {
+ int retryCount = 0;
+ ResponseFrame response = null;
+
+ while (retryCount < task.RetryCount && !_cancellationTokenSource.Token.IsCancellationRequested)
+ {
+ try
+ {
+ // 发送命令
+ var commandData = task.Command.ToBytes();
+ _serialPortClient.Send(commandData);
+
+ // 等待响应
+ var timeoutTask = Task.Delay(task.TimeoutMs, _cancellationTokenSource.Token);
+ var responseTask = task.CompletionSource.Task;
+
+ var completedTask = await Task.WhenAny(responseTask, timeoutTask);
+
+ if (completedTask == responseTask)
+ {
+ response = await responseTask;
+ if (response.IsValid)
+ {
+ UpdateDeviceState(task.DeviceAddress, true);
+ task.CompletionSource.SetResult(response);
+ return;
+ }
+ else
+ {
+ throw new RS485CRCException(task.DeviceAddress, task.Command.FunctionCode);
+ }
+ }
+ else
+ {
+ throw new RS485TimeoutException(task.DeviceAddress, task.Command.FunctionCode);
+ }
+ }
+ catch (RS485CommunicationException ex)
+ {
+ retryCount++;
+ UpdateDeviceState(task.DeviceAddress, false);
+
+ if (retryCount >= task.RetryCount)
+ {
+ task.CompletionSource.SetException(ex);
+ return;
+ }
+
+ // 等待一段时间后重试
+ await Task.Delay(InterFrameDelay, _cancellationTokenSource.Token);
+ }
+ }
+ }
+
+ ///
+ /// 轮询所有设备状态
+ ///
+ private async Task PollDevices()
+ {
+ foreach (var deviceState in _deviceStates.Values.ToList())
+ {
+ // 检查设备是否需要轮询(例如,超过一定时间未通信)
+ if ((DateTime.Now - deviceState.LastCommunication).TotalSeconds > 30)
+ {
+ try
+ {
+ // 发送诊断命令(如读取设备状态)
+ await ReadHoldingRegistersAsync(deviceState.Address, 0, 1);
+ Console.WriteLine($"设备 {deviceState.Address} 轮询成功");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"设备 {deviceState.Address} 轮询失败: {ex.Message}");
+ }
+
+ // 添加帧间延迟
+ await Task.Delay(InterFrameDelay, _cancellationTokenSource.Token);
+ }
+ }
+ }
+
+ ///
+ /// 数据接收处理
+ ///
+ private Task OnDataReceived(ISerialPortClient client, ReceivedDataEventArgs e)
+ {
+ try
+ {
+ // 解析响应帧
+ var response = ResponseFrame.FromBytes(e.ByteBlock.ReadBytesPackage());
+
+ if (response.IsValid)
+ {
+ // 查找匹配的任务并设置结果
+ foreach (var task in _taskQueue.Where(t => t.DeviceAddress.Value == response.DeviceAddress.Value))
+ {
+ task.CompletionSource.TrySetResult(response);
+ }
+
+ UpdateDeviceState(response.DeviceAddress, true);
+ }
+ else
+ {
+ Console.WriteLine($"收到无效响应: {response.Error}");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"处理接收数据时出错: {ex.Message}");
+ }
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// 更新设备状态
+ ///
+ private void UpdateDeviceState(DeviceAddress address, bool communicationSuccess)
+ {
+ if (_deviceStates.TryGetValue(address, out var state))
+ {
+ state.LastCommunication = DateTime.Now;
+
+ if (communicationSuccess)
+ {
+ state.IsOnline = true;
+ state.TimeoutCount = 0;
+ }
+ else
+ {
+ state.TimeoutCount++;
+ if (state.TimeoutCount > MaxRetries)
+ {
+ state.IsOnline = false;
+ Console.WriteLine($"设备 {address} 已离线");
+ }
+ }
+ }
+ }
+
+ ///
+ /// 获取所有设备状态
+ ///
+ public Dictionary GetDeviceStatuses()
+ {
+ return _deviceStates.ToDictionary(
+ kvp => kvp.Key,
+ kvp => kvp.Value.IsOnline);
+ }
+
+ ///
+ /// 资源释放
+ ///
+ public void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+ _cancellationTokenSource.Cancel();
+ _serialPortClient.SafeDispose();
+ _cancellationTokenSource.Dispose();
+ }
+ }
+ }
+ #endregion
+
+ #region 使用示例
+ class Program
+ {
+ static async Task Main(string[] args)
+ {
+ // 创建485主站实例
+ using (var master = new RS485MasterStation("COM1", 9600))
+
+ try
+ {
+ // 添加要监控的设备
+ master.AddDevice(0x01);
+ master.AddDevice(0x02);
+ master.AddDevice(0x03);
+
+ // 启动主站
+ master.Start();
+
+ // 等待一段时间让主站初始化
+ await Task.Delay(1000);
+
+ // 示例1: 读取设备1的保持寄存器
+ try
+ {
+ var data = await master.ReadHoldingRegistersAsync(0x01, 0, 10);
+ Console.WriteLine($"成功读取设备1的寄存器数据: {BitConverter.ToString(data)}");
+ }
+ catch (RS485CommunicationException ex)
+ {
+ Console.WriteLine(ex.Message);
+ }
+
+ // 示例2: 向设备2写入寄存器值
+ try
+ {
+ await master.WriteSingleRegisterAsync(0x02, 0x1000, 0x1234);
+ Console.WriteLine("成功向设备2写入寄存器值");
+ }
+ catch (RS485CommunicationException ex)
+ {
+ Console.WriteLine(ex.Message);
+ }
+
+ // 显示所有设备状态
+ var statuses = master.GetDeviceStatuses();
+ foreach (var status in statuses)
+ {
+ Console.WriteLine($"设备 {status.Key}: {(status.Value ? "在线" : "离线")}");
+ }
+
+ // 保持运行
+ Console.WriteLine("按任意键退出...");
+ Console.ReadKey();
+ }
+ finally
+ {
+ master.Stop();
+ }
+ }
+ }
+ #endregion
+}
diff --git a/ViewModel/MainWindowViewModel.cs b/ViewModel/MainWindowViewModel.cs
index 22a8c31..130fb76 100644
--- a/ViewModel/MainWindowViewModel.cs
+++ b/ViewModel/MainWindowViewModel.cs
@@ -32,10 +32,11 @@ using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
using TouchSocket.Core;
-using TouchSocket.Sockets;
using TouchSocket.SerialPorts;
+using TouchSocket.Sockets;
using static MaterialDesignThemes.Wpf.Theme;
using static SunlightCentralizedControlManagement_SCCM_.UserClass.SqliteHelper;
+using static SunlightCentralizedControlManagement_SCCM_.View.MachinesView;
using static SunlightCentralizedControlManagement_SCCM_.ViewModel.ManualDyelotModel;
using static System.Net.WebRequestMethods;
using static System.Windows.Forms.AxHost;
@@ -120,6 +121,8 @@ namespace SunlightCentralizedControlManagement_SCCM_.ViewModel
UPort();//启动串口
CountDown();
TcpClientNEW();
+ PortClientNEW();
+ ClientNEW();
SQL_LINK();
}
public static void USERCapacity(string user) //权限
@@ -361,7 +364,7 @@ namespace SunlightCentralizedControlManagement_SCCM_.ViewModel
"AND EndTime >'" + DateTime.Now.AddHours(-8).ToString("yyyy/MM/dd HH:mm:ss") + "'", null).Tables[0]; //读取表写入缓存
for (int k = 0; k < WorkOrderstepdata.Rows.Count; k++)
{
- if (Selet_Machines(Machines, "Type", "ID='" + k + "'").ToString() == "838")
+ if (Convert.ToBoolean(Selet_Machines(Machines, "Type", "ID='" + k + "'")))
{
string WorkOrderdata_m = WorkOrderstepdata.Select()[k].Field