Fixed #2: Improve connection status and changing servers
This commit is contained in:
parent
d6b9970d51
commit
3d229b5ea8
@ -17,5 +17,11 @@
|
|||||||
Username = username;
|
Username = username;
|
||||||
Password = password;
|
Password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Host}:{Port}{VirtualHost}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
PettingZoo.Core/Connection/DynamicConnection.cs
Normal file
100
PettingZoo.Core/Connection/DynamicConnection.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PettingZoo.Core.Connection
|
||||||
|
{
|
||||||
|
public class DynamicConnection : IConnection
|
||||||
|
{
|
||||||
|
public Guid ConnectionId => currentConnection?.ConnectionId ?? Guid.Empty;
|
||||||
|
public ConnectionParams? ConnectionParams { get; private set; }
|
||||||
|
public ConnectionStatus Status { get; private set; } = ConnectionStatus.Disconnected;
|
||||||
|
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||||
|
|
||||||
|
|
||||||
|
private IConnection? currentConnection;
|
||||||
|
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (currentConnection != null)
|
||||||
|
await currentConnection.DisposeAsync();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
currentConnection.Connect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask Disconnect()
|
||||||
|
{
|
||||||
|
if (currentConnection == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var disconnectedConnectionId = currentConnection.ConnectionId;
|
||||||
|
await currentConnection.DisposeAsync();
|
||||||
|
currentConnection = null;
|
||||||
|
|
||||||
|
ConnectionStatusChanged(this, new StatusChangedEventArgs(disconnectedConnectionId, ConnectionStatus.Disconnected));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetConnection(IConnection connection)
|
||||||
|
{
|
||||||
|
if (currentConnection != null)
|
||||||
|
{
|
||||||
|
currentConnection.StatusChanged -= ConnectionStatusChanged;
|
||||||
|
ConnectionStatusChanged(this, new StatusChangedEventArgs(currentConnection.ConnectionId, ConnectionStatus.Disconnected));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentConnection = connection;
|
||||||
|
|
||||||
|
// Assume we get the new connection before Connect is called, thus before the status changes
|
||||||
|
if (currentConnection != null)
|
||||||
|
currentConnection.StatusChanged += ConnectionStatusChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ISubscriber Subscribe(string exchange, string routingKey)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
return currentConnection.Subscribe(exchange, routingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ISubscriber Subscribe()
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
return currentConnection.Subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task Publish(PublishMessageInfo messageInfo)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
return currentConnection.Publish(messageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ConnectionParams = e.ConnectionParams;
|
||||||
|
Status = e.Status;
|
||||||
|
|
||||||
|
StatusChanged?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(currentConnection))]
|
||||||
|
private void CheckConnection()
|
||||||
|
{
|
||||||
|
if (currentConnection == null)
|
||||||
|
throw new InvalidOperationException("No current connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,15 @@ namespace PettingZoo.Core.Connection
|
|||||||
{
|
{
|
||||||
public interface IConnection : IAsyncDisposable
|
public interface IConnection : IAsyncDisposable
|
||||||
{
|
{
|
||||||
|
Guid ConnectionId { get; }
|
||||||
|
ConnectionParams? ConnectionParams { get; }
|
||||||
|
ConnectionStatus Status { get; }
|
||||||
|
|
||||||
event EventHandler<StatusChangedEventArgs> StatusChanged;
|
event EventHandler<StatusChangedEventArgs> StatusChanged;
|
||||||
|
|
||||||
|
|
||||||
|
void Connect();
|
||||||
|
|
||||||
ISubscriber Subscribe(string exchange, string routingKey);
|
ISubscriber Subscribe(string exchange, string routingKey);
|
||||||
ISubscriber Subscribe();
|
ISubscriber Subscribe();
|
||||||
|
|
||||||
@ -25,14 +32,18 @@ namespace PettingZoo.Core.Connection
|
|||||||
|
|
||||||
public class StatusChangedEventArgs : EventArgs
|
public class StatusChangedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
|
public Guid ConnectionId { get; }
|
||||||
public ConnectionStatus Status { get; }
|
public ConnectionStatus Status { get; }
|
||||||
public string? Context { get; }
|
public ConnectionParams? ConnectionParams { get; }
|
||||||
|
public Exception? Exception { get; }
|
||||||
|
|
||||||
|
|
||||||
public StatusChangedEventArgs(ConnectionStatus status, string? context)
|
public StatusChangedEventArgs(Guid connectionId, ConnectionStatus status, ConnectionParams? connectionParams = null, Exception? exception = null)
|
||||||
{
|
{
|
||||||
|
ConnectionId = connectionId;
|
||||||
Status = status;
|
Status = status;
|
||||||
Context = context;
|
ConnectionParams = connectionParams;
|
||||||
|
Exception = exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,51 +3,56 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PettingZoo.Core.Connection;
|
using PettingZoo.Core.Connection;
|
||||||
using RabbitMQ.Client;
|
using RabbitMQ.Client;
|
||||||
|
using IConnection = RabbitMQ.Client.IConnection;
|
||||||
|
|
||||||
namespace PettingZoo.RabbitMQ
|
namespace PettingZoo.RabbitMQ
|
||||||
{
|
{
|
||||||
public class RabbitMQClientConnection : Core.Connection.IConnection
|
public class RabbitMQClientConnection : Core.Connection.IConnection
|
||||||
{
|
{
|
||||||
|
public Guid ConnectionId { get; } = Guid.NewGuid();
|
||||||
|
public ConnectionParams? ConnectionParams { get; }
|
||||||
|
public ConnectionStatus Status { get; set; }
|
||||||
|
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||||
|
|
||||||
|
|
||||||
private const int ConnectRetryDelay = 5000;
|
private const int ConnectRetryDelay = 5000;
|
||||||
|
|
||||||
private readonly CancellationTokenSource connectionTaskToken = new();
|
private readonly CancellationTokenSource connectionTaskToken = new();
|
||||||
private readonly Task connectionTask;
|
private Task? connectionTask;
|
||||||
private readonly object connectionLock = new();
|
private readonly object connectionLock = new();
|
||||||
private global::RabbitMQ.Client.IConnection? connection;
|
private IConnection? connection;
|
||||||
private IModel? model;
|
|
||||||
|
|
||||||
|
|
||||||
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
|
||||||
|
|
||||||
|
|
||||||
public RabbitMQClientConnection(ConnectionParams connectionParams)
|
public RabbitMQClientConnection(ConnectionParams connectionParams)
|
||||||
{
|
{
|
||||||
connectionTask = Task.Factory.StartNew(() => TryConnection(connectionParams, connectionTaskToken.Token), CancellationToken.None);
|
ConnectionParams = connectionParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
if (connectionTask == null)
|
||||||
|
return;
|
||||||
|
|
||||||
connectionTaskToken.Cancel();
|
connectionTaskToken.Cancel();
|
||||||
if (!connectionTask.IsCompleted)
|
if (!connectionTask.IsCompleted)
|
||||||
await connectionTask;
|
await connectionTask;
|
||||||
|
|
||||||
lock (connectionLock)
|
lock (connectionLock)
|
||||||
{
|
{
|
||||||
if (model != null)
|
|
||||||
{
|
|
||||||
model.Dispose();
|
|
||||||
model = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
{
|
{
|
||||||
connection.Dispose();
|
connection.Dispose();
|
||||||
connection = null;
|
connection = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
connectionTask = Task.Factory.StartNew(() => TryConnection(ConnectionParams!, connectionTaskToken.Token), CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,6 +72,7 @@ namespace PettingZoo.RabbitMQ
|
|||||||
{
|
{
|
||||||
lock (connectionLock)
|
lock (connectionLock)
|
||||||
{
|
{
|
||||||
|
var model = connection?.CreateModel();
|
||||||
var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey);
|
var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey);
|
||||||
if (model != null)
|
if (model != null)
|
||||||
return subscriber;
|
return subscriber;
|
||||||
@ -79,10 +85,10 @@ namespace PettingZoo.RabbitMQ
|
|||||||
|
|
||||||
lock (connectionLock)
|
lock (connectionLock)
|
||||||
{
|
{
|
||||||
if (model == null)
|
if (connection == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
subscriber.Connected(model);
|
subscriber.Connected(connection.CreateModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusChanged -= ConnectSubscriber;
|
StatusChanged -= ConnectSubscriber;
|
||||||
@ -97,12 +103,30 @@ namespace PettingZoo.RabbitMQ
|
|||||||
|
|
||||||
public Task Publish(PublishMessageInfo messageInfo)
|
public Task Publish(PublishMessageInfo messageInfo)
|
||||||
{
|
{
|
||||||
if (model == null)
|
IConnection? lockedConnection;
|
||||||
|
|
||||||
|
lock (connectionLock)
|
||||||
|
{
|
||||||
|
lockedConnection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockedConnection == null)
|
||||||
throw new InvalidOperationException("Not connected");
|
throw new InvalidOperationException("Not connected");
|
||||||
|
|
||||||
model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
|
using (var model = lockedConnection.CreateModel())
|
||||||
RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties, model.CreateBasicProperties()),
|
{
|
||||||
messageInfo.Body);
|
try
|
||||||
|
{
|
||||||
|
model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
|
||||||
|
RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties,
|
||||||
|
model.CreateBasicProperties()),
|
||||||
|
messageInfo.Body);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
model.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@ -119,22 +143,22 @@ namespace PettingZoo.RabbitMQ
|
|||||||
Password = connectionParams.Password
|
Password = connectionParams.Password
|
||||||
};
|
};
|
||||||
|
|
||||||
var statusContext = $"{connectionParams.Host}:{connectionParams.Port}{connectionParams.VirtualHost}";
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
DoStatusChanged(ConnectionStatus.Connecting, statusContext);
|
DoStatusChanged(ConnectionStatus.Connecting);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
connection = factory.CreateConnection();
|
lock (connectionLock)
|
||||||
model = connection.CreateModel();
|
{
|
||||||
|
connection = factory.CreateConnection();
|
||||||
DoStatusChanged(ConnectionStatus.Connected, statusContext);
|
}
|
||||||
|
|
||||||
|
DoStatusChanged(ConnectionStatus.Connected);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
DoStatusChanged(ConnectionStatus.Error, e.Message);
|
DoStatusChanged(ConnectionStatus.Error, e);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -148,9 +172,10 @@ namespace PettingZoo.RabbitMQ
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void DoStatusChanged(ConnectionStatus status, string? context = null)
|
private void DoStatusChanged(ConnectionStatus status, Exception? exception = null)
|
||||||
{
|
{
|
||||||
StatusChanged?.Invoke(this, new StatusChangedEventArgs(status, context));
|
Status = status;
|
||||||
|
StatusChanged?.Invoke(this, new StatusChangedEventArgs(ConnectionId, status, ConnectionParams, exception));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
PettingZoo/UI/Main/MainWindowStrings.Designer.cs
generated
2
PettingZoo/UI/Main/MainWindowStrings.Designer.cs
generated
@ -160,7 +160,7 @@ namespace PettingZoo.UI.Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Connected.
|
/// Looks up a localized string similar to Connected to {0}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string StatusConnected {
|
public static string StatusConnected {
|
||||||
get {
|
get {
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
<value>Importing messages...</value>
|
<value>Importing messages...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="StatusConnected" xml:space="preserve">
|
<data name="StatusConnected" xml:space="preserve">
|
||||||
<value>Connected</value>
|
<value>Connected to {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="StatusConnecting" xml:space="preserve">
|
<data name="StatusConnecting" xml:space="preserve">
|
||||||
<value>Connecting to {0}...</value>
|
<value>Connecting to {0}...</value>
|
||||||
|
@ -43,7 +43,7 @@ namespace PettingZoo.UI.Main
|
|||||||
private readonly IExportImportFormatProvider exportImportFormatProvider;
|
private readonly IExportImportFormatProvider exportImportFormatProvider;
|
||||||
|
|
||||||
private SubscribeDialogParams? subscribeDialogParams;
|
private SubscribeDialogParams? subscribeDialogParams;
|
||||||
private IConnection? connection;
|
private readonly DynamicConnection connection = new();
|
||||||
private string connectionStatus;
|
private string connectionStatus;
|
||||||
private ITab? activeTab;
|
private ITab? activeTab;
|
||||||
private readonly Dictionary<ITab, Window> undockedTabs = new();
|
private readonly Dictionary<ITab, Window> undockedTabs = new();
|
||||||
@ -141,15 +141,15 @@ namespace PettingZoo.UI.Main
|
|||||||
closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute);
|
closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute);
|
||||||
undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute);
|
undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute);
|
||||||
importCommand = new DelegateCommand(ImportExecute);
|
importCommand = new DelegateCommand(ImportExecute);
|
||||||
|
|
||||||
|
connection.StatusChanged += ConnectionStatusChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
await connection.DisposeAsync();
|
||||||
if (connection != null)
|
|
||||||
await connection.DisposeAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -159,13 +159,9 @@ namespace PettingZoo.UI.Main
|
|||||||
if (connectionSettings == null)
|
if (connectionSettings == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (connection != null)
|
connection.SetConnection(connectionFactory.CreateConnection(new ConnectionParams(
|
||||||
await connection.DisposeAsync();
|
|
||||||
|
|
||||||
connection = connectionFactory.CreateConnection(new ConnectionParams(
|
|
||||||
connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port,
|
connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port,
|
||||||
connectionSettings.Username, connectionSettings.Password));
|
connectionSettings.Username, connectionSettings.Password)));
|
||||||
connection.StatusChanged += ConnectionStatusChanged;
|
|
||||||
|
|
||||||
if (connectionSettings.Subscribe)
|
if (connectionSettings.Subscribe)
|
||||||
{
|
{
|
||||||
@ -173,40 +169,22 @@ namespace PettingZoo.UI.Main
|
|||||||
tabFactory.CreateSubscriberTab(connection, subscriber);
|
tabFactory.CreateSubscriberTab(connection, subscriber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection.Connect();
|
||||||
ConnectionChanged();
|
ConnectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void DisconnectExecute()
|
private async void DisconnectExecute()
|
||||||
{
|
{
|
||||||
Tabs.Clear();
|
await connection.Disconnect();
|
||||||
|
|
||||||
var capturedUndockedTabs = undockedTabs.ToList();
|
|
||||||
undockedTabs.Clear();
|
|
||||||
|
|
||||||
foreach (var undockedTab in capturedUndockedTabs)
|
|
||||||
undockedTab.Value.Close();
|
|
||||||
|
|
||||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
|
||||||
undockTabCommand.RaiseCanExecuteChanged();
|
|
||||||
|
|
||||||
if (connection != null)
|
|
||||||
{
|
|
||||||
await connection.DisposeAsync();
|
|
||||||
connection = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionStatus = GetConnectionStatus(null);
|
|
||||||
ConnectionStatusType = ConnectionStatusType.Error;
|
|
||||||
ConnectionChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void SubscribeExecute()
|
private void SubscribeExecute()
|
||||||
{
|
{
|
||||||
if (connection == null)
|
if (connection.Status != Core.Connection.ConnectionStatus.Connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var newParams = subscribeDialog.Show(subscribeDialogParams);
|
var newParams = subscribeDialog.Show(subscribeDialogParams);
|
||||||
if (newParams == null)
|
if (newParams == null)
|
||||||
return;
|
return;
|
||||||
@ -220,16 +198,16 @@ namespace PettingZoo.UI.Main
|
|||||||
|
|
||||||
private void PublishExecute()
|
private void PublishExecute()
|
||||||
{
|
{
|
||||||
if (connection == null)
|
if (connection.Status != Core.Connection.ConnectionStatus.Connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tabFactory.CreatePublisherTab(connection);
|
tabFactory.CreatePublisherTab(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool IsConnectedCanExecute()
|
private bool IsConnectedCanExecute()
|
||||||
{
|
{
|
||||||
return connection != null;
|
return connection.Status == Core.Connection.ConnectionStatus.Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -419,6 +397,8 @@ namespace PettingZoo.UI.Main
|
|||||||
Core.Connection.ConnectionStatus.Connecting => ConnectionStatusType.Connecting,
|
Core.Connection.ConnectionStatus.Connecting => ConnectionStatusType.Connecting,
|
||||||
_ => ConnectionStatusType.Error
|
_ => ConnectionStatusType.Error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(ConnectionChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -427,9 +407,9 @@ namespace PettingZoo.UI.Main
|
|||||||
{
|
{
|
||||||
return args?.Status switch
|
return args?.Status switch
|
||||||
{
|
{
|
||||||
Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.Context),
|
Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.ConnectionParams),
|
||||||
Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.Context),
|
Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.ConnectionParams),
|
||||||
Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Context),
|
Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Exception?.Message),
|
||||||
Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected,
|
Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected,
|
||||||
_ => MainWindowStrings.StatusDisconnected
|
_ => MainWindowStrings.StatusDisconnected
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ namespace PettingZoo.UI.Tab
|
|||||||
{
|
{
|
||||||
public interface ITabFactory
|
public interface ITabFactory
|
||||||
{
|
{
|
||||||
void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber);
|
void CreateSubscriberTab(IConnection connection, ISubscriber subscriber);
|
||||||
string CreateReplySubscriberTab(IConnection connection);
|
string CreateReplySubscriberTab(IConnection connection);
|
||||||
void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null);
|
void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||||
xmlns:valueConverters="clr-namespace:PettingZoo.WPF.ValueConverters;assembly=PettingZoo.WPF"
|
xmlns:valueConverters="clr-namespace:PettingZoo.WPF.ValueConverters;assembly=PettingZoo.WPF"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="1200"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}"
|
d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}"
|
||||||
Background="White">
|
Background="White">
|
||||||
@ -34,6 +34,7 @@
|
|||||||
<RowDefinition Height="16" />
|
<RowDefinition Height="16" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
||||||
@ -81,6 +82,7 @@
|
|||||||
<ContentControl Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 8 0 0" Content="{Binding MessageTypeControl}" />
|
<ContentControl Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 8 0 0" Content="{Binding MessageTypeControl}" />
|
||||||
|
|
||||||
<Button Grid.Row="11" Grid.Column="1" Command="{Binding PublishCommand}" Content="{x:Static res:PublisherViewStrings.CommandPublish}" HorizontalAlignment="Left" />
|
<Button Grid.Row="11" Grid.Column="1" Command="{Binding PublishCommand}" Content="{x:Static res:PublisherViewStrings.CommandPublish}" HorizontalAlignment="Left" />
|
||||||
|
<TextBlock Grid.Row="12" Grid.Column="1" Text="{x:Static res:PublisherViewStrings.Published}" Visibility="{Binding PublishedVisibility}" />
|
||||||
</controls:GridLayout>
|
</controls:GridLayout>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Accessibility;
|
using System.Windows.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using PettingZoo.Core.Connection;
|
using PettingZoo.Core.Connection;
|
||||||
@ -17,11 +17,12 @@ using PettingZoo.Core.Generator;
|
|||||||
using PettingZoo.Core.Macros;
|
using PettingZoo.Core.Macros;
|
||||||
using PettingZoo.Core.Settings;
|
using PettingZoo.Core.Settings;
|
||||||
using PettingZoo.WPF.ViewModel;
|
using PettingZoo.WPF.ViewModel;
|
||||||
|
using Application = System.Windows.Application;
|
||||||
using UserControl = System.Windows.Controls.UserControl;
|
using UserControl = System.Windows.Controls.UserControl;
|
||||||
|
|
||||||
namespace PettingZoo.UI.Tab.Publisher
|
namespace PettingZoo.UI.Tab.Publisher
|
||||||
{
|
{
|
||||||
public class PublisherViewModel : BaseViewModel, ITabToolbarCommands, ITabHostWindowNotify, IPublishDestination
|
public class PublisherViewModel : BaseViewModel, IDisposable, ITabToolbarCommands, ITabHostWindowNotify, IPublishDestination
|
||||||
{
|
{
|
||||||
private readonly IConnection connection;
|
private readonly IConnection connection;
|
||||||
private readonly IExampleGenerator exampleGenerator;
|
private readonly IExampleGenerator exampleGenerator;
|
||||||
@ -197,6 +198,19 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
public ICommand ImportCommand => importCommand;
|
public ICommand ImportCommand => importCommand;
|
||||||
|
|
||||||
|
|
||||||
|
private readonly DispatcherTimer publishedVisibilityTimer = new()
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromSeconds(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
private Visibility publishedVisibility = Visibility.Hidden;
|
||||||
|
public Visibility PublishedVisibility
|
||||||
|
{
|
||||||
|
get => publishedVisibility;
|
||||||
|
set => SetField(ref publishedVisibility, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public string Title => SendToQueue
|
public string Title => SendToQueue
|
||||||
? string.IsNullOrWhiteSpace(Queue) ? PublisherViewStrings.TabTitleEmpty :
|
? string.IsNullOrWhiteSpace(Queue) ? PublisherViewStrings.TabTitleEmpty :
|
||||||
string.Format(PublisherViewStrings.TabTitle, Queue)
|
string.Format(PublisherViewStrings.TabTitle, Queue)
|
||||||
@ -244,17 +258,51 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
|
|
||||||
|
|
||||||
PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
|
PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
|
||||||
|
|
||||||
|
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse - null in design time
|
||||||
|
if (connection != null)
|
||||||
|
connection.StatusChanged += ConnectionStatusChanged;
|
||||||
|
|
||||||
|
publishedVisibilityTimer.Tick += (_, _) =>
|
||||||
|
{
|
||||||
|
PublishedVisibility = Visibility.Hidden;
|
||||||
|
publishedVisibilityTimer.Stop();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
connection.StatusChanged -= ConnectionStatusChanged;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
publishCommand.RaiseCanExecuteChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void PublishExecute()
|
private void PublishExecute()
|
||||||
{
|
{
|
||||||
messageTypePublishCommand?.Execute(null);
|
messageTypePublishCommand?.Execute(null);
|
||||||
|
|
||||||
|
PublishedVisibility = Visibility.Visible;
|
||||||
|
publishedVisibilityTimer.Stop();
|
||||||
|
publishedVisibilityTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool PublishCanExecute()
|
private bool PublishCanExecute()
|
||||||
{
|
{
|
||||||
|
if (connection.Status != ConnectionStatus.Connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (SendToExchange)
|
if (SendToExchange)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Exchange) || string.IsNullOrWhiteSpace(RoutingKey))
|
if (string.IsNullOrWhiteSpace(Exchange) || string.IsNullOrWhiteSpace(RoutingKey))
|
||||||
@ -571,6 +619,8 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
SelectedStoredMessage = StoredMessages[0];
|
SelectedStoredMessage = StoredMessages[0];
|
||||||
ActiveStoredMessage = StoredMessages[1];
|
ActiveStoredMessage = StoredMessages[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PublishedVisibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Visibility ExchangeVisibility => Visibility.Visible;
|
public override Visibility ExchangeVisibility => Visibility.Visible;
|
||||||
|
@ -177,6 +177,15 @@ namespace PettingZoo.UI.Tab.Publisher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Message published.
|
||||||
|
/// </summary>
|
||||||
|
public static string Published {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Published", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Re: .
|
/// Looks up a localized string similar to Re: .
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -156,6 +156,9 @@
|
|||||||
<data name="PanelTitleMessages" xml:space="preserve">
|
<data name="PanelTitleMessages" xml:space="preserve">
|
||||||
<value>Saved messages</value>
|
<value>Saved messages</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Published" xml:space="preserve">
|
||||||
|
<value>Message published</value>
|
||||||
|
</data>
|
||||||
<data name="ReplyToCorrelationIdPrefix" xml:space="preserve">
|
<data name="ReplyToCorrelationIdPrefix" xml:space="preserve">
|
||||||
<value>Re: </value>
|
<value>Re: </value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -37,8 +37,8 @@
|
|||||||
<ListBox.ItemsSource>
|
<ListBox.ItemsSource>
|
||||||
<CompositeCollection>
|
<CompositeCollection>
|
||||||
<CollectionContainer Collection="{Binding Source={StaticResource Messages}}" />
|
<CollectionContainer Collection="{Binding Source={StaticResource Messages}}" />
|
||||||
<ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False">
|
<ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False" Visibility="{Binding UnreadMessagesVisibility}">
|
||||||
<Grid Visibility="{Binding UnreadMessagesVisibility}">
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition SharedSizeGroup="DateTime" />
|
<ColumnDefinition SharedSizeGroup="DateTime" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
@ -51,6 +51,17 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</ListBoxItem>
|
</ListBoxItem>
|
||||||
<CollectionContainer Collection="{Binding Source={StaticResource UnreadMessages}}" />
|
<CollectionContainer Collection="{Binding Source={StaticResource UnreadMessages}}" />
|
||||||
|
<ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False" Visibility="{Binding StatusVisibility}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition SharedSizeGroup="DateTime" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding StatusText}" HorizontalAlignment="Center" Background="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Foreground="{x:Static SystemColors.GrayTextBrush}" />
|
||||||
|
</Grid>
|
||||||
|
</ListBoxItem>
|
||||||
</CompositeCollection>
|
</CompositeCollection>
|
||||||
</ListBox.ItemsSource>
|
</ListBox.ItemsSource>
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
|
@ -25,7 +25,7 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
{
|
{
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
private readonly ITabFactory tabFactory;
|
private readonly ITabFactory tabFactory;
|
||||||
private readonly IConnection? connection;
|
private readonly IConnection connection;
|
||||||
private readonly ISubscriber subscriber;
|
private readonly ISubscriber subscriber;
|
||||||
private readonly IExportImportFormatProvider exportImportFormatProvider;
|
private readonly IExportImportFormatProvider exportImportFormatProvider;
|
||||||
private ReceivedMessageInfo? selectedMessage;
|
private ReceivedMessageInfo? selectedMessage;
|
||||||
@ -106,8 +106,14 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
public Visibility ReplyTabVisibility => IsReplyTab ? Visibility.Visible : Visibility.Collapsed;
|
public Visibility ReplyTabVisibility => IsReplyTab ? Visibility.Visible : Visibility.Collapsed;
|
||||||
// ReSharper restore UnusedMember.Global
|
// ReSharper restore UnusedMember.Global
|
||||||
|
|
||||||
|
private readonly Guid subscribeConnectionId;
|
||||||
|
private ConnectionStatus connectionStatus = ConnectionStatus.Connecting;
|
||||||
|
|
||||||
public SubscriberViewModel(ILogger logger, ITabFactory tabFactory, IConnection? connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider, bool isReplyTab)
|
public Visibility StatusVisibility => connectionStatus is ConnectionStatus.Connecting or ConnectionStatus.Disconnected or ConnectionStatus.Error ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
public string StatusText => connectionStatus == ConnectionStatus.Connecting ? SubscriberViewStrings.StatusConnecting : SubscriberViewStrings.StatusDisconnected;
|
||||||
|
|
||||||
|
|
||||||
|
public SubscriberViewModel(ILogger logger, ITabFactory tabFactory, IConnection connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider, bool isReplyTab)
|
||||||
{
|
{
|
||||||
IsReplyTab = isReplyTab;
|
IsReplyTab = isReplyTab;
|
||||||
|
|
||||||
@ -133,6 +139,8 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
|
|
||||||
createPublisherCommand = new DelegateCommand(CreatePublisherExecute, CreatePublisherCanExecute);
|
createPublisherCommand = new DelegateCommand(CreatePublisherExecute, CreatePublisherCanExecute);
|
||||||
|
|
||||||
|
subscribeConnectionId = connection.ConnectionId;
|
||||||
|
connection.StatusChanged += ConnectionStatusChanged;
|
||||||
subscriber.MessageReceived += SubscriberMessageReceived;
|
subscriber.MessageReceived += SubscriberMessageReceived;
|
||||||
subscriber.Start();
|
subscriber.Start();
|
||||||
}
|
}
|
||||||
@ -141,11 +149,47 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
connection.StatusChanged -= ConnectionStatusChanged;
|
||||||
newMessageTimer?.Dispose();
|
newMessageTimer?.Dispose();
|
||||||
subscriber.Dispose();
|
subscriber.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// If another connection has been made, this does not concern us
|
||||||
|
if (e.ConnectionId != subscribeConnectionId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The subscriber will not reconnect, so after the first disconnect it's over for us
|
||||||
|
if (connectionStatus is ConnectionStatus.Disconnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (e.Status)
|
||||||
|
{
|
||||||
|
case ConnectionStatus.Connecting:
|
||||||
|
case ConnectionStatus.Error:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ConnectionStatus.Connected:
|
||||||
|
connectionStatus = ConnectionStatus.Connected;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConnectionStatus.Disconnected:
|
||||||
|
default:
|
||||||
|
connectionStatus = ConnectionStatus.Disconnected;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
|
||||||
|
RaisePropertyChanged(nameof(StatusVisibility));
|
||||||
|
RaisePropertyChanged(nameof(StatusText));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ClearExecute()
|
private void ClearExecute()
|
||||||
{
|
{
|
||||||
Messages.Clear();
|
Messages.Clear();
|
||||||
@ -254,7 +298,7 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
|
|
||||||
private void CreatePublisherExecute()
|
private void CreatePublisherExecute()
|
||||||
{
|
{
|
||||||
if (connection == null)
|
if (connection.Status != ConnectionStatus.Connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tabFactory.CreatePublisherTab(connection, SelectedMessage);
|
tabFactory.CreatePublisherTab(connection, SelectedMessage);
|
||||||
@ -263,7 +307,7 @@ namespace PettingZoo.UI.Tab.Subscriber
|
|||||||
|
|
||||||
private bool CreatePublisherCanExecute()
|
private bool CreatePublisherCanExecute()
|
||||||
{
|
{
|
||||||
return connection != null && SelectedMessage != null;
|
return SelectedMessage != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,5 +194,23 @@ namespace PettingZoo.UI.Tab.Subscriber {
|
|||||||
return ResourceManager.GetString("ReplyTabTitle", resourceCulture);
|
return ResourceManager.GetString("ReplyTabTitle", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Connecting....
|
||||||
|
/// </summary>
|
||||||
|
public static string StatusConnecting {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("StatusConnecting", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Disconnected.
|
||||||
|
/// </summary>
|
||||||
|
public static string StatusDisconnected {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("StatusDisconnected", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,4 +162,10 @@
|
|||||||
<data name="ReplyTabTitle" xml:space="preserve">
|
<data name="ReplyTabTitle" xml:space="preserve">
|
||||||
<value>Replies</value>
|
<value>Replies</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="StatusConnecting" xml:space="preserve">
|
||||||
|
<value>Connecting...</value>
|
||||||
|
</data>
|
||||||
|
<data name="StatusDisconnected" xml:space="preserve">
|
||||||
|
<value>Disconnected</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -36,7 +36,7 @@ namespace PettingZoo.UI.Tab
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber)
|
public void CreateSubscriberTab(IConnection connection, ISubscriber subscriber)
|
||||||
{
|
{
|
||||||
InternalCreateSubscriberTab(connection, subscriber, false);
|
InternalCreateSubscriberTab(connection, subscriber, false);
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ namespace PettingZoo.UI.Tab
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ITab InternalCreateSubscriberTab(IConnection? connection, ISubscriber subscriber, bool isReplyTab)
|
private ITab InternalCreateSubscriberTab(IConnection connection, ISubscriber subscriber, bool isReplyTab)
|
||||||
{
|
{
|
||||||
var viewModel = new SubscriberViewModel(logger, this, connection, subscriber, exportImportFormatProvider, isReplyTab);
|
var viewModel = new SubscriberViewModel(logger, this, connection, subscriber, exportImportFormatProvider, isReplyTab);
|
||||||
var tab = new ViewTab<SubscriberView, SubscriberViewModel>(
|
var tab = new ViewTab<SubscriberView, SubscriberViewModel>(
|
||||||
|
Loading…
Reference in New Issue
Block a user