Moving proxies to matching projects

This commit is contained in:
2025-01-23 21:59:04 +01:00
parent 1cf49f74ea
commit ce3d873cd0
6 changed files with 112 additions and 168 deletions

View File

@@ -1,41 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<PackageId>AMWD.Protocols.Modbus.Proxy</PackageId>
<AssemblyName>amwd-modbus-proxy</AssemblyName>
<RootNamespace>AMWD.Protocols.Modbus.Proxy</RootNamespace>
<Product>Modbus Proxy Clients</Product>
<Description>Plugging Modbus Servers and Clients together to create Modbus Proxies.</Description>
<PackageTags>Modbus Protocol Proxy</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" />
<Compile Include="$(SolutionDir)/AMWD.Protocols.Modbus.Tcp/Extensions/StreamExtensions.cs" Link="Extensions/StreamExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.IO.Ports" Version="4.7.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="System.IO.Ports" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Common\AMWD.Protocols.Modbus.Common.csproj" />
<ProjectReference Include="$(SolutionDir)\AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,14 +0,0 @@
# Modbus Protocol for .NET | Proxy
With this package the server and client implementations will be combined as proxy.
You can use any `ModbusBaseClient` implementation as target client and plug it into the implemented `ModbusTcpProxy` or `ModbusRtuProxy`, which implement the server side.
---
Published under MIT License (see [choose a license])
[choose a license]: https://choosealicense.com/licenses/mit/

View File

