mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-19 02:53:06 +08:00
Implement new managed client.
This commit is contained in:
@@ -1,52 +1,56 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IManagedMqttClient : IDisposable
|
||||
{
|
||||
event Func<ApplicationMessageDroppedEventArgs, Task> ApplicationMessageDroppedAsync;
|
||||
|
||||
event Func<EventArgs, Task> ApplicationMessageEnqueueingAsync;
|
||||
|
||||
event Func<ApplicationMessageProcessedEventArgs, Task> ApplicationMessageProcessedAsync;
|
||||
|
||||
|
||||
event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync;
|
||||
|
||||
event Func<ApplicationMessageSkippedEventArgs, Task> ApplicationMessageSkippedAsync;
|
||||
|
||||
|
||||
event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync;
|
||||
|
||||
|
||||
event Func<ConnectingFailedEventArgs, Task> ConnectingFailedAsync;
|
||||
|
||||
event Func<EventArgs, Task> ConnectionStateChangedAsync;
|
||||
|
||||
|
||||
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
||||
|
||||
event Func<ManagedProcessFailedEventArgs, Task> SynchronizingSubscriptionsFailedAsync;
|
||||
|
||||
|
||||
event Func<SubscribeProcessedEventArgs, Task> SubscribeProcessedAsync;
|
||||
|
||||
event Func<UnsubscribeProcessedEventArgs, Task> UnsubscribeProcessedAsync;
|
||||
|
||||
int EnqueuedApplicationMessagesCount { get; }
|
||||
|
||||
IMqttClient InternalClient { get; }
|
||||
|
||||
|
||||
bool IsConnected { get; }
|
||||
|
||||
|
||||
bool IsStarted { get; }
|
||||
|
||||
|
||||
ManagedMqttClientOptions Options { get; }
|
||||
|
||||
int PendingApplicationMessagesCount { get; }
|
||||
|
||||
|
||||
Task EnqueueAsync(MqttApplicationMessage applicationMessage);
|
||||
|
||||
|
||||
Task EnqueueAsync(ManagedMqttApplicationMessage applicationMessage);
|
||||
|
||||
|
||||
Task PingAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
|
||||
Task StartAsync(ManagedMqttClientOptions options);
|
||||
|
||||
Task StopAsync();
|
||||
|
||||
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
||||
|
||||
Task UnsubscribeAsync(ICollection<string> topics);
|
||||
|
||||
Task StopAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
|
||||
|
||||
Task SubscribeAsync(MqttClientSubscribeOptions options);
|
||||
|
||||
Task UnsubscribeAsync(MqttClientUnsubscribeOptions options);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<AssemblyName>MQTTnet.Extensions.ManagedClient</AssemblyName>
|
||||
<RootNamespace>MQTTnet.Extensions.ManagedClient</RootNamespace>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=new/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=new_005Cevents/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=new_005Coptions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -6,10 +6,16 @@ using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttApplicationMessage
|
||||
public sealed class ManagedMqttApplicationMessage
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public ManagedMqttApplicationMessage(string id, MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
Id = id ?? throw new ArgumentNullException(nameof(id));
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||
public MqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,19 @@ using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttApplicationMessageBuilder
|
||||
public sealed class ManagedMqttApplicationMessageBuilder
|
||||
{
|
||||
private Guid _id = Guid.NewGuid();
|
||||
private MqttApplicationMessage _applicationMessage;
|
||||
MqttApplicationMessage _applicationMessage;
|
||||
string _id = Guid.NewGuid().ToString();
|
||||
|
||||
public ManagedMqttApplicationMessageBuilder WithId(Guid id)
|
||||
public ManagedMqttApplicationMessage Build()
|
||||
{
|
||||
_id = id;
|
||||
return this;
|
||||
if (_applicationMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("The ApplicationMessage cannot be null.");
|
||||
}
|
||||
|
||||
return new ManagedMqttApplicationMessage(_id, _applicationMessage);
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessageBuilder WithApplicationMessage(MqttApplicationMessage applicationMessage)
|
||||
@@ -25,7 +29,10 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public ManagedMqttApplicationMessageBuilder WithApplicationMessage(Action<MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
if (builder == null) throw new ArgumentNullException(nameof(builder));
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
var internalBuilder = new MqttApplicationMessageBuilder();
|
||||
builder(internalBuilder);
|
||||
@@ -34,18 +41,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage Build()
|
||||
public ManagedMqttApplicationMessageBuilder WithId(Guid id)
|
||||
{
|
||||
if (_applicationMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("The ApplicationMessage cannot be null.");
|
||||
}
|
||||
_id = id.ToString();
|
||||
return this;
|
||||
}
|
||||
|
||||
return new ManagedMqttApplicationMessage
|
||||
{
|
||||
Id = _id,
|
||||
ApplicationMessage = _applicationMessage
|
||||
};
|
||||
public ManagedMqttApplicationMessageBuilder WithId(string id)
|
||||
{
|
||||
_id = id ?? throw new ArgumentNullException(nameof(id));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,10 @@ using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ManagedMqttClient : Disposable, IManagedMqttClient
|
||||
public sealed class ManagedMqttClient : Disposable
|
||||
{
|
||||
readonly AsyncEvent<ApplicationMessageProcessedEventArgs> _applicationMessageProcessedEvent = new AsyncEvent<ApplicationMessageProcessedEventArgs>();
|
||||
readonly AsyncEvent<ApplicationMessageSkippedEventArgs> _applicationMessageSkippedEvent = new AsyncEvent<ApplicationMessageSkippedEventArgs>();
|
||||
readonly AsyncEvent<ApplicationMessageDroppedEventArgs> _applicationMessageSkippedEvent = new AsyncEvent<ApplicationMessageDroppedEventArgs>();
|
||||
readonly AsyncEvent<ConnectingFailedEventArgs> _connectingFailedEvent = new AsyncEvent<ConnectingFailedEventArgs>();
|
||||
readonly AsyncEvent<EventArgs> _connectionStateChangedEvent = new AsyncEvent<EventArgs>();
|
||||
readonly AsyncEvent<ManagedProcessFailedEventArgs> _synchronizingSubscriptionsFailedEvent = new AsyncEvent<ManagedProcessFailedEventArgs>();
|
||||
@@ -75,7 +75,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_logger = logger.WithSource(nameof(ManagedMqttClient));
|
||||
}
|
||||
|
||||
public event Func<ApplicationMessageSkippedEventArgs, Task> ApplicationMessageSkippedAsync
|
||||
public event Func<ApplicationMessageDroppedEventArgs, Task> ApplicationMessageSkippedAsync
|
||||
{
|
||||
add => _applicationMessageSkippedEvent.AddHandler(value);
|
||||
remove => _applicationMessageSkippedEvent.RemoveHandler(value);
|
||||
@@ -117,12 +117,18 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
remove => InternalClient.DisconnectedAsync -= value;
|
||||
}
|
||||
|
||||
public event Func<ManagedProcessFailedEventArgs, Task> SynchronizingSubscriptionsFailedAsync
|
||||
public event Func<SubscribeProcessedEventArgs, Task> SubscribeProcessedAsync;
|
||||
|
||||
public event Func<UnsubscribeProcessedEventArgs, Task> UnsubscribeProcessedAsync;
|
||||
|
||||
public event Func<ManagedProcessFailedEventArgs, Task> SubscribedAsync
|
||||
{
|
||||
add => _synchronizingSubscriptionsFailedEvent.AddHandler(value);
|
||||
remove => _synchronizingSubscriptionsFailedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> ApplicationMessageEnqueued;
|
||||
|
||||
public IMqttClient InternalClient { get; }
|
||||
|
||||
public bool IsConnected => InternalClient.IsConnected;
|
||||
@@ -163,7 +169,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
||||
|
||||
ManagedMqttApplicationMessage removedMessage = null;
|
||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||
ApplicationMessageDroppedEventArgs applicationMessageDroppedEventArgs = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -174,7 +180,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(applicationMessage);
|
||||
applicationMessageDroppedEventArgs = new ApplicationMessageDroppedEventArgs(applicationMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -182,7 +188,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
removedMessage = _messageQueue.RemoveFirst();
|
||||
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(removedMessage);
|
||||
applicationMessageDroppedEventArgs = new ApplicationMessageDroppedEventArgs(removedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,9 +207,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (applicationMessageSkippedEventArgs != null && _applicationMessageSkippedEvent.HasHandlers)
|
||||
if (applicationMessageDroppedEventArgs != null && _applicationMessageSkippedEvent.HasHandlers)
|
||||
{
|
||||
await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||
await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageDroppedEventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -38,6 +39,32 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return managedMqttClient.EnqueueAsync(applicationMessage);
|
||||
}
|
||||
|
||||
public static Task StopAsync(this IManagedMqttClient managedMqttClient, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (managedMqttClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(managedMqttClient));
|
||||
}
|
||||
|
||||
return managedMqttClient.StopAsync(new MqttClientDisconnectOptions(), cancellationToken);
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedMqttClient, MqttTopicFilter topicFilter)
|
||||
{
|
||||
if (managedMqttClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(managedMqttClient));
|
||||
}
|
||||
|
||||
if (topicFilter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topicFilter));
|
||||
}
|
||||
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topicFilter).Build();
|
||||
return managedMqttClient.SubscribeAsync(subscribeOptions);
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(
|
||||
this IManagedMqttClient managedMqttClient,
|
||||
string topic,
|
||||
@@ -53,11 +80,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
throw new ArgumentNullException(nameof(topic));
|
||||
}
|
||||
|
||||
return managedMqttClient.SubscribeAsync(
|
||||
new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()
|
||||
});
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topic, qualityOfServiceLevel).Build();
|
||||
return managedMqttClient.SubscribeAsync(subscribeOptions);
|
||||
}
|
||||
|
||||
public static Task UnsubscribeAsync(this IManagedMqttClient managedMqttClient, string topic)
|
||||
@@ -72,7 +96,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
throw new ArgumentNullException(nameof(topic));
|
||||
}
|
||||
|
||||
return managedMqttClient.UnsubscribeAsync(new List<string> { topic });
|
||||
var unsubscribeOptions = new MqttClientUnsubscribeOptionsBuilder().WithTopicFilter(topic).Build();
|
||||
return managedMqttClient.UnsubscribeAsync(unsubscribeOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,24 +10,54 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static class MqttFactoryExtensions
|
||||
{
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (mqttClient == null)
|
||||
{
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
return new NewManagedMqttClient(factory.CreateMqttClient(), logger);
|
||||
}
|
||||
|
||||
return new ManagedMqttClient(mqttClient, factory.DefaultLogger);
|
||||
|
||||
return new NewManagedMqttClient(mqttClient, logger);
|
||||
}
|
||||
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (mqttClient == null)
|
||||
{
|
||||
return new NewManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
}
|
||||
|
||||
return new NewManagedMqttClient(mqttClient, factory.DefaultLogger);
|
||||
}
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
return new NewManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||
}
|
||||
|
||||
public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory factory)
|
||||
{
|
||||
return new ManagedMqttClientOptionsBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ApplicationMessagesManager
|
||||
{
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly Queue<ManagedMqttApplicationMessage> _messageQueue = new Queue<ManagedMqttApplicationMessage>();
|
||||
readonly IMqttClient _mqttClient;
|
||||
|
||||
public ApplicationMessagesManager(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
_logger = logger.WithSource<ApplicationMessagesManager>();
|
||||
}
|
||||
|
||||
public AsyncEvent<ApplicationMessageDroppedEventArgs> ApplicationMessageDroppedEvent { get; } = new AsyncEvent<ApplicationMessageDroppedEventArgs>();
|
||||
|
||||
public AsyncEvent<ApplicationMessageEnqueueingEventArgs> ApplicationMessageEnqueueingEvent { get; } = new AsyncEvent<ApplicationMessageEnqueueingEventArgs>();
|
||||
|
||||
public int EnqueuedApplicationMessagesCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
return _messageQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dequeue()
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
_messageQueue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Enqueue(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
// Fire the event first so that the handler can make sure that this message is
|
||||
// properly backed up in a suitable storage.
|
||||
if (ApplicationMessageEnqueueingEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new ApplicationMessageEnqueueingEventArgs(applicationMessage, 0);
|
||||
await ApplicationMessageEnqueueingEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (_messageQueue)
|
||||
{
|
||||
|
||||
// TODO: Drop if too much!
|
||||
|
||||
_messageQueue.Enqueue(applicationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPeek(out ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
if (_messageQueue.Count == 0)
|
||||
{
|
||||
applicationMessage = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
applicationMessage = _messageQueue.Peek();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -6,13 +6,13 @@ using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ApplicationMessageSkippedEventArgs : EventArgs
|
||||
public sealed class ApplicationMessageDroppedEventArgs : EventArgs
|
||||
{
|
||||
public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
|
||||
public ApplicationMessageDroppedEventArgs(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ApplicationMessageEnqueueingEventArgs : EventArgs
|
||||
{
|
||||
public ApplicationMessageEnqueueingEventArgs(ManagedMqttApplicationMessage applicationMessage, ushort packetIdentifier)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
PacketIdentifier = packetIdentifier;
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
|
||||
public ushort PacketIdentifier { get; }
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -15,10 +15,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Then this is _null_ the message was processed successfully without any error.
|
||||
/// Gets or sets the exception which was thrown while sending the message.
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
|
||||
public bool HasFailed => Exception != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
|
||||
@@ -12,7 +16,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is null when the connection was failing and the server was not reachable.
|
||||
/// This is null when the server was not reachable and thus no MQTT response was transmitted.
|
||||
/// </summary>
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class SubscribeProcessedEventArgs : EventArgs
|
||||
{
|
||||
public SubscribeProcessedEventArgs(MqttClientSubscribeOptions options, MqttClientSubscribeResultCode result, IReadOnlyCollection<MqttUserProperty> userProperties)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
Result = result;
|
||||
UserProperties = userProperties;
|
||||
}
|
||||
|
||||
public MqttClientSubscribeOptions Options { get; }
|
||||
|
||||
public MqttClientSubscribeResultCode Result { get; }
|
||||
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class UnsubscribeProcessedEventArgs : EventArgs
|
||||
{
|
||||
public UnsubscribeProcessedEventArgs(MqttClientUnsubscribeOptions options, MqttClientUnsubscribeResultCode result, IReadOnlyCollection<MqttUserProperty> userProperties)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
Result = result;
|
||||
UserProperties = userProperties;
|
||||
}
|
||||
|
||||
public MqttClientUnsubscribeOptions Options { get; }
|
||||
|
||||
public MqttClientUnsubscribeResultCode Result { get; }
|
||||
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public enum MaintainConnectionResult
|
||||
{
|
||||
NoChange,
|
||||
|
||||
ConnectedEstablished,
|
||||
|
||||
NotConnected
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Client;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ManagedMqttSubscription
|
||||
{
|
||||
public ManagedMqttSubscription(MqttClientSubscribeOptions subscribeOptions)
|
||||
{
|
||||
SubscribeOptions = subscribeOptions ?? throw new ArgumentNullException(nameof(subscribeOptions));
|
||||
|
||||
Topic = subscribeOptions.TopicFilters.First().Topic;
|
||||
}
|
||||
|
||||
public bool IsObsolete => UnsubscribeOptions != null;
|
||||
|
||||
public bool IsPending { get; set; } = true;
|
||||
|
||||
public MqttClientSubscribeOptions SubscribeOptions { get; }
|
||||
|
||||
public string Topic { get; }
|
||||
|
||||
public MqttClientUnsubscribeOptions UnsubscribeOptions { get; private set; }
|
||||
|
||||
public void MarkAsUnsubscribed(MqttClientUnsubscribeOptions options)
|
||||
{
|
||||
UnsubscribeOptions = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ManagedMqttSubscriptionsManager
|
||||
{
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttClient _mqttClient;
|
||||
|
||||
// Do not use a dictionary in order to preserve subscription ordering (retained messages).
|
||||
readonly List<ManagedMqttSubscription> _subscriptions = new List<ManagedMqttSubscription>();
|
||||
|
||||
public ManagedMqttSubscriptionsManager(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
_logger = logger.WithSource<ManagedMqttSubscriptionsManager>();
|
||||
}
|
||||
|
||||
public AsyncEvent<SubscribeProcessedEventArgs> SubscribeProcessedEvent { get; } = new AsyncEvent<SubscribeProcessedEventArgs>();
|
||||
|
||||
public AsyncEvent<UnsubscribeProcessedEventArgs> UnsubscribeProcessedEvent { get; } = new AsyncEvent<UnsubscribeProcessedEventArgs>();
|
||||
|
||||
public void ResubscribeAll()
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
foreach (var subscription in _subscriptions)
|
||||
{
|
||||
subscription.IsPending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(MqttClientSubscribeOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
if (options.TopicFilters.Count > 1)
|
||||
{
|
||||
throw new NotSupportedException("The managed client only supports subscribing to one topic at the same time.");
|
||||
}
|
||||
|
||||
var topic = options.TopicFilters.First().Topic;
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(topic);
|
||||
|
||||
_subscriptions.RemoveAll(s => s.Topic.Equals(topic));
|
||||
_subscriptions.Add(new ManagedMqttSubscription(options));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Synchronize(CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: Remember real changes.
|
||||
|
||||
List<ManagedMqttSubscription> pendingSubscriptions;
|
||||
List<ManagedMqttSubscription> obsoleteSubscriptions;
|
||||
lock (_subscriptions)
|
||||
{
|
||||
pendingSubscriptions = _subscriptions.Where(s => s.IsPending).ToList();
|
||||
obsoleteSubscriptions = _subscriptions.Where(s => s.IsObsolete).ToList();
|
||||
}
|
||||
|
||||
if (pendingSubscriptions.Count == 0 && obsoleteSubscriptions.Count == 0)
|
||||
{
|
||||
_logger.Verbose("No subscriptions to synchronize");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var subscription in pendingSubscriptions)
|
||||
{
|
||||
var subscribeResult = await _mqttClient.SubscribeAsync(subscription.SubscribeOptions, cancellationToken).ConfigureAwait(false);
|
||||
subscription.IsPending = false;
|
||||
await FireSubscribeProcessedEvent(subscription, subscribeResult).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
foreach (var subscription in obsoleteSubscriptions)
|
||||
{
|
||||
var unsubscribeResult = await _mqttClient.UnsubscribeAsync(subscription.UnsubscribeOptions, cancellationToken).ConfigureAwait(false);
|
||||
lock (_subscriptions)
|
||||
{
|
||||
_subscriptions.Remove(subscription);
|
||||
}
|
||||
|
||||
await FireUnsubscribeProcessedEvent(subscription, unsubscribeResult).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(MqttClientUnsubscribeOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
var topic = options.TopicFilters.First();
|
||||
var existingSubscription = _subscriptions.FirstOrDefault(s => s.Topic.Equals(topic));
|
||||
|
||||
if (existingSubscription != null)
|
||||
{
|
||||
existingSubscription.MarkAsUnsubscribed(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task FireSubscribeProcessedEvent(ManagedMqttSubscription subscription, MqttClientSubscribeResult subscribeResult)
|
||||
{
|
||||
_logger.Info("Subscribed topic '{0}'.", subscription.Topic);
|
||||
|
||||
if (SubscribeProcessedEvent.HasHandlers)
|
||||
{
|
||||
var resultCode = subscribeResult?.Items.First().ResultCode ?? MqttClientSubscribeResultCode.UnspecifiedError;
|
||||
var eventArgs = new SubscribeProcessedEventArgs(subscription.SubscribeOptions, resultCode, subscribeResult?.UserProperties);
|
||||
await SubscribeProcessedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task FireUnsubscribeProcessedEvent(ManagedMqttSubscription subscription, MqttClientUnsubscribeResult unsubscribeResult)
|
||||
{
|
||||
_logger.Info("Unsubscribed topic '{0}'.", subscription.Topic);
|
||||
|
||||
if (UnsubscribeProcessedEvent.HasHandlers)
|
||||
{
|
||||
var resultCode = unsubscribeResult?.Items.First().ResultCode ?? MqttClientUnsubscribeResultCode.UnspecifiedError;
|
||||
var eventArgs = new UnsubscribeProcessedEventArgs(subscription.UnsubscribeOptions, resultCode, unsubscribeResult?.UserProperties);
|
||||
await UnsubscribeProcessedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class MqttConnectionManager
|
||||
{
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttClient _mqttClient;
|
||||
|
||||
bool _wasConnected;
|
||||
|
||||
public MqttConnectionManager(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
_logger = logger.WithSource<MqttConnectionManager>();
|
||||
|
||||
_mqttClient.DisconnectedAsync += OnClientDisconnected;
|
||||
}
|
||||
|
||||
public AsyncEvent<MqttClientConnectedEventArgs> ConnectedEvent { get; } = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
|
||||
public AsyncEvent<ConnectingFailedEventArgs> ConnectingFailedEvent { get; } = new AsyncEvent<ConnectingFailedEventArgs>();
|
||||
|
||||
public AsyncEvent<MqttClientDisconnectedEventArgs> DisconnectedEvent { get; } = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
|
||||
public async Task<MaintainConnectionResult> MaintainConnection(ManagedMqttClientOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (_wasConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
// The only valid way for testing the connection is to send a PING. So we start with this
|
||||
// in order to check if reconnecting etc. is require or not.
|
||||
await _mqttClient.PingAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// The client is properly connection. No further action required.
|
||||
return MaintainConnectionResult.NoChange;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while checking connection.");
|
||||
}
|
||||
}
|
||||
|
||||
MqttClientConnectResult connectResult = null;
|
||||
try
|
||||
{
|
||||
// The PING was not successful so that there is no working connection. So we connect.
|
||||
connectResult = await _mqttClient.ConnectAsync(options.ClientOptions, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
_wasConnected = true;
|
||||
_logger.Info("Connected.");
|
||||
|
||||
if (ConnectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
await ConnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return MaintainConnectionResult.ConnectedEstablished;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (!(exception is OperationCanceledException))
|
||||
{
|
||||
_logger.Error(exception, "Error while connecting.");
|
||||
}
|
||||
|
||||
if (ConnectingFailedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new ConnectingFailedEventArgs(connectResult, exception);
|
||||
await ConnectingFailedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
_wasConnected = false;
|
||||
return MaintainConnectionResult.NotConnected;
|
||||
}
|
||||
|
||||
Task OnClientDisconnected(MqttClientDisconnectedEventArgs arg)
|
||||
{
|
||||
_wasConnected = false;
|
||||
return DisconnectedEvent?.InvokeAsync(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class NewManagedMqttClient : Disposable, IManagedMqttClient
|
||||
{
|
||||
readonly AsyncEvent<ApplicationMessageProcessedEventArgs> _applicationMessageProcessedEvent = new AsyncEvent<ApplicationMessageProcessedEventArgs>();
|
||||
readonly ApplicationMessagesManager _applicationMessagesManager;
|
||||
readonly MqttConnectionManager _connectionManager;
|
||||
readonly AsyncEvent<EventArgs> _isConnectedChangedEvent = new AsyncEvent<EventArgs>();
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly ManagedMqttSubscriptionsManager _subscriptionsManager;
|
||||
|
||||
readonly ManualResetEventSlim _workSignal = new ManualResetEventSlim();
|
||||
|
||||
CancellationTokenSource _workerCancellationToken;
|
||||
Task _workerTask;
|
||||
|
||||
public NewManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||
{
|
||||
InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
InternalClient.DisconnectedAsync += OnInternalClientDisconnected;
|
||||
|
||||
_logger = logger.WithSource(nameof(NewManagedMqttClient));
|
||||
|
||||
_connectionManager = new MqttConnectionManager(mqttClient, logger);
|
||||
_subscriptionsManager = new ManagedMqttSubscriptionsManager(mqttClient, logger);
|
||||
_applicationMessagesManager = new ApplicationMessagesManager(mqttClient, logger);
|
||||
|
||||
// TODO: Start timer for periodical connection checking.
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> ApplicationMessageEnqueueingAsync
|
||||
{
|
||||
add => _applicationMessagesManager.ApplicationMessageEnqueueingEvent.AddHandler(value);
|
||||
remove => _applicationMessagesManager.ApplicationMessageEnqueueingEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ApplicationMessageProcessedEventArgs, Task> ApplicationMessageProcessedAsync
|
||||
{
|
||||
add => _applicationMessageProcessedEvent.AddHandler(value);
|
||||
remove => _applicationMessageProcessedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
||||
{
|
||||
add => InternalClient.ApplicationMessageReceivedAsync += value;
|
||||
remove => InternalClient.ApplicationMessageReceivedAsync += value;
|
||||
}
|
||||
|
||||
public event Func<ApplicationMessageDroppedEventArgs, Task> ApplicationMessageDroppedAsync
|
||||
{
|
||||
add => _applicationMessagesManager.ApplicationMessageDroppedEvent.AddHandler(value);
|
||||
remove => _applicationMessagesManager.ApplicationMessageDroppedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
||||
{
|
||||
add => _connectionManager.ConnectedEvent.AddHandler(value);
|
||||
remove => _connectionManager.ConnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<ConnectingFailedEventArgs, Task> ConnectingFailedAsync
|
||||
{
|
||||
add => _connectionManager.ConnectingFailedEvent.AddHandler(value);
|
||||
remove => _connectionManager.ConnectingFailedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
||||
{
|
||||
add => _connectionManager.DisconnectedEvent.AddHandler(value);
|
||||
remove => _connectionManager.DisconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<SubscribeProcessedEventArgs, Task> SubscribeProcessedAsync
|
||||
{
|
||||
add => _subscriptionsManager.SubscribeProcessedEvent.AddHandler(value);
|
||||
remove => _subscriptionsManager.SubscribeProcessedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<UnsubscribeProcessedEventArgs, Task> UnsubscribeProcessedAsync
|
||||
{
|
||||
add => _subscriptionsManager.UnsubscribeProcessedEvent.AddHandler(value);
|
||||
remove => _subscriptionsManager.UnsubscribeProcessedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public IMqttClient InternalClient { get; }
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
public bool IsStarted { get; private set; }
|
||||
|
||||
public ManagedMqttClientOptions Options { get; private set; }
|
||||
|
||||
public int EnqueuedApplicationMessagesCount => _applicationMessagesManager.EnqueuedApplicationMessagesCount;
|
||||
|
||||
public Task EnqueueAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
return EnqueueAsync(new ManagedMqttApplicationMessage(Guid.NewGuid().ToString(), applicationMessage));
|
||||
}
|
||||
|
||||
public async Task EnqueueAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
await _applicationMessagesManager.Enqueue(applicationMessage).ConfigureAwait(false);
|
||||
_workSignal.Set();
|
||||
}
|
||||
|
||||
public Task PingAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return InternalClient.PingAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StartAsync(ManagedMqttClientOptions options)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
_workerCancellationToken = new CancellationTokenSource();
|
||||
|
||||
_workerTask = Task.Factory.StartNew(
|
||||
() => WorkerLoop(_workerCancellationToken.Token),
|
||||
_workerCancellationToken.Token,
|
||||
TaskCreationOptions.PreferFairness,
|
||||
TaskScheduler.Default);
|
||||
|
||||
_workSignal.Set();
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
// TODO: Send proper Disconnect signal.
|
||||
|
||||
try
|
||||
{
|
||||
await InternalClient.DisconnectAsync(options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting.");
|
||||
}
|
||||
|
||||
_workerCancellationToken?.Cancel(false);
|
||||
_workerCancellationToken?.Dispose();
|
||||
|
||||
_workerCancellationToken = null;
|
||||
|
||||
if (_workerTask != null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(MqttClientSubscribeOptions options)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_subscriptionsManager.Subscribe(options);
|
||||
_workSignal.Set();
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UnsubscribeAsync(MqttClientUnsubscribeOptions options)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
_subscriptionsManager.Unsubscribe(options);
|
||||
_workSignal.Set();
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Task OnInternalClientDisconnected(MqttClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
_workSignal.Set();
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
}
|
||||
|
||||
async Task UpdateConnectionStatus(MaintainConnectionResult maintainConnectionResult)
|
||||
{
|
||||
var isConnected = maintainConnectionResult != MaintainConnectionResult.NotConnected;
|
||||
|
||||
if (IsConnected.Equals(isConnected))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsConnected = isConnected;
|
||||
|
||||
if (_isConnectedChangedEvent.HasHandlers)
|
||||
{
|
||||
await _isConnectedChangedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task WorkerLoop(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsStarted = true;
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
_workSignal.Wait(cancellationToken);
|
||||
_workSignal.Reset();
|
||||
|
||||
var maintainConnectionResult = await _connectionManager.MaintainConnection(Options, cancellationToken).ConfigureAwait(false);
|
||||
await UpdateConnectionStatus(maintainConnectionResult).ConfigureAwait(false);
|
||||
|
||||
if (maintainConnectionResult == MaintainConnectionResult.ConnectedEstablished && Options.ClientOptions.CleanSession)
|
||||
{
|
||||
// Since the session is clean the server will now know any subscription so that
|
||||
// we have to subscribe to all of them again.
|
||||
_subscriptionsManager.ResubscribeAll();
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
await _subscriptionsManager.Synchronize(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
// TODO: Move into pending messages manager + locking.
|
||||
while (_applicationMessagesManager.TryPeek(out var nextPendingMessage))
|
||||
{
|
||||
Exception processingException = null;
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Publishing managed application message {0}.", nextPendingMessage.Id);
|
||||
|
||||
await InternalClient.PublishAsync(nextPendingMessage.ApplicationMessage, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// The message was delivered successfully. So we can get rid of it.
|
||||
_applicationMessagesManager.Dequeue();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
processingException = exception;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_applicationMessageProcessedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new ApplicationMessageProcessedEventArgs(nextPendingMessage, processingException);
|
||||
await _applicationMessageProcessedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
#if !NETSTANDARD1_3
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while maintaining connection.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsStarted = false;
|
||||
|
||||
await UpdateConnectionStatus(MaintainConnectionResult.NotConnected).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// TODO: Disconnected!
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -10,6 +10,15 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public sealed class ManagedMqttClientOptions
|
||||
{
|
||||
public ManagedMqttClientOptions()
|
||||
{
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptions(MqttClientOptions clientOptions)
|
||||
{
|
||||
ClientOptions = clientOptions;
|
||||
}
|
||||
|
||||
public MqttClientOptions ClientOptions { get; set; }
|
||||
|
||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||
+1
-1
@@ -8,7 +8,7 @@ using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttClientOptionsBuilder
|
||||
public sealed class ManagedMqttClientOptionsBuilder
|
||||
{
|
||||
readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
|
||||
MqttClientOptionsBuilder _clientOptionsBuilder;
|
||||
Reference in New Issue
Block a user