/// <summary> /// Notify discovery / registration callbacks /// </summary> /// <param name="siteId"></param> /// <param name="supervisorId"></param> /// <param name="result"></param> /// <param name="exception"></param> /// <returns></returns> private async Task CallDiscoveryCallbacksAsync(string siteId, string supervisorId, DiscoveryResultModel result, Exception exception) { try { var callbacks = result?.DiscoveryConfig?.Callbacks; if (callbacks == null || callbacks.Count == 0) { return; } await _client.CallAsync(JToken.FromObject( new { id = result.Id, supervisorId, siteId = siteId ?? supervisorId, result = new { config = result.DiscoveryConfig, diagnostics = exception != null ? JToken.FromObject(exception) : result.Diagnostics } }), callbacks.ToArray()); } catch (Exception ex) { _logger.Debug(ex, "Failed to notify callbacks. Continue..."); // Continue... } }
/// <inheritdoc/> public async Task ProcessDiscoveryResultsAsync(string discovererId, DiscoveryResultModel result, IEnumerable <DiscoveryEventModel> events) { await _client.ProcessDiscoveryResultsAsync(discovererId, new DiscoveryResultListApiModel { Result = result.Map <DiscoveryResultApiModel>(), Events = events.Map <List <DiscoveryEventApiModel> >() }); }
/// <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); }
/// <summary> /// Add another discovery from discoverer /// </summary> /// <param name="model"></param> /// <returns></returns> public void Enqueue(DiscoveryEventModel model) { _maxIndex = Math.Max(model.Index, _maxIndex); if (model.Registration != null) { _endpoints.Add(model); } else { Result = model.Result ?? new DiscoveryResultModel(); } }
/// <inheritdoc/> public async Task ProcessDiscoveryEventsAsync(IEnumerable <EndpointInfoModel> newEndpoints, DiscoveryResultModel result, string discovererId, string supervisorId, string applicationId, bool hardDelete) { if (newEndpoints == null) { throw new ArgumentNullException(nameof(newEndpoints)); } var context = result.Context.Validate(); var found = newEndpoints .Select(e => e.ToEndpointRegistration(_serializer, false, discovererId, supervisorId)) .ToList(); var existing = Enumerable.Empty <EndpointRegistration>(); if (!string.IsNullOrEmpty(applicationId)) { // Merge with existing endpoints of the application existing = await GetEndpointsAsync(applicationId, true); } var remove = new HashSet <EndpointRegistration>(existing, EndpointRegistrationEx.Logical); var add = new HashSet <EndpointRegistration>(found, EndpointRegistrationEx.Logical); var unchange = new HashSet <EndpointRegistration>(existing, EndpointRegistrationEx.Logical); var change = new HashSet <EndpointRegistration>(found, EndpointRegistrationEx.Logical); unchange.IntersectWith(add); change.IntersectWith(remove); remove.ExceptWith(found); add.ExceptWith(existing); var added = 0; var updated = 0; var unchanged = 0; var removed = 0; if (!(result.RegisterOnly ?? false)) { // Remove or disable an endpoint foreach (var item in remove) { try { // Only touch applications the discoverer owns. if (item.DiscovererId == discovererId) { if (hardDelete) { var device = await _iothub.GetAsync(item.DeviceId); // First we update any registration var existingEndpoint = device.ToEndpointRegistration(false); if (!string.IsNullOrEmpty(existingEndpoint.SupervisorId)) { await ClearSupervisorTwinSecretAsync(device.Id, existingEndpoint.SupervisorId); } // Then hard delete... await _iothub.DeleteAsync(item.DeviceId); await _broker.NotifyAllAsync(l => l.OnEndpointDeletedAsync(context, item.DeviceId, item.ToServiceModel())); } else if (!(item.IsDisabled ?? false)) { var endpoint = item.ToServiceModel(); var update = endpoint.ToEndpointRegistration(_serializer, true); await _iothub.PatchAsync(item.Patch(update, _serializer), true); await _broker.NotifyAllAsync( l => l.OnEndpointDisabledAsync(context, endpoint)); } else { unchanged++; continue; } removed++; } else { // Skip the ones owned by other supervisors unchanged++; } } catch (Exception ex) { unchanged++; _logger.Error(ex, "Exception during discovery removal."); } } } // Update endpoints that were disabled foreach (var exists in unchange) { try { if (exists.DiscovererId == null || exists.DiscovererId == discovererId || (exists.IsDisabled ?? false)) { // Get the new one we will patch over the existing one... var patch = change.First(x => EndpointRegistrationEx.Logical.Equals(x, exists)); if (exists.Activated ?? false) { patch.ActivationState = exists.ActivationState; } else { await ApplyActivationFilterAsync(result.DiscoveryConfig?.ActivationFilter, patch, context); } if (exists != patch) { await _iothub.PatchAsync(exists.Patch(patch, _serializer), true); var endpoint = patch.ToServiceModel(); // await _broker.NotifyAllAsync( // l => l.OnEndpointUpdatedAsync(context, endpoint)); if (exists.IsDisabled ?? false) { await _broker.NotifyAllAsync( l => l.OnEndpointEnabledAsync(context, endpoint)); } updated++; continue; } } unchanged++; } catch (Exception ex) { unchanged++; _logger.Error(ex, "Exception during update."); } } // Add endpoint foreach (var item in add) { try { await ApplyActivationFilterAsync(result.DiscoveryConfig?.ActivationFilter, item, context); await _iothub.CreateAsync(item.ToDeviceTwin(_serializer), true); var endpoint = item.ToServiceModel(); await _broker.NotifyAllAsync(l => l.OnEndpointNewAsync(context, endpoint)); await _broker.NotifyAllAsync(l => l.OnEndpointEnabledAsync(context, endpoint)); added++; } catch (Exception ex) { unchanged++; _logger.Error(ex, "Exception adding endpoint from discovery."); } } if (added != 0 || removed != 0) { _logger.Information("processed endpoint results: {added} endpoints added, {updated} " + "updated, {removed} removed or disabled, and {unchanged} unchanged.", added, updated, removed, unchanged); } }
/// <inheritdoc/> public Task ProcessDiscoveryEventsAsync(string siteId, string supervisorId, DiscoveryResultModel result, IEnumerable <DiscoveryEventModel> events) { return(Task.CompletedTask); }
/// <inheritdoc/> public Task ProcessDiscoveryEventsAsync(IEnumerable <EndpointInfoModel> found, DiscoveryResultModel context, string supervisorId, string applicationId, bool hardDelete) { return(Task.CompletedTask); }
/// <inheritdoc/> public async Task ProcessDiscoveryResultsAsync(string supervisorId, DiscoveryResultModel result, IEnumerable <DiscoveryEventModel> events) { if (string.IsNullOrEmpty(supervisorId)) { throw new ArgumentNullException(nameof(supervisorId)); } 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() ?? supervisorId; try { // // Merge in global discovery configuration into the one sent // by the supervisor. // var supervisor = await _supervisors.GetSupervisorAsync(supervisorId, false); if (result.DiscoveryConfig == null) { // Use global discovery configuration result.DiscoveryConfig = supervisor.DiscoveryConfig; } else { if (supervisor.DiscoveryConfig?.Callbacks != null) { if (result.DiscoveryConfig.Callbacks == null) { result.DiscoveryConfig.Callbacks = supervisor.DiscoveryConfig.Callbacks; } else { result.DiscoveryConfig.Callbacks.AddRange( supervisor.DiscoveryConfig.Callbacks); } } if (result.DiscoveryConfig.ActivationFilter == null) { // Use global activation filter result.DiscoveryConfig.ActivationFilter = supervisor.DiscoveryConfig?.ActivationFilter; } } // Process discovery events await _applications.ProcessDiscoveryEventsAsync(siteId, supervisorId, result, events); // Notify callbacks await CallDiscoveryCallbacksAsync(siteId, supervisorId, result, null); } catch (Exception ex) { // Notify callbacks await CallDiscoveryCallbacksAsync(siteId, supervisorId, result, ex); throw ex; } }