예제 #1
0
        private MqttApplicationMessage BuildMessage(string methodName, byte[] payload, bool utf8Payload, string contentType, MqttQualityOfServiceLevel qos, TimeSpan timeout,
                                                    string target, Action <MqttApplicationMessageBuilder> action = null)
        {
            if (methodName == null)
            {
                throw new ArgumentNullException(nameof(methodName));
            }

            var builder = new MqttApplicationMessageBuilder()
                          .WithTopic(GetRequestTopic(target, qos))
                          .WithContentType(contentType)
                          .WithUserProperty("Method", methodName)
                          .WithQualityOfServiceLevel(qos);

            if (payload != null)
            {
                builder
                .WithPayload(payload)
                .WithPayloadFormatIndicator(utf8Payload ? MqttPayloadFormatIndicator.CharacterData : MqttPayloadFormatIndicator.Unspecified);
            }

            if (timeout != default && timeout != Timeout.InfiniteTimeSpan)
            {
                builder
                .WithMessageExpiryInterval(Convert.ToUInt32(timeout.TotalSeconds))
                .WithUserProperty("Timeout", Convert.ToInt32(timeout.TotalSeconds).ToString());
            }

            action?.Invoke(builder);

            return(builder.Build());
        }
예제 #2
0
        private async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
        {
            if (!eventArgs.ApplicationMessage.Topic.Contains(GetRequestTopic(null, null)))
            {
                return;
            }

            var message    = eventArgs.ApplicationMessage;
            var methodName = message.GetUserProperty("Method");
            var id         = message.GetUserProperty("Id");
            var timeout    = message.GetUserProperty <int?>("Timeout");
            var broadcast  = message.GetUserProperty <bool?>("Broadcast") ?? false;
            var noResponse = message.GetUserProperty <bool?>("NoResponse") ?? false;
            var clientId   = GetSource(message.Topic);

            using var serviceScope = _serviceProvider.CreateScope();
            using var cts          = timeout.HasValue ? new CancellationTokenSource(timeout.Value * 1000) : new CancellationTokenSource();
            var responseBuilder = new MqttApplicationMessageBuilder()
                                  .WithContentType(DefaultSerializer.ContentType)
                                  .WithTopic(GetResponseTopic(GetSource(message.Topic)))
                                  .WithAtLeastOnceQoS();

            if (broadcast)
            {
                responseBuilder.WithUserProperty("Broadcast", true.ToString());
            }
            if (timeout > 0)
            {
                responseBuilder.WithMessageExpiryInterval((uint)timeout.Value);
            }

            try
            {
                if (!RpcMethodResolver.Methods.TryGetValue(methodName, out var method))
                {
                    if (string.IsNullOrEmpty(id) || noResponse)
                    {
                        return;
                    }
                    throw new EntryPointNotFoundException($"Method '{methodName}' not found.");
                }

                var parameters = method.GetParameters();

                object[] args;
                switch (parameters.Length)
                {
                case 0:
                    args = null;
                    break;

                case 1:
                    var parameterInfo = parameters.First();
                    args = parameterInfo.ParameterType == typeof(byte[])
                            ? new[] { (object)message.Payload }
                            : new[] { DefaultSerializer.Deserialize(message.Payload, parameterInfo.ParameterType) };
                    break;

                default:
                    _logger.Error(new NotImplementedException(), "Multiple parameters resolving has not been supported yet, please use a key-value object.");
                    return;
                }

                var rpcService = (IRpcService)serviceScope.ServiceProvider.GetService(method.DeclaringType);

                rpcService.CurrentContext = new RpcContext
                {
                    Topic          = message.Topic,
                    RemoteClientId = clientId
                };

                var task = Task.Run(async() =>
                {
                    var returnValue = method.Invoke(rpcService, args);
                    if (returnValue is Task t)
                    {
                        await t.ConfigureAwait(false);
                        if (t.GetType().IsGenericType)
                        {
                            var resultProperty = t.GetType().GetProperty("Result");
                            Debug.Assert(resultProperty != null);
                            returnValue = resultProperty.GetValue(t);
                        }
                    }

                    return(returnValue);
                }, cts.Token);

                if (!string.IsNullOrEmpty(id))
                {
                    if (!_waitingCalls.TryAdd(id, new CancellableTask(task, cts)))
                    {
                        throw new InvalidOperationException();
                    }
                }
                else
                {
                    _noIdCalls.TryAdd(cts, task);
                }

                var result = await task.ConfigureAwait(false);

                responseBuilder.WithUserProperty("Success", true.ToString());

                if (!noResponse)
                {
                    responseBuilder.WithUserProperty("Id", id)
                    .WithPayload(DefaultSerializer.Serialize(result));
                }
            }
            catch (Exception ex)
            {
                responseBuilder.WithUserProperty("Success", false.ToString())
                .WithUserProperty("ErrorCode", ex.HResult.ToString())
                .WithUserProperty("ErrorMessage", ex.Message);
            }
            finally
            {
                if (!string.IsNullOrEmpty(id))
                {
                    _waitingCalls.TryRemove(id, out _);
                }
                else
                {
                    _noIdCalls.TryRemove(cts, out _);
                }

                if (!noResponse)
                {
                    await _mqttClient.PublishAsync(responseBuilder.Build()).ConfigureAwait(false);
                }
            }
        }