Added VirtualModbusClient to Common
This commit is contained in:
@@ -8,19 +8,26 @@ namespace AMWD.Protocols.Modbus.Common.Events
|
|||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class CoilWrittenEventArgs : EventArgs
|
public class CoilWrittenEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
|
internal CoilWrittenEventArgs(byte unitId, ushort address, bool value)
|
||||||
|
{
|
||||||
|
UnitId = unitId;
|
||||||
|
Address = address;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unit id.
|
/// Gets or sets the unit id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte UnitId { get; set; }
|
public byte UnitId { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the coil address.
|
/// Gets or sets the coil address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Address { get; set; }
|
public ushort Address { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the coil value.
|
/// Gets or sets the coil value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Value { get; set; }
|
public bool Value { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,29 +8,39 @@ namespace AMWD.Protocols.Modbus.Common.Events
|
|||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class RegisterWrittenEventArgs : EventArgs
|
public class RegisterWrittenEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
|
internal RegisterWrittenEventArgs(byte unitId, ushort address, byte highByte, byte lowByte)
|
||||||
|
{
|
||||||
|
UnitId = unitId;
|
||||||
|
Address = address;
|
||||||
|
HighByte = highByte;
|
||||||
|
LowByte = lowByte;
|
||||||
|
|
||||||
|
Value = new[] { highByte, lowByte }.GetBigEndianUInt16();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unit id.
|
/// Gets or sets the unit id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte UnitId { get; set; }
|
public byte UnitId { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the address of the register.
|
/// Gets or sets the address of the register.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Address { get; set; }
|
public ushort Address { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value of the register.
|
/// Gets or sets the value of the register.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Value { get; set; }
|
public ushort Value { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the high byte of the register.
|
/// Gets or sets the high byte of the register.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte HighByte { get; set; }
|
public byte HighByte { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the low byte of the register.
|
/// Gets or sets the low byte of the register.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte LowByte { get; set; }
|
public byte LowByte { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace AMWD.Protocols.Modbus.Common
|
namespace AMWD.Protocols.Modbus.Common
|
||||||
@@ -12,14 +13,14 @@ namespace AMWD.Protocols.Modbus.Common
|
|||||||
Array.Reverse(bytes);
|
Array.Reverse(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ushort GetBigEndianUInt16(this byte[] bytes, int offset = 0)
|
public static ushort GetBigEndianUInt16(this IReadOnlyList<byte> bytes, int offset = 0)
|
||||||
{
|
{
|
||||||
byte[] b = bytes.Skip(offset).Take(2).ToArray();
|
byte[] b = bytes.Skip(offset).Take(2).ToArray();
|
||||||
b.SwapBigEndian();
|
b.SwapBigEndian();
|
||||||
return BitConverter.ToUInt16(b, 0);
|
return BitConverter.ToUInt16(b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] ToBigEndianBytes(this ushort value)
|
public static IReadOnlyList<byte> ToBigEndianBytes(this ushort value)
|
||||||
{
|
{
|
||||||
byte[] b = BitConverter.GetBytes(value);
|
byte[] b = BitConverter.GetBytes(value);
|
||||||
b.SwapBigEndian();
|
b.SwapBigEndian();
|
||||||
|
|||||||
@@ -17,15 +17,11 @@ namespace AMWD.Protocols.Modbus.Common
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
byte[] blob = [HighByte, LowByte];
|
return new[] { HighByte, LowByte }.GetBigEndianUInt16();
|
||||||
blob.SwapBigEndian();
|
|
||||||
return BitConverter.ToUInt16(blob, 0);
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
byte[] blob = BitConverter.GetBytes(value);
|
var blob = value.ToBigEndianBytes();
|
||||||
blob.SwapBigEndian();
|
|
||||||
|
|
||||||
HighByte = blob[0];
|
HighByte = blob[0];
|
||||||
LowByte = blob[1];
|
LowByte = blob[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ namespace AMWD.Protocols.Modbus.Common
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
byte[] blob = [HighByte, LowByte];
|
return new[] { HighByte, LowByte }.GetBigEndianUInt16();
|
||||||
blob.SwapBigEndian();
|
|
||||||
return BitConverter.ToUInt16(blob, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace AMWD.Protocols.Modbus.Common.Models
|
|||||||
/// Initializes a new instance of the <see cref="ModbusDevice"/> class.
|
/// Initializes a new instance of the <see cref="ModbusDevice"/> class.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="id">The <see cref="ModbusDevice"/> ID.</param>
|
/// <param name="id">The <see cref="ModbusDevice"/> ID.</param>
|
||||||
public class ModbusDevice(byte id) : IDisposable
|
internal class ModbusDevice(byte id) : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ReaderWriterLockSlim _rwLockCoils = new();
|
private readonly ReaderWriterLockSlim _rwLockCoils = new();
|
||||||
private readonly ReaderWriterLockSlim _rwLockDiscreteInputs = new();
|
private readonly ReaderWriterLockSlim _rwLockDiscreteInputs = new();
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadCoils:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadCoils:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// LRC
|
// LRC
|
||||||
@@ -151,11 +151,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadDiscreteInputs:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadDiscreteInputs:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// LRC
|
// LRC
|
||||||
@@ -209,11 +209,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadHoldingRegisters:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadHoldingRegisters:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// LRC
|
// LRC
|
||||||
@@ -264,11 +264,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadInputRegisters:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.ReadInputRegisters:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// LRC
|
// LRC
|
||||||
@@ -383,7 +383,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteSingleCoil:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteSingleCoil:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = coil.Address.ToBigEndianBytes();
|
var addrBytes = coil.Address.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
@@ -426,7 +426,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteSingleRegister:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteSingleRegister:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = register.Address.ToBigEndianBytes();
|
var addrBytes = register.Address.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
@@ -497,11 +497,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteMultipleCoils:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteMultipleCoils:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// Byte count
|
// Byte count
|
||||||
@@ -567,11 +567,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteMultipleRegisters:X2}";
|
string request = $":{unitId:X2}{(byte)ModbusFunctionCode.WriteMultipleRegisters:X2}";
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
request += $"{addrBytes[0]:X2}{addrBytes[1]:X2}";
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
request += $"{countBytes[0]:X2}{countBytes[1]:X2}";
|
||||||
|
|
||||||
// Byte count
|
// Byte count
|
||||||
|
|||||||
@@ -119,12 +119,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadCoils;
|
request[7] = (byte)ModbusFunctionCode.ReadCoils;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -182,12 +182,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
request[7] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -245,12 +245,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
request[7] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -305,12 +305,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
request[7] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -432,7 +432,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[7] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
request[7] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
||||||
|
|
||||||
byte[] addrBytes = coil.Address.ToBigEndianBytes();
|
var addrBytes = coil.Address.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[7] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
request[7] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
||||||
|
|
||||||
byte[] addrBytes = register.Address.ToBigEndianBytes();
|
var addrBytes = register.Address.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
@@ -542,12 +542,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
request[7] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -622,12 +622,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
request[7] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -747,7 +747,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
|
|
||||||
// Transaction id
|
// Transaction id
|
||||||
ushort txId = GetNextTransacitonId();
|
ushort txId = GetNextTransacitonId();
|
||||||
byte[] txBytes = txId.ToBigEndianBytes();
|
var txBytes = txId.ToBigEndianBytes();
|
||||||
header[0] = txBytes[0];
|
header[0] = txBytes[0];
|
||||||
header[1] = txBytes[1];
|
header[1] = txBytes[1];
|
||||||
|
|
||||||
@@ -756,7 +756,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
header[3] = 0x00;
|
header[3] = 0x00;
|
||||||
|
|
||||||
// Number of following bytes
|
// Number of following bytes
|
||||||
byte[] countBytes = ((ushort)followingBytes).ToBigEndianBytes();
|
var countBytes = ((ushort)followingBytes).ToBigEndianBytes();
|
||||||
header[4] = countBytes[0];
|
header[4] = countBytes[0];
|
||||||
header[5] = countBytes[1];
|
header[5] = countBytes[1];
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[1] = (byte)ModbusFunctionCode.ReadCoils;
|
request[1] = (byte)ModbusFunctionCode.ReadCoils;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
@@ -156,12 +156,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[1] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
request[1] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
@@ -216,12 +216,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[1] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
request[1] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
@@ -273,12 +273,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[1] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
request[1] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
@@ -394,7 +394,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[1] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
request[1] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
||||||
|
|
||||||
byte[] addrBytes = coil.Address.ToBigEndianBytes();
|
var addrBytes = coil.Address.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[1] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
request[1] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
||||||
|
|
||||||
byte[] addrBytes = register.Address.ToBigEndianBytes();
|
var addrBytes = register.Address.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
@@ -495,11 +495,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
|
|
||||||
request[1] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
request[1] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
||||||
|
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
@@ -565,11 +565,11 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[0] = unitId;
|
request[0] = unitId;
|
||||||
request[1] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
request[1] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
||||||
|
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[2] = addrBytes[0];
|
request[2] = addrBytes[0];
|
||||||
request[3] = addrBytes[1];
|
request[3] = addrBytes[1];
|
||||||
|
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[4] = countBytes[0];
|
request[4] = countBytes[0];
|
||||||
request[5] = countBytes[1];
|
request[5] = countBytes[1];
|
||||||
|
|
||||||
|
|||||||
@@ -101,12 +101,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadCoils;
|
request[7] = (byte)ModbusFunctionCode.ReadCoils;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -159,12 +159,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
request[7] = (byte)ModbusFunctionCode.ReadDiscreteInputs;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -217,12 +217,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
request[7] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -272,12 +272,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
request[7] = (byte)ModbusFunctionCode.ReadInputRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = startAddress.ToBigEndianBytes();
|
var addrBytes = startAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = count.ToBigEndianBytes();
|
var countBytes = count.ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[7] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
request[7] = (byte)ModbusFunctionCode.WriteSingleCoil;
|
||||||
|
|
||||||
byte[] addrBytes = coil.Address.ToBigEndianBytes();
|
var addrBytes = coil.Address.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
@@ -431,7 +431,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
// Function code
|
// Function code
|
||||||
request[7] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
request[7] = (byte)ModbusFunctionCode.WriteSingleRegister;
|
||||||
|
|
||||||
byte[] addrBytes = register.Address.ToBigEndianBytes();
|
var addrBytes = register.Address.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
@@ -489,12 +489,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
request[7] = (byte)ModbusFunctionCode.WriteMultipleCoils;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -564,12 +564,12 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
request[7] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
request[7] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
|
||||||
|
|
||||||
// Starting address
|
// Starting address
|
||||||
byte[] addrBytes = firstAddress.ToBigEndianBytes();
|
var addrBytes = firstAddress.ToBigEndianBytes();
|
||||||
request[8] = addrBytes[0];
|
request[8] = addrBytes[0];
|
||||||
request[9] = addrBytes[1];
|
request[9] = addrBytes[1];
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
byte[] countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
var countBytes = ((ushort)orderedList.Count).ToBigEndianBytes();
|
||||||
request[10] = countBytes[0];
|
request[10] = countBytes[0];
|
||||||
request[11] = countBytes[1];
|
request[11] = countBytes[1];
|
||||||
|
|
||||||
@@ -678,7 +678,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
|
|
||||||
// Transaction id
|
// Transaction id
|
||||||
ushort txId = GetNextTransacitonId();
|
ushort txId = GetNextTransacitonId();
|
||||||
byte[] txBytes = txId.ToBigEndianBytes();
|
var txBytes = txId.ToBigEndianBytes();
|
||||||
header[0] = txBytes[0];
|
header[0] = txBytes[0];
|
||||||
header[1] = txBytes[1];
|
header[1] = txBytes[1];
|
||||||
|
|
||||||
@@ -687,7 +687,7 @@ namespace AMWD.Protocols.Modbus.Common.Protocols
|
|||||||
header[3] = 0x00;
|
header[3] = 0x00;
|
||||||
|
|
||||||
// Number of following bytes
|
// Number of following bytes
|
||||||
byte[] countBytes = ((ushort)followingBytes).ToBigEndianBytes();
|
var countBytes = ((ushort)followingBytes).ToBigEndianBytes();
|
||||||
header[4] = countBytes[0];
|
header[4] = countBytes[0];
|
||||||
header[5] = countBytes[1];
|
header[5] = countBytes[1];
|
||||||
|
|
||||||
|
|||||||
478
AMWD.Protocols.Modbus.Common/Protocols/VirtualProtocol.cs
Normal file
478
AMWD.Protocols.Modbus.Common/Protocols/VirtualProtocol.cs
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Contracts;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Events;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Models;
|
||||||
|
|
||||||
|
namespace AMWD.Protocols.Modbus.Common.Protocols
|
||||||
|
{
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
internal class VirtualProtocol : IModbusProtocol, IDisposable
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
private readonly ReaderWriterLockSlim _deviceListLock = new();
|
||||||
|
private readonly Dictionary<byte, ModbusDevice> _devices = [];
|
||||||
|
|
||||||
|
#endregion Fields
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
|
||||||
|
_deviceListLock.Dispose();
|
||||||
|
|
||||||
|
foreach (var device in _devices.Values)
|
||||||
|
device.Dispose();
|
||||||
|
|
||||||
|
_devices.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
public event EventHandler<CoilWrittenEventArgs> CoilWritten;
|
||||||
|
|
||||||
|
public event EventHandler<RegisterWrittenEventArgs> RegisterWritten;
|
||||||
|
|
||||||
|
#endregion Events
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Name => nameof(VirtualProtocol);
|
||||||
|
|
||||||
|
#endregion Properties
|
||||||
|
|
||||||
|
#region Protocol
|
||||||
|
|
||||||
|
public bool CheckResponseComplete(IReadOnlyList<byte> responseBytes) => true;
|
||||||
|
|
||||||
|
public IReadOnlyList<Coil> DeserializeReadCoils(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
return Enumerable.Range(0, count)
|
||||||
|
.Select(i => device.GetCoil((ushort)(start + i)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceIdentificationRaw DeserializeReadDeviceIdentification(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
var result = new DeviceIdentificationRaw
|
||||||
|
{
|
||||||
|
AllowsIndividualAccess = false,
|
||||||
|
MoreRequestsNeeded = false,
|
||||||
|
Objects = []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (response[1] >= 1)
|
||||||
|
{
|
||||||
|
string version = GetType().Assembly
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
|
.InformationalVersion;
|
||||||
|
|
||||||
|
result.Objects.Add(0, Encoding.UTF8.GetBytes("AM.WD"));
|
||||||
|
result.Objects.Add(1, Encoding.UTF8.GetBytes("AMWD.Protocols.Modbus"));
|
||||||
|
result.Objects.Add(2, Encoding.UTF8.GetBytes(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response[1] >= 2)
|
||||||
|
{
|
||||||
|
result.Objects.Add(3, Encoding.UTF8.GetBytes("https://github.com/AM-WD/AMWD.Protocols.Modbus"));
|
||||||
|
result.Objects.Add(4, Encoding.UTF8.GetBytes("Modbus Protocol for .NET"));
|
||||||
|
result.Objects.Add(5, Encoding.UTF8.GetBytes("Virtual Device"));
|
||||||
|
result.Objects.Add(6, Encoding.UTF8.GetBytes("Virtual Modbus Client"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response[1] >= 3)
|
||||||
|
{
|
||||||
|
for (int i = 128; i < 256; i++)
|
||||||
|
result.Objects.Add((byte)i, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DiscreteInput> DeserializeReadDiscreteInputs(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
return Enumerable.Range(0, count)
|
||||||
|
.Select(i => device.GetDiscreteInput((ushort)(start + i)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<HoldingRegister> DeserializeReadHoldingRegisters(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
return Enumerable.Range(0, count)
|
||||||
|
.Select(i => device.GetHoldingRegister((ushort)(start + i)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<InputRegister> DeserializeReadInputRegisters(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
return Enumerable.Range(0, count)
|
||||||
|
.Select(i => device.GetInputRegister((ushort)(start + i)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public (ushort FirstAddress, ushort NumberOfCoils) DeserializeWriteMultipleCoils(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var coil = new Coil
|
||||||
|
{
|
||||||
|
Address = (ushort)(start + i),
|
||||||
|
HighByte = response[5 + i]
|
||||||
|
};
|
||||||
|
device.SetCoil(coil);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CoilWritten?.Invoke(this, new CoilWrittenEventArgs(
|
||||||
|
unitId: response[0],
|
||||||
|
address: coil.Address,
|
||||||
|
value: coil.Value));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (start, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public (ushort FirstAddress, ushort NumberOfRegisters) DeserializeWriteMultipleHoldingRegisters(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
ushort start = response.GetBigEndianUInt16(1);
|
||||||
|
ushort count = response.GetBigEndianUInt16(3);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var register = new HoldingRegister
|
||||||
|
{
|
||||||
|
Address = (ushort)(start + i),
|
||||||
|
HighByte = response[5 + i * 2],
|
||||||
|
LowByte = response[5 + i * 2 + 1]
|
||||||
|
};
|
||||||
|
device.SetHoldingRegister(register);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegisterWritten?.Invoke(this, new RegisterWrittenEventArgs(
|
||||||
|
unitId: response[0],
|
||||||
|
address: register.Address,
|
||||||
|
highByte: register.HighByte,
|
||||||
|
lowByte: register.LowByte));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (start, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coil DeserializeWriteSingleCoil(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
var coil = new Coil
|
||||||
|
{
|
||||||
|
Address = response.GetBigEndianUInt16(1),
|
||||||
|
HighByte = response[3]
|
||||||
|
};
|
||||||
|
device.SetCoil(coil);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CoilWritten?.Invoke(this, new CoilWrittenEventArgs(
|
||||||
|
unitId: response[0],
|
||||||
|
address: coil.Address,
|
||||||
|
value: coil.Value));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return coil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HoldingRegister DeserializeWriteSingleHoldingRegister(IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!_devices.TryGetValue(response[0], out var device))
|
||||||
|
throw new TimeoutException("Device not found.");
|
||||||
|
|
||||||
|
var register = new HoldingRegister
|
||||||
|
{
|
||||||
|
Address = response.GetBigEndianUInt16(1),
|
||||||
|
HighByte = response[3],
|
||||||
|
LowByte = response[4]
|
||||||
|
};
|
||||||
|
device.SetHoldingRegister(register);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegisterWritten?.Invoke(this, new RegisterWrittenEventArgs(
|
||||||
|
unitId: response[0],
|
||||||
|
address: register.Address,
|
||||||
|
highByte: register.HighByte,
|
||||||
|
lowByte: register.LowByte));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return register;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeReadCoils(byte unitId, ushort startAddress, ushort count)
|
||||||
|
{
|
||||||
|
return [unitId, .. startAddress.ToBigEndianBytes(), .. count.ToBigEndianBytes()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeReadDeviceIdentification(byte unitId, ModbusDeviceIdentificationCategory category, ModbusDeviceIdentificationObject objectId)
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(ModbusDeviceIdentificationCategory), category))
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(category));
|
||||||
|
|
||||||
|
return [unitId, (byte)category];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeReadDiscreteInputs(byte unitId, ushort startAddress, ushort count)
|
||||||
|
{
|
||||||
|
return [unitId, .. startAddress.ToBigEndianBytes(), .. count.ToBigEndianBytes()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeReadHoldingRegisters(byte unitId, ushort startAddress, ushort count)
|
||||||
|
{
|
||||||
|
return [unitId, .. startAddress.ToBigEndianBytes(), .. count.ToBigEndianBytes()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeReadInputRegisters(byte unitId, ushort startAddress, ushort count)
|
||||||
|
{
|
||||||
|
return [unitId, .. startAddress.ToBigEndianBytes(), .. count.ToBigEndianBytes()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeWriteMultipleCoils(byte unitId, IReadOnlyList<Coil> coils)
|
||||||
|
{
|
||||||
|
ushort start = coils.OrderBy(c => c.Address).First().Address;
|
||||||
|
ushort count = (ushort)coils.Count;
|
||||||
|
byte[] values = coils.Select(c => c.HighByte).ToArray();
|
||||||
|
|
||||||
|
return [unitId, .. start.ToBigEndianBytes(), .. count.ToBigEndianBytes(), .. values];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeWriteMultipleHoldingRegisters(byte unitId, IReadOnlyList<HoldingRegister> registers)
|
||||||
|
{
|
||||||
|
ushort start = registers.OrderBy(c => c.Address).First().Address;
|
||||||
|
ushort count = (ushort)registers.Count;
|
||||||
|
byte[] values = registers.SelectMany(r => new[] { r.HighByte, r.LowByte }).ToArray();
|
||||||
|
|
||||||
|
return [unitId, .. start.ToBigEndianBytes(), .. count.ToBigEndianBytes(), .. values];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeWriteSingleCoil(byte unitId, Coil coil)
|
||||||
|
{
|
||||||
|
return [unitId, .. coil.Address.ToBigEndianBytes(), coil.HighByte];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<byte> SerializeWriteSingleHoldingRegister(byte unitId, HoldingRegister register)
|
||||||
|
{
|
||||||
|
return [unitId, .. register.Address.ToBigEndianBytes(), register.HighByte, register.LowByte];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ValidateResponse(IReadOnlyList<byte> request, IReadOnlyList<byte> response)
|
||||||
|
{
|
||||||
|
if (!request.SequenceEqual(response))
|
||||||
|
throw new InvalidOperationException("Request and response have to be the same on virtual protocol.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Protocol
|
||||||
|
|
||||||
|
#region Device Handling
|
||||||
|
|
||||||
|
public bool AddDevice(byte unitId)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.ContainsKey(unitId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_devices.Add(unitId, new ModbusDevice(unitId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveDevice(byte unitId)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.ContainsKey(unitId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _devices.Remove(unitId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Device Handling
|
||||||
|
|
||||||
|
#region Entity Handling
|
||||||
|
|
||||||
|
public Coil GetCoil(byte unitId, ushort address)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetReadLock())
|
||||||
|
{
|
||||||
|
return _devices.TryGetValue(unitId, out var device)
|
||||||
|
? device.GetCoil(address)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCoil(byte unitId, Coil coil)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.TryGetValue(unitId, out var device))
|
||||||
|
device.SetCoil(coil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscreteInput GetDiscreteInput(byte unitId, ushort address)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetReadLock())
|
||||||
|
{
|
||||||
|
return _devices.TryGetValue(unitId, out var device)
|
||||||
|
? device.GetDiscreteInput(address)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDiscreteInput(byte unitId, DiscreteInput discreteInput)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.TryGetValue(unitId, out var device))
|
||||||
|
device.SetDiscreteInput(discreteInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HoldingRegister GetHoldingRegister(byte unitId, ushort address)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetReadLock())
|
||||||
|
{
|
||||||
|
return _devices.TryGetValue(unitId, out var device)
|
||||||
|
? device.GetHoldingRegister(address)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetHoldingRegister(byte unitId, HoldingRegister holdingRegister)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.TryGetValue(unitId, out var device))
|
||||||
|
device.SetHoldingRegister(holdingRegister);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputRegister GetInputRegister(byte unitId, ushort address)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetReadLock())
|
||||||
|
{
|
||||||
|
return _devices.TryGetValue(unitId, out var device)
|
||||||
|
? device.GetInputRegister(address)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInputRegister(byte unitId, InputRegister inputRegister)
|
||||||
|
{
|
||||||
|
Assertions();
|
||||||
|
using (_deviceListLock.GetWriteLock())
|
||||||
|
{
|
||||||
|
if (_devices.TryGetValue(unitId, out var device))
|
||||||
|
device.SetInputRegister(inputRegister);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Entity Handling
|
||||||
|
|
||||||
|
private void Assertions()
|
||||||
|
{
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
ObjectDisposedException.ThrowIf(_isDisposed, this);
|
||||||
|
#else
|
||||||
|
if (_isDisposed)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,8 @@ The different types handled by the Modbus Protocol.
|
|||||||
In addition, you'll find the `DeviceIdentification` there.
|
In addition, you'll find the `DeviceIdentification` there.
|
||||||
It is used for a "special" function called _Read Device Identification_ (0x2B / 43), not supported on all devices.
|
It is used for a "special" function called _Read Device Identification_ (0x2B / 43), not supported on all devices.
|
||||||
|
|
||||||
The `ModbusDevice` is used for the server implementations in the derived packages.
|
The `ModbusDevice` is used for the `VirtualModbusClient`.
|
||||||
|
In combination with the *Proxy implementations (in the derived packages) it can be used as server.
|
||||||
|
|
||||||
|
|
||||||
### Protocols
|
### Protocols
|
||||||
|
|||||||
179
AMWD.Protocols.Modbus.Common/Utils/VirtualModbusClient.cs
Normal file
179
AMWD.Protocols.Modbus.Common/Utils/VirtualModbusClient.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Contracts;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Events;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Models;
|
||||||
|
using AMWD.Protocols.Modbus.Common.Protocols;
|
||||||
|
|
||||||
|
namespace AMWD.Protocols.Modbus.Common.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements a virtual Modbus client.
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class VirtualModbusClient : ModbusClientBase
|
||||||
|
{
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="VirtualModbusClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><strong>DO NOT MODIFY</strong> connection or protocol.</remarks>
|
||||||
|
public VirtualModbusClient()
|
||||||
|
: base(new VirtualConnection())
|
||||||
|
{
|
||||||
|
Protocol = new VirtualProtocol();
|
||||||
|
|
||||||
|
TypedProtocol.CoilWritten += (sender, e) => CoilWritten?.Invoke(this, e);
|
||||||
|
TypedProtocol.RegisterWritten += (sender, e) => RegisterWritten?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Constructor
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that a <see cref="Coil"/>-value received through a remote client has been written.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<CoilWrittenEventArgs> CoilWritten;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that a <see cref="HoldingRegister"/>-value received from a remote client has been written.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<RegisterWrittenEventArgs> RegisterWritten;
|
||||||
|
|
||||||
|
#endregion Events
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
internal VirtualProtocol TypedProtocol => Protocol as VirtualProtocol;
|
||||||
|
|
||||||
|
#endregion Properties
|
||||||
|
|
||||||
|
#region Device Handling
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a device to the virtual client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit id of the device.</param>
|
||||||
|
/// <returns><see langword="true"/> if the device was added successfully, <see langword="false"/> otherwise.</returns>
|
||||||
|
public bool AddDevice(byte unitId)
|
||||||
|
=> TypedProtocol.AddDevice(unitId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a device from the virtual client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit id of the device.</param>
|
||||||
|
/// <returns><see langword="true"/> if the device was removed successfully, <see langword="false"/> otherwise.</returns>
|
||||||
|
public bool RemoveDevice(byte unitId)
|
||||||
|
=> TypedProtocol.RemoveDevice(unitId);
|
||||||
|
|
||||||
|
#endregion Device Handling
|
||||||
|
|
||||||
|
#region Entity Handling
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Coil"/> from the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="address">The address of the <see cref="Coil"/>.</param>
|
||||||
|
public Coil GetCoil(byte unitId, ushort address)
|
||||||
|
=> TypedProtocol.GetCoil(unitId, address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="Coil"/> to the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="coil">The <see cref="Coil"/> to set.</param>
|
||||||
|
public void SetCoil(byte unitId, Coil coil)
|
||||||
|
=> TypedProtocol.SetCoil(unitId, coil);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="DiscreteInput"/> from the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="address">The address of the <see cref="DiscreteInput"/>.</param>
|
||||||
|
public DiscreteInput GetDiscreteInput(byte unitId, ushort address)
|
||||||
|
=> TypedProtocol.GetDiscreteInput(unitId, address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="DiscreteInput"/> to the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="discreteInput">The <see cref="DiscreteInput"/> to set.</param>
|
||||||
|
public void SetDiscreteInput(byte unitId, DiscreteInput discreteInput)
|
||||||
|
=> TypedProtocol.SetDiscreteInput(unitId, discreteInput);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="HoldingRegister"/> from the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="address">The address of the <see cref="HoldingRegister"/>.</param>
|
||||||
|
public HoldingRegister GetHoldingRegister(byte unitId, ushort address)
|
||||||
|
=> TypedProtocol.GetHoldingRegister(unitId, address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="HoldingRegister"/> to the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="holdingRegister">The <see cref="HoldingRegister"/> to set.</param>
|
||||||
|
public void SetHoldingRegister(byte unitId, HoldingRegister holdingRegister)
|
||||||
|
=> TypedProtocol.SetHoldingRegister(unitId, holdingRegister);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="InputRegister"/> from the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="address">The address of the <see cref="InputRegister"/>.</param>
|
||||||
|
public InputRegister GetInputRegister(byte unitId, ushort address)
|
||||||
|
=> TypedProtocol.GetInputRegister(unitId, address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a <see cref="InputRegister"/> to the specified <see cref="ModbusDevice"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unitId">The unit ID of the device.</param>
|
||||||
|
/// <param name="inputRegister">The <see cref="InputRegister"/> to set.</param>
|
||||||
|
public void SetInputRegister(byte unitId, InputRegister inputRegister)
|
||||||
|
=> TypedProtocol.SetInputRegister(unitId, inputRegister);
|
||||||
|
|
||||||
|
#endregion Entity Handling
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
TypedProtocol.Dispose();
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Methods
|
||||||
|
|
||||||
|
#region Connection
|
||||||
|
|
||||||
|
internal class VirtualConnection : IModbusConnection
|
||||||
|
{
|
||||||
|
public string Name => nameof(VirtualConnection);
|
||||||
|
|
||||||
|
public TimeSpan IdleTimeout { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan ConnectTimeout { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan ReadTimeout { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan WriteTimeout { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{ /* nothing to do */ }
|
||||||
|
|
||||||
|
public Task<IReadOnlyList<byte>> InvokeAsync(
|
||||||
|
IReadOnlyList<byte> request,
|
||||||
|
Func<IReadOnlyList<byte>, bool> validateResponseComplete,
|
||||||
|
CancellationToken cancellationToken = default) => Task.FromResult(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Connection
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Small CLI tool to test Modbus client communication.
|
- Small CLI tool to test Modbus client communication.
|
||||||
|
- `VirtualModbusClient` added to `AMWD.Protocols.Modbus.Common`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user