From 7adb7db2b862eb4973cbd9b1d21b976d9e60c788 Mon Sep 17 00:00:00 2001 From: hsc Date: Thu, 13 Nov 2025 16:52:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BOB.sln | 12 +- BOB/BOB.csproj | 2 +- BOB/Models/DeviceConfigModel.cs | 43 +++++++ BOB/SystemConfig.cs | 88 ++++++++------ Command/Command.csproj | 4 + DeviceCommand/Base/ModbusTcp.cs | 146 ++++++++++++++++++++++ DeviceCommand/Base/Serial_Port.cs | 160 ++++++++++++++++++++++++ DeviceCommand/Base/TCP.cs | 169 ++++++++++++++++++++++++++ DeviceCommand/Device/E36233A.cs | 13 ++ DeviceCommand/Device/EAEL9080.cs | 13 ++ DeviceCommand/Device/IT6724C.cs | 14 +++ DeviceCommand/Device/LQ7500-D.cs | 13 ++ DeviceCommand/Device/PSB11000.cs | 13 ++ DeviceCommand/Device/SQ0030G1D.cs | 13 ++ DeviceCommand/Device/WS-68030-380T.cs | 13 ++ DeviceCommand/Device/ZXKS.cs | 13 ++ DeviceCommand/DeviceCommand.csproj | 14 +++ ORM/ORM.csproj | 1 - Service/Service.csproj | 1 - 19 files changed, 701 insertions(+), 44 deletions(-) create mode 100644 BOB/Models/DeviceConfigModel.cs create mode 100644 DeviceCommand/Base/ModbusTcp.cs create mode 100644 DeviceCommand/Base/Serial_Port.cs create mode 100644 DeviceCommand/Base/TCP.cs create mode 100644 DeviceCommand/Device/E36233A.cs create mode 100644 DeviceCommand/Device/EAEL9080.cs create mode 100644 DeviceCommand/Device/IT6724C.cs create mode 100644 DeviceCommand/Device/LQ7500-D.cs create mode 100644 DeviceCommand/Device/PSB11000.cs create mode 100644 DeviceCommand/Device/SQ0030G1D.cs create mode 100644 DeviceCommand/Device/WS-68030-380T.cs create mode 100644 DeviceCommand/Device/ZXKS.cs create mode 100644 DeviceCommand/DeviceCommand.csproj diff --git a/BOB.sln b/BOB.sln index f5d88c8..0ebc5f9 100644 --- a/BOB.sln +++ b/BOB.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.14.36221.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BOB", "BOB\BOB.csproj", "{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Logger\Logger.csproj", "{9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ORM", "ORM\ORM.csproj", "{4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}" @@ -17,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command", "Command\Command.csproj", "{49D249DE-CB09-4390-89DC-6165965C3933}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCommand", "DeviceCommand\DeviceCommand.csproj", "{94177FB3-45E4-466E-BB9F-761295736D35}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,10 +27,6 @@ Global {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.Build.0 = Release|Any CPU - {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Release|Any CPU.Build.0 = Release|Any CPU {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Debug|Any CPU.Build.0 = Debug|Any CPU {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -51,6 +47,10 @@ Global {49D249DE-CB09-4390-89DC-6165965C3933}.Debug|Any CPU.Build.0 = Debug|Any CPU {49D249DE-CB09-4390-89DC-6165965C3933}.Release|Any CPU.ActiveCfg = Release|Any CPU {49D249DE-CB09-4390-89DC-6165965C3933}.Release|Any CPU.Build.0 = Release|Any CPU + {94177FB3-45E4-466E-BB9F-761295736D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94177FB3-45E4-466E-BB9F-761295736D35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94177FB3-45E4-466E-BB9F-761295736D35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94177FB3-45E4-466E-BB9F-761295736D35}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BOB/BOB.csproj b/BOB/BOB.csproj index 8b9be29..898faab 100644 --- a/BOB/BOB.csproj +++ b/BOB/BOB.csproj @@ -45,8 +45,8 @@ + - diff --git a/BOB/Models/DeviceConfigModel.cs b/BOB/Models/DeviceConfigModel.cs new file mode 100644 index 0000000..7319518 --- /dev/null +++ b/BOB/Models/DeviceConfigModel.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Models +{ + public class DeviceConfigModel + { + public Guid Id { get; set; } + public string DeviceName { get; set; } + public string DeviceType { get; set; } + public string Remark { get; set; } + public ICommunicationConfig CommunicationConfig { get; set; } + + public bool IsEnabled { get; set; } + } + public class TcpConfig : ICommunicationConfig + { + public string IPAddress { get; set; } + public int Port { get; set; } + public int ReadTimeout { get; set; } + public int WriteTimeout { get; set; } + } + public class SerialPortConfig : ICommunicationConfig + { + public string COMPort { get; set; } + public int BaudRate { get; set; } + public int DataBit { get; set; } = 8; + public StopBits StopBit { get; set; } = StopBits.One; + public Parity ParityBit { get; set; } = Parity.None; + public int ReadTimeout { get; set; } + public int WriteTimeout { get; set; } + } + public interface ICommunicationConfig + { + int ReadTimeout { get; set; } + int WriteTimeout { get; set; } + } + +} diff --git a/BOB/SystemConfig.cs b/BOB/SystemConfig.cs index a49f8b2..7e1f5c0 100644 --- a/BOB/SystemConfig.cs +++ b/BOB/SystemConfig.cs @@ -1,4 +1,5 @@ -using Logger; +using BOB.Models; +using Logger; using Newtonsoft.Json; using System; using System.IO; @@ -29,7 +30,7 @@ namespace BOB } [JsonIgnore] - public string SystemPath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB系统"); + public string SystemPath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB"); public int PerformanceLevel { get; set; } = 50; @@ -38,38 +39,9 @@ namespace BOB public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\"; public string DefaultSubProgramFilePath { get; set; } = ""; + public List DeviceList { get; set; } = new(); - // 配置加载方法 - public void LoadFromFile() - { - string configPath = Path.Combine(SystemPath, "system.config"); - - if (!File.Exists(configPath)) - { - string json = JsonConvert.SerializeObject(Instance, Formatting.Indented); - File.WriteAllText(configPath, json); - return; - } - try - { - string json = File.ReadAllText(configPath); - var loadedConfig = JsonConvert.DeserializeObject(json); - - // 复制所有可写属性(排除JsonIgnore属性) - PropertyInfo[] properties = typeof(SystemConfig).GetProperties(); - foreach (var prop in properties) - { - if (prop.CanWrite && !Attribute.IsDefined(prop, typeof(JsonIgnoreAttribute))) - { - prop.SetValue(this, prop.GetValue(loadedConfig)); - } - } - } - catch (Exception ex) - { - LoggerHelper.ErrorWithNotify($"配置加载失败: {ex.Message}",ex.StackTrace); - } - } + #region 配置加载方法 public void SaveToFile() { lock (_lock) @@ -79,8 +51,14 @@ namespace BOB if (!Directory.Exists(SystemPath)) Directory.CreateDirectory(SystemPath); - string configPath = Path.Combine(SystemPath, "system.config"); - string json = JsonConvert.SerializeObject(this, Formatting.Indented); + string configPath = Path.Combine(SystemPath, "system.json"); + + // 支持接口多态序列化 + string json = JsonConvert.SerializeObject(this, Formatting.Indented, + new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); File.WriteAllText(configPath, json); @@ -92,5 +70,45 @@ namespace BOB } } } + + public void LoadFromFile() + { + string configPath = Path.Combine(SystemPath, "system.json"); + + if (!File.Exists(configPath)) + { + // 文件不存在则保存当前配置 + SaveToFile(); + return; + } + + try + { + string json = File.ReadAllText(configPath); + + // 支持接口多态反序列化 + var loadedConfig = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + // 复制所有可写属性(排除 JsonIgnore 属性) + PropertyInfo[] properties = typeof(SystemConfig).GetProperties(); + foreach (var prop in properties) + { + if (prop.CanWrite && !Attribute.IsDefined(prop, typeof(JsonIgnoreAttribute))) + { + prop.SetValue(this, prop.GetValue(loadedConfig)); + } + } + } + catch (Exception ex) + { + LoggerHelper.ErrorWithNotify($"配置加载失败: {ex.Message}", ex.StackTrace); + } + } + + #endregion } } \ No newline at end of file diff --git a/Command/Command.csproj b/Command/Command.csproj index a88feaf..18b694a 100644 --- a/Command/Command.csproj +++ b/Command/Command.csproj @@ -7,6 +7,10 @@ enable + + + + diff --git a/DeviceCommand/Base/ModbusTcp.cs b/DeviceCommand/Base/ModbusTcp.cs new file mode 100644 index 0000000..852c381 --- /dev/null +++ b/DeviceCommand/Base/ModbusTcp.cs @@ -0,0 +1,146 @@ +using NModbus; +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace DeviceCommand.Base +{ + public class ModbusTcp + { + public string IPAddress { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 502; + public int SendTimeout { get; set; } = 3000; + public int ReceiveTimeout { get; set; } = 3000; + public TcpClient TcpClient { get; set; } = new(); + public IModbusMaster Modbus { get; set; } + + public ModbusTcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) + { + IPAddress = ipAddress; + Port = port; + SendTimeout = sendTimeout; + ReceiveTimeout = receiveTimeout; + return this; + } + + public static void ChangeDeviceConfig(ModbusTcp modbusTcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) + { + modbusTcp.IPAddress = ipAddress; + modbusTcp.Port = port; + if (sendTimeout > 0) modbusTcp.SendTimeout = sendTimeout; + if (receiveTimeout > 0) modbusTcp.ReceiveTimeout = receiveTimeout; + } + + public static async Task ConnectAsync(ModbusTcp modbusTcp, CancellationToken ct = default) + { + if (!modbusTcp.TcpClient.Connected) + { + modbusTcp.TcpClient.Close(); + modbusTcp.TcpClient.Dispose(); + modbusTcp.TcpClient = new TcpClient(); + await modbusTcp.TcpClient.ConnectAsync(modbusTcp.IPAddress, modbusTcp.Port, ct); + modbusTcp.Modbus = new ModbusFactory().CreateMaster(modbusTcp.TcpClient); + } + else + { + var remoteEndPoint = (IPEndPoint)modbusTcp.TcpClient.Client.RemoteEndPoint!; + var ip = remoteEndPoint.Address.MapToIPv4().ToString(); + bool isSameAddress = ip.Equals(modbusTcp.IPAddress); + bool isSamePort = remoteEndPoint.Port == modbusTcp.Port; + if (!isSameAddress || !isSamePort) + { + modbusTcp.TcpClient.Close(); + modbusTcp.TcpClient.Dispose(); + modbusTcp.TcpClient = new TcpClient(); + await modbusTcp.TcpClient.ConnectAsync(modbusTcp.IPAddress, modbusTcp.Port, ct); + modbusTcp.Modbus = new ModbusFactory().CreateMaster(modbusTcp.TcpClient); + } + } + return true; + } + + public static void ModbusTcpInitialize(ModbusTcp modbusTcp) + { + if (!modbusTcp.TcpClient.Connected) + { + modbusTcp.TcpClient = new TcpClient(); + modbusTcp.TcpClient.SendTimeout = modbusTcp.SendTimeout; + modbusTcp.TcpClient.ReceiveTimeout = modbusTcp.ReceiveTimeout; + } + } + + public static void Close(ModbusTcp modbusTcp) + { + if (modbusTcp.TcpClient.Connected) + { + modbusTcp.TcpClient.Close(); + } + } + + public static async Task ReadHoldingRegistersAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) + { + var readTask = modbusTcp.Modbus.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP读取保持寄存器超时"); + return await readTask; + } + + public static async Task WriteSingleRegisterAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default) + { + var sendTask = modbusTcp.Modbus.WriteSingleRegisterAsync(slaveAddress, registerAddress, value).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP写单寄存器超时"); + } + + public static async Task WriteMultipleRegistersAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default) + { + var sendTask = modbusTcp.Modbus.WriteMultipleRegistersAsync(slaveAddress, startAddress, values).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP写多寄存器超时"); + } + + public static async Task ReadCoilAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) + { + var readTask = modbusTcp.Modbus.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP读取线圈超时"); + return await readTask; + } + + public static async Task WriteSingleCoilAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default) + { + var sendTask = modbusTcp.Modbus.WriteSingleCoilAsync(slaveAddress, coilAddress, value).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP写单线圈超时"); + } + + public static async Task WriteMultipleCoilsAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, bool[] values, CancellationToken ct = default) + { + var sendTask = modbusTcp.Modbus.WriteMultipleCoilsAsync(slaveAddress, startAddress, values).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP写多线圈超时"); + } + + public static void ModbusTcpStopNow(ModbusTcp modbusTcp) + { + if (modbusTcp.TcpClient.Connected) modbusTcp.TcpClient.Close(); + } + + public static async Task ReadInputRegisterAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) + { + var readTask = modbusTcp.Modbus.ReadInputRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); + var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct); + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"ModbusTCP读取输入寄存器超时"); + return await readTask; + } + } +} diff --git a/DeviceCommand/Base/Serial_Port.cs b/DeviceCommand/Base/Serial_Port.cs new file mode 100644 index 0000000..eb085bd --- /dev/null +++ b/DeviceCommand/Base/Serial_Port.cs @@ -0,0 +1,160 @@ +using System.Text; +using System.IO.Ports; + +namespace DeviceCommand.Base +{ + public class Serial_Port + { + public string PortName { get; set; } = "COM1"; + public int BaudRate { get; set; } = 9600; + public int DataBits { get; set; } = 8; + public StopBits StopBits { get; set; } = StopBits.One; + public Parity Parity { get; set; } = Parity.None; + public int ReadTimeout { get; set; } = 3000; + public int WriteTimeout { get; set; } = 3000; + public SerialPort _SerialPort { get; set; } = new SerialPort(); + + public Serial_Port CreateDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000) + { + PortName = portName; + BaudRate = baudRate; + DataBits = dataBits; + StopBits = stopBits; + Parity = parity; + ReadTimeout = readTimeout; + WriteTimeout = writeTimeout; + return this; + } + + public static void ChangeDeviceConfig(Serial_Port serialPort, string PortName, int BaudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int ReadTimeout = 3000, int WriteTimeout = 3000) + { + serialPort.PortName = PortName; + serialPort.BaudRate = BaudRate; + serialPort.DataBits = dataBits; + serialPort.StopBits = stopBits; + serialPort.Parity = parity; + if (ReadTimeout > 0) serialPort.ReadTimeout = ReadTimeout; + if (WriteTimeout > 0) serialPort.WriteTimeout = WriteTimeout; + } + + public static async Task ConnectAsync(Serial_Port serialPort, CancellationToken ct = default) + { + if (serialPort._SerialPort.PortName != serialPort.PortName + || serialPort._SerialPort.BaudRate != serialPort.BaudRate + || serialPort._SerialPort.Parity != serialPort.Parity + || serialPort._SerialPort.DataBits != serialPort.DataBits + || serialPort._SerialPort.StopBits != serialPort.StopBits + || serialPort._SerialPort.ReadTimeout != serialPort.ReadTimeout + || serialPort._SerialPort.WriteTimeout != serialPort.WriteTimeout) + { + serialPort._SerialPort.Close(); + serialPort._SerialPort.PortName = serialPort.PortName; + serialPort._SerialPort.BaudRate = serialPort.BaudRate; + serialPort._SerialPort.Parity = serialPort.Parity; + serialPort._SerialPort.DataBits = serialPort.DataBits; + serialPort._SerialPort.StopBits = serialPort.StopBits; + serialPort._SerialPort.ReadTimeout = serialPort.ReadTimeout; + serialPort._SerialPort.WriteTimeout = serialPort.WriteTimeout; + serialPort._SerialPort.Open(); + } + else if (!serialPort._SerialPort.IsOpen) + { + serialPort._SerialPort.Open(); + } + return true; + } + + public static void Close(Serial_Port serialPort) + { + if (serialPort._SerialPort.IsOpen) serialPort._SerialPort.Close(); + } + + public static async Task SendAsync(Serial_Port serialPort, byte[] bytes, CancellationToken ct = default) + { + if (!serialPort._SerialPort.IsOpen) return; + var timeoutMs = serialPort.WriteTimeout; + if (timeoutMs <= 0) + { + serialPort._SerialPort.Write(bytes, 0, bytes.Length); + return; + } + var sendTask = Task.Run(() => serialPort._SerialPort.Write(bytes, 0, bytes.Length), ct); + var timeoutTask = Task.Delay(timeoutMs, ct); + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"串口通讯异常:写入操作在 {timeoutMs} ms内未完成"); + await sendTask; + } + + public static async Task SendAsync(Serial_Port serialPort, string str, CancellationToken ct = default) + { + if (!serialPort._SerialPort.IsOpen) return; + byte[] bytes = Encoding.UTF8.GetBytes(str); + await SendAsync(serialPort, bytes, ct); + } + + public static async Task ReadAsync(Serial_Port serialPort, byte[] buffer, CancellationToken ct = default) + { + if (!serialPort._SerialPort.IsOpen) return null; + var timeoutMs = serialPort.ReadTimeout; + var readTask = ReadByte(serialPort, buffer, ct); + if (timeoutMs <= 0) return await readTask; + var timeoutTask = Task.Delay(timeoutMs, ct); + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"串口通讯异常:读取操作在 {timeoutMs} ms内未完成"); + return await readTask; + } + + public static async Task ReadByte(Serial_Port serialPort, byte[] buffer, CancellationToken ct) + { + int bytesRead = 0; + while (bytesRead < buffer.Length) + { + if (serialPort._SerialPort.BytesToRead > 0) + { + bytesRead += serialPort._SerialPort.Read(buffer, bytesRead, buffer.Length - bytesRead); + } + } + return buffer; + } + + public static async Task ReadAsync(Serial_Port serialPort, string delimiter = "\n", CancellationToken ct = default) + { + if (!serialPort._SerialPort.IsOpen) return null; + var timeoutMs = serialPort.ReadTimeout; + var readTask = ReadDefaultString(serialPort, delimiter, ct); + if (timeoutMs <= 0) return await readTask; + var timeoutTask = Task.Delay(timeoutMs, ct); + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) throw new TimeoutException($"串口通讯异常:读取操作在 {timeoutMs} ms内未完成"); + return await readTask; + } + + private static async Task ReadDefaultString(Serial_Port serialPort, string delimiter, CancellationToken ct) + { + delimiter ??= "\n"; + MemoryStream memoryStream = new(); + byte[] buffer = new byte[2048]; + string data = string.Empty; + await Task.Run(() => + { + while (true) + { + if (ct.IsCancellationRequested) return; + if (serialPort._SerialPort.BytesToRead > 0) + { + int bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length); + memoryStream.Write(buffer, 0, bytesRead); + data = Encoding.UTF8.GetString(memoryStream.ToArray()); + int lineEndIndex = data.IndexOf(delimiter); + if (lineEndIndex >= 0) + { + data = data.Substring(0, lineEndIndex).Trim(); + return; + } + } + } + }, ct); + return data; + } + } +} diff --git a/DeviceCommand/Base/TCP.cs b/DeviceCommand/Base/TCP.cs new file mode 100644 index 0000000..3abd9bb --- /dev/null +++ b/DeviceCommand/Base/TCP.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Base +{ + public class Tcp + { + public string IPAddress { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 502; + public int SendTimeout { get; set; } = 3000; + public int ReceiveTimeout { get; set; } = 3000; + public TcpClient TcpClient { get; set; } = new(); + + public Tcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) + { + IPAddress = ipAddress; + Port = port; + SendTimeout = sendTimeout; + ReceiveTimeout = receiveTimeout; + return this; + } + + public static void ChangeDeviceConfig(Tcp tcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) + { + tcp.IPAddress = ipAddress; + tcp.Port = port; + if (sendTimeout > 0) tcp.SendTimeout = sendTimeout; + if (receiveTimeout > 0) tcp.ReceiveTimeout = receiveTimeout; + } + + public static async Task ConnectAsync(Tcp tcp, CancellationToken ct = default) + { + if (!tcp.TcpClient.Connected) + { + tcp.TcpClient.Close(); + tcp.TcpClient.Dispose(); + tcp.TcpClient = new TcpClient(); + await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct); + } + else + { + var remoteEndPoint = (IPEndPoint)tcp.TcpClient.Client.RemoteEndPoint!; + var ip = remoteEndPoint.Address.MapToIPv4().ToString(); + bool isSameAddress = ip.Equals(tcp.IPAddress); + bool isSamePort = remoteEndPoint.Port == tcp.Port; + + if (!isSameAddress || !isSamePort) + { + tcp.TcpClient.Close(); + tcp.TcpClient.Dispose(); + tcp.TcpClient = new TcpClient(); + await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct); + } + } + return true; + } + + public static void Close(Tcp tcp) + { + tcp.TcpClient.Close(); + } + + public static async Task SendAsync(Tcp tcp, byte[] bytes, CancellationToken ct = default) + { + var timeoutMs = tcp.SendTimeout; + if (timeoutMs <= 0) + { + await tcp.TcpClient.Client.SendAsync(bytes, ct); + return; + } + + var sendTask = tcp.TcpClient.Client.SendAsync(bytes, ct).AsTask(); + var timeoutTask = Task.Delay(timeoutMs, ct); + + var completedTask = await Task.WhenAny(sendTask, timeoutTask); + if (completedTask == timeoutTask) + throw new TimeoutException($"TCP通讯异常:写入操作在 {timeoutMs} ms内未完成"); + + await sendTask; + } + + public static async Task SendAsync(Tcp tcp, string str, CancellationToken ct = default) + { + await SendAsync(tcp, Encoding.UTF8.GetBytes(str), ct); + } + + public static async Task ReadAsync(Tcp tcp, byte[] buffer, CancellationToken ct = default) + { + if (!tcp.TcpClient.Connected) return null; + + var timeoutMs = tcp.ReceiveTimeout; + if (timeoutMs <= 0) + return await ReadBytes(tcp, buffer, ct); + + var readTask = ReadBytes(tcp, buffer, ct); + var timeoutTask = Task.Delay(timeoutMs, ct); + + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) + throw new TimeoutException($"TCP通讯异常:读取操作在 {timeoutMs} ms内未完成"); + + return await readTask; + } + + private static async Task ReadBytes(Tcp tcp, byte[] buffer, CancellationToken ct) + { + NetworkStream stream = tcp.TcpClient.GetStream(); + int bytesRead = 0; + while (bytesRead < buffer.Length) + { + int read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead, ct); + if (read == 0) return null; + bytesRead += read; + } + return buffer; + } + + public static async Task ReadAsync(Tcp tcp, string delimiter = "\n", CancellationToken ct = default) + { + delimiter ??= "\n"; + var timeoutMs = tcp.ReceiveTimeout; + if (timeoutMs <= 0) + return await ReadString(tcp, delimiter, ct); + + var readTask = ReadString(tcp, delimiter, ct); + var timeoutTask = Task.Delay(timeoutMs, ct); + + var completedTask = await Task.WhenAny(readTask, timeoutTask); + if (completedTask == timeoutTask) + throw new TimeoutException($"TCP通讯异常:读取操作在 {timeoutMs} ms内未完成"); + + return await readTask; + } + + private static async Task ReadString(Tcp tcp, string delimiter, CancellationToken ct) + { + NetworkStream stream = tcp.TcpClient.GetStream(); + MemoryStream memoryStream = new(); + byte[] buffer = new byte[2048]; + + int bytesRead; + while ((bytesRead = await stream.ReadAsync(buffer, ct)) > 0) + { + memoryStream.Write(buffer, 0, bytesRead); + string data = Encoding.UTF8.GetString(memoryStream.ToArray()); + int lineEndIndex = data.IndexOf(delimiter); + if (lineEndIndex >= 0) + return data[..lineEndIndex].Trim(); + } + return null; + } + + public async Task WriteRead(Tcp tcp, string str, string endstr, CancellationToken ct = default) + { + await SendAsync(tcp, str, ct); + return await ReadAsync(tcp, endstr, ct); + } + + public async Task ConnectAsync(CancellationToken ct = default) + { + return await ConnectAsync(this, ct); + } + } +} diff --git a/DeviceCommand/Device/E36233A.cs b/DeviceCommand/Device/E36233A.cs new file mode 100644 index 0000000..eab3c2d --- /dev/null +++ b/DeviceCommand/Device/E36233A.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class E36233A:Tcp + { + } +} diff --git a/DeviceCommand/Device/EAEL9080.cs b/DeviceCommand/Device/EAEL9080.cs new file mode 100644 index 0000000..984a0e9 --- /dev/null +++ b/DeviceCommand/Device/EAEL9080.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class EAEL9080:ModbusTcp + { + } +} diff --git a/DeviceCommand/Device/IT6724C.cs b/DeviceCommand/Device/IT6724C.cs new file mode 100644 index 0000000..48e8632 --- /dev/null +++ b/DeviceCommand/Device/IT6724C.cs @@ -0,0 +1,14 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class IT6724C: Serial_Port + { + } +} diff --git a/DeviceCommand/Device/LQ7500-D.cs b/DeviceCommand/Device/LQ7500-D.cs new file mode 100644 index 0000000..415fe31 --- /dev/null +++ b/DeviceCommand/Device/LQ7500-D.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class LQ7500_D: Tcp + { + } +} diff --git a/DeviceCommand/Device/PSB11000.cs b/DeviceCommand/Device/PSB11000.cs new file mode 100644 index 0000000..7b000b6 --- /dev/null +++ b/DeviceCommand/Device/PSB11000.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class PSB11000: ModbusTcp + { + } +} diff --git a/DeviceCommand/Device/SQ0030G1D.cs b/DeviceCommand/Device/SQ0030G1D.cs new file mode 100644 index 0000000..ddbe3bc --- /dev/null +++ b/DeviceCommand/Device/SQ0030G1D.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class SQ0030G1D: Tcp + { + } +} diff --git a/DeviceCommand/Device/WS-68030-380T.cs b/DeviceCommand/Device/WS-68030-380T.cs new file mode 100644 index 0000000..dc1aecd --- /dev/null +++ b/DeviceCommand/Device/WS-68030-380T.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class WS_68030_380T:ModbusTcp + { + } +} diff --git a/DeviceCommand/Device/ZXKS.cs b/DeviceCommand/Device/ZXKS.cs new file mode 100644 index 0000000..09e3753 --- /dev/null +++ b/DeviceCommand/Device/ZXKS.cs @@ -0,0 +1,13 @@ +using DeviceCommand.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeviceCommand.Device +{ + public class ZXKS:ModbusTcp + { + } +} diff --git a/DeviceCommand/DeviceCommand.csproj b/DeviceCommand/DeviceCommand.csproj new file mode 100644 index 0000000..b9d7cb2 --- /dev/null +++ b/DeviceCommand/DeviceCommand.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/ORM/ORM.csproj b/ORM/ORM.csproj index 72f7a86..bb6e9b3 100644 --- a/ORM/ORM.csproj +++ b/ORM/ORM.csproj @@ -12,7 +12,6 @@ - diff --git a/Service/Service.csproj b/Service/Service.csproj index 7745202..e092d0c 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -12,7 +12,6 @@ -