Exemplo n.º 1
0
        internal async Task DiffAndReportAsync(AgentState currentState, AgentState agentState)
        {
            try
            {
                JToken  currentJson  = JToken.FromObject(currentState);
                JToken  reportedJson = JToken.FromObject(agentState);
                JObject patch        = JsonEx.Diff(reportedJson, currentJson);

                if (patch.HasValues)
                {
                    // send reported props
                    await this.edgeAgentConnection.UpdateReportedPropertiesAsync(new TwinCollection(patch.ToString()));

                    // update our cached copy of reported properties
                    this.SetReported(currentState);

                    Events.UpdatedReportedProperties();
                }
            }
            catch (Exception e)
            {
                Events.UpdateReportedPropertiesFailed(e);

                // Swallow the exception as the device could be offline. The reported properties will get updated
                // during the next reconcile when we have connectivity.
            }
        }
Exemplo n.º 2
0
        async Task SyncTwinAndSendDesiredPropertyUpdates(string id, Twin storeTwin)
        {
            Option <Twin> twinOption = await this.cloudSync.GetTwin(id);

            await twinOption.ForEachAsync(
                async cloudTwin =>
            {
                Events.UpdatingTwinOnDeviceConnect(id);
                await this.StoreTwinInStore(id, cloudTwin);

                JObject diffPatch = JsonEx.Diff(JToken.FromObject(storeTwin.Properties.Desired), JToken.FromObject(cloudTwin.Properties.Desired));
                if (diffPatch != null && diffPatch.HasValues)
                {
                    var patch             = new TwinCollection(diffPatch.ToString());
                    IMessage patchMessage = this.twinCollectionConverter.ToMessage(patch);
                    await this.SendPatchToDevice(id, patchMessage);
                }
            });
        }
Exemplo n.º 3
0
        async Task SyncTwinAndSendDesiredPropertyUpdates(string id, Twin storeTwin)
        {
            Option <Twin> twinOption = await this.cloudSync.GetTwin(id);

            await twinOption.ForEachAsync(
                async cloudTwin =>
            {
                Events.UpdatingTwinOnDeviceConnect(id);
                await this.StoreTwinInStore(id, cloudTwin);

                string diffPatch = JsonEx.Diff(storeTwin.Properties.Desired, cloudTwin.Properties.Desired);
                if (!string.IsNullOrWhiteSpace(diffPatch))
                {
                    var patch             = new TwinCollection(diffPatch);
                    IMessage patchMessage = this.twinCollectionConverter.ToMessage(patch);
                    await this.SendPatchToDevice(id, patchMessage);
                }
            });
        }
Exemplo n.º 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();

                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);
        }
Exemplo n.º 5
0
        public void TestDiffAllCases()
        {
            // Arrange
            var baseline = new
            {
                name = new
                {
                    level0 = "nochange",
                    level1 = "value1",
                    level2 = new
                    {
                        level3 = "value3"
                    },
                    level6 = (Dictionary <string, string>)null,
                },
                overwrite = new
                {
                    level1 = "value1"
                },
                create           = "yes",
                arrayWithObjects = new object[] { new object[] { 100L, false } },
                arrayWithValue   = new string[] { "a" },
                arrayChangeType  = new string[] { "a" },
                arrayNoChange    = new string[] { "a" }
            };

            var patch = new
            {
                name = new
                {
                    // ["level0"] = "nochange", // unchanged
                    level1 = (Dictionary <string, string>)null, // existing in base. remove property
                    level2 = new
                    {
                        level3 = "newvalue3" // existing in base, update property
                    },
                    level4 = "value4",       // non existant in base, add new property
                },
                overwrite = "yes",           // overwrite object with value
                create    = new              // overwrite value with object
                {
                    level1 = "value1",
                },
                arrayWithObjects = new object[] { new object[] { 100L, true }, new { a = 0, b = "doom" } },
                arrayWithValue   = new string[] { "b" },
                arrayChangeType  = "a",
                // arrayNoChange = new string[] { "a" } // unchanged
            };

            var mergedExcludeNull = new
            {
                name = new
                {
                    level0 = "nochange", // unchanged
                    level2 = new
                    {
                        level3 = "newvalue3"
                    },
                    level4 = "value4",
                    level6 = (Dictionary <string, string>)null,
                },
                overwrite = "yes",
                create    = new
                {
                    level1 = "value1",
                },
                arrayWithObjects = new object[] { new object[] { 100L, true }, new { a = 0, b = "doom" } },
                arrayWithValue   = new string[] { "b" },
                arrayChangeType  = "a",
                arrayNoChange    = new string[] { "a" }
            };

            var removeAll = new
            {
                name             = (Dictionary <string, string>)null,
                overwrite        = (Dictionary <string, string>)null,
                create           = (Dictionary <string, string>)null,
                arrayWithObjects = (int[])null,
                arrayWithValue   = (string[])null,
                arrayChangeType  = (string[])null,
                arrayNoChange    = (string[])null,
            };

            var emptyBaseline = new { };

            var emptyPatch = new { };

            // Act
            JToken resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(mergedExcludeNull));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(patch)), JToken.FromObject(patch).ToString());

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(baseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(emptyPatch)), resultCollection.ToString());

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(emptyBaseline), JToken.FromObject(emptyBaseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(emptyPatch)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(emptyBaseline), JToken.FromObject(mergedExcludeNull));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(mergedExcludeNull)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(emptyBaseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(removeAll)));
        }
Exemplo n.º 6
0
        public void TestDiffAllCases()
        {
            // Arrange
            var baseline = new
            {
                name = new
                {
                    level0 = "nochange",
                    level1 = "value1",
                    level2 = new
                    {
                        level3 = "value3"
                    },
                    level6 = (Dictionary <string, string>)null,
                },
                overwrite = new
                {
                    level1 = "value1"
                },
                create = "yes"
            };

            var patch = new
            {
                name = new
                {
                    // ["level0"] = "nochange", // unchanged
                    level1 = (Dictionary <string, string>)null, // existing in base. remove property
                    level2 = new
                    {
                        level3 = "newvalue3" // existing in base, update property
                    },
                    level4 = "value4",       // non existant in base, add new property
                },
                overwrite = "yes",           // overwrite object with value
                create    = new              // overwrite value with object
                {
                    level1 = "value1",
                },
            };

            var mergedExcludeNull = new
            {
                name = new
                {
                    level0 = "nochange", // unchanged
                    level2 = new
                    {
                        level3 = "newvalue3"
                    },
                    level4 = "value4",
                    level6 = (Dictionary <string, string>)null,
                },
                overwrite = "yes",
                create    = new
                {
                    level1 = "value1",
                }
            };

            var removeAll = new
            {
                name      = (Dictionary <string, string>)null,
                overwrite = (Dictionary <string, string>)null,
                create    = (Dictionary <string, string>)null
            };

            var emptyBaseline = new { };

            var emptyPatch = new { };

            // Act
            JToken resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(mergedExcludeNull));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(patch)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(baseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(emptyPatch)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(emptyBaseline), JToken.FromObject(emptyBaseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(emptyPatch)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(emptyBaseline), JToken.FromObject(mergedExcludeNull));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(mergedExcludeNull)));

            // Act
            resultCollection = JsonEx.Diff(JToken.FromObject(baseline), JToken.FromObject(emptyBaseline));

            // Assert
            Assert.True(JToken.DeepEquals(resultCollection, JToken.FromObject(removeAll)));
        }