diff --git a/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs b/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs
index de7604d..d3e0f69 100644
--- a/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs
+++ b/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
namespace AMWD.Protocols.Modbus.Common
{
@@ -10,5 +11,19 @@ namespace AMWD.Protocols.Modbus.Common
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
}
+
+ public static ushort NetworkUInt16(this byte[] bytes, int offset = 0)
+ {
+ byte[] b = bytes.Skip(offset).Take(2).ToArray();
+ b.SwapNetworkOrder();
+ return BitConverter.ToUInt16(b, 0);
+ }
+
+ public static byte[] ToNetworkBytes(this ushort value)
+ {
+ byte[] b = BitConverter.GetBytes(value);
+ b.SwapNetworkOrder();
+ return b;
+ }
}
}
diff --git a/AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs b/AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs
index d8e0288..b15eca9 100644
--- a/AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs
+++ b/AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace System
{
// ================================================================================================================================== //
- // Source: https://git.am-wd.de/am.wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Extensions/EnumExtensions.cs //
+ // Source: https://git.am-wd.de/am-wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Extensions/EnumExtensions.cs //
// ================================================================================================================================== //
[Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static class EnumExtensions
diff --git a/AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs b/AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs
new file mode 100644
index 0000000..e0fcec6
--- /dev/null
+++ b/AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs
@@ -0,0 +1,92 @@
+namespace System.Threading
+{
+ // ================================================================================================================================================== //
+ // Source: https://git.am-wd.de/am-wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Extensions/ReaderWriterLockSlimExtensions.cs //
+ // ================================================================================================================================================== //
+ [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ internal static class ReaderWriterLockSlimExtensions
+ {
+ ///
+ /// Acquires a read lock on a lock object that can be released with an
+ /// instance.
+ ///
+ /// The lock object.
+ /// The number of milliseconds to wait, or -1
+ /// () to wait indefinitely.
+ /// An instance to release the lock.
+ public static IDisposable GetReadLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
+ {
+ if (!rwLock.TryEnterReadLock(timeoutMilliseconds))
+ throw new TimeoutException("The read lock could not be acquired.");
+
+ return new DisposableReadWriteLock(rwLock, LockMode.Read);
+ }
+
+ ///
+ /// Acquires a upgradeable read lock on a lock object that can be released with an
+ /// instance. The lock can be upgraded to a write lock temporarily
+ /// with or until the lock is released with
+ /// alone.
+ ///
+ /// The lock object.
+ /// The number of milliseconds to wait, or -1
+ /// () to wait indefinitely.
+ /// An instance to release the lock. If the lock was
+ /// upgraded to a write lock, that will be released as well.
+ public static IDisposable GetUpgradeableReadLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
+ {
+ if (!rwLock.TryEnterUpgradeableReadLock(timeoutMilliseconds))
+ throw new TimeoutException("The upgradeable read lock could not be acquired.");
+
+ return new DisposableReadWriteLock(rwLock, LockMode.Upgradable);
+ }
+
+ ///
+ /// Acquires a write lock on a lock object that can be released with an
+ /// instance.
+ ///
+ /// The lock object.
+ /// The number of milliseconds to wait, or -1
+ /// () to wait indefinitely.
+ /// An instance to release the lock.
+ public static IDisposable GetWriteLock(this ReaderWriterLockSlim rwLock, int timeoutMilliseconds = -1)
+ {
+ if (!rwLock.TryEnterWriteLock(timeoutMilliseconds))
+ throw new TimeoutException("The write lock could not be acquired.");
+
+ return new DisposableReadWriteLock(rwLock, LockMode.Write);
+ }
+
+ private struct DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
+ : IDisposable
+ {
+ private readonly ReaderWriterLockSlim _rwLock = rwLock;
+ private LockMode _lockMode = lockMode;
+
+ public void Dispose()
+ {
+ if (_lockMode == LockMode.Read)
+ _rwLock.ExitReadLock();
+
+ if (_lockMode == LockMode.Upgradable && _rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
+ _rwLock.ExitWriteLock();
+
+ if (_lockMode == LockMode.Upgradable)
+ _rwLock.ExitUpgradeableReadLock();
+
+ if (_lockMode == LockMode.Write)
+ _rwLock.ExitWriteLock();
+
+ _lockMode = LockMode.None;
+ }
+ }
+
+ private enum LockMode
+ {
+ None = 0,
+ Read = 1,
+ Upgradable = 2,
+ Write = 3,
+ }
+ }
+}
diff --git a/AMWD.Protocols.Modbus.Common/Models/ModbusDevice.cs b/AMWD.Protocols.Modbus.Common/Models/ModbusDevice.cs
new file mode 100644
index 0000000..b7b23a2
--- /dev/null
+++ b/AMWD.Protocols.Modbus.Common/Models/ModbusDevice.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace AMWD.Protocols.Modbus.Common.Models
+{
+ ///
+ /// Represents a Modbus device used in a Modbus server implementation.
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ID.
+ public class ModbusDevice(byte id) : IDisposable
+ {
+ private readonly ReaderWriterLockSlim _rwLockCoils = new();
+ private readonly ReaderWriterLockSlim _rwLockDiscreteInputs = new();
+ private readonly ReaderWriterLockSlim _rwLockHoldingRegisters = new();
+ private readonly ReaderWriterLockSlim _rwLockInputRegisters = new();
+
+ private readonly HashSet _coils = [];
+ private readonly HashSet _discreteInputs = [];
+ private readonly Dictionary _holdingRegisters = [];
+ private readonly Dictionary _inputRegisters = [];
+
+ private bool _isDisposed;
+
+ ///
+ /// Gets the ID of the .
+ ///
+ public byte Id { get; } = id;
+
+ ///
+ /// Releases the unmanaged resources used by the
+ /// and optionally also discards the managed resources.
+ ///
+ public void Dispose()
+ {
+ if (_isDisposed)
+ return;
+
+ _isDisposed = true;
+
+ _rwLockCoils.Dispose();
+ _rwLockDiscreteInputs.Dispose();
+ _rwLockHoldingRegisters.Dispose();
+ _rwLockInputRegisters.Dispose();
+
+ _coils.Clear();
+ _discreteInputs.Clear();
+ _holdingRegisters.Clear();
+ _inputRegisters.Clear();
+ }
+
+ ///
+ /// Gets a from the .
+ ///
+ /// The address of the .
+ public Coil GetCoil(ushort address)
+ {
+ Assertions();
+ using (_rwLockCoils.GetReadLock())
+ {
+ return new Coil
+ {
+ Address = address,
+ HighByte = (byte)(_coils.Contains(address) ? 0xFF : 0x00)
+ };
+ }
+ }
+
+ ///
+ /// Sets a to the .
+ ///
+ /// The to set.
+ public void SetCoil(Coil coil)
+ {
+ Assertions();
+ using (_rwLockCoils.GetWriteLock())
+ {
+ if (coil.Value)
+ _coils.Add(coil.Address);
+ else
+ _coils.Remove(coil.Address);
+ }
+ }
+
+ ///
+ /// Gets a from the .
+ ///
+ /// The address of the .
+ public DiscreteInput GetDiscreteInput(ushort address)
+ {
+ Assertions();
+ using (_rwLockDiscreteInputs.GetReadLock())
+ {
+ return new DiscreteInput
+ {
+ Address = address,
+ HighByte = (byte)(_discreteInputs.Contains(address) ? 0xFF : 0x00)
+ };
+ }
+ }
+
+ ///
+ /// Sets a to the .
+ ///
+ /// The to set.
+ public void SetDiscreteInput(DiscreteInput input)
+ {
+ using (_rwLockDiscreteInputs.GetWriteLock())
+ {
+ if (input.Value)
+ _discreteInputs.Add(input.Address);
+ else
+ _discreteInputs.Remove(input.Address);
+ }
+ }
+
+ ///
+ /// Gets a from the .
+ ///
+ /// The address of the .
+ public HoldingRegister GetHoldingRegister(ushort address)
+ {
+ Assertions();
+ using (_rwLockHoldingRegisters.GetReadLock())
+ {
+ if (!_holdingRegisters.TryGetValue(address, out ushort value))
+ value = 0x0000;
+
+ byte[] blob = BitConverter.GetBytes(value);
+ blob.SwapNetworkOrder();
+
+ return new HoldingRegister
+ {
+ Address = address,
+ HighByte = blob[0],
+ LowByte = blob[1]
+ };
+ }
+ }
+
+ ///
+ /// Sets a to the .
+ ///
+ /// The to set.
+ public void SetHoldingRegister(HoldingRegister register)
+ {
+ Assertions();
+ using (_rwLockHoldingRegisters.GetWriteLock())
+ {
+ if (register.Value == 0)
+ {
+ _holdingRegisters.Remove(register.Address);
+ return;
+ }
+
+ byte[] blob = [register.HighByte, register.LowByte];
+ blob.SwapNetworkOrder();
+ _holdingRegisters[register.Address] = BitConverter.ToUInt16(blob, 0);
+ }
+ }
+
+ ///
+ /// Gets a from the .
+ ///
+ /// The address of the .
+ public InputRegister GetInputRegister(ushort address)
+ {
+ Assertions();
+ using (_rwLockInputRegisters.GetReadLock())
+ {
+ if (!_inputRegisters.TryGetValue(address, out ushort value))
+ value = 0x0000;
+
+ byte[] blob = BitConverter.GetBytes(value);
+ blob.SwapNetworkOrder();
+
+ return new InputRegister
+ {
+ Address = address,
+ HighByte = blob[0],
+ LowByte = blob[1]
+ };
+ }
+ }
+
+ ///
+ /// Sets a to the .
+ ///
+ /// The to set.
+ public void SetInputRegister(InputRegister register)
+ {
+ Assertions();
+ using (_rwLockInputRegisters.GetWriteLock())
+ {
+ if (register.Value == 0)
+ {
+ _inputRegisters.Remove(register.Address);
+ return;
+ }
+
+ byte[] blob = [register.HighByte, register.LowByte];
+ blob.SwapNetworkOrder();
+ _inputRegisters[register.Address] = BitConverter.ToUInt16(blob, 0);
+ }
+ }
+
+ private void Assertions()
+ {
+#if NET8_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
+ if (_isDisposed)
+ throw new ObjectDisposedException(GetType().FullName);
+#endif
+ }
+ }
+}
diff --git a/AMWD.Protocols.Modbus.Common/Protocols/TcpProtocol.cs b/AMWD.Protocols.Modbus.Common/Protocols/TcpProtocol.cs
index 0107cc7..868f8c8 100644
--- a/AMWD.Protocols.Modbus.Common/Protocols/TcpProtocol.cs
+++ b/AMWD.Protocols.Modbus.Common/Protocols/TcpProtocol.cs
@@ -89,12 +89,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.ReadCoils;
// Starting address
- byte[] addrBytes = ToNetworkBytes(startAddress);
+ byte[] addrBytes = startAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
// Quantity
- byte[] countBytes = ToNetworkBytes(count);
+ byte[] countBytes = count.ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -144,12 +144,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
// Starting address
- byte[] addrBytes = ToNetworkBytes(startAddress);
+ byte[] addrBytes = startAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
// Quantity
- byte[] countBytes = ToNetworkBytes(count);
+ byte[] countBytes = count.ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -199,12 +199,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
// Starting address
- byte[] addrBytes = ToNetworkBytes(startAddress);
+ byte[] addrBytes = startAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
// Quantity
- byte[] countBytes = ToNetworkBytes(count);
+ byte[] countBytes = count.ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -251,12 +251,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.ReadInputRegisters;
// Starting address
- byte[] addrBytes = ToNetworkBytes(startAddress);
+ byte[] addrBytes = startAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
// Quantity
- byte[] countBytes = ToNetworkBytes(count);
+ byte[] countBytes = count.ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -362,7 +362,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
// Function code
request[7] = (byte)ModbusFunctionCode.WriteSingleCoil;
- byte[] addrBytes = ToNetworkBytes(coil.Address);
+ byte[] addrBytes = coil.Address.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
@@ -377,7 +377,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
{
return new Coil
{
- Address = ToNetworkUInt16(response.Skip(8).Take(2).ToArray()),
+ Address = response.ToArray().NetworkUInt16(8),
HighByte = response[10],
LowByte = response[11]
};
@@ -401,7 +401,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
// Function code
request[7] = (byte)ModbusFunctionCode.WriteSingleRegister;
- byte[] addrBytes = ToNetworkBytes(register.Address);
+ byte[] addrBytes = register.Address.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
@@ -416,7 +416,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
{
return new HoldingRegister
{
- Address = ToNetworkUInt16(response.Skip(8).Take(2).ToArray()),
+ Address = response.ToArray().NetworkUInt16(8),
HighByte = response[10],
LowByte = response[11]
};
@@ -454,11 +454,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.WriteMultipleCoils;
- byte[] addrBytes = ToNetworkBytes(firstAddress);
+ byte[] addrBytes = firstAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
- byte[] countBytes = ToNetworkBytes((ushort)orderedList.Count);
+ byte[] countBytes = ((ushort)orderedList.Count).ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -483,8 +483,8 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
///
public (ushort FirstAddress, ushort NumberOfCoils) DeserializeWriteMultipleCoils(IReadOnlyList response)
{
- ushort firstAddress = ToNetworkUInt16(response.Skip(8).Take(2).ToArray());
- ushort numberOfCoils = ToNetworkUInt16(response.Skip(10).Take(2).ToArray());
+ ushort firstAddress = response.ToArray().NetworkUInt16(8);
+ ushort numberOfCoils = response.ToArray().NetworkUInt16(10);
return (firstAddress, numberOfCoils);
}
@@ -521,11 +521,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
request[7] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
- byte[] addrBytes = ToNetworkBytes(firstAddress);
+ byte[] addrBytes = firstAddress.ToNetworkBytes();
request[8] = addrBytes[0];
request[9] = addrBytes[1];
- byte[] countBytes = ToNetworkBytes((ushort)orderedList.Count);
+ byte[] countBytes = ((ushort)orderedList.Count).ToNetworkBytes();
request[10] = countBytes[0];
request[11] = countBytes[1];
@@ -544,8 +544,8 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
///
public (ushort FirstAddress, ushort NumberOfRegisters) DeserializeWriteMultipleHoldingRegisters(IReadOnlyList response)
{
- ushort firstAddress = ToNetworkUInt16(response.Skip(8).Take(2).ToArray());
- ushort numberOfRegisters = ToNetworkUInt16(response.Skip(10).Take(2).ToArray());
+ ushort firstAddress = response.ToArray().NetworkUInt16(8);
+ ushort numberOfRegisters = response.ToArray().NetworkUInt16(10);
return (firstAddress, numberOfRegisters);
}
@@ -563,7 +563,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
if (responseBytes.Count < 6)
return false;
- ushort followingBytes = ToNetworkUInt16(responseBytes.Skip(4).Take(2).ToArray());
+ ushort followingBytes = responseBytes.ToArray().NetworkUInt16(4);
if (responseBytes.Count < followingBytes + 6)
return false;
@@ -582,7 +582,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
if (request[2] != response[2] || request[3] != response[3])
throw new ModbusException("Protocol Identifier does not match.");
- ushort count = ToNetworkUInt16(response.Skip(4).Take(2).ToArray());
+ ushort count = response.ToArray().NetworkUInt16(4);
if (count != response.Count - 6)
throw new ModbusException("Number of following bytes does not match.");
@@ -636,7 +636,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
// Transaction id
ushort txId = GetNextTransacitonId();
- byte[] txBytes = ToNetworkBytes(txId);
+ byte[] txBytes = txId.ToNetworkBytes();
header[0] = txBytes[0];
header[1] = txBytes[1];
@@ -645,7 +645,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
header[3] = 0x00;
// Number of following bytes
- byte[] countBytes = ToNetworkBytes((ushort)followingBytes);
+ byte[] countBytes = ((ushort)followingBytes).ToNetworkBytes();
header[4] = countBytes[0];
header[5] = countBytes[1];
@@ -655,24 +655,6 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
return header;
}
- private static byte[] ToNetworkBytes(ushort value)
- {
- byte[] bytes = BitConverter.GetBytes(value);
-
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
-
- return bytes;
- }
-
- private static ushort ToNetworkUInt16(byte[] bytes)
- {
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
-
- return BitConverter.ToUInt16(bytes, 0);
- }
-
#endregion Private helpers
}
}
diff --git a/AMWD.Protocols.Modbus.Tcp/Utils/ModbusTcpConnection.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs
similarity index 100%
rename from AMWD.Protocols.Modbus.Tcp/Utils/ModbusTcpConnection.cs
rename to AMWD.Protocols.Modbus.Tcp/ModbusTcpConnection.cs
diff --git a/AMWD.Protocols.Modbus.Tcp/Utils/AsyncQueue.cs b/AMWD.Protocols.Modbus.Tcp/Utils/AsyncQueue.cs
index af686e1..836e028 100644
--- a/AMWD.Protocols.Modbus.Tcp/Utils/AsyncQueue.cs
+++ b/AMWD.Protocols.Modbus.Tcp/Utils/AsyncQueue.cs
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
namespace System.Collections.Generic
{
// ============================================================================================================================= //
- // Source: https://git.am-wd.de/am.wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Utilities/AsyncQueue.cs //
+ // Source: https://git.am-wd.de/am-wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Utilities/AsyncQueue.cs //
// ============================================================================================================================= //
[Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class AsyncQueue
diff --git a/AMWD.Protocols.Modbus.Tests/Common/Models/ModbusDeviceTest.cs b/AMWD.Protocols.Modbus.Tests/Common/Models/ModbusDeviceTest.cs
new file mode 100644
index 0000000..d01ace4
--- /dev/null
+++ b/AMWD.Protocols.Modbus.Tests/Common/Models/ModbusDeviceTest.cs
@@ -0,0 +1,288 @@
+using System.Collections.Generic;
+using System.Reflection;
+using AMWD.Protocols.Modbus.Common.Models;
+
+namespace AMWD.Protocols.Modbus.Tests.Common.Models
+{
+ [TestClass]
+ public class ModbusDeviceTest
+ {
+ [TestMethod]
+ public void ShouldAllowMultipleDispose()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+
+ // Act
+ device.Dispose();
+ device.Dispose();
+
+ // Assert - no exception
+ }
+
+ [TestMethod]
+ public void ShouldAssertDisposed()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ device.Dispose();
+
+ // Act
+ try
+ {
+ device.GetCoil(111);
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.SetCoil(new Coil { Address = 222 });
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.GetDiscreteInput(111);
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.SetDiscreteInput(new DiscreteInput { Address = 222 });
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.GetHoldingRegister(111);
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.SetHoldingRegister(new HoldingRegister { Address = 222 });
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.GetInputRegister(111);
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+
+ try
+ {
+ device.SetInputRegister(new InputRegister { Address = 222 });
+ Assert.Fail();
+ }
+ catch (ObjectDisposedException)
+ { }
+ }
+
+ [TestMethod]
+ public void ShouldGetCoil()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((HashSet)device.GetType()
+ .GetField("_coils", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333);
+
+ // Act
+ var coilFalse = device.GetCoil(111);
+ var coilTrue = device.GetCoil(333);
+
+ // Assert
+ Assert.AreEqual(111, coilFalse.Address);
+ Assert.IsFalse(coilFalse.Value);
+
+ Assert.AreEqual(333, coilTrue.Address);
+ Assert.IsTrue(coilTrue.Value);
+ }
+
+ [TestMethod]
+ public void ShouldSetCoil()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((HashSet)device.GetType()
+ .GetField("_coils", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333);
+
+ // Act
+ device.SetCoil(new Coil { Address = 111, Value = true });
+ device.SetCoil(new Coil { Address = 333, Value = false });
+
+ // Assert
+ ushort[] coils = ((HashSet)device.GetType()
+ .GetField("_coils", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device)).ToArray();
+
+ Assert.AreEqual(1, coils.Length);
+ Assert.AreEqual(111, coils.First());
+ }
+
+ [TestMethod]
+ public void ShouldGetDiscreteInput()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((HashSet)device.GetType()
+ .GetField("_discreteInputs", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333);
+
+ // Act
+ var inputFalse = device.GetDiscreteInput(111);
+ var inputTrue = device.GetDiscreteInput(333);
+
+ // Assert
+ Assert.AreEqual(111, inputFalse.Address);
+ Assert.IsFalse(inputFalse.Value);
+
+ Assert.AreEqual(333, inputTrue.Address);
+ Assert.IsTrue(inputTrue.Value);
+ }
+
+ [TestMethod]
+ public void ShouldSetDiscreteInput()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((HashSet)device.GetType()
+ .GetField("_discreteInputs", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333);
+
+ // Act
+ device.SetDiscreteInput(new DiscreteInput { Address = 111, HighByte = 0xFF });
+ device.SetDiscreteInput(new DiscreteInput { Address = 333, HighByte = 0x00 });
+
+ // Assert
+ ushort[] discreteInputs = ((HashSet)device.GetType()
+ .GetField("_discreteInputs", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device)).ToArray();
+
+ Assert.AreEqual(1, discreteInputs.Length);
+ Assert.AreEqual(111, discreteInputs.First());
+ }
+
+ [TestMethod]
+ public void ShouldGetHoldingRegister()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((Dictionary)device.GetType()
+ .GetField("_holdingRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333, 42);
+
+ // Act
+ var zeroRegister = device.GetHoldingRegister(111);
+ var valueRegister = device.GetHoldingRegister(333);
+
+ // Assert
+ Assert.AreEqual(111, zeroRegister.Address);
+ Assert.AreEqual(0, zeroRegister.Value);
+ Assert.AreEqual(0x00, zeroRegister.HighByte);
+ Assert.AreEqual(0x00, zeroRegister.LowByte);
+
+ Assert.AreEqual(333, valueRegister.Address);
+ Assert.AreEqual(42, valueRegister.Value);
+ Assert.AreEqual(0x00, valueRegister.HighByte);
+ Assert.AreEqual(0x2A, valueRegister.LowByte);
+ }
+
+ [TestMethod]
+ public void ShouldSetHoldingRegister()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((Dictionary)device.GetType()
+ .GetField("_holdingRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333, 42);
+
+ // Act
+ device.SetHoldingRegister(new HoldingRegister { Address = 333, Value = 0 });
+ device.SetHoldingRegister(new HoldingRegister { Address = 111, Value = 42 });
+
+ // Assert
+ var registers = ((Dictionary)device.GetType()
+ .GetField("_holdingRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .ToDictionary(x => x.Key, x => x.Value);
+
+ Assert.AreEqual(1, registers.Count);
+ Assert.AreEqual(111, registers.First().Key);
+ Assert.AreEqual(42, registers.First().Value);
+ }
+
+ [TestMethod]
+ public void ShouldGetInputRegister()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((Dictionary)device.GetType()
+ .GetField("_inputRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333, 42);
+
+ // Act
+ var zeroRegister = device.GetInputRegister(111);
+ var valueRegister = device.GetInputRegister(333);
+
+ // Assert
+ Assert.AreEqual(111, zeroRegister.Address);
+ Assert.AreEqual(0, zeroRegister.Value);
+ Assert.AreEqual(0x00, zeroRegister.HighByte);
+ Assert.AreEqual(0x00, zeroRegister.LowByte);
+
+ Assert.AreEqual(333, valueRegister.Address);
+ Assert.AreEqual(42, valueRegister.Value);
+ Assert.AreEqual(0x00, valueRegister.HighByte);
+ Assert.AreEqual(0x2A, valueRegister.LowByte);
+ }
+
+ [TestMethod]
+ public void ShouldSetInputRegister()
+ {
+ // Arrange
+ var device = new ModbusDevice(123);
+ ((Dictionary)device.GetType()
+ .GetField("_inputRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .Add(333, 42);
+
+ // Act
+ device.SetInputRegister(new InputRegister { Address = 333, LowByte = 0 });
+ device.SetInputRegister(new InputRegister { Address = 111, LowByte = 42 });
+
+ // Assert
+ var registers = ((Dictionary)device.GetType()
+ .GetField("_inputRegisters", BindingFlags.NonPublic | BindingFlags.Instance)
+ .GetValue(device))
+ .ToDictionary(x => x.Key, x => x.Value);
+
+ Assert.AreEqual(1, registers.Count);
+ Assert.AreEqual(111, registers.First().Key);
+ Assert.AreEqual(42, registers.First().Value);
+ }
+ }
+}
diff --git a/Directory.Build.props b/Directory.Build.props
index 0a18424..3efe4ee 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,7 +8,7 @@
false
git
- https://github.com/AndreasAmMueller/AMWD.Protocols.Modbus.git
+ https://github.com/AM-WD/AMWD.Protocols.Modbus.git
true
true