public async Task <bool> UpdateDeviceTwinAsync(DeviceServiceModel device)
        {
            Twin azureTwin = await registry.UpdateTwinAsync(device.Id, device.Twin.ToAzureModel(), "*");

            return(azureTwin != null);
        }
 public async Task UpdateTwinAsync(string deviceId, Twin twinPatch, string eTag)
 {
     await this.registry.UpdateTwinAsync(deviceId, twinPatch, eTag);
 }
Example #3
0
 public virtual IActionResult ModulePatchTwin([FromRoute][Required] string connectionId, [FromBody] Twin twin)
 {
     // Replaced impl in merge
     module_glue.SendTwinPatchAsync(connectionId, twin).Wait();
     return(StatusCode(200));
 }
Example #4
0
        private async Task <CloudDTDLSettings> GetCloudDTDLModelSettingsAsync(string deviceId)
        {
            if (string.IsNullOrEmpty(deviceId))
            {
                throw new ArgumentNullException(nameof(deviceId));
            }

            string logPrefix = "getdtdlmodelsettings".BuildLogPrefix();

            _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Getting the DTDL settings of the device.");

            Twin twin = await _deviceClient.GetTwinAsync();

            if (twin == null)
            {
                throw new Exception($"No device twin has been found for the device {deviceId}.");
            }

            _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Device twin loaded.");

            CloudDTDLSettings result = new CloudDTDLSettings {
                DefaultModelId = twin.ModelId
            };

            _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Default DTDL model settings updated.");

            if (twin.Tags != null && twin.Tags.Contains(_TWIN_TAG_SUPPORTED_MODELS_PROPERTY_NAME))
            {
                TwinCollection supportedModels = twin.Tags[_TWIN_TAG_SUPPORTED_MODELS_PROPERTY_NAME];
                if (supportedModels != null)
                {
                    _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Other supported DTDL models loaded.");

                    JArray        jSupportedModels = JArray.Parse(supportedModels.ToJson());
                    string        itemModelId      = string.Empty;
                    DTDLModelType itemModelType;
                    foreach (var jModel in jSupportedModels)
                    {
                        itemModelId   = jModel.Value <string>("modelId");
                        itemModelType = jModel.Value <DTDLModelType>("modelType");
                        if (itemModelId == result.DefaultModelId)
                        {
                            if (result.Models.SingleOrDefault(i => i.ModelId == result.DefaultModelId) != null)
                            {
                                result.Models.Single(i => i.ModelId == result.DefaultModelId).ModelType = itemModelType;
                            }
                            else
                            {
                                result.Models.Add(new DTDLModelItem {
                                    ModelId = itemModelId, ModelType = itemModelType
                                });
                            }
                        }
                        else
                        {
                            result.Models.Add(new DTDLModelItem {
                                ModelId = itemModelId, ModelType = itemModelType
                            });
                        }

                        _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Additional supported DTDL model {itemModelId} of type {itemModelType.ToString()} added.");
                    }
                }
            }

            return(result);
        }
        public async Task RunSampleAsync()
        {
            string jobId = "JOBSAMPLE" + Guid.NewGuid().ToString();

            // The query condition can also be on a single device Id or on a list of device Ids.
            // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language covers
            //   IoT Hub query language in additional detail.
            string query = $"DeviceId IN ['{DeviceId}']";

            var twin = new Twin(DeviceId)
            {
                Tags = new TwinCollection()
            };

            twin.Tags[TestTagName] = TestTagValue;


            // *************************************** Schedule twin job ***************************************
            Console.WriteLine($"Schedule twin job {jobId} for {DeviceId}...");
            JobResponse createJobResponse = await _jobClient
                                            .ScheduleTwinUpdateAsync(
                jobId,
                query,
                twin,
                DateTime.UtcNow,
                (long)TimeSpan.FromMinutes(2).TotalSeconds)
                                            .ConfigureAwait(false);

            Console.WriteLine("Schedule response");
            Console.WriteLine(JsonSerializer.Serialize(createJobResponse, new JsonSerializerOptions {
                WriteIndented = true
            }));
            Console.WriteLine();

            // *************************************** Get all Jobs ***************************************
            IEnumerable <JobResponse> queryResults = await _jobClient.CreateQuery().GetNextAsJobResponseAsync().ConfigureAwait(false);

            List <JobResponse> getJobs = queryResults.ToList();

            Console.WriteLine($"getJobs return {getJobs.Count} result(s)");

            foreach (JobResponse job in getJobs)
            {
                Console.WriteLine(JsonSerializer.Serialize(job, new JsonSerializerOptions {
                    WriteIndented = true
                }));
            }

            Console.WriteLine();

            // *************************************** Check completion ***************************************
            Console.WriteLine("Monitoring jobClient for job completion...");
            JobResponse jobResponse = await _jobClient.GetJobAsync(jobId).ConfigureAwait(false);

            Console.WriteLine("First result");
            Console.WriteLine(JsonSerializer.Serialize(jobResponse, new JsonSerializerOptions {
                WriteIndented = true
            }));

            Console.Write("Waiting for completion ");
            while (jobResponse.Status != JobStatus.Completed)
            {
                Console.Write(". ");
                await Task.Delay(TimeSpan.FromMilliseconds(500));

                jobResponse = await _jobClient.GetJobAsync(jobId).ConfigureAwait(false);
            }

            Console.WriteLine("DONE");

            Console.WriteLine($"Job ends with status {jobResponse.Status}");
        }
Example #6
0
        public async Task When_Getting_Device_Information_From_Twin_Returns_JoinAccept(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            // Create Rxpk
            var rxpk = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var devNonce = ConversionHelper.ByteArrayToString(joinRequest.DevNonce);
            var devAddr  = string.Empty;
            var devEUI   = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI   = simulatedDevice.LoRaDevice.AppEUI;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin);

            // Device twin will be updated
            string afterJoinAppSKey = null;
            string afterJoinNwkSKey = null;
            string afterJoinDevAddr = null;

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey = updatedTwin[TwinProperty.AppSKey];
                afterJoinNwkSKey = updatedTwin[TwinProperty.NwkSKey];
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            var loRaDeviceFactory = new TestLoRaDeviceFactory(this.LoRaDeviceClient.Object);

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var request = this.CreateWaitableRequest(rxpk);

            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(this.PacketForwarder.DownlinkMessages);
            var downlinkMessage = this.PacketForwarder.DownlinkMessages[0];
            var joinAccept      = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr));

            // check that the device is in cache
            Assert.True(memoryCache.TryGetValue <DevEUIToLoRaDeviceDictionary>(afterJoinDevAddr, out var cachedDevices));
            Assert.True(cachedDevices.TryGetValue(devEUI, out var cachedDevice));
            Assert.Equal(afterJoinAppSKey, cachedDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, cachedDevice.NwkSKey);
            Assert.Equal(afterJoinDevAddr, cachedDevice.DevAddr);
            Assert.True(cachedDevice.IsOurDevice);
            if (deviceGatewayID == null)
            {
                Assert.Null(cachedDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, cachedDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, cachedDevice.FCntUp);
            Assert.Equal(0U, cachedDevice.FCntDown);
            Assert.False(cachedDevice.HasFrameCountChanges);
        }
Example #7
0
        public async Task When_Getting_RXDelay_Offset_From_Twin_Returns_JoinAccept_With_Correct_Settings_And_Behaves_Correctly(int rxDelay, uint expectedDelay)
        {
            string deviceGatewayID     = ServerGatewayID;
            var    simulatedDevice     = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var    joinRequest         = simulatedDevice.CreateJoinRequest();
            string afterJoinAppSKey    = null;
            string afterJoinNwkSKey    = null;
            string afterJoinDevAddr    = null;
            int    afterJoinFcntDown   = -1;
            int    afterJoinFcntUp     = -1;
            uint   startingPayloadFcnt = 0;

            // Create Rxpk
            var rxpk = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];

            var devNonce = ConversionHelper.ByteArrayToString(joinRequest.DevNonce);
            var devAddr  = string.Empty;
            var devEUI   = simulatedDevice.LoRaDevice.DeviceID;
            var appEUI   = simulatedDevice.LoRaDevice.AppEUI;

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t))
            .ReturnsAsync(true);

            // C2D message will be checked
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]          = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]          = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]          = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]       = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder]   = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Desired[TwinProperty.RXDelay]         = rxDelay;
            twin.Properties.Desired[TwinProperty.PreferredWindow] = 1;

            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin);

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                afterJoinAppSKey  = updatedTwin[TwinProperty.AppSKey].Value;
                afterJoinNwkSKey  = updatedTwin[TwinProperty.NwkSKey].Value;
                afterJoinDevAddr  = updatedTwin[TwinProperty.DevAddr].Value;
                afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown].Value;
                afterJoinFcntUp   = updatedTwin[TwinProperty.FCntUp].Value;
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));

            var loRaDeviceFactory = new TestLoRaDeviceFactory(this.LoRaDeviceClient.Object);

            var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);

            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);

            var request = this.CreateWaitableRequest(rxpk);

            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(this.PacketForwarder.DownlinkMessages);
            var downlinkMessage = this.PacketForwarder.DownlinkMessages[0];
            var joinAccept      = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            joinAccept.RxDelay.Span.Reverse();
            if (rxDelay > 0 && rxDelay < 16)
            {
                Assert.Equal((int)expectedDelay, (int)joinAccept.RxDelay.Span[0]);
            }
            else
            {
                Assert.Equal(0, (int)joinAccept.RxDelay.Span[0]);
            }

            // Send a message
            simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey;
            simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey;
            simulatedDevice.LoRaDevice.DevAddr = afterJoinDevAddr;

            // sends confirmed message
            var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1);
            var confirmedMessageRxpk    = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0];
            var confirmedRequest        = this.CreateWaitableRequest(confirmedMessageRxpk);

            messageProcessor.DispatchRequest(confirmedRequest);
            Assert.True(await confirmedRequest.WaitCompleteAsync());
            Assert.True(confirmedRequest.ProcessingSucceeded);
            Assert.NotNull(confirmedRequest.ResponseDownlink);
            Assert.NotNull(confirmedRequest.ResponseDownlink.Txpk);
            Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count);
            var downstreamMessage = this.PacketForwarder.DownlinkMessages[1];

            // Message was sent on RX1 with correct delay and with a correct datarate offset
            if (rxDelay > 0 && rxDelay < 16)
            {
                Assert.Equal(expectedDelay * 1000000, downstreamMessage.Txpk.Tmst - confirmedMessageRxpk.Tmst);
            }
            else
            {
                Assert.Equal(expectedDelay * 1000000, downstreamMessage.Txpk.Tmst - confirmedMessageRxpk.Tmst);
            }
        }
        public async Task InitiateSimulationAsync()
        {
            string logPrefix = "system".BuildLogPrefix();

            try
            {
                //Connectivity tests
                //Control if a connection string exists (ideally, stored in TPM/HSM or any secured location.
                //If there is no connection string, check if the DPS settings are provided.
                //If so, provision the device and persist the connection string for upcoming boots.
                if (string.IsNullOrEmpty(ModuleSettings.ConnectionString))
                {
                    ModuleSettings.ConnectionString = await _provisioningService.AddModuleIdentityToDevice(ModuleSettings.ModuleId);

                    if (string.IsNullOrEmpty(ModuleSettings.ConnectionString))
                    {
                        _logger.LogWarning($"{logPrefix}::{ModuleSettings.ArtifactId}::No module connection string has been created.");
                    }
                    else
                    {
                        _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::Module connection string being persisted.");
                        await ConfigurationHelpers.WriteModulesSettings(ModuleSettings, _environmentName);
                    }
                }

                IoTTools.CheckModuleConnectionStringData(ModuleSettings.ConnectionString, _logger);

                // Connect to the IoT hub using the MQTT protocol
                _moduleClient = ModuleClient.CreateFromConnectionString(ModuleSettings.ConnectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt);
                _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::Module client created.");

                if (SimulationSettings.EnableTwinPropertiesDesiredChangesNotifications)
                {
                    await _moduleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChange, null);

                    _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::Twin Desired Properties update callback handler registered.");
                }

                //Configuration
                if (SimulationSettings.EnableC2DDirectMethods)
                {
                    //Register C2D Direct methods handlers
                    await RegisterC2DDirectMethodsHandlersAsync(_moduleClient, ModuleSettings, _logger);
                }

                if (SimulationSettings.EnableC2DMessages)
                {
                    //Start receiving C2D messages

                    ReceiveC2DMessagesAsync(_moduleClient, ModuleSettings, _logger);
                }

                //Messages
                if (SimulationSettings.EnableTelemetryMessages)
                {
                    SendDeviceToCloudMessagesAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, _logger); //interval is a global variable changed by processes
                }
                if (SimulationSettings.EnableErrorMessages)
                {
                    SendDeviceToCloudErrorAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, SimulationSettings.ErrorFrecuency, _logger);
                }

                if (SimulationSettings.EnableCommissioningMessages)
                {
                    SendDeviceToCloudCommissioningAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, SimulationSettings.CommissioningFrecuency, _logger);
                }

                if (SimulationSettings.EnableReadingTwinProperties)
                {
                    //Twins
                    _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::Retrieving twin.");
                    Twin twin = await _moduleClient.GetTwinAsync();

                    if (twin != null)
                    {
                        _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::Device twin: {JsonConvert.SerializeObject(twin, Formatting.Indented)}.");
                    }
                    else
                    {
                        _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::No device twin.");
                    }
                }

                _moduleClient.SetConnectionStatusChangesHandler(new ConnectionStatusChangesHandler(ConnectionStatusChanged));
            }
            catch (ConnectionStringException ex)
            {
                _logger.LogError($"{logPrefix}::{ModuleSettings.ArtifactId}::ConnectionStringException:{ex.Message}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"{logPrefix}::{ModuleSettings.ArtifactId}::{ex.Message}");
            }
        }
