初始化设备

This commit is contained in:
hsc 2025-11-13 16:52:09 +08:00
parent 691d894a12
commit 7adb7db2b8
19 changed files with 701 additions and 44 deletions

12
BOB.sln
View File

@ -5,8 +5,6 @@ VisualStudioVersion = 17.14.36221.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BOB", "BOB\BOB.csproj", "{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BOB", "BOB\BOB.csproj", "{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}"
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Logger\Logger.csproj", "{9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ORM", "ORM\ORM.csproj", "{4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}" 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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command", "Command\Command.csproj", "{49D249DE-CB09-4390-89DC-6165965C3933}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command", "Command\Command.csproj", "{49D249DE-CB09-4390-89DC-6165965C3933}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCommand", "DeviceCommand\DeviceCommand.csproj", "{94177FB3-45E4-466E-BB9F-761295736D35}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Debug|Any CPU.Build.0 = 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 {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}.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.ActiveCfg = Release|Any CPU
{49D249DE-CB09-4390-89DC-6165965C3933}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -45,8 +45,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Command\Command.csproj" /> <ProjectReference Include="..\Command\Command.csproj" />
<ProjectReference Include="..\Common\Common.csproj" /> <ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
<ProjectReference Include="..\Logger\Logger.csproj" /> <ProjectReference Include="..\Logger\Logger.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\ORM\ORM.csproj" /> <ProjectReference Include="..\ORM\ORM.csproj" />
<ProjectReference Include="..\Service\Service.csproj" /> <ProjectReference Include="..\Service\Service.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -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; }
}
}

View File

@ -1,4 +1,5 @@
using Logger; using BOB.Models;
using Logger;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.IO; using System.IO;
@ -29,7 +30,7 @@ namespace BOB
} }
[JsonIgnore] [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; public int PerformanceLevel { get; set; } = 50;
@ -38,22 +39,59 @@ namespace BOB
public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\"; public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\";
public string DefaultSubProgramFilePath { get; set; } = ""; public string DefaultSubProgramFilePath { get; set; } = "";
public List<DeviceConfigModel> DeviceList { get; set; } = new();
#region
public void SaveToFile()
{
lock (_lock)
{
try
{
if (!Directory.Exists(SystemPath))
Directory.CreateDirectory(SystemPath);
string configPath = Path.Combine(SystemPath, "system.json");
// 支持接口多态序列化
string json = JsonConvert.SerializeObject(this, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
File.WriteAllText(configPath, json);
LoggerHelper.InfoWithNotify("系统配置已保存。");
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"配置保存失败: {ex.Message}");
}
}
}
// 配置加载方法
public void LoadFromFile() public void LoadFromFile()
{ {
string configPath = Path.Combine(SystemPath, "system.config"); string configPath = Path.Combine(SystemPath, "system.json");
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
string json = JsonConvert.SerializeObject(Instance, Formatting.Indented); // 文件不存在则保存当前配置
File.WriteAllText(configPath, json); SaveToFile();
return; return;
} }
try try
{ {
string json = File.ReadAllText(configPath); string json = File.ReadAllText(configPath);
var loadedConfig = JsonConvert.DeserializeObject<SystemConfig>(json);
// 支持接口多态反序列化
var loadedConfig = JsonConvert.DeserializeObject<SystemConfig>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
// 复制所有可写属性(排除 JsonIgnore 属性) // 复制所有可写属性(排除 JsonIgnore 属性)
PropertyInfo[] properties = typeof(SystemConfig).GetProperties(); PropertyInfo[] properties = typeof(SystemConfig).GetProperties();
@ -70,27 +108,7 @@ namespace BOB
LoggerHelper.ErrorWithNotify($"配置加载失败: {ex.Message}", ex.StackTrace); LoggerHelper.ErrorWithNotify($"配置加载失败: {ex.Message}", ex.StackTrace);
} }
} }
public void SaveToFile()
{
lock (_lock)
{
try
{
if (!Directory.Exists(SystemPath))
Directory.CreateDirectory(SystemPath);
string configPath = Path.Combine(SystemPath, "system.config"); #endregion
string json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(configPath, json);
LoggerHelper.InfoWithNotify("系统配置已保存。");
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"配置保存失败: {ex.Message}");
}
}
}
} }
} }

View File

@ -7,6 +7,10 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Ports" Version="10.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" /> <ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -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<bool> 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<ushort[]> 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<bool[]> 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<ushort[]> 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;
}
}
}

View File

@ -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<bool> 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<byte[]> 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<byte[]> 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<string> 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<string> 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;
}
}
}

169
DeviceCommand/Base/TCP.cs Normal file
View File

@ -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<bool> 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<byte[]> 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<byte[]> 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<string> 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<string> 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<string> WriteRead(Tcp tcp, string str, string endstr, CancellationToken ct = default)
{
await SendAsync(tcp, str, ct);
return await ReadAsync(tcp, endstr, ct);
}
public async Task<bool> ConnectAsync(CancellationToken ct = default)
{
return await ConnectAsync(this, ct);
}
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -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
{
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="System.IO.Ports" Version="10.0.0" />
</ItemGroup>
</Project>

View File

@ -12,7 +12,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Logger\Logger.csproj" /> <ProjectReference Include="..\Logger\Logger.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -12,7 +12,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Logger\Logger.csproj" /> <ProjectReference Include="..\Logger\Logger.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\ORM\ORM.csproj" /> <ProjectReference Include="..\ORM\ORM.csproj" />
</ItemGroup> </ItemGroup>