@ -1,5 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel ;
using CommunityToolkit.Mvvm.Input ;
using MultiTerm.Core.Types ;
using MultiTerm.Protocols ;
using MultiTerm.Protocols.Model ;
using System.Collections.ObjectModel ;
@ -9,6 +10,8 @@ namespace MultiTerm.Core.ViewModel;
public partial class CommunicationDataViewModel : ObservableObject
{
private ICommunicationProtocol ? communicationProtocol ;
private NewlineSeparatorType currentReceiveNewlineSeparatorType = NewlineSeparatorType . None ;
private NewlineSeparatorType currentSentNewlineSeparatorType = NewlineSeparatorType . None ;
/// <summary>
/// Represents the collection of received characters from a communication protocol.
@ -76,22 +79,220 @@ public partial class CommunicationDataViewModel : ObservableObject
}
}
private void CommunicationProtocol_ReceivedDataEvent ( object? sender , ReceivedDataEventArgs e )
/// <summary>
/// Overwrites local variables for current newline separators and triggers recration of datacollections according to new newline separators (only if they changed).
/// </summary>
/// <param name="receiveNewlineSeparatorType">new receive newline separator type or null</param>
/// <param name="sendNewlineSeparatorType">new send newline separator type or null</param>
public void ConfigureNewlineSeparators ( NewlineSeparatorType ? receiveNewlineSeparatorType , NewlineSeparatorType ? sendNewlineSeparatorType )
{
foreach ( var receivedChar in e . ReceivedCharacters )
if ( receiveNewlineSeparatorType ! = null )
{
//this.ReceivedData.Add(receivedChar); // TODO Fix
// if it is the same as the current newline separator type => ignore
if ( receiveNewlineSeparatorType = = this . currentReceiveNewlineSeparatorType ) { return ; }
// set new newline separator type and overwrite data collection (triggers property changed)
this . currentReceiveNewlineSeparatorType = ( NewlineSeparatorType ) receiveNewlineSeparatorType ;
this . ReceivedData = ReorderDataCollection ( this . ReceivedData , this . currentReceiveNewlineSeparatorType ) ;
}
if ( sendNewlineSeparatorType ! = null )
{
// if it is the same as the current newline separator type => ignore
if ( sendNewlineSeparatorType = = this . currentSentNewlineSeparatorType ) { return ; }
// set new newline separator type and overwrite data collection (triggers property changed)
this . currentSentNewlineSeparatorType = ( NewlineSeparatorType ) sendNewlineSeparatorType ;
this . SentData = ReorderDataCollection ( this . SentData , this . currentSentNewlineSeparatorType ) ;
}
}
/// <summary>
/// Function that reorders a given collection using the given <paramref name="newlineSeparatorType"/>.
/// Reordered collection is returned, but LineIdentifier is also overwritten in the <paramref name="currentCollection"/> parameter.
/// </summary>
/// <param name="currentCollection">items in this collection will be reordered</param>
/// <param name="newlineSeparatorType">separator between lines</param>
/// <returns>reordered collection</returns>
private static ObservableCollection < DataViewModel > ReorderDataCollection ( ObservableCollection < DataViewModel > currentCollection , NewlineSeparatorType newlineSeparatorType )
{
ObservableCollection < DataViewModel > newCollection = new ( ) ;
int lineCounter = 0 ;
List < char > ? previousCharacters = null ;
foreach ( var item in currentCollection )
{
item . LineIdentifier = lineCounter ;
newCollection . Add ( item ) ;
switch ( ShouldIntroduceNewlineAfterThisCharacter ( item . Character . Character , previousCharacters , newlineSeparatorType ) )
{
case IntroduceNewlineAfterThisCharacterResult . NoNewline :
// a decision could be made, previousCharacters can be cleared
if ( previousCharacters ! = null ) { previousCharacters = 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
lineCounter + + ;
break ;
case IntroduceNewlineAfterThisCharacterResult . RequiresMoreCharacters :
// first time that more characters are required => create new list
previousCharacters ? ? = new List < char > ( ) ;
// add current character to list
previousCharacters . Add ( item . Character . Character ) ;
break ;
default :
throw new Exception ( $"'{nameof(ReorderDataCollection)}()' failed because of error when checking if a newline should be introduced." ) ;
}
}
return newCollection ;
}
/// <summary>
/// Result type for <see cref="ShouldIntroduceNewlineAfterThisCharacter"/>
/// </summary>
public enum IntroduceNewlineAfterThisCharacterResult
{
/// <summary>
/// No newline is required.
/// </summary>
NoNewline ,
/// <summary>
/// A newline shall be introduced after this character.
/// </summary>
IntroduceNewline ,
/// <summary>
/// Following characters are required to finalize result wether a newline shall be introduced or not.
/// </summary>
RequiresMoreCharacters
}
/// <summary>
/// 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.
/// </summary>
/// <param name="character">the current character in the collection (or a single character)</param>
/// <param name="previousCharacters">list of previous character, newest at the last position of the list, null if not required</param>
/// <param name="newlineSeparatorType">separator type</param>
/// <returns>complex result of type <see cref="IntroduceNewlineAfterThisCharacterResult"/></returns>
/// <exception cref="NotImplementedException">if the handling for the <paramref name="newlineSeparatorType"/> is not implemented</exception>
private static IntroduceNewlineAfterThisCharacterResult ShouldIntroduceNewlineAfterThisCharacter ( char character , List < char > ? previousCharacters , NewlineSeparatorType newlineSeparatorType )
{
var result = IntroduceNewlineAfterThisCharacterResult . NoNewline ;
switch ( newlineSeparatorType )
{
case NewlineSeparatorType . None :
break ;
case NewlineSeparatorType . CR :
if ( character = = '\r' )
{
result = IntroduceNewlineAfterThisCharacterResult . IntroduceNewline ;
}
break ;
case NewlineSeparatorType . LF :
if ( character = = '\n' )
{
result = IntroduceNewlineAfterThisCharacterResult . IntroduceNewline ;
}
break ;
case NewlineSeparatorType . CR_LF :
if ( character = = '\r' )
{
result = IntroduceNewlineAfterThisCharacterResult . RequiresMoreCharacters ;
}
if ( character = = '\n' )
{
if ( previousCharacters ! = null & & previousCharacters . Last ( ) = = '\r' )
{
result = IntroduceNewlineAfterThisCharacterResult . IntroduceNewline ;
}
}
break ;
default :
throw new NotImplementedException ( $"'{nameof(ShouldIntroduceNewlineAfterThisCharacter)}()' does not implement handling for {nameof(NewlineSeparatorType)} {newlineSeparatorType}" ) ;
}
return result ;
}
private int receivedDataCharacterCount = 0 ;
private List < char > ? listOfPreviouslyReceivedCharacters = null ;
private void CommunicationProtocol_ReceivedDataEvent ( object? sender , ReceivedDataEventArgs e )
{
HandleNewCharacters ( this . ReceivedData , ref this . receivedDataCharacterCount , ref this . listOfPreviouslyReceivedCharacters , this . currentReceiveNewlineSeparatorType , e . ReceivedCharacters ) ;
}
private int sentDataCharacterCount = 0 ;
private List < char > ? listOfPreviouslySentCharacters = null ;
private void CommunicationProtocol_SentDataEvent ( object? sender , SentDataEventArgs e )
{
foreach ( var sentChar in e . SentCharacters )
HandleNewCharacters ( this . SentData , ref this . sentDataCharacterCount , ref this . listOfPreviouslySentCharacters , this . currentSentNewlineSeparatorType , e . SentCharacters ) ;
}
/// <summary>
/// Function that handles a list of new characters that should end up in the collection <paramref name="dataCollection"/>.
/// In case a new line is required, according to the given <paramref name="newlineSeparatorType"/>, it is automatically introduced.
/// Following parameters need to be referenced and stored outside: <paramref name="collectionLineCounter"/> and <paramref name="previousCharacters"/>.
/// </summary>
/// <param name="dataCollection">collection to add the characters to</param>
/// <param name="collectionLineCounter">current line count</param>
/// <param name="previousCharacters">list of previous character, newest at the last position of the list, null if nothing is stored</param>
/// <param name="newlineSeparatorType">separator between seperate lines</param>
/// <param name="characters">characters to add to the <paramref name="dataCollection"/></param>
/// <exception cref="Exception">in case of any error</exception>
private void HandleNewCharacters ( ObservableCollection < DataViewModel > dataCollection ,
ref int collectionLineCounter ,
ref List < char > ? previousCharacters ,
NewlineSeparatorType newlineSeparatorType ,
IEnumerable < ExtendedChar > characters )
{
// go through every character
foreach ( var newExtdChar in characters )
{
//this.SentCharacters.Add(sentChar); // TODO Fix
// add to collection with the current counter
dataCollection . Add ( new DataViewModel ( newExtdChar , collectionLineCounter ) ) ;
switch ( ShouldIntroduceNewlineAfterThisCharacter ( newExtdChar . Character , previousCharacters , newlineSeparatorType ) )
{
case IntroduceNewlineAfterThisCharacterResult . NoNewline :
// a decision could be made, previousCharacters can be cleared
if ( previousCharacters ! = null ) { previousCharacters = 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
collectionLineCounter + + ;
break ;
case IntroduceNewlineAfterThisCharacterResult . RequiresMoreCharacters :
// first time that more characters are required => create new list
previousCharacters ? ? = new List < char > ( ) ;
// add current character to list
previousCharacters . Add ( newExtdChar . Character ) ;
break ;
default :
throw new Exception ( $"'{nameof(HandleNewCharacters)}()' failed because of error when checking if a newline should be introduced." ) ;
}
}
}
[RelayCommand]
private void ClearReceivedData ( )
{