diff --git a/AMWD.Protocols.Modbus.Common/Contracts/IModbusConnection.cs b/AMWD.Protocols.Modbus.Common/Contracts/IModbusConnection.cs
index fe609e0..ad9bd3e 100644
--- a/AMWD.Protocols.Modbus.Common/Contracts/IModbusConnection.cs
+++ b/AMWD.Protocols.Modbus.Common/Contracts/IModbusConnection.cs
@@ -25,6 +25,21 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
///
TimeSpan IdleTimeout { get; set; }
+ ///
+ /// Gets or sets the maximum time until the connect attempt is given up.
+ ///
+ TimeSpan ConnectTimeout { get; set; }
+
+ ///
+ /// Gets or sets the receive time out value of the connection.
+ ///
+ TimeSpan ReadTimeout { get; set; }
+
+ ///
+ /// Gets or sets the send time out value of the connection.
+ ///
+ TimeSpan WriteTimeout { get; set; }
+
///
/// Invokes a Modbus request.
///
diff --git a/AMWD.Protocols.Modbus.Tcp/Utils/RequestQueueItem.cs b/AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs
similarity index 86%
rename from AMWD.Protocols.Modbus.Tcp/Utils/RequestQueueItem.cs
rename to AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs
index fd53bb1..daf24d0 100644
--- a/AMWD.Protocols.Modbus.Tcp/Utils/RequestQueueItem.cs
+++ b/AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs
@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
-namespace AMWD.Protocols.Modbus.Tcp.Utils
+namespace AMWD.Protocols.Modbus.Common.Utils
{
internal class RequestQueueItem
{
diff --git a/AMWD.Protocols.Modbus.Tcp/AMWD.Protocols.Modbus.Tcp.csproj b/AMWD.Protocols.Modbus.Tcp/AMWD.Protocols.Modbus.Tcp.csproj
index 693f52a..818a888 100644
--- a/AMWD.Protocols.Modbus.Tcp/AMWD.Protocols.Modbus.Tcp.csproj
+++ b/AMWD.Protocols.Modbus.Tcp/AMWD.Protocols.Modbus.Tcp.csproj
@@ -18,6 +18,7 @@
+
diff --git a/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
index f10cef6..d4c2725 100644
--- a/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
+++ b/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
@@ -27,7 +27,7 @@ namespace AMWD.Protocols.Modbus.Tcp
{ }
///
- /// Initializes a new instance of the class with a specific .
+ /// Initializes a new instance of the class with a specific .
///
/// The responsible for invoking the requests.
///
@@ -43,6 +43,34 @@ namespace AMWD.Protocols.Modbus.Tcp
///
public override IModbusProtocol Protocol { get; set; }
+ ///
+ public TimeSpan IdleTimeout
+ {
+ get => connection.IdleTimeout;
+ set => connection.IdleTimeout = value;
+ }
+
+ ///
+ public TimeSpan ConnectTimeout
+ {
+ get => connection.ConnectTimeout;
+ set => connection.ConnectTimeout = value;
+ }
+
+ ///
+ public TimeSpan ReadTimeout
+ {
+ get => connection.ReadTimeout;
+ set => connection.ReadTimeout = value;
+ }
+
+ ///
+ public TimeSpan WriteTimeout
+ {
+ get => connection.WriteTimeout;
+ set => connection.WriteTimeout = value;
+ }
+
///
public string Hostname
{
@@ -76,73 +104,5 @@ namespace AMWD.Protocols.Modbus.Tcp
tcpConnection.Port = value;
}
}
-
- ///
- public TimeSpan ReadTimeout
- {
- get
- {
- if (connection is ModbusTcpConnection tcpConnection)
- return tcpConnection.ReadTimeout;
-
- return default;
- }
- set
- {
- if (connection is ModbusTcpConnection tcpConnection)
- tcpConnection.ReadTimeout = value;
- }
- }
-
- ///
- public TimeSpan WriteTimeout
- {
- get
- {
- if (connection is ModbusTcpConnection tcpConnection)
- return tcpConnection.WriteTimeout;
-
- return default;
- }
- set
- {
- if (connection is ModbusTcpConnection tcpConnection)
- tcpConnection.WriteTimeout = value;
- }
- }
-
- ///
- public TimeSpan ReconnectTimeout
- {
- get
- {
- if (connection is ModbusTcpConnection tcpConnection)
- return tcpConnection.ConnectTimeout;
-
- return default;
- }
- set
- {
- if (connection is ModbusTcpConnection tcpConnection)
- tcpConnection.ConnectTimeout = value;
- }
- }
-
- ///
- public TimeSpan IdleTimeout
- {
- get
- {
- if (connection is ModbusTcpConnection tcpConnection)
- return tcpConnection.IdleTimeout;
-
- return default;
- }
- set
- {
- if (connection is ModbusTcpConnection tcpConnection)
- tcpConnection.IdleTimeout = value;
- }
- }
}
}
diff --git a/AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs
index 6992b16..a24ba6c 100644
--- a/AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs
+++ b/AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols;
+using AMWD.Protocols.Modbus.Common.Utils;
using AMWD.Protocols.Modbus.Tcp.Utils;
namespace AMWD.Protocols.Modbus.Tcp
@@ -26,7 +27,7 @@ namespace AMWD.Protocols.Modbus.Tcp
private readonly CancellationTokenSource _disposeCts = new();
private readonly SemaphoreSlim _clientLock = new(1, 1);
- private readonly TcpClientWrapper _client = new();
+ private readonly TcpClientWrapper _tcpClient = new();
private readonly Timer _idleTimer;
private readonly Task _processingTask;
@@ -49,7 +50,24 @@ namespace AMWD.Protocols.Modbus.Tcp
public string Name => "TCP";
///
- public virtual TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(6);
+ public virtual TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(6);
+
+ ///
+ public virtual TimeSpan ConnectTimeout { get; set; } = TimeSpan.MaxValue;
+
+ ///
+ public virtual TimeSpan ReadTimeout
+ {
+ get => TimeSpan.FromMilliseconds(_tcpClient.ReceiveTimeout);
+ set => _tcpClient.ReceiveTimeout = (int)value.TotalMilliseconds;
+ }
+
+ ///
+ public virtual TimeSpan WriteTimeout
+ {
+ get => TimeSpan.FromMilliseconds(_tcpClient.SendTimeout);
+ set => _tcpClient.SendTimeout = (int)value.TotalMilliseconds;
+ }
///
/// The DNS name of the remote host to which the connection is intended to.
@@ -81,32 +99,11 @@ namespace AMWD.Protocols.Modbus.Tcp
}
}
- ///
- /// Gets or sets the receive time out value of the connection.
- ///
- public virtual TimeSpan ReadTimeout
- {
- get => TimeSpan.FromMilliseconds(_client.ReceiveTimeout);
- set => _client.ReceiveTimeout = (int)value.TotalMilliseconds;
- }
-
- ///
- /// Gets or sets the send time out value of the connection.
- ///
- public virtual TimeSpan WriteTimeout
- {
- get => TimeSpan.FromMilliseconds(_client.SendTimeout);
- set => _client.SendTimeout = (int)value.TotalMilliseconds;
- }
-
- ///
- /// Gets or sets the maximum time until the connect attempt is given up.
- ///
- public virtual TimeSpan ConnectTimeout { get; set; } = TimeSpan.MaxValue;
-
#endregion Properties
- ///
+ ///
+ /// Releases all managed and unmanaged resources used by the .
+ ///
public void Dispose()
{
if (_isDisposed)
@@ -127,7 +124,7 @@ namespace AMWD.Protocols.Modbus.Tcp
OnIdleTimer(null);
- _client.Dispose();
+ _tcpClient.Dispose();
_clientLock.Dispose();
while (_requestQueue.TryDequeue(out var item))
@@ -204,7 +201,7 @@ namespace AMWD.Protocols.Modbus.Tcp
// Ensure connection is up
await AssertConnection(linkedCts.Token).ConfigureAwait(false);
- var stream = _client.GetStream();
+ var stream = _tcpClient.GetStream();
await stream.FlushAsync(linkedCts.Token).ConfigureAwait(false);
#if NET6_0_OR_GREATER
@@ -270,7 +267,7 @@ namespace AMWD.Protocols.Modbus.Tcp
// Has to be called within _clientLock!
private async Task AssertConnection(CancellationToken cancellationToken)
{
- if (_client.Connected)
+ if (_tcpClient.Connected)
return;
int delay = 1;
@@ -287,17 +284,17 @@ namespace AMWD.Protocols.Modbus.Tcp
{
foreach (var ipAddress in ipAddresses)
{
- _client.Close();
+ _tcpClient.Close();
#if NET6_0_OR_GREATER
- using var connectTask = _client.ConnectAsync(ipAddress, Port, cancellationToken);
+ using var connectTask = _tcpClient.ConnectAsync(ipAddress, Port, cancellationToken);
#else
- using var connectTask = _client.ConnectAsync(ipAddress, Port);
+ using var connectTask = _tcpClient.ConnectAsync(ipAddress, Port);
#endif
if (await Task.WhenAny(connectTask, Task.Delay(ReadTimeout, cancellationToken)) == connectTask)
{
await connectTask;
- if (_client.Connected)
+ if (_tcpClient.Connected)
return;
}
}
@@ -327,10 +324,10 @@ namespace AMWD.Protocols.Modbus.Tcp
_clientLock.Wait(_disposeCts.Token);
try
{
- if (!_client.Connected)
+ if (!_tcpClient.Connected)
return;
- _client.Close();
+ _tcpClient.Close();
}
finally
{
diff --git a/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpClientTest.cs b/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpClientTest.cs
index e4bc49c..1c3e2c9 100644
--- a/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpClientTest.cs
+++ b/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpClientTest.cs
@@ -1,5 +1,4 @@
-using AMWD.Protocols.Modbus.Common.Contracts;
-using AMWD.Protocols.Modbus.Tcp;
+using AMWD.Protocols.Modbus.Tcp;
using Moq;
namespace AMWD.Protocols.Modbus.Tests.Tcp
@@ -14,13 +13,20 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
public void Initialize()
{
_genericConnectionMock = new Mock();
+ _genericConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(40));
+ _genericConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(30));
+ _genericConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(20));
+ _genericConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(10));
+
_tcpConnectionMock = new Mock();
+
+ _tcpConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(10));
+ _tcpConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(20));
+ _tcpConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(30));
+ _tcpConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(40));
+
_tcpConnectionMock.Setup(c => c.Hostname).Returns("127.0.0.1");
_tcpConnectionMock.Setup(c => c.Port).Returns(502);
- _tcpConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(10));
- _tcpConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(20));
- _tcpConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(30));
- _tcpConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(40));
}
[TestMethod]
@@ -32,18 +38,10 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act
string hostname = client.Hostname;
int port = client.Port;
- TimeSpan readTimeout = client.ReadTimeout;
- TimeSpan writeTimeout = client.WriteTimeout;
- TimeSpan reconnectTimeout = client.ReconnectTimeout;
- TimeSpan idleTimeout = client.IdleTimeout;
// Assert
Assert.IsNull(hostname);
Assert.AreEqual(0, port);
- Assert.AreEqual(TimeSpan.Zero, readTimeout);
- Assert.AreEqual(TimeSpan.Zero, writeTimeout);
- Assert.AreEqual(TimeSpan.Zero, reconnectTimeout);
- Assert.AreEqual(TimeSpan.Zero, idleTimeout);
_genericConnectionMock.VerifyNoOtherCalls();
}
@@ -57,17 +55,59 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act
client.Hostname = "localhost";
client.Port = 205;
- client.ReadTimeout = TimeSpan.FromSeconds(123);
- client.WriteTimeout = TimeSpan.FromSeconds(456);
- client.ReconnectTimeout = TimeSpan.FromSeconds(789);
- client.IdleTimeout = TimeSpan.FromSeconds(321);
// Assert
_genericConnectionMock.VerifyNoOtherCalls();
}
[TestMethod]
- public void ShouldReturnValuesForTcpConnection()
+ public void ShouldReturnValuesForGenericConnection()
+ {
+ // Arrange
+ var client = new ModbusTcpClient(_genericConnectionMock.Object);
+
+ // Act
+ var idleTimeout = client.IdleTimeout;
+ var connectTimeout = client.ConnectTimeout;
+ var readTimeout = client.ReadTimeout;
+ var writeTimeout = client.WriteTimeout;
+
+ // Assert
+ Assert.AreEqual(TimeSpan.FromSeconds(40), idleTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(30), connectTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(20), readTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(10), writeTimeout);
+
+ _genericConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
+ _genericConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
+ _genericConnectionMock.VerifyGet(c => c.ReadTimeout, Times.Once);
+ _genericConnectionMock.VerifyGet(c => c.WriteTimeout, Times.Once);
+ _genericConnectionMock.VerifyNoOtherCalls();
+ }
+
+ [TestMethod]
+ public void ShouldSetValuesForGenericConnection()
+ {
+ // Arrange
+ var client = new ModbusTcpClient(_genericConnectionMock.Object);
+
+ // Act
+ client.IdleTimeout = TimeSpan.FromSeconds(10);
+ client.ConnectTimeout = TimeSpan.FromSeconds(20);
+ client.ReadTimeout = TimeSpan.FromSeconds(30);
+ client.WriteTimeout = TimeSpan.FromSeconds(40);
+
+ // Assert
+ _genericConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(10), Times.Once);
+ _genericConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(20), Times.Once);
+ _genericConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(30), Times.Once);
+ _genericConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(40), Times.Once);
+
+ _genericConnectionMock.VerifyNoOtherCalls();
+ }
+
+ [TestMethod]
+ public void ShouldGetValuesForTcpConnection()
{
// Arrange
var client = new ModbusTcpClient(_tcpConnectionMock.Object);
@@ -75,25 +115,26 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act
string hostname = client.Hostname;
int port = client.Port;
- TimeSpan readTimeout = client.ReadTimeout;
- TimeSpan writeTimeout = client.WriteTimeout;
- TimeSpan reconnectTimeout = client.ReconnectTimeout;
- TimeSpan keepAliveInterval = client.IdleTimeout;
+ var idleTimeout = client.IdleTimeout;
+ var connectTimeout = client.ConnectTimeout;
+ var readTimeout = client.ReadTimeout;
+ var writeTimeout = client.WriteTimeout;
// Assert
Assert.AreEqual("127.0.0.1", hostname);
Assert.AreEqual(502, port);
- Assert.AreEqual(10, readTimeout.TotalSeconds);
- Assert.AreEqual(20, writeTimeout.TotalSeconds);
- Assert.AreEqual(30, reconnectTimeout.TotalSeconds);
- Assert.AreEqual(40, keepAliveInterval.TotalSeconds);
+ Assert.AreEqual(TimeSpan.FromSeconds(10), idleTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(20), connectTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(30), readTimeout);
+ Assert.AreEqual(TimeSpan.FromSeconds(40), writeTimeout);
_tcpConnectionMock.VerifyGet(c => c.Hostname, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.Port, Times.Once);
+ _tcpConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
+ _tcpConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.ReadTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.WriteTimeout, Times.Once);
- _tcpConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
- _tcpConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
+
_tcpConnectionMock.VerifyNoOtherCalls();
}
@@ -106,18 +147,19 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act
client.Hostname = "localhost";
client.Port = 205;
- client.ReadTimeout = TimeSpan.FromSeconds(123);
- client.WriteTimeout = TimeSpan.FromSeconds(456);
- client.ReconnectTimeout = TimeSpan.FromSeconds(789);
- client.IdleTimeout = TimeSpan.FromSeconds(321);
+ client.IdleTimeout = TimeSpan.FromSeconds(40);
+ client.ConnectTimeout = TimeSpan.FromSeconds(30);
+ client.ReadTimeout = TimeSpan.FromSeconds(20);
+ client.WriteTimeout = TimeSpan.FromSeconds(10);
// Assert
_tcpConnectionMock.VerifySet(c => c.Hostname = "localhost", Times.Once);
_tcpConnectionMock.VerifySet(c => c.Port = 205, Times.Once);
- _tcpConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(123), Times.Once);
- _tcpConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(456), Times.Once);
- _tcpConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(789), Times.Once);
- _tcpConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(321), Times.Once);
+ _tcpConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(40), Times.Once);
+ _tcpConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(30), Times.Once);
+ _tcpConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(20), Times.Once);
+ _tcpConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(10), Times.Once);
+
_tcpConnectionMock.VerifyNoOtherCalls();
}
}
diff --git a/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpConnectionTest.cs b/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpConnectionTest.cs
index 5ca6cc4..4e2ca66 100644
--- a/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpConnectionTest.cs
+++ b/AMWD.Protocols.Modbus.Tests/Tcp/ModbusTcpConnectionTest.cs
@@ -4,7 +4,6 @@ using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Tcp;
using AMWD.Protocols.Modbus.Tcp.Utils;
using Moq;
@@ -528,18 +527,12 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
Port = 502
};
- // Replace real TCP client with mock
- var clientField = connection.GetType().GetField("_client", BindingFlags.NonPublic | BindingFlags.Instance);
- (clientField.GetValue(connection) as TcpClientWrapper)?.Dispose();
- clientField.SetValue(connection, _tcpClientMock.Object);
+ // Replace real connection with mock
+ var connectionField = connection.GetType().GetField("_tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
+ (connectionField.GetValue(connection) as TcpClientWrapper)?.Dispose();
+ connectionField.SetValue(connection, _tcpClientMock.Object);
return connection;
}
-
- private void ClearInvocations()
- {
- _networkStreamMock.Invocations.Clear();
- _tcpClientMock.Invocations.Clear();
- }
}
}