diff --git a/MultiTerm.Core.Tests/ExtendedByteTests.cs b/MultiTerm.Core.Tests/ExtendedByteTests.cs new file mode 100644 index 0000000..f956352 --- /dev/null +++ b/MultiTerm.Core.Tests/ExtendedByteTests.cs @@ -0,0 +1,87 @@ +using FluentAssertions; +using MultiTerm.Protocols.Model; + +namespace MultiTerm.Protocols.Tests; + +[TestFixture(Author = "JonasArnold")] +public class ExtendedByteTests +{ + // testname: thema_aktion_expectedout + // ablauf: arrange, act, assert + [SetUp] + public void Setup() + { + } + + [Test] + [TestCase((byte)40 , "(")] + [TestCase((byte)10, "\n")] + [TestCase((byte)13, "\r")] + [TestCase((byte)32, " ")] + [TestCase((byte)125, "}")] + [TestCase((byte)128, "?")] + public void ToString_ByteConverted_ResultsInAsciiEncodedString(byte dataByte, string expectedOutcome) + { + // Arrange + ExtendedByte extdByte = new(dataByte); + + // Act + var toStringResult = extdByte.ToString(); + + // Assert + toStringResult.Should().BeEquivalentTo(expectedOutcome); + } + + [Test] + [TestCase((byte)40, "(")] + [TestCase((byte)10, "\u240A")] + [TestCase((byte)13, "\u240D")] + [TestCase((byte)32, " ")] + [TestCase((byte)125, "}")] + [TestCase((byte)128, "?")] + public void ToCharacterString_ByteConverted_ResultsInDisplayableCharacterString(byte dataByte, string expectedOutcome) + { + // Arrange + ExtendedByte extdByte = new(dataByte); + + // Act + var toStringResult = extdByte.ToCharacterString(); + + // Assert + toStringResult.Should().BeEquivalentTo(expectedOutcome); + } + + [Test] + [TestCase((byte)30, "00011110")] + [TestCase((byte)10, "00001010")] + [TestCase((byte)240, "11110000")] + public void ToBinaryString_ByteConverted_ResultsInCorrectlyFormattedBinaryString(byte dataByte, string expectedOutcome) + { + // Arrange + ExtendedByte extdByte = new(dataByte); + + // Act + var toStringResult = extdByte.ToBinaryString(); + + // Assert + toStringResult.Should().BeEquivalentTo(expectedOutcome); + } + + [Test] + [TestCase((byte)2, "02")] + [TestCase((byte)10, "0A")] + [TestCase((byte)127, "7F")] + [TestCase((byte)240, "F0")] + public void ToHexString_ByteConverted_ResultsInCorrectlyFormattedHexString(byte dataByte, string expectedOutcome) + { + // Arrange + ExtendedByte extdByte = new(dataByte); + + // Act + var toStringResult = extdByte.ToHexString(); + + // Assert + toStringResult.Should().BeEquivalentTo(expectedOutcome); + } + +} \ No newline at end of file diff --git a/MultiTerm.Core.Tests/MultiTerm.Protocols.Tests.csproj b/MultiTerm.Core.Tests/MultiTerm.Protocols.Tests.csproj new file mode 100644 index 0000000..413d002 --- /dev/null +++ b/MultiTerm.Core.Tests/MultiTerm.Protocols.Tests.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + + + diff --git a/MultiTerm.Core.Tests/Usings.cs b/MultiTerm.Core.Tests/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/MultiTerm.Core.Tests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/MultiTerm.Core/ViewModel/ByteDataViewModel.cs b/MultiTerm.Core/ViewModel/ByteDataViewModel.cs new file mode 100644 index 0000000..b5ef3fd --- /dev/null +++ b/MultiTerm.Core/ViewModel/ByteDataViewModel.cs @@ -0,0 +1,53 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using MultiTerm.Protocols.Model; +using System.Text; + +namespace MultiTerm.Core.ViewModel; + +public partial class ByteDataViewModel : ObservableObject, IDataViewModel +{ + /// + /// Object of data model. + /// + private readonly ExtendedByte data; + + #region IDataViewModel Implementation + + [ObservableProperty] + private int lineIdentifier; + + [ObservableProperty] + private TimeOnly time; + + [ObservableProperty] + private string displayStringChar = String.Empty; + + [ObservableProperty] + private string displayStringHex = String.Empty; + + [ObservableProperty] + private string displayStringBin = String.Empty; + + #endregion + + /// + /// Allows access to the low level data byte. + /// + public byte Byte => this.data.Byte; + + /// + /// Instanciates new . + /// Initializes internal variables with data according to . + /// + /// data + /// identifies line in which this byte is located + public ByteDataViewModel(ExtendedByte extendedByte, int lineIdentifier) + { + this.data = extendedByte; + this.lineIdentifier = lineIdentifier; + this.DisplayStringChar = extendedByte.ToCharacterString(); + this.DisplayStringHex = extendedByte.ToHexString(); + this.DisplayStringBin = extendedByte.ToBinaryString(); + this.Time = extendedByte.Time; + } +} diff --git a/MultiTerm.Core/ViewModel/CharacterDataViewModel.cs b/MultiTerm.Core/ViewModel/CharacterDataViewModel.cs deleted file mode 100644 index 59f9d1e..0000000 --- a/MultiTerm.Core/ViewModel/CharacterDataViewModel.cs +++ /dev/null @@ -1,46 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using MultiTerm.Protocols.Model; - -namespace MultiTerm.Core.ViewModel; - -public partial class CharacterDataViewModel : ObservableObject, IDataViewModel -{ - /// - /// Object of data model. - /// - private readonly ExtendedChar character; - - #region IDataViewModel Implementation - - [ObservableProperty] - private int lineIdentifier; - - [ObservableProperty] - private TimeOnly time; - - [ObservableProperty] - private string displayString = String.Empty; - - [ObservableProperty] - private string displayStringHex = String.Empty; - - [ObservableProperty] - private string displayStringBin = String.Empty; - - #endregion - - /// - /// Allows access to character of this instance. - /// - public char Character { get { return this.character.Character; } } - - public CharacterDataViewModel(ExtendedChar character, int lineIdentifier) - { - this.character = character; - this.lineIdentifier = lineIdentifier; - this.DisplayString = character.ToUtf16String(); - this.DisplayStringHex = character.ToHexString(); - this.DisplayStringBin = character.ToBinaryString(); - this.Time = character.Time; - } -} diff --git a/MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs b/MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs index 81e96ba..ca97217 100644 --- a/MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs +++ b/MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs @@ -4,26 +4,27 @@ using CommunityToolkit.Mvvm.ComponentModel; using MultiTerm.Core.Types; using MultiTerm.Protocols.Model; using System.Collections.ObjectModel; +using System.Text; namespace MultiTerm.Core.ViewModel; -public partial class CommunicationDataViewModel : ObservableObject, ICommunicationDataViewModel +public partial class CommunicationDataViewModel : ObservableObject, ICommunicationDataViewModel { private readonly IContext uiContext; private int dataCharacterCount = 0; - private List? listOfPreviousCharacters = null; + private List? listOfPreviousCharacters = null; #region ICommunicationDataViewModel Implementation [ObservableProperty] - private ObservableCollection data = new(); + private ObservableCollection data = new(); [ObservableProperty] private string dataAsString = string.Empty; [ObservableProperty] - private ObservableCollection? selected = new(); + private ObservableCollection? selected = new(); [ObservableProperty] NewlineSeparatorType newlineSeparator = NewlineSeparatorType.None; @@ -42,29 +43,30 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati // int counter = 0; // int lineNumber = 1; // //List listOfChars = new(); - // foreach (var character in exampleData) + // foreach (var dataByte in exampleData) // { - // if(++counter > 100 || character == '\n') + // if(++counter > 100 || dataByte == '\n') // { // //this.ReceivedData.Add(); // //listOfChars = new List(); // counter = 0; // lineNumber += 1; // } - // var extdChar = new ExtendedChar(character); + // var extdChar = new ExtendedChar(dataByte); // //listOfChars.Add(extdChar); // this.ReceivedData.Add(new DataViewModel(extdChar, lineNumber)); // } //} } - public void HandleNewData(IEnumerable newRawData) + public void HandleNewData(IEnumerable newRawData) { // update collection and string, invoke ui thread if necessary ContextHelpers.InvokeIfNecessary(this.uiContext, (Action)delegate { - InsertNewNewCharactersIntoCollection(this.Data, ref this.dataCharacterCount, ref this.listOfPreviousCharacters, this.NewlineSeparator, newRawData); - this.DataAsString = InsertNewCharactersIntoString(this.DataAsString, newRawData); + this.DataAsString = InsertNewNewCharactersIntoCollection(this.Data, this.DataAsString, + ref this.dataCharacterCount, ref this.listOfPreviousCharacters, + this.NewlineSeparator, newRawData); }); } @@ -76,51 +78,69 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati // update collection, invoke ui thread if necessary ContextHelpers.InvokeIfNecessary(this.uiContext, (Action)delegate { - this.Data = ReorderCollection(this.Data, value); - this.DataAsString = ReorderString(this.Data, value); + var reorderedCollection = ReorderCollection(this.Data, value); + this.Data = reorderedCollection.Item1; + this.DataAsString = reorderedCollection.Item2; + }); + } + + + public void Clear() + { + // update collection and string, invoke ui thread if necessary + ContextHelpers.InvokeIfNecessary(this.uiContext, (Action)delegate + { + this.DataAsString = string.Empty; + this.Data.Clear(); }); } #region Collection Manipulation /// - /// Function that reorders a given collection with item type using the given . + /// Function that reorders a given collection with item type using the given . /// Reordered collection is returned, but LineIdentifier is also overwritten in the parameter. /// /// items in this collection will be reordered /// separator between lines - /// reordered collection - private static ObservableCollection ReorderCollection(ObservableCollection currentCollection, NewlineSeparatorType newlineSeparatorType) + /// reordered collection and string + private static Tuple, string> ReorderCollection(ObservableCollection currentCollection, NewlineSeparatorType newlineSeparatorType) { - ObservableCollection newCollection = new(); + ObservableCollection newCollection = new(); + StringBuilder stringBuilder = new(); + + // local vars int lineCounter = 0; - List? previousCharacters = null; + List? previousBytes = null; // iterate through items - foreach (CharacterDataViewModel item in currentCollection) + foreach (ByteDataViewModel item in currentCollection) { item.LineIdentifier = lineCounter; newCollection.Add(item); + stringBuilder.Append(item.DisplayStringChar); - switch (ShouldIntroduceNewlineAfterThisCharacter(item.Character, previousCharacters, newlineSeparatorType)) + switch (ShouldIntroduceNewlineAfterThisByte(item.Byte, previousBytes, newlineSeparatorType)) { - case IntroduceNewlineAfterThisCharacterResult.NoNewline: - // a decision could be made, previousCharacters can be cleared - if (previousCharacters != null) { previousCharacters = null; } + case ShouldIntroduceNewlineAfterThisByteResult.NoNewline: + // a decision could be made, previousBytes can be cleared + if (previousBytes != null) { previousBytes = null; } // nothing to do break; - case IntroduceNewlineAfterThisCharacterResult.IntroduceNewline: - // a decision could be made, previousCharacters can be cleared - if (previousCharacters != null) { previousCharacters = null; } - // increase line count and break + case ShouldIntroduceNewlineAfterThisByteResult.IntroduceNewline: + // a decision could be made, previousBytes can be cleared + if (previousBytes != null) { previousBytes = null; } + // increase line count lineCounter++; + // append line in string + stringBuilder.AppendLine(); break; - case IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters: - // first time that more characters are required => create new list - previousCharacters ??= new List(); - // add current character to list - previousCharacters.Add(item.Character); + case ShouldIntroduceNewlineAfterThisByteResult.RequiresMoreCharacters: + // first time that more bytes are required => create new list + previousBytes ??= new List(); + // add current byte to list + previousBytes.Add(item.Byte); break; default: @@ -128,65 +148,77 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati } } - return newCollection; + return Tuple.Create(newCollection, stringBuilder.ToString()); } /// - /// Function that handles a collection of new characters that should end up in the collection . + /// Function that handles a collection of new bytes that should end up in the collection . /// In case a new line is required, according to the given , it is automatically introduced. - /// Following parameters need to be referenced and stored outside: and . + /// Following parameters need to be referenced and stored outside: and . /// - /// collection to add the characters to + /// collection to add the bytes to + /// collection as string /// current line count - /// list of previous character, newest at the last position of the list, null if nothing is stored + /// list of previous bytes, newest at the last position of the list, null if nothing is stored /// separator between seperate lines - /// characters to add to the + /// bytes to add to the + /// string representation of data /// in case of any error - private static void InsertNewNewCharactersIntoCollection(ObservableCollection dataCollection, + private static string InsertNewNewCharactersIntoCollection( + ObservableCollection dataCollection, + string dataCollectionAsString, ref int collectionLineCounter, - ref List? previousCharacters, + ref List? previousBytes, NewlineSeparatorType newlineSeparatorType, - IEnumerable newCharacters) + IEnumerable newBytes) { - // go through every character - foreach (var newExtdChar in newCharacters) + StringBuilder stringBuilder = new(); + stringBuilder.Append(dataCollectionAsString); + + // go through every byte + foreach (ExtendedByte newByte in newBytes) { - // add to collection with the current counter, invoking UI context if necssary + // add to collection and string with the current counter, invoking UI context if necssary var currentLineCounter = collectionLineCounter; - dataCollection.Add(new CharacterDataViewModel(newExtdChar, currentLineCounter)); + dataCollection.Add(new ByteDataViewModel(newByte, currentLineCounter)); + stringBuilder.Append(newByte.ToCharacterString()); - switch (ShouldIntroduceNewlineAfterThisCharacter(newExtdChar.Character, previousCharacters, newlineSeparatorType)) + switch (ShouldIntroduceNewlineAfterThisByte(newByte.Byte, previousBytes, newlineSeparatorType)) { - case IntroduceNewlineAfterThisCharacterResult.NoNewline: - // a decision could be made, previousCharacters can be cleared - if (previousCharacters != null) { previousCharacters = null; } + case ShouldIntroduceNewlineAfterThisByteResult.NoNewline: + // a decision could be made, previousBytes can be cleared + if (previousBytes != null) { previousBytes = null; } // nothing to do break; - case IntroduceNewlineAfterThisCharacterResult.IntroduceNewline: - // a decision could be made, previousCharacters can be cleared - if (previousCharacters != null) { previousCharacters = null; } - // increase line count and break + case ShouldIntroduceNewlineAfterThisByteResult.IntroduceNewline: + // a decision could be made, previousBytes can be cleared + if (previousBytes != null) { previousBytes = null; } + // increase line count collectionLineCounter++; + // append line in string + stringBuilder.AppendLine(); break; - case IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters: - // first time that more characters are required => create new list - previousCharacters ??= new List(); - // add current character to list - previousCharacters.Add(newExtdChar.Character); + case ShouldIntroduceNewlineAfterThisByteResult.RequiresMoreCharacters: + // first time that more bytes are required => create new list + previousBytes ??= new List(); + // add current byte to list + previousBytes.Add(newByte.Byte); break; default: throw new Exception($"'{nameof(InsertNewNewCharactersIntoCollection)}()' failed because of error when checking if a newline should be introduced."); } } + + return stringBuilder.ToString(); } /// - /// Result type for + /// Result type for /// - public enum IntroduceNewlineAfterThisCharacterResult + public enum ShouldIntroduceNewlineAfterThisByteResult { /// /// No newline is required. @@ -194,7 +226,7 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati NoNewline, /// - /// A newline shall be introduced after this character. + /// A newline shall be introduced after this byte. /// IntroduceNewline, @@ -205,17 +237,17 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati } /// - /// Function to check wether a newline shall be introduced after the given character. - /// Since some newline sequences will require multiple characters in correct order, a more complex handling is required, which is possible using this function. + /// Function to check wether a newline shall be introduced after the given . + /// Since some newline sequences will require multiple bytes in correct order, a more complex handling is required, which is possible using this function. /// - /// the current character in the collection (or a single character) - /// list of previous character, newest at the last position of the list, null if not required + /// the current dataByte in the collection (or a single byte) + /// list of previous bytes, newest at the last position of the list, null if not required /// separator type - /// complex result of type + /// enum result of type /// if the handling for the is not implemented - private static IntroduceNewlineAfterThisCharacterResult ShouldIntroduceNewlineAfterThisCharacter(char character, List? previousCharacters, NewlineSeparatorType newlineSeparatorType) + private static ShouldIntroduceNewlineAfterThisByteResult ShouldIntroduceNewlineAfterThisByte(byte dataByte, List? previousBytes, NewlineSeparatorType newlineSeparatorType) { - var result = IntroduceNewlineAfterThisCharacterResult.NoNewline; + var result = ShouldIntroduceNewlineAfterThisByteResult.NoNewline; switch (newlineSeparatorType) { @@ -223,63 +255,41 @@ public partial class CommunicationDataViewModel : ObservableObject, ICommunicati break; case NewlineSeparatorType.CR: - if (character == '\r') + if (dataByte == (byte)'\r') { - result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + result = ShouldIntroduceNewlineAfterThisByteResult.IntroduceNewline; } break; case NewlineSeparatorType.LF: - if (character == '\n') + if (dataByte == (byte)'\n') { - result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + result = ShouldIntroduceNewlineAfterThisByteResult.IntroduceNewline; } break; case NewlineSeparatorType.CR_LF: - if (character == '\r') + if (dataByte == (byte)'\r') { - result = IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters; + result = ShouldIntroduceNewlineAfterThisByteResult.RequiresMoreCharacters; } - if (character == '\n') + if (dataByte == (byte)'\n') { - if (previousCharacters != null && previousCharacters.Last() == '\r') + if (previousBytes != null && previousBytes.Last() == (byte)'\r') { - result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline; + result = ShouldIntroduceNewlineAfterThisByteResult.IntroduceNewline; } } break; default: - throw new NotImplementedException($"'{nameof(ShouldIntroduceNewlineAfterThisCharacter)}()' does not implement handling for {nameof(NewlineSeparatorType)} {newlineSeparatorType}"); + throw new NotImplementedException($"'{nameof(ShouldIntroduceNewlineAfterThisByte)}()' does not implement handling for {nameof(NewlineSeparatorType)} {newlineSeparatorType}"); } return result; } - #endregion - - #region String Manipulation - - // TODO Implement Line Handling - private static string InsertNewCharactersIntoString(string dataAsString, IEnumerable newRawData) - { - string newDataAsString = dataAsString; - - foreach (ExtendedChar character in newRawData) - { - newDataAsString += character.Character; - } - - return newDataAsString; - } - - private static string ReorderString(ObservableCollection collection, NewlineSeparatorType value) - { - // Not yet implemented - return "Not implemented"; - } #endregion } diff --git a/MultiTerm.Core/ViewModel/ICommunicationDataViewModel.cs b/MultiTerm.Core/ViewModel/ICommunicationDataViewModel.cs index 076a452..a5a6622 100644 --- a/MultiTerm.Core/ViewModel/ICommunicationDataViewModel.cs +++ b/MultiTerm.Core/ViewModel/ICommunicationDataViewModel.cs @@ -32,4 +32,9 @@ public interface ICommunicationDataViewModel where T_Data : IData /// /// collection of new data to insert void HandleNewData(IEnumerable data); + + /// + /// Allows to clear the data. + /// + void Clear(); } \ No newline at end of file diff --git a/MultiTerm.Core/ViewModel/IDataViewModel.cs b/MultiTerm.Core/ViewModel/IDataViewModel.cs index 7ba1c8f..50337a9 100644 --- a/MultiTerm.Core/ViewModel/IDataViewModel.cs +++ b/MultiTerm.Core/ViewModel/IDataViewModel.cs @@ -14,9 +14,9 @@ public interface IDataViewModel TimeOnly Time { get; } /// - /// Hosts a displayable string of the character (UTF-16 encoded). + /// Hosts a displayable string of the character (.NET: UTF-16 encoded). /// - string DisplayString { get; } + string DisplayStringChar { get; } /// /// Hosts a string of the character (hexadecimal format). diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs index 105fa0f..0b25d36 100644 --- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs +++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs @@ -62,13 +62,13 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie /// Holds communication data that was received via the communication protocol. /// [ObservableProperty] - private ICommunicationDataViewModel receivedData; + private ICommunicationDataViewModel receivedData; /// /// Holds communication data that was sent via the communication protocol. /// [ObservableProperty] - private ICommunicationDataViewModel sentData; + private ICommunicationDataViewModel sentData; /// /// Defines at which newline sequence the displayed data is wrapped. Defaults to none. @@ -137,7 +137,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie // guard null if (this.ReceivedData == null) { throw new NullReferenceException($"{nameof(CommunicationProtocol_ReceivedDataEvent)} found {nameof(this.ReceivedData)} to be null."); } // handover data - this.ReceivedData.HandleNewData(e.ReceivedCharacters); + this.ReceivedData.HandleNewData(e.ReceivedData); } private void CommunicationProtocol_SentDataEvent(object? sender, SentDataEventArgs e) @@ -145,7 +145,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie // guard null if (this.SentData == null) { throw new NullReferenceException($"{nameof(CommunicationProtocol_SentDataEvent)} found {nameof(this.SentData)} to be null."); } // handover data - this.SentData.HandleNewData(e.SentCharacters); + this.SentData.HandleNewData(e.SentData); } #endregion diff --git a/MultiTerm.Protocols/Model/ExtendedByte.cs b/MultiTerm.Protocols/Model/ExtendedByte.cs new file mode 100644 index 0000000..90b2f25 --- /dev/null +++ b/MultiTerm.Protocols/Model/ExtendedByte.cs @@ -0,0 +1,131 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System.Text; + +namespace MultiTerm.Protocols.Model; + +/// +/// A class that represents a single and contains some additional information and methods. +/// A time can be stored in combination with this using the property. E.g. to represent arrived or sent time. +/// Several methods to display the in other formats are provided. +/// +public partial class ExtendedByte +{ + /// + /// Data in the form of a byte. + /// + public byte Byte { get; set; } + + /// + /// Time that is associated with the . + /// E.g. to represent arrived or sent time. + /// + public TimeOnly Time { get; private set; } + + /// + /// Creates an instance of with a given . + /// Sets to now. + /// + /// data + public ExtendedByte(byte dataByte) : this(dataByte, TimeOnly.FromDateTime(DateTime.Now)) { } + + /// + /// Creates an instance of with a given and . + /// + /// data + /// time + public ExtendedByte(byte dataByte, TimeOnly time) + { + this.Byte = dataByte; + this.Time = time; + } + + /// + /// Returns this as ASCII encoded String. + /// + /// + public override string ToString() + { + return Encoding.ASCII.GetString(new byte[] { this.Byte }); + } + + /// + /// Returns displayable character as string. + /// ASCII control sequences are replaced with unicode control pictures. + /// + /// string with single character + public string ToCharacterString() + { + char character = (char)this.Byte; + string characterString; + + // character is ASCII encoded and is a control sequence + if (char.IsAscii(character) && character <= '\x001F') + { + // conver to unicode Control Picture (see https://en.wikipedia.org/wiki/Control_Pictures) + characterString = ((char)('\u2400' + character)).ToString(); + } + // else just convert using ASCII code + else if(char.IsAscii(character)) + { + characterString = Encoding.ASCII.GetString(new byte[] { this.Byte }); + } + else + { + characterString = "?"; + } + + return characterString; + } + + /// + /// Returns displayable hex string. + /// + /// + public string ToHexString() + { + // hexadecimal converter for byte to string + static string hexConverter(byte ch) => Convert.ToHexString(new byte[] { ch }); + + // extract bytes from character and convert to correct format + return ConvertToString(new byte[] { this.Byte }, hexConverter, 2); + } + + /// + /// Returns displayable binary string. + /// + /// + public string ToBinaryString() + { + // binary converter for byte to string + static string binaryConverter(byte ch) => Convert.ToString(ch, 2); + + // extract bytes from character and convert to correct format + return ConvertToString(new byte[] { this.Byte }, binaryConverter, 8); + } + + /// + /// Converts a to a string, using the given to convert a single byte to a character. + /// defines how many characters the result string should contain (the left side will be padded with zeroes). + /// + /// converte string + private static string ConvertToString(byte[] byteArray, Converter conversionFunction, int oneByteWidth) + { + // foreach byte convert to binary and add to string + string resultString = String.Empty; + + // go through whole array + for (int i = 0; i < byteArray.Length; i++) + { + // add byte to string in correct format, padding left with zeros up to width of one byte + resultString += $"{conversionFunction(byteArray[i]).PadLeft(oneByteWidth, '0')}"; + + // if not last byte add space separator + if (i < byteArray.Length - 1) + { + resultString += " "; + } + } + + return resultString; + } +} diff --git a/MultiTerm.Protocols/Model/ExtendedChar.cs b/MultiTerm.Protocols/Model/ExtendedChar.cs deleted file mode 100644 index 70aa18a..0000000 --- a/MultiTerm.Protocols/Model/ExtendedChar.cs +++ /dev/null @@ -1,156 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using System.Text; - -namespace MultiTerm.Protocols.Model; - -/// -/// A class that represents a Character and contains some additional information and methods. -/// A time can be stored in combination with this Character using the property. E.g. to represent arrived or sent time. -/// Several methods to display the Character in other formats than Unicode are provided. -/// -public partial class ExtendedChar -{ - /// - /// Data in the form of a character. UTF-16 code unit. - /// - public char Character { get; set; } - - /// - /// Time that is associated with the . - /// E.g. to represent arrived or sent time. - /// - public TimeOnly Time { get; private set; } - - /// - /// Creates an instance of ExtendedChar with a given . - /// Sets to now. - /// - /// data - public ExtendedChar(char character) : this(character, TimeOnly.FromDateTime(DateTime.Now)) { } - - /// - /// Creates an instance of ExtendedChar with a given and . - /// Initializes string properties , and . - /// - /// data - /// time - public ExtendedChar(char character, TimeOnly time) - { - this.Character = character; - this.Time = time; - } - - public override string ToString() - { - return this.Character.ToString(); - } - - public string ToUtf16String() - { - string characterString; - - // character is ASCII encoded and is a control sequence - if (char.IsAscii(this.Character) && this.Character <= '\x001F') - { - // conver to unicode Control Picture (see https://en.wikipedia.org/wiki/Control_Pictures) - characterString = ((char)('\u2400' + this.Character)).ToString(); - - // TODO Remove - //characterString = this.Character switch - //{ - // '\t' => "\\t", - // ' ' => " ", - // '\n' => "\u240A", - // '\r' => "\u240D", - // '\v' => "\\v", - // '\f' => "\\f", - // _ => this.Character.ToString() - //}; - } - // TODO Remove - //else if (char.IsControl(this.Character)) - //{ - // characterString = ""; - //} - else - { - characterString = this.Character.ToString(); - } - - return characterString; - } - - public string ToHexString() - { - // hexadecimal converter for byte to string - Converter hexConverter = (ch) => Convert.ToHexString(new byte[] { ch }); - - // extract bytes from character and convert to correct format - return ConvertToString(GetCharacterByteArray(this.Character), hexConverter, 2); - - //// foreach byte convert to hex and add to string - //string hexString = String.Empty; - //for (int i = 0; i < byteArray.Length; i++) - //{ - // // add byte to string in binary format, padding left with zeros up to 8 - // hexString += $"{Convert.ToString(byteArray[i], 2).PadLeft(8, '0')}"; - // // if not last byte add space separator - // if (i < byteArray.Length - 1) - // { - // hexString += " "; - // } - //} - //return hexString; - - - //// get byte array from character and convert to hexadecimal - //return Convert.ToHexString(GetCharacterByteArray(this.Character)).PadLeft(2); - } - - public string ToBinaryString() - { - // binary converter for byte to string - Converter binaryConverter = (ch) => Convert.ToString(ch, 2); - - // extract bytes from character and convert to correct format - return ConvertToString(GetCharacterByteArray(this.Character), binaryConverter, 8); - } - - private static string ConvertToString(byte[] byteArray, Converter conversionFunction, int oneByteWidth) - { - // foreach byte convert to binary and add to string - string resultString = String.Empty; - - // go through whole array - for (int i = 0; i < byteArray.Length; i++) - { - // add byte to string in correct format, padding left with zeros up to width of one byte - resultString += $"{conversionFunction(byteArray[i]).PadLeft(oneByteWidth, '0')}"; - - // if not last byte add space separator - if (i < byteArray.Length - 1) - { - resultString += " "; - } - } - - return resultString; - } - - private static byte[] GetCharacterByteArray(char character) - { - byte[] byteArray = Array.Empty(); - - if (char.IsAscii(character)) - { - byteArray = Encoding.ASCII.GetBytes(character.ToString()); - } - else - { - // extract bytes from utf16 character - byteArray = Encoding.Unicode.GetBytes(character.ToString()); - } - - return byteArray; - } -} diff --git a/MultiTerm.Protocols/ReceivedDataEventArgs.cs b/MultiTerm.Protocols/ReceivedDataEventArgs.cs index 856185b..99ef028 100644 --- a/MultiTerm.Protocols/ReceivedDataEventArgs.cs +++ b/MultiTerm.Protocols/ReceivedDataEventArgs.cs @@ -4,10 +4,10 @@ namespace MultiTerm.Protocols; public class ReceivedDataEventArgs : EventArgs { - public IEnumerable ReceivedCharacters { get; private set; } + public IEnumerable ReceivedData { get; private set; } - public ReceivedDataEventArgs(IEnumerable receivedCharacters) + public ReceivedDataEventArgs(IEnumerable receivedData) { - this.ReceivedCharacters = receivedCharacters; + this.ReceivedData = receivedData; } } diff --git a/MultiTerm.Protocols/SentDataEventArgs.cs b/MultiTerm.Protocols/SentDataEventArgs.cs index ffa3c77..bb5b2d8 100644 --- a/MultiTerm.Protocols/SentDataEventArgs.cs +++ b/MultiTerm.Protocols/SentDataEventArgs.cs @@ -4,10 +4,10 @@ namespace MultiTerm.Protocols; public class SentDataEventArgs : EventArgs { - public IEnumerable SentCharacters { get; private set; } + public IEnumerable SentData { get; private set; } - public SentDataEventArgs(IEnumerable sentCharacters) + public SentDataEventArgs(IEnumerable sentData) { - this.SentCharacters = sentCharacters; + this.SentData = sentData; } } \ No newline at end of file diff --git a/MultiTerm.Protocols/Serial/SerialProtocol.cs b/MultiTerm.Protocols/Serial/SerialProtocol.cs index ab5c2c7..2350239 100644 --- a/MultiTerm.Protocols/Serial/SerialProtocol.cs +++ b/MultiTerm.Protocols/Serial/SerialProtocol.cs @@ -76,14 +76,14 @@ public class SerialProtocol : CommunicationProtocol while(ct.IsCancellationRequested == false) { // reads character based on configured encoding (here ASCII) - int readCharacter = serialPort.ReadChar(); - if (readCharacter != -1) // -1 = timeout + int readByte = serialPort.ReadByte(); + if (readByte != -1) // -1 = end of stream { // create extended char type - var character = new ExtendedChar((char)readCharacter); + var character = new ExtendedByte((byte)readByte); // report new data with event - this.OnReceivedData(new ReceivedDataEventArgs(new ExtendedChar[] { character })); + this.OnReceivedData(new ReceivedDataEventArgs(new ExtendedByte[] { character })); } } } diff --git a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs index 031ac40..eb9d807 100644 --- a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs +++ b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs @@ -27,7 +27,7 @@ public class MultiFormatDataView : Control #region Dependency Properties public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", - typeof(ICommunicationDataViewModel), typeof(MultiFormatDataView), + typeof(ICommunicationDataViewModel), typeof(MultiFormatDataView), new PropertyMetadata(null, OnDataSourcePropertyChanged)); public static readonly DependencyProperty SelectorItemsSourceProperty = @@ -69,9 +69,9 @@ public class MultiFormatDataView : Control /// .NET Property for DataSourceProperty. /// [Bindable(true)] - public ICommunicationDataViewModel DataSource + public ICommunicationDataViewModel DataSource { - get { return (ICommunicationDataViewModel)GetValue(DataSourceProperty); } + get { return (ICommunicationDataViewModel)GetValue(DataSourceProperty); } set { SetValue(DataSourceProperty, value); } } @@ -195,12 +195,12 @@ public class MultiFormatDataView : Control // manually create collection view ICollectionView cv = CollectionViewSource.GetDefaultView(mfdv.DataSource.Data); // add grouping - PropertyGroupDescription groupDescription = new(nameof(CharacterDataViewModel.LineIdentifier)); + PropertyGroupDescription groupDescription = new(nameof(ByteDataViewModel.LineIdentifier)); cv.GroupDescriptions.Add(groupDescription); // add live grouping if (cv is ICollectionViewLiveShaping cvLiveShaping && cvLiveShaping.CanChangeLiveGrouping) { - cvLiveShaping.LiveGroupingProperties.Add(nameof(CharacterDataViewModel.LineIdentifier)); + cvLiveShaping.LiveGroupingProperties.Add(nameof(ByteDataViewModel.LineIdentifier)); cvLiveShaping.IsLiveGrouping = true; } // save collection view @@ -247,7 +247,8 @@ public class MultiFormatDataView : Control private void OnClearButtonClicked(object sender, RoutedEventArgs e) { // raise clear requested event - this.DataSource.Data.Clear(); + RoutedEventArgs args = new(ClearRequestedEvent); + RaiseEvent(args); } #region Selected Items handling @@ -260,11 +261,11 @@ public class MultiFormatDataView : Control if (this.tbCharOnlyView != null && this.tbCharOnlyView.SelectionLength > 0) { this.tbCharOnlyView.Select(0, 0); } // otherwise update internal list - foreach (CharacterDataViewModel item in e.RemovedItems) + foreach (ByteDataViewModel item in e.RemovedItems) { this.DataSource.Selected.Remove(item); } - foreach (CharacterDataViewModel item in e.AddedItems) + foreach (ByteDataViewModel item in e.AddedItems) { this.DataSource.Selected.Add(item); } @@ -272,7 +273,7 @@ public class MultiFormatDataView : Control private void TextBoxCharOnlyView_SelectionChanged(object sender, RoutedEventArgs e) { - var newSelection = new ObservableCollection(); + var newSelection = new ObservableCollection(); int selectionStartIndex = this.tbCharOnlyView!.SelectionStart; // TEMP OLD // extract text from the beginning to the start of the selected text diff --git a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml index 019f419..45193bf 100644 --- a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml +++ b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml @@ -56,7 +56,7 @@ -