diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs index 1af58bc71..0eaaae4d2 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs @@ -1,36 +1,54 @@ using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Eventing; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Models.Card; using Speckle.Sdk; namespace Speckle.Connectors.CSiShared.Bindings; -public class CsiSharedBasicConnectorBinding( - IBrowserBridge parent, - ISpeckleApplication speckleApplication, - DocumentModelStore store -) : IBasicConnectorBinding +public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding { + private readonly ISpeckleApplication _speckleApplication; + private readonly DocumentModelStore _store; + private readonly IEventAggregator _eventAggregator; public string Name => "baseBinding"; - public IBrowserBridge Parent { get; } = parent; - public BasicConnectorBindingCommands Commands { get; } = new(parent); + public IBrowserBridge Parent { get; } + public BasicConnectorBindingCommands Commands { get; } - public string GetConnectorVersion() => speckleApplication.SpeckleVersion; + public CsiSharedBasicConnectorBinding( + IBrowserBridge parent, + ISpeckleApplication speckleApplication, + DocumentModelStore store, + IEventAggregator eventAggregator + ) + { + Parent = parent; + _speckleApplication = speckleApplication; + _store = store; + Commands = new BasicConnectorBindingCommands(Parent); + _eventAggregator = eventAggregator; - public string GetSourceApplicationName() => speckleApplication.Slug; + _eventAggregator.GetEvent().Subscribe(OnDocumentStoreChangedEvent); + } - public string GetSourceApplicationVersion() => speckleApplication.HostApplicationVersion; + private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged(); + + public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; + + public string GetSourceApplicationName() => _speckleApplication.Slug; + + public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion; public DocumentInfo? GetDocumentInfo() => new("ETABS Model", "ETABS Model", "1"); - public DocumentModelStore GetDocumentState() => store; + public DocumentModelStore GetDocumentState() => _store; - public void AddModel(ModelCard model) => store.AddModel(model); + public void AddModel(ModelCard model) => _store.AddModel(model); - public void UpdateModel(ModelCard model) => store.UpdateModel(model); + public void UpdateModel(ModelCard model) => _store.UpdateModel(model); - public void RemoveModel(ModelCard model) => store.RemoveModel(model); + public void RemoveModel(ModelCard model) => _store.RemoveModel(model); public Task HighlightModel(string modelCardId) => Task.CompletedTask; diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs index 5a160a0d2..0e1094b03 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs @@ -1,40 +1,94 @@ using System.IO; +using System.Timers; using Microsoft.Extensions.Logging; +using Speckle.Connectors.DUI.Eventing; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.DUI.Utils; using Speckle.Sdk; using Speckle.Sdk.Helpers; using Speckle.Sdk.Logging; +using Timer = System.Timers.Timer; namespace Speckle.Connectors.CSiShared.HostApp; -public class CsiDocumentModelStore( - IJsonSerializer jsonSerializerSettings, - ISpeckleApplication speckleApplication, - ILogger logger, - ICsiApplicationService csiApplicationService -) : DocumentModelStore(jsonSerializerSettings) +public class CsiDocumentModelStore : DocumentModelStore, IDisposable { + private readonly ISpeckleApplication _speckleApplication; + private readonly ILogger _logger; + private readonly ICsiApplicationService _csiApplicationService; + private readonly Timer _modelCheckTimer; + private readonly IEventAggregator _eventAggregator; + private string _lastModelFilename = string.Empty; + private bool _disposed; private string HostAppUserDataPath { get; set; } private string DocumentStateFile { get; set; } private string ModelPathHash { get; set; } - public override Task OnDocumentStoreInitialized() + public CsiDocumentModelStore( + IJsonSerializer jsonSerializer, + ISpeckleApplication speckleApplication, + ILogger logger, + ICsiApplicationService csiApplicationService, + IEventAggregator eventAggregator + ) + : base(jsonSerializer) + { + _speckleApplication = speckleApplication; + _logger = logger; + _csiApplicationService = csiApplicationService; + _eventAggregator = eventAggregator; + + // initialize timer to check for model changes + _modelCheckTimer = new Timer(1000); + _modelCheckTimer.Elapsed += CheckModelChanges; + _modelCheckTimer.Start(); + } + + private async void CheckModelChanges(object? source, ElapsedEventArgs e) { + string currentFilename = _csiApplicationService.SapModel.GetModelFilename(); + + if (string.IsNullOrEmpty(currentFilename) || currentFilename == _lastModelFilename) + { + return; + } + + _lastModelFilename = currentFilename; SetPaths(); LoadState(); + + await _eventAggregator.GetEvent().PublishAsync(new object()); + } + + public override Task OnDocumentStoreInitialized() + { + var currentFilename = _csiApplicationService.SapModel.GetModelFilename(); + if (!string.IsNullOrEmpty(currentFilename)) + { + _lastModelFilename = currentFilename; + SetPaths(); + LoadState(); + } return Task.CompletedTask; } private void SetPaths() { - ModelPathHash = Crypt.Md5(csiApplicationService.SapModel.GetModelFilepath(), length: 32); - HostAppUserDataPath = Path.Combine( - SpecklePathProvider.UserSpeckleFolderPath, - "ConnectorsFileData", - speckleApplication.Slug - ); - DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json"); + try + { + ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilename(), length: 32); + HostAppUserDataPath = Path.Combine( + SpecklePathProvider.UserSpeckleFolderPath, + "ConnectorsFileData", + _speckleApplication.Slug + ); + DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json"); + _logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}"); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, "Error in setting paths for CsiDocumentModelStore"); + } } protected override void HostAppSaveState(string modelCardState) @@ -45,29 +99,53 @@ protected override void HostAppSaveState(string modelCardState) { Directory.CreateDirectory(HostAppUserDataPath); } + File.WriteAllText(DocumentStateFile, modelCardState); } catch (Exception ex) when (!ex.IsFatal()) { - logger.LogError(ex.Message); + _logger.LogError(ex, "Failed to save state"); } } protected override void LoadState() { - if (!Directory.Exists(HostAppUserDataPath)) + try + { + if (!File.Exists(DocumentStateFile)) + { + ClearAndSave(); + return; + } + + string serializedState = File.ReadAllText(DocumentStateFile); + LoadFromString(serializedState); + } + catch (Exception ex) when (!ex.IsFatal()) { + _logger.LogError(ex, "Failed to load state, initializing empty state"); ClearAndSave(); - return; } + } - if (!File.Exists(DocumentStateFile)) + protected virtual void Dispose(bool disposing) + { + if (_disposed) { - ClearAndSave(); return; } - string serializedState = File.ReadAllText(DocumentStateFile); - LoadFromString(serializedState); + if (disposing) + { + _modelCheckTimer.Dispose(); + } + + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs index aeb162f76..d9b933842 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs @@ -76,13 +76,10 @@ public void Initialize(ref cSapModel sapModel, ref cPluginCallback pluginCallbac Host = new() { Child = webview, Dock = DockStyle.Fill }; Controls.Add(Host); FormBorderStyle = FormBorderStyle.Sizable; + // this.TopLevel = true; + // TODO: Get IntrPtr for Csi window FormClosing += Form1Closing; } - private void Form1Closing(object? sender, FormClosingEventArgs e) - { - Host.Dispose(); - _pluginCallback.Finish(0); - _container.Dispose(); - } + private void Form1Closing(object? sender, FormClosingEventArgs e) => _pluginCallback.Finish(0); }