worked on conversion of strings,

changed sending data as bytes rather than as encoded string,
fixed bug in MultiFormatTextBox
master
Jonas Arnold 3 years ago
parent eac55765c2
commit 39e6420a58
  1. 94
      MultiTerm.Core/Model/MultiFormatString.cs
  2. 4
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  3. 23
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  4. 5
      MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs

@ -197,14 +197,15 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
}
}
#region ToString
#region GetBytes, Conversions
/// <summary>
/// Converts the <see cref="MultiFormatString"/> to a string that only contains Unicode (UTF-16) characters.
/// Converts this <see cref="MultiFormatString"/> to a byte array. Associated bundles of <see cref="FormattedCharacter"/> are converted to bytes.
/// Characters of Format <see cref="FormatType.Character"/> are are ASCII Encoded.
/// </summary>
/// <returns>converted string</returns>
public override string ToString() // TODO Implement Unit Tests!
/// <returns>byte array of this <see cref="MultiFormatString"/></returns>
public byte[] GetBytes()
{
StringBuilder stringBuilder = new();
List<byte> bytes = new();
string hexConversionCharacters = String.Empty, binaryConversionCharacters = String.Empty;
// Internal function to check and finalize a Hexadecimal Conversion
@ -219,8 +220,8 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
formattedChar.Format != FormatType.Hexadecimal ||
hexConversionCharacters.Length == charsPerHexBundle)
{
// finalize conversion
stringBuilder.Append(FromHexString(hexConversionCharacters));
// finalize conversion, expects one byte only => therefore use first
bytes.Add(GetBytesFromString(hexConversionCharacters, 16, charsPerHexBundle).First());
// reset ongoing conversion
hexConversionCharacters = String.Empty;
}
@ -239,18 +240,17 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
formattedChar.Format != FormatType.Binary ||
binaryConversionCharacters.Length == charsPerBinBundle)
{
// finalize conversion
stringBuilder.Append(FromBinaryString(binaryConversionCharacters));
// finalize conversion, expects one byte only => therefore use first
bytes.Add(GetBytesFromString(hexConversionCharacters, 2, charsPerBinBundle).First());
// reset ongoing conversion
binaryConversionCharacters = String.Empty;
}
}
}
// go through every character
foreach (var character in this.Items)
foreach (IFormattedCharacter character in this.Items)
{
// can only handle FormattedCharacters
// skip all characters that are not of type FormattedCharacter
if(character is not FormattedCharacter formattedCharacter) { continue; }
// finalize ongoing conversions if there are any
@ -260,7 +260,7 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
switch (formattedCharacter.Format) // switch by format
{
case FormatType.Character:
stringBuilder.Append(formattedCharacter.Character);
bytes.Add(Encoding.ASCII.GetBytes(formattedCharacter.Character).First());
break;
case FormatType.Hexadecimal:
@ -272,44 +272,74 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
break;
default:
throw new NotImplementedException($"'{nameof(ToString)}()' does not implement conversion for format {formattedCharacter.Format}");
throw new NotImplementedException($"'{nameof(GetBytes)}()' does not implement conversion for format {formattedCharacter.Format}");
}
}
// fully finalize after the loop, finish all unfinished conversions
finalizeHexConversion(null);
finalizeBinaryConversion(null);
return stringBuilder.ToString();
return bytes.ToArray();
}
// from: https://stackoverflow.com/questions/724862/converting-from-hex-to-string
// test with: returns: "Hello world" for "48656C6C6F20776F726C64"
// see https://www.fileformat.info/info/charset/UTF-16/list.htm
private static string FromHexString(string hexString)
// TODO Implement Unit Tests!
/// <summary>
/// Converts the <see cref="MultiFormatString"/> to a string that only contains ASCII characters.
/// Therefore this method converts all parts of other formats (e.g. <see cref="FormatType.Hexadecimal"/> or <see cref="FormatType.Binary"/>) to ASCII characters.
/// </summary>
/// <returns>converted string</returns>
public string ToAsciiEncodedString()
{
// Added PadLeft so strings with one character do not get ignored
string internalHexString = hexString.PadLeft(charsPerHexBundle, '0');
var bytes = new byte[internalHexString.Length / 2];
return Encoding.ASCII.GetString(this.GetBytes());
}
// TODO Unit Tests
/// <summary>
/// Converts an input string to a bytes array with the given base (<paramref name="fromBase"/>) and bundle size (<paramref name="byteWidth"/>).
/// Input string gets padded with zeroes left.
/// Idea from: https://stackoverflow.com/questions/724862/converting-from-hex-to-string
/// </summary>
/// <param name="str">input string, without separators between individual bytes! </param>
/// <param name="fromBase">number base of the input string (e.g. 2 for binary)</param>
/// <param name="byteWidth">amount of characters required for a complete byte in the <paramref name="fromBase"/> format (e.g. 8 for a binary byte)</param>
/// <returns>byte array of the given input string</returns>
private static byte[] GetBytesFromString(string str, int fromBase, int byteWidth)
{
// Added left padded zeroes (total width + spill) so the block size matches the number base
string paddedString = str.PadLeft((str.Length + (str.Length % byteWidth)), '0');
// creates bytes array of correct size
var bytes = new byte[paddedString.Length / byteWidth];
// convert every part of the string to
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(internalHexString.Substring(i * 2, 2), 16);
bytes[i] = Convert.ToByte(paddedString.Substring(i * byteWidth, byteWidth), fromBase);
}
return Encoding.ASCII.GetString(bytes);
return bytes;
}
private static string FromBinaryString(string binaryString)
{
string internalBinaryString = binaryString.PadLeft(charsPerBinBundle, '0');
var bytes = new byte[internalBinaryString.Length / 8];
for (var i = 0; i < bytes.Length; i++)
/// <summary>
/// Returns ASCII encoded string from a hex string of <see cref="charsPerHexBundle"/> amount of hexadecimal characters.
/// </summary>
/// <param name="hexString">hex string with maximum amount of hex characters according to <see cref="charsPerHexBundle"/>. hex characters must be without any separator (no : or - inbetween bytes)!</param>
/// <returns>ASCII Encoded string of the <paramref name="hexString"/></returns>
private static string GetAsciiEncodedStringFromHexString(string hexString)
{
bytes[i] = Convert.ToByte(internalBinaryString.Substring(i * 8, 8), 2);
var bytes = GetBytesFromString(hexString, 16, charsPerHexBundle);
return Encoding.ASCII.GetString(bytes);
}
/// <summary>
/// Returns ASCII encoded string from a binary string of <see cref="charsPerBinBundle"/> amount of binary characters.
/// </summary>
/// <param name="binaryString">hex string with maximum amount of binaryString characters according to <see cref="charsPerBinBundle"/>. binary characters must be without any separator (no space or - inbetween bytes)!</param>
/// <returns>ASCII Encoded string of the <paramref name="binaryString"/></returns>
private static string GetAsciiEncodedStringFromBinaryString(string binaryString)
{
var bytes = GetBytesFromString(binaryString, 2, charsPerBinBundle);
return Encoding.ASCII.GetString(bytes);
}
#endregion
}

