/// <summary> /// Notify about new connectivity state using any status callback registered. /// </summary> /// <param name="state"></param> /// <returns></returns> private async Task NotifyConnectivityStateChangeAsync(EndpointConnectivityState state) { var previous = _lastState; if (previous == state) { return; } if (previous != EndpointConnectivityState.Connecting && previous != EndpointConnectivityState.Ready && state == EndpointConnectivityState.Error) { // Do not change state to generic error once we have // a specific error state already set... _logger.Debug( "{session}: Error, but leaving {url} via {endpoint} state at {previous}.", _sessionId, _endpoint.Url, _endpointUrl, previous); return; } _lastState = state; _logger.Information( "{session}: Endpoint {url} via {endpoint} changed from {previous} to {state}", _sessionId, _endpoint.Url, _endpointUrl, previous, state); try { await _statusCb?.Invoke(_endpoint, state); } catch (Exception ex) { _logger.Error(ex, "{session}: Exception during state callback", _sessionId); } }
internal static string ToSerializedValue(this EndpointConnectivityState value) { switch (value) { case EndpointConnectivityState.Connecting: return("Connecting"); case EndpointConnectivityState.NotReachable: return("NotReachable"); case EndpointConnectivityState.Busy: return("Busy"); case EndpointConnectivityState.NoTrust: return("NoTrust"); case EndpointConnectivityState.CertificateInvalid: return("CertificateInvalid"); case EndpointConnectivityState.Ready: return("Ready"); case EndpointConnectivityState.Error: return("Error"); } return(null); }
/// <summary> /// Notify about session/endpoint state changes /// </summary> /// <param name="ep"></param> /// <param name="state"></param> /// <returns></returns> private Task NotifyStateChangeAsync(EndpointModel ep, EndpointConnectivityState state) { var id = new EndpointIdentifier(ep); if (_callbacks.TryGetValue(id, out var cb)) { return(cb(state)); } return(Task.CompletedTask); }
/// <summary> /// Notify about session/endpoint state changes /// </summary> /// <param name="connection"></param> /// <param name="state"></param> /// <returns></returns> private Task NotifyStateChangeAsync(ConnectionModel connection, EndpointConnectivityState state) { var id = new ConnectionIdentifier(connection); if (_callbacks.TryGetValue(id, out var list)) { lock (list) { if (list.Count > 0) { return(Task.WhenAll(list.Select(cb => cb.Callback.Invoke(state))) .ContinueWith(_ => Task.CompletedTask)); } } } return(Task.CompletedTask); }
/// <summary> /// Notify about new connectivity state using any status callback registered. /// </summary> /// <param name="state"></param> /// <returns></returns> private async Task NotifyConnectivityStateChangeAsync(EndpointConnectivityState state) { var previous = _lastState; if (previous == state) { return; } if (previous != EndpointConnectivityState.Connecting && previous != EndpointConnectivityState.Ready && state == EndpointConnectivityState.Error) { // Do not change state to generic error once we have // a specific error state already set... _logger.Debug( "Error, connection to {endpoint} - leaving state at {previous}.", _endpointUrl, previous); return; } _lastState = state; _logger.Information( "Connecting to {endpoint} changed from {previous} to {state}", _endpointUrl, previous, state); try { await _statusCb?.Invoke(_connection, state); } catch (Exception ex) { _logger.Error(ex, "Exception during state callback"); } if (state == EndpointConnectivityState.Ready) { // Notify waiting threads that a session is ready _acquired.TrySetResult(_session); } else { // Keep any thread waiting if (_acquired.Task.IsCompleted) { _acquired = new TaskCompletionSource <Session>(); } } }
/// <inheritdoc/> public async Task SetEndpointAsync(EndpointModel endpoint) { await _lock.WaitAsync(); try { var previous = _endpoint.Task.IsCompleted ? _endpoint.Task.Result : null; if (!endpoint.IsSameAs(previous)) { _logger.Debug( "Updating twin {device} endpoint from {previous} to {endpoint}...", _events?.DeviceId, previous?.Url, endpoint?.Url); if (_endpoint.Task.IsCompleted) { _endpoint = new TaskCompletionSource <EndpointModel>(); } // Unregister old endpoint if (previous != null) { _callback?.Dispose(); _callback = null; _session?.Dispose(); _session = null; // Clear state State = EndpointConnectivityState.Disconnected; } // Register callback to report endpoint state property if (endpoint != null) { var connection = new ConnectionModel { Endpoint = endpoint }; _session = _client.GetSessionHandle(connection); // Set initial state State = _session.State; // update reported state _callback = _client.RegisterCallback(connection, state => { State = state; return(_events?.ReportAsync("State", state)); }); _logger.Information("Endpoint {endpoint} ({device}, {module}) updated.", endpoint?.Url, _events.DeviceId, _events.ModuleId); // ready to use _endpoint.TrySetResult(endpoint); } _logger.Information("Twin {device} endpoint updated to {endpoint}.", _events?.DeviceId, endpoint?.Url); } } finally { _lock.Release(); } }