From aeedc1c1629db6bfd5d3d7f0fff226d17d2b5b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20K=C3=B6nig?= Date: Thu, 3 Dec 2020 20:45:56 +0100 Subject: [PATCH] Finished support for sending messages by typing in Hex values in the Gui --- ....cs => ConnectionProfileViewModelTests.cs} | 17 ++- .../Data/IMessageRecognizerAppSettings.cs | 4 +- .../ConnectionProfileViewModel.cs | 12 +- .../Views/_ConnectionProfile/_Misc.cs | 2 +- .../HexFormatUtilTests.cs | 103 +++++++++++++++++ MessageCommunicator/Util/HexFormatUtil.cs | 105 ++++++++++++++++++ .../_ByteStreamHandler/_Tcp/TcpAsyncUtil.cs | 31 ------ .../_Tcp/TcpByteStreamHandler.cs | 4 +- 8 files changed, 231 insertions(+), 47 deletions(-) rename MessageCommunicator.TestGui.Tests/{ConnectionProfileViewTests.cs => ConnectionProfileViewModelTests.cs} (85%) create mode 100644 MessageCommunicator.Tests/HexFormatUtilTests.cs create mode 100644 MessageCommunicator/Util/HexFormatUtil.cs diff --git a/MessageCommunicator.TestGui.Tests/ConnectionProfileViewTests.cs b/MessageCommunicator.TestGui.Tests/ConnectionProfileViewModelTests.cs similarity index 85% rename from MessageCommunicator.TestGui.Tests/ConnectionProfileViewTests.cs rename to MessageCommunicator.TestGui.Tests/ConnectionProfileViewModelTests.cs index 84779e2..dd1c74b 100644 --- a/MessageCommunicator.TestGui.Tests/ConnectionProfileViewTests.cs +++ b/MessageCommunicator.TestGui.Tests/ConnectionProfileViewModelTests.cs @@ -11,13 +11,13 @@ namespace MessageCommunicator.TestGui.Tests { [TestClass] - public class ConnectionProfileViewTests + public class ConnectionProfileViewModelTests { [TestMethod] public void SendPlainMessage() { var connParams = new ConnectionParameters(); - var connProfile = this.BuildFakeConnectionProfile(connParams); + var connProfile = BuildFakeConnectionProfile(connParams); // Catch outgoing message string? sentMessage = null; @@ -39,7 +39,7 @@ public void SendPlainMessage() public void SendEscapedMessage() { var connParams = new ConnectionParameters(); - var connProfile = this.BuildFakeConnectionProfile(connParams); + var connProfile = BuildFakeConnectionProfile(connParams); // Catch outgoing message string? sentMessage = null; @@ -61,7 +61,7 @@ public void SendEscapedMessage() public void SendHexMessage() { var connParams = new ConnectionParameters(); - var connProfile = this.BuildFakeConnectionProfile(connParams); + var connProfile = BuildFakeConnectionProfile(connParams); // Catch outgoing message string? sentMessage = null; @@ -73,13 +73,13 @@ public void SendHexMessage() }); var testObject = new ConnectionProfileViewModel(connProfile); - testObject.SendFormattingMode = SendFormattingMode.Hex; - testObject.Command_SendMessage.Execute("DummyMessage \\\\"); + testObject.SendFormattingMode = SendFormattingMode.BinaryHex; + testObject.Command_SendMessage.Execute("41 42 43"); - Assert.IsTrue(sentMessage == "DummyMessage \\"); + Assert.IsTrue(sentMessage == "ABC"); } - private IConnectionProfile BuildFakeConnectionProfile(ConnectionParameters connParams) + private static IConnectionProfile BuildFakeConnectionProfile(ConnectionParameters connParams) { var loggingMessages = new ObservableCollection(); var sendReceiveMessages = new ObservableCollection(); @@ -95,6 +95,5 @@ private IConnectionProfile BuildFakeConnectionProfile(ConnectionParameters connP return connProfile; } - } } diff --git a/MessageCommunicator.TestGui/Data/IMessageRecognizerAppSettings.cs b/MessageCommunicator.TestGui/Data/IMessageRecognizerAppSettings.cs index d2b1272..0d6f838 100644 --- a/MessageCommunicator.TestGui/Data/IMessageRecognizerAppSettings.cs +++ b/MessageCommunicator.TestGui/Data/IMessageRecognizerAppSettings.cs @@ -6,6 +6,8 @@ namespace MessageCommunicator.TestGui.Data { public interface IMessageRecognizerAppSettings { - public MessageRecognizerSettings CreateLibSettings(); + string Encoding { get; } + + MessageRecognizerSettings CreateLibSettings(); } } diff --git a/MessageCommunicator.TestGui/Views/_ConnectionProfile/ConnectionProfileViewModel.cs b/MessageCommunicator.TestGui/Views/_ConnectionProfile/ConnectionProfileViewModel.cs index 48b7daf..557928f 100644 --- a/MessageCommunicator.TestGui/Views/_ConnectionProfile/ConnectionProfileViewModel.cs +++ b/MessageCommunicator.TestGui/Views/_ConnectionProfile/ConnectionProfileViewModel.cs @@ -1,7 +1,9 @@ using System; using System.Reactive; +using System.Text; using System.Text.RegularExpressions; using MessageCommunicator.TestGui.Logic; +using MessageCommunicator.Util; using ReactiveUI; namespace MessageCommunicator.TestGui.Views @@ -99,7 +101,7 @@ public ConnectionProfileViewModel(IConnectionProfile connProfile) { try { - if (message == null) { message = string.Empty; } + message ??= string.Empty; switch (this.SendFormattingMode) { @@ -110,9 +112,13 @@ public ConnectionProfileViewModel(IConnectionProfile connProfile) message = Regex.Unescape(message); break; - case SendFormattingMode.Hex: - + case SendFormattingMode.BinaryHex: + var encoding = Encoding.GetEncoding(this.Model.Parameters.RecognizerSettings.Encoding); + message = encoding.GetString(HexFormatUtil.ToByteArray(message)); break; + + default: + throw new InvalidOperationException($"Unhandled {nameof(Views.SendFormattingMode)} {this.SendFormattingMode}!"); } await this.Model.SendMessageAsync(message); diff --git a/MessageCommunicator.TestGui/Views/_ConnectionProfile/_Misc.cs b/MessageCommunicator.TestGui/Views/_ConnectionProfile/_Misc.cs index f8d0461..62e1036 100644 --- a/MessageCommunicator.TestGui/Views/_ConnectionProfile/_Misc.cs +++ b/MessageCommunicator.TestGui/Views/_ConnectionProfile/_Misc.cs @@ -10,6 +10,6 @@ public enum SendFormattingMode Escaped, - Hex + BinaryHex } } diff --git a/MessageCommunicator.Tests/HexFormatUtilTests.cs b/MessageCommunicator.Tests/HexFormatUtilTests.cs new file mode 100644 index 0000000..9ed36a6 --- /dev/null +++ b/MessageCommunicator.Tests/HexFormatUtilTests.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MessageCommunicator.Util; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MessageCommunicator.Tests +{ + [TestClass] + public class HexFormatUtilTests + { + [TestMethod] + public void BytesToHexString() + { + var testArray = new byte[] {1, 2, 3, 16, 17, 255}; + + var hexString = HexFormatUtil.ToHexString(testArray); + + Assert.IsTrue(hexString == "01 02 03 10 11 FF"); + } + + [TestMethod] + public void HexStringToBytes() + { + var hexString = "01 02 03 10 11 FF"; + + var byteArray = HexFormatUtil.ToByteArray(hexString); + + Assert.IsTrue(byteArray.Length == 6); + Assert.IsTrue(byteArray[0] == 1); + Assert.IsTrue(byteArray[1] == 2); + Assert.IsTrue(byteArray[2] == 3); + Assert.IsTrue(byteArray[3] == 16); + Assert.IsTrue(byteArray[4] == 17); + Assert.IsTrue(byteArray[5] == 255); + } + + [TestMethod] + public void HexStringToBytes_WithoutSpaces() + { + var hexString = "0102031011FF"; + + var byteArray = HexFormatUtil.ToByteArray(hexString); + + Assert.IsTrue(byteArray.Length == 6); + Assert.IsTrue(byteArray[0] == 1); + Assert.IsTrue(byteArray[1] == 2); + Assert.IsTrue(byteArray[2] == 3); + Assert.IsTrue(byteArray[3] == 16); + Assert.IsTrue(byteArray[4] == 17); + Assert.IsTrue(byteArray[5] == 255); + } + + [TestMethod] + public void HexStringToBytes_MixedFormat() + { + var hexString = " 010203 1 011fF "; + + var byteArray = HexFormatUtil.ToByteArray(hexString); + + Assert.IsTrue(byteArray.Length == 6); + Assert.IsTrue(byteArray[0] == 1); + Assert.IsTrue(byteArray[1] == 2); + Assert.IsTrue(byteArray[2] == 3); + Assert.IsTrue(byteArray[3] == 16); + Assert.IsTrue(byteArray[4] == 17); + Assert.IsTrue(byteArray[5] == 255); + } + + [TestMethod] + public void HexStringToBytes_EmptyString() + { + var hexString = string.Empty; + + var byteArray = HexFormatUtil.ToByteArray(hexString); + + Assert.IsTrue(byteArray.Length == 0); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void HexStringToBytes_NullString() + { + HexFormatUtil.ToByteArray(null!); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void HexStringToBytes_InvalidCharCount() + { + HexFormatUtil.ToByteArray("01 0"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void HexStringToBytes_InvalidCharacter() + { + HexFormatUtil.ToByteArray("t"); + } + } +} diff --git a/MessageCommunicator/Util/HexFormatUtil.cs b/MessageCommunicator/Util/HexFormatUtil.cs new file mode 100644 index 0000000..468f724 --- /dev/null +++ b/MessageCommunicator/Util/HexFormatUtil.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Light.GuardClauses; + +namespace MessageCommunicator.Util +{ + public static class HexFormatUtil + { + private const string HEX_ALPHABET = "0123456789ABCDEF"; + + public static string ToHexString(byte[] bytes) + { + return ToHexString(new ArraySegment(bytes, 0, bytes.Length)); + } + + public static string ToHexString(ArraySegment bytes) + { + bytes.MustNotBeDefault(nameof(bytes)); + + if (bytes.Count == 0) { return string.Empty; } + + var length = bytes.Count; + if (length > 1) { length += (length - 1); } + var stringBuffer = StringBuffer.Acquire(length); + var bytesSpan = bytes.AsSpan(); + try + { + for (var loop = 0; loop < bytesSpan.Length; loop++) + { + if(stringBuffer.Count > 0){ stringBuffer.Append(' '); } + + var actByte = bytesSpan[loop]; + stringBuffer.Append(HEX_ALPHABET[actByte >> 4]); + stringBuffer.Append(HEX_ALPHABET[actByte & 0xF]); + } + + return stringBuffer.ToString(); + } + finally + { + StringBuffer.Release(stringBuffer); + } + } + + public static unsafe byte[] ToByteArray(string hexString) + { + hexString.MustNotBeNull(nameof(hexString)); + + // Count hex characters + var hexCharCount = 0; + for (var loop = 0; loop < hexString.Length; loop++) + { + if(hexString[loop] == ' '){ continue; } + hexCharCount++; + } + if (hexCharCount == 0) { return new byte[0]; } + if (hexCharCount % 2 != 0) + { + throw new ArgumentException("Provided uneven count of hex characters!", nameof(hexString)); + } + + // Parse all bytes + var result = new byte[hexCharCount / 2]; + var resultPos = 0; + var hexPos = 0; + var hexValues = stackalloc int[2]; + for (var loop = 0; loop < hexString.Length; loop++) + { + if(hexString[loop] == ' '){ continue; } + + var asciiValue = (int) (hexString[loop]); + if ((asciiValue > 47) && (asciiValue < 58)) + { + // 0, 1, 2... + hexValues[hexPos] = asciiValue - 48; + } + else if ((asciiValue > 64) && (asciiValue < 71)) + { + // A, B, C... + hexValues[hexPos] = (asciiValue - 65) + 10; + } + else if ((asciiValue > 96) && (asciiValue < 103)) + { + // a, b, c... + hexValues[hexPos] = (asciiValue - 97) + 10; + } + else + { + throw new ArgumentException($"Invalid hex sign '{hexString[loop]}' at position {loop}!", nameof(hexString)); + } + + hexPos++; + if (hexPos == 2) + { + result[resultPos] = (byte)(hexValues[0] * 16 + hexValues[1]); + resultPos++; + hexPos = 0; + } + } + + return result; + } + } +} diff --git a/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpAsyncUtil.cs b/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpAsyncUtil.cs index 0f989a6..974fdda 100644 --- a/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpAsyncUtil.cs +++ b/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpAsyncUtil.cs @@ -55,36 +55,5 @@ public static void SafeDispose(Socket? socket) socket = null; } - - public static string ToHexString(ArraySegment bytes) - { - bytes.MustNotBeDefault(nameof(bytes)); - - if (bytes.Count == 0) { return string.Empty; } - - const string HEX_ALPHABET = "0123456789ABCDEF"; - - var length = bytes.Count; - if (length > 1) { length += (length - 1); } - var stringBuffer = StringBuffer.Acquire(length); - var bytesSpan = bytes.AsSpan(); - try - { - for (var loop = 0; loop < bytesSpan.Length; loop++) - { - if(stringBuffer.Count > 0){ stringBuffer.Append(' '); } - - var actByte = bytesSpan[loop]; - stringBuffer.Append(HEX_ALPHABET[actByte >> 4]); - stringBuffer.Append(HEX_ALPHABET[actByte & 0xF]); - } - - return stringBuffer.ToString(); - } - finally - { - StringBuffer.Release(stringBuffer); - } - } } } diff --git a/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpByteStreamHandler.cs b/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpByteStreamHandler.cs index e832500..c9c43c4 100644 --- a/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpByteStreamHandler.cs +++ b/MessageCommunicator/_ByteStreamHandler/_Tcp/TcpByteStreamHandler.cs @@ -141,7 +141,7 @@ await currentClient.Client.SendAsync(buffer, SocketFlags.None) { this.Log( LoggingMessageType.Info, - StringBuffer.Format("Sent {0} bytes: {1}", buffer.Count, TcpAsyncUtil.ToHexString(buffer))); + StringBuffer.Format("Sent {0} bytes: {1}", buffer.Count, HexFormatUtil.ToHexString(buffer))); } return true; @@ -342,7 +342,7 @@ private void ProcessReceivedBytes(bool newConnection, byte[] buffer, int receive { this.Log( LoggingMessageType.Info, - StringBuffer.Format("Received {0} bytes: {1}", receiveBuffer.Count, TcpAsyncUtil.ToHexString(receiveBuffer))); + StringBuffer.Format("Received {0} bytes: {1}", receiveBuffer.Count, HexFormatUtil.ToHexString(receiveBuffer))); } // Notify received bytes