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. 96
      MultiTerm.Core/Model/MultiFormatString.cs
  2. 4
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  3. 23
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  4. 9
      MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs

@ -197,14 +197,15 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
} }
} }
#region ToString #region GetBytes, Conversions
/// <summary> /// <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> /// </summary>
/// <returns>converted string</returns> /// <returns>byte array of this <see cref="MultiFormatString"/></returns>
public override string ToString() // TODO Implement Unit Tests! public byte[] GetBytes()
{ {
StringBuilder stringBuilder = new(); List<byte> bytes = new();
string hexConversionCharacters = String.Empty, binaryConversionCharacters = String.Empty; string hexConversionCharacters = String.Empty, binaryConversionCharacters = String.Empty;
// Internal function to check and finalize a Hexadecimal Conversion // Internal function to check and finalize a Hexadecimal Conversion
@ -219,8 +220,8 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
formattedChar.Format != FormatType.Hexadecimal || formattedChar.Format != FormatType.Hexadecimal ||
hexConversionCharacters.Length == charsPerHexBundle) hexConversionCharacters.Length == charsPerHexBundle)
{ {
// finalize conversion // finalize conversion, expects one byte only => therefore use first
stringBuilder.Append(FromHexString(hexConversionCharacters)); bytes.Add(GetBytesFromString(hexConversionCharacters, 16, charsPerHexBundle).First());
// reset ongoing conversion // reset ongoing conversion
hexConversionCharacters = String.Empty; hexConversionCharacters = String.Empty;
} }
@ -239,19 +240,18 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
formattedChar.Format != FormatType.Binary || formattedChar.Format != FormatType.Binary ||
binaryConversionCharacters.Length == charsPerBinBundle) binaryConversionCharacters.Length == charsPerBinBundle)
{ {
// finalize conversion // finalize conversion, expects one byte only => therefore use first
stringBuilder.Append(FromBinaryString(binaryConversionCharacters)); bytes.Add(GetBytesFromString(hexConversionCharacters, 2, charsPerBinBundle).First());
// reset ongoing conversion // reset ongoing conversion
binaryConversionCharacters = String.Empty; binaryConversionCharacters = String.Empty;
} }
} }
} }
// go through every character foreach (IFormattedCharacter character in this.Items)
foreach (var character in this.Items)
{ {
// can only handle FormattedCharacters // skip all characters that are not of type FormattedCharacter
if (character is not FormattedCharacter formattedCharacter) { continue; } if(character is not FormattedCharacter formattedCharacter) { continue; }
// finalize ongoing conversions if there are any // finalize ongoing conversions if there are any
finalizeHexConversion(formattedCharacter); finalizeHexConversion(formattedCharacter);
@ -260,7 +260,7 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
switch (formattedCharacter.Format) // switch by format switch (formattedCharacter.Format) // switch by format
{ {
case FormatType.Character: case FormatType.Character:
stringBuilder.Append(formattedCharacter.Character); bytes.Add(Encoding.ASCII.GetBytes(formattedCharacter.Character).First());
break; break;
case FormatType.Hexadecimal: case FormatType.Hexadecimal:
@ -272,44 +272,74 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
break; break;
default: 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 // fully finalize after the loop, finish all unfinished conversions
finalizeHexConversion(null); finalizeHexConversion(null);
finalizeBinaryConversion(null); finalizeBinaryConversion(null);
return stringBuilder.ToString(); return bytes.ToArray();
} }
// from: https://stackoverflow.com/questions/724862/converting-from-hex-to-string // TODO Implement Unit Tests!
// test with: returns: "Hello world" for "48656C6C6F20776F726C64" /// <summary>
// see https://www.fileformat.info/info/charset/UTF-16/list.htm /// Converts the <see cref="MultiFormatString"/> to a string that only contains ASCII characters.
private static string FromHexString(string hexString) /// 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 return Encoding.ASCII.GetString(this.GetBytes());
string internalHexString = hexString.PadLeft(charsPerHexBundle, '0');
var bytes = new byte[internalHexString.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(internalHexString.Substring(i * 2, 2), 16);
}
return Encoding.ASCII.GetString(bytes);
} }
private static string FromBinaryString(string binaryString) // 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)
{ {
string internalBinaryString = binaryString.PadLeft(charsPerBinBundle, '0'); // Added left padded zeroes (total width + spill) so the block size matches the number base
var bytes = new byte[internalBinaryString.Length / 8]; 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++) for (var i = 0; i < bytes.Length; i++)
{ {
bytes[i] = Convert.ToByte(internalBinaryString.Substring(i * 8, 8), 2); bytes[i] = Convert.ToByte(paddedString.Substring(i * byteWidth, byteWidth), fromBase);
} }
return bytes;
}
/// <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)
{
var bytes = GetBytesFromString(hexString, 16, charsPerHexBundle);
return Encoding.ASCII.GetString(bytes); 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 #endregion
} }

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

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

@ -217,13 +217,18 @@ public class MultiFormatTextBox : Control
} }
// if this is the first element to enter in the paragraph => simply add it // if this is the first element to enter in the paragraph => simply add it
if(paragraph!.Inlines.Count == 0) if (paragraph!.Inlines.Count == 0)
{ {
paragraph.Inlines.Add(run); 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 else // add to paragraph of richtextbox after previous inline
{ {
var previousInline = paragraph.Inlines.ElementAt(new Index(indexCounter-1)); // paragraph.Inlines = zero based counting var previousInline = paragraph.Inlines.ElementAt(new Index(indexCounter - 1)); // paragraph.Inlines = zero based counting
paragraph.Inlines.InsertAfter(previousInline, run); paragraph.Inlines.InsertAfter(previousInline, run);
} }

Loading…
Cancel
Save