public async Task <Option <EdgeHubConfig> > GetConfig() { try { Option <IMessage> twinMessage = await this.twinManager.GetCachedTwinAsync(this.id); var config = twinMessage.FlatMap((message) => { Shared.Twin twin = this.twinMessageConverter.FromMessage(message); if (twin.Properties.Desired.Count > 0) { var desiredProperties = JsonConvert.DeserializeObject <EdgeHubDesiredProperties>(twin.Properties.Desired.ToJson()); return(Option.Some(EdgeHubConfigParser.GetEdgeHubConfig(desiredProperties, this.routeFactory))); } else { return(Option.None <EdgeHubConfig>()); } }); return(config); } catch (Exception e) { Log.LogWarning(HubCoreEventIds.LocalEdgeHubConfig, e, "Failed to get local config"); return(Option.None <EdgeHubConfig>()); } }
// This method updates local state and should be called only after acquiring edgeHubConfigLock async Task <Option <EdgeHubConfig> > GetConfigInternal() { Option <EdgeHubConfig> edgeHubConfig; try { IMessage message = await this.twinManager.GetTwinAsync(this.edgeHubIdentity.Id); Shared.Twin twin = this.twinMessageConverter.FromMessage(message); this.lastDesiredProperties = Option.Some(twin.Properties.Desired); try { var desiredProperties = JsonConvert.DeserializeObject <EdgeHubDesiredProperties>(twin.Properties.Desired.ToJson()); edgeHubConfig = Option.Some(EdgeHubConfigParser.GetEdgeHubConfig(desiredProperties, this.routeFactory)); await this.UpdateReportedProperties(twin.Properties.Desired.Version, new LastDesiredStatus(200, string.Empty)); Events.GetConfigSuccess(); } catch (Exception ex) { await this.UpdateReportedProperties(twin.Properties.Desired.Version, new LastDesiredStatus(400, $"Error while parsing desired properties - {ex.Message}")); throw; } } catch (Exception ex) { edgeHubConfig = Option.None <EdgeHubConfig>(); Events.ErrorGettingEdgeHubConfig(ex); } return(edgeHubConfig); }
async Task UpdateReportedPropertiesWhenTwinStoreHasTwinAsync(string id, TwinCollection reported, bool cloudVerified) { using (await this.twinLock.LockAsync()) { IEntityStore <string, TwinInfo> twinStore = this.TwinStore.Expect(() => new InvalidOperationException("Missing twin store")); await twinStore.Update( id, u => { if (u.Twin == null) { if (!cloudVerified) { ValidateTwinCollectionSize(reported); } var twinProperties = new TwinProperties { Desired = new TwinCollection(), Reported = reported }; var twin = new Shared.Twin(twinProperties); Events.UpdatedCachedReportedProperties(id, reported.Version, cloudVerified); return(new TwinInfo(twin, reported)); } else { string mergedJson = JsonEx.Merge(u.Twin.Properties.Reported, reported, /*treatNullAsDelete*/ true); var mergedReportedProperties = new TwinCollection(mergedJson); if (!cloudVerified) { ValidateTwinCollectionSize(mergedReportedProperties); } u.Twin.Properties.Reported = mergedReportedProperties; Events.UpdatedCachedReportedProperties(id, mergedReportedProperties.Version, cloudVerified); return(u); } }); } }
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(); Shared.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); }
private async Task ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base( Client.TransportType protocol, Func <RegistryManager, string, Task> registryManagerOperation) { var amqpTransportSettings = new AmqpTransportSettings(protocol); var transportSettings = new ITransportSettings[] { amqpTransportSettings }; TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix + $"_{Guid.NewGuid()}", ModulePrefix, Logger).ConfigureAwait(false); ConnectionStatus? status = null; ConnectionStatusChangeReason?statusChangeReason = null; int deviceDisabledReceivedCount = 0; ConnectionStatusChangesHandler statusChangeHandler = (s, r) => { if (r == ConnectionStatusChangeReason.Device_Disabled) { status = s; statusChangeReason = r; deviceDisabledReceivedCount++; } }; using (var moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transportSettings)) { moduleClient.SetConnectionStatusChangesHandler(statusChangeHandler); Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: Created {nameof(ModuleClient)} with moduleId={testModule.Id}"); await moduleClient.OpenAsync().ConfigureAwait(false); // Receiving the module twin should succeed right now. Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: ModuleClient GetTwinAsync."); Shared.Twin twin = await moduleClient.GetTwinAsync().ConfigureAwait(false); Assert.IsNotNull(twin); // Delete/disable the device in IoT hub. using (var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await registryManagerOperation(registryManager, testModule.DeviceId).ConfigureAwait(false); } Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: Completed RegistryManager operation."); // Artificial sleep waiting for the connection status change handler to get triggered. int sleepCount = 50; for (int i = 0; i < sleepCount; i++) { await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false); if (deviceDisabledReceivedCount == 1) { break; } } Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: Asserting connection status change."); Assert.AreEqual(1, deviceDisabledReceivedCount); Assert.AreEqual(ConnectionStatus.Disconnected, status); Assert.AreEqual(ConnectionStatusChangeReason.Device_Disabled, statusChangeReason); } }
public TwinInfo(Shared.Twin twin, TwinCollection reportedPropertiesPatch) { this.Twin = twin; this.ReportedPropertiesPatch = reportedPropertiesPatch; }