示例#1
0
        /// <summary>
        /// Try to activate endpoint on any supervisor in site
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="additional"></param>
        /// <param name="context"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task <bool> ActivateAsync(EndpointRegistration endpoint,
                                                IEnumerable <string> additional, RegistryOperationContextModel context,
                                                CancellationToken ct)
        {
            // Get site of this endpoint
            var site = endpoint.SiteId;

            if (string.IsNullOrEmpty(site))
            {
                // Use discovery id gateway part if no site found
                site = DiscovererModelEx.ParseDeviceId(endpoint.DiscovererId, out _);
                if (string.IsNullOrEmpty(site))
                {
                    // Try supervisor id gateway part
                    site = DiscovererModelEx.ParseDeviceId(endpoint.SupervisorId, out _);
                }
            }

            // Get all supervisors in site
            endpoint.SiteId = site;
            var supervisorsInSite = await _supervisors.QueryAllSupervisorsAsync(
                new SupervisorQueryModel { SiteId = site });

            var candidateSupervisors = supervisorsInSite.Select(s => s.Id)
                                       .ToList().Shuffle();

            // Add all supervisors that managed this endpoint before.
            // TODO: Consider removing as it is a source of bugs
            if (additional != null)
            {
                candidateSupervisors.AddRange(additional);
            }

            // Remove previously failing one
            candidateSupervisors.Remove(endpoint.SupervisorId);
            // Loop through all randomly and try to take one that works.
            foreach (var supervisorId in candidateSupervisors)
            {
                endpoint.SupervisorId = supervisorId;
                endpoint.Activated    = false;
                try {
                    await ActivateAsync(endpoint, context, ct);

                    _logger.Information("Activate twin on supervisor {supervisorId}!",
                                        supervisorId);
                    // Done - endpoint was also patched thus has new supervisor id
                    return(true);
                }
                catch (Exception ex) {
                    _logger.Debug(ex, "Failed to activate twin on supervisor {supervisorId} " +
                                  "- trying next...", supervisorId);
                }
            }
            // Failed
            return(false);
        }
        /// <inheritdoc/>
        public async Task ProcessDiscoveryResultsAsync(string discovererId, DiscoveryResultModel result,
                                                       IEnumerable <DiscoveryEventModel> events)
        {
            if (string.IsNullOrEmpty(discovererId))
            {
                throw new ArgumentNullException(nameof(discovererId));
            }

            var gatewayId = DiscovererModelEx.ParseDeviceId(discovererId, out _);

            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }
            if (events == null)
            {
                throw new ArgumentNullException(nameof(events));
            }
            if ((result.RegisterOnly ?? false) && !events.Any())
            {
                return;
            }

            var sites = events.Select(e => e.Application.SiteId).Distinct();

            if (sites.Count() > 1)
            {
                throw new ArgumentException("Unexpected number of sites in discovery");
            }
            var siteId  = sites.SingleOrDefault() ?? gatewayId;
            var gateway = await _gateways.GetGatewayAsync(gatewayId);

            //
            // Merge in global discovery configuration into the one sent
            // by the discoverer.
            //
            if (result.DiscoveryConfig == null)
            {
                // Use global discovery configuration
                result.DiscoveryConfig = gateway.Modules?.Discoverer?.DiscoveryConfig;
            }
            else
            {
                if (result.DiscoveryConfig.ActivationFilter == null)
                {
                    // Use global activation filter
                    result.DiscoveryConfig.ActivationFilter =
                        gateway.Modules?.Discoverer?.DiscoveryConfig?.ActivationFilter;
                }
            }

            // Process discovery events
            await _applications.ProcessDiscoveryEventsAsync(siteId, discovererId,
                                                            gateway.Modules?.Supervisor?.Id, result, events);
        }
