async Task UpdateReportedPropertiesPatchAsync(string id, TwinInfo newTwinInfo, TwinCollection reportedProperties) { try { using (await this.twinLock.LockAsync()) { IEntityStore <string, TwinInfo> twinStore = this.TwinStore.Expect(() => new InvalidOperationException("Missing twin store")); await twinStore.PutOrUpdate( id, newTwinInfo, u => { string mergedJson = JsonEx.Merge(u.ReportedPropertiesPatch, reportedProperties, /*treatNullAsDelete*/ false); var mergedPatch = new TwinCollection(mergedJson); Events.UpdatingReportedPropertiesPatchCollection(id, mergedPatch.Version); return(new TwinInfo(u.Twin, mergedPatch)); }); } } catch (Exception e) { throw new InvalidOperationException($"Error updating twin patch for device {id}", e); } }
public static void GetTwinFromStoreWhenOffline(string id, TwinInfo twinInfo, Exception e) { if (twinInfo.Twin != null) { Log.LogDebug((int)EventIds.GetTwinFromStoreWhenOffline, $"Getting twin for {id} at desired version " + $"{twinInfo.Twin.Properties.Desired.Version} reported version {twinInfo.Twin.Properties.Reported.Version} from local store. Get from cloud threw {e.GetType()} {e.Message}"); } else { Log.LogDebug((int)EventIds.GetTwinFromStoreWhenOffline, $"Getting twin info for {id}, but twin is null. Get from cloud threw {e.GetType()} {e.Message}"); } }
public async Task <IMessage> GetTwinAsync(string id) { return(await this.TwinStore.Match( async (store) => { TwinInfo twinInfo = await this.GetTwinInfoWithStoreSupportAsync(id); return this.twinConverter.ToMessage(twinInfo.Twin); }, async() => { // pass through to cloud proxy Option <ICloudProxy> cloudProxy = await this.connectionManager.GetCloudConnection(id); return await cloudProxy.Match(async(cp) => await cp.GetTwinAsync(), () => throw new InvalidOperationException($"Cloud proxy unavailable for device {id}")); }));
async Task <TwinInfo> GetTwinInfoWhenCloudOfflineAsync(string id, Exception e) { TwinInfo twinInfo = null; await this.ExecuteOnTwinStoreResultAsync( id, t => { twinInfo = t; Events.GetTwinFromStoreWhenOffline(id, twinInfo, e); return(Task.CompletedTask); }, () => throw new InvalidOperationException($"Error getting twin for device {id}", e)); return(twinInfo); }
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); }