diff --git a/AMWD.Protocols.Modbus.Common/Contracts/IModbusProxy.cs b/AMWD.Protocols.Modbus.Common/Contracts/IModbusProxy.cs new file mode 100644 index 0000000..2f3cbd3 --- /dev/null +++ b/AMWD.Protocols.Modbus.Common/Contracts/IModbusProxy.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using System.Threading; + +namespace AMWD.Protocols.Modbus.Common.Contracts +{ + /// + /// Represents a Modbus proxy. + /// + public interface IModbusProxy : IDisposable + { + /// + /// Starts the proxy. + /// + /// A cancellation token used to propagate notification that this operation should be canceled. + Task StartAsync(CancellationToken cancellationToken = default); + + /// + /// Stops the proxy. + /// + /// A cancellation token used to propagate notification that this operation should be canceled. + Task StopAsync(CancellationToken cancellationToken = default); + } +} diff --git a/AMWD.Protocols.Modbus.Serial/ModbusRtuProxy.cs b/AMWD.Protocols.Modbus.Serial/ModbusRtuProxy.cs index 3070dc4..192cd23 100644 --- a/AMWD.Protocols.Modbus.Serial/ModbusRtuProxy.cs +++ b/AMWD.Protocols.Modbus.Serial/ModbusRtuProxy.cs @@ -15,7 +15,7 @@ namespace AMWD.Protocols.Modbus.Serial /// /// Implements a Modbus serial line RTU server proxying all requests to a Modbus client of choice. /// - public class ModbusRtuProxy : IDisposable + public class ModbusRtuProxy : IModbusProxy { #region Fields @@ -165,7 +165,7 @@ namespace AMWD.Protocols.Modbus.Serial #region Control Methods /// - /// Starts the server. + /// Starts the proxy. /// /// A cancellation token used to propagate notification that this operation should be canceled. public Task StartAsync(CancellationToken cancellationToken = default) @@ -186,7 +186,7 @@ namespace AMWD.Protocols.Modbus.Serial } /// - /// Stops the server. + /// Stops the proxy. /// /// A cancellation token used to propagate notification that this operation should be canceled. public Task StopAsync(CancellationToken cancellationToken = default) diff --git a/AMWD.Protocols.Modbus.Tcp/ModbusTcpProxy.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpProxy.cs index 10a6584..3e985c8 100644 --- a/AMWD.Protocols.Modbus.Tcp/ModbusTcpProxy.cs +++ b/AMWD.Protocols.Modbus.Tcp/ModbusTcpProxy.cs @@ -17,7 +17,7 @@ namespace AMWD.Protocols.Modbus.Tcp /// /// Implements a Modbus TCP server proxying all requests to a Modbus client of choice. /// - public class ModbusTcpProxy : IDisposable + public class ModbusTcpProxy : IModbusProxy { #region Fields @@ -41,7 +41,7 @@ namespace AMWD.Protocols.Modbus.Tcp /// Initializes a new instance of the class. /// /// The used to request the remote device, that should be proxied. - /// An to listen on (Default: ). + /// An to listen on. public ModbusTcpProxy(ModbusClientBase client, IPAddress listenAddress) { Client = client ?? throw new ArgumentNullException(nameof(client)); diff --git a/AMWD.Protocols.Modbus.Tcp/ModbusTcpServer.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpServer.cs index 648ead7..d937b52 100644 --- a/AMWD.Protocols.Modbus.Tcp/ModbusTcpServer.cs +++ b/AMWD.Protocols.Modbus.Tcp/ModbusTcpServer.cs @@ -12,6 +12,10 @@ namespace AMWD.Protocols.Modbus.Tcp [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public class ModbusTcpServer : ModbusTcpProxy { + /// + /// Initializes a new instance of the class. + /// + /// The to listen on. public ModbusTcpServer(IPAddress listenAddress) : base(new VirtualModbusClient(), listenAddress) { diff --git a/AMWD.Protocols.Modbus.sln b/AMWD.Protocols.Modbus.sln index f21ce82..999f0bf 100644 --- a/AMWD.Protocols.Modbus.sln +++ b/AMWD.Protocols.Modbus.sln @@ -37,6 +37,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Seria EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliClient", "CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliProxy", "CliProxy\CliProxy.csproj", "{AC922E80-E9B6-493D-B1D1-752527E883ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -63,6 +65,10 @@ Global {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.Build.0 = Release|Any CPU + {AC922E80-E9B6-493D-B1D1-752527E883ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC922E80-E9B6-493D-B1D1-752527E883ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC922E80-E9B6-493D-B1D1-752527E883ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC922E80-E9B6-493D-B1D1-752527E883ED}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CHANGELOG.md b/CHANGELOG.md index 89ec91b..d549998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Small CLI tool to test Modbus client communication. +- Small CLI client for Modbus communication. +- Small CLI proxy to forward messages. - `VirtualModbusClient` added to `AMWD.Protocols.Modbus.Common`. ### Changed diff --git a/CliClient/CliClient.csproj b/CliClient/CliClient.csproj index 9948614..11b41c0 100644 --- a/CliClient/CliClient.csproj +++ b/CliClient/CliClient.csproj @@ -13,6 +13,8 @@ false false false + + true diff --git a/CliProxy/Cli/Argument.cs b/CliProxy/Cli/Argument.cs new file mode 100644 index 0000000..5e4a76a --- /dev/null +++ b/CliProxy/Cli/Argument.cs @@ -0,0 +1,35 @@ +namespace AMWD.Common.Cli +{ + /// + /// Represents a logical argument in the command line. Options with their additional + /// parameters are combined in one argument. + /// + internal class Argument + { + /// + /// Initialises a new instance of the class. + /// + /// The that is set in this argument; or null. + /// The additional parameter values for the option; or the argument value. + internal Argument(Option option, string[] values) + { + Option = option; + Values = values; + } + + /// + /// Gets the that is set in this argument; or null. + /// + public Option Option { get; private set; } + + /// + /// Gets the additional parameter values for the option; or the argument value. + /// + public string[] Values { get; private set; } + + /// + /// Gets the first item of ; or null. + /// + public string Value => Values.Length > 0 ? Values[0] : null; + } +} diff --git a/CliProxy/Cli/CommandLineParser.cs b/CliProxy/Cli/CommandLineParser.cs new file mode 100644 index 0000000..0577971 --- /dev/null +++ b/CliProxy/Cli/CommandLineParser.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AMWD.Common.Cli +{ + /// + /// Provides options and arguments parsing from command line arguments or a single string. + /// + internal class CommandLineParser + { + #region Private data + + private string[] _args; + private List _parsedArguments; + private readonly List