@@ -8,9 +8,9 @@ using System.Threading.Tasks;
using AMWD.Protocols.Modbus.Common; using AMWD.Protocols.Modbus.Common;
using AMWD.Protocols.Modbus.Common.Contracts; using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
using AMWD.Protocols.Modbus.Serial; using AMWD.Protocols.Modbus.Serial.Utils;
namespace AMWD.Protocols.Modbus.Proxy namespace AMWD.Protocols.Modbus.Serial
{ {
/// <summary> /// <summary>
/// Implements a Modbus serial line RTU server proxying all requests to a Modbus client of choice. /// Implements a Modbus serial line RTU server proxying all requests to a Modbus client of choice.
@@ -21,7 +21,7 @@ namespace AMWD.Protocols.Modbus.Proxy
private bool _isDisposed; private bool _isDisposed;
private readonly SerialPort _serialPort; private readonly SerialPortWrapper _serialPort;
private CancellationTokenSource _stopCts; private CancellationTokenSource _stopCts;
#endregion Fields #endregion Fields
@@ -33,31 +33,25 @@ namespace AMWD.Protocols.Modbus.Proxy
/// </summary> /// </summary>
/// <param name="client">The <see cref="ModbusClientBase"/> used to request the remote device, that should be proxied.</param> /// <param name="client">The <see cref="ModbusClientBase"/> used to request the remote device, that should be proxied.</param>
/// <param name="portName">The name of the serial port to use.</param> /// <param name="portName">The name of the serial port to use.</param>
/// <param name="baudRate">The baud rate of the serial port (Default: 19.200).</param> public ModbusRtuProxy(ModbusClientBase client, string portName)
public ModbusRtuProxy(ModbusClientBase client, string portName, BaudRate baudRate = BaudRate.Baud19200)
{ {
Client = client ?? throw new ArgumentNullException(nameof(client)); Client = client ?? throw new ArgumentNullException(nameof(client));
if (string.IsNullOrWhiteSpace(portName)) if (string.IsNullOrWhiteSpace(portName))
throw new ArgumentNullException(nameof(portName)); throw new ArgumentNullException(nameof(portName));
if (!Enum.IsDefined(typeof(BaudRate), baudRate)) _serialPort = new SerialPortWrapper
throw new ArgumentOutOfRangeException(nameof(baudRate));
if (!ModbusSerialClient.AvailablePortNames.Contains(portName))
throw new ArgumentException($"The serial port ({portName}) is not available.", nameof(portName));
_serialPort = new SerialPort
{ {
PortName = portName, PortName = portName,
BaudRate = (int)baudRate,
Handshake = Handshake.None, BaudRate = (int)BaudRate.Baud19200,
DataBits = 8, DataBits = 8,
ReadTimeout = 1000,
RtsEnable = false,
StopBits = StopBits.One, StopBits = StopBits.One,
Parity = Parity.Even,
Handshake = Handshake.None,
ReadTimeout = 1000,
WriteTimeout = 1000, WriteTimeout = 1000,
Parity = Parity.Even RtsEnable = false,
}; };
} }
@@ -70,73 +64,101 @@ namespace AMWD.Protocols.Modbus.Proxy
/// </summary> /// </summary>
public ModbusClientBase Client { get; } public ModbusClientBase Client { get; }
/// <inheritdoc cref="SerialPort.PortName"/> #region SerialPort Properties
public string PortName => _serialPort.PortName;
/// <summary> /// <inheritdoc cref="SerialPort.PortName" />
/// Gets or sets the baud rate of the serial port. public virtual string PortName
/// </summary> {
public BaudRate BaudRate get => _serialPort.PortName;
set => _serialPort.PortName = value;
}
/// <inheritdoc cref="SerialPort.BaudRate" />
public virtual BaudRate BaudRate
{ {
get => (BaudRate)_serialPort.BaudRate; get => (BaudRate)_serialPort.BaudRate;
set => _serialPort.BaudRate = (int)value; set => _serialPort.BaudRate = (int)value;
} }
/// <inheritdoc cref="SerialPort.Handshake"/>
public Handshake Handshake
{
get => _serialPort.Handshake;
set => _serialPort.Handshake = value;
}
/// <inheritdoc cref="SerialPort.DataBits" /> /// <inheritdoc cref="SerialPort.DataBits" />
public int DataBits /// <remarks>
/// From the Specs:
/// <br/>
/// On <see cref="AsciiProtocol"/> it can be 7 or 8.
/// <br/>
/// On <see cref="RtuProtocol"/> it has to be 8.
/// </remarks>
public virtual int DataBits
{ {
get => _serialPort.DataBits; get => _serialPort.DataBits;
set => _serialPort.DataBits = value; set => _serialPort.DataBits = value;
} }
/// <inheritdoc cref="SerialPort.IsOpen"/> /// <inheritdoc cref="SerialPort.Handshake" />
public bool IsOpen => _serialPort.IsOpen; public virtual Handshake Handshake
/// <summary>
/// Gets or sets the <see cref="TimeSpan"/> before a time-out occurs when a read operation does not finish.
/// </summary>
public TimeSpan ReadTimeout
{ {
get => TimeSpan.FromMilliseconds(_serialPort.ReadTimeout); get => _serialPort.Handshake;
set => _serialPort.ReadTimeout = (int)value.TotalMilliseconds; set => _serialPort.Handshake = value;
}
/// <inheritdoc cref="SerialPort.Parity" />
/// <remarks>
/// From the Specs:
/// <br/>
/// <see cref="Parity.Even"/> is recommended and therefore the default value.
/// <br/>
/// If you use <see cref="Parity.None"/>, <see cref="StopBits.Two"/> is required,
/// otherwise <see cref="StopBits.One"/> should work fine.
/// </remarks>
public virtual Parity Parity
{
get => _serialPort.Parity;
set => _serialPort.Parity = value;
} }
/// <inheritdoc cref="SerialPort.RtsEnable" /> /// <inheritdoc cref="SerialPort.RtsEnable" />
public bool RtsEnable public virtual bool RtsEnable
{ {
get => _serialPort.RtsEnable; get => _serialPort.RtsEnable;
set => _serialPort.RtsEnable = value; set => _serialPort.RtsEnable = value;
} }
/// <inheritdoc cref="SerialPort.StopBits" /> /// <inheritdoc cref="SerialPort.StopBits" />
public StopBits StopBits /// <remarks>
/// From the Specs:
/// <br/>
/// Should be <see cref="StopBits.One"/> for <see cref="Parity.Even"/> or <see cref="Parity.Odd"/>.
/// <br/>
/// Should be <see cref="StopBits.Two"/> for <see cref="Parity.None"/>.
/// </remarks>
public virtual StopBits StopBits
{ {
get => _serialPort.StopBits; get => _serialPort.StopBits;
set => _serialPort.StopBits = value; set => _serialPort.StopBits = value;
} }
/// <inheritdoc cref="SerialPortWrapper.IsOpen"/>
public bool IsOpen => _serialPort.IsOpen;
/// <summary> /// <summary>
/// Gets or sets the <see cref="TimeSpan"/> before a time-out occurs when a write operation does not finish. /// Gets or sets the <see cref="TimeSpan"/> before a time-out occurs when a read/receive operation does not finish.
/// </summary> /// </summary>
public TimeSpan WriteTimeout public virtual TimeSpan ReadTimeout
{
get => TimeSpan.FromMilliseconds(_serialPort.ReadTimeout);
set => _serialPort.ReadTimeout = (int)value.TotalMilliseconds;
}
/// <summary>
/// Gets or sets the <see cref="TimeSpan"/> before a time-out occurs when a write/send operation does not finish.
/// </summary>
public virtual TimeSpan WriteTimeout
{ {
get => TimeSpan.FromMilliseconds(_serialPort.WriteTimeout); get => TimeSpan.FromMilliseconds(_serialPort.WriteTimeout);
set => _serialPort.WriteTimeout = (int)value.TotalMilliseconds; set => _serialPort.WriteTimeout = (int)value.TotalMilliseconds;
} }
/// <inheritdoc cref="SerialPort.Parity"/> #endregion SerialPort Properties
public Parity Parity
{
get => _serialPort.Parity;
set => _serialPort.Parity = value;
}
#endregion Properties #endregion Properties
@@ -175,7 +197,7 @@ namespace AMWD.Protocols.Modbus.Proxy
private Task StopAsyncInternal(CancellationToken cancellationToken) private Task StopAsyncInternal(CancellationToken cancellationToken)
{ {
_stopCts.Cancel(); _stopCts?.Cancel();
_serialPort.Close(); _serialPort.Close();
_serialPort.DataReceived -= OnDataReceived; _serialPort.DataReceived -= OnDataReceived;
@@ -207,13 +229,16 @@ namespace AMWD.Protocols.Modbus.Proxy
if (_isDisposed) if (_isDisposed)
throw new ObjectDisposedException(GetType().FullName); throw new ObjectDisposedException(GetType().FullName);
#endif #endif
if (string.IsNullOrWhiteSpace(PortName))
throw new ArgumentNullException(nameof(PortName), "The serial port name cannot be empty.");
} }
#endregion Control Methods #endregion Control Methods
#region Client Handling #region Client Handling
private void OnDataReceived(object _, SerialDataReceivedEventArgs evArgs) private void OnDataReceived(object _, SerialDataReceivedEventArgs __)
{ {
try try
{ {
@@ -282,16 +307,14 @@ namespace AMWD.Protocols.Modbus.Proxy
default: // unknown function default: // unknown function
{ {
byte[] responseBytes = new byte[5]; var responseBytes = new List<byte>();
Array.Copy(requestBytes, 0, responseBytes, 0, 2); responseBytes.AddRange(requestBytes.Take(2));
responseBytes.Add((byte)ModbusErrorCode.IllegalFunction);
// Mark as error // Mark as error
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes[2] = (byte)ModbusErrorCode.IllegalFunction; return ReturnResponse(responseBytes);
SetCrc(responseBytes);
return responseBytes;
} }
} }
} }
@@ -332,8 +355,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleReadDiscreteInputsAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleReadDiscreteInputsAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -372,8 +394,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleReadHoldingRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleReadHoldingRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -407,8 +428,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleReadInputRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleReadInputRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -442,8 +462,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleWriteSingleCoilAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleWriteSingleCoilAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -461,8 +480,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue); responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
try try
@@ -492,8 +510,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleWriteSingleRegisterAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleWriteSingleRegisterAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -532,8 +549,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleWriteMultipleCoilsAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleWriteMultipleCoilsAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -553,8 +569,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue); responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
try try
@@ -594,8 +609,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleWriteMultipleRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleWriteMultipleRegistersAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -615,8 +629,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue); responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
try try
@@ -653,8 +666,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
} }
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
private async Task<byte[]> HandleEncapsulatedInterfaceAsync(byte[] requestBytes, CancellationToken cancellationToken) private async Task<byte[]> HandleEncapsulatedInterfaceAsync(byte[] requestBytes, CancellationToken cancellationToken)
@@ -667,8 +679,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalFunction); responseBytes.Add((byte)ModbusErrorCode.IllegalFunction);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
var firstObject = (ModbusDeviceIdentificationObject)requestBytes[4]; var firstObject = (ModbusDeviceIdentificationObject)requestBytes[4];
@@ -677,8 +688,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalDataAddress); responseBytes.Add((byte)ModbusErrorCode.IllegalDataAddress);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
var category = (ModbusDeviceIdentificationCategory)requestBytes[3]; var category = (ModbusDeviceIdentificationCategory)requestBytes[3];
@@ -687,8 +697,7 @@ namespace AMWD.Protocols.Modbus.Proxy
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue); responseBytes.Add((byte)ModbusErrorCode.IllegalDataValue);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
try try
@@ -755,16 +764,14 @@ namespace AMWD.Protocols.Modbus.Proxy
bodyBytes[5] = numberOfObjects; bodyBytes[5] = numberOfObjects;
responseBytes.AddRange(bodyBytes); responseBytes.AddRange(bodyBytes);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
catch catch
{ {
responseBytes[1] |= 0x80; responseBytes[1] |= 0x80;
responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure); responseBytes.Add((byte)ModbusErrorCode.SlaveDeviceFailure);
AddCrc(responseBytes); return ReturnResponse(responseBytes);
return [.. responseBytes];
} }
} }
@@ -848,18 +855,10 @@ namespace AMWD.Protocols.Modbus.Proxy
return [.. result]; return [.. result];
} }
private static void SetCrc(byte[] bytes) private static byte[] ReturnResponse(List<byte> response)
{ {
byte[] crc = RtuProtocol.CRC16(bytes, 0, bytes.Length - 2); response.AddRange(RtuProtocol.CRC16(response));
bytes[bytes.Length - 2] = crc[0]; return [.. response];
bytes[bytes.Length - 1] = crc[1];
}
private static void AddCrc(List<byte> bytes)
{
byte[] crc = RtuProtocol.CRC16(bytes);
bytes.Add(crc[0]);
bytes.Add(crc[1]);
} }
#endregion Request Handling #endregion Request Handling

