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); }
public virtual IActionResult ModulePatchTwin([FromRoute][Required] string connectionId, [FromBody] Twin twin) { // Replaced impl in merge module_glue.SendTwinPatchAsync(connectionId, twin).Wait(); return(StatusCode(200)); }
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}"); }
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); }
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}"); } }
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)); } } }
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); }
public static bool IsDesiredPropertyEmpty(this Twin that, string name) { return(string.IsNullOrWhiteSpace(DesiredProperty(that, name))); }
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)); }
public static PSDeviceTwin ToPSDeviceTwin(Twin deviceTwin) { return(IotHubUtils.ConvertObject <Twin, PSDeviceTwin>(deviceTwin)); }
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); }
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)); }
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); }
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); }
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); }
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); }
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); } }
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(); }
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; } } }