BDU/DeviceCommand/Base/ModbusTcp.cs

355 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Common.Attributes;
using NModbus;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Common.Attributes.ATSCommandAttribute;
namespace DeviceCommand.Base
{
/// <summary>
/// ModbusTcp协议
/// </summary>
[ATSCommand]
[DeviceCategory("全部驱动")] // 添加分类属性
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; }
/// <summary>
/// 创建ModbusTCP设备对象
/// </summary>
/// <param name="ipAddress">IP地址</param>
/// <param name="port">端口号</param>
/// <param name="sendTimeout">发送超时时间</param>
/// <param name="receiveTimeout">接收超时时间</param>
public ModbusTcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
{
IPAddress = ipAddress;
Port = port;
SendTimeout = sendTimeout;
ReceiveTimeout = receiveTimeout;
return this;
}
/// <summary>
/// 修改ModbusTCP连接参数
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="ipAddress">IP地址</param>
/// <param name="port">端口号</param>
/// <param name="sendTimeout">发送超时时间</param>
/// <param name="receiveTimeout">接收超时时间</param>
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;
}
}
/// <summary>
/// 连接
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="ct"></param>
/// <returns></returns>
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!;
// 比较IP地址和端口
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;
}
/// <summary>
/// 设备初始化
/// </summary>
/// <param name="modbusTcp">ModbusTcp实例</param>
public static void ModbusTcpInitialize(ModbusTcp modbusTcp)
{
try
{
if (modbusTcp.TcpClient.Connected)
{
Console.WriteLine("设备已初始化,无需重复初始化");
return;
}
modbusTcp.TcpClient = new TcpClient();
modbusTcp.TcpClient.SendTimeout = modbusTcp.SendTimeout;
modbusTcp.TcpClient.ReceiveTimeout = modbusTcp.ReceiveTimeout;
Console.WriteLine($"ModbusTCP设备初始化完成");
}
catch (Exception ex)
{
Console.WriteLine($"设备初始化失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 断开
/// </summary>
/// <param name="modbusTcp">ModbusTcp实例</param>
public static void Close(ModbusTcp modbusTcp)
{
try
{
if (modbusTcp.TcpClient.Connected)
{
modbusTcp.TcpClient.Close();
Console.WriteLine("ModbusTCP连接已断开");
}
}
catch (Exception ex)
{
Console.WriteLine($"断开连接失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 读保存寄存器
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="slaveAddress">设备地址</param>
/// <param name="startAddress">起始地址</param>
/// <param name="numberOfPoints">读取数量</param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
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通讯异常读取操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
return await readTask;
}
/// <summary>
/// 写入单个寄存器
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="slaveAddress">从设备地址</param>
/// <param name="registerAddress">寄存器地址</param>
/// <param name="value">值</param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
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通讯异常写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
}
/// <summary>
/// 写入多个寄存器
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="slaveAddress">从设备地址</param>
/// <param name="startAddress">起始寄存器地址</param>
/// <param name="values">值列表</param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
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通讯异常写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
}
/// <summary>
/// 读取线圈
/// </summary>
/// <param name="modbusTcp">ModbusTcp实例</param>
/// <param name="slaveAddress">从机地址</param>
/// <param name="startAddress">起始地址</param>
/// <param name="numberOfPoints">读取数量</param>
/// <param name="ct">支持中途取消发送指令</param>
/// <returns>布尔数组</returns>
public static async Task<bool[]> ReadCoilAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
{
try
{
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读取线圈操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
return await readTask;
}
catch (Exception ex)
{
Console.WriteLine($"读取线圈失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 写入单个线圈
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="slaveAddress">从设备地址</param>
/// <param name="coilAddress">线圈地址</param>
/// <param name="value">值</param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
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通讯异常写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
}
/// <summary>
/// 写入多个线圈
/// </summary>
/// <param name="modbusTcp"></param>
/// <param name="slaveAddress">从设备地址</param>
/// <param name="startAddress">起始线圈地址</param>
/// <param name="values">值列表</param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
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通讯异常写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
}
/// <summary>
/// 紧急停止
/// </summary>
/// <param name="modbusTcp">ModbusTcp实例</param>
public static void ModbusTcpStopNow(ModbusTcp modbusTcp)
{
try
{
if (modbusTcp.TcpClient.Connected)
{
modbusTcp.TcpClient.Close();
Console.WriteLine("紧急停止ModbusTCP连接已断开");
}
}
catch (Exception ex)
{
Console.WriteLine($"紧急停止失败: {ex.Message}");
}
}
/// <summary>
/// 读取输入寄存器
/// </summary>
/// <param name="modbusTcp">ModbusTcp实例</param>
/// <param name="slaveAddress">从机地址</param>
/// <param name="startAddress">起始地址</param>
/// <param name="numberOfPoints">读取数量</param>
/// <param name="ct">支持中途取消发送指令</param>
/// <returns>ushort数组</returns>
public static async Task<ushort[]> ReadInputRegisterAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
{
try
{
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读取输入寄存器操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
}
return await readTask;
}
catch (Exception ex)
{
Console.WriteLine($"读取输入寄存器失败: {ex.Message}");
throw;
}
}
}
}