private async Task ReceiveMessagesAsync(CancellationToken cancellationToken) { var c2dReceiveExceptionsToBeIgnored = new Dictionary <Type, string>(_exceptionsToBeIgnored) { { typeof(DeviceMessageLockLostException), "Attempted to complete a received message whose lock token has expired" } }; while (!cancellationToken.IsCancellationRequested) { if (!IsDeviceConnected) { await Task.Delay(s_sleepDuration); continue; } else if (_transportType == TransportType.Http1) { // The call to ReceiveAsync over HTTP completes immediately, rather than waiting up to the specified // time or when a cancellation token is signaled, so if we want it to poll at the same rate, we need // to add an explicit delay here. await Task.Delay(s_sleepDuration); } _logger.LogInformation($"Device waiting for C2D messages from the hub for {s_sleepDuration}." + $"\nUse the IoT Hub Azure Portal or Azure IoT Explorer to send a message to this device."); await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : "ReceiveAndCompleteC2DMessage", asyncOperation : async() => await ReceiveMessageAndCompleteAsync(), shouldExecuteOperation : () => IsDeviceConnected, logger : _logger, exceptionsToBeIgnored : c2dReceiveExceptionsToBeIgnored, cancellationToken : cancellationToken); } }
private async Task SendMessagesAsync(CancellationToken cancellationToken) { int messageCount = 0; while (!cancellationToken.IsCancellationRequested) { if (IsDeviceConnected) { _logger.LogInformation($"Device sending message {++messageCount} to IoT hub."); using Message message = PrepareMessage(messageCount); await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : $"SendD2CMessage_{messageCount}", asyncOperation : async() => await s_deviceClient.SendEventAsync(message), shouldExecuteOperation : () => IsDeviceConnected, logger : _logger, exceptionsToBeIgnored : _exceptionsToBeIgnored, cancellationToken : cancellationToken); _logger.LogInformation($"Device sent message {messageCount} to IoT hub."); } await Task.Delay(s_sleepDuration); } }
private async Task GetTwinAndDetectChangesAsync(CancellationToken cancellationToken) { Twin twin = null; // Allow a single thread to call GetTwin here await _initSemaphore.WaitAsync(cancellationToken); await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : "GetTwin", asyncOperation : async() => { twin = await s_deviceClient.GetTwinAsync(); _logger.LogInformation($"Device retrieving twin values: {twin.ToJson()}"); TwinCollection twinCollection = twin.Properties.Desired; long serverDesiredPropertyVersion = twinCollection.Version; // Check if the desired property version is outdated on the local side. if (serverDesiredPropertyVersion > s_localDesiredPropertyVersion) { _logger.LogDebug($"The desired property version cached on local is changing from {s_localDesiredPropertyVersion} to {serverDesiredPropertyVersion}."); await HandleTwinUpdateNotificationsAsync(twinCollection, cancellationToken); } }, shouldExecuteOperation : () => IsDeviceConnected, logger : _logger, exceptionsToBeIgnored : _exceptionsToBeIgnored, cancellationToken : cancellationToken); _initSemaphore.Release(); }
private async Task HandleTwinUpdateNotificationsAsync(TwinCollection twinUpdateRequest, object userContext) { CancellationToken cancellationToken = (CancellationToken)userContext; if (!cancellationToken.IsCancellationRequested) { var reportedProperties = new TwinCollection(); _logger.LogInformation($"Twin property update requested: \n{twinUpdateRequest.ToJson()}"); // For the purpose of this sample, we'll blindly accept all twin property write requests. foreach (KeyValuePair <string, object> desiredProperty in twinUpdateRequest) { _logger.LogInformation($"Setting property {desiredProperty.Key} to {desiredProperty.Value}."); reportedProperties[desiredProperty.Key] = desiredProperty.Value; } // For the purpose of this sample, we'll blindly accept all twin property write requests. await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : "UpdateReportedProperties", asyncOperation : async() => await s_deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken), shouldExecuteOperation : () => IsDeviceConnected, logger : _logger, exceptionsToBeIgnored : _exceptionsToBeIgnored, cancellationToken : cancellationToken); } }
private async Task InitializeAndSetupClientAsync(CancellationToken cancellationToken) { if (ShouldClientBeInitialized(s_connectionStatus)) { // Allow a single thread to dispose and initialize the client instance. await _initSemaphore.WaitAsync(cancellationToken); try { if (ShouldClientBeInitialized(s_connectionStatus)) { _logger.LogDebug($"Attempting to initialize the client instance, current status={s_connectionStatus}"); // If the device client instance has been previously initialized, close and dispose it. if (s_deviceClient != null) { try { await s_deviceClient.CloseAsync(cancellationToken); } catch (UnauthorizedException) { } // if the previous token is now invalid, this call may fail s_deviceClient.Dispose(); } s_deviceClient = DeviceClient.CreateFromConnectionString(_deviceConnectionStrings.First(), _transportType, _clientOptions); s_deviceClient.SetConnectionStatusChangesHandler(ConnectionStatusChangeHandler); _logger.LogDebug("Initialized the client instance."); } } finally { _initSemaphore.Release(); } // Force connection now. // We have set the "shouldExecuteOperation" function to always try to open the connection. // OpenAsync() is an idempotent call, it has the same effect if called once or multiple times on the same client. await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : "OpenConnection", asyncOperation : async() => await s_deviceClient.OpenAsync(cancellationToken), shouldExecuteOperation : () => true, logger : _logger, exceptionsToBeIgnored : _exceptionsToBeIgnored, cancellationToken : cancellationToken); _logger.LogDebug($"The client instance has been opened."); // You will need to subscribe to the client callbacks any time the client is initialized. await RetryOperationHelper.RetryTransientExceptionsAsync( operationName : "SubscribeTwinUpdates", asyncOperation : async() => await s_deviceClient.SetDesiredPropertyUpdateCallbackAsync(HandleTwinUpdateNotificationsAsync, cancellationToken), shouldExecuteOperation : () => IsDeviceConnected, logger : _logger, exceptionsToBeIgnored : _exceptionsToBeIgnored, cancellationToken : cancellationToken); _logger.LogDebug("The client has subscribed to desired property update notifications."); } }