Example #9
0
        private Mock <RegistryManager> InitRegistryManager(List <DevAddrCacheInfo> deviceIds, int numberOfDeviceDeltaUpdates = 2)
        {
            List <DevAddrCacheInfo> currentDevAddrContext = new List <DevAddrCacheInfo>();
            List <DevAddrCacheInfo> currentDevices        = deviceIds;
            var  mockRegistryManager = new Mock <RegistryManager>(MockBehavior.Strict);
            bool hasMoreShouldReturn = true;

            var primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));

            mockRegistryManager
            .Setup(x => x.GetDeviceAsync(It.IsAny <string>()))
            .ReturnsAsync((string deviceId) => new Device(deviceId)
            {
                Authentication = new AuthenticationMechanism()
                {
                    SymmetricKey = new SymmetricKey()
                    {
                        PrimaryKey = primaryKey
                    }
                }
            });

            mockRegistryManager
            .Setup(x => x.GetTwinAsync(It.IsNotNull <string>()))
            .ReturnsAsync((string deviceId) => new Twin(deviceId));

            int numberOfDevices = deviceIds.Count;

            // CacheMiss query
            var cacheMissQueryMock = new Mock <IQuery>(MockBehavior.Strict);

            // we only want to run hasmoreresult once
            cacheMissQueryMock
            .Setup(x => x.HasMoreResults)
            .Returns(() =>
            {
                if (hasMoreShouldReturn)
                {
                    hasMoreShouldReturn = false;
                    return(true);
                }

                return(false);
            });

            cacheMissQueryMock
            .Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(() =>
            {
                var devAddressesToConsider = currentDevAddrContext;
                List <Twin> twins          = new List <Twin>();
                foreach (var devaddrItem in devAddressesToConsider)
                {
                    var deviceTwin        = new Twin();
                    deviceTwin.DeviceId   = devaddrItem.DevEUI;
                    deviceTwin.Properties = new TwinProperties()
                    {
                        Desired = new TwinCollection($"{{\"{LoraKeysManagerFacadeConstants.TwinProperty_DevAddr}\": \"{devaddrItem.DevAddr}\", \"{LoraKeysManagerFacadeConstants.TwinProperty_GatewayID}\": \"{devaddrItem.GatewayId}\"}}", $"{{\"$lastUpdated\": \"{devaddrItem.LastUpdatedTwins.ToString(LoraKeysManagerFacadeConstants.RoundTripDateTimeStringFormat)}\"}}"),
                    };

                    twins.Add(deviceTwin);
                }

                return(twins);
            });

            mockRegistryManager
            .Setup(x => x.CreateQuery(It.Is <string>(z => z.Contains("SELECT * FROM devices WHERE properties.desired.DevAddr =")), 100))
            .Returns((string query, int pageSize) =>
            {
                hasMoreShouldReturn   = true;
                currentDevAddrContext = currentDevices.Where(v => v.DevAddr == query.Split('\'')[1]).ToList();
                return(cacheMissQueryMock.Object);
            });

            mockRegistryManager
            .Setup(x => x.CreateQuery(It.Is <string>(z => z.Contains("SELECT * FROM devices WHERE is_defined(properties.desired.AppKey) "))))
            .Returns((string query) =>
            {
                hasMoreShouldReturn   = true;
                currentDevAddrContext = currentDevices;
                return(cacheMissQueryMock.Object);
            });

            mockRegistryManager
            .Setup(x => x.CreateQuery(It.Is <string>(z => z.Contains("SELECT * FROM devices where properties.desired.$metadata.$lastUpdated >="))))
            .Returns((string query) =>
            {
                currentDevAddrContext = currentDevices.Take(numberOfDeviceDeltaUpdates).ToList();
                // reset device count in case HasMoreResult is called more than once
                hasMoreShouldReturn = true;
                return(cacheMissQueryMock.Object);
            });
            return(mockRegistryManager);
        }
        private void OnTwinChangesReceived(AmqpMessage amqpMessage)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, amqpMessage, nameof(OnTwinChangesReceived));
            }

            try
            {
                _receivingAmqpLink.DisposeDelivery(amqpMessage, true, AmqpIotConstants.AcceptedOutcome);
                string correlationId = amqpMessage.Properties?.CorrelationId?.ToString();
                int    status        = GetStatus(amqpMessage);

                Twin           twin           = null;
                TwinCollection twinProperties = null;

                if (status >= 400)
                {
                    // Handle failures
                    if (correlationId.StartsWith(AmqpTwinMessageType.Get.ToString(), StringComparison.OrdinalIgnoreCase) ||
                        correlationId.StartsWith(AmqpTwinMessageType.Patch.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        string error = null;
                        using var reader = new StreamReader(amqpMessage.BodyStream, System.Text.Encoding.UTF8);
                        error            = reader.ReadToEnd();

                        // Retry for Http status code request timeout, Too many requests and server errors
                        var exception = new IotHubException(error, status >= 500 || status == 429 || status == 408);
                        _onTwinMessageReceived.Invoke(null, correlationId, null, exception);
                    }
                }
                else
                {
                    if (correlationId == null)
                    {
                        // Here we are getting desired property update notifications and want to handle it first
                        using var reader = new StreamReader(amqpMessage.BodyStream, System.Text.Encoding.UTF8);
                        string patch = reader.ReadToEnd();
                        twinProperties = JsonConvert.DeserializeObject <TwinCollection>(patch);
                    }
                    else if (correlationId.StartsWith(AmqpTwinMessageType.Get.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        // This a response of a GET TWIN so return (set) the full twin
                        using var reader = new StreamReader(amqpMessage.BodyStream, System.Text.Encoding.UTF8);
                        string         body       = reader.ReadToEnd();
                        TwinProperties properties = JsonConvert.DeserializeObject <TwinProperties>(body);
                        twin = new Twin(properties);
                    }
                    else if (correlationId.StartsWith(AmqpTwinMessageType.Patch.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        // This can be used to coorelate success response with updating reported properties
                        // However currently we do not have it as request response style implementation
                        if (Logging.IsEnabled)
                        {
                            Logging.Info("Updated twin reported properties successfully", nameof(OnTwinChangesReceived));
                        }
                    }
                    else if (correlationId.StartsWith(AmqpTwinMessageType.Put.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        // This is an acknowledgement received from service for subscribing to desired property updates
                        if (Logging.IsEnabled)
                        {
                            Logging.Info("Subscribed for twin successfully", nameof(OnTwinChangesReceived));
                        }
                    }
                    else
                    {
                        // This shouldn't happen
                        if (Logging.IsEnabled)
                        {
                            Logging.Info("Received a correlation Id for Twin operation that does not match Get, Patch or Put request", nameof(OnTwinChangesReceived));
                        }
                    }
                    _onTwinMessageReceived.Invoke(twin, correlationId, twinProperties, null);
                }
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, amqpMessage, nameof(OnTwinChangesReceived));
                }
            }
        }
Example #11
0
        public async Task SaveDeviceTwinOperationAsync(string eventData, ILogger log, string tenant, string deviceId, string operationType, TelemetryTimestamp timeStamp, EventHubHelper eventHubHelper)
        {
            try
            {
                string  deviceTwin     = eventData;
                Twin    twin           = null;
                JObject deviceTwinJson = new JObject();
                deviceTwinJson.Add(DeviceTelemetryKeyConstants.DeviceId, deviceId.ToString());
                deviceTwinJson.Add(DeviceTelemetryKeyConstants.TimeStamp, timeStamp.DateTime);
                deviceTwinJson.Add(DeviceTelemetryKeyConstants.TimeReceived, timeStamp.EpochTimestamp);
                deviceTwinJson.Add(DeviceTelemetryKeyConstants.EventOpType, operationType);
                deviceTwinJson.Add(DeviceTelemetryKeyConstants.IsDeleted, false);

                if (operationType.ToString().Equals("createDeviceIdentity"))
                {
                    deviceTwinJson.Add(DeviceTelemetryKeyConstants.DeviceCreatedDate, timeStamp.DateTime);

                    try
                    {
                        twin = await TenantConnectionHelper.GetRegistry(Convert.ToString(tenant)).GetTwinAsync(deviceId.ToString());

                        deviceTwin = twin.ToJson();
                    }
                    catch (Exception e)
                    {
                        log.LogError($"Unable to fetch DeviceId: {deviceId} device twin from iothub of tenant: {tenant}.", e);
                    }
                }
                else
                {
                    JObject         previousTwin = null;
                    KustoOperations kustoClient  = await KustoOperations.GetClientAsync();

                    string kustoQuery     = $"DeviceTwin | where DeviceId == \"{deviceId}\" | summarize arg_max(TimeStamp, *) by DeviceId | where IsDeleted == false";
                    var    deviceTwinList = await kustoClient.QueryAsync <DeviceTwinModel>($"IoT-{tenant}", kustoQuery, null);

                    DeviceTwinModel preDeviceTwin = deviceTwinList.FirstOrDefault();

                    if (preDeviceTwin != null)
                    {
                        previousTwin = preDeviceTwin.Twin;
                        deviceTwinJson.Add(DeviceTelemetryKeyConstants.DeviceCreatedDate, preDeviceTwin.DeviceCreatedDate);
                    }
                    else
                    {
                        deviceTwinJson.Add(DeviceTelemetryKeyConstants.DeviceCreatedDate, default(DateTime)); // Set Device Created Date to Default if twin is not present in storage.

                        try
                        {
                            twin = await TenantConnectionHelper.GetRegistry(Convert.ToString(tenant)).GetTwinAsync(deviceId.ToString());

                            if (twin != null)
                            {
                                previousTwin = JObject.Parse(twin.ToJson());
                            }
                        }
                        catch (Exception e)
                        {
                            log.LogError($"Unable to fetch DeviceId: {deviceId} device twin from iothub of tenant: {tenant}.", e);
                        }
                    }

                    switch (operationType)
                    {
                    case "deviceConnected":
                        if (preDeviceTwin != null)
                        {
                            previousTwin["connectionState"]  = "Connected";
                            previousTwin["lastActivityTime"] = timeStamp.DateTime;
                        }

                        break;

                    case "deviceDisconnected":
                        if (preDeviceTwin != null)
                        {
                            previousTwin["connectionState"]  = "Disconnected";
                            previousTwin["lastActivityTime"] = timeStamp.DateTime;
                        }

                        break;

                    case "updateTwin":
                        if (preDeviceTwin != null)
                        {
                            JObject twinFragment = JObject.Parse(eventData);
                            previousTwin = previousTwin.UpdateJson(twinFragment);
                        }
                        else
                        {
                            previousTwin = JObject.Parse(eventData);
                        }

                        break;

                    case "deleteDeviceIdentity":
                        deviceTwinJson[DeviceTelemetryKeyConstants.IsDeleted] = true;
                        break;

                    default:
                        break;
                    }

                    deviceTwin = previousTwin?.ToString();
                }

                deviceTwinJson.Add(DeviceTelemetryKeyConstants.Data, deviceTwin);

                // Save the Device Twin Data to EventHub for further processing
                var byteMessage         = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(deviceTwinJson));
                var eventDeviceTwinData = new Azure.Messaging.EventHubs.EventData(byteMessage);
                eventDeviceTwinData.Properties.Add("deviceid", deviceId.ToString());

                await eventHubHelper.SendMessageToEventHub($"{tenant}-devicetwin", new Azure.Messaging.EventHubs.EventData[] { eventDeviceTwinData });
            }
            catch (Exception exception)
            {
                throw new ApplicationException($"Save Device Twin operation failed: {exception}, operation type: {operationType}, tenant: {tenant}, deviceId {deviceId}");
            }
        }
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            TimeSpan      imageUpdateDue;
            TimeSpan      imageUpdatePeriod;

            // Log the Application build, OS version information etc.
            LoggingFields startupInformation = new LoggingFields();

            startupInformation.AddString("Timezone", TimeZoneSettings.CurrentTimeZoneDisplayName);
            startupInformation.AddString("OSVersion", Environment.OSVersion.VersionString);
            startupInformation.AddString("MachineName", Environment.MachineName);

            // This is from the application manifest
            Package        package   = Package.Current;
            PackageId      packageId = package.Id;
            PackageVersion version   = packageId.Version;

            startupInformation.AddString("ApplicationVersion", string.Format($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"));

            this.logging.LogEvent("Application startup", startupInformation);

            #region Configuration file settings load and creation if not present
            try
            {
                // see if the configuration file is present if not copy minimal sample one from application directory
                if (localFolder.TryGetItemAsync(ConfigurationFilename).AsTask().Result == null)
                {
                    StorageFile templateConfigurationfile = Package.Current.InstalledLocation.GetFileAsync(ConfigurationFilename).AsTask().Result;
                    templateConfigurationfile.CopyAsync(localFolder, ConfigurationFilename).AsTask();

                    this.logging.LogMessage("JSON configuration file missing, templated created", LoggingLevel.Warning);
                    return;
                }

                LoggingFields  connnectionInformation = new LoggingFields();
                IConfiguration configuration          = new ConfigurationBuilder().AddJsonFile(Path.Combine(localFolder.Path, ConfigurationFilename), false, true).Build();

                this.azureIoTHubConnectionString = configuration.GetSection("AzureIoTHubConnectionString").Value;
                connnectionInformation.AddString("AzureIoTHubConnectionString", this.azureIoTHubConnectionString);

                this.transportType = (TransportType)Enum.Parse(typeof(TransportType), configuration.GetSection("TransportType").Value);
                connnectionInformation.AddString("TransportType", this.transportType.ToString());

                this.logging.LogEvent("Connection settings", connnectionInformation);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("JSON configuration file load or AzureIoT Hub connection settings missing/invalid" + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region AzureIoT Hub connection string creation
            try
            {
                this.azureIoTHubClient = DeviceClient.CreateFromConnectionString(this.azureIoTHubConnectionString, this.transportType);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("AzureIOT Hub DeviceClient.CreateFromConnectionString failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Report device and application properties to AzureIoT Hub
            try
            {
                TwinCollection reportedProperties = new TwinCollection();

                // This is from the OS
                reportedProperties["Timezone"]    = TimeZoneSettings.CurrentTimeZoneDisplayName;
                reportedProperties["OSVersion"]   = Environment.OSVersion.VersionString;
                reportedProperties["MachineName"] = Environment.MachineName;

                reportedProperties["ApplicationDisplayName"] = package.DisplayName;
                reportedProperties["ApplicationName"]        = packageId.Name;
                reportedProperties["ApplicationVersion"]     = string.Format($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}");

                // Unique identifier from the hardware
                SystemIdentificationInfo systemIdentificationInfo = SystemIdentification.GetSystemIdForPublisher();
                using (DataReader reader = DataReader.FromBuffer(systemIdentificationInfo.Id))
                {
                    byte[] bytes = new byte[systemIdentificationInfo.Id.Length];
                    reader.ReadBytes(bytes);
                    reportedProperties["SystemId"] = BitConverter.ToString(bytes);
                }

                this.azureIoTHubClient.UpdateReportedPropertiesAsync(reportedProperties).Wait();
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client UpdateReportedPropertiesAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Retrieve device twin settings
            try
            {
                LoggingFields configurationInformation = new LoggingFields();

                Twin deviceTwin = this.azureIoTHubClient.GetTwinAsync().GetAwaiter().GetResult();

                if (!deviceTwin.Properties.Desired.Contains("AzureImageFilenameLatestFormat"))
                {
                    this.logging.LogMessage("DeviceTwin.Properties AzureImageFilenameLatestFormat setting missing", LoggingLevel.Warning);
                    return;
                }
                this.azureStorageimageFilenameLatestFormat = deviceTwin.Properties.Desired["AzureImageFilenameLatestFormat"].Value;
                configurationInformation.AddString("AzureImageFilenameLatestFormat", this.azureStorageimageFilenameLatestFormat);

                if (!deviceTwin.Properties.Desired.Contains("AzureImageFilenameHistoryFormat"))
                {
                    this.logging.LogMessage("DeviceTwin.Properties AzureImageFilenameHistoryFormat setting missing", LoggingLevel.Warning);
                    return;
                }
                this.azureStorageImageFilenameHistoryFormat = deviceTwin.Properties.Desired["AzureImageFilenameHistoryFormat"].Value;
                configurationInformation.AddString("AzureImageFilenameHistoryFormat", this.azureStorageImageFilenameHistoryFormat);

                if (!deviceTwin.Properties.Desired.Contains("ImageUpdateDue") || !TimeSpan.TryParse(deviceTwin.Properties.Desired["ImageUpdateDue"].Value.ToString(), out imageUpdateDue))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ImageUpdateDue setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddTimeSpan("ImageUpdateDue", imageUpdateDue);

                if (!deviceTwin.Properties.Desired.Contains("ImageUpdatePeriod") || !TimeSpan.TryParse(deviceTwin.Properties.Desired["ImageUpdatePeriod"].Value.ToString(), out imageUpdatePeriod))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ImageUpdatePeriod setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddTimeSpan("ImageUpdatePeriod", imageUpdatePeriod);

                this.logging.LogEvent("Configuration settings", configurationInformation);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client GetTwinAsync failed or property missing/invalid" + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Initialise the camera hardware
            try
            {
                this.mediaCapture = new MediaCapture();
                this.mediaCapture.InitializeAsync().AsTask().Wait();
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("mediaCapture.InitializeAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Wire up command handler for image capture request
            try
            {
                this.azureIoTHubClient.SetMethodHandlerAsync("ImageCapture", this.ImageUpdateHandler, null);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client ImageCapture SetMethodHandlerAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Wire up command handler for device reboot request
            try
            {
                this.azureIoTHubClient.SetMethodHandlerAsync("DeviceReboot", this.DeviceRebootAsync, null);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client DeviceReboot SetMethodHandlerAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            this.imageUpdatetimer = new Timer(this.ImageUpdateTimerCallback, null, imageUpdateDue, imageUpdatePeriod);

            this.logging.LogEvent("Application startup completed");

            // enable task to continue running in background
            this.backgroundTaskDeferral = taskInstance.GetDeferral();
        }
        public async Task <DeviceTwinServiceModel> GetDeviceTwinAsync(string id)
        {
            Twin content = await registry.GetTwinAsync(id);

            return(new DeviceTwinServiceModel(content));
        }
        public async Task <bool> UpdateDeviceTwinAsync(string deviceId, string twinPatch)
        {
            Twin azureTwin = await registry.UpdateTwinAsync(deviceId, twinPatch, "*");

            return(azureTwin != null);
        }
Example #15
0
 public static bool IsDesiredPropertyEmpty(this Twin that, string name)
 {
     return(string.IsNullOrWhiteSpace(DesiredProperty(that, name)));
 }
Example #16
0
        public async Task InitiateSimulationAsync()
        {
            string logPrefix = "system".BuildLogPrefix();

            IoTTools.CheckModuleConnectionStringData(ModuleSettings.ConnectionString, _logger);

            // Connect to the IoT hub using the MQTT protocol

            _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::Module client created.");

            if (SimulationSettings.EnableTwinPropertiesDesiredChangesNotifications)
            {
                await _moduleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChange, null);

                _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::Twin Desired Properties update callback handler registered.");
            }

            //Configuration
            if (SimulationSettings.EnableC2DDirectMethods)
            {
                //Register C2D Direct methods handlers
                await RegisterC2DDirectMethodsHandlersAsync(_moduleClient, ModuleSettings, _logger);
            }

            if (SimulationSettings.EnableC2DMessages)
            {
                //Start receiving C2D messages

                ReceiveC2DMessagesAsync(_moduleClient, ModuleSettings, _logger);
            }

            //Messages
            if (SimulationSettings.EnableTelemetryMessages)
            {
                SendDeviceToCloudMessagesAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, _logger); //interval is a global variable changed by processes
            }
            if (SimulationSettings.EnableErrorMessages)
            {
                SendDeviceToCloudErrorAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, SimulationSettings.ErrorFrecuency, _logger);
            }

            if (SimulationSettings.EnableCommissioningMessages)
            {
                SendDeviceToCloudCommissioningAsync(_moduleClient, ModuleSettings.DeviceId, ModuleSettings.ModuleId, SimulationSettings.CommissioningFrecuency, _logger);
            }

            if (SimulationSettings.EnableReadingTwinProperties)
            {
                //Twins
                _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::Retrieving twin.");
                Twin twin = await _moduleClient.GetTwinAsync();

                if (twin != null)
                {
                    _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::Device twin: {JsonConvert.SerializeObject(twin, Formatting.Indented)}.");
                }
                else
                {
                    _logger.LogDebug($"{logPrefix}::{ModuleSettings.ArtifactId}::INITIALIZATION::No device twin.");
                }
            }

            _moduleClient.SetConnectionStatusChangesHandler(new ConnectionStatusChangesHandler(ConnectionStatusChanged));
        }
Example #17
0
 public static PSDeviceTwin ToPSDeviceTwin(Twin deviceTwin)
 {
     return(IotHubUtils.ConvertObject <Twin, PSDeviceTwin>(deviceTwin));
 }
Example #18
0
        static async Task <int> MainAsync()
        {
            Console.WriteLine("SimulatedTemperatureSensor Main() started.");

            IConfiguration configuration = new ConfigurationBuilder()
                                           .SetBasePath(Directory.GetCurrentDirectory())
                                           .AddJsonFile("config/appsettings.json", optional: true)
                                           .AddEnvironmentVariables()
                                           .Build();

            messageDelay = configuration.GetValue("MessageDelay", TimeSpan.FromSeconds(5));
            int messageCount        = configuration.GetValue(MessageCountConfigKey, 500);
            var simulatorParameters = new SimulatorParameters
            {
                MachineTempMin     = configuration.GetValue <double>("machineTempMin", 21),
                MachineTempMax     = configuration.GetValue <double>("machineTempMax", 100),
                MachinePressureMin = configuration.GetValue <double>("machinePressureMin", 1),
                MachinePressureMax = configuration.GetValue <double>("machinePressureMax", 10),
                AmbientTemp        = configuration.GetValue <double>("ambientTemp", 21),
                HumidityPercent    = configuration.GetValue("ambientHumidity", 25)
            };

            Console.WriteLine(
                $"Initializing simulated temperature sensor to send {(SendUnlimitedMessages(messageCount) ? "unlimited" : messageCount.ToString())} "
                + $"messages, at an interval of {messageDelay.TotalSeconds} seconds.\n"
                + $"To change this, set the environment variable {MessageCountConfigKey} to the number of messages that should be sent (set it to -1 to send unlimited messages).");

            TransportType transportType = configuration.GetValue("ClientTransportType", TransportType.Amqp_Tcp_Only);

            ModuleClient moduleClient = await CreateModuleClientAsync(
                transportType,
                DefaultTimeoutErrorDetectionStrategy,
                DefaultTransientRetryStrategy);

            await moduleClient.OpenAsync();

            await moduleClient.SetMethodHandlerAsync("reset", ResetMethod, null);

            (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), null);

            Twin currentTwinProperties = await moduleClient.GetTwinAsync();

            if (currentTwinProperties.Properties.Desired.Contains(SendIntervalConfigKey))
            {
                messageDelay = TimeSpan.FromSeconds((int)currentTwinProperties.Properties.Desired[SendIntervalConfigKey]);
            }

            if (currentTwinProperties.Properties.Desired.Contains(SendDataConfigKey))
            {
                sendData = (bool)currentTwinProperties.Properties.Desired[SendDataConfigKey];
                if (!sendData)
                {
                    Console.WriteLine("Sending data disabled. Change twin configuration to start sending again.");
                }
            }

            ModuleClient userContext = moduleClient;
            await moduleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertiesUpdated, userContext);

            await moduleClient.SetInputMessageHandlerAsync("control", ControlMessageHandle, userContext);

            await SendEvents(moduleClient, messageCount, simulatorParameters, cts);

            await cts.Token.WhenCanceled();

            completed.Set();
            handler.ForEach(h => GC.KeepAlive(h));
            Console.WriteLine("SimulatedTemperatureSensor Main() finished.");
            return(0);
        }
Example #19
0
        public async Task When_Join_With_Custom_Join_Update_Old_Desired_Properties()
        {
            var    beforeJoinValues = 2;
            var    afterJoinValues  = 3;
            int    reportedBeforeJoinRx1DROffsetValue = 0;
            int    reportedBeforeJoinRx2DRValue       = 0;
            int    reportedBeforeJoinRxDelayValue     = 0;
            string deviceGatewayID   = ServerGatewayID;
            var    simulatedDevice   = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            string afterJoinAppSKey  = null;
            string afterJoinNwkSKey  = null;
            string afterJoinDevAddr  = null;
            int    afterJoinFcntDown = -1;
            int    afterJoinFcntUp   = -1;
            var    devAddr           = string.Empty;
            var    devEUI            = simulatedDevice.LoRaDevice.DeviceID;
            var    appEUI            = simulatedDevice.LoRaDevice.AppEUI;

            // message will be sent
            var sentTelemetry = new List <LoRaDeviceTelemetry>();

            this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t))
            .ReturnsAsync(true);

            // C2D message will be checked
            this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI]        = devEUI;
            twin.Properties.Desired[TwinProperty.AppEUI]        = simulatedDevice.LoRaDevice.AppEUI;
            twin.Properties.Desired[TwinProperty.AppKey]        = simulatedDevice.LoRaDevice.AppKey;
            twin.Properties.Desired[TwinProperty.GatewayID]     = deviceGatewayID;
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            twin.Properties.Desired[TwinProperty.RX2DataRate]   = afterJoinValues;
            twin.Properties.Desired[TwinProperty.RX1DROffset]   = afterJoinValues;
            twin.Properties.Desired[TwinProperty.RXDelay]       = afterJoinValues;
            this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin);

            this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>()))
            .Callback <TwinCollection>((updatedTwin) =>
            {
                if (updatedTwin.Contains(TwinProperty.AppSKey))
                {
                    afterJoinAppSKey  = updatedTwin[TwinProperty.AppSKey].Value;
                    afterJoinNwkSKey  = updatedTwin[TwinProperty.NwkSKey].Value;
                    afterJoinDevAddr  = updatedTwin[TwinProperty.DevAddr].Value;
                    afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown].Value;
                    afterJoinFcntUp   = updatedTwin[TwinProperty.FCntUp].Value;
                }
                else
                {
                    reportedBeforeJoinRx1DROffsetValue = updatedTwin[TwinProperty.RX1DROffset].Value;
                    reportedBeforeJoinRx2DRValue       = updatedTwin[TwinProperty.RX2DataRate].Value;
                    reportedBeforeJoinRxDelayValue     = updatedTwin[TwinProperty.RXDelay].Value;
                }
            })
            .ReturnsAsync(true);

            // create a state before the join
            var startingTwin = new TwinCollection();

            startingTwin[TwinProperty.RX2DataRate] = beforeJoinValues;
            startingTwin[TwinProperty.RX1DROffset] = beforeJoinValues;
            startingTwin[TwinProperty.RXDelay]     = beforeJoinValues;
            await this.LoRaDeviceClient.Object.UpdateReportedPropertiesAsync(startingTwin);

            var memoryCache       = new MemoryCache(new MemoryCacheOptions());
            var deviceRegistry    = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory);
            var loRaDeviceFactory = new TestLoRaDeviceFactory(this.LoRaDeviceClient.Object);
            // Send to message processor
            var messageProcessor = new MessageDispatcher(
                this.ServerConfiguration,
                deviceRegistry,
                this.FrameCounterUpdateStrategyProvider);
            var joinRequest = simulatedDevice.CreateJoinRequest();
            // Create Rxpk
            var rxpk     = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0];
            var devNonce = ConversionHelper.ByteArrayToString(joinRequest.DevNonce);

            // Lora device api will be search by devices with matching deveui,
            this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList()));
            var request = this.CreateWaitableRequest(rxpk);

            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            twin.Properties.Desired[TwinProperty.RX2DataRate] = 3;
            await Task.Delay(TimeSpan.FromMilliseconds(10));

            var downlinkMessage = this.PacketForwarder.DownlinkMessages[0];
            var joinAccept      = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey);

            joinAccept.DlSettings.Span.Reverse();
            Assert.Equal(afterJoinValues, joinAccept.Rx2Dr);
            Assert.Equal(afterJoinValues, joinAccept.Rx1DrOffset);
            Assert.Equal(beforeJoinValues, reportedBeforeJoinRx1DROffsetValue);
            Assert.Equal(beforeJoinValues, reportedBeforeJoinRx2DRValue);
            Assert.Equal(afterJoinValues, (int)joinAccept.RxDelay.Span[0]);
            Assert.Equal(beforeJoinValues, reportedBeforeJoinRxDelayValue);
        }
        public async Task <HttpResponseMessage> CreateEdgeDeviceImp(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ExecutionContext context)
        {
            // parse query parameter
            var queryStrings = req.GetQueryParameterDictionary();

            queryStrings.TryGetValue("deviceName", out string deviceName);
            queryStrings.TryGetValue("publishingUserName", out string publishingUserName);
            queryStrings.TryGetValue("publishingPassword", out string publishingPassword);
            queryStrings.TryGetValue("region", out string region);
            queryStrings.TryGetValue("resetPin", out string resetPin);
            queryStrings.TryGetValue("spiSpeed", out string spiSpeed);
            queryStrings.TryGetValue("spiDev", out string spiDev);

            bool.TryParse(Environment.GetEnvironmentVariable("DEPLOY_DEVICE"), out bool deployEndDevice);

            // Get function facade key
            var    base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{publishingUserName}:{publishingPassword}"));
            var    apiUrl     = new Uri($"https://{Environment.GetEnvironmentVariable("WEBSITE_CONTENTSHARE")}.scm.azurewebsites.net/api");
            var    siteUrl    = new Uri($"https://{Environment.GetEnvironmentVariable("WEBSITE_CONTENTSHARE")}.azurewebsites.net");
            string jwt;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", $"Basic {base64Auth}");
                var result = client.GetAsync($"{apiUrl}/functions/admin/token").Result;
                jwt = result.Content.ReadAsStringAsync().Result.Trim('"'); // get  JWT for call funtion key
            }

            string facadeKey = string.Empty;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + jwt);

                string  jsonResult = client.GetAsync($"{siteUrl}/admin/host/keys").Result.Content.ReadAsStringAsync().Result;
                dynamic resObject  = JsonConvert.DeserializeObject(jsonResult);
                facadeKey = resObject.keys[0].value;
            }

            Device edgeGatewayDevice = new Device(deviceName);

            edgeGatewayDevice.Capabilities = new DeviceCapabilities()
            {
                IotEdge = true
            };

            try
            {
                await this.registryManager.AddDeviceAsync(edgeGatewayDevice);

                string deviceConfigurationUrl = Environment.GetEnvironmentVariable("DEVICE_CONFIG_LOCATION");
                string json = null;

                // todo correct
                using (WebClient wc = new WebClient())
                {
                    json = wc.DownloadString(deviceConfigurationUrl);
                }

                json = ReplaceJsonWithCorrectValues(region, resetPin, json, spiSpeed, spiDev);

                ConfigurationContent spec = JsonConvert.DeserializeObject <ConfigurationContent>(json);
                await this.registryManager.AddModuleAsync(new Module(deviceName, "LoRaWanNetworkSrvModule"));

                await this.registryManager.ApplyConfigurationContentOnDeviceAsync(deviceName, spec);

                Twin twin = new Twin();
                twin.Properties.Desired = new TwinCollection(@"{FacadeServerUrl:'" + string.Format("https://{0}.azurewebsites.net/api/", GetEnvironmentVariable("FACADE_HOST_NAME")) + "',FacadeAuthCode: " +
                                                             "'" + facadeKey + "'}");
                var remoteTwin = await this.registryManager.GetTwinAsync(deviceName);

                await this.registryManager.UpdateTwinAsync(deviceName, "LoRaWanNetworkSrvModule", twin, remoteTwin.ETag);

                // This section will get deployed ONLY if the user selected the "deploy end device" options.
                // Information in this if clause, is for demo purpose only and should not be used for productive workloads.
                if (deployEndDevice)
                {
                    var otaaDevice = new Device(OtaaDeviceId);

                    await this.registryManager.AddDeviceAsync(otaaDevice);

                    var otaaEndTwin = new Twin();
                    otaaEndTwin.Properties.Desired = new TwinCollection(@"{AppEUI:'BE7A0000000014E2',AppKey:'8AFE71A145B253E49C3031AD068277A1',GatewayID:'',SensorDecoder:'DecoderValueSensor'}");
                    var otaaRemoteTwin = await this.registryManager.GetTwinAsync(OtaaDeviceId);

                    await this.registryManager.UpdateTwinAsync(OtaaDeviceId, otaaEndTwin, otaaRemoteTwin.ETag);

                    var abpDevice = new Device(AbpDeviceId);
                    await this.registryManager.AddDeviceAsync(abpDevice);

                    var abpTwin = new Twin();
                    abpTwin.Properties.Desired = new TwinCollection(@"{AppSKey:'2B7E151628AED2A6ABF7158809CF4F3C',NwkSKey:'3B7E151628AED2A6ABF7158809CF4F3C',GatewayID:'',DevAddr:'0228B1B1',SensorDecoder:'DecoderValueSensor'}");
                    var abpRemoteTwin = await this.registryManager.GetTwinAsync(AbpDeviceId);

                    await this.registryManager.UpdateTwinAsync(AbpDeviceId, abpTwin, abpRemoteTwin.ETag);
                }
            }
            catch (Exception)
            {
                // In case of an exception in device provisioning we want to make sure that we return a proper template if our devices are successfullycreated
                var edgeGateway = await this.registryManager.GetDeviceAsync(deviceName);

                if (edgeGateway == null)
                {
                    return(PrepareResponse(HttpStatusCode.Conflict));
                }

                if (deployEndDevice)
                {
                    var abpDevice = await this.registryManager.GetDeviceAsync(AbpDeviceId);

                    var otaaDevice = await this.registryManager.GetDeviceAsync(OtaaDeviceId);

                    if (abpDevice == null || otaaDevice == null)
                    {
                        return(PrepareResponse(HttpStatusCode.Conflict));
                    }
                }

                return(PrepareResponse(HttpStatusCode.OK));
            }

            return(PrepareResponse(HttpStatusCode.OK));
        }
Example #21
0
        public async Task InitiateSimulationAsync()
        {
            string logPrefix = "system".BuildLogPrefix();

            try
            {
                IoTTools.CheckDeviceConnectionStringData(_deviceSettings.ConnectionString, _logger);

                // Connect to the IoT hub using the MQTT protocol
                if (!string.IsNullOrEmpty(_deviceSettings.DefaultModelId))
                {
                    _deviceClient = DeviceClient.CreateFromConnectionString(_deviceSettings.ConnectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt, new ClientOptions {
                        ModelId = _deviceSettings.DefaultModelId
                    });
                    _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Device client created. ModelId:{_deviceSettings.DefaultModelId}");
                }
                else
                {
                    _deviceClient = DeviceClient.CreateFromConnectionString(_deviceSettings.ConnectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt);
                    _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Device client created.");
                }

                if (_simulationSettings.EnableTwinPropertiesDesiredChangesNotifications)
                {
                    await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChange, null);

                    _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::Twin Desired Properties update callback handler registered.");
                }

                //Configuration
                if (_simulationSettings.EnableC2DDirectMethods)
                {
                    //Register C2D Direct methods handlers
                    await RegisterC2DDirectMethodsHandlersAsync();
                }

                if (_simulationSettings.EnableC2DMessages)
                {
                    //Start receiving C2D messages
                    ReceiveC2DMessagesAsync();
                }

                //Messages
                if (_simulationSettings.EnableLatencyTests)
                {
                    SendDeviceToCloudLatencyTestAsync(_deviceId, _simulationSettings.LatencyTestsFrecuency);
                }

                if (_simulationSettings.EnableTelemetryMessages)
                {
                    SendDeviceToCloudMessagesAsync(_deviceId); //interval is a global variable changed by processes
                }
                if (_simulationSettings.EnableReadingTwinProperties)
                {
                    //Twins
                    _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::INITIALIZATION::Retrieving twin.");
                    Twin twin = await _deviceClient.GetTwinAsync();

                    if (twin != null)
                    {
                        _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::INITIALIZATION::Device twin: {JsonConvert.SerializeObject(twin, Formatting.Indented)}.");
                    }
                    else
                    {
                        _logger.LogDebug($"{logPrefix}::{_deviceSettings.ArtifactId}::INITIALIZATION::No device twin.");
                    }
                }

                if (_simulationSettings.EnableFileUpload)
                {
                    throw new NotImplementedException("File upload feature has not been implemented yet.");
                }

                _deviceClient.SetConnectionStatusChangesHandler(new ConnectionStatusChangesHandler(ConnectionStatusChanged));
            }
            catch (Exception ex)
            {
                _logger.LogError($"{logPrefix}::{_deviceSettings.ArtifactId}::ERROR::InitiateSimulationAsync:{ex.Message}.");
            }
        }
        private async Task Twin_DeviceDesiredPropertyUpdateRecovery(Client.TransportType transport, string faultType, string reason, int delayInSec)
        {
            var tcs       = new TaskCompletionSource <bool>();
            var propName  = Guid.NewGuid().ToString();
            var propValue = Guid.NewGuid().ToString();

            TestDevice testDevice = await TestDevice.GetTestDeviceAsync(DevicePrefix).ConfigureAwait(false);

            RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

            var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport);

            ConnectionStatus?            lastConnectionStatus             = null;
            ConnectionStatusChangeReason?lastConnectionStatusChangeReason = null;
            int setConnectionStatusChangesHandlerCount = 0;
            var tcsConnected    = new TaskCompletionSource <bool>();
            var tcsDisconnected = new TaskCompletionSource <bool>();

            deviceClient.SetConnectionStatusChangesHandler((status, statusChangeReason) =>
            {
                _log.WriteLine("Connection Changed to {0} because {1}", status, statusChangeReason);

                if (status == ConnectionStatus.Disconnected_Retrying)
                {
                    tcsDisconnected.TrySetResult(true);
                    Assert.AreEqual(ConnectionStatusChangeReason.No_Network, statusChangeReason);
                }
                else if (status == ConnectionStatus.Connected)
                {
                    tcsConnected.TrySetResult(true);
                }

                lastConnectionStatus             = status;
                lastConnectionStatusChangeReason = statusChangeReason;
                setConnectionStatusChangesHandlerCount++;
            });

            await deviceClient.SetDesiredPropertyUpdateCallbackAsync((patch, context) =>
            {
                return(Task.Run(() =>
                {
                    try
                    {
                        Assert.AreEqual(patch[propName].ToString(), propValue);
                    }
                    catch (Exception e)
                    {
                        tcs.SetException(e);
                    }
                    finally
                    {
                        tcs.SetResult(true);
                    }
                }));
            }, null).ConfigureAwait(false);

            // assert on successful connection
            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(1000).ConfigureAwait(false);
            }), tcsConnected.Task).ConfigureAwait(false);

            Assert.IsTrue(tcsConnected.Task.IsCompleted, "Initial connection failed");
            if (transport != Client.TransportType.Http1)
            {
                Assert.AreEqual(1, setConnectionStatusChangesHandlerCount);
                Assert.AreEqual(ConnectionStatus.Connected, lastConnectionStatus);
                Assert.AreEqual(ConnectionStatusChangeReason.Connection_Ok, lastConnectionStatusChangeReason);
            }

            var twinPatch = new Twin();

            twinPatch.Properties.Desired[propName] = propValue;
            await registryManager.UpdateTwinAsync(testDevice.Id, twinPatch, "*").ConfigureAwait(false);

            await tcs.Task.ConfigureAwait(false);

            // send error command
            await deviceClient.SendEventAsync(FaultInjection.ComposeErrorInjectionProperties(faultType, reason, delayInSec)).ConfigureAwait(false);

            // reset ConnectionStatusChangesHandler data
            setConnectionStatusChangesHandlerCount = 0;
            tcsConnected    = new TaskCompletionSource <bool>();
            tcsDisconnected = new TaskCompletionSource <bool>();

            // wait for disconnection
            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
            }), tcsDisconnected.Task).ConfigureAwait(false);

            Assert.IsTrue(tcsDisconnected.Task.IsCompleted, "Error injection did not interrupt the device");

            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(TimeSpan.FromSeconds(MaximumRecoveryTimeSeconds)).ConfigureAwait(false);
                return(Task.FromResult(true));
            }), tcsConnected.Task).ConfigureAwait(false);

            Assert.IsTrue(tcsConnected.Task.IsCompleted, "Recovery connection failed");

            tcs       = new TaskCompletionSource <bool>();
            twinPatch = new Twin();
            twinPatch.Properties.Desired[propName] = propValue;
            await registryManager.UpdateTwinAsync(testDevice.Id, twinPatch, "*").ConfigureAwait(false);

            await tcs.Task.ConfigureAwait(false);

            await deviceClient.CloseAsync().ConfigureAwait(false);

            await registryManager.CloseAsync().ConfigureAwait(false);
        }
Example #23
0
        public static async System.Threading.Tasks.Task <HttpResponseMessage> RunAsync([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, TraceWriter log, ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                         .SetBasePath(context.FunctionAppDirectory)
                         .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                         .AddEnvironmentVariables()
                         .Build();
            string          connectionString       = config.GetConnectionString("IoTHubConnectionString");
            string          deviceConfigurationUrl = Environment.GetEnvironmentVariable("DEVICE_CONFIG_LOCATION");
            RegistryManager manager = RegistryManager.CreateFromConnectionString(connectionString);
            // parse query parameter
            var    queryStrings = req.GetQueryParameterDictionary();
            string deviceName   = "";
            string facadeKey    = "";

            queryStrings.TryGetValue("deviceName", out deviceName);
            queryStrings.TryGetValue("facadeKey", out facadeKey);

            Device edgeGatewayDevice = new Device(deviceName);

            edgeGatewayDevice.Capabilities = new DeviceCapabilities()
            {
                IotEdge = true
            };
            await manager.AddDeviceAsync(edgeGatewayDevice);

            string json = "";

            //todo correct
            using (WebClient wc = new WebClient())
            {
                json = wc.DownloadString(deviceConfigurationUrl);
            }
            ConfigurationContent spec = JsonConvert.DeserializeObject <ConfigurationContent>(json);
            await manager.AddModuleAsync(new Module(deviceName, "lorawannetworksrvmodule"));

            await manager.ApplyConfigurationContentOnDeviceAsync(deviceName, spec);

            Twin twin = new Twin();

            twin.Properties.Desired = new TwinCollection(@"{FacadeServerUrl:'" + String.Format("https://{0}.azurewebsites.net/api/", GetEnvironmentVariable("FACADE_HOST_NAME")) + "',FacadeAuthCode: " +
                                                         "'" + facadeKey + "'}");
            var remoteTwin = await manager.GetTwinAsync(deviceName);

            await manager.UpdateTwinAsync(deviceName, "lorawannetworksrvmodule", twin, remoteTwin.ETag);

            bool deployEndDevice = false;

            Boolean.TryParse(Environment.GetEnvironmentVariable("DEPLOY_DEVICE"), out deployEndDevice);

            if (deployEndDevice)
            {
                Device endDevice = new Device("47AAC86800430028");
                await manager.AddDeviceAsync(endDevice);

                Twin endTwin = new Twin();
                endTwin.Tags = new TwinCollection(@"{AppEUI:'BE7A0000000014E2',AppKey:'8AFE71A145B253E49C3031AD068277A1',GatewayID:''," +
                                                  "SensorDecoder:'DecoderRotatorySensor'}");

                var endRemoteTwin = await manager.GetTwinAsync(deviceName);

                await manager.UpdateTwinAsync("47AAC86800430028", endTwin, endRemoteTwin.ETag);
            }


            var template = @"{'$schema': 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#', 'contentVersion': '1.0.0.0', 'parameters': {}, 'variables': {}, 'resources': []}";
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);

            Console.WriteLine(template);

            response.Content = new StringContent(template, System.Text.Encoding.UTF8, "application/json");

            return(response);
        }
Example #24
0
        internal async Task <TwinInfo> GetTwinInfoWhenCloudOnlineAsync(string id, ICloudProxy cp, bool sendDesiredPropertyUpdate)
        {
            TwinCollection diff = null;
            // Used for returning value to caller
            TwinInfo cached;

            using (await this.twinLock.LockAsync())
            {
                IMessage twinMessage = await cp.GetTwinAsync();

                Twin cloudTwin = this.twinConverter.FromMessage(twinMessage);
                Events.GotTwinFromCloudSuccess(id, cloudTwin.Properties.Desired.Version, cloudTwin.Properties.Reported.Version);
                var newTwin = new TwinInfo(cloudTwin, null);
                cached = newTwin;

                IEntityStore <string, TwinInfo> twinStore = this.TwinStore.Expect(() => new InvalidOperationException("Missing twin store"));

                await twinStore.PutOrUpdate(
                    id,
                    newTwin,
                    t =>
                {
                    // If the new twin is more recent than the cached twin, update the cached copy.
                    // If not, reject the cloud twin
                    if (t.Twin == null ||
                        cloudTwin.Properties.Desired.Version > t.Twin.Properties.Desired.Version ||
                        cloudTwin.Properties.Reported.Version > t.Twin.Properties.Reported.Version)
                    {
                        if (t.Twin != null)
                        {
                            Events.UpdateCachedTwin(
                                id,
                                t.Twin.Properties.Desired.Version,
                                cloudTwin.Properties.Desired.Version,
                                t.Twin.Properties.Reported.Version,
                                cloudTwin.Properties.Reported.Version);
                            cached = new TwinInfo(cloudTwin, t.ReportedPropertiesPatch);
                            // If the device is subscribed to desired property updates and we are refreshing twin as a result
                            // of a connection reset or desired property update, send a patch to the downstream device
                            if (sendDesiredPropertyUpdate)
                            {
                                Option <IReadOnlyDictionary <DeviceSubscription, bool> > subscriptions = this.connectionManager.GetSubscriptions(id);
                                subscriptions.ForEach(
                                    s =>
                                {
                                    if (s.TryGetValue(DeviceSubscription.DesiredPropertyUpdates, out bool hasDesiredPropertyUpdatesSubscription) &&
                                        hasDesiredPropertyUpdatesSubscription)
                                    {
                                        Events.SendDesiredPropertyUpdateToSubscriber(
                                            id,
                                            t.Twin.Properties.Desired.Version,
                                            cloudTwin.Properties.Desired.Version);
                                        diff = new TwinCollection(JsonEx.Diff(t.Twin.Properties.Desired, cloudTwin.Properties.Desired));
                                    }
                                });
                            }
                        }
                    }
                    else
                    {
                        Events.PreserveCachedTwin(
                            id,
                            t.Twin.Properties.Desired.Version,
                            cloudTwin.Properties.Desired.Version,
                            t.Twin.Properties.Reported.Version,
                            cloudTwin.Properties.Reported.Version);
                        cached = t;
                    }

                    return(cached);
                });
            }

            if ((diff != null) && (diff.Count != 0))
            {
                Events.SendDiffToDeviceProxy(diff.ToString(), id);
                IMessage message = this.twinCollectionConverter.ToMessage(diff);
                await this.SendDesiredPropertiesToDeviceProxy(id, message);
            }

            return(cached);
        }
Example #25
0
        public void BackwardCompatTest()
        {
            // Arrange
            var twinReportedProperties = new TwinCollection();

            twinReportedProperties["P1"] = "v1";
            var twinDesiredProperties = new TwinCollection();

            twinDesiredProperties["P2"] = "v2";
            var twin = new Twin(new TwinProperties {
                Desired = twinDesiredProperties, Reported = twinReportedProperties
            });

            var reportedProperties = new TwinCollection();

            reportedProperties["r1"] = "rv1";

            // Act
            var    twinInfo1        = new TwinInfo(twin, reportedProperties);
            string json1            = twinInfo1.ToJson();
            var    twinStoreEntity1 = json1.FromJson <TwinStoreEntity>();

            // Assert
            Assert.NotNull(twinStoreEntity1);
            Assert.True(twinStoreEntity1.Twin.HasValue);
            Assert.Equal("v1", (string)twinStoreEntity1.Twin.OrDefault().Properties.Reported["P1"]);
            Assert.Equal("v2", (string)twinStoreEntity1.Twin.OrDefault().Properties.Desired["P2"]);
            Assert.True(twinStoreEntity1.ReportedPropertiesPatch.HasValue);
            Assert.Equal("rv1", (string)twinStoreEntity1.ReportedPropertiesPatch.OrDefault()["r1"]);

            // Act
            var    twinInfo2        = new TwinInfo(twin, null);
            string json2            = twinInfo2.ToJson();
            var    twinStoreEntity2 = json2.FromJson <TwinStoreEntity>();

            // Assert
            Assert.NotNull(twinStoreEntity2);
            Assert.True(twinStoreEntity2.Twin.HasValue);
            Assert.Equal("v1", (string)twinStoreEntity2.Twin.OrDefault().Properties.Reported["P1"]);
            Assert.Equal("v2", (string)twinStoreEntity2.Twin.OrDefault().Properties.Desired["P2"]);
            Assert.False(twinStoreEntity2.ReportedPropertiesPatch.HasValue);

            // Act
            var    twinInfo3        = new TwinInfo(null, reportedProperties);
            string json3            = twinInfo3.ToJson();
            var    twinStoreEntity3 = json3.FromJson <TwinStoreEntity>();

            // Assert
            Assert.NotNull(twinStoreEntity3);
            Assert.False(twinStoreEntity3.Twin.HasValue);
            Assert.True(twinStoreEntity3.ReportedPropertiesPatch.HasValue);
            Assert.Equal("rv1", (string)twinStoreEntity3.ReportedPropertiesPatch.OrDefault()["r1"]);

            // Act
            var    twinInfo4        = new TwinInfo(null, null);
            string json4            = twinInfo4.ToJson();
            var    twinStoreEntity4 = json4.FromJson <TwinStoreEntity>();

            // Assert
            Assert.NotNull(twinStoreEntity4);
            Assert.False(twinStoreEntity4.Twin.HasValue);
            Assert.False(twinStoreEntity4.ReportedPropertiesPatch.HasValue);
        }
Example #26
0
        private async Task ConfirmDeviceWorksAfterReprovisioning(
            DeviceRegistrationResult result,
            Client.IAuthenticationMethod auth,
            Client.TransportType transportProtocol,
            ReprovisionPolicy reprovisionPolicy,
            bool transportProtocolSupportsTwinOperations)
        {
            using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol))
            {
                Logger.Trace("DeviceClient OpenAsync.");
                await iotClient.OpenAsync().ConfigureAwait(false);
                Logger.Trace("DeviceClient SendEventAsync.");

                using var testMessage = new Client.Message(Encoding.UTF8.GetBytes("TestMessage"));
                await iotClient.SendEventAsync(testMessage).ConfigureAwait(false);

                // Twin can be configured to revert back to default twin when provisioned, or to keep twin
                // from previous hub's records.
                if (transportProtocolSupportsTwinOperations)
                {
                    Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false);

                    // Reprovision
                    if (reprovisionPolicy.UpdateHubAssignment)
                    {
                        // Migrate data
                        if (reprovisionPolicy.MigrateDeviceData)
                        {
                            // The reprovisioned twin should have an entry for the reprorted property updated previously in the test
                            // as a part of ConfirmRegisteredDeviceWorks.
                            // On device creation the twin reported property version starts at 1. For this scenario the reported property update
                            // operation should increment the version to 2.
                            twin.Properties.Reported.Count.Should().Be(1);
                            twin.Properties.Reported.Version.Should().Be(2);
                            result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataMigrated);
                        }
                        // Reset to initial configuration
                        else
                        {
                            // The reprovisioned twin should not have an entry for the reprorted property updated previously in the test
                            // as a part of ConfirmRegisteredDeviceWorks.
                            // On device creation the twin reported property version starts at 1.
                            twin.Properties.Reported.Count.Should().Be(0);
                            twin.Properties.Reported.Version.Should().Be(1);
                            result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataReset);
                        }
                    }
                    // Do not reprovision
                    else
                    {
                        // The reprovisioned twin should have an entry for the reprorted property updated previously in the test
                        // as a part of ConfirmRegisteredDeviceWorks.
                        // On device creation the twin reported property version starts at 1. For this scenario the reported property update
                        // operation should increment the version to 2.
                        twin.Properties.Reported.Count.Should().Be(1);
                        twin.Properties.Reported.Version.Should().Be(2);
                        result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.InitialAssignment);
                    }
                }

                Logger.Trace("DeviceClient CloseAsync.");
                await iotClient.CloseAsync().ConfigureAwait(false);
            }
        }
Example #27
0
        private static async Task SendTelemetry(CancellationTokenSource cts) {
            while (!cts.Token.IsCancellationRequested) {
                // Flag used when we need to pause telemetry send due to needdd reindexing
                bool breakForReindexing = false;


                RecEdgeMessage recEdgeMessage = new RecEdgeMessage() { 
                    deviceId = ioTEdgeDeviceId,
                    observations = new List<Observation>()
                };

                // Iterate over all previously indexed sensors
                foreach(KeyValuePair<HueDeviceDescription, HueClientDescription> sensorDescriptionEntry in hueSensors)
                {
                    // Extract sensor and client metadata
                    HueDeviceDescription sensorDescription = sensorDescriptionEntry.Key;
                    HueClientDescription clientDescription = sensorDescriptionEntry.Value;
                    LocalHueClient client = clientDescription.client;

                    // Query for current sensor state
                    Sensor sensor = await client.GetSensorAsync(sensorDescription.id);

                    // Verify that the indexed sensor is still correct; if not, 
                    // some change to the underlying Hue environment has occured; 
                    // if so, return and trigger reindexing
                    if (sensor.UniqueId != sensorDescription.uniqueId) {
                        breakForReindexing = true;
                        break;
                    }
                    
                    // Extract sensor state; set up a new observation using either 
                    // the last updated value from sensor state, or if that is missing, right now
                    SensorState sensorState = sensor.State;
                    Observation sensorObservation = new Observation();
                    if (sensorState.Lastupdated.HasValue) {
                        sensorObservation.observationTime = sensorState.Lastupdated.Value;
                    }
                    else {
                        sensorObservation.observationTime = DateTime.Now;
                    }
                    
                    // Defined sensor URI based on local bridge IP and sensor unique id
                    // TODO: Make sensor URI prefix configurable through Module Twin
                    sensorObservation.sensorId = new Uri($"http://{clientDescription.ip}/{sensor.UniqueId}");

                    // Depending on the type of sensor, set the quantity kind and extract values accordingly
                    // TODO: Figure out quantity kind for boolean, i.e., ZLLPresence
                    // TODO: Connect this switch statement to the sensorType enum for future maintainability
                    switch (sensor.Type) {
                        case "ZLLTemperature":
                            if (sensorState.Temperature.HasValue) {
                                sensorObservation.numericValue = ((double)sensorState.Temperature.Value / (double)100);
                                sensorObservation.quantityKind = new Uri("http://qudt.org/vocab/quantitykind/Temperature");
                            }
                            break;
                        case "ZLLLightLevel":
                            if (sensorState.LightLevel.HasValue) {
                                // Parse measurement data (expressed on log scale) into Lux; 
                                // see https://developers.meethue.com/develop/hue-api/supported-devices/#supportred-sensors for details
                                double luxValue = Math.Pow(10,(((double)sensorState.LightLevel.Value-1.0)/10000.0));
                                sensorObservation.numericValue = luxValue;
                                sensorObservation.quantityKind = new Uri("http://qudt.org/vocab/quantitykind/Illuminance");
                            }
                            break;
                        case "ZLLPresence":
                            if (sensorState.Presence.HasValue) {
                                sensorObservation.booleanValue = sensorState.Presence.Value;
                            }
                            break;
                    }

                    recEdgeMessage.observations.Add(sensorObservation);
                }
                
                if (breakForReindexing) {
                    // If we broke out of last loop due to sensor index inconsistency, reindex before proceeding in next SendTelemetry iteration
                    Twin moduleTwin = await ioTHubModuleClient.GetTwinAsync();
                    await IndexHueBridges(moduleTwin.Properties.Desired);
                    breakForReindexing = false;
                }
                else {
                    // Otherwise, serialize the REC Edge Message into JSON, built an IoT Edge message, and pass it to the IoT Hub
                    JsonSerializerSettings serializerSettings = new JsonSerializerSettings {
                        NullValueHandling = NullValueHandling.Ignore
                    };
                    string recEdgeMessageJson = JsonConvert.SerializeObject(recEdgeMessage, Formatting.None, serializerSettings);
                    Message telemetryMessage = new Message(Encoding.ASCII.GetBytes(recEdgeMessageJson)) {
                        ContentType = "application/json",
                        ContentEncoding = "utf-8"
                    };
                    Console.WriteLine($"Sending message: {recEdgeMessageJson}");
                    await ioTHubModuleClient.SendEventAsync(telemetryMessage);
                }
                
                await Task.Delay(telemetryInterval, cts.Token);
            }
        }
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            TimeSpan      imageUpdateDue;
            TimeSpan      imageUpdatePeriod;

            this.logging.LogEvent("Application starting");

            // Log the Application build, OS version information etc.
            LoggingFields startupInformation = new LoggingFields();

            startupInformation.AddString("Timezone", TimeZoneSettings.CurrentTimeZoneDisplayName);
            startupInformation.AddString("OSVersion", Environment.OSVersion.VersionString);
            startupInformation.AddString("MachineName", Environment.MachineName);

            // This is from the application manifest
            Package        package   = Package.Current;
            PackageId      packageId = package.Id;
            PackageVersion version   = packageId.Version;

            startupInformation.AddString("ApplicationVersion", string.Format($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"));

            try
            {
                // see if the configuration file is present if not copy minimal sample one from application directory
                if (localFolder.TryGetItemAsync(ConfigurationFilename).AsTask().Result == null)
                {
                    StorageFile templateConfigurationfile = Package.Current.InstalledLocation.GetFileAsync(ConfigurationFilename).AsTask().Result;
                    templateConfigurationfile.CopyAsync(localFolder, ConfigurationFilename).AsTask();
                }

                IConfiguration configuration = new ConfigurationBuilder().AddJsonFile(Path.Combine(localFolder.Path, ConfigurationFilename), false, true).Build();

                this.interruptPinNumber = int.Parse(configuration.GetSection("InterruptPinNumber").Value);
                startupInformation.AddInt32("Interrupt pin", this.interruptPinNumber);

                this.interruptTriggerOn = (GpioPinEdge)Enum.Parse(typeof(GpioPinEdge), configuration.GetSection("interruptTriggerOn").Value);
                startupInformation.AddString("Interrupt Trigger on", this.interruptTriggerOn.ToString());

                this.displayPinNumber = int.Parse(configuration.GetSection("DisplayPinNumber").Value);
                startupInformation.AddInt32("Display pin", this.interruptPinNumber);

                this.azureIoTHubConnectionString = configuration.GetSection("AzureIoTHubConnectionString").Value;
                startupInformation.AddString("AzureIoTHubConnectionString", this.azureIoTHubConnectionString);

                this.transportType = (TransportType)Enum.Parse(typeof(TransportType), configuration.GetSection("TransportType").Value);
                startupInformation.AddString("TransportType", this.transportType.ToString());
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("JSON configuration file load or settings retrieval failed " + ex.Message, LoggingLevel.Error);
                return;
            }

            #region AzureIoT Hub connection string creation
            try
            {
                this.azureIoTHubClient = DeviceClient.CreateFromConnectionString(this.azureIoTHubConnectionString, this.transportType);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("AzureIOT Hub DeviceClient.CreateFromConnectionString failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Report device and application properties to AzureIoT Hub
            try
            {
                TwinCollection reportedProperties = new TwinCollection();

                // This is from the OS
                reportedProperties["Timezone"]    = TimeZoneSettings.CurrentTimeZoneDisplayName;
                reportedProperties["OSVersion"]   = Environment.OSVersion.VersionString;
                reportedProperties["MachineName"] = Environment.MachineName;

                reportedProperties["ApplicationDisplayName"] = package.DisplayName;
                reportedProperties["ApplicationName"]        = packageId.Name;
                reportedProperties["ApplicationVersion"]     = string.Format($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}");

                // Unique identifier from the hardware
                SystemIdentificationInfo systemIdentificationInfo = SystemIdentification.GetSystemIdForPublisher();
                using (DataReader reader = DataReader.FromBuffer(systemIdentificationInfo.Id))
                {
                    byte[] bytes = new byte[systemIdentificationInfo.Id.Length];
                    reader.ReadBytes(bytes);
                    reportedProperties["SystemId"] = BitConverter.ToString(bytes);
                }

                this.azureIoTHubClient.UpdateReportedPropertiesAsync(reportedProperties).Wait();
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client UpdateReportedPropertiesAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Retrieve device twin settings
            try
            {
                LoggingFields configurationInformation = new LoggingFields();

                Twin deviceTwin = this.azureIoTHubClient.GetTwinAsync().GetAwaiter().GetResult();

                if (!deviceTwin.Properties.Desired.Contains("ImageUpdateDue") || !TimeSpan.TryParse(deviceTwin.Properties.Desired["ImageUpdateDue"].value.ToString(), out imageUpdateDue))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ImageUpdateDue setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddTimeSpan("ImageUpdateDue", imageUpdateDue);

                if (!deviceTwin.Properties.Desired.Contains("ImageUpdatePeriod") || !TimeSpan.TryParse(deviceTwin.Properties.Desired["ImageUpdatePeriod"].value.ToString(), out imageUpdatePeriod))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ImageUpdatePeriod setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddTimeSpan("ImageUpdatePeriod", imageUpdatePeriod);

                if (!deviceTwin.Properties.Desired.Contains("ModelType") || (!Enum.TryParse(deviceTwin.Properties.Desired["ModelType"].value.ToString(), out modelType)))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ModelType setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddString("ModelType", modelType.ToString());

                if (!deviceTwin.Properties.Desired.Contains("ModelPublishedName") || (string.IsNullOrWhiteSpace(deviceTwin.Properties.Desired["ModelPublishedName"].value.ToString())))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ModelPublishedName setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                modelPublishedName = deviceTwin.Properties.Desired["ModelPublishedName"].value.ToString();
                configurationInformation.AddString("ModelPublishedName", modelPublishedName);

                if (!deviceTwin.Properties.Desired.Contains("ProjectID") || (!Guid.TryParse(deviceTwin.Properties.Desired["ProjectID"].value.ToString(), out projectId)))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ProjectId setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddGuid("ProjectID", projectId);

                if (!deviceTwin.Properties.Desired.Contains("ProbabilityThreshold") || (!Double.TryParse(deviceTwin.Properties.Desired["ProbabilityThreshold"].value.ToString(), out probabilityThreshold)))
                {
                    this.logging.LogMessage("DeviceTwin.Properties ProbabilityThreshold setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddDouble("ProbabilityThreshold", probabilityThreshold);

                if (!deviceTwin.Properties.Desired.Contains("AzureCognitiveServicesEndpoint") || (string.IsNullOrWhiteSpace(deviceTwin.Properties.Desired["AzureCognitiveServicesEndpoint"].value.ToString())))
                {
                    this.logging.LogMessage("DeviceTwin.Properties AzureCognitiveServicesEndpoint setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                azureCognitiveServicesEndpoint = deviceTwin.Properties.Desired["AzureCognitiveServicesEndpoint"].value.ToString();
                configurationInformation.AddString("AzureCognitiveServicesEndpoint", modelPublishedName);

                if (!deviceTwin.Properties.Desired.Contains("AzureCognitiveServicesSubscriptionKey") || (string.IsNullOrWhiteSpace(deviceTwin.Properties.Desired["AzureCognitiveServicesSubscriptionKey"].value.ToString())))
                {
                    this.logging.LogMessage("DeviceTwin.Properties AzureCognitiveServicesSubscriptionKey setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                azureCognitiveServicesSubscriptionKey = deviceTwin.Properties.Desired["AzureCognitiveServicesSubscriptionKey"].value.ToString();
                configurationInformation.AddString("AzureCognitiveServicesSubscriptionKey", azureCognitiveServicesSubscriptionKey);

                if (!deviceTwin.Properties.Desired.Contains("DebounceTimeout") || !TimeSpan.TryParse(deviceTwin.Properties.Desired["DebounceTimeout"].value.ToString(), out debounceTimeout))
                {
                    this.logging.LogMessage("DeviceTwin.Properties DebounceTimeout setting missing or invalid format", LoggingLevel.Warning);
                    return;
                }
                configurationInformation.AddTimeSpan("DebounceTimeout", debounceTimeout);

                this.logging.LogEvent("Configuration settings", configurationInformation);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client GetTwinAsync failed or property missing/invalid" + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            try
            {
                this.customVisionClient = new CustomVisionPredictionClient(new System.Net.Http.DelegatingHandler[] { })
                {
                    ApiKey   = this.azureCognitiveServicesSubscriptionKey,
                    Endpoint = this.azureCognitiveServicesEndpoint,
                };
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure Cognitive Services Custom Vision Client configuration failed " + ex.Message, LoggingLevel.Error);
                return;
            }

            try
            {
                this.mediaCapture = new MediaCapture();
                this.mediaCapture.InitializeAsync().AsTask().Wait();
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Camera configuration failed " + ex.Message, LoggingLevel.Error);
                return;
            }

            this.displayOffTimer = new Timer(this.TimerCallback, null, Timeout.Infinite, Timeout.Infinite);

            #region Wire up interupt handler for image capture request
            if (this.interruptPinNumber != 0)
            {
                try
                {
                    GpioController gpioController = GpioController.GetDefault();
                    this.interruptGpioPin = gpioController.OpenPin(this.interruptPinNumber);
                    this.interruptGpioPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
                    this.interruptGpioPin.ValueChanged += this.InterruptGpioPin_ValueChanged;

                    this.displayGpioPin = gpioController.OpenPin(this.displayPinNumber);
                    this.displayGpioPin.SetDriveMode(GpioPinDriveMode.Output);
                    this.displayGpioPin.Write(GpioPinValue.Low);
                }
                catch (Exception ex)
                {
                    this.logging.LogMessage("Digital input configuration failed " + ex.Message, LoggingLevel.Error);
                    return;
                }
            }
            #endregion

            #region Wire up command handler for image capture request
            try
            {
                this.azureIoTHubClient.SetMethodHandlerAsync("ImageCapture", this.ImageUpdateHandler, null);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client ImageCapture SetMethodHandlerAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            #region Wire up command handler for device reboot request
            try
            {
                this.azureIoTHubClient.SetMethodHandlerAsync("DeviceReboot", this.DeviceRebootAsync, null);
            }
            catch (Exception ex)
            {
                this.logging.LogMessage("Azure IoT Hub client DeviceReboot SetMethodHandlerAsync failed " + ex.Message, LoggingLevel.Error);
                return;
            }
            #endregion

            if ((imageUpdateDue != TimeSpan.MinValue) || (imageUpdatePeriod != TimeSpan.MinValue))
            {
                this.imageUpdatetimer = new Timer(this.ImageUpdateTimerCallback, null, imageUpdateDue, imageUpdatePeriod);
            }

            this.logging.LogEvent("Application started", startupInformation);

            // enable task to continue running in background
            this.backgroundTaskDeferral = taskInstance.GetDeferral();
        }
Example #29
0
        private async Task _Twin_DeviceDesiredPropertyUpdateRecovery(Client.TransportType transport, string faultType, string reason, int delayInSec)
        {
            var tcs       = new TaskCompletionSource <bool>();
            var propName  = Guid.NewGuid().ToString();
            var propValue = Guid.NewGuid().ToString();

            Tuple <string, string> deviceInfo = TestUtil.CreateDevice(DevicePrefix, hostName, registryManager);
            var deviceClient = DeviceClient.CreateFromConnectionString(deviceInfo.Item2, transport);

            ConnectionStatus?            lastConnectionStatus             = null;
            ConnectionStatusChangeReason?lastConnectionStatusChangeReason = null;
            int setConnectionStatusChangesHandlerCount = 0;
            var tcsConnected    = new TaskCompletionSource <bool>();
            var tcsDisconnected = new TaskCompletionSource <bool>();

            deviceClient.SetConnectionStatusChangesHandler((status, statusChangeReason) =>
            {
                if (status == ConnectionStatus.Disconnected_Retrying)
                {
                    tcsDisconnected.TrySetResult(true);
                    Assert.AreEqual(ConnectionStatusChangeReason.No_Network, statusChangeReason);
                }
                else if (status == ConnectionStatus.Connected)
                {
                    tcsConnected.TrySetResult(true);
                }

                lastConnectionStatus             = status;
                lastConnectionStatusChangeReason = statusChangeReason;
                setConnectionStatusChangesHandlerCount++;
            });

            await deviceClient.SetDesiredPropertyUpdateCallbackAsync((patch, context) =>
            {
                return(Task.Run(() =>
                {
                    try
                    {
                        Assert.AreEqual(patch[propName].ToString(), propValue);
                    }
                    catch (Exception e)
                    {
                        tcs.SetException(e);
                    }
                    finally
                    {
                        tcs.SetResult(true);
                    }
                }));
            }, null);

            // assert on successfuly connection
            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(1000);
            }), tcsConnected.Task);

            Assert.IsTrue(tcsConnected.Task.IsCompleted, "Initial connection failed");
            if (transport != Client.TransportType.Http1)
            {
                Assert.AreEqual(1, setConnectionStatusChangesHandlerCount);
                Assert.AreEqual(ConnectionStatus.Connected, lastConnectionStatus);
                Assert.AreEqual(ConnectionStatusChangeReason.Connection_Ok, lastConnectionStatusChangeReason);
            }

            var twinPatch = new Twin();

            twinPatch.Properties.Desired[propName] = propValue;
            await registryManager.UpdateTwinAsync(deviceInfo.Item1, twinPatch, "*");

            await tcs.Task;

            // send error command
            await deviceClient.SendEventAsync(TestUtil.ComposeErrorInjectionProperties(faultType, reason, delayInSec));

            // reset ConnectionStatusChangesHandler data
            setConnectionStatusChangesHandlerCount = 0;
            tcsConnected    = new TaskCompletionSource <bool>();
            tcsDisconnected = new TaskCompletionSource <bool>();

            // wait for disconnection
            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(TimeSpan.FromSeconds(10));
            }), tcsDisconnected.Task);

            Assert.IsTrue(tcsDisconnected.Task.IsCompleted, "Error injection did not interrupt the device");

            // allow max 30s for connection recovery
            await Task.WhenAny(
                Task.Run(async() =>
            {
                await Task.Delay(TimeSpan.FromSeconds(10));
                return(Task.FromResult(true));
            }), tcsConnected.Task);

            Assert.IsTrue(tcsConnected.Task.IsCompleted, "Recovery connection failed");

            tcs       = new TaskCompletionSource <bool>();
            twinPatch = new Twin();
            twinPatch.Properties.Desired[propName] = propValue;
            await registryManager.UpdateTwinAsync(deviceInfo.Item1, twinPatch, "*");

            await tcs.Task;

            await deviceClient.CloseAsync();

            await TestUtil.RemoveDeviceAsync(deviceInfo.Item1, registryManager);
        }
        public static void AddSupportedMethodsFromReportedProperty(DeviceModel device, Twin twin)
        {
            foreach (var pair in twin.Properties.Reported.AsEnumerableFlatten().Where(pair => IsSupportedMethodProperty(pair.Key)))
            {
                try
                {
                    var command = Command.Deserialize(
                        pair.Key.Substring(SupportedMethodsKey.Length + 1),
                        pair.Value.Value.Value.ToString());

                    if (!device.Commands.Any(c => c.Name == command.Name && c.DeliveryType == DeliveryType.Method))
                    {
                        device.Commands.Add(command);
                    }
                }
                catch (Exception ex)
                {
                    Trace.TraceError(FormattableString.Invariant($"Exception raised while deserializing method {pair.Key}: {ex}"));
                    continue;
                }
            }
        }