/// <summary> /// Creates a new subscription for the specified topic. /// </summary> /// <typeparam name="T">The type of data the subscription is expected to return.</typeparam> /// <typeparam name="TPayloadConverter">The type of the converter that can convert from bytes to the type T.</typeparam> /// <param name="topic">The topic to subscribe to.</param> /// <param name="qos">The QOS level to subscribe at.</param> /// <returns>An observable that yields messages when they arrive.</returns> private IObservable <MqttReceivedMessage <T> > CreateNewSubscription <T, TPayloadConverter>(string topic, MqttQos qos) where TPayloadConverter : IPayloadConverter <T>, new() { Log.Info(m => m("Creating subscription for topic {0} @ QOS {1}.", topic, qos)); // Get an ID that represents the subscription. We will use this same ID for unsubscribe as well. var msgId = messageIdentifierDispenser.GetNextMessageIdentifier("subscriptions"); // create a new observable that is used to yield messages // that arrive for the topic. var observable = CreateObservableForSubscription(topic, msgId); var sub = new Subscription { Topic = topic, Qos = qos, MessageIdentifier = msgId, CreatedTime = DateTime.Now, Observable = observable, }; pendingSubscriptions.Add(sub.MessageIdentifier, sub); // build a subscribe message for the caller and send it off to the broker. var msg = new MqttSubscribeMessage().WithMessageIdentifier(sub.MessageIdentifier) .ToTopic(sub.Topic) .AtQos(sub.Qos); connectionHandler.SendMessage(msg); return(WrapSubscriptionObservable <T, TPayloadConverter>(topic, sub.Observable)); }
/// <summary> /// Pings the message broker if there has been no activity for the specified amount of idle time. /// </summary> /// <param name="state"></param> private void PingRequired(object state) { // if we can't get the montor then the connection has been / is currently being disposed, so // we don't want to do a ping (the connection handler might no longer be valid) if (Monitor.TryEnter(shutdownPadlock)) { try { var pingMsg = new MqttPingRequestMessage(); connectionHandler.SendMessage(pingMsg); } finally { Monitor.Exit(shutdownPadlock); } } }
/// <summary> /// Publish a message to the broker on the specified topic. /// </summary> /// <param name="topic">The topic to send the message to.</param> /// <param name="qualityOfService">The QOS to use when publishing the message.</param> /// <param name="data">The message to send.</param> /// <returns>The message identifier assigned to the message.</returns> public short Publish<T, TPayloadConverter>(string topic, MqttQos qualityOfService, T data) where TPayloadConverter : IPayloadConverter<T>, new() { var msgId = messageIdentifierDispenser.GetNextMessageIdentifier(String.Format("Topic:{0}", topic)); Log.DebugFormat("Publishing message ID {0} on topic {1} using QOS {2}", msgId, topic, qualityOfService); var converter = GetPayloadConverter<TPayloadConverter>(); var msg = new MqttPublishMessage() .ToTopic(topic) .WithMessageIdentifier(msgId) .WithQos(qualityOfService) .PublishData(converter.ConvertToBytes(data)); // QOS level 1 or 2 messages need to be saved so we can do the ack processes if (qualityOfService == MqttQos.AtLeastOnce || qualityOfService == MqttQos.ExactlyOnce) { publishedMessages.Add(msgId, msg); } connectionHandler.SendMessage(msg); return msgId; }
/// <summary> /// Publish a message to the broker on the specified topic. /// </summary> /// <param name="topic">The topic to send the message to.</param> /// <param name="payload">The message to send.</param> /// <returns>The message identifier assigned to the message.</returns> public short Publish <TDataConverter>(string topic, MqttQos qualityOfService, object data) where TDataConverter : IPublishDataConverter { short msgID = MessageIdentifierDispenser.GetNextMessageIdentifier(String.Format("Topic:{0}", topic)); IPublishDataConverter converter = GetPublishDataConverter <TDataConverter>(); MqttPublishMessage msg = new MqttPublishMessage() .ToTopic(topic) .WithMessageIdentifier(msgID) .WithQos(qualityOfService) .PublishData(converter.ConvertToBytes(data)); // QOS level 1 or 2 messages need to be saved so we can do the ack processes if (qualityOfService == MqttQos.AtLeastOnce || qualityOfService == MqttQos.ExactlyOnce) { publishedMessages.Add(msgID, msg); } connectionHandler.SendMessage(msg); return(msgID); }
/// <summary> /// Registers a new subscription with the subscription manager. /// </summary> /// <param name="topic"></param> /// <param name="qos"></param> /// <returns>The subscription message identifier.</returns> internal short RegisterSubscription <TPublishDataConverter>(string topic, MqttQos qos) where TPublishDataConverter : IPublishDataConverter { // check we don't have a pending subscription request for the topic. var pendingSubs = from ps in pendingSubscriptions.Values where ps.Topic.Equals(topic) select ps; if (pendingSubs.Count <Subscription>() > 0) { throw new ArgumentException("There is already a pending subscription for this topic"); } // no pending subscription, if we already have a subscription then throw it back out as well if (subscriptions.ContainsKey(topic)) { // TODO: we might want to treat this as an ignore/silent confirm because they will be receiving messages for the topic already throw new ArgumentException("You are already subscribed for this topic"); } // Add a pending subscription... Subscription sub = new Subscription() { Topic = topic, Qos = qos, MessageIdentifier = MessageIdentifierDispenser.GetNextMessageIdentifier("subscriptions"), CreatedTime = DateTime.Now, DataProcessor = Activator.CreateInstance <TPublishDataConverter>() }; pendingSubscriptions.Add(sub.MessageIdentifier, sub); // build a subscribe message for the caller. MqttSubscribeMessage msg = new MqttSubscribeMessage() .WithMessageIdentifier(sub.MessageIdentifier) .ToTopic(sub.Topic) .AtQos(sub.Qos); connectionHandler.SendMessage(msg); return(msg.VariableHeader.MessageIdentifier); }