@ -40,9 +40,9 @@ public partial class SendReceiveViewModel : TerminalViewModel
// Temp
//var items = this.CommunicationData.SelectedReceivedData;
//Debugger.Break();
this.TempSentDataString = this.SendableData.ToString();
this.TempSentDataString = this.SendableData.ToAsciiEncodedString();
// send data
this.SendToCommunicationProtocol(this.SendableData.ToString());
this.SendToCommunicationProtocol(this.SendableData.GetBytes());
// clear textbox
this.SendableData.Clear();

@ -151,11 +151,11 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
#endregion
/// <summary>
/// Sends the given string to this instances <see cref="CommunicationProtocol"/>, UTF-16 encoded.
/// Sends the given data to this instances <see cref="CommunicationProtocol"/>.
/// Appends configured <see cref="SendNewlineSeparatorType"/> to the end of string.
/// </summary>
/// <param name="data">data string</param>
protected void SendToCommunicationProtocol(string data)
/// <param name="data">data in form of enumerable (e.g. array)</param>
protected void SendToCommunicationProtocol(IEnumerable<byte> data)
{
// guard null values
if(this.CommunicationProtocol == null) { throw new NullReferenceException($"'{nameof(SendToCommunicationProtocol)}()' was called but {nameof(CommunicationProtocol)} is null."); }
@ -168,22 +168,21 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
}
// add newline sequence to end of data
string newlineSequence = this.SendNewlineSeparatorType switch
byte[] newlineSequence = this.SendNewlineSeparatorType switch
{
NewlineSeparatorType.None => String.Empty,
NewlineSeparatorType.CR => "\r",
NewlineSeparatorType.LF => "\n",
NewlineSeparatorType.CR_LF => "\r\n",
NewlineSeparatorType.None => Array.Empty<byte>(),
NewlineSeparatorType.CR => new byte[] { (byte)'\r' },
NewlineSeparatorType.LF => new byte[] { (byte)'\n' },
NewlineSeparatorType.CR_LF => new byte[] { (byte)'\r', (byte)'\n' },
_ => throw new NotImplementedException($"'{nameof(SendToCommunicationProtocol)}()' " +
$"does not implement handling for {nameof(NewlineSeparatorType)} {this.SendNewlineSeparatorType}.")
};
string modifiedData = data + newlineSequence;
// extract unicode byte array
var byteArray = Encoding.Unicode.GetBytes(modifiedData);
// join data and newline sequence
var dataWithNewlineSequence = data.Concat(newlineSequence);
// send
this.CommunicationProtocol.SendBytes(byteArray);
this.CommunicationProtocol.SendBytes(dataWithNewlineSequence.ToArray());
}
#region Protocol Settings

@ -221,6 +221,11 @@ public class MultiFormatTextBox : Control
{
paragraph.Inlines.Add(run);
}
// if the previous item is outside of the range => insert at first position
else if(indexCounter - 1 < 0)
{
paragraph.Inlines.InsertBefore(paragraph.Inlines.FirstInline, run);
}
else // add to paragraph of richtextbox after previous inline
{
var previousInline = paragraph.Inlines.ElementAt(new Index(indexCounter - 1)); // paragraph.Inlines = zero based counting

Loading…
Cancel
Save