View File

@@ -11,7 +11,7 @@ using AMWD.Protocols.Modbus.Common;
using AMWD.Protocols.Modbus.Common.Contracts; using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
namespace AMWD.Protocols.Modbus.Proxy namespace AMWD.Protocols.Modbus.Tcp
{ {
/// <summary> /// <summary>
/// Implements a Modbus TCP server proxying all requests to a Modbus client of choice. /// Implements a Modbus TCP server proxying all requests to a Modbus client of choice.
@@ -875,7 +875,7 @@ namespace AMWD.Protocols.Modbus.Proxy
private static byte[] ReturnResponse(List<byte> response) private static byte[] ReturnResponse(List<byte> response)
{ {
ushort followingBytes = (ushort)(response.Count - 6); ushort followingBytes = (ushort)(response.Count - 6);
byte[] bytes = followingBytes.ToBigEndianBytes(); var bytes = followingBytes.ToBigEndianBytes();
response[4] = bytes[0]; response[4] = bytes[0];
response[5] = bytes[1]; response[5] = bytes[1];

View File

@@ -35,9 +35,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Tcp",
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Serial", "AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj", "{D966826F-EE6C-4BC0-9185-C2A9A50FD586}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Serial", "AMWD.Protocols.Modbus.Serial\AMWD.Protocols.Modbus.Serial.csproj", "{D966826F-EE6C-4BC0-9185-C2A9A50FD586}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Proxy", "AMWD.Protocols.Modbus.Proxy\AMWD.Protocols.Modbus.Proxy.csproj", "{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliClient", "CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI Client", "CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -61,10 +59,6 @@ Global
{D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Debug|Any CPU.Build.0 = Debug|Any CPU {D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Release|Any CPU.ActiveCfg = Release|Any CPU {D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Release|Any CPU.Build.0 = Release|Any CPU {D966826F-EE6C-4BC0-9185-C2A9A50FD586}.Release|Any CPU.Build.0 = Release|Any CPU
{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}.Release|Any CPU.Build.0 = Release|Any CPU
{B0E53462-B0ED-4685-8AA5-948DC160EE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0E53462-B0ED-4685-8AA5-948DC160EE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0E53462-B0ED-4685-8AA5-948DC160EE27}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0E53462-B0ED-4685-8AA5-948DC160EE27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0E53462-B0ED-4685-8AA5-948DC160EE27}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0E53462-B0ED-4685-8AA5-948DC160EE27}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -14,6 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- The `ModbusTcpProxy.ReadWriteTimeout` has a default value of 100 seconds (same default as a `HttpClient` has). - The `ModbusTcpProxy.ReadWriteTimeout` has a default value of 100 seconds (same default as a `HttpClient` has).
- The `ModbusRtuProxy` moved from `AMWD.Protocols.Modbus.Proxy` to `AMWD.Protocols.Modbus.Serial`.
- The `ModbusTcpProxy` moved from `AMWD.Protocols.Modbus.Proxy` to `AMWD.Protocols.Modbus.Tcp`.
### Removed
- The `AMWD.Protocols.Modbus.Proxy` (introduced in [v0.3.0]) has been removed.
### Fixed ### Fixed