private void ProcessTypeBatch(List <Type> batch, out bool changeDetected) { //This method assumes it's not run in parallel //GSA.App.Messenger.ResetLoggedMessageCount(); #if DEBUG changeDetected = false; foreach (var t in batch) { SerialiseType(t, ref changeDetected); if (changeDetected) // This will skip the first read but it avoids flickering { statusProgress.Report("Reading " + t.Name); } //Nodes are a special case because they're the main type of records that would be cached but not actually sent var numObjects = t.GetProperties().Any(p => p.Name.ToLower().Contains("forcesend")) ? 0 : GSA.SenderDictionaries.Sum(d => d.Count(t)); progressEstimator.AppendCurrent(WorkPhase.Conversion, numObjects); } #else var changeLock = new object(); var parallelChangeDetected = false; Parallel.ForEach(batch, t => { bool changed = false; SerialiseType(t, ref changed); if (changed) // This will skip the first read but it avoids flickering { lock (changeLock) { parallelChangeDetected = true; } statusProgress.Report("Reading " + t.Name); } } ); foreach (var t in batch) { progressEstimator.AppendCurrent(WorkPhase.Conversion, GSA.SenderDictionaries.Sum(d => d.Count(t))); } changeDetected = parallelChangeDetected; #endif GSA.App.LocalMessenger.Trigger(); lock (traversedSerialisedLock) { traversedSerialisedTypes.AddRange(batch); } }
/// <summary> /// Trigger to update stream. Is called automatically when update-global ws message is received on stream. /// </summary> public void Trigger(object sender, EventArgs e) { if ((IsBusy) || (!IsInit)) return; IsBusy = true; var startTime = DateTime.Now; //GSA.App.Settings.Units = GSA.App.Proxy.GetUnits(); lock (traversedSerialisedLock) { traversedSerialisedTypes.Clear(); } lock (traversedDeserialisedLock) { traversedDeserialisedTypes.Clear(); } GSA.SenderDictionaries.Clear(); // Read objects statusProgress.Report("Receiving streams"); var streamIds = StreamReceivers.Keys.ToList(); var rxObjsByStream = new Dictionary<string, List<SpeckleObject>>(); foreach (var streamId in StreamReceivers.Keys) { rxObjsByStream.Add(streamId, StreamReceivers[streamId].GetObjects()); //This calls UpdateGlobal(), which is the trigger for pulilng information from the server progressEstimator.AppendCurrent(WorkPhase.ApiCalls, 1); } progressEstimator.UpdateTotal(WorkPhase.Conversion, rxObjsByStream.Keys.Sum(k => rxObjsByStream[k].Count())); //This list will contain ALL speckle objects received across all streams var rxObjs = new List<SpeckleObject>(); var units = GSA.GsaApp.Settings.Units; foreach (var streamId in StreamReceivers.Keys) { double factor = 1; if (StreamReceivers[streamId].Units == null) { //Let the user know if any streams have no unit information this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Error, "Streams with no unit information", streamId)); } else { factor = (1.0).ConvertUnit(StreamReceivers[streamId].Units.ShortUnitName(), units); } foreach (var o in rxObjsByStream[streamId]) { if (string.IsNullOrEmpty(o.ApplicationId)) { this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Information, o.GetType().Name + ((string.IsNullOrEmpty(o.Name)) ? " with no name nor ApplicationId (identified by hashes)" : " with no name nor ApplicationId (identified by hashes)"), o.Hash)); } else { o.Properties.Add("StreamId", streamId); try { o.Scale(factor); } catch (Exception ex) { this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Error, "Scaling issue for objects with _ids on stream: " + streamId, o._id)); this.loggingProgress.Report(new MessageEventArgs(MessageIntent.TechnicalLog, MessageLevel.Error, ex, "Scaling issue", "StreamId=" + streamId, "_id=" + o._id)); } //Populate the cache with stream IDs - review if this is needed anymroe GSA.App.LocalCache.SetStream(o.ApplicationId, streamId); rxObjs.Add(o); } } } progressEstimator.UpdateTotal(WorkPhase.Conversion, rxObjs.Count()); //GSA.App.Messenger.Trigger(); TimeSpan duration = DateTime.Now - startTime; this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Information, "Duration of reception from Speckle and scaling: " + duration.ToString(@"hh\:mm\:ss"))); this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Telemetry, MessageLevel.Information, "receive", "reception and scaling", "duration", duration.ToString(@"hh\:mm\:ss"))); if (rxObjs.Count() == 0) { this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Information, "No processing needed because the stream(s) contain(s) no objects")); statusProgress.Report("Finished receiving"); IsBusy = false; return; } startTime = DateTime.Now; streamIds.ForEach(s => GSA.App.LocalCache.Snapshot(s)); ProcessRxObjects(rxObjs); var toBeAddedGwa = GSA.App.LocalCache.GetNewGwaSetCommands(); toBeAddedGwa.ForEach(tba => GSA.App.Proxy.SetGwa(tba)); var toBeDeletedGwa = GSA.App.LocalCache.GetExpiredData(); var setDeletes = toBeDeletedGwa.Where(t => t.Item4 == GwaSetCommandType.Set).ToList(); setDeletes.ForEach(sd => GSA.App.Proxy.DeleteGWA(sd.Item1, sd.Item2, GwaSetCommandType.Set)); var setAtDeletes = toBeDeletedGwa.Where(t => t.Item4 == GwaSetCommandType.SetAt).OrderByDescending(t => t.Item2).ToList(); setAtDeletes.ForEach(sad => GSA.App.Proxy.DeleteGWA(sad.Item1, sad.Item2, GwaSetCommandType.SetAt)); GSA.App.Proxy.Sync(); GSA.App.Proxy.UpdateCasesAndTasks(); GSA.App.Proxy.UpdateViews(); duration = DateTime.Now - startTime; this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Display, MessageLevel.Information, "Duration of conversion from Speckle: " + duration.ToString(@"hh\:mm\:ss"))); this.loggingProgress.Report(new MessageEventArgs(MessageIntent.Telemetry, MessageLevel.Information, "receive", "conversion", "duration", duration.ToString(@"hh\:mm\:ss"))); startTime = DateTime.Now; statusProgress.Report("Finished receiving"); IsBusy = false; }