diff --git a/AMWD.Protocols.Modbus.Common/Models/DeviceIdentification.cs b/AMWD.Protocols.Modbus.Common/Models/DeviceIdentification.cs
index 64ee898..30387ec 100644
--- a/AMWD.Protocols.Modbus.Common/Models/DeviceIdentification.cs
+++ b/AMWD.Protocols.Modbus.Common/Models/DeviceIdentification.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Text;
namespace AMWD.Protocols.Modbus.Common
{
@@ -91,5 +92,23 @@ namespace AMWD.Protocols.Modbus.Common
/// Gets or sets a value indicating whether individual access () is allowed.
///
public bool IsIndividualAccessAllowed { get; set; }
+
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine(nameof(DeviceIdentification));
+ sb.AppendLine($" {nameof(VendorName)}: {VendorName}");
+ sb.AppendLine($" {nameof(ProductCode)}: {ProductCode}");
+ sb.AppendLine($" {nameof(MajorMinorRevision)}: {MajorMinorRevision}");
+ sb.AppendLine($" {nameof(VendorUrl)}: {VendorUrl}");
+ sb.AppendLine($" {nameof(ProductName)}: {ProductName}");
+ sb.AppendLine($" {nameof(ModelName)}: {ModelName}");
+ sb.AppendLine($" {nameof(UserApplicationName)}: {UserApplicationName}");
+ sb.AppendLine($" {nameof(IsIndividualAccessAllowed)}: {IsIndividualAccessAllowed}");
+
+ return sb.ToString();
+ }
}
}
diff --git a/AMWD.Protocols.Modbus.Serial/ModbusSerialClient.cs b/AMWD.Protocols.Modbus.Serial/ModbusSerialClient.cs
index 506e3ad..d0251de 100644
--- a/AMWD.Protocols.Modbus.Serial/ModbusSerialClient.cs
+++ b/AMWD.Protocols.Modbus.Serial/ModbusSerialClient.cs
@@ -1,5 +1,6 @@
using System;
using System.IO.Ports;
+using System.Text;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols;
@@ -223,5 +224,22 @@ namespace AMWD.Protocols.Modbus.Serial
serialConnection.StopBits = value;
}
}
+
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine($"Serial Client {PortName}");
+ sb.AppendLine($" {nameof(BaudRate)}: {(int)BaudRate}");
+ sb.AppendLine($" {nameof(DataBits)}: {DataBits}");
+ sb.AppendLine($" {nameof(StopBits)}: {(StopBits == StopBits.OnePointFive ? "1.5" : ((int)StopBits).ToString())}");
+ sb.AppendLine($" {nameof(Parity)}: {Parity.ToString().ToLower()}");
+ sb.AppendLine($" {nameof(Handshake)}: {Handshake.ToString().ToLower()}");
+ sb.AppendLine($" {nameof(RtsEnable)}: {RtsEnable.ToString().ToLower()}");
+ sb.AppendLine($" {nameof(DriverEnabledRS485)}: {DriverEnabledRS485.ToString().ToLower()}");
+
+ return sb.ToString();
+ }
}
}
diff --git a/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs b/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
index a73ad6f..7f2c322 100644
--- a/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
+++ b/AMWD.Protocols.Modbus.Tcp/ModbusTcpClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Text;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols;
@@ -101,5 +102,16 @@ namespace AMWD.Protocols.Modbus.Tcp
tcpConnection.Port = value;
}
}
+
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine($"TCP Client {Hostname}");
+ sb.AppendLine($" {nameof(Port)}: {Port}");
+
+ return sb.ToString();
+ }
}
}
diff --git a/AMWD.Protocols.Modbus.sln b/AMWD.Protocols.Modbus.sln
index f1cf8e3..640e790 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Protocols.Modbus.Proxy", "AMWD.Protocols.Modbus.Proxy\AMWD.Protocols.Modbus.Proxy.csproj", "{C30EBE45-E3B8-4997-95DF-8F94B31C8E1A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI Client", "CliClient\CliClient.csproj", "{B0E53462-B0ED-4685-8AA5-948DC160EE27}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,10 @@ Global
{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.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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ede9233..16ec5c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Small CLI tool to test Modbus client communication.
+
### Changed
- The `ModbusTcpProxy.ReadWriteTimeout` has a default value of 100 seconds (same default as a `HttpClient` has).
diff --git a/CliClient/Cli/Argument.cs b/CliClient/Cli/Argument.cs
new file mode 100644
index 0000000..5e4a76a
--- /dev/null
+++ b/CliClient/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/CliClient/Cli/CommandLineParser.cs b/CliClient/Cli/CommandLineParser.cs
new file mode 100644
index 0000000..0577971
--- /dev/null
+++ b/CliClient/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