示例#3
0
        /// <summary>
        /// Create server model
        /// </summary>
        /// <param name="result"></param>
        /// <param name="hostAddress"></param>
        /// <param name="siteId"></param>
        /// <param name="gatewayId"></param>
        /// <param name="moduleId"></param>
        /// <returns></returns>
        public static ApplicationRegistrationModel ToServiceModel(this DiscoveredEndpointModel result,
                                                                  string hostAddress, string siteId, string gatewayId, string moduleId)
        {
            var type = result.Description.Server.ApplicationType.ToServiceType() ??
                       ApplicationType.Server;
            var discovererId = DiscovererModelEx.CreateDiscovererId(gatewayId, moduleId);

            return(new ApplicationRegistrationModel {
                Application = new ApplicationInfoModel {
                    SiteId = siteId,
                    DiscovererId = discovererId,
                    ApplicationType = type,
                    ApplicationId = ApplicationInfoModelEx.CreateApplicationId(siteId ?? gatewayId,
                                                                               result.Description.Server.ApplicationUri, type), // TODO: Assign at onboarder and leave null
                    ProductUri = result.Description.Server.ProductUri,
                    ApplicationUri = result.Description.Server.ApplicationUri,
                    DiscoveryUrls = new HashSet <string>(result.Description.Server.DiscoveryUrls),
                    DiscoveryProfileUri = result.Description.Server.DiscoveryProfileUri,
                    HostAddresses = new HashSet <string> {
                        hostAddress
                    },
                    ApplicationName = result.Description.Server.ApplicationName.Text,
                    LocalizedNames = string.IsNullOrEmpty(result.Description.Server.ApplicationName.Locale) ?
                                     null : new Dictionary <string, string> {
                        [result.Description.Server.ApplicationName.Locale] =
                            result.Description.Server.ApplicationName.Text
                    },
                    NotSeenSince = null,
                    Certificate = result.Description.ServerCertificate,
                    Capabilities = new HashSet <string>(result.Capabilities)
                },
                Endpoints = new List <EndpointRegistrationModel> {
                    new EndpointRegistrationModel {
                        SiteId = siteId,
                        DiscovererId = discovererId,
                        SupervisorId = null,
                        Id = null,
                        SecurityLevel = result.Description.SecurityLevel,
                        AuthenticationMethods = result.Description.UserIdentityTokens.ToServiceModel(),
                        EndpointUrl = result.Description.EndpointUrl, // Reported
                        Endpoint = new EndpointModel {
                            Url = result.AccessibleEndpointUrl,       // Accessible
                            AlternativeUrls = new HashSet <string> {
                                result.AccessibleEndpointUrl,
                                result.Description.EndpointUrl,
                            },
                            Certificate = result.Description.ServerCertificate,
                            SecurityMode = result.Description.SecurityMode.ToServiceType() ??
                                           SecurityMode.None,
                            SecurityPolicy = result.Description.SecurityPolicyUri
                        }
                    }
                }
            });
        }
        /// <summary>
        /// Send application alert
        /// </summary>
        /// <param name="application"></param>
        /// <param name="message"></param>
        /// <param name="usedConfiguration"></param>
        /// <returns></returns>
        private Task SendApplicationAlertAsync(ApplicationInfoModel application,
                                               string message, string usedConfiguration)
        {
#if !USE_APPLICATION_IDENTITY
            var deviceId = DiscovererModelEx.ParseDeviceId(
                application.DiscovererId, out var moduleId);
#else
            var deviceId = application.ApplicationId;
            var moduleId = (string)null;
#endif
            return(SendAlertAsync(deviceId, moduleId, message, usedConfiguration,
                                  FlattenToDict(application)));
        }
        /// <summary>
        /// Send endpoint alert
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="message"></param>
        /// <param name="usedConfiguration"></param>
        /// <returns></returns>
        private Task SendEndpointAlertAsync(EndpointInfoModel endpoint,
                                            string message, string usedConfiguration)
        {
#if USE_DISCOVERER_IDENTITY
            var deviceId = DiscovererModelEx.ParseDeviceId(
                endpoint.Registration.DiscovererId, out var moduleId);
#else
            var deviceId = endpoint.Registration.Id;
            var moduleId = (string)null;
#endif
            return(SendAlertAsync(deviceId, moduleId, message, usedConfiguration,
                                  FlattenToDict(endpoint.Registration)));
        }
示例#6
0
        /// <summary>
        /// helper to invoke service
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="discovererId"></param>
        /// <param name="service"></param>
        /// <param name="request"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task CallServiceOnDiscovererAsync <T>(string discovererId,
                                                            string service, T request, CancellationToken ct)
        {
            if (string.IsNullOrEmpty(discovererId))
            {
                throw new ArgumentNullException(nameof(discovererId));
            }
            var sw       = Stopwatch.StartNew();
            var deviceId = DiscovererModelEx.ParseDeviceId(discovererId,
                                                           out var moduleId);
            var result = await _client.CallMethodAsync(deviceId, moduleId, service,
                                                       _serializer.SerializeToString(request), null, ct);

            _logger.Debug("Calling discoverer service '{service}' on " +
                          "{deviceId}/{moduleId} took {elapsed} ms.", service,
                          deviceId, moduleId, sw.ElapsedMilliseconds);
        }
        /// <inheritdoc/>
        public async Task <DiscovererModel> GetDiscovererAsync(string id,
                                                               CancellationToken ct)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(nameof(id));
            }
            var deviceId = DiscovererModelEx.ParseDeviceId(id, out var moduleId);
            var device   = await _iothub.GetAsync(deviceId, moduleId, ct);

            var registration = device.ToEntityRegistration()
                               as DiscovererRegistration;

            if (registration == null)
            {
                throw new ResourceNotFoundException(
                          $"{id} is not a discoverer registration.");
            }
            return(registration.ToServiceModel());
        }
示例#8
0
        public void ProcessOneDiscoveryWithDifferentDiscoverersFromExistingWhenExistingDisabled()
        {
            var fix         = new Fixture();
            var discoverer2 = DiscovererModelEx.CreateDiscovererId(fix.Create <string>(), fix.Create <string>());

            // Readjust existing to be reported from different Discoverer...
            CreateFixtures(out var site, out var discoverer, out var supervisor,
                           out var publisher, out var gateway, out var existing,
                           out var found, out var registry, -1, x => {
                x.Application.DiscovererId = discoverer2;
                x.Endpoints.ForEach(e => e.DiscovererId = discoverer2);
                return(x);
            }, true);

            // Found one app and endpoint
            found = new List <DiscoveryEventModel> {
                found.First()
            };
            var count = registry.Devices.Count();

            // Assert disabled items are now enabled

            using (var mock = AutoMock.GetLoose()) {
                // Setup
                var service = Setup(mock, registry);

                // Run
                service.ProcessDiscoveryResultsAsync(discoverer, new DiscoveryResultModel(), found).Wait();

                // Assert
                var inreg = ApplicationsIn(registry);
                Assert.Equal(count, registry.Devices.Count());
                Assert.False(inreg.IsSameAs(existing));
                Assert.Equal(discoverer, inreg.First().Application.DiscovererId);
                Assert.Null(inreg.First().Application.NotSeenSince);
                Assert.Equal(discoverer, inreg.First().Endpoints.First().DiscovererId);
            }
        }
        public void ProcessDiscoveryWithDifferentDiscoverersSameSiteApplications()
        {
            var fix         = new Fixture();
            var discoverer2 = DiscovererModelEx.CreateDiscovererId(fix.Create <string>(), fix.Create <string>());

            // Readjust existing to be reported from different Discoverer...
            CreateFixtures(out var site, out var discoverer, out var supervisor,
                           out var publisher, out var gateway, out var existing,
                           out var found, out var registry, -1, x => {
                x.Application.DiscovererId = discoverer2;
                x.Endpoints.ForEach(e => e.DiscovererId = discoverer2);
                return(x);
            });

            // Assert no changes

            using (var mock = Setup(registry, out var service)) {
                // Run
                service.ProcessDiscoveryResultsAsync(discoverer, new DiscoveryResultModel(), found).Wait();

                // Assert
                Assert.True(ApplicationsIn(registry).IsSameAs(existing));
            }
        }
        /// <inheritdoc/>
        public async Task HandleAsync(string deviceId, string moduleId,
                                      byte[] payload, IDictionary <string, string> properties, Func <Task> checkpoint)
        {
            DiscoveryEventModel discovery;

            try {
                discovery = _serializer.Deserialize <DiscoveryEventModel>(payload);
            }
            catch (Exception ex) {
                _logger.Error(ex, "Failed to convert discovery result {json}",
                              Encoding.UTF8.GetString(payload));
                return;
            }
            try {
                var discovererId = DiscovererModelEx.CreateDiscovererId(
                    deviceId, moduleId?.ToString());

                await ProcessServerEndpointDiscoveryAsync(discovererId,
                                                          discovery, checkpoint);
            }
            catch (Exception ex) {
                _logger.Error(ex, "Handling discovery event failed with exception - skip");
            }
        }
        /// <summary>
        /// Helper to create fixtures
        /// </summary>
        /// <param name="site"></param>
        /// <param name="discoverer"></param>
        /// <param name="existing"></param>
        /// <param name="found"></param>
        /// <param name="registry"></param>
        /// <param name="countDevices"></param>
        /// <param name="fixup"></param>
        /// <param name="disable"></param>
        private void CreateFixtures(out string site, out string discoverer,
                                    out string supervisor, out string publisher, out string gateway,
                                    out List <ApplicationRegistrationModel> existing, out List <DiscoveryEventModel> found,
                                    out IoTHubServices registry, int countDevices = -1,
                                    Func <ApplicationRegistrationModel, ApplicationRegistrationModel> fixup = null,
                                    bool disable = false)
        {
            var fix = new Fixture();

            // Create template applications and endpoints
            fix.Customizations.Add(new TypeRelay(typeof(VariantValue), typeof(VariantValue)));
            fix.Behaviors.OfType <ThrowingRecursionBehavior>().ToList()
            .ForEach(b => fix.Behaviors.Remove(b));
            fix.Behaviors.Add(new OmitOnRecursionBehavior());
            var sitex = site = fix.Create <string>();

            gateway = fix.Create <string>();
            var Gateway = (new GatewayModel {
                SiteId = site,
                Id = gateway
            }.ToGatewayRegistration().ToDeviceTwin(),
                           new DeviceModel {
                Id = gateway
            });
            var module      = fix.Create <string>();
            var discovererx = discoverer = DiscovererModelEx.CreateDiscovererId(gateway, module);
            var Discoverer  = (new DiscovererModel {
                SiteId = site,
                Id = discovererx
            }.ToDiscovererRegistration().ToDeviceTwin(_serializer),
                               new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var supervisorx = supervisor = SupervisorModelEx.CreateSupervisorId(gateway, module);
            var Supervisor  = (new SupervisorModel {
                SiteId = site,
                Id = supervisorx
            }.ToSupervisorRegistration().ToDeviceTwin(_serializer),
                               new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var publisherx = publisher = PublisherModelEx.CreatePublisherId(gateway, module);
            var Publisher  = (new PublisherModel {
                SiteId = site,
                Id = publisherx
            }.ToPublisherRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            var template = fix
                           .Build <ApplicationRegistrationModel>()
                           .Without(x => x.Application)
                           .Do(c => c.Application = fix
                                                    .Build <ApplicationInfoModel>()
                                                    .Without(x => x.NotSeenSince)
                                                    .With(x => x.SiteId, sitex)
                                                    .With(x => x.DiscovererId, discovererx)
                                                    .Create())
                           .Without(x => x.Endpoints)
                           .Do(c => c.Endpoints = fix
                                                  .Build <EndpointRegistrationModel>()
                                                  .With(x => x.SiteId, sitex)
                                                  .With(x => x.DiscovererId, discovererx)
                                                  .With(x => x.SupervisorId, supervisorx)
                                                  .CreateMany(5)
                                                  .ToList())
                           .CreateMany(5)
                           .ToList();

            template.ForEach(a =>
                             a.Application.ApplicationId =
                                 ApplicationInfoModelEx.CreateApplicationId(a.Application)
                             );

            // Create discovery results from template
            var i = 0; var now = DateTime.UtcNow;

            found = template
                    .SelectMany(a => a.Endpoints.Select(
                                    e => new DiscoveryEventModel {
                Application  = a.Application,
                Registration = e,
                Index        = i++,
                TimeStamp    = now
            }))
                    .ToList();

            // Clone and fixup existing applications as per test case
            existing = template
                       .Select(e => e.Clone())
                       .Select(fixup ?? (a => a))
                       .ToList();
            // and fill registry with them...
            var appdevices = existing
                             .Select(a => a.Application.ToApplicationRegistration(disable))
                             .Select(a => a.ToDeviceTwin(_serializer))
                             .Select(d => (d, new DeviceModel {
                Id = d.Id
            }));
            var epdevices = existing
                            .SelectMany(a => a.Endpoints
                                        .Select(e =>
                                                new EndpointInfoModel {
                ApplicationId = a.Application.ApplicationId,
                Registration  = e
            }.ToEndpointRegistration(_serializer, disable))
                                        .Select(e => e.ToDeviceTwin(_serializer)))
                            .Select(d => (d, new DeviceModel {
                Id = d.Id
            }));

            appdevices = appdevices.Concat(epdevices);
            if (countDevices != -1)
            {
                appdevices = appdevices.Take(countDevices);
            }
            registry = IoTHubServices.Create(appdevices
                                             .Concat(Gateway.YieldReturn())
                                             .Concat(Discoverer.YieldReturn())
                                             .Concat(Supervisor.YieldReturn())
                                             .Concat(Publisher.YieldReturn()));
        }
        public void ProcessDiscoveryWithNoResultsAndNoExistingApplications()
        {
            var found = new List <DiscoveryEventModel>();
            var fix   = new Fixture();

            var gateway = fix.Create <string>();
            var Gateway = (new GatewayModel {
                Id = gateway
            }.ToGatewayRegistration().ToDeviceTwin(),
                           new DeviceModel {
                Id = gateway
            });
            var module     = fix.Create <string>();
            var discoverer = DiscovererModelEx.CreateDiscovererId(gateway, module);
            var Discoverer = (new DiscovererModel {
                Id = discoverer
            }.ToDiscovererRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var supervisor = SupervisorModelEx.CreateSupervisorId(gateway, module);
            var Supervisor = (new SupervisorModel {
                Id = supervisor
            }.ToSupervisorRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var publisher = PublisherModelEx.CreatePublisherId(gateway, module);
            var Publisher = (new PublisherModel {
                Id = publisher
            }.ToPublisherRegistration().ToDeviceTwin(_serializer),
                             new DeviceModel {
                Id = gateway, ModuleId = module
            });

            var registry = IoTHubServices.Create(Gateway.YieldReturn() // Single device
                                                 .Append(Discoverer)
                                                 .Append(Supervisor)
                                                 .Append(Publisher));

            using (var mock = AutoMock.GetLoose(builder => {
                // Setup
                builder.RegisterInstance(registry).As <IIoTHubTwinServices>();
                builder.RegisterType <ApplicationTwins>().As <IApplicationRepository>();
                builder.RegisterType <DiscovererRegistry>().As <IDiscovererRegistry>();
                builder.RegisterType <SupervisorRegistry>().As <ISupervisorRegistry>();
                builder.RegisterType <PublisherRegistry>().As <IPublisherRegistry>();
                builder.RegisterType <GatewayRegistry>().As <IGatewayRegistry>();
                builder.RegisterType <ApplicationRegistry>().As <IApplicationBulkProcessor>();
                builder.RegisterType <EndpointRegistry>().As <IEndpointBulkProcessor>();
            })) {
                var service = mock.Create <DiscoveryProcessor>();

                // Run
                service.ProcessDiscoveryResultsAsync(discoverer, new DiscoveryResultModel(), found).Wait();

                // Assert
                Assert.Single(registry.Devices);
                Assert.Equal(gateway, registry.Devices.First().Device.Id);
            }
        }
        /// <inheritdoc/>
        public async Task UpdateDiscovererAsync(string discovererId,
                                                DiscovererUpdateModel request, CancellationToken ct)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (string.IsNullOrEmpty(discovererId))
            {
                throw new ArgumentException(nameof(discovererId));
            }

            // Get existing endpoint and compare to see if we need to patch.
            var deviceId = DiscovererModelEx.ParseDeviceId(discovererId, out var moduleId);

            while (true)
            {
                try {
                    var twin = await _iothub.GetAsync(deviceId, moduleId, ct);

                    if (twin.Id != deviceId && twin.ModuleId != moduleId)
                    {
                        throw new ArgumentException("Id must be same as twin to patch",
                                                    nameof(discovererId));
                    }

                    var registration = twin.ToEntityRegistration(true) as DiscovererRegistration;
                    if (registration == null)
                    {
                        throw new ResourceNotFoundException(
                                  $"{discovererId} is not a discoverer registration.");
                    }

                    // Update registration from update request
                    var patched = registration.ToServiceModel();
                    if (request.Discovery != null)
                    {
                        patched.RequestedMode = (DiscoveryMode)request.Discovery;
                    }

                    if (request.SiteId != null)
                    {
                        patched.SiteId = string.IsNullOrEmpty(request.SiteId) ?
                                         null : request.SiteId;
                    }

                    if (request.LogLevel != null)
                    {
                        patched.LogLevel = request.LogLevel == TraceLogLevel.Information ?
                                           null : request.LogLevel;
                    }

                    if (request.DiscoveryConfig != null)
                    {
                        if (patched.RequestedConfig == null)
                        {
                            patched.RequestedConfig = new DiscoveryConfigModel();
                        }
                        if (request.DiscoveryConfig.AddressRangesToScan != null)
                        {
                            patched.RequestedConfig.AddressRangesToScan =
                                string.IsNullOrEmpty(
                                    request.DiscoveryConfig.AddressRangesToScan.Trim()) ?
                                null : request.DiscoveryConfig.AddressRangesToScan;
                        }
                        if (request.DiscoveryConfig.PortRangesToScan != null)
                        {
                            patched.RequestedConfig.PortRangesToScan =
                                string.IsNullOrEmpty(
                                    request.DiscoveryConfig.PortRangesToScan.Trim()) ?
                                null : request.DiscoveryConfig.PortRangesToScan;
                        }
                        if (request.DiscoveryConfig.IdleTimeBetweenScans != null)
                        {
                            patched.RequestedConfig.IdleTimeBetweenScans =
                                request.DiscoveryConfig.IdleTimeBetweenScans.Value.Ticks < 0 ?
                                null : request.DiscoveryConfig.IdleTimeBetweenScans;
                        }
                        if (request.DiscoveryConfig.MaxNetworkProbes != null)
                        {
                            patched.RequestedConfig.MaxNetworkProbes =
                                request.DiscoveryConfig.MaxNetworkProbes <= 0 ?
                                null : request.DiscoveryConfig.MaxNetworkProbes;
                        }
                        if (request.DiscoveryConfig.NetworkProbeTimeout != null)
                        {
                            patched.RequestedConfig.NetworkProbeTimeout =
                                request.DiscoveryConfig.NetworkProbeTimeout.Value.Ticks <= 0 ?
                                null : request.DiscoveryConfig.NetworkProbeTimeout;
                        }
                        if (request.DiscoveryConfig.MaxPortProbes != null)
                        {
                            patched.RequestedConfig.MaxPortProbes =
                                request.DiscoveryConfig.MaxPortProbes <= 0 ?
                                null : request.DiscoveryConfig.MaxPortProbes;
                        }
                        if (request.DiscoveryConfig.MinPortProbesPercent != null)
                        {
                            patched.RequestedConfig.MinPortProbesPercent =
                                request.DiscoveryConfig.MinPortProbesPercent <= 0 ||
                                request.DiscoveryConfig.MinPortProbesPercent > 100 ?
                                null : request.DiscoveryConfig.MinPortProbesPercent;
                        }
                        if (request.DiscoveryConfig.PortProbeTimeout != null)
                        {
                            patched.RequestedConfig.PortProbeTimeout =
                                request.DiscoveryConfig.PortProbeTimeout.Value.Ticks <= 0 ?
                                null : request.DiscoveryConfig.PortProbeTimeout;
                        }
                        if (request.DiscoveryConfig.ActivationFilter != null)
                        {
                            patched.RequestedConfig.ActivationFilter =
                                request.DiscoveryConfig.ActivationFilter.SecurityMode == null &&
                                request.DiscoveryConfig.ActivationFilter.SecurityPolicies == null &&
                                request.DiscoveryConfig.ActivationFilter.TrustLists == null ?
                                null : request.DiscoveryConfig.ActivationFilter;
                        }
                    }
                    // Patch
                    await _iothub.PatchAsync(registration.Patch(
                                                 patched.ToDiscovererRegistration()), false, ct);

                    return;
                }
                catch (ResourceOutOfDateException ex) {
                    _logger.Debug(ex, "Retrying updating discoverer...");
                    continue;
                }
            }
        }