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);
        }
Exemple #3
0
        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);
                    }
                });
            }
        }
Exemple #4
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();

                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);
            }
        }
Exemple #6
0
 public TwinInfo(Shared.Twin twin, TwinCollection reportedPropertiesPatch)
 {
     this.Twin = twin;
     this.ReportedPropertiesPatch = reportedPropertiesPatch;
 }