diff --git a/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj b/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj
new file mode 100644
index 0000000..bd81288
--- /dev/null
+++ b/PettingZoo.Benchmark/PettingZoo.Benchmark.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/PettingZoo.Benchmark/Program.cs b/PettingZoo.Benchmark/Program.cs
new file mode 100644
index 0000000..7d27337
--- /dev/null
+++ b/PettingZoo.Benchmark/Program.cs
@@ -0,0 +1,138 @@
+using System.Text;
+using System.Text.Json;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Running;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PettingZoo.Benchmark
+{
+ /*
+
+
+ Small JSON
+
+
+ | Method | Mean | Error | StdDev | Gen 0 | Allocated |
+ |----------------- |----------:|----------:|----------:|-------:|----------:|
+ | TestJsonConvert | 13.226 us | 0.1808 us | 0.1603 us | 3.6316 | 15 KB |
+ | TestJTokenParse | 12.360 us | 0.2453 us | 0.5010 us | 3.6011 | 15 KB |
+ | TestReaderWriter | 6.398 us | 0.1260 us | 0.1294 us | 2.0294 | 8 KB |
+ | TestJsonDocument | 4.400 us | 0.0758 us | 0.0902 us | 2.1019 | 9 KB |
+
+
+
+ Large JSON (25 MB)
+
+ | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
+ |----------------- |-----------:|---------:|---------:|-----------:|-----------:|----------:|----------:|
+ | TestJsonConvert | 1,331.6 ms | 20.36 ms | 19.05 ms | 57000.0000 | 21000.0000 | 3000.0000 | 390 MB |
+ | TestJTokenParse | 1,411.0 ms | 27.28 ms | 24.18 ms | 62000.0000 | 23000.0000 | 4000.0000 | 415 MB |
+ | TestReaderWriter | 298.6 ms | 5.89 ms | 9.34 ms | 25000.0000 | 8000.0000 | 2000.0000 | 199 MB |
+ | TestJsonDocument | 278.5 ms | 5.29 ms | 6.30 ms | - | - | - | 246 MB |
+
+
+ */
+ [MemoryDiagnoser]
+ public class JsonPrettyPrint
+ {
+ // Small Json file, which is likely to be typical for most RabbitMQ messages.
+ private const string Json = "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\",\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup Language\",\"Acronym\":\"SGML\",\"Abbrev\":\"ISO 8879:1986\",\"GlossDef\":{\"para\":\"A meta-markup language, used to create markup languages such as DocBook.\",\"GlossSeeAlso\":[\"GML\",\"XML\"]},\"GlossSee\":\"markup\"}}}}}";
+
+ // To test with a large file instead, specify the file name here.
+ // For example, I've benchmarked it with this 25 MB JSON file: https://raw.githubusercontent.com/json-iterator/test-data/master/large-file.json
+ //private const string JsonFilename = "";
+ private const string JsonFilename = "D:\\Temp\\large-file.json";
+
+
+ private readonly string testJson;
+
+
+ public JsonPrettyPrint()
+ {
+ testJson = string.IsNullOrEmpty(JsonFilename)
+ ? Json
+ : File.ReadAllText(JsonFilename);
+ }
+
+
+ [Benchmark]
+ public string TestJsonConvert()
+ {
+ var obj = JsonConvert.DeserializeObject(testJson);
+ return JsonConvert.SerializeObject(obj, Formatting.Indented);
+ }
+
+
+ [Benchmark]
+ public string TestJTokenParse()
+ {
+ var obj = JToken.Parse(testJson);
+ return obj.ToString(Formatting.Indented);
+ }
+
+
+ [Benchmark]
+ public string TestReaderWriter()
+ {
+ using var stringReader = new StringReader(testJson);
+ using var jsonTextReader = new JsonTextReader(stringReader);
+ using var stringWriter = new StringWriter();
+ using var jsonWriter = new JsonTextWriter(stringWriter);
+
+ jsonWriter.Formatting = Formatting.Indented;
+
+ while (jsonTextReader.Read())
+ jsonWriter.WriteToken(jsonTextReader);
+
+ jsonWriter.Flush();
+ return stringWriter.ToString();
+ }
+
+
+ [Benchmark]
+ public string TestJsonDocument()
+ {
+ var doc = JsonDocument.Parse(testJson);
+
+ using var stream = new MemoryStream();
+ var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
+ doc.WriteTo(writer);
+ writer.Flush();
+
+ return Encoding.UTF8.GetString(stream.ToArray());
+ }
+
+
+
+ }
+
+
+ public class Program
+ {
+ public static void Main()
+ {
+ BenchmarkRunner.Run();
+
+ // To prove they all provide the correct output
+ /*
+ var prettyPrint = new JsonPrettyPrint();
+ Console.WriteLine("JsonConvert");
+ Console.WriteLine("-----------");
+ Console.WriteLine(prettyPrint.TestJsonConvert());
+
+ Console.WriteLine("JToken");
+ Console.WriteLine("------");
+ Console.WriteLine(prettyPrint.TestJTokenParse());
+
+ Console.WriteLine("ReaderWriter");
+ Console.WriteLine("------------");
+ Console.WriteLine(prettyPrint.TestReaderWriter());
+
+ Console.WriteLine("JsonDocument");
+ Console.WriteLine("------------");
+ Console.WriteLine(prettyPrint.TestJsonDocument());
+ */
+ }
+ }
+}
\ No newline at end of file
diff --git a/PettingZoo.Core/Connection/ConnectionParams.cs b/PettingZoo.Core/Connection/ConnectionParams.cs
index fb63f5e..44e55e1 100644
--- a/PettingZoo.Core/Connection/ConnectionParams.cs
+++ b/PettingZoo.Core/Connection/ConnectionParams.cs
@@ -17,5 +17,11 @@
Username = username;
Password = password;
}
+
+
+ public override string ToString()
+ {
+ return $"{Host}:{Port}{VirtualHost}";
+ }
}
}
diff --git a/PettingZoo.Core/Connection/DynamicConnection.cs b/PettingZoo.Core/Connection/DynamicConnection.cs
new file mode 100644
index 0000000..ba9043b
--- /dev/null
+++ b/PettingZoo.Core/Connection/DynamicConnection.cs
@@ -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? 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");
+ }
+ }
+}
diff --git a/PettingZoo.Core/Connection/IConnection.cs b/PettingZoo.Core/Connection/IConnection.cs
index f4ac798..6f49fd1 100644
--- a/PettingZoo.Core/Connection/IConnection.cs
+++ b/PettingZoo.Core/Connection/IConnection.cs
@@ -5,8 +5,15 @@ namespace PettingZoo.Core.Connection
{
public interface IConnection : IAsyncDisposable
{
+ Guid ConnectionId { get; }
+ ConnectionParams? ConnectionParams { get; }
+ ConnectionStatus Status { get; }
+
event EventHandler StatusChanged;
+
+ void Connect();
+
ISubscriber Subscribe(string exchange, string routingKey);
ISubscriber Subscribe();
@@ -25,14 +32,18 @@ namespace PettingZoo.Core.Connection
public class StatusChangedEventArgs : EventArgs
{
+ public Guid ConnectionId { 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;
- Context = context;
+ ConnectionParams = connectionParams;
+ Exception = exception;
}
}
}
diff --git a/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs b/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs
new file mode 100644
index 0000000..9c930cd
--- /dev/null
+++ b/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using PettingZoo.Core.Connection;
+
+namespace PettingZoo.Core.ExportImport.Publisher
+{
+ public enum PublisherMessageType
+ {
+ Raw,
+ Tapeti
+ }
+
+
+
+ public class PublisherMessage : IEquatable
+ {
+ public PublisherMessageType MessageType { get; init; }
+ public bool SendToExchange { get; init; }
+ public string? Exchange { get; init; }
+ public string? RoutingKey { get; init; }
+ public string? Queue { get; init; }
+ public bool ReplyToNewSubscriber { get; init; }
+ public string? ReplyTo { get; init; }
+
+ public RawPublisherMessage? RawPublisherMessage { get; init; }
+ public TapetiPublisherMessage? TapetiPublisherMessage { get; init; }
+
+
+ public bool Equals(PublisherMessage? other)
+ {
+ if (other == null) return false;
+ if (ReferenceEquals(this, other)) return true;
+
+ return MessageType == other.MessageType &&
+ SendToExchange == other.SendToExchange &&
+ Exchange == other.Exchange &&
+ RoutingKey == other.RoutingKey &&
+ Queue == other.Queue &&
+ ReplyToNewSubscriber == other.ReplyToNewSubscriber &&
+ ReplyTo == other.ReplyTo &&
+ Equals(RawPublisherMessage, other.RawPublisherMessage) &&
+ Equals(TapetiPublisherMessage, other.TapetiPublisherMessage);
+ }
+
+
+ public override bool Equals(object? obj)
+ {
+ if (obj == null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+
+ return obj is PublisherMessage publisherMessage && Equals(publisherMessage);
+ }
+
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add((int)MessageType);
+ hashCode.Add(SendToExchange);
+ hashCode.Add(Exchange);
+ hashCode.Add(RoutingKey);
+ hashCode.Add(Queue);
+ hashCode.Add(ReplyToNewSubscriber);
+ hashCode.Add(ReplyTo);
+ hashCode.Add(RawPublisherMessage);
+ hashCode.Add(TapetiPublisherMessage);
+ return hashCode.ToHashCode();
+ }
+ }
+
+
+ public class RawPublisherMessage : IEquatable
+ {
+ public MessageDeliveryMode DeliveryMode { get; init; }
+
+ public string? ContentType { get; init; }
+ public string? CorrelationId { get; init; }
+ public string? AppId { get; init; }
+ public string? ContentEncoding { get; init; }
+ public string? Expiration { get; init; }
+ public string? MessageId { get; init; }
+ public string? Priority { get; init; }
+ public string? Timestamp { get; init; }
+ public string? TypeProperty { get; init; }
+ public string? UserId { get; init; }
+ public string? Payload { get; init; }
+ public bool EnableMacros { get; init; }
+
+ public Dictionary? Headers { get; init; }
+
+
+ public bool Equals(RawPublisherMessage? other)
+ {
+ if (other == null) return false;
+ if (ReferenceEquals(this, other)) return true;
+
+ return DeliveryMode == other.DeliveryMode &&
+ ContentType == other.ContentType &&
+ CorrelationId == other.CorrelationId &&
+ AppId == other.AppId &&
+ ContentEncoding == other.ContentEncoding &&
+ Expiration == other.Expiration &&
+ MessageId == other.MessageId &&
+ Priority == other.Priority &&
+ Timestamp == other.Timestamp &&
+ TypeProperty == other.TypeProperty &&
+ UserId == other.UserId &&
+ Payload == other.Payload &&
+ EnableMacros == other.EnableMacros &&
+ HeadersEquals(other.Headers);
+ }
+
+
+ private bool HeadersEquals(Dictionary? other)
+ {
+ if (other == null)
+ return Headers == null || Headers.Count == 0;
+
+ if (Headers == null)
+ return other.Count == 0;
+
+ return other.OrderBy(h => h.Key).SequenceEqual(Headers.OrderBy(h => h.Key));
+ }
+
+
+ public override bool Equals(object? obj)
+ {
+ if (obj == null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+
+ return obj is RawPublisherMessage rawPublisherMessage && Equals(rawPublisherMessage);
+ }
+
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add((int)DeliveryMode);
+ hashCode.Add(ContentType);
+ hashCode.Add(CorrelationId);
+ hashCode.Add(AppId);
+ hashCode.Add(ContentEncoding);
+ hashCode.Add(Expiration);
+ hashCode.Add(MessageId);
+ hashCode.Add(Priority);
+ hashCode.Add(Timestamp);
+ hashCode.Add(TypeProperty);
+ hashCode.Add(UserId);
+ hashCode.Add(Payload);
+ hashCode.Add(EnableMacros);
+
+ if (Headers != null)
+ foreach (var (key, value) in Headers)
+ {
+ hashCode.Add(key);
+ hashCode.Add(value);
+ }
+
+ return hashCode.ToHashCode();
+ }
+ }
+
+
+ public class TapetiPublisherMessage : IEquatable
+ {
+ public string? CorrelationId { get; init; }
+ public string? Payload { get; init; }
+ public bool EnableMacros { get; init; }
+ public string? ClassName { get; init; }
+ public string? AssemblyName { get; init; }
+
+
+ public bool Equals(TapetiPublisherMessage? other)
+ {
+ if (other == null) return false;
+ if (ReferenceEquals(this, other)) return true;
+
+ return CorrelationId == other.CorrelationId &&
+ Payload == other.Payload &&
+ EnableMacros == other.EnableMacros &&
+ ClassName == other.ClassName &&
+ AssemblyName == other.AssemblyName;
+ }
+
+
+ public override bool Equals(object? obj)
+ {
+ if (obj == null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+
+ return obj is TapetiPublisherMessage tapetiPublisherMessage && Equals(tapetiPublisherMessage);
+ }
+
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(CorrelationId, Payload, EnableMacros, ClassName, AssemblyName);
+ }
+ }
+}
diff --git a/PettingZoo.Core/ExportImport/BaseProgressDecorator.cs b/PettingZoo.Core/ExportImport/Subscriber/BaseProgressDecorator.cs
similarity index 95%
rename from PettingZoo.Core/ExportImport/BaseProgressDecorator.cs
rename to PettingZoo.Core/ExportImport/Subscriber/BaseProgressDecorator.cs
index 8e02cc4..89dcf3f 100644
--- a/PettingZoo.Core/ExportImport/BaseProgressDecorator.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/BaseProgressDecorator.cs
@@ -1,6 +1,6 @@
using System;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public abstract class BaseProgressDecorator
{
diff --git a/PettingZoo.Core/ExportImport/ExportImportFormatProvider.cs b/PettingZoo.Core/ExportImport/Subscriber/ExportImportFormatProvider.cs
similarity index 93%
rename from PettingZoo.Core/ExportImport/ExportImportFormatProvider.cs
rename to PettingZoo.Core/ExportImport/Subscriber/ExportImportFormatProvider.cs
index 1482bf2..190a18c 100644
--- a/PettingZoo.Core/ExportImport/ExportImportFormatProvider.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/ExportImportFormatProvider.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Linq;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public class ExportImportFormatProvider : IExportImportFormatProvider
{
diff --git a/PettingZoo.Core/ExportImport/IExportImportFormat.cs b/PettingZoo.Core/ExportImport/Subscriber/IExportImportFormat.cs
similarity index 92%
rename from PettingZoo.Core/ExportImport/IExportImportFormat.cs
rename to PettingZoo.Core/ExportImport/Subscriber/IExportImportFormat.cs
index e586348..d7b9c94 100644
--- a/PettingZoo.Core/ExportImport/IExportImportFormat.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/IExportImportFormat.cs
@@ -4,7 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using PettingZoo.Core.Connection;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public interface IExportImportFormat
{
diff --git a/PettingZoo.Core/ExportImport/IExportImportFormatProvider.cs b/PettingZoo.Core/ExportImport/Subscriber/IExportImportFormatProvider.cs
similarity index 82%
rename from PettingZoo.Core/ExportImport/IExportImportFormatProvider.cs
rename to PettingZoo.Core/ExportImport/Subscriber/IExportImportFormatProvider.cs
index 1991668..55ab49a 100644
--- a/PettingZoo.Core/ExportImport/IExportImportFormatProvider.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/IExportImportFormatProvider.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public interface IExportImportFormatProvider
{
diff --git a/PettingZoo.Core/ExportImport/ImportSubscriber.cs b/PettingZoo.Core/ExportImport/Subscriber/ImportSubscriber.cs
similarity index 95%
rename from PettingZoo.Core/ExportImport/ImportSubscriber.cs
rename to PettingZoo.Core/ExportImport/Subscriber/ImportSubscriber.cs
index 49ffaca..dbfc831 100644
--- a/PettingZoo.Core/ExportImport/ImportSubscriber.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/ImportSubscriber.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using PettingZoo.Core.Connection;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public class ImportSubscriber : ISubscriber
{
diff --git a/PettingZoo.Core/ExportImport/ListEnumerableProgressDecorator.cs b/PettingZoo.Core/ExportImport/Subscriber/ListEnumerableProgressDecorator.cs
similarity index 98%
rename from PettingZoo.Core/ExportImport/ListEnumerableProgressDecorator.cs
rename to PettingZoo.Core/ExportImport/Subscriber/ListEnumerableProgressDecorator.cs
index d74425a..4c63f66 100644
--- a/PettingZoo.Core/ExportImport/ListEnumerableProgressDecorator.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/ListEnumerableProgressDecorator.cs
@@ -2,7 +2,7 @@
using System.Collections;
using System.Collections.Generic;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public class ListEnumerableProgressDecorator : BaseProgressDecorator, IEnumerable
{
diff --git a/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs b/PettingZoo.Core/ExportImport/Subscriber/StreamProgressDecorator.cs
similarity index 99%
rename from PettingZoo.Core/ExportImport/StreamProgressDecorator.cs
rename to PettingZoo.Core/ExportImport/Subscriber/StreamProgressDecorator.cs
index b2abeaa..16eebe8 100644
--- a/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs
+++ b/PettingZoo.Core/ExportImport/Subscriber/StreamProgressDecorator.cs
@@ -3,7 +3,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace PettingZoo.Core.ExportImport
+namespace PettingZoo.Core.ExportImport.Subscriber
{
public class StreamProgressDecorator : BaseProgressDecorator
{
diff --git a/PettingZoo.Core/PettingZoo.Core.csproj b/PettingZoo.Core/PettingZoo.Core.csproj
index 6302265..5d0aac6 100644
--- a/PettingZoo.Core/PettingZoo.Core.csproj
+++ b/PettingZoo.Core/PettingZoo.Core.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs
index 4482da1..f184fa0 100644
--- a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs
+++ b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Text;
using Newtonsoft.Json;
@@ -26,8 +27,18 @@ namespace PettingZoo.Core.Rendering
var bodyText = Encoding.UTF8.GetString(body);
try
{
- var obj = JsonConvert.DeserializeObject(bodyText);
- return JsonConvert.SerializeObject(obj, Formatting.Indented);
+ using var stringReader = new StringReader(bodyText);
+ using var jsonTextReader = new JsonTextReader(stringReader);
+ using var stringWriter = new StringWriter();
+ using var jsonWriter = new JsonTextWriter(stringWriter);
+
+ jsonWriter.Formatting = Formatting.Indented;
+
+ while (jsonTextReader.Read())
+ jsonWriter.WriteToken(jsonTextReader);
+
+ jsonWriter.Flush();
+ return stringWriter.ToString();
}
catch
{
diff --git a/PettingZoo.Core/Settings/IPublisherMessagesRepository.cs b/PettingZoo.Core/Settings/IPublisherMessagesRepository.cs
new file mode 100644
index 0000000..51d438c
--- /dev/null
+++ b/PettingZoo.Core/Settings/IPublisherMessagesRepository.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using PettingZoo.Core.ExportImport.Publisher;
+
+namespace PettingZoo.Core.Settings
+{
+ public interface IPublisherMessagesRepository
+ {
+ // For now read everything into memory, you need quite a few and/or huge messsages before that becomes an issue
+ Task> GetStored();
+
+ Task Add(string displayName, PublisherMessage message);
+ Task Update(Guid id, string displayName, PublisherMessage message);
+ Task Delete(Guid id);
+ }
+
+
+ public class StoredPublisherMessage
+ {
+ public Guid Id { get; }
+ public string DisplayName { get; }
+ public PublisherMessage Message { get; }
+
+
+ public StoredPublisherMessage(Guid id, string displayName, PublisherMessage message)
+ {
+ Id = id;
+ DisplayName = displayName;
+ Message = message;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj b/PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj
index 45d6771..ad52b6a 100644
--- a/PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj
+++ b/PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
diff --git a/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs b/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs
index 9f790c8..1db1574 100644
--- a/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs
+++ b/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs
@@ -3,51 +3,56 @@ using System.Threading;
using System.Threading.Tasks;
using PettingZoo.Core.Connection;
using RabbitMQ.Client;
+using IConnection = RabbitMQ.Client.IConnection;
namespace PettingZoo.RabbitMQ
{
public class RabbitMQClientConnection : Core.Connection.IConnection
{
+ public Guid ConnectionId { get; } = Guid.NewGuid();
+ public ConnectionParams? ConnectionParams { get; }
+ public ConnectionStatus Status { get; set; }
+ public event EventHandler? StatusChanged;
+
+
private const int ConnectRetryDelay = 5000;
private readonly CancellationTokenSource connectionTaskToken = new();
- private readonly Task connectionTask;
+ private Task? connectionTask;
private readonly object connectionLock = new();
- private global::RabbitMQ.Client.IConnection? connection;
- private IModel? model;
+ private IConnection? connection;
- public event EventHandler? StatusChanged;
-
-
public RabbitMQClientConnection(ConnectionParams connectionParams)
{
- connectionTask = Task.Factory.StartNew(() => TryConnection(connectionParams, connectionTaskToken.Token), CancellationToken.None);
+ ConnectionParams = connectionParams;
}
public async ValueTask DisposeAsync()
{
+ GC.SuppressFinalize(this);
+ if (connectionTask == null)
+ return;
+
connectionTaskToken.Cancel();
if (!connectionTask.IsCompleted)
await connectionTask;
lock (connectionLock)
{
- if (model != null)
- {
- model.Dispose();
- model = null;
- }
-
if (connection != null)
{
connection.Dispose();
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)
{
+ var model = connection?.CreateModel();
var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey);
if (model != null)
return subscriber;
@@ -79,10 +85,10 @@ namespace PettingZoo.RabbitMQ
lock (connectionLock)
{
- if (model == null)
+ if (connection == null)
return;
- subscriber.Connected(model);
+ subscriber.Connected(connection.CreateModel());
}
StatusChanged -= ConnectSubscriber;
@@ -97,12 +103,30 @@ namespace PettingZoo.RabbitMQ
public Task Publish(PublishMessageInfo messageInfo)
{
- if (model == null)
+ IConnection? lockedConnection;
+
+ lock (connectionLock)
+ {
+ lockedConnection = connection;
+ }
+
+ if (lockedConnection == null)
throw new InvalidOperationException("Not connected");
- model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
- RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties, model.CreateBasicProperties()),
- messageInfo.Body);
+ using (var model = lockedConnection.CreateModel())
+ {
+ try
+ {
+ model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
+ RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties,
+ model.CreateBasicProperties()),
+ messageInfo.Body);
+ }
+ finally
+ {
+ model.Close();
+ }
+ }
return Task.CompletedTask;
}
@@ -119,22 +143,22 @@ namespace PettingZoo.RabbitMQ
Password = connectionParams.Password
};
- var statusContext = $"{connectionParams.Host}:{connectionParams.Port}{connectionParams.VirtualHost}";
-
while (!cancellationToken.IsCancellationRequested)
{
- DoStatusChanged(ConnectionStatus.Connecting, statusContext);
+ DoStatusChanged(ConnectionStatus.Connecting);
try
{
- connection = factory.CreateConnection();
- model = connection.CreateModel();
-
- DoStatusChanged(ConnectionStatus.Connected, statusContext);
+ lock (connectionLock)
+ {
+ connection = factory.CreateConnection();
+ }
+
+ DoStatusChanged(ConnectionStatus.Connected);
break;
}
catch (Exception e)
{
- DoStatusChanged(ConnectionStatus.Error, e.Message);
+ DoStatusChanged(ConnectionStatus.Error, e);
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));
}
}
}
diff --git a/PettingZoo.Settings.LiteDB/LiteDBPublisherMessagesRepository.cs b/PettingZoo.Settings.LiteDB/LiteDBPublisherMessagesRepository.cs
new file mode 100644
index 0000000..81c766e
--- /dev/null
+++ b/PettingZoo.Settings.LiteDB/LiteDBPublisherMessagesRepository.cs
@@ -0,0 +1,80 @@
+using PettingZoo.Core.ExportImport.Publisher;
+using PettingZoo.Core.Settings;
+
+namespace PettingZoo.Settings.LiteDB
+{
+ public class LiteDBPublisherMessagesRepository : BaseLiteDBRepository, IPublisherMessagesRepository
+ {
+ private const string CollectionMessages = "messages";
+
+
+ public LiteDBPublisherMessagesRepository() : base(@"publisherMessages")
+ {
+ }
+
+
+ public async Task> GetStored()
+ {
+ using var database = GetDatabase();
+ var collection = database.GetCollection(CollectionMessages);
+
+ return (await collection.FindAllAsync())
+ .Select(r => new StoredPublisherMessage(r.Id, r.DisplayName, r.Message))
+ .ToArray();
+ }
+
+
+ public async Task Add(string displayName, PublisherMessage message)
+ {
+ using var database = GetDatabase();
+ var collection = database.GetCollection(CollectionMessages);
+
+ var id = Guid.NewGuid();
+ await collection.InsertAsync(PublisherMessageRecord.FromPublisherMessage(id, displayName, message));
+
+ return new StoredPublisherMessage(id, displayName, message);
+ }
+
+
+ public async Task Update(Guid id, string displayName, PublisherMessage message)
+ {
+ using var database = GetDatabase();
+ var collection = database.GetCollection(CollectionMessages);
+
+ await collection.UpdateAsync(PublisherMessageRecord.FromPublisherMessage(id, displayName, message));
+ return new StoredPublisherMessage(id, displayName, message);
+ }
+
+
+ public async Task Delete(Guid id)
+ {
+ using var database = GetDatabase();
+ var collection = database.GetCollection(CollectionMessages);
+
+ await collection.DeleteAsync(id);
+ }
+
+
+ // ReSharper disable MemberCanBePrivate.Local - for LiteDB
+ // ReSharper disable PropertyCanBeMadeInitOnly.Local
+ private class PublisherMessageRecord
+ {
+ public Guid Id { get; set; }
+ public string DisplayName { get; set; } = null!;
+ public PublisherMessage Message { get; set; } = null!;
+
+
+ public static PublisherMessageRecord FromPublisherMessage(Guid id, string displayName, PublisherMessage message)
+ {
+ return new PublisherMessageRecord
+ {
+ Id = id,
+ DisplayName = displayName,
+ Message = message
+ };
+ }
+ }
+ // ReSharper restore PropertyCanBeMadeInitOnly.Local
+ // ReSharper restore MemberCanBePrivate.Local
+ }
+}
\ No newline at end of file
diff --git a/PettingZoo.Settings.LiteDB/PettingZoo.Settings.LiteDB.csproj b/PettingZoo.Settings.LiteDB/PettingZoo.Settings.LiteDB.csproj
index f5fef95..e7d3208 100644
--- a/PettingZoo.Settings.LiteDB/PettingZoo.Settings.LiteDB.csproj
+++ b/PettingZoo.Settings.LiteDB/PettingZoo.Settings.LiteDB.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
diff --git a/PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs b/PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs
similarity index 93%
rename from PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs
rename to PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs
index 6bfd721..cd54b2c 100644
--- a/PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs
+++ b/PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
-using PettingZoo.Core.ExportImport;
+using PettingZoo.Core.ExportImport.Subscriber;
-namespace PettingZoo.Tapeti.Export
+namespace PettingZoo.Tapeti.ExportImport
{
public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat
{
diff --git a/PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs
similarity index 97%
rename from PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs
rename to PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs
index 7967827..fdce801 100644
--- a/PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs
+++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs
@@ -8,10 +8,9 @@ using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PettingZoo.Core.Connection;
-using PettingZoo.Core.ExportImport;
+using PettingZoo.Core.ExportImport.Subscriber;
-
-namespace PettingZoo.Tapeti.Export
+namespace PettingZoo.Tapeti.ExportImport
{
public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat
{
diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs
similarity index 95%
rename from PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs
rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs
index a9d3bf5..38103e4 100644
--- a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs
+++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs
@@ -8,7 +8,7 @@
//
//------------------------------------------------------------------------------
-namespace PettingZoo.Tapeti.Export {
+namespace PettingZoo.Tapeti.ExportImport {
using System;
@@ -39,7 +39,7 @@ namespace PettingZoo.Tapeti.Export {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.Export.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.ExportImport.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.resx b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.resx
similarity index 100%
rename from PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.resx
rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.resx
diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs
similarity index 97%
rename from PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs
rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs
index 58f88d4..c5af72e 100644
--- a/PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs
+++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs
@@ -7,9 +7,9 @@ using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PettingZoo.Core.Connection;
-using PettingZoo.Core.ExportImport;
+using PettingZoo.Core.ExportImport.Subscriber;
-namespace PettingZoo.Tapeti.Export
+namespace PettingZoo.Tapeti.ExportImport
{
public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat
{
diff --git a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj
index 68c879d..c2fc81f 100644
--- a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj
+++ b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj
@@ -8,18 +8,19 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -33,7 +34,7 @@
True
AssemblyParserStrings.resx
-
+
True
True
TapetiCmdImportExportStrings.resx
@@ -60,7 +61,7 @@
ResXFileCodeGenerator
AssemblyParserStrings.Designer.cs
-
+
ResXFileCodeGenerator
TapetiCmdImportExportStrings.Designer.cs
diff --git a/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj b/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj
deleted file mode 100644
index 9e25216..0000000
--- a/PettingZoo.Tapeti/PettingZoo.Tapeti_ooj5vuwa_wpftmp.csproj
+++ /dev/null
@@ -1,325 +0,0 @@
-
-
- PettingZoo.Tapeti
- obj\Debug\
- obj\
- P:\Development\PettingZoo\PettingZoo.Tapeti\obj\
- <_TargetAssemblyProjectName>PettingZoo.Tapeti
-
-
-
- net6.0-windows
- 0.1
- true
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- AssemblyParserStrings.resx
-
-
- True
- True
- TapetiCmdImportExportStrings.resx
-
-
- True
- True
- TapetiClassLibraryExampleGeneratorStrings.resx
-
-
- True
- True
- ClassSelectionStrings.resx
-
-
- True
- True
- PackageSelectionStrings.resx
-
-
-
-
- ResXFileCodeGenerator
- AssemblyParserStrings.Designer.cs
-
-
- ResXFileCodeGenerator
- TapetiCmdImportExportStrings.Designer.cs
-
-
- ResXFileCodeGenerator
- TapetiClassLibraryExampleGeneratorStrings.Designer.cs
-
-
- PublicResXFileCodeGenerator
- ClassSelectionStrings.Designer.cs
-
-
- PublicResXFileCodeGenerator
- PackageSelectionStrings.Designer.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/PettingZoo.Test/PettingZoo.Test.csproj b/PettingZoo.Test/PettingZoo.Test.csproj
index cf3907a..69bda7a 100644
--- a/PettingZoo.Test/PettingZoo.Test.csproj
+++ b/PettingZoo.Test/PettingZoo.Test.csproj
@@ -7,11 +7,11 @@
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs b/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs
new file mode 100644
index 0000000..2d531ec
--- /dev/null
+++ b/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs
@@ -0,0 +1,26 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PettingZoo.WPF.Controls
+{
+ public class AutoOverflowToolBar : ToolBar
+ {
+ public AutoOverflowToolBar()
+ {
+ Loaded += (_, _) =>
+ {
+ // Hide overflow arrow on the right side of the toolbar when not required
+ if (Template.FindName("OverflowButton", this) is not FrameworkElement overflowButton)
+ return;
+
+ if (!overflowButton.IsEnabled)
+ overflowButton.Visibility = Visibility.Hidden;
+
+ overflowButton.IsEnabledChanged += (_, _) =>
+ {
+ overflowButton.Visibility = overflowButton.IsEnabled ? Visibility.Visible : Visibility.Hidden;
+ };
+ };
+ }
+ }
+}
diff --git a/PettingZoo.WPF/PettingZoo.WPF.csproj b/PettingZoo.WPF/PettingZoo.WPF.csproj
index 3adc030..ef051e4 100644
--- a/PettingZoo.WPF/PettingZoo.WPF.csproj
+++ b/PettingZoo.WPF/PettingZoo.WPF.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/PettingZoo.WPF/ValueConverters/SameReferenceConverter.cs b/PettingZoo.WPF/ValueConverters/SameReferenceConverter.cs
new file mode 100644
index 0000000..ea0ff70
--- /dev/null
+++ b/PettingZoo.WPF/ValueConverters/SameReferenceConverter.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace PettingZoo.WPF.ValueConverters
+{
+ public class SameReferenceConverter : IMultiValueConverter
+ {
+ public object Convert(object?[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ return ReferenceEquals(values[0], values[1]);
+ }
+
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/PettingZoo.sln b/PettingZoo.sln
index e840afc..e606ce1 100644
--- a/PettingZoo.sln
+++ b/PettingZoo.sln
@@ -20,7 +20,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Settings.LiteDB"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Benchmark", "PettingZoo.Benchmark\PettingZoo.Benchmark.csproj", "{C25BC83A-D302-46D2-97F6-5F888B824E2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -56,6 +58,10 @@ Global
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/PettingZoo/Images/Delete.svg b/PettingZoo/Images/Delete.svg
new file mode 100644
index 0000000..557bc3c
--- /dev/null
+++ b/PettingZoo/Images/Delete.svg
@@ -0,0 +1,22 @@
+
+
+
diff --git a/PettingZoo/Images/Publish.svg b/PettingZoo/Images/Publish.svg
index 1702435..67cd318 100644
--- a/PettingZoo/Images/Publish.svg
+++ b/PettingZoo/Images/Publish.svg
@@ -1,32 +1,53 @@
-
-
-