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.
236 lines
8.7 KiB
236 lines
8.7 KiB
using Common;
|
|
using Common.AppSettings;
|
|
using Common.Helpers;
|
|
using Common.Messaging;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
using MultiTerm.Core.Types;
|
|
using MultiTerm.Protocols;
|
|
using MultiTerm.Protocols.Types;
|
|
using System.Text;
|
|
|
|
namespace MultiTerm.Core.ViewModel;
|
|
|
|
public abstract partial class TerminalViewModel : ObservableObject, ITerminalViewModel
|
|
{
|
|
private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator";
|
|
private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator";
|
|
private readonly IAppSettingsProvider appSettings;
|
|
private readonly IMessenger messenger;
|
|
private readonly IContext context;
|
|
|
|
public abstract TerminalViewType ViewType { get; }
|
|
|
|
private ProtocolType protocolType;
|
|
|
|
public ProtocolType ProtocolType
|
|
{
|
|
get { return this.protocolType; }
|
|
set
|
|
{
|
|
this.protocolType = value;
|
|
this.UpdateTitle();
|
|
}
|
|
}
|
|
|
|
#region Title
|
|
/// <summary>
|
|
/// Terminal title, to allow user to distinguish between different terminals.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private string title = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Update the Property <see cref="Title"/>. To be called when it was changed.
|
|
/// </summary>
|
|
private void UpdateTitle()
|
|
{
|
|
if(this.CommunicationProtocol == null)
|
|
{
|
|
this.Title = $"{ProtocolType}";
|
|
}
|
|
else
|
|
{
|
|
this.Title = $"{ProtocolType} {this.CommunicationProtocol?.InstanceIdentifier}";
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Holds communication data, meaning data that was sent to or received over the communication protocol.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private CommunicationDataViewModel? communicationData;
|
|
|
|
/// <summary>
|
|
/// Newline Separator Type that is selected for receival data of this Terminal. Defaults to none.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private NewlineSeparatorType receiveNewlineSeparatorType = NewlineSeparatorType.None;
|
|
|
|
/// <summary>
|
|
/// Newline Separator Type that is selected for sent out data of this Terminal. Defaults to none.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private NewlineSeparatorType sendNewlineSeparatorType = NewlineSeparatorType.None;
|
|
|
|
private ICommunicationProtocol? communicationProtocol;
|
|
public ICommunicationProtocol? CommunicationProtocol
|
|
{
|
|
get { return this.communicationProtocol; }
|
|
set
|
|
{
|
|
// store communication protocol
|
|
this.communicationProtocol = value;
|
|
|
|
// register communication protocol in the Communication Data View Model
|
|
this.CommunicationData = new CommunicationDataViewModel(this.communicationProtocol, this.context);
|
|
|
|
// initializes default newline separators, requires communicationData to be not null
|
|
this.InitializeNewlineSeparatorsFromAppSettings();
|
|
|
|
// initialize both newline Separators. initializes with null if they are not available.
|
|
this.CommunicationData.ConfigureNewlineSeparators(this.ReceiveNewlineSeparatorType, this.SendNewlineSeparatorType);
|
|
}
|
|
}
|
|
|
|
private IProtocolSettingsViewModel? protocolSettings;
|
|
public IProtocolSettingsViewModel? ProtocolSettings
|
|
{
|
|
get { return this.protocolSettings; }
|
|
set
|
|
{
|
|
this.protocolSettings = value;
|
|
// register event handler for connection request from viewmodel
|
|
if(value != null)
|
|
{
|
|
this.protocolSettings!.ConnectRequested += OnViewModelRequestedConnect;
|
|
this.protocolSettings!.DisconnectRequested += OnViewModelRequestedDisconnect;
|
|
}
|
|
}
|
|
}
|
|
|
|
public TerminalViewModel(IAppSettingsProvider appSettings, IMessenger messenger, IContext context)
|
|
{
|
|
this.appSettings = appSettings;
|
|
this.messenger = messenger;
|
|
this.context = context;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends the given string to this instances <see cref="CommunicationProtocol"/>, UTF-16 encoded.
|
|
/// Appends configured <see cref="SendNewlineSeparatorType"/> to the end of string.
|
|
/// </summary>
|
|
/// <param name="data">data string</param>
|
|
protected void SendToCommunicationProtocol(string data)
|
|
{
|
|
// guard communication protocol null
|
|
if(this.CommunicationProtocol == null) { throw new NullReferenceException($"'{nameof(SendToCommunicationProtocol)}()' was called but {nameof(CommunicationProtocol)} is null."); }
|
|
|
|
// inform user and quit if communication protocol is not connected
|
|
if (this.CommunicationProtocol.IsConnected == false)
|
|
{
|
|
this.messenger.Send<IUserInterfaceMessage>(new ProtocolNotConnectedMessage("Cannot send message"));
|
|
return;
|
|
}
|
|
|
|
// add newline sequence to end of data
|
|
string newlineSequence = this.SendNewlineSeparatorType switch
|
|
{
|
|
NewlineSeparatorType.None => String.Empty,
|
|
NewlineSeparatorType.CR => "\r",
|
|
NewlineSeparatorType.LF => "\n",
|
|
NewlineSeparatorType.CR_LF => "\r\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);
|
|
|
|
// send
|
|
this.CommunicationProtocol.SendBytes(byteArray);
|
|
}
|
|
|
|
#region Connect/Disconnect
|
|
private void OnViewModelRequestedConnect(object? sender, ConnectionRequestEventArgs e)
|
|
{
|
|
// guard uninitialized CommunicationProtocol
|
|
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); }
|
|
|
|
e.Success = this.CommunicationProtocol.Connect(e.Settings);
|
|
this.UpdateTitle();
|
|
}
|
|
|
|
private void OnViewModelRequestedDisconnect(object? sender, EventArgs e)
|
|
{
|
|
// guard uninitialized CommunicationProtocol
|
|
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); }
|
|
|
|
this.CommunicationProtocol.Disconnect();
|
|
}
|
|
#endregion
|
|
|
|
#region Closing handling
|
|
public event EventHandler? ClosingEvent;
|
|
|
|
/// <summary>
|
|
/// Method to override if any closing actions are required.
|
|
/// Closing can be cancelled using the return value.
|
|
/// </summary>
|
|
/// <returns>true if closing is allowed. false if it shall be cancelled.</returns>
|
|
protected virtual bool ClosingActions() { return true; }
|
|
|
|
[RelayCommand]
|
|
public void CloseRequest()
|
|
{
|
|
if (this.ClosingActions() == true)
|
|
{
|
|
ClosingEvent?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Newline Separator handling
|
|
/// <summary>
|
|
/// Gets the default settings for both newline separators from the AppSettings Provider.
|
|
/// Writes them to the local variables <see cref="ReceiveNewlineSeparatorType"/> and <see cref="SendNewlineSeparatorType"/>.
|
|
/// In case there are no default settings, it does not overwrite the local variables.
|
|
/// In case the Communication Data is null, nothing is done.
|
|
/// </summary>
|
|
private void InitializeNewlineSeparatorsFromAppSettings()
|
|
{
|
|
// guard null objects
|
|
if(this.CommunicationData == null) { return; }
|
|
|
|
// get newline separators from persistent settings, if not available catch exceptions
|
|
this.appSettings.TryReadSetting(defaultReceiveNewlineSeparatorAppSettingsKey, out string settingValueReceiveNLSep);
|
|
try
|
|
{
|
|
this.ReceiveNewlineSeparatorType = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueReceiveNLSep);
|
|
}
|
|
catch { }
|
|
this.appSettings.TryReadSetting(defaultSendNewlineSeparatorAppSettingsKey, out string settingValueSendNLSep);
|
|
try
|
|
{
|
|
this.SendNewlineSeparatorType = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueSendNLSep);
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
partial void OnReceiveNewlineSeparatorTypeChanged(NewlineSeparatorType value)
|
|
{
|
|
// triggers rearranging the received data with the new NewlineSeparatorType
|
|
this.CommunicationData?.ConfigureNewlineSeparators(value, null);
|
|
}
|
|
|
|
partial void OnSendNewlineSeparatorTypeChanged(NewlineSeparatorType value)
|
|
{
|
|
// triggers rearranging the sent data with the new NewlineSeparatorType
|
|
this.CommunicationData?.ConfigureNewlineSeparators(null, value);
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|