Multiprocotol Terminalprogram (BAT)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MultiTerm/MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs

316 lines
20 KiB

using Common;
using Common.Helpers;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Types;
using MultiTerm.Protocols;
using MultiTerm.Protocols.Model;
using System.Collections.ObjectModel;
namespace MultiTerm.Core.ViewModel;
public partial class CommunicationDataViewModel : ObservableObject
{
private ICommunicationProtocol? communicationProtocol;
private readonly IContext uiContext;
private NewlineSeparatorType currentReceiveNewlineSeparatorType = NewlineSeparatorType.None;
private NewlineSeparatorType currentSentNewlineSeparatorType = NewlineSeparatorType.None;
/// <summary>
/// Represents the collection of received characters from a communication protocol.
/// </summary>
[ObservableProperty]
private ObservableCollection<DataViewModel> receivedData = new();
/// <summary>
/// Represents the collection of the selection of received characters.
/// </summary>
[ObservableProperty]
private List<DataViewModel>? selectedReceivedData;
/// <summary>
/// Represents the collection of sent characters to a communication protocol.
/// </summary>
[ObservableProperty]
private ObservableCollection<DataViewModel> sentData = new();
/// <summary>
/// Represents the collection of the selection of sent characters.
/// </summary>
[ObservableProperty]
private List<DataViewModel>? selectedSentData;
public CommunicationDataViewModel(ICommunicationProtocol? communicationProtocol, IContext context)
{
this.uiContext = context;
this.communicationProtocol = communicationProtocol;
if (this.communicationProtocol != null)
{
this.communicationProtocol.ReceivedDataEvent += CommunicationProtocol_ReceivedDataEvent;
this.communicationProtocol.SentDataEvent += CommunicationProtocol_SentDataEvent;
}
else // TEMP
{
//string exampleData = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pellentesque elit eget gravida cum sociis natoque penatibus et magnis. Purus sit amet volutpat consequat mauris nunc congue nisi vitae. Id ornare arcu odio ut sem. Neque ornare aenean euismod elementum nisi quis eleifend. Faucibus vitae aliquet nec ullamcorper sit. Fermentum iaculis eu non diam phasellus vestibulum lorem sed risus. Pellentesque nec nam aliquam sem et tortor consequat id porta. Diam sollicitudin tempor id eu nisl. Fames ac turpis egestas sed tempus urna et. Commodo odio aenean sed adipiscing diam donec adipiscing tristique risus. Id aliquet lectus proin nibh nisl condimentum id. Dolor sit amet consectetur adipiscing elit duis. Sed vulputate odio ut enim blandit. Neque convallis a cras semper auctor neque vitae.\r\n\r\nLacus laoreet non curabitur gravida arcu ac tortor. Volutpat maecenas volutpat blandit aliquam. Neque laoreet suspendisse interdum consectetur. Tincidunt augue interdum velit euismod in pellentesque massa placerat. Magna fringilla urna porttitor rhoncus dolor purus non enim praesent. Fermentum leo vel orci porta non pulvinar neque laoreet suspendisse. Scelerisque viverra mauris in aliquam sem fringilla. Nec feugiat nisl pretium fusce id velit ut. Urna cursus eget nunc scelerisque viverra mauris. Condimentum mattis pellentesque id nibh tortor id aliquet. Enim sed faucibus turpis in eu. Adipiscing elit pellentesque habitant morbi tristique. A pellentesque sit amet porttitor eget dolor morbi non arcu. Amet commodo nulla facilisi nullam vehicula ipsum a arcu. Hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper. Curabitur vitae nunc sed velit dignissim sodales ut. Malesuada fames ac turpis egestas maecenas pharetra. Tellus pellentesque eu tincidunt tortor aliquam.\r\n\r\nVitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra. Eget mi proin sed libero enim sed faucibus. Commodo nulla facilisi nullam vehicula ipsum. Proin libero nunc consequat interdum varius sit amet mattis vulputate. Morbi tristique senectus et netus. Feugiat scelerisque varius morbi enim nunc. Nulla aliquet enim tortor at auctor urna nunc. Non pulvinar neque laoreet suspendisse interdum consectetur libero id. Tellus orci ac auctor augue mauris augue neque gravida in. In egestas erat imperdiet sed euismod. Amet volutpat consequat mauris nunc congue nisi. Massa eget egestas purus viverra accumsan in. Eget duis at tellus at. Mi sit amet mauris commodo quis imperdiet. Nibh mauris cursus mattis molestie a iaculis at.\r\n\r\nNetus et malesuada fames ac turpis. Sit amet dictum sit amet justo donec. Euismod quis viverra nibh cras pulvinar mattis. Sit amet commodo nulla facilisi nullam vehicula ipsum a arcu. Scelerisque felis imperdiet proin fermentum. Ac tincidunt vitae semper quis lectus nulla at. Sit amet commodo nulla facilisi nullam vehicula. Enim lobortis scelerisque fermentum dui faucibus in ornare quam. Quam id leo in vitae turpis massa sed. Quam quisque id diam vel quam. A condimentum vitae sapien pellentesque. Neque aliquam vestibulum morbi blandit cursus risus at. Velit laoreet id donec ultrices tincidunt arcu non sodales neque. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum sociis. Egestas tellus rutrum tellus pellentesque eu tincidunt tortor. Nec feugiat in fermentum posuere. Dui accumsan sit amet nulla. Lacus viverra vitae congue eu consequat ac felis. Etiam tempor orci eu lobortis. Fermentum leo vel orci porta non pulvinar.";
string exampleData = "\r\nUnicode Latin Letter D: \u018A\n--------------------------------------------------------------\r\nSPLITFLAP\r\n--------------------------------------------------------------\r\nMcuShell ; Group of McuShell commands\r\n help|status ; Print help or status information\r\nSplitFlap ; Group of McuRTOS commands\r\n help ; Print help or status information\r\n setId <SetupId> <hwId> ; sets the position (setupId) of the sf and its id (hwId)\r\n initAll ; init all Splitflaps\r\n Display <string> ; displays as many chars of the string as sf are available\r\n addId <hwId> <offset> ; add new hwId with offset\r\nMcuRTOS ; Group of McuRTOS commands\r\n help|status ; Print help or status information\r\n tasklist ; Print tasklist\r\nMcuFlash ; Group of flash ini commands\r\n help|status ; Print help or status information\r\n dump <start> <size> ; Dump memory data\r\n erase <addr> <size> ; Erase memory at address\r\nini ; Group of flash ini commands\r\n help|status ; Print help or status information\r\n dump ; Dump data information\r\n erase ; Erase data information\r\nMcuMinINI ; Group of McuMinINI commands\r\n help|status ; Print help or status information\r\n read <f> <s> <k> ; Read a key from a section in a file\r\n write <f> <s> <k> <v> ; Write a key with value to a section in a file\r\n delkey <f> <s> <k> ; Delete a key in a section of file\r\n delsec <f> <s> ; Delete a section in a file\r\nnvmc ; Group of NVMC commands\r\n help|status ; Print help or status information\r\n flags <val> ; Set flags\r\nrs ; Group of RS-485 commands\r\n help|status ; Print help or status information\r\n addr <addr> ; Set RS-485 address\r\n send <text> ; Send a text to the RS-485 bus\r\n sendcmd <addr> <cmd> ; Send a shell command to the RS-485 address and check response\r\n log on|off ; Log RS-485 bus activity to McuLog\r\nMcuUart485 ; Group of RS-485 commands\r\n help|status ; Print help or status information\r\n clear <flags> ; Clear UART ISR flags\r\n\r\nCMD>";
//string exampleData = "This is some example Text\nSecond line\nNow a very long line: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
int counter = 0;
int lineNumber = 1;
//List<ExtendedChar> listOfChars = new();
foreach (var character in exampleData)
{
if(++counter > 100 || character == '\n')
{
//this.ReceivedData.Add();
//listOfChars = new List<ExtendedChar>();
counter = 0;
lineNumber += 1;
}
var extdChar = new ExtendedChar(character);
//listOfChars.Add(extdChar);
this.ReceivedData.Add(new DataViewModel(extdChar, lineNumber));
}
}
}
~CommunicationDataViewModel()
{
if (this.communicationProtocol != null)
{
this.communicationProtocol.ReceivedDataEvent -= CommunicationProtocol_ReceivedDataEvent;
this.communicationProtocol.SentDataEvent -= CommunicationProtocol_SentDataEvent;
this.communicationProtocol = null;
}
}
/// <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)
{
if (receiveNewlineSeparatorType != null)
{
// 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)
{
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)
{
// add to collection with the current counter, invoking UI context if necssary
var currentLineCounter = collectionLineCounter;
ContextHelpers.InvokeIfNecessary(this.uiContext, (Action)delegate
{
dataCollection.Add(new DataViewModel(newExtdChar, currentLineCounter));
});
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()
{
this.ReceivedData = new ObservableCollection<DataViewModel>();
}
[RelayCommand]
private void ClearSentData()
{
this.SentData = new ObservableCollection<DataViewModel>();
}
}