2017-12-28 Update 1 Add UdpConnector
This commit is contained in:
@@ -5,6 +5,14 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Modbus.Net.Modbus
|
namespace Modbus.Net.Modbus
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Udp字节伸缩
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusUdpProtocolLinkerBytesExtend : ModbusTcpProtocolLinkerBytesExtend
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rtu透传字节伸缩
|
/// Rtu透传字节伸缩
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
45
Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs
Normal file
45
Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocol.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Modbus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus/Udp协议
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusUdpProtocol : ModbusProtocol
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slaveAddress">从站号</param>
|
||||||
|
/// <param name="masterAddress">主站号</param>
|
||||||
|
public ModbusUdpProtocol(byte slaveAddress, byte masterAddress)
|
||||||
|
: this(ConfigurationManager.AppSettings["IP"], slaveAddress, masterAddress)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">ip地址</param>
|
||||||
|
/// <param name="slaveAddress">从站号</param>
|
||||||
|
/// <param name="masterAddress">主站号</param>
|
||||||
|
public ModbusUdpProtocol(string ip, byte slaveAddress, byte masterAddress)
|
||||||
|
: base(slaveAddress, masterAddress)
|
||||||
|
{
|
||||||
|
ProtocolLinker = new ModbusUdpProtocolLinker(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">ip地址</param>
|
||||||
|
/// <param name="port">端口</param>
|
||||||
|
/// <param name="slaveAddress">从站号</param>
|
||||||
|
/// <param name="masterAddress">主站号</param>
|
||||||
|
public ModbusUdpProtocol(string ip, int port, byte slaveAddress, byte masterAddress)
|
||||||
|
: base(slaveAddress, masterAddress)
|
||||||
|
{
|
||||||
|
ProtocolLinker = new ModbusUdpProtocolLinker(ip, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs
Normal file
47
Modbus.Net/Modbus.Net.Modbus/ModbusUdpProtocolLinker.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Modbus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus/Udp协议连接器
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusUdpProtocolLinker : UdpProtocolLinker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">IP地址</param>
|
||||||
|
public ModbusUdpProtocolLinker(string ip)
|
||||||
|
: this(ip, int.Parse(ConfigurationManager.AppSettings["ModbusPort"] ?? "502"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">IP地址</param>
|
||||||
|
/// <param name="port">端口</param>
|
||||||
|
public ModbusUdpProtocolLinker(string ip, int port) : base(ip, port)
|
||||||
|
{
|
||||||
|
((BaseConnector)BaseConnector).AddController(new FifoController(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 校验返回数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">设备返回的数据</param>
|
||||||
|
/// <returns>数据是否正确</returns>
|
||||||
|
public override bool? CheckRight(byte[] content)
|
||||||
|
{
|
||||||
|
//ProtocolLinker的CheckRight不会返回null
|
||||||
|
if (base.CheckRight(content) != true) return false;
|
||||||
|
//长度校验失败
|
||||||
|
if (content[5] != content.Length - 6)
|
||||||
|
throw new ModbusProtocolErrorException(500);
|
||||||
|
//Modbus协议错误
|
||||||
|
if (content[7] > 127)
|
||||||
|
throw new ModbusProtocolErrorException(content[2] > 0 ? content[2] : content[8]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,10 @@ namespace Modbus.Net.Modbus
|
|||||||
/// Ascii连接Tcp透传
|
/// Ascii连接Tcp透传
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AsciiInTcp = 4,
|
AsciiInTcp = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// Udp连接
|
||||||
|
/// </summary>
|
||||||
|
Udp = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -193,6 +197,17 @@ namespace Modbus.Net.Modbus
|
|||||||
MasterAddress));
|
MasterAddress));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//Udp协议
|
||||||
|
case ModbusType.Udp:
|
||||||
|
{
|
||||||
|
Wrapper = ConnectionString == null
|
||||||
|
? new ModbusUdpProtocol(SlaveAddress, MasterAddress)
|
||||||
|
: (ConnectionStringPort == null
|
||||||
|
? new ModbusUdpProtocol(ConnectionString, SlaveAddress, MasterAddress)
|
||||||
|
: new ModbusUdpProtocol(ConnectionStringIp, ConnectionStringPort.Value, SlaveAddress,
|
||||||
|
MasterAddress));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
Modbus.Net/Modbus.Net/SocketMessageEventArgs.cs
Normal file
29
Modbus.Net/Modbus.Net/SocketMessageEventArgs.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
/// <summary>
|
||||||
|
/// Socket收到的数据
|
||||||
|
/// </summary>
|
||||||
|
public class SocketMessageEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">需要返回的信息</param>
|
||||||
|
public SocketMessageEventArgs(byte[] message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回的信息
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Message { get; }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -8,26 +8,6 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Modbus.Net
|
namespace Modbus.Net
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Socket收到的数据
|
|
||||||
/// </summary>
|
|
||||||
public class SocketMessageEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 构造器
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">需要返回的信息</param>
|
|
||||||
public SocketMessageEventArgs(byte[] message)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回的信息
|
|
||||||
/// </summary>
|
|
||||||
public byte[] Message { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Socket收发类
|
/// Socket收发类
|
||||||
/// 作者:本类来源于CSDN,并由罗圣(Chris L.)根据实际需要修改
|
/// 作者:本类来源于CSDN,并由罗圣(Chris L.)根据实际需要修改
|
||||||
|
|||||||
287
Modbus.Net/Modbus.Net/UdpConnector.cs
Normal file
287
Modbus.Net/Modbus.Net/UdpConnector.cs
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Nito.AsyncEx;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Modbus.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Udp收发类
|
||||||
|
/// </summary>
|
||||||
|
public class UdpConnector : BaseConnector, IDisposable
|
||||||
|
{
|
||||||
|
private readonly string _host;
|
||||||
|
private readonly int _port;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1MB 的接收缓冲区
|
||||||
|
/// </summary>
|
||||||
|
private readonly byte[] _receiveBuffer = new byte[1024];
|
||||||
|
|
||||||
|
private int _errorCount;
|
||||||
|
private int _receiveCount;
|
||||||
|
|
||||||
|
private int _sendCount;
|
||||||
|
|
||||||
|
private UdpClient _socketClient;
|
||||||
|
|
||||||
|
private bool m_disposed;
|
||||||
|
|
||||||
|
private Task _receiveThread;
|
||||||
|
private bool _taskCancel = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ipaddress">Ip地址</param>
|
||||||
|
/// <param name="port">端口</param>
|
||||||
|
/// <param name="timeoutTime">超时时间</param>
|
||||||
|
public UdpConnector(string ipaddress, int port, int timeoutTime = 10000) : base(timeoutTime)
|
||||||
|
{
|
||||||
|
_host = ipaddress;
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ConnectionToken => _host;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override int TimeoutTime { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool IsConnected => _socketClient?.Client != null && _socketClient.Client.Connected;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override AsyncLock Lock { get; } = new AsyncLock();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
//.NET Framework 类库
|
||||||
|
// GC..::.SuppressFinalize 方法
|
||||||
|
//请求系统不要调用指定对象的终结器。
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 虚方法,可供子类重写
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"></param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!m_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Release managed resources
|
||||||
|
}
|
||||||
|
// Release unmanaged resources
|
||||||
|
if (_socketClient != null)
|
||||||
|
{
|
||||||
|
CloseClientSocket();
|
||||||
|
#if NET40 || NET45 || NET451 || NET452
|
||||||
|
_socketClient.Close();
|
||||||
|
#else
|
||||||
|
_socketClient.Dispose();
|
||||||
|
#endif
|
||||||
|
Log.Debug("Tcp client {ConnectionToken} Disposed", ConnectionToken);
|
||||||
|
}
|
||||||
|
m_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 析构函数
|
||||||
|
/// 当客户端没有显示调用Dispose()时由GC完成资源回收功能
|
||||||
|
/// </summary>
|
||||||
|
~UdpConnector()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task<bool> ConnectAsync()
|
||||||
|
{
|
||||||
|
using (await Lock.LockAsync())
|
||||||
|
{
|
||||||
|
if (_socketClient != null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_socketClient = new UdpClient();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(TimeoutTime);
|
||||||
|
await Task.Run(() => _socketClient.Connect(_host, _port), cts.Token);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Tcp client {ConnectionToken} connect error", ConnectionToken);
|
||||||
|
}
|
||||||
|
if (_socketClient.Client.Connected)
|
||||||
|
{
|
||||||
|
_taskCancel = false;
|
||||||
|
Controller.SendStart();
|
||||||
|
ReceiveMsgThreadStart();
|
||||||
|
Log.Information("Tcp client {ConnectionToken} connected", ConnectionToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log.Error("Tcp client {ConnectionToken} connect failed.", ConnectionToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
Log.Error(err, "Tcp client {ConnectionToken} connect exception", ConnectionToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Disconnect()
|
||||||
|
{
|
||||||
|
if (_socketClient == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
Log.Information("Tcp client {ConnectionToken} disconnected successfully", ConnectionToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
Log.Error(err, "Tcp client {ConnectionToken} disconnected exception", ConnectionToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_socketClient = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override async Task SendMsgWithoutConfirm(byte[] message)
|
||||||
|
{
|
||||||
|
var datagram = message;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!IsConnected)
|
||||||
|
await ConnectAsync();
|
||||||
|
|
||||||
|
RefreshSendCount();
|
||||||
|
|
||||||
|
Log.Verbose("Tcp client {ConnectionToken} send text len = {Length}", ConnectionToken, datagram.Length);
|
||||||
|
Log.Verbose($"Tcp client {ConnectionToken} send: {String.Concat(datagram.Select(p => " " + p.ToString("X2")))}");
|
||||||
|
await _socketClient.SendAsync(datagram, datagram.Length);
|
||||||
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
Log.Error(err, "Tcp client {ConnectionToken} send exception", ConnectionToken);
|
||||||
|
CloseClientSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void ReceiveMsgThreadStart()
|
||||||
|
{
|
||||||
|
_receiveThread = Task.Run(ReceiveMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void ReceiveMsgThreadStop()
|
||||||
|
{
|
||||||
|
_taskCancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接收返回消息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>返回的消息</returns>
|
||||||
|
protected async Task ReceiveMessage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!_taskCancel)
|
||||||
|
{
|
||||||
|
var receive = await _socketClient.ReceiveAsync();
|
||||||
|
|
||||||
|
var len = receive.Buffer.Length;
|
||||||
|
// 异步接收回答
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
if (receive.Buffer.Clone() is byte[] receiveBytes)
|
||||||
|
{
|
||||||
|
Log.Verbose("Tcp client {ConnectionToken} receive text len = {Length}", ConnectionToken,
|
||||||
|
receiveBytes.Length);
|
||||||
|
Log.Verbose(
|
||||||
|
$"Tcp client {ConnectionToken} receive: {String.Concat(receiveBytes.Select(p => " " + p.ToString("X2")))}");
|
||||||
|
var isMessageConfirmed = Controller.ConfirmMessage(receiveBytes);
|
||||||
|
if (isMessageConfirmed == false)
|
||||||
|
{
|
||||||
|
//主动传输事件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshReceiveCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
Log.Error(err, "Tcp client {ConnectionToken} receive exception", ConnectionToken);
|
||||||
|
//CloseClientSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshSendCount()
|
||||||
|
{
|
||||||
|
_sendCount++;
|
||||||
|
Log.Verbose("Tcp client {ConnectionToken} send count: {SendCount}", ConnectionToken, _sendCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshReceiveCount()
|
||||||
|
{
|
||||||
|
_receiveCount++;
|
||||||
|
Log.Verbose("Tcp client {ConnectionToken} receive count: {SendCount}", ConnectionToken, _receiveCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshErrorCount()
|
||||||
|
{
|
||||||
|
_errorCount++;
|
||||||
|
Log.Verbose("Tcp client {ConnectionToken} error count: {ErrorCount}", ConnectionToken, _errorCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseClientSocket()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Controller.SendStop();
|
||||||
|
Controller.Clear();
|
||||||
|
ReceiveMsgThreadStop();
|
||||||
|
if (_socketClient.Client.Connected)
|
||||||
|
{
|
||||||
|
_socketClient?.Client.Disconnect(false);
|
||||||
|
}
|
||||||
|
_socketClient?.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Tcp client {ConnectionToken} client close exception", ConnectionToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
Modbus.Net/Modbus.Net/UdpProtocolLinker.cs
Normal file
48
Modbus.Net/Modbus.Net/UdpProtocolLinker.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
|
||||||
|
namespace Modbus.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Udp连接对象
|
||||||
|
/// </summary>
|
||||||
|
public abstract class UdpProtocolLinker : ProtocolLinker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造器
|
||||||
|
/// </summary>
|
||||||
|
protected UdpProtocolLinker(int port)
|
||||||
|
: this(ConfigurationManager.AppSettings["IP"], port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">Ip地址</param>
|
||||||
|
/// <param name="port">端口</param>
|
||||||
|
protected UdpProtocolLinker(string ip, int port)
|
||||||
|
: this(ip, port, int.Parse(ConfigurationManager.AppSettings["IPConnectionTimeout"] ?? "-1"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">Ip地址</param>
|
||||||
|
/// <param name="port">端口</param>
|
||||||
|
/// <param name="connectionTimeout">超时时间</param>
|
||||||
|
protected UdpProtocolLinker(string ip, int port, int connectionTimeout)
|
||||||
|
{
|
||||||
|
if (connectionTimeout == -1)
|
||||||
|
{
|
||||||
|
//初始化连接对象
|
||||||
|
BaseConnector = new UdpConnector(ip, port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//初始化连接对象
|
||||||
|
BaseConnector = new UdpConnector(ip, port, connectionTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user