From ff0ab2559c9efcdec6af5e3186ead3e63a1315c7 Mon Sep 17 00:00:00 2001 From: ksobon Date: Tue, 15 May 2018 18:01:42 -0400 Subject: [PATCH 1/6] new beginnings --- .idea/SpeckleRevit.iml | 12 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/workspace.xml | 120 ++++ SpeckleCore | 2 +- SpeckleRevit.sln.DotSettings.user | 5 + .../SpeckleRevitConverter.csproj.user | 7 + .../Classes/ISpeckleRevitClient.cs | 26 + SpeckleRevitPlugin/Classes/Interop.cs | 463 ++++++++++++++ SpeckleRevitPlugin/Classes/RevitReceiver.cs | 296 +++++++++ SpeckleRevitPlugin/Classes/SpeckleAccount.cs | 12 + .../Classes/SpeckleRhinoSender.cs | 572 ++++++++++++++++++ SpeckleRevitPlugin/Entry/AppMain.cs | 15 +- SpeckleRevitPlugin/SpeckleRevitPlugin.csproj | 13 +- SpeckleRevitPlugin/UI/form_MainDock.xaml | 7 +- SpeckleRevitPlugin/UI/form_MainDock.xaml.cs | 96 +-- SpeckleView | 2 +- UpgradeLog.htm | Bin 0 -> 33092 bytes 18 files changed, 1607 insertions(+), 55 deletions(-) create mode 100644 .idea/SpeckleRevit.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 SpeckleRevit.sln.DotSettings.user create mode 100644 SpeckleRevitConverter/SpeckleRevitConverter.csproj.user create mode 100644 SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs create mode 100644 SpeckleRevitPlugin/Classes/Interop.cs create mode 100644 SpeckleRevitPlugin/Classes/RevitReceiver.cs create mode 100644 SpeckleRevitPlugin/Classes/SpeckleAccount.cs create mode 100644 SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs create mode 100644 UpgradeLog.htm diff --git a/.idea/SpeckleRevit.iml b/.idea/SpeckleRevit.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/SpeckleRevit.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..56d9dce --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..ca3bd8a --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1526389059417 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SpeckleCore b/SpeckleCore index a8e4f98..53332c9 160000 --- a/SpeckleCore +++ b/SpeckleCore @@ -1 +1 @@ -Subproject commit a8e4f98984e02c983c954dca0c6a47cc1fc76822 +Subproject commit 53332c9fd2c281a6d16d9e37e7488db8a826c74d diff --git a/SpeckleRevit.sln.DotSettings.user b/SpeckleRevit.sln.DotSettings.user new file mode 100644 index 0000000..4e1d7a6 --- /dev/null +++ b/SpeckleRevit.sln.DotSettings.user @@ -0,0 +1,5 @@ + + <AssemblyExplorer> + <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\cefsharp.wpf\63.0.2\CefSharp\x64\CefSharp.Wpf.dll" /> + <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\newtonsoft.json\11.0.1\lib\net45\Newtonsoft.Json.dll" /> +</AssemblyExplorer> \ No newline at end of file diff --git a/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user b/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user new file mode 100644 index 0000000..e5e922d --- /dev/null +++ b/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user @@ -0,0 +1,7 @@ + + + + Program + C:\Program Files\Autodesk\Revit 2018\Revit.exe + + \ No newline at end of file diff --git a/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs new file mode 100644 index 0000000..5b1d324 --- /dev/null +++ b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.Serialization; +using SpeckleCore; + +namespace SpeckleRevitPlugin.Classes +{ + /// + /// Generalises some methhods for both senders and receivers. + /// + public interface ISpeckleRevitClient : IDisposable, ISerializable + { + ClientRole GetRole(); + + string GetClientId(); + + void TogglePaused(bool status); + + void ToggleVisibility(bool status); + + void ToggleLayerVisibility(string layerId, bool status); + + void ToggleLayerHover(string layerId, bool status); + + void Dispose(bool delete = false); + } +} diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs new file mode 100644 index 0000000..036acef --- /dev/null +++ b/SpeckleRevitPlugin/Classes/Interop.cs @@ -0,0 +1,463 @@ +#region Namespaces +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using CefSharp; +using CefSharp.Wpf; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using SpeckleCore; +#endregion + +namespace SpeckleRevitPlugin.Classes +{ + /// + /// CEF Bound object. + /// If CEF will be removed, porting to url hacks will be necessary, + /// so let's keep the methods as simple as possible. + /// + public class Interop : IDisposable + { + private List _userAccounts; + public ChromiumWebBrowser Browser; + public List UserClients; + public Dictionary SpeckleObjectCache; + public bool SpeckleIsReady; + public bool SelectionInfoNeedsToBeSentYeMighty = false; // should be false + + public Interop(ChromiumWebBrowser originalBrowser) + { + // Makes sure we always get some camelCaseLove + JsonConvert.DefaultSettings = () => new JsonSerializerSettings() + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + Browser = originalBrowser; + UserClients = new List(); + _userAccounts = new List(); + SpeckleObjectCache = new Dictionary(); + ReadUserAccounts(); + } + + public void SetBrowser( ChromiumWebBrowser browser ) + { + Browser = browser; + } + + public void Dispose( ) + { + RemoveAllClients(); + } + + public void ShowDev( ) + { + Browser.ShowDevTools(); + } + + public string GetDocumentName() + { + //TODO: Fix this! + return "Revit doc name."; + //return Rhino.RhinoDoc.ActiveDoc.Name; + } + + public string GetDocumentGuid() + { + //TODO: Fix this! + return "Revit GUID"; + //return Rhino.RhinoDoc.ActiveDoc.DocumentId.ToString(); + } + + /// + /// Do not call this from the constructor as you'll get confilcts with + /// browser load, etc. + /// + public void AppReady( ) + { + SpeckleIsReady = true; + InstantiateFileClients(); + } + + //TODO: This is called when document is saved. In Revit that would be either document save or synch events. + //public void SaveFileClients( ) + //{ + // RhinoDoc myDoc = RhinoDoc.ActiveDoc; + // foreach ( ISpeckleRhinoClient rhinoClient in UserClients ) + // { + // using ( var ms = new MemoryStream() ) + // { + // var formatter = new BinaryFormatter(); + // formatter.Serialize( ms, rhinoClient ); + // string section = rhinoClient.GetRole() == ClientRole.Receiver ? "speckle-client-receivers" : "speckle-client-senders"; + // var client = Convert.ToBase64String( ms.ToArray() ); + // var clientId = rhinoClient.GetClientId(); + // RhinoDoc.ActiveDoc.Strings.SetString( section, clientId, client ); + // } + // } + //} + + public void InstantiateFileClients() + { + //TODO: these can be stored in a extensible storage schema and then de-serialized here! + //if (!SpeckleIsReady) return; + + //Debug.WriteLine("Instantiate file clients."); + + //string[] receiverKeys = RhinoDoc.ActiveDoc.Strings.GetEntryNames("speckle-client-receivers"); + + //foreach (string rec in receiverKeys) + //{ + // //if ( UserClients.Any( cl => cl.GetClientId() == rec ) ) + // // continue; + + // byte[] serialisedClient = Convert.FromBase64String(RhinoDoc.ActiveDoc.Strings.GetValue("speckle-client-receivers", rec)); + // using (var ms = new MemoryStream()) + // { + // ms.Write(serialisedClient, 0, serialisedClient.Length); + // ms.Seek(0, SeekOrigin.Begin); + // RhinoReceiver client = (RhinoReceiver)new BinaryFormatter().Deserialize(ms); + // client.Context = this; + // } + //} + + //string[] senderKeys = RhinoDoc.ActiveDoc.Strings.GetEntryNames("speckle-client-senders"); + + //foreach (string sen in senderKeys) + //{ + // byte[] serialisedClient = Convert.FromBase64String(RhinoDoc.ActiveDoc.Strings.GetValue("speckle-client-senders", sen)); + + // using (var ms = new MemoryStream()) + // { + // ms.Write(serialisedClient, 0, serialisedClient.Length); + // ms.Seek(0, SeekOrigin.Begin); + // RhinoSender client = (RhinoSender)new BinaryFormatter().Deserialize(ms); + // client.CompleteDeserialisation(this); + // } + //} + } + + #region Account Management + + public string GetUserAccounts( ) + { + ReadUserAccounts(); + return JsonConvert.SerializeObject(_userAccounts, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + } + + private void ReadUserAccounts( ) + { + _userAccounts = new List(); + var strPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + strPath = strPath + @"\SpeckleSettings"; + + if (!Directory.Exists(strPath) || !Directory.EnumerateFiles(strPath, "*.txt").Any()) return; + + foreach (var file in Directory.EnumerateFiles(strPath, "*.txt")) + { + var content = File.ReadAllText(file); + var pieces = content.TrimEnd('\r', '\n').Split(','); + _userAccounts.Add(new SpeckleAccount + { + email = pieces[0], + apiToken = pieces[1], + serverName = pieces[2], + restApi = pieces[3], + rootUrl = pieces[4], + fileName = file + }); + } + } + + public void AddAccount( string payload ) + { + var pieces = payload.Split(','); + var strPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + + Directory.CreateDirectory(strPath + @"\SpeckleSettings"); + strPath = strPath + @"\SpeckleSettings\"; + + var fileName = pieces[0] + "." + pieces[2] + ".txt"; + var file = new StreamWriter(strPath + fileName); + file.WriteLine(payload); + file.Close(); + } + + public void RemoveAccount( string payload ) + { + var unused = _userAccounts.RemoveAll(account => account.fileName == payload); + if (File.Exists(payload)) File.Delete(payload); + } + + #endregion + + #region Client Management + + public bool AddReceiverClient( string payload ) + { + var unused = new RevitReceiver(payload, this); + return true; + } + + //public bool AddSenderClientFromSelection( string _payload ) + //{ + // var mySender = new RhinoSender( _payload, this ); + // return true; + //} + + //public bool RemoveClient( string _payload ) + //{ + // var myClient = UserClients.FirstOrDefault( client => client.GetClientId() == _payload ); + // if ( myClient == null ) return false; + + // RhinoDoc.ActiveDoc.Strings.Delete( myClient.GetRole() == ClientRole.Receiver ? "speckle-client-receivers" : "speckle-client-senders", myClient.GetClientId() ); + + // myClient.Dispose( true ); + + // return UserClients.Remove( myClient ); + //} + + public bool RemoveAllClients( ) + { + foreach (var uc in UserClients) + { + uc.Dispose(); + } + UserClients.RemoveAll(c => true); + return true; + } + + public string GetAllClients( ) + { + foreach (var client in UserClients) + { + switch (client) + { + case RevitSender rvtSender: + NotifySpeckleFrame("client-add", rvtSender.StreamId, JsonConvert.SerializeObject(new + { + stream = rvtSender.Client.Stream, + client = rvtSender.Client + })); + break; + case RevitReceiver rvtReceiver: + NotifySpeckleFrame("client-add", rvtReceiver.StreamId, JsonConvert.SerializeObject(new + { + stream = rvtReceiver.Client.Stream, + client = rvtReceiver.Client + })); + break; + } + } + + return JsonConvert.SerializeObject(UserClients); + } + + #endregion + + #region To UI (Generic) + + public void NotifySpeckleFrame( string eventType, string streamId, string eventInfo ) + { + if (!SpeckleIsReady) + { + Debug.WriteLine("Speckle was not ready, trying to send " + eventType); + return; + } + + var script = $"window.EventBus.$emit('{eventType}', '{streamId}', '{eventInfo}')"; + + try + { + Browser.GetMainFrame().EvaluateScriptAsync(script); + } + catch + { + Debug.WriteLine("For some reason, this browser was not initialised."); + } + } + #endregion + + #region From UI (..) + + public void BakeClient(string clientId) + { + //TODO: Implement client baking + } + + public void BakeLayer(string clientId, string layerGuid) + { + //TODO: Implement baking + } + + public void SetClientPause(string clientId, bool status) + { + var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId); + myClient?.TogglePaused(status); + } + + public void SetClientVisibility(string clientId, bool status) + { + var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId); + myClient?.ToggleVisibility(status); + } + + public void SetClientHover(string clientId, bool status) + { + var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId); + myClient?.ToggleVisibility(status); + } + + public void SetLayerVisibility(string clientId, string layerId, bool status) + { + //TODO: create geometry previews + } + + public void SetLayerHover(string clientId, string layerId, bool status) + { + //TODO: highlight geometry previews + } + + public void SetObjectHover(string clientId, string layerId, bool status) + { + //TODO: implement object hover + } + + //public void AddRemoveObjects( string clientId, string _guids, bool remove ) + //{ + // string[ ] guids = JsonConvert.DeserializeObject( _guids ); + + // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId ); + // if ( myClient != null ) + // try + // { + // if ( !remove ) + // ( ( RhinoSender ) myClient ).AddTrackedObjects( guids ); + // else ( ( RhinoSender ) myClient ).RemoveTrackedObjects( guids ); + + // } + // catch { throw new Exception( "Force send client was not a sender. whoopsie poopsiee." ); } + //} + + public void RefreshClient(string clientId) + { + var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId); + if (myClient == null) return; + + try + { + ((RevitReceiver) myClient).UpdateGlobal(); + } + catch + { + throw new Exception("Refresh client was not a receiver. whoopsie poopsiee."); + } + } + + //public void forceSend( string clientId ) + //{ + // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId ); + // if ( myClient != null ) + // try + // { + // ( ( RhinoSender ) myClient ).ForceUpdate(); + // } + // catch { throw new Exception( "Force send client was not a sender. whoopsie poopsiee." ); } + //} + + public void OpenUrl(string url) + { + Process.Start(url); + } + + //public void setName( string clientId, string name ) + //{ + // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId ); + // if ( myClient != null && myClient is RhinoSender ) + // { + // ( ( RhinoSender ) myClient ).Client.Stream.Name = name; + // ( ( RhinoSender ) myClient ).Client.BroadcastMessage( new { eventType = "update-name" } ); + // } + //} + + #endregion + + #region Sender Helpers + + //public string getLayersAndObjectsInfo( bool ignoreSelection = false ) + //{ + // List SelectedObjects; + // List layerInfoList = new List(); + + // if ( !ignoreSelection ) + // { + // SelectedObjects = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects( false, false ).ToList(); + // if ( SelectedObjects.Count == 0 || SelectedObjects[ 0 ] == null ) + // return JsonConvert.SerializeObject( layerInfoList ); + // } + // else + // { + // SelectedObjects = RhinoDoc.ActiveDoc.Objects.ToList(); + // if ( SelectedObjects.Count == 0 || SelectedObjects[ 0 ] == null ) + // return JsonConvert.SerializeObject( layerInfoList ); + + // foreach ( Rhino.DocObjects.Layer ll in RhinoDoc.ActiveDoc.Layers ) + // { + // layerInfoList.Add( new LayerSelection() + // { + // objectCount = 0, + // layerName = ll.FullPath, + // color = System.Drawing.ColorTranslator.ToHtml( ll.Color ), + // ObjectGuids = new List(), + // ObjectTypes = new List() + // } ); + // } + // } + + // SelectedObjects = SelectedObjects.OrderBy( o => o.Attributes.LayerIndex ).ToList(); + + // foreach ( var obj in SelectedObjects ) + // { + // var layer = RhinoDoc.ActiveDoc.Layers[ obj.Attributes.LayerIndex ]; + // var myLInfo = layerInfoList.FirstOrDefault( l => l.layerName == layer.FullPath ); + + // if ( myLInfo != null ) + // { + // myLInfo.objectCount++; + // myLInfo.ObjectGuids.Add( obj.Id.ToString() ); + // myLInfo.ObjectTypes.Add( obj.Geometry.GetType().ToString() ); + // } + // else + // { + // var myNewLinfo = new LayerSelection() + // { + // objectCount = 1, + // layerName = layer.FullPath, + // color = System.Drawing.ColorTranslator.ToHtml( layer.Color ), + // ObjectGuids = new List( new string[ ] { obj.Id.ToString() } ), + // ObjectTypes = new List( new string[ ] { obj.Geometry.GetType().ToString() } ) + // }; + // layerInfoList.Add( myNewLinfo ); + // } + // } + + // return Convert.ToBase64String( System.Text.Encoding.UTF8.GetBytes( JsonConvert.SerializeObject( layerInfoList ) ) ); + //} + #endregion + } + + [Serializable] + public class LayerSelection + { + public string LayerName; + public int ObjectCount; + public string Color; + public List ObjectGuids; + public List ObjectTypes; + } +} diff --git a/SpeckleRevitPlugin/Classes/RevitReceiver.cs b/SpeckleRevitPlugin/Classes/RevitReceiver.cs new file mode 100644 index 0000000..0688919 --- /dev/null +++ b/SpeckleRevitPlugin/Classes/RevitReceiver.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using SpeckleCore; + +namespace SpeckleRevitPlugin.Classes +{ + /// + /// Class that holds a rhino receiver client warpped around the + /// SpeckleApiClient. + /// + [Serializable] + public class RevitReceiver : ISpeckleRevitClient + { + public Interop Context { get; set; } + public SpeckleApiClient Client { get; set; } + public List Objects { get; set; } + public string StreamId { get; private set; } + public bool Paused { get; set; } + public bool Visible { get; set; } = true; + + public RevitReceiver( string payload, Interop parent ) + { + Context = parent; + dynamic p = JsonConvert.DeserializeObject(payload); + + StreamId = (string) p.streamId; + Client = new SpeckleApiClient((string) p.account.restApi, true); + + Client.OnReady += Client_OnReady; + Client.OnLogData += Client_OnLogData; + Client.OnWsMessage += Client_OnWsMessage; + Client.OnError += Client_OnError; + + Client.IntializeReceiver((string) p.streamId, + Context.GetDocumentName(), + "Revit", + Context.GetDocumentGuid(), + (string) p.account.apiToken); + + Objects = new List(); + } + + public string GetClientId() + { + return Client.ClientId; + } + + public ClientRole GetRole() + { + return ClientRole.Receiver; + } + + #region Events + + private void Client_OnError( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( e.EventData ) ); + } + + public virtual void Client_OnLogData( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( e.EventData ) ); + } + + public virtual void Client_OnReady(object source, SpeckleEventArgs e) + { + Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new + { + client = Client, + stream = Client.Stream + })); + + Context.UserClients.Add(this); + UpdateGlobal(); + } + + public virtual void Client_OnWsMessage(object source, SpeckleEventArgs e) + { + if (Paused) + { + Context.NotifySpeckleFrame("client-expired", StreamId, ""); + return; + } + + switch ((string)e.EventObject.args.eventType) + { + case "update-global": + UpdateGlobal(); + break; + case "update-meta": + UpdateMeta(); + break; + case "update-name": + UpdateName(); + break; + case "update-object": + break; + case "update-children": + UpdateChildren(); + break; + default: + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Unkown event: " + (string)e.EventObject.args.eventType)); + break; + } + } + #endregion + + #region Updates + + public void UpdateName() + { + try + { + var response = Client.StreamGetAsync(StreamId, "fields=name"); + Client.Stream.Name = response.Result.Resource.Name; + Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson()); + } + catch (Exception err) + { + Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + } + } + + public void UpdateMeta() + { + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Metadata update received.")); + + try + { + var streamGetResponse = Client.StreamGetAsync(StreamId, null).Result; + + if (streamGetResponse.Success == false) + { + Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message); + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Failed to retrieve global update.")); + } + + Client.Stream = streamGetResponse.Resource; + + Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson()); + } + catch (Exception err) + { + Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + } + } + + public void UpdateGlobal() + { + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Global update received.")); + + try + { + var streamGetResponse = Client.StreamGetAsync(StreamId, null).Result; + if (streamGetResponse.Success == false) + { + Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message); + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Failed to retrieve global update.")); + } + + Client.Stream = streamGetResponse.Resource; + Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson()); + Context.NotifySpeckleFrame("client-is-loading", StreamId, ""); + + // prepare payload + var payload = Client.Stream.Objects.Where(o => !Context.SpeckleObjectCache.ContainsKey(o._id)).Select(obj => obj._id).ToArray(); + var getObjectsResult = Client.ObjectGetBulkAsync(payload, "omit=displayValue").Result; + + if (getObjectsResult.Success == false) + Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message); + + // add to cache + foreach (var obj in getObjectsResult.Resources) + { + Context.SpeckleObjectCache[obj._id] = obj; + } + + // populate real objects + Objects.Clear(); + foreach (var obj in Client.Stream.Objects) + { + Objects.Add(Context.SpeckleObjectCache[obj._id]); + } + + //DisplayContents(); + Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + } + catch (Exception err) + { + Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + } + } + + public void UpdateChildren() + { + try + { + var getStream = Client.StreamGetAsync(StreamId, null).Result; + Client.Stream = getStream.Resource; + + Context.NotifySpeckleFrame("client-children", StreamId, Client.Stream.ToJson()); + } + catch (Exception err) + { + Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + } + } + + #endregion + + #region Toggles + + public void TogglePaused(bool status) + { + Paused = status; + } + + public void ToggleVisibility(bool status) + { + //TODO: Implement + } + + public void ToggleLayerVisibility(string layerId, bool status) + { + //TODO: Implement + } + + public void ToggleLayerHover(string layerId, bool status) + { + //TODO: Implement + } + + #endregion + + #region Serialisation & Dispose + + public void Dispose(bool delete = false) + { + Client.Dispose(delete); + } + + public void Dispose() + { + Client.Dispose(); + } + + protected RevitReceiver(SerializationInfo info, StreamingContext context) + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + Objects = new List(); + + var serialisedClient = Convert.FromBase64String(info.GetString("client")); + + using (var ms = new MemoryStream()) + { + ms.Write(serialisedClient, 0, serialisedClient.Length); + ms.Seek(0, SeekOrigin.Begin); + Client = (SpeckleApiClient)new BinaryFormatter().Deserialize(ms); + StreamId = Client.StreamId; + } + + Client.OnReady += Client_OnReady; + Client.OnLogData += Client_OnLogData; + Client.OnWsMessage += Client_OnWsMessage; + Client.OnError += Client_OnError; + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + using (var ms = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(ms, Client); + info.AddValue("client", Convert.ToBase64String(ms.ToArray())); + info.AddValue("paused", Paused); + info.AddValue("visible", Visible); + } + } + + #endregion + } +} diff --git a/SpeckleRevitPlugin/Classes/SpeckleAccount.cs b/SpeckleRevitPlugin/Classes/SpeckleAccount.cs new file mode 100644 index 0000000..9949c4c --- /dev/null +++ b/SpeckleRevitPlugin/Classes/SpeckleAccount.cs @@ -0,0 +1,12 @@ +namespace SpeckleRevitPlugin.Classes +{ + internal class SpeckleAccount + { + public string email { get; set; } + public string apiToken { get; set; } + public string serverName { get; set; } + public string restApi { get; set; } + public string rootUrl { get; set; } + public string fileName { get; set; } + } +} diff --git a/SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs b/SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs new file mode 100644 index 0000000..aee1b7b --- /dev/null +++ b/SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs @@ -0,0 +1,572 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Timers; +using Newtonsoft.Json; +using SpeckleCore; + +namespace SpeckleRevitPlugin.Classes +{ + /// + /// Rhino Sender Client + /// + [Serializable] + public class RevitSender : ISpeckleRevitClient + { + public Interop Context { get; set; } + + public SpeckleApiClient Client { get; private set; } + + public List Objects { get; set; } + + //public SpeckleDisplayConduit Display; + + public string StreamId { get; set; } + + public bool Paused { get; set; } = false; + + public bool Visible { get; set; } = true; + + System.Timers.Timer DataSender, MetadataSender; + + public string StreamName; + + public bool IsSendingUpdate = false, Expired = false; + + //public RhinoSender( string _payload, Interop _Context ) + //{ + // Context = _Context; + + // dynamic InitPayload = JsonConvert.DeserializeObject( _payload ); + + // Client = new SpeckleApiClient( ( string ) InitPayload.account.restApi, true ); + + // StreamName = ( string ) InitPayload.streamName; + + // SetClientEvents(); + // SetRhinoEvents(); + // SetTimers(); + + // Display = new SpeckleDisplayConduit(); + // Display.Enabled = true; + + // Context.NotifySpeckleFrame( "set-gl-load", "", "true" ); + + // Client.IntializeSender( ( string ) InitPayload.account.apiToken, Context.GetDocumentName(), "Rhino", Context.GetDocumentGuid() ) + // .ContinueWith( res => + // { + // StreamId = Client.Stream.StreamId; + // Client.Stream.Name = StreamName; + + // Context.NotifySpeckleFrame( "set-gl-load", "", "false" ); + // Context.NotifySpeckleFrame( "client-add", StreamId, JsonConvert.SerializeObject( new { stream = Client.Stream, client = Client } ) ); + // Context.UserClients.Add( this ); + + // InitTrackedObjects( InitPayload ); + // DataSender.Start(); + // } ); + + //} + + //public void InitTrackedObjects( dynamic payload ) + //{ + // foreach ( string guid in payload.selection ) + // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId ); + //} + + //public void AddTrackedObjects( string[ ] guids ) + //{ + // foreach ( string guid in guids ) + // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId ); + + // DataSender.Start(); + //} + + //public void RemoveTrackedObjects( string[ ] guids ) + //{ + // foreach ( string guid in guids ) + // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, null ); + + // DataSender.Start(); + //} + + //public void SetRhinoEvents( ) + //{ + // RhinoDoc.ModifyObjectAttributes += RhinoDoc_ModifyObjectAttributes; + // RhinoDoc.DeleteRhinoObject += RhinoDoc_DeleteRhinoObject; + // RhinoDoc.AddRhinoObject += RhinoDoc_AddRhinoObject; + // RhinoDoc.UndeleteRhinoObject += RhinoDoc_UndeleteRhinoObject; + // RhinoDoc.LayerTableEvent += RhinoDoc_LayerTableEvent; + //} + + //public void UnsetRhinoEvents( ) + //{ + // RhinoDoc.ModifyObjectAttributes -= RhinoDoc_ModifyObjectAttributes; + // RhinoDoc.DeleteRhinoObject -= RhinoDoc_DeleteRhinoObject; + // RhinoDoc.AddRhinoObject -= RhinoDoc_AddRhinoObject; + // RhinoDoc.UndeleteRhinoObject -= RhinoDoc_UndeleteRhinoObject; + // RhinoDoc.LayerTableEvent -= RhinoDoc_LayerTableEvent; + //} + + //private void RhinoDoc_LayerTableEvent( object sender, Rhino.DocObjects.Tables.LayerTableEventArgs e ) + //{ + // DataSender.Start(); + //} + + //private void RhinoDoc_UndeleteRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // //Debug.WriteLine("UNDELETE Event"); + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + //private void RhinoDoc_AddRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // //Debug.WriteLine("ADD Event"); + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + //private void RhinoDoc_DeleteRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + //private void RhinoDoc_ModifyObjectAttributes( object sender, RhinoModifyObjectAttributesEventArgs e ) + //{ + // //Debug.WriteLine("MODIFY Event"); + // //Prevents https://github.com/speckleworks/SpeckleRhino/issues/51 from happening + // if ( Converter.getBase64( e.NewAttributes ) == Converter.getBase64( e.OldAttributes ) ) return; + + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.RhinoObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + public void SetClientEvents( ) + { + Client.OnError += Client_OnError; + Client.OnLogData += Client_OnLogData; + Client.OnWsMessage += Client_OnWsMessage; + Client.OnReady += Client_OnReady; + } + + //public void SetTimers( ) + //{ + // MetadataSender = new System.Timers.Timer( 500 ) { AutoReset = false, Enabled = false }; + // MetadataSender.Elapsed += MetadataSender_Elapsed; + + // DataSender = new System.Timers.Timer( 2000 ) { AutoReset = false, Enabled = false }; + // DataSender.Elapsed += DataSender_Elapsed; + //} + + private void Client_OnReady( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Ready Event." ) ); + } + + //private void DataSender_Elapsed( object sender, ElapsedEventArgs e ) + //{ + // Debug.WriteLine( "Boing! Boing!" ); + // DataSender.Stop(); + // SendStaggeredUpdate(); + // Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Update Sent." ) ); + //} + + private void MetadataSender_Elapsed( object sender, ElapsedEventArgs e ) + { + Debug.WriteLine( "Ping! Ping!" ); + MetadataSender.Stop(); + Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Update Sent." ) ); + } + + private void Client_OnWsMessage( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "WS message received and ignored." ) ); + } + + private void Client_OnLogData( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( e.EventData ) ); + } + + private void Client_OnError( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( e.EventData ) ); + } + + //public void ForceUpdate( ) + //{ + // SendStaggeredUpdate( true ); + //} + + // TODO: This method, or an abstracted version of it, should move to Speckle Core. + //public async void SendStaggeredUpdate( bool force = false ) + //{ + + // if ( Paused && !force ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + + // if ( IsSendingUpdate ) + // { + // Expired = true; + // return; + // } + + // IsSendingUpdate = true; + + // Context.NotifySpeckleFrame( "client-is-loading", StreamId, "" ); + + // var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString( "spk_" + this.StreamId, "*", false ).OrderBy( obj => obj.Attributes.LayerIndex ); + + // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Converting " + objs.Count() + " objects..." ); + + // List pLayers = new List(); + // List convertedObjects = new List(); + // List> objectUpdatePayloads = new List>(); + + // long totalBucketSize = 0; + // long currentBucketSize = 0; + // List currentBucketObjects = new List(); + // List allObjects = new List(); + + // int lindex = -1, count = 0, orderIndex = 0; + // foreach ( RhinoObject obj in objs ) + // { + // // layer list creation + // Rhino.DocObjects.Layer layer = RhinoDoc.ActiveDoc.Layers[ obj.Attributes.LayerIndex ]; + // if ( lindex != obj.Attributes.LayerIndex ) + // { + // var spkLayer = new SpeckleCore.Layer() + // { + // Name = layer.FullPath, + // Guid = layer.Id.ToString(), + // ObjectCount = 1, + // StartIndex = count, + // OrderIndex = orderIndex++, + // Properties = new LayerProperties() { Color = new SpeckleCore.SpeckleBaseColor() { A = 1, Hex = System.Drawing.ColorTranslator.ToHtml( layer.Color ) }, } + // }; + + // pLayers.Add( spkLayer ); + // lindex = obj.Attributes.LayerIndex; + // } + // else + // { + // var spkl = pLayers.FirstOrDefault( pl => pl.Name == layer.FullPath ); + // spkl.ObjectCount++; + // } + + // count++; + + // // object conversion + // SpeckleObject convertedObject; + + // convertedObject = Converter.Serialise( obj.Geometry ); + // convertedObject.ApplicationId = obj.Id.ToString(); + // allObjects.Add( convertedObject ); + + // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Converted " + count + " objects out of " + objs.Count() + "." ); + + // // check cache and see what the response from the server is when sending placeholders + // // in the ObjectCreateBulkAsyncRoute + // if ( Context.SpeckleObjectCache.ContainsKey( convertedObject.Hash ) ) + // { + // convertedObject = new SpecklePlaceholder() { Hash = convertedObject.Hash, _id = Context.SpeckleObjectCache[ convertedObject.Hash ]._id, ApplicationId = Context.SpeckleObjectCache[ convertedObject.Hash ].ApplicationId }; + // } + + // // size checking & bulk object creation payloads creation + // long size = Converter.getBytes( convertedObject ).Length; + // currentBucketSize += size; + // totalBucketSize += size; + // currentBucketObjects.Add( convertedObject ); + + // if ( currentBucketSize > 2e6 ) + // { + // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL + // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This stream contains a super big object. These are not supported yet :(" ) ); + // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); + // IsSendingUpdate = false; + // return; + // } + + // if ( currentBucketSize > 5e5 ) // restrict max to ~500kb; should it be user config? anyway these functions should go into core. at one point. + // { + // Debug.WriteLine( "Reached payload limit. Making a new one, current #: " + objectUpdatePayloads.Count ); + // objectUpdatePayloads.Add( currentBucketObjects ); + // currentBucketObjects = new List(); + // currentBucketSize = 0; + // } + + // // catch overflows early + // if ( totalBucketSize >= 50e6 ) + // { + // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)" ) ); + // IsSendingUpdate = false; + // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); + // return; + // } + // } + + // // last bucket + // if ( currentBucketObjects.Count > 0 ) + // objectUpdatePayloads.Add( currentBucketObjects ); + + // Debug.WriteLine( "Finished, payload object update count is: " + objectUpdatePayloads.Count + " total bucket size is (kb) " + totalBucketSize / 1000 ); + + // if ( objectUpdatePayloads.Count > 100 || totalBucketSize >= 50e6 ) + // { + // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL + // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)" ) ); + // IsSendingUpdate = false; + // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); + // return; + // } + + // // create bulk object creation tasks + // int k = 0; + // List responses = new List(); + // foreach ( var payload in objectUpdatePayloads ) + // { + // Context.NotifySpeckleFrame( "client-progress-message", StreamId, String.Format( "Sending payload {0} out of {1}", k++, objectUpdatePayloads.Count ) ); + // try + // { + // responses.Add( await Client.ObjectCreateAsync( payload ) ); + // } + // catch ( Exception err ) + // { + // Context.NotifySpeckleFrame( "client-error", Client.Stream.StreamId, JsonConvert.SerializeObject( err.Message ) ); + // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); + // IsSendingUpdate = false; + // return; + // } + // } + + // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Updating stream..." ); + + // // finalise layer creation + // foreach ( var layer in pLayers ) + // layer.Topology = "0-" + layer.ObjectCount + " "; + + // // create placeholders for stream update payload + // List placeholders = new List(); + // int m = 0; + // foreach ( var myResponse in responses ) + // foreach ( var obj in myResponse.Resources ) placeholders.Add( new SpecklePlaceholder() { _id = obj._id, ApplicationId = allObjects[ m++ ].ApplicationId } ); + + // // create stream update payload + // SpeckleStream streamUpdatePayload = new SpeckleStream(); + // streamUpdatePayload.Layers = pLayers; + // streamUpdatePayload.Objects = placeholders; + // streamUpdatePayload.Name = Client.Stream.Name; + + // // set some base properties (will be overwritten) + // var baseProps = new Dictionary(); + // baseProps[ "units" ] = RhinoDoc.ActiveDoc.ModelUnitSystem.ToString(); + // baseProps[ "tolerance" ] = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; + // baseProps[ "angleTolerance" ] = RhinoDoc.ActiveDoc.ModelAngleToleranceRadians; + // streamUpdatePayload.BaseProperties = baseProps; + + // // push it to the server yo! + // ResponseBase response = null; + // try + // { + // response = await Client.StreamUpdateAsync( Client.Stream.StreamId, streamUpdatePayload ); + // } + // catch ( Exception err ) + // { + // Context.NotifySpeckleFrame( "client-error", Client.Stream.StreamId, JsonConvert.SerializeObject( err.Message ) ); + // IsSendingUpdate = false; + // return; + // } + + // // put the objects in the cache + // int l = 0; + + // foreach ( var obj in streamUpdatePayload.Objects ) + // { + // obj._id = placeholders[ l ]._id; + // Context.SpeckleObjectCache[ allObjects[ l ].Hash ] = placeholders[ l ]; + // l++; + // } + + // // emit events, etc. + // Client.Stream.Layers = streamUpdatePayload.Layers.ToList(); + // Client.Stream.Objects = placeholders; + + // Context.NotifySpeckleFrame( "client-metadata-update", StreamId, Client.Stream.ToJson() ); + // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); + + // Client.BroadcastMessage( new { eventType = "update-global" } ); + + // IsSendingUpdate = false; + // if ( Expired ) + // { + // DataSender.Start(); + // } + // Expired = false; + //} + + public ClientRole GetRole( ) + { + return ClientRole.Sender; + } + + public string GetClientId( ) + { + return Client.ClientId; + } + + public void TogglePaused( bool status ) + { + Paused = status; + } + + public void ToggleVisibility( bool status ) + { + Visible = status; + } + + //public void ToggleLayerHover( string layerId, bool status ) + //{ + // Debug.WriteLine( "OHAI: " + layerId + " " + status ); + // Display.Enabled = true; + // Display.Geometry = new List(); + // if ( !status ) + // { + // Display.HoverRange = new Interval( 0, 0 ); + // RhinoDoc.ActiveDoc.Views.Redraw(); + // return; + // } + + // int myLIndex = RhinoDoc.ActiveDoc.Layers.Find( new Guid( layerId ), true ); + + // var objs1 = RhinoDoc.ActiveDoc.Objects.FindByUserString( "spk_" + this.StreamId, "*", false ); + // var cop = objs1; + // var objs = objs1.OrderBy( obj => obj.Attributes.LayerIndex ).ToList(); + // var count = objs.Count; + // foreach ( var obj in objs ) + // { + // if ( obj.Attributes.LayerIndex == myLIndex ) + // Display.Geometry.Add( obj.Geometry ); + // } + + // Display.HoverRange = new Interval( 0, Display.Geometry.Count ); + // RhinoDoc.ActiveDoc.Views.Redraw(); + + //} + + public void ToggleLayerVisibility(string layerId, bool status) + { + throw new NotImplementedException(); + } + + public void ToggleLayerHover(string layerId, bool status) + { + throw new NotImplementedException(); + } + + public void Dispose(bool delete = false) + { + if (delete) + { + //TODO: Fix this! + //var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString("spk_" + StreamId, "*", false); + //foreach (var o in objs) + // o.Attributes.SetUserString("spk_" + StreamId, null); + } + + DataSender.Dispose(); + MetadataSender.Dispose(); + //UnsetRhinoEvents(); + Client.Dispose(delete); + } + + public void Dispose() + { + DataSender.Dispose(); + MetadataSender.Dispose(); + //TODO: What events do we track if any? + //UnsetRhinoEvents(); + Client.Dispose(); + } + + //public void CompleteDeserialisation( Interop _Context ) + //{ + // Context = _Context; + + // Context.NotifySpeckleFrame( "client-add", StreamId, JsonConvert.SerializeObject( new { stream = Client.Stream, client = Client } ) ); + // Context.UserClients.Add( this ); + //} + + //protected RhinoSender( SerializationInfo info, StreamingContext context ) + //{ + // JsonConvert.DefaultSettings = ( ) => new JsonSerializerSettings() + // { + // ContractResolver = new CamelCasePropertyNamesContractResolver() + // }; + + // byte[ ] serialisedClient = Convert.FromBase64String( ( string ) info.GetString( "client" ) ); + + // using ( var ms = new MemoryStream() ) + // { + // ms.Write( serialisedClient, 0, serialisedClient.Length ); + // ms.Seek( 0, SeekOrigin.Begin ); + // Client = ( SpeckleApiClient ) new BinaryFormatter().Deserialize( ms ); + // StreamId = Client.StreamId; + // } + + // SetClientEvents(); + // SetRhinoEvents(); + // SetTimers(); + + // Display = new SpeckleDisplayConduit(); + // Display.Enabled = true; + //} + + public void GetObjectData( SerializationInfo info, StreamingContext context ) + { + using (var ms = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(ms, Client); + info.AddValue("client", Convert.ToBase64String(ms.ToArray())); + info.AddValue("paused", Paused); + info.AddValue("visible", Visible); + } + } + } +} diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index a959ab2..1101943 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -14,6 +14,8 @@ using Autodesk.Revit.Attributes; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Events; +using SpeckleRevitPlugin.UI; + #endregion namespace SpeckleRevitPlugin @@ -25,7 +27,7 @@ class AppMain : IExternalApplication static UIControlledApplication uiApp; static AppMain _thisApp; - internal static form_MainDock MainDock; + internal static FormMainDock MainDock; internal DockablePaneProviderData DockData; internal static SettingsHelper Settings { get; set; } @@ -176,7 +178,7 @@ public Result OnStartup(UIControlledApplication a) // Register the dockable pane if (MainDock == null) { - MainDock = new form_MainDock(); + MainDock = new FormMainDock(); DockData = new DockablePaneProviderData { FrameworkElement = MainDock, @@ -186,7 +188,9 @@ public Result OnStartup(UIControlledApplication a) } }; } - uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), MainDock as IDockablePaneProvider); + + uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), + MainDock as IDockablePaneProvider); // Detect when a new model is in focus a.ViewActivated += OnViewActivated; @@ -198,7 +202,10 @@ public Result OnStartup(UIControlledApplication a) return Result.Succeeded; } - catch { return Result.Failed; } + catch (Exception e) + { + return Result.Failed; + } } #endregion diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj index 17d0bbd..69ff79c 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj @@ -51,7 +51,12 @@ + + + + + @@ -73,7 +78,7 @@ - 63.0.2 + 63.0.1 11.0.1 @@ -92,6 +97,12 @@ Designer + + + {cfe27d3d-8a1a-43f9-9387-8fd9e119e174} + SpeckleCore + + diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml b/SpeckleRevitPlugin/UI/form_MainDock.xaml index f60d2b1..b24bb8e 100644 --- a/SpeckleRevitPlugin/UI/form_MainDock.xaml +++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml @@ -1,15 +1,12 @@ - - diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs index 6c32071..f16dff2 100644 --- a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs +++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs @@ -1,79 +1,71 @@ #region Namespaces using System; -using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; +using System.IO; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -using Autodesk.Revit.DB; using Autodesk.Revit.UI; -using Autodesk.Revit.UI.Events; using CefSharp; -using Visibility = System.Windows.Visibility; -using Path = System.IO.Path; -using System.IO; -using System.Diagnostics; +using SpeckleRevitPlugin.Classes; #endregion -namespace SpeckleRevitPlugin +namespace SpeckleRevitPlugin.UI { /// /// Interaction logic for form_MainDock.xaml /// - public partial class form_MainDock : Page, IDockablePaneProvider + public partial class FormMainDock : IDockablePaneProvider { - private ExternalEvent _extEvent; - private ExtCmdModeless _handler = new ExtCmdModeless(); + private readonly ExternalEvent _extEvent; + private readonly ExtCmdModeless _handler = new ExtCmdModeless(); + public static Interop Store; /// /// Main Dockable window /// - public form_MainDock() + public FormMainDock() { InitializeCef(); InitializeComponent(); InitializeChromium(); + + // initialise one store + Store = new Interop(Browser); + + // make them talk together + Browser.RegisterAsyncJsObject("Interop", Store); + _extEvent = ExternalEvent.Create(_handler); } - - void InitializeCef() + private static void InitializeCef() { - Cef.EnableHighDPISupport(); var assemblyLocation = Assembly.GetExecutingAssembly().Location; var assemblyPath = Path.GetDirectoryName(assemblyLocation); + if (assemblyPath == null) return; + var pathSubprocess = Path.Combine(assemblyPath, "CefSharp.BrowserSubprocess.exe"); CefSharpSettings.LegacyJavascriptBindingEnabled = true; var settings = new CefSettings { LogSeverity = LogSeverity.Verbose, LogFile = "ceflog.txt", - BrowserSubprocessPath = pathSubprocess - }; - - // Initialize cef with the provided settings + BrowserSubprocessPath = pathSubprocess, + RemoteDebuggingPort = 8088 + }; + // Initialize cef with the provided settings + settings.CefCommandLineArgs.Add("allow-file-access-from-files", "1"); + settings.CefCommandLineArgs.Add("disable-web-security", "1"); Cef.Initialize(settings); - } public void InitializeChromium() { #if DEBUG - - Browser.Address = @"http://localhost:9090/"; + Browser.Address = @"http://localhost:2020/"; #else var path = Directory.GetParent(Assembly.GetExecutingAssembly().Location); @@ -94,19 +86,29 @@ public void InitializeChromium() FileAccessFromFileUrls = CefState.Enabled, UniversalAccessFromFileUrls = CefState.Enabled }; - - - //Browser.Dock = DockStyle.Fill; } #region Form Events + /// - /// Safely raise an external command transaction event (for modeless) + /// Can't show the Dev Tools panel until window finishes initialization. /// /// /// - private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs e) + private void Form_MainDock_OnLoaded(object sender, RoutedEventArgs e) + { +#if DEBUG + Browser.ShowDevTools(); +#endif + } + + /// + /// Safely raise an external command transaction event (for modeless) + /// + /// + /// + private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs args) { try { @@ -116,11 +118,16 @@ private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs e) // Safe Update _extEvent.Raise(); } - catch { } + catch (Exception e) + { + Debug.WriteLine(e.Message); + } } + #endregion #region Dockable Pane + /// /// IDockablePaneProvider Implementation /// @@ -128,10 +135,13 @@ private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs e) public void SetupDockablePane(DockablePaneProviderData data) { data.FrameworkElement = this; - DockablePaneProviderData d = new DockablePaneProviderData(); - data.InitialState = new DockablePaneState(); - data.InitialState.DockPosition = DockPosition.Right; + var unused = new DockablePaneProviderData(); + data.InitialState = new DockablePaneState + { + DockPosition = DockPosition.Right + }; } + #endregion } } diff --git a/SpeckleView b/SpeckleView index 8f4850c..29f4379 160000 --- a/SpeckleView +++ b/SpeckleView @@ -1 +1 @@ -Subproject commit 8f4850c297f79ea4ad4165568d760b145c810c32 +Subproject commit 29f4379c354b8718edc4a8f91de96247d4259e87 diff --git a/UpgradeLog.htm b/UpgradeLog.htm new file mode 100644 index 0000000000000000000000000000000000000000..3d964baaa3d9b73c956aa32d38b50b833590d34f GIT binary patch literal 33092 zcmeI5`&Sf6mVoou-E;PTsL`F)UI%$8qCNH;o{AufAhwQu<{%&-h)D6$nb|*mcfT)! zFf*%)q_PBi`mCo;Rc1y;+_)8OG!R%J{BlX^J_YgSu zvaM{F<2to=`8}Y8&?4*x&{J=X>?HKMOF#EGYjWKTkgtS3b}4OUtJGiT?h5C99JeSD zlP2G{K=0#PyzZU0KFF@nm%XghW-Xk*4|uGAiFG4({@$nVI@f*l*W^5M;vn0jWIg*x z=@#@`YogSyFPj7QZni;<6@J%4+x~O=k}D_l+y8|JKLwmNfwB(0ncAJ&w8BW&{n>xfVjNu8fS{%(NPpt$ZJ?n+ znOe}`tF*Vk*Q+(|uEJ%t!tw^N`fK&BuG?=NjQ?J%cB1nQ9A3?~1Fs!lyYzbxjIUJD5I-z(W7+}_~65_palUdfcy z67@DIb$omMAjo8$Q5Ub5x!#9nM_Vk>CTTJH_ zge0|f;ruhmuZ}iF{eK7tq(MFm(r!Oao3m3B$BP)Lk?%!+)jm?M5TQlid<5p}*>Q*jw0y>5AD~0e=9qSjuW}u| zaFdey<9O&BjN@Dy#j!P*jef=0%3)rdgp>AkvOBlG*qug}pZ_*mei52D9Q3DJ6PrXG zZ%(~!QP1?&-ZMUL;7`=NJv8z|_U=?89cM-Xdi?r2Is8p^^5XR;*UhrNt2e#+l8ds8 zm#y2~_`CyjB}Q^$V`OgRSZ^%#ItHB0MenQT?(lKu7iUk-ZH9zWgzjODoPWzJ<)rniWrFchGphqc!zD z@iOIT`jC^BU;_)_m{PONH}xi=9I6qzQZMFCQcS9?=?#9a0T=Jd+P%dvdgW|lrskzRM z)rNd7sR#P{k=QAUh~90Q5gxyFM|pj1ihh{Bnh zS<@mM^~ZKw!OGkt8|kMtTW!Rh=6!nE7&QsU`Jk96fpV-8>zlQY5&AW(Sw8o@MtQPK z=JjKi)MzNSvn*!W8pQGDAH8z^nexW|DReC$5Rbm`O{aMX_$Q zFv2h&P;X9fGf?5`S&=65+n29{iEpibU#=wX*3*~zBQeTziLs{zZsl}QK2e^nHS(w_ zdJ4zzA(Anl=)LU#_Yhy6&xu|AjZtuMZWm`rVo^7%bF&3+C8<_jz66iaP1q+|#D9l-49y!fYdr+bDHcTzVNhSHtLwn(A-8k@xo? zpMRrOcc1o%HeaV1I*kD>>-5rLzCGGJBdc7C zw4h$8QA*Sixe}t_ypD0wTkhN?TKyx(`Zn(>5X!3nMJ&Jds9C{Y7^&8?bV+9-fBFhM zG?u?Z9^G+RPb0rp_GQ}`<(n*=S1GdA_8oKeDd<3wV^P9&E#TD_3s~;^TT3)jR8OA0 z@_sB>`UWfi(b7qIUGCld(s~hHT;&tvjVZ>dP|dBZg?$8h%^B{Q9jU7GB1n8U%tkgU zYfh23agAfYrPk$3&Ye&0RLZsQ9^Sy!vtH-%38KCwUmeY1)*F*)oOg<@`{0*5i`=c? z3aoR`a#=kctnSMlOs=`4HPL%PPrhxOR@P551ySqcUZh-CFKY~3zCOlybRJ$C9c#-c zIge}WMi`DobEe)cM>+3<4UBQfn^5ZmT78pRGt^F5m3)2@vm?ii>l-c-w&s>t)L;H}-a*9dLRo4Bh{d(vPPL(Ih~o@+hg&anh9u5+#h8X z8>eOCU+#`&3(Q%^*lnBYzr}9%6BllNDVadZ<4EQO$H%9ou42R%Pq6W0^tnvW$MEsZ zY56!BJB8$@=wTV$ZqfQ9k@XT9^PKMlzt7mm^pGfWkVtETeqUveXf;kx?#IJ;wD0XB)tOz?GTWF=}knzk6L@a6X65%~3~dv_`#sT0dr=!Wz`g(8B`zpkF}e zE$-~Xsa0@$3Rd>+Jm5}PNo~TL_q139n*pH7=_Bgx!nLPx#2MQxWuNKwh??{CT-mUTb9Qf}!KcU=S2k7Mi_oe$GUet~>vIgZj|g7YWzIRyrXq0I*E zci@5W%-KAluSKYPPhZ18dCqZ!I}5;`<$Ml0*1`7?(m&$Y9UyYx23U>-xjmxR5_ryn zm$Aka_m1fE2>xr)PJrQFkvCv81Fv3!!8&y&;D=e_U%+t+eBXlK2XI&dpEY_P0q!IE zaJ{>qJDZd&(Pofd9R+W}@eOs{Upi(*`)Ka_<9u z89(kI#SdWp7%1N;MVBJ2%ZYPRUuBP0hsv12NZz&EzoR44Zlf%Xf@@{EUVEfK7ZCyyGm^cMVIoi}haPd&r$<*k7My z8m0APJjEd8MjCTK;Pq7=~b>Lfd2xEIuX2%)_wt)tJvgeU>r8IIDgUeicT@4C$OQ`)T3=MKKWKIfryh*r{ZLfLfabCK4@Da}w*STCUV z7cjXC)Vtj22d5*>Hh|(DXrrG=&Q?Rq4S4*F*2Xz+>FXV>#%c2q_=Dk&T>S_?=b_*+ zFx>61!hL=I4ji4OkA>h7XYh$$H=)V7;RCpO7i#VXUp+vduYkQxZ8NO>U^+^zXIvlC zW+8BTh>~G2Gr}Iik1m6!k>lr3_L6(^K-)oP6F_?fZ8x}Qgfbd9qxatc&-auJLgy5? zd_o$Z!NOV1Ea#T%^_9FkvW(njXmb*@>WH$poPFZ1QeK98x552R(6U*|??Bf=z-WSg zHo31Xhbh_S_bEJl5qfKeUMHb+5NeEkoT-|f_z*tHpBu6MwjRBl1G^kgUhd<&KR-Z= zzGo~_cA%^eoiY|>{rT?4e>*=uhVAVoW1*+gb{8`id)UfFZ0`bAb%LXHrm?@bIF4a! z$0?iR&I!kTZ1-re(JPeu6sPm%2`u>{cJ~=avk-T<8pGx;;D7WITJ;y)GrP5bH+X@y z{v}v{t^O}~jTvzIM9bS8Khn!Ey&q85nd%c-n=x^|ZYJh7HD3W?7`s2gXO{aff#O(R z2p;wp{&5NlK4SgnI3D7mj`1Rs;Pr{mXXtYl_>OyerFo#=pv@TNH-O@^XU^=G!Nh2! z3D36iAm#?1g3}RjMxf2v_8zsw)EvtQb(}r-gV8ou!W*HFMW`|Ru?w8TK=Ez*cRkz) zVwBeX9AD6S7KoNTq=k2k7nXo=gI$9EYiI2Ggv~E*N=d8an%dJPoIA@p;7kW$vk4@-lhHaqc{$#cjB{4^B$b&;4&W z)iGuR&@!R`W$Ph z&wGY#Ge>xg?S6_Ed5(=6#VU_srH}b_PBDb7Jfxl(>M`uAb2Vp3Q&?{2Ipf&KDXgh> z)>+py<*rH$(9bF*W{AxeAJTdg`>H>+Z|^umnxht3ygsng{6nZ6%UYTkSVTf9q4^UjOl`T(2#fF7r~+QLeI=CcDvgOtxfhqFd~ z;W9JCePFzy?I67z2M;)bJ=c#t#s|Ej=1b^V5BRK5G6z@Qg1f7NN6=x`(>d26aGUhK z$Y+o?x4>~2DDriL`@_H(4loaCA+IK>@fa@Np|_V@-vzHxup9&WD`2|X;GQ5?k!OOZ zahJqkkk$+}Zc}dyoY$!J5_-)6Peao*M2I$*W zMPnYhc0die{vJ8M0iM467!T@B`&X2|0J?hMy+O*&=0BwFHjvD48x@HEAa~5tzXHR3 zYEN)A2@S>t2S9XYdIEj@eD|o;4DBAkwRhmQOuws?Y;fleJYS>5JbjPzkt;j&ryh#U z7BbLpJNw^7I?l=`>HPs%-r?@&z`GfEy+m*C_{@O&6u2A%%enO#2 zYge6~!Y65+gr-|iX58ei(!20+k^a@eZOYTOu3Pn%l&Ux(K25L)F0Q*Cz(sd?p5MZi zC3A1C_1YqN;dFGBJidRzU8H8O^v`l2u9@d8THm?SkDRxtYx!~5u@hJM)3X(=>-??l z(UD>aqFs?tHHR9Ik%j|#=w^3j_FWgs~bHSBD@lh)4$XwY~^JA}olr0@4+P>VbnKd_~tA6^lgm%!~I9h{hyp4p)_D2ht@LU2$+>89|wD)nan?Hqo z{YxKTJMMkTBU(qba0maMXw@3F>w?ks0VA$x{hGuF`gvz1IfmD$x6gPuPpjkDPyXUM zT)a^*@9N$KZo2T_|5EE5%BKC6rcu$rnam5T%++U`bNz^sKu@$es zg72^FVv8Iaqqg*`U1o6Bv0XlU7d4|>u0(927x)8jdS+q>ajT^_db($TgIHgU`;#VS7L@zqVRx78k!cQ1Cr`WhUqyK`KeI^ppb z%FB9|Wv)lf`fk5E+k~odck%WsNvPAfJKK&{k2~Ad9sl3L zjyABw+TwOvFVDi3H9xmo@ja=IN%u<_T{s$jm)P~-o7``pcdj0TMXM9f-RJBa((I3s zkZTmax1xJE)wZV_>lN!%S*cFA=CO9QrLD%Vs@E8CIuCJ2V2TQhQD>)!G35waXSL1` zc`8Z`w_>SMOKh-)`GeOKJHOG$-aWs`a@W%dLdcn9Mow zkL(|m5AZcMah#`n<;btP|0sQbyTv56Xw~jWZTH$8b*GBQsiy{6B%gvdn`VTTzkOJ_ zD?8srti+WmUg;V){`_t@y!qUtR;UVMHxnS*d{w3_x&dwkG`ZI%}5yk_2(R;5EAS-&n2JXvk~gsb7KYQwOdQS6hc%e%RII&D>3% zA@fNZBX@V?S8?q;`%|8II$KPssR!`TtW?jE`K4xk}(JFQ0-Kymy#3~tizfOMZ z3OZSoTbwygFh*QIiSO#a0nE-E>n7(^2b*ghv2p)vT>aQ*Rm$g5j&xo zpmS{{Va)HZtC#)*$qrYpIOKjVty)x%YH9RyF*3-l^VeD%ajLdBRd?g+e7?r+`n>se>T21z7@|W zVFh?U=$4tCC1Aw=SFu&=-N=Wbj(xViX(smS+HPEj5>NFn9yi0$|8t3FaaF7pyQjg< zr?@I#(<*vS`zhBG%k$v63hvI-gk3wtSmS(H`QEu^1-xRd^I?|vsaL^mt$9A|vRABv z-CDBcf0I{k^BEci`-$O4>8l^Kbz@idYus(F_%YX+*wi1ZWQ-e z{S@Td+3M!D%(E0(L^;O@&GSum_qJh^N1WS>R;}4lmFgi|8A8Bj`xiKi+s`7=TU2C?^}$_o*^y&$w)d< zgeR|yF=ROcDO0ECHEUqp(Hd)(qn)!O!*ado^Ydsn#X{0mGeU@!l=N`L_rS5f{|3~{-ju4xt_V#B+hjTPX!5*Jz zAB^IYZ$IX~HY4r7b2i*Po_g=>}c-I}`JF%36mdEulOY%3Bx%bSycx&MuWbV>W z@9@fJ-0EoTFJ;K{dAk`dcU6mh91ro0gXOnQxHBlNkokY7W(|D8-FMA5Qn_t`N&Le%FwGzVD~a&MXcLQu9Ii68e_V;kT*#H!mE zErywePP23G4xc`D+53L3+nkMa9AcHm{~z_~MxW>#p>&egz87E;O)(NLdcmY|XIs#A zeJULF}-8#!S%RaQ*Ti@`tJj5a*lyxui1x1j1%JH6!`#bmGsX-8sO zF)C_OCoc4=twp%MUmsf5mPkjDvb2WM8W)}KOD|H>llN_nFA{cRSYBhW8AJ(S2w$g8lvpGmtJ{HmHFLnU(#A= ztDJh*f)uBy#oc<+q&cnjC1Lmf*S;hUT&{g_^cCl@zAMAk7v)kuFRl_9Wgei{JJkC> DLHoom literal 0 HcmV?d00001 From 878eb83253105eca8d2014a6baf1c4116eaa17a6 Mon Sep 17 00:00:00 2001 From: ksobon Date: Tue, 22 May 2018 13:42:16 -0400 Subject: [PATCH 2/6] stream serialization in extensible storage --- SpeckleRevit.sln.DotSettings.user | 1 + SpeckleRevitPlugin/Classes/Interop.cs | 195 +++++++++----- SpeckleRevitPlugin/Classes/RevitReceiver.cs | 23 +- .../{SpeckleRhinoSender.cs => RevitSender.cs} | 12 +- SpeckleRevitPlugin/Classes/SettingsHelper.cs | 225 +++++++++-------- SpeckleRevitPlugin/Entry/AppMain.cs | 237 ++++++++++-------- SpeckleRevitPlugin/Entry/ExtCmd.cs | 12 +- SpeckleRevitPlugin/Entry/ExtCmdModeless.cs | 108 -------- .../Entry/SpeckleRequestHandler.cs | 91 +++++++ .../Properties/Resources.Designer.cs | 20 +- SpeckleRevitPlugin/Properties/Resources.resx | 6 + SpeckleRevitPlugin/SpeckleRevitPlugin.addin | 4 +- SpeckleRevitPlugin/SpeckleRevitPlugin.csproj | 17 +- SpeckleRevitPlugin/UI/form_Main.xaml.cs | 25 +- SpeckleRevitPlugin/UI/form_MainDock.xaml.cs | 10 +- .../Utilities/BinaryFormatterUtilities.cs | 92 +++++++ SpeckleRevitPlugin/Utilities/JsonUtilities.cs | 48 ++++ .../Utilities/SchemaUtilities.cs | 127 ++++++++++ 18 files changed, 818 insertions(+), 435 deletions(-) rename SpeckleRevitPlugin/Classes/{SpeckleRhinoSender.cs => RevitSender.cs} (98%) delete mode 100644 SpeckleRevitPlugin/Entry/ExtCmdModeless.cs create mode 100644 SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs create mode 100644 SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs create mode 100644 SpeckleRevitPlugin/Utilities/JsonUtilities.cs create mode 100644 SpeckleRevitPlugin/Utilities/SchemaUtilities.cs diff --git a/SpeckleRevit.sln.DotSettings.user b/SpeckleRevit.sln.DotSettings.user index 4e1d7a6..ef2654a 100644 --- a/SpeckleRevit.sln.DotSettings.user +++ b/SpeckleRevit.sln.DotSettings.user @@ -2,4 +2,5 @@ <AssemblyExplorer> <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\cefsharp.wpf\63.0.2\CefSharp\x64\CefSharp.Wpf.dll" /> <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\newtonsoft.json\11.0.1\lib\net45\Newtonsoft.Json.dll" /> + <Assembly Path="C:\Windows\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll" /> </AssemblyExplorer> \ No newline at end of file diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs index 036acef..f5550f3 100644 --- a/SpeckleRevitPlugin/Classes/Interop.cs +++ b/SpeckleRevitPlugin/Classes/Interop.cs @@ -4,11 +4,16 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; +using Autodesk.Revit.DB; using CefSharp; using CefSharp.Wpf; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using SpeckleCore; +using SpeckleRevitPlugin.Entry; +using SpeckleRevitPlugin.Utilities; #endregion namespace SpeckleRevitPlugin.Classes @@ -21,38 +26,97 @@ namespace SpeckleRevitPlugin.Classes public class Interop : IDisposable { private List _userAccounts; - public ChromiumWebBrowser Browser; public List UserClients; public Dictionary SpeckleObjectCache; + public ChromiumWebBrowser Browser; public bool SpeckleIsReady; public bool SelectionInfoNeedsToBeSentYeMighty = false; // should be false public Interop(ChromiumWebBrowser originalBrowser) { - // Makes sure we always get some camelCaseLove + // (Luis) Makes sure we always get some camelCaseLove JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }; - Browser = originalBrowser; - UserClients = new List(); _userAccounts = new List(); + UserClients = new List(); SpeckleObjectCache = new Dictionary(); + Browser = originalBrowser; ReadUserAccounts(); + + AppMain.OnModelSynched += Revit_ModelSynched; + SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved; + } + + private void OnClientsRetrieved(IDictionary recivers, IDictionary senders) + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var kv in recivers) + { + var serialisedClient = Convert.FromBase64String(kv.Value); + var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); + if (client.Client == null) return; + + client.CompleteDeserialisation(this); + + + //using (var ms = new MemoryStream()) + //{ + // ms.Write(serialisedClient, 0, serialisedClient.Length); + // ms.Seek(0, SeekOrigin.Begin); + // var bf = new BinaryFormatter + // { + // Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) + // }; + // var client = (RevitReceiver)bf.Deserialize(ms); + // client.CompleteDeserialisation(this); + //} + } + + foreach (var kv in senders) + { + var serialisedClient = Convert.FromBase64String(kv.Value); + var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); + if (client.Client == null) return; + + client.CompleteDeserialisation(this); + //var serialisedClient = Convert.FromBase64String(kv.Value); + + //using (var ms = new MemoryStream()) + //{ + // ms.Write(serialisedClient, 0, serialisedClient.Length); + // ms.Seek(0, SeekOrigin.Begin); + // var bf = new BinaryFormatter + // { + // Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) + // }; + // var client = (RevitSender)bf.Deserialize(ms); + // client.CompleteDeserialisation(this); + //} + } + } + + private void Revit_ModelSynched(Document doc) + { + SaveFileClients(doc); } - public void SetBrowser( ChromiumWebBrowser browser ) + public void SetBrowser(ChromiumWebBrowser browser) { Browser = browser; } - public void Dispose( ) + public void Dispose() { RemoveAllClients(); + + AppMain.OnModelSynched -= Revit_ModelSynched; + SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved; } - public void ShowDev( ) + public void ShowDev() { Browser.ShowDevTools(); } @@ -75,68 +139,70 @@ public string GetDocumentGuid() /// Do not call this from the constructor as you'll get confilcts with /// browser load, etc. /// - public void AppReady( ) + public void AppReady() { SpeckleIsReady = true; - InstantiateFileClients(); + + // (Konrad) This is the thread safe way of interacting with Revit. + // Also it's possible that the Speckle app initiates before Revit + // Document is open/ready making Extensible Storage inaccessible. + AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients); + AppMain.SpeckleEvent.Raise(); } - //TODO: This is called when document is saved. In Revit that would be either document save or synch events. - //public void SaveFileClients( ) - //{ - // RhinoDoc myDoc = RhinoDoc.ActiveDoc; - // foreach ( ISpeckleRhinoClient rhinoClient in UserClients ) - // { - // using ( var ms = new MemoryStream() ) - // { - // var formatter = new BinaryFormatter(); - // formatter.Serialize( ms, rhinoClient ); - // string section = rhinoClient.GetRole() == ClientRole.Receiver ? "speckle-client-receivers" : "speckle-client-senders"; - // var client = Convert.ToBase64String( ms.ToArray() ); - // var clientId = rhinoClient.GetClientId(); - // RhinoDoc.ActiveDoc.Strings.SetString( section, clientId, client ); - // } - // } - //} + /// + /// + /// + /// + public void SaveFileClients(Document doc) + { + var senders = new Dictionary(); + var receivers = new Dictionary(); + foreach (var rClient in UserClients) + { + using (var ms = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(ms, rClient); + var client = Convert.ToBase64String(ms.ToArray()); + var clientId = rClient.GetClientId(); + + if (rClient.GetRole() == ClientRole.Receiver) receivers.Add(clientId, client); + else senders.Add(clientId, client); + } + } + + var pInfo = SchemaUtilities.GetProjectInfo(doc); + var schemaExists = SchemaUtilities.SchemaExist(Properties.Resources.SchemaName); + var schema = schemaExists + ? SchemaUtilities.GetSchema(Properties.Resources.SchemaName) + : SchemaUtilities.CreateSchema(); + + using (var trans = new Transaction(doc, "Store Clients")) + { + trans.Start(); + + if (schemaExists) + { + SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "senders", senders); + SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "receivers", receivers); + } + else + { + SchemaUtilities.AddSchemaEntity(schema, pInfo, "senders", senders); + SchemaUtilities.AddSchemaEntity(schema, pInfo, "receivers", receivers); + } + + trans.Commit(); + } + } - public void InstantiateFileClients() + /// + /// + /// + public void InstantiateFileClients(Dictionary recivers, Dictionary senders) { - //TODO: these can be stored in a extensible storage schema and then de-serialized here! - //if (!SpeckleIsReady) return; - - //Debug.WriteLine("Instantiate file clients."); - - //string[] receiverKeys = RhinoDoc.ActiveDoc.Strings.GetEntryNames("speckle-client-receivers"); - - //foreach (string rec in receiverKeys) - //{ - // //if ( UserClients.Any( cl => cl.GetClientId() == rec ) ) - // // continue; - - // byte[] serialisedClient = Convert.FromBase64String(RhinoDoc.ActiveDoc.Strings.GetValue("speckle-client-receivers", rec)); - // using (var ms = new MemoryStream()) - // { - // ms.Write(serialisedClient, 0, serialisedClient.Length); - // ms.Seek(0, SeekOrigin.Begin); - // RhinoReceiver client = (RhinoReceiver)new BinaryFormatter().Deserialize(ms); - // client.Context = this; - // } - //} - - //string[] senderKeys = RhinoDoc.ActiveDoc.Strings.GetEntryNames("speckle-client-senders"); - - //foreach (string sen in senderKeys) - //{ - // byte[] serialisedClient = Convert.FromBase64String(RhinoDoc.ActiveDoc.Strings.GetValue("speckle-client-senders", sen)); - - // using (var ms = new MemoryStream()) - // { - // ms.Write(serialisedClient, 0, serialisedClient.Length); - // ms.Seek(0, SeekOrigin.Begin); - // RhinoSender client = (RhinoSender)new BinaryFormatter().Deserialize(ms); - // client.CompleteDeserialisation(this); - // } - //} + } #region Account Management @@ -150,6 +216,9 @@ public string GetUserAccounts( ) }); } + /// + /// + /// private void ReadUserAccounts( ) { _userAccounts = new List(); diff --git a/SpeckleRevitPlugin/Classes/RevitReceiver.cs b/SpeckleRevitPlugin/Classes/RevitReceiver.cs index 0688919..245c7e8 100644 --- a/SpeckleRevitPlugin/Classes/RevitReceiver.cs +++ b/SpeckleRevitPlugin/Classes/RevitReceiver.cs @@ -2,17 +2,18 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using SpeckleCore; +using SpeckleRevitPlugin.Utilities; namespace SpeckleRevitPlugin.Classes { /// - /// Class that holds a rhino receiver client warpped around the - /// SpeckleApiClient. + /// Class that holds a rhino receiver client warpped around the SpeckleApiClient. /// [Serializable] public class RevitReceiver : ISpeckleRevitClient @@ -24,6 +25,10 @@ public class RevitReceiver : ISpeckleRevitClient public bool Paused { get; set; } public bool Visible { get; set; } = true; + public RevitReceiver() + { + } + public RevitReceiver( string payload, Interop parent ) { Context = parent; @@ -254,6 +259,14 @@ public void Dispose() Client.Dispose(); } + public void CompleteDeserialisation(Interop _Context) + { + Context = _Context; + + Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client })); + Context.UserClients.Add(this); + } + protected RevitReceiver(SerializationInfo info, StreamingContext context) { JsonConvert.DefaultSettings = () => new JsonSerializerSettings @@ -269,7 +282,11 @@ protected RevitReceiver(SerializationInfo info, StreamingContext context) { ms.Write(serialisedClient, 0, serialisedClient.Length); ms.Seek(0, SeekOrigin.Begin); - Client = (SpeckleApiClient)new BinaryFormatter().Deserialize(ms); + var bf = new BinaryFormatter + { + Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) + }; + Client = (SpeckleApiClient)bf.Deserialize(ms); StreamId = Client.StreamId; } diff --git a/SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs b/SpeckleRevitPlugin/Classes/RevitSender.cs similarity index 98% rename from SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs rename to SpeckleRevitPlugin/Classes/RevitSender.cs index aee1b7b..e5bbca7 100644 --- a/SpeckleRevitPlugin/Classes/SpeckleRhinoSender.cs +++ b/SpeckleRevitPlugin/Classes/RevitSender.cs @@ -524,13 +524,13 @@ public void Dispose() Client.Dispose(); } - //public void CompleteDeserialisation( Interop _Context ) - //{ - // Context = _Context; + public void CompleteDeserialisation(Interop _Context) + { + Context = _Context; - // Context.NotifySpeckleFrame( "client-add", StreamId, JsonConvert.SerializeObject( new { stream = Client.Stream, client = Client } ) ); - // Context.UserClients.Add( this ); - //} + Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client })); + Context.UserClients.Add(this); + } //protected RhinoSender( SerializationInfo info, StreamingContext context ) //{ diff --git a/SpeckleRevitPlugin/Classes/SettingsHelper.cs b/SpeckleRevitPlugin/Classes/SettingsHelper.cs index a8fa48e..d8812f6 100644 --- a/SpeckleRevitPlugin/Classes/SettingsHelper.cs +++ b/SpeckleRevitPlugin/Classes/SettingsHelper.cs @@ -1,112 +1,127 @@ #region Namespaces -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - using Autodesk.Revit.ApplicationServices; using Autodesk.Revit.DB; using Autodesk.Revit.UI; -using Autodesk.Revit.UI.Selection; +using SpeckleRevitPlugin.Entry; #endregion -namespace SpeckleRevitPlugin +namespace SpeckleRevitPlugin.Classes { - public class SettingsHelper - { - private ExternalCommandData _cmd; - private Application _app; - private Document _doc; - - /// - /// Constructor - /// - /// - public SettingsHelper(ExternalCommandData cmd) - { - _doc = null; - _cmd = cmd; - } - - /// - /// Constructor - from application (events) - /// - /// - /// - public SettingsHelper(Document d, Application a) - { - _app = a; - _doc = d; - } - - #region Command Data - /// - /// Revit UI application - /// - public UIApplication UiApp - { - get - { - try - { - return _cmd.Application; - } - catch { } - return null; - } - } - - /// - /// Revit Application - /// - public Application App - { - get - { - try - { - return _cmd.Application.Application; - } - catch { } - return null; - } - } - - /// - /// Revit UI Document - /// - public UIDocument UiDoc - { - get - { - try - { - return _cmd.Application.ActiveUIDocument; - } - catch { } - return null; - } - } - - /// - /// Revit Document - /// - public Document ActiveDoc - { - get - { - try - { - return UiDoc.Document; - } - catch { } - return null; - } - } - #endregion - - #region Public Properties - Modeless - internal EnumCommandType CommandType { get; set; } - #endregion - } -} \ No newline at end of file + //public class SettingsHelper + //{ + // //private readonly ExternalCommandData _cmd; + // private Application _app; + // private Document _doc; + + // ///// + // ///// Constructor + // ///// + // ///// + // //public SettingsHelper(ExternalCommandData cmd) + // //{ + // // _doc = null; + // // _cmd = cmd; + // //} + + // /// + // /// Constructor - from application (events) + // /// + // /// + // /// + // public SettingsHelper(Document d, Application a) + // { + // _app = a; + // _doc = d; + // } + + // #region Command Data + + // /// + // /// Revit UI application + // /// + // public UIApplication UiApp + // { + // get + // { + // try + // { + // return _doc.Application.; + // } + // catch + // { + // // ignored + // } + + // return null; + // } + // } + + // /// + // /// Revit Application + // /// + // public Application App + // { + // get + // { + // try + // { + // return _cmd.Application.Application; + // } + // catch + // { + // // ignored + // } + + // return null; + // } + // } + + // /// + // /// Revit UI Document + // /// + // public UIDocument UiDoc + // { + // get + // { + // try + // { + // return _cmd.Application.ActiveUIDocument; + // } + // catch + // { + // // ignored + // } + + // return null; + // } + // } + + // /// + // /// Revit Document + // /// + // public Document ActiveDoc + // { + // get + // { + // try + // { + // return UiDoc.Document; + // } + // catch + // { + // // ignored + // } + + // return null; + // } + // } + + // #endregion + + // #region Public Properties - Modeless + + // internal EnumCommandType CommandType { get; set; } + + // #endregion + //} +} diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index 1101943..7179ccc 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -1,37 +1,135 @@ #region Namespaces -using Microsoft.VisualBasic; -using System; -using System.Collections; using System.Collections.Generic; -using System.Data; using System.Diagnostics; -using System.Windows.Media.Imaging; using System.IO; using System.Reflection; using System.Windows.Media; - -using Autodesk.Revit.ApplicationServices; +using System.Windows.Media.Imaging; using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.Events; using Autodesk.Revit.UI; -using Autodesk.Revit.UI.Events; +using SpeckleRevitPlugin.Classes; using SpeckleRevitPlugin.UI; - +using SpeckleRevitPlugin.Utilities; #endregion -namespace SpeckleRevitPlugin +namespace SpeckleRevitPlugin.Entry { [Transaction(TransactionMode.Manual)] - class AppMain : IExternalApplication + public class AppMain : IExternalApplication { - static string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - static UIControlledApplication uiApp; - static AppMain _thisApp; + private static readonly string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + private static UIControlledApplication uiApp; + private static AppMain _thisApp; + public static ExternalEvent SpeckleEvent; + public static SpeckleRequestHandler SpeckleHandler = new SpeckleRequestHandler(); internal static FormMainDock MainDock; internal DockablePaneProviderData DockData; - internal static SettingsHelper Settings { get; set; } + //internal static SettingsHelper Settings { get; set; } + + public delegate void ModelSynched(Document doc); + public static event ModelSynched OnModelSynched; + + public Result OnStartup(UIControlledApplication a) + { + try + { + uiApp = a; + _thisApp = this; + + if (MainDock == null) + { + MainDock = new FormMainDock(); + DockData = new DockablePaneProviderData + { + FrameworkElement = MainDock, + InitialState = new DockablePaneState + { + DockPosition = DockPosition.Right + } + }; + } + + uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), MainDock); + + // (Konrad) We are going to use this External Event Handler all across Speckle + // It's best to keep it on the main app, and keep it public. + SpeckleHandler = new SpeckleRequestHandler(); + SpeckleEvent = ExternalEvent.Create(SpeckleHandler); + + a.ControlledApplication.DocumentCreated += OnDocumentCreated; + a.ControlledApplication.DocumentOpened += OnDocumentOpened; + a.ControlledApplication.DocumentSynchronizedWithCentral += OnDocumentSynchronized; + a.ControlledApplication.DocumentSaving += OnDocumentSaving; + + AddRibbonPanel(a); + + return Result.Succeeded; + } + catch + { + return Result.Failed; + } + } + + private static void OnDocumentSaving(object sender, DocumentSavingEventArgs e) + { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) return; + + OnModelSynched?.Invoke(doc); + } + + private static void OnDocumentSynchronized(object sender, DocumentSynchronizedWithCentralEventArgs e) + { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) return; + + OnModelSynched?.Invoke(doc); + } + + private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e) + { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) + { + HideDockablePane(); + } + else + { + //Settings = new SettingsHelper(doc, doc.Application); + //OnModelReady?.Invoke(doc); + } + + + // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc + } + + private void OnDocumentCreated(object sender, DocumentCreatedEventArgs e) + { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) + { + HideDockablePane(); + } + else + { + //Settings = new SettingsHelper(doc, doc.Application); + //OnModelReady?.Invoke(doc); + } + + // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc + } + + public Result OnShutdown(UIControlledApplication a) + { + return Result.Succeeded; + } + + #region Utilities - #region Setup /// /// Load an Image Source from File /// @@ -43,13 +141,13 @@ private ImageSource LoadPngImgSource(string SourceName) try { // Assembly - Assembly assembly = Assembly.GetExecutingAssembly(); + var assembly = Assembly.GetExecutingAssembly(); // Stream - Stream icon = assembly.GetManifestResourceStream(SourceName); + var icon = assembly.GetManifestResourceStream(SourceName); // Decoder - PngBitmapDecoder decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); + var decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); // Source ImageSource m_source = decoder.Frames[0]; @@ -57,11 +155,10 @@ private ImageSource LoadPngImgSource(string SourceName) } catch { + // ignored } - // Fail return null; - } /// @@ -82,13 +179,13 @@ public void AddRibbonPanel(UIControlledApplication a) } // Tools - AddButton("Speckle", + AddButton("Speckle", + "Plugin\r\nTest", "Plugin\r\nTest", - "Plugin\r\nTest", - "SpeckleRevitPlugin.Resources.Template_16.png", - "SpeckleRevitPlugin.Resources.Template_32.png", - (m_Path + "\\SpeckleRevitPlugin.dll"), - "SpeckleRevitPlugin.ExtCmd", + "SpeckleRevitPlugin.Resources.Template_16.png", + "SpeckleRevitPlugin.Resources.Template_32.png", + (m_Path + "\\SpeckleRevitPlugin.dll"), + "SpeckleRevitPlugin.Entry.ExtCmd", "Speckle connection test for Revit."); } @@ -113,7 +210,7 @@ private bool AddButton(string Rpanel, string ButtonName, string ButtonText, stri RibbonPanel ribbonPanel = null; // Find the Panel within the Case Tab - List rp = new List(); + var rp = new List(); rp = uiApp.GetRibbonPanels("Speckle"); foreach (RibbonPanel x in rp) { @@ -130,7 +227,7 @@ private bool AddButton(string Rpanel, string ButtonName, string ButtonText, stri } // Create the Pushbutton Data - PushButtonData pushButtonData = new PushButtonData(ButtonName, ButtonText, dllPath, dllClass); + var pushButtonData = new PushButtonData(ButtonName, ButtonText, dllPath, dllClass); if (!string.IsNullOrEmpty(ImagePath16)) { try @@ -156,84 +253,14 @@ private bool AddButton(string Rpanel, string ButtonName, string ButtonText, stri pushButtonData.ToolTip = Tooltip; // Add the button to the tab - PushButton pushButtonDataAdd = (PushButton)ribbonPanel.AddItem(pushButtonData); + var unused = (PushButton)ribbonPanel.AddItem(pushButtonData); } catch { - // Quiet Fail + // ignored } return true; } - #endregion - - #region Startup - public Result OnStartup(UIControlledApplication a) - { - try - { - // The Shared uiApp variable - uiApp = a; - _thisApp = this; - - // Register the dockable pane - if (MainDock == null) - { - MainDock = new FormMainDock(); - DockData = new DockablePaneProviderData - { - FrameworkElement = MainDock, - InitialState = new DockablePaneState - { - DockPosition = DockPosition.Right - } - }; - } - - uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), - MainDock as IDockablePaneProvider); - - // Detect when a new model is in focus - a.ViewActivated += OnViewActivated; - - // Add the Ribbon Panel!! - AddRibbonPanel(a); - - // Return Success - return Result.Succeeded; - - } - catch (Exception e) - { - return Result.Failed; - } - } - #endregion - - #region Shutdown - public Result OnShutdown(UIControlledApplication a) - { - return Result.Succeeded; - } - #endregion - - #region Internal Members - Events - /// - /// View change will detect a model change - /// - /// - /// - void OnViewActivated(object sender, ViewActivatedEventArgs e) - { - try - { - if (Settings == null) Settings = new SettingsHelper(e.Document, null); - if (Settings.App == null) - { - HideDockablePane(); - } - } - catch { } - } /// /// Close the Dockable Pane @@ -242,11 +269,15 @@ internal void HideDockablePane() { try { - DockablePane m_dp = uiApp.GetDockablePane(GlobalHelper.MainDockablePaneId); + var m_dp = uiApp.GetDockablePane(GlobalHelper.MainDockablePaneId); m_dp.Hide(); } - catch { } + catch + { + // ignored + } } + #endregion } } diff --git a/SpeckleRevitPlugin/Entry/ExtCmd.cs b/SpeckleRevitPlugin/Entry/ExtCmd.cs index cfc91c6..c69c54a 100644 --- a/SpeckleRevitPlugin/Entry/ExtCmd.cs +++ b/SpeckleRevitPlugin/Entry/ExtCmd.cs @@ -1,18 +1,16 @@ #region Namespaces - using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; +using SpeckleRevitPlugin.Classes; #endregion -namespace SpeckleRevitPlugin +namespace SpeckleRevitPlugin.Entry { [Transaction(TransactionMode.Manual)] public class ExtCmd : IExternalCommand { - //public static ChromiumWebBrowser Browser; - public Result Execute( ExternalCommandData commandData, ref string message, @@ -20,11 +18,11 @@ public Result Execute( { // Settings Helper Class // NOTE: You can use the AppMain.Settings throughout project to access active Revit Document, UIDocument, Application, etc. - AppMain.Settings = new SettingsHelper(commandData); + //AppMain.Settings = new SettingsHelper(commandData); // SHOW DOCKABLE WINDOW - DockablePaneId m_dpID = GlobalHelper.MainDockablePaneId; - DockablePane m_dp = commandData.Application.GetDockablePane(m_dpID); + var m_dpID = GlobalHelper.MainDockablePaneId; + var m_dp = commandData.Application.GetDockablePane(m_dpID); m_dp.Show(); diff --git a/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs b/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs deleted file mode 100644 index d31f859..0000000 --- a/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs +++ /dev/null @@ -1,108 +0,0 @@ -#region Namespaces -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; - -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -#endregion - -namespace SpeckleRevitPlugin -{ - public enum EnumCommandType : int - { - Command1, - Command2, - Command3 - } - - /// - /// Model Updates for Modeless Dialog - /// - /// - class ExtCmdModeless : IExternalEventHandler - { - /// - /// Model Updates for Modeless Dialog - /// - /// - public void Execute(UIApplication uiapp) - { - switch (AppMain.Settings.CommandType) - { - case EnumCommandType.Command1: - SampleCommand1(uiapp); - break; - case EnumCommandType.Command2: - SampleCommand2(uiapp); - break; - case EnumCommandType.Command3: - SampleCommand3(uiapp); - break; - } - } - - /// - /// Command 1 - /// - /// - private void SampleCommand1(UIApplication uiapp) - { - try - { - using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something")) - { - t.Start(); - - t.Commit(); - } - } - catch { } - } - - /// - /// Command 2 - /// - /// - private void SampleCommand2(UIApplication uiapp) - { - try - { - using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something")) - { - t.Start(); - - t.Commit(); - } - } - catch { } - } - - /// - /// Command 3 - /// - /// - private void SampleCommand3(UIApplication uiapp) - { - try - { - using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something")) - { - t.Start(); - - t.Commit(); - } - } - catch { } - } - - /// - /// External event anme - /// - /// - public string GetName() { return "ExtCmdModeless"; } - } -} diff --git a/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs new file mode 100644 index 0000000..7f39fd1 --- /dev/null +++ b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs @@ -0,0 +1,91 @@ +#region Namespaces +using System; +using System.Collections.Generic; +using System.Threading; +using Autodesk.Revit.DB; +using Autodesk.Revit.UI; +using SpeckleRevitPlugin.Utilities; + +#endregion + +namespace SpeckleRevitPlugin.Entry +{ + public class SpeckleRequestHandler : IExternalEventHandler + { + public SpeckleRequest Request { get; set; } = new SpeckleRequest(); + public delegate void ClientsRetrieved(IDictionary recivers, IDictionary senders); + public static event ClientsRetrieved OnClientsRetrieved; + + public void Execute(UIApplication uiapp) + { + try + { + switch (Request.Take()) + { + case SpeckleCommandType.None: + return; + case SpeckleCommandType.GetClients: + GetClients(uiapp); + break; + case SpeckleCommandType.Command2: + break; + case SpeckleCommandType.Command3: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + /// + /// + /// + /// + private void GetClients(UIApplication app) + { + if (!SchemaUtilities.SchemaExist(Properties.Resources.SchemaName)) return; + + var doc = app.ActiveUIDocument.Document; + var schema = SchemaUtilities.GetSchema(Properties.Resources.SchemaName); + var pInfo = SchemaUtilities.GetProjectInfo(doc); + var recivers = pInfo.GetEntity(schema).Get>(schema.GetField("receivers")); + var senders = pInfo.GetEntity(schema).Get>(schema.GetField("senders")); + + OnClientsRetrieved?.Invoke(recivers, senders); + } + + /// + /// External event name + /// + /// + public string GetName() { return "SpeckleRequestHandler"; } + } + + public class SpeckleRequest + { + private int _request = (int)SpeckleCommandType.None; + + public SpeckleCommandType Take() + { + return (SpeckleCommandType)Interlocked.Exchange(ref _request, (int)SpeckleCommandType.None); + } + + public void Make(SpeckleCommandType request) + { + Interlocked.Exchange(ref _request, (int)request); + } + } + + public enum SpeckleCommandType + { + None, + GetClients, + Command2, + Command3 + } +} diff --git a/SpeckleRevitPlugin/Properties/Resources.Designer.cs b/SpeckleRevitPlugin/Properties/Resources.Designer.cs index 23f25c5..8eb7081 100644 --- a/SpeckleRevitPlugin/Properties/Resources.Designer.cs +++ b/SpeckleRevitPlugin/Properties/Resources.Designer.cs @@ -22,7 +22,7 @@ namespace SpeckleRevitPlugin.Properties { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -36,7 +36,7 @@ internal Resources() { /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SpeckleRevitPlugin.Properties.Resources", typeof(Resources).Assembly); @@ -51,7 +51,7 @@ internal Resources() { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -61,22 +61,20 @@ internal Resources() { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// Looks up a localized string similar to a2666a4c-0eed-4f9c-b4da-7a9d97e1ce40. /// - internal static System.Drawing.Bitmap Template_16 { + public static string SchemaId { get { - object obj = ResourceManager.GetObject("Template_16", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); + return ResourceManager.GetString("SchemaId", resourceCulture); } } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// Looks up a localized string similar to SpeckleSchema. /// - internal static System.Drawing.Bitmap Template_32 { + public static string SchemaName { get { - object obj = ResourceManager.GetObject("Template_32", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); + return ResourceManager.GetString("SchemaName", resourceCulture); } } } diff --git a/SpeckleRevitPlugin/Properties/Resources.resx b/SpeckleRevitPlugin/Properties/Resources.resx index 1af7de1..573c813 100644 --- a/SpeckleRevitPlugin/Properties/Resources.resx +++ b/SpeckleRevitPlugin/Properties/Resources.resx @@ -117,4 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + a2666a4c-0eed-4f9c-b4da-7a9d97e1ce40 + + + SpeckleSchema + \ No newline at end of file diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin index 7d1d9ec..45dd60a 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin @@ -4,7 +4,7 @@ Command SpeckleRevitPlugin Speckle plugin for Revit .\Speckle\SpeckleRevitPlugin.dll - SpeckleRevitPlugin.ExtCmd + SpeckleRevitPlugin.Entry.ExtCmd 76028dcb-3f9b-4a5b-9e8b-36b7a5564a56 Speckle http://speckle.works/ @@ -12,7 +12,7 @@ Application SpeckleRevitPlugin .\Speckle\SpeckleRevitPlugin.dll - SpeckleRevitPlugin.AppMain + SpeckleRevitPlugin.Entry.AppMain 0a03f52e-0ee0-4c19-82c4-8181626ec816 Speckle http://speckle.works/ diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj index 69ff79c..1e015a1 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj @@ -56,17 +56,25 @@ - + - + + + True + True + Resources.resx + form_Main.xaml form_MainDock.xaml + + + @@ -74,7 +82,10 @@ Designer - + + PublicResXFileCodeGenerator + Resources.Designer.cs + diff --git a/SpeckleRevitPlugin/UI/form_Main.xaml.cs b/SpeckleRevitPlugin/UI/form_Main.xaml.cs index 92647aa..55da6c4 100644 --- a/SpeckleRevitPlugin/UI/form_Main.xaml.cs +++ b/SpeckleRevitPlugin/UI/form_Main.xaml.cs @@ -1,18 +1,6 @@ #region Namespaces -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; #endregion namespace SpeckleRevitPlugin @@ -20,17 +8,17 @@ namespace SpeckleRevitPlugin /// /// Interaction logic for form_Main.xaml /// - public partial class form_Main : Window + public partial class form_Main { - private SettingsHelper _s; + //private SettingsHelper _s; - public form_Main(SettingsHelper s) + public form_Main() { InitializeComponent(); Label_Version.Content = "v." + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); //widen scope - _s = s; + //_s = s; } #region Form Events @@ -41,7 +29,7 @@ public form_Main(SettingsHelper s) /// private void Button_Close_Click(object sender, RoutedEventArgs e) { - this.Close(); + Close(); } /// @@ -61,8 +49,7 @@ private void Button_Run_Click(object sender, RoutedEventArgs e) /// private void Window_MouseDown(object sender, MouseButtonEventArgs e) { - if (e.ChangedButton == MouseButton.Left) - this.DragMove(); + if (e.ChangedButton == MouseButton.Left) DragMove(); } #endregion } diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs index f16dff2..92b91ad 100644 --- a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs +++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs @@ -7,6 +7,8 @@ using Autodesk.Revit.UI; using CefSharp; using SpeckleRevitPlugin.Classes; +using SpeckleRevitPlugin.Entry; + #endregion namespace SpeckleRevitPlugin.UI @@ -16,8 +18,6 @@ namespace SpeckleRevitPlugin.UI /// public partial class FormMainDock : IDockablePaneProvider { - private readonly ExternalEvent _extEvent; - private readonly ExtCmdModeless _handler = new ExtCmdModeless(); public static Interop Store; /// @@ -35,7 +35,7 @@ public FormMainDock() // make them talk together Browser.RegisterAsyncJsObject("Interop", Store); - _extEvent = ExternalEvent.Create(_handler); + //_extEvent = ExternalEvent.Create(_handler); } private static void InitializeCef() @@ -113,10 +113,10 @@ private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs args try { //textBox_Comments.Text; - AppMain.Settings.CommandType = EnumCommandType.Command1; + //AppMain.Settings.CommandType = EnumCommandType.Command1; // Safe Update - _extEvent.Raise(); + //_extEvent.Raise(); } catch (Exception e) { diff --git a/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs new file mode 100644 index 0000000..624666d --- /dev/null +++ b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + +namespace SpeckleRevitPlugin.Utilities +{ + public static class BinaryFormatterUtilities + { + public static T Read(byte[] content, Assembly currentAssembly) where T : new() + { + var result = default(T); + + try + { + using (var ms = new MemoryStream()) + { + ms.Write(content, 0, content.Length); + ms.Seek(0, SeekOrigin.Begin); + var bf = new BinaryFormatter + { + Binder = new SearchAssembliesBinder(currentAssembly, true) + }; + result = (T)bf.Deserialize(ms); + } + } + catch + { + // ignored + } + + return result; + } + + //public static void Write(T obj, string filename) + //{ + // FileStream fileStream = new FileStream(filename, FileMode.Create); + // BinaryFormatter formatter = new BinaryFormatter(); + // try + // { + // formatter.Serialize(fileStream, obj); + // } + // finally + // { + // fileStream.Close(); + // } + //} + } + + internal sealed class SearchAssembliesBinder : SerializationBinder + { + private readonly bool _searchInDlls; + private readonly Assembly _currentAssembly; + + public SearchAssembliesBinder(Assembly currentAssembly, bool searchInDlls) + { + _currentAssembly = currentAssembly; + _searchInDlls = searchInDlls; + } + + public override Type BindToType(string assemblyName, string typeName) + { + var assemblyNames = new List(); + assemblyNames.Add(_currentAssembly.GetName()); // EXE + + if (_searchInDlls) + { + assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies()); // DLLs + } + + foreach (var an in assemblyNames) + { + var typeToDeserialize = GetTypeToDeserialize(typeName, an); + if (typeToDeserialize != null) + { + return typeToDeserialize; // found + } + } + + return null; // not found + } + + private static Type GetTypeToDeserialize(string typeName, AssemblyName an) + { + var fullTypeName = $"{typeName}, {an.FullName}"; + var typeToDeserialize = Type.GetType(fullTypeName); + return typeToDeserialize; + } + } +} diff --git a/SpeckleRevitPlugin/Utilities/JsonUtilities.cs b/SpeckleRevitPlugin/Utilities/JsonUtilities.cs new file mode 100644 index 0000000..68be954 --- /dev/null +++ b/SpeckleRevitPlugin/Utilities/JsonUtilities.cs @@ -0,0 +1,48 @@ +using System; +using Newtonsoft.Json; + +namespace SpeckleRevitPlugin.Utilities +{ + public static class JsonUtilities + { + public static JsonSerializerSettings Settings { get; set; } + + static JsonUtilities() + { + Settings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + MissingMemberHandling = MissingMemberHandling.Ignore, + CheckAdditionalContent = true, + Formatting = Formatting.Indented + }; + } + + public static T Deserialize(string json) where T : new() + { + try + { + var obj = JsonConvert.DeserializeObject(json, Settings); + return obj; + } + catch (Exception e) + { + throw new Exception("Failed deserializing Json: " + e.Message); + } + } + + public static string Serialize(object obj) + { + try + { + var json = JsonConvert.SerializeObject(obj, Settings); + return json; + } + catch (Exception e) + { + throw new Exception("Failed serializing Json: " + e.Message); + } + } + + } +} diff --git a/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs new file mode 100644 index 0000000..c4ba9a3 --- /dev/null +++ b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.ExtensibleStorage; + +namespace SpeckleRevitPlugin.Utilities +{ + public static class SchemaUtilities + { + /// + /// + /// + /// + /// + public static Schema GetSchema(string schemaName) + { + var schemas = Schema.ListSchemas(); + if (schemas == null || !schemas.Any()) return null; + return schemas.FirstOrDefault(s => s.SchemaName == schemaName); + } + + /// + /// + /// + /// + /// + public static bool SchemaExist(string schemaName) + { + return GetSchema(schemaName) != null; + } + + public static Schema CreateSchema() + { + var schemaGuid = new Guid(Properties.Resources.SchemaId); + var schemaBuilder = new SchemaBuilder(schemaGuid); + schemaBuilder.SetReadAccessLevel(AccessLevel.Public); + schemaBuilder.SetWriteAccessLevel(AccessLevel.Application); + + // (Konrad) These values are set in addin manifest. + schemaBuilder.SetApplicationGUID(new Guid("0a03f52e-0ee0-4c19-82c4-8181626ec816")); + schemaBuilder.SetVendorId("Speckle"); + + schemaBuilder.SetSchemaName(Properties.Resources.SchemaName); + schemaBuilder.SetDocumentation("Speckle schema."); + schemaBuilder.AddMapField("senders", typeof(string), typeof(string)); + schemaBuilder.AddMapField("receivers", typeof(string), typeof(string)); + var schema = schemaBuilder.Finish(); + + return schema; + } + + public static void AddSchemaEntity(Schema schema, Element e, string fieldName, Dictionary fieldValue) + { + if (schema == null) + { + throw new NullReferenceException("schema"); + } + if (e == null) + { + throw new NullReferenceException("element"); + } + if (string.IsNullOrEmpty(fieldName)) + { + throw new NullReferenceException("fieldName"); + } + + try + { + var entity = new Entity(schema); + var settingsField = schema.GetField(fieldName); + entity.Set>(settingsField, fieldValue); + + e.SetEntity(entity); + } + catch (Exception ex) + { + Debug.Write(ex.Message); + } + } + + /// + /// + /// + /// + /// + public static Element GetProjectInfo(Document doc) + { + var pInfo = new FilteredElementCollector(doc) + .OfClass(typeof(ProjectInfo)) + .FirstElement(); + return pInfo; + } + + public static void UpdateSchemaEntity(Schema schema, Element e, string fieldName, Dictionary fieldValue) + { + if (schema == null) + { + throw new NullReferenceException("schema"); + } + if (e == null) + { + throw new NullReferenceException("element"); + } + if (string.IsNullOrEmpty(fieldName)) + { + throw new NullReferenceException("fieldName"); + } + + try + { + var entity = e.GetEntity(schema); + var field = schema.GetField(fieldName); + entity.Set>(field, fieldValue); + + e.SetEntity(entity); + } + catch(Exception ex) + { + Debug.Write(ex.Message); + } + } + } +} From 62dfb9010106e825b69534a2d58b23aee3ada6a2 Mon Sep 17 00:00:00 2001 From: ksobon Date: Tue, 22 May 2018 14:35:21 -0400 Subject: [PATCH 3/6] implementation of the external event class --- SpeckleRevitPlugin/Classes/Interop.cs | 197 ++++++++---------- SpeckleRevitPlugin/Classes/RevitReceiver.cs | 4 +- SpeckleRevitPlugin/Entry/AppMain.cs | 17 +- SpeckleRevitPlugin/Entry/ExtCmd.cs | 2 - .../Entry/SpeckleRequestHandler.cs | 53 ++++- 5 files changed, 135 insertions(+), 138 deletions(-) diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs index f5550f3..779c662 100644 --- a/SpeckleRevitPlugin/Classes/Interop.cs +++ b/SpeckleRevitPlugin/Classes/Interop.cs @@ -50,59 +50,6 @@ public Interop(ChromiumWebBrowser originalBrowser) SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved; } - private void OnClientsRetrieved(IDictionary recivers, IDictionary senders) - { - var assembly = Assembly.GetExecutingAssembly(); - foreach (var kv in recivers) - { - var serialisedClient = Convert.FromBase64String(kv.Value); - var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); - if (client.Client == null) return; - - client.CompleteDeserialisation(this); - - - //using (var ms = new MemoryStream()) - //{ - // ms.Write(serialisedClient, 0, serialisedClient.Length); - // ms.Seek(0, SeekOrigin.Begin); - // var bf = new BinaryFormatter - // { - // Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) - // }; - // var client = (RevitReceiver)bf.Deserialize(ms); - // client.CompleteDeserialisation(this); - //} - } - - foreach (var kv in senders) - { - var serialisedClient = Convert.FromBase64String(kv.Value); - var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); - if (client.Client == null) return; - - client.CompleteDeserialisation(this); - //var serialisedClient = Convert.FromBase64String(kv.Value); - - //using (var ms = new MemoryStream()) - //{ - // ms.Write(serialisedClient, 0, serialisedClient.Length); - // ms.Seek(0, SeekOrigin.Begin); - // var bf = new BinaryFormatter - // { - // Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) - // }; - // var client = (RevitSender)bf.Deserialize(ms); - // client.CompleteDeserialisation(this); - //} - } - } - - private void Revit_ModelSynched(Document doc) - { - SaveFileClients(doc); - } - public void SetBrowser(ChromiumWebBrowser browser) { Browser = browser; @@ -142,67 +89,7 @@ public string GetDocumentGuid() public void AppReady() { SpeckleIsReady = true; - - // (Konrad) This is the thread safe way of interacting with Revit. - // Also it's possible that the Speckle app initiates before Revit - // Document is open/ready making Extensible Storage inaccessible. - AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients); - AppMain.SpeckleEvent.Raise(); - } - - /// - /// - /// - /// - public void SaveFileClients(Document doc) - { - var senders = new Dictionary(); - var receivers = new Dictionary(); - foreach (var rClient in UserClients) - { - using (var ms = new MemoryStream()) - { - var formatter = new BinaryFormatter(); - formatter.Serialize(ms, rClient); - var client = Convert.ToBase64String(ms.ToArray()); - var clientId = rClient.GetClientId(); - - if (rClient.GetRole() == ClientRole.Receiver) receivers.Add(clientId, client); - else senders.Add(clientId, client); - } - } - - var pInfo = SchemaUtilities.GetProjectInfo(doc); - var schemaExists = SchemaUtilities.SchemaExist(Properties.Resources.SchemaName); - var schema = schemaExists - ? SchemaUtilities.GetSchema(Properties.Resources.SchemaName) - : SchemaUtilities.CreateSchema(); - - using (var trans = new Transaction(doc, "Store Clients")) - { - trans.Start(); - - if (schemaExists) - { - SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "senders", senders); - SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "receivers", receivers); - } - else - { - SchemaUtilities.AddSchemaEntity(schema, pInfo, "senders", senders); - SchemaUtilities.AddSchemaEntity(schema, pInfo, "receivers", receivers); - } - - trans.Commit(); - } - } - - /// - /// - /// - public void InstantiateFileClients(Dictionary recivers, Dictionary senders) - { - + InstantiateFileClients(); } #region Account Management @@ -267,12 +154,94 @@ public void RemoveAccount( string payload ) #region Client Management + /// + /// Called by SpeckleView when new Receiver was added. + /// + /// Info needed to create RevitReceiver + /// public bool AddReceiverClient( string payload ) { var unused = new RevitReceiver(payload, this); return true; } + /// + /// Handler for an event called by Revit when Clients have been retrived from Schema. + /// + /// Dictionary of Revit Receivers serialized into string. + /// Dictionary of Revit Senders serialized into string. + private void OnClientsRetrieved(IDictionary receivers, IDictionary senders) + { + UserClients = new List(); + + var assembly = Assembly.GetExecutingAssembly(); + foreach (var kv in receivers) + { + var serialisedClient = Convert.FromBase64String(kv.Value); + var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); + if (client.Client == null) return; + + client.CompleteDeserialisation(this); + } + + foreach (var kv in senders) + { + var serialisedClient = Convert.FromBase64String(kv.Value); + var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); + if (client.Client == null) return; + + client.CompleteDeserialisation(this); + } + } + + /// + /// Handler for an event called by Revit when Document is either saved/synched. + /// It's a good time to store Clients in an Extensible Storage then. + /// + private void Revit_ModelSynched() + { + SaveFileClients(); + } + + /// + /// + /// + public void InstantiateFileClients() + { + // (Konrad) This is the thread safe way of interacting with Revit. + // Also it's possible that the Speckle app initiates before Revit + // Document is open/ready making Extensible Storage inaccessible. + AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients); + AppMain.SpeckleEvent.Raise(); + } + + /// + /// It's used by Speckle View to trigger Client storage. + /// + public void SaveFileClients() + { + var senders = new Dictionary(); + var receivers = new Dictionary(); + foreach (var rClient in UserClients) + { + using (var ms = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(ms, rClient); + var client = Convert.ToBase64String(ms.ToArray()); + var clientId = rClient.GetClientId(); + + if (rClient.GetRole() == ClientRole.Receiver) receivers.Add(clientId, client); + else senders.Add(clientId, client); + } + } + + AppMain.SpeckleHandler.Arg1 = senders; + AppMain.SpeckleHandler.Arg2 = receivers; + AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.SaveClients); + AppMain.SpeckleEvent.Raise(); + } + //public bool AddSenderClientFromSelection( string _payload ) //{ // var mySender = new RhinoSender( _payload, this ); diff --git a/SpeckleRevitPlugin/Classes/RevitReceiver.cs b/SpeckleRevitPlugin/Classes/RevitReceiver.cs index 245c7e8..4b49829 100644 --- a/SpeckleRevitPlugin/Classes/RevitReceiver.cs +++ b/SpeckleRevitPlugin/Classes/RevitReceiver.cs @@ -1,4 +1,5 @@ -using System; +#region Namespaces +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,6 +10,7 @@ using Newtonsoft.Json.Serialization; using SpeckleCore; using SpeckleRevitPlugin.Utilities; +#endregion namespace SpeckleRevitPlugin.Classes { diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index 7179ccc..74a409e 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -29,7 +29,7 @@ public class AppMain : IExternalApplication internal DockablePaneProviderData DockData; //internal static SettingsHelper Settings { get; set; } - public delegate void ModelSynched(Document doc); + public delegate void ModelSynched(); public static event ModelSynched OnModelSynched; public Result OnStartup(UIControlledApplication a) @@ -79,7 +79,7 @@ private static void OnDocumentSaving(object sender, DocumentSavingEventArgs e) var doc = e.Document; if (doc == null || doc.IsFamilyDocument) return; - OnModelSynched?.Invoke(doc); + OnModelSynched?.Invoke(); } private static void OnDocumentSynchronized(object sender, DocumentSynchronizedWithCentralEventArgs e) @@ -87,7 +87,7 @@ private static void OnDocumentSynchronized(object sender, DocumentSynchronizedWi var doc = e.Document; if (doc == null || doc.IsFamilyDocument) return; - OnModelSynched?.Invoke(doc); + OnModelSynched?.Invoke(); } private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e) @@ -97,12 +97,6 @@ private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e) { HideDockablePane(); } - else - { - //Settings = new SettingsHelper(doc, doc.Application); - //OnModelReady?.Invoke(doc); - } - // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc } @@ -114,11 +108,6 @@ private void OnDocumentCreated(object sender, DocumentCreatedEventArgs e) { HideDockablePane(); } - else - { - //Settings = new SettingsHelper(doc, doc.Application); - //OnModelReady?.Invoke(doc); - } // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc } diff --git a/SpeckleRevitPlugin/Entry/ExtCmd.cs b/SpeckleRevitPlugin/Entry/ExtCmd.cs index c69c54a..4cdc176 100644 --- a/SpeckleRevitPlugin/Entry/ExtCmd.cs +++ b/SpeckleRevitPlugin/Entry/ExtCmd.cs @@ -2,8 +2,6 @@ using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; -using SpeckleRevitPlugin.Classes; - #endregion namespace SpeckleRevitPlugin.Entry diff --git a/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs index 7f39fd1..2e27567 100644 --- a/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs +++ b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs @@ -5,7 +5,6 @@ using Autodesk.Revit.DB; using Autodesk.Revit.UI; using SpeckleRevitPlugin.Utilities; - #endregion namespace SpeckleRevitPlugin.Entry @@ -13,7 +12,10 @@ namespace SpeckleRevitPlugin.Entry public class SpeckleRequestHandler : IExternalEventHandler { public SpeckleRequest Request { get; set; } = new SpeckleRequest(); - public delegate void ClientsRetrieved(IDictionary recivers, IDictionary senders); + public object Arg1 { get; set; } + public object Arg2 { get; set; } + + public delegate void ClientsRetrieved(IDictionary receivers, IDictionary senders); public static event ClientsRetrieved OnClientsRetrieved; public void Execute(UIApplication uiapp) @@ -27,7 +29,8 @@ public void Execute(UIApplication uiapp) case SpeckleCommandType.GetClients: GetClients(uiapp); break; - case SpeckleCommandType.Command2: + case SpeckleCommandType.SaveClients: + SaveClients(uiapp); break; case SpeckleCommandType.Command3: break; @@ -46,17 +49,53 @@ public void Execute(UIApplication uiapp) /// /// /// - private void GetClients(UIApplication app) + private static void GetClients(UIApplication app) { if (!SchemaUtilities.SchemaExist(Properties.Resources.SchemaName)) return; var doc = app.ActiveUIDocument.Document; var schema = SchemaUtilities.GetSchema(Properties.Resources.SchemaName); var pInfo = SchemaUtilities.GetProjectInfo(doc); - var recivers = pInfo.GetEntity(schema).Get>(schema.GetField("receivers")); + var receivers = pInfo.GetEntity(schema).Get>(schema.GetField("receivers")); var senders = pInfo.GetEntity(schema).Get>(schema.GetField("senders")); - OnClientsRetrieved?.Invoke(recivers, senders); + OnClientsRetrieved?.Invoke(receivers, senders); + } + + /// + /// + /// + /// + private void SaveClients(UIApplication app) + { + // (Konrad) Extract the variables used by this method + var senders = Arg1 as Dictionary; + var receivers = Arg2 as Dictionary; + + var doc = app.ActiveUIDocument.Document; + var pInfo = SchemaUtilities.GetProjectInfo(doc); + var schemaExists = SchemaUtilities.SchemaExist(Properties.Resources.SchemaName); + var schema = schemaExists + ? SchemaUtilities.GetSchema(Properties.Resources.SchemaName) + : SchemaUtilities.CreateSchema(); + + using (var trans = new Transaction(doc, "Store Clients")) + { + trans.Start(); + + if (schemaExists) + { + SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "senders", senders); + SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "receivers", receivers); + } + else + { + SchemaUtilities.AddSchemaEntity(schema, pInfo, "senders", senders); + SchemaUtilities.AddSchemaEntity(schema, pInfo, "receivers", receivers); + } + + trans.Commit(); + } } /// @@ -85,7 +124,7 @@ public enum SpeckleCommandType { None, GetClients, - Command2, + SaveClients, Command3 } } From e867e2af531acc2335ca946557d67eec46a9d7fd Mon Sep 17 00:00:00 2001 From: ksobon Date: Tue, 22 May 2018 17:25:51 -0400 Subject: [PATCH 4/6] small cleanup --- .../Classes/ISpeckleRevitClient.cs | 2 +- SpeckleRevitPlugin/Classes/Interop.cs | 39 +- SpeckleRevitPlugin/Classes/RevitReceiver.cs | 73 +- SpeckleRevitPlugin/Classes/RevitSender.cs | 859 +++++++++--------- SpeckleRevitPlugin/Entry/AppMain.cs | 77 +- SpeckleRevitPlugin/SpeckleRevitPlugin.csproj | 1 + .../Tools/WallTool/SpeckleWallCmd.cs | 15 + .../Utilities/BinaryFormatterUtilities.cs | 12 +- .../Utilities/SchemaUtilities.cs | 61 +- 9 files changed, 583 insertions(+), 556 deletions(-) create mode 100644 SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs diff --git a/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs index 5b1d324..b8e154d 100644 --- a/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs +++ b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs @@ -5,7 +5,7 @@ namespace SpeckleRevitPlugin.Classes { /// - /// Generalises some methhods for both senders and receivers. + /// Generalises some methods for both senders and receivers. /// public interface ISpeckleRevitClient : IDisposable, ISerializable { diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs index 779c662..eff256a 100644 --- a/SpeckleRevitPlugin/Classes/Interop.cs +++ b/SpeckleRevitPlugin/Classes/Interop.cs @@ -172,26 +172,23 @@ public bool AddReceiverClient( string payload ) /// Dictionary of Revit Senders serialized into string. private void OnClientsRetrieved(IDictionary receivers, IDictionary senders) { - UserClients = new List(); - var assembly = Assembly.GetExecutingAssembly(); foreach (var kv in receivers) { var serialisedClient = Convert.FromBase64String(kv.Value); var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); - if (client.Client == null) return; - - client.CompleteDeserialisation(this); + client.Context = this; } - foreach (var kv in senders) - { - var serialisedClient = Convert.FromBase64String(kv.Value); - var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); - if (client.Client == null) return; + //TODO: Let's deal with senders later! + //foreach (var kv in senders) + //{ + // var serialisedClient = Convert.FromBase64String(kv.Value); + // var client = BinaryFormatterUtilities.Read(serialisedClient, assembly); + // if (client.Client == null) return; - client.CompleteDeserialisation(this); - } + // client.CompleteDeserialisation(this); + //} } /// @@ -248,17 +245,19 @@ public void SaveFileClients() // return true; //} - //public bool RemoveClient( string _payload ) - //{ - // var myClient = UserClients.FirstOrDefault( client => client.GetClientId() == _payload ); - // if ( myClient == null ) return false; + public bool RemoveClient(string _payload) + { + var myClient = UserClients.FirstOrDefault(client => client.GetClientId() == _payload); + if (myClient == null) return false; - // RhinoDoc.ActiveDoc.Strings.Delete( myClient.GetRole() == ClientRole.Receiver ? "speckle-client-receivers" : "speckle-client-senders", myClient.GetClientId() ); + myClient.Dispose(true); + var result = UserClients.Remove(myClient); - // myClient.Dispose( true ); + // (Konrad) Update Revit Schema. + SaveFileClients(); - // return UserClients.Remove( myClient ); - //} + return result; + } public bool RemoveAllClients( ) { diff --git a/SpeckleRevitPlugin/Classes/RevitReceiver.cs b/SpeckleRevitPlugin/Classes/RevitReceiver.cs index 4b49829..0d4a8d2 100644 --- a/SpeckleRevitPlugin/Classes/RevitReceiver.cs +++ b/SpeckleRevitPlugin/Classes/RevitReceiver.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json.Serialization; using SpeckleCore; using SpeckleRevitPlugin.Utilities; +#pragma warning disable 4014 #endregion namespace SpeckleRevitPlugin.Classes @@ -31,7 +32,7 @@ public RevitReceiver() { } - public RevitReceiver( string payload, Interop parent ) + public RevitReceiver(string payload, Interop parent) { Context = parent; dynamic p = JsonConvert.DeserializeObject(payload); @@ -53,6 +54,35 @@ public RevitReceiver( string payload, Interop parent ) Objects = new List(); } + protected RevitReceiver(SerializationInfo info, StreamingContext context) + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + Objects = new List(); + + var serialisedClient = Convert.FromBase64String(info.GetString("client")); + + using (var ms = new MemoryStream()) + { + ms.Write(serialisedClient, 0, serialisedClient.Length); + ms.Seek(0, SeekOrigin.Begin); + var bf = new BinaryFormatter + { + Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) + }; + Client = (SpeckleApiClient)bf.Deserialize(ms); + StreamId = Client.StreamId; + } + + Client.OnReady += Client_OnReady; + Client.OnLogData += Client_OnLogData; + Client.OnWsMessage += Client_OnWsMessage; + Client.OnError += Client_OnError; + } + public string GetClientId() { return Client.ClientId; @@ -251,51 +281,14 @@ public void ToggleLayerHover(string layerId, bool status) #region Serialisation & Dispose - public void Dispose(bool delete = false) - { - Client.Dispose(delete); - } - public void Dispose() { Client.Dispose(); } - public void CompleteDeserialisation(Interop _Context) - { - Context = _Context; - - Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client })); - Context.UserClients.Add(this); - } - - protected RevitReceiver(SerializationInfo info, StreamingContext context) + public void Dispose(bool delete = false) { - JsonConvert.DefaultSettings = () => new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - Objects = new List(); - - var serialisedClient = Convert.FromBase64String(info.GetString("client")); - - using (var ms = new MemoryStream()) - { - ms.Write(serialisedClient, 0, serialisedClient.Length); - ms.Seek(0, SeekOrigin.Begin); - var bf = new BinaryFormatter - { - Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true) - }; - Client = (SpeckleApiClient)bf.Deserialize(ms); - StreamId = Client.StreamId; - } - - Client.OnReady += Client_OnReady; - Client.OnLogData += Client_OnLogData; - Client.OnWsMessage += Client_OnWsMessage; - Client.OnError += Client_OnError; + Client.Dispose(delete); } public void GetObjectData(SerializationInfo info, StreamingContext context) diff --git a/SpeckleRevitPlugin/Classes/RevitSender.cs b/SpeckleRevitPlugin/Classes/RevitSender.cs index e5bbca7..3245713 100644 --- a/SpeckleRevitPlugin/Classes/RevitSender.cs +++ b/SpeckleRevitPlugin/Classes/RevitSender.cs @@ -1,464 +1,465 @@ -using System; +#region Namespaces +using System; using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Timers; using Newtonsoft.Json; using SpeckleCore; +#endregion namespace SpeckleRevitPlugin.Classes { - /// - /// Rhino Sender Client - /// - [Serializable] - public class RevitSender : ISpeckleRevitClient + /// + /// Rhino Sender Client + /// + [Serializable] + public class RevitSender : ISpeckleRevitClient { - public Interop Context { get; set; } + public Interop Context { get; set; } + public SpeckleApiClient Client { get; private set; } + public List Objects { get; set; } + //public SpeckleDisplayConduit Display; + public string StreamId { get; set; } + public bool Paused { get; set; } = false; + public bool Visible { get; set; } = true; + Timer DataSender, MetadataSender; + public string StreamName; + public bool IsSendingUpdate = false, Expired = false; + + public RevitSender() + { + } - public SpeckleApiClient Client { get; private set; } + public RevitSender(string _payload, Interop _Context) + { + Context = _Context; - public List Objects { get; set; } + dynamic InitPayload = JsonConvert.DeserializeObject(_payload); - //public SpeckleDisplayConduit Display; + Client = new SpeckleApiClient((string)InitPayload.account.restApi, true); - public string StreamId { get; set; } + StreamName = (string)InitPayload.streamName; - public bool Paused { get; set; } = false; + SetClientEvents(); + //SetRhinoEvents(); + SetTimers(); - public bool Visible { get; set; } = true; + //Display = new SpeckleDisplayConduit(); + //Display.Enabled = true; - System.Timers.Timer DataSender, MetadataSender; + Context.NotifySpeckleFrame("set-gl-load", "", "true"); - public string StreamName; + Client.IntializeSender((string)InitPayload.account.apiToken, Context.GetDocumentName(), "Rhino", Context.GetDocumentGuid()) + .ContinueWith(res => + { + StreamId = Client.Stream.StreamId; + Client.Stream.Name = StreamName; - public bool IsSendingUpdate = false, Expired = false; + Context.NotifySpeckleFrame("set-gl-load", "", "false"); + Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client })); + Context.UserClients.Add(this); - //public RhinoSender( string _payload, Interop _Context ) - //{ - // Context = _Context; - - // dynamic InitPayload = JsonConvert.DeserializeObject( _payload ); - - // Client = new SpeckleApiClient( ( string ) InitPayload.account.restApi, true ); - - // StreamName = ( string ) InitPayload.streamName; - - // SetClientEvents(); - // SetRhinoEvents(); - // SetTimers(); - - // Display = new SpeckleDisplayConduit(); - // Display.Enabled = true; - - // Context.NotifySpeckleFrame( "set-gl-load", "", "true" ); - - // Client.IntializeSender( ( string ) InitPayload.account.apiToken, Context.GetDocumentName(), "Rhino", Context.GetDocumentGuid() ) - // .ContinueWith( res => - // { - // StreamId = Client.Stream.StreamId; - // Client.Stream.Name = StreamName; - - // Context.NotifySpeckleFrame( "set-gl-load", "", "false" ); - // Context.NotifySpeckleFrame( "client-add", StreamId, JsonConvert.SerializeObject( new { stream = Client.Stream, client = Client } ) ); - // Context.UserClients.Add( this ); - - // InitTrackedObjects( InitPayload ); - // DataSender.Start(); - // } ); - - //} - - //public void InitTrackedObjects( dynamic payload ) - //{ - // foreach ( string guid in payload.selection ) - // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId ); - //} - - //public void AddTrackedObjects( string[ ] guids ) - //{ - // foreach ( string guid in guids ) - // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId ); - - // DataSender.Start(); - //} - - //public void RemoveTrackedObjects( string[ ] guids ) - //{ - // foreach ( string guid in guids ) - // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, null ); - - // DataSender.Start(); - //} - - //public void SetRhinoEvents( ) - //{ - // RhinoDoc.ModifyObjectAttributes += RhinoDoc_ModifyObjectAttributes; - // RhinoDoc.DeleteRhinoObject += RhinoDoc_DeleteRhinoObject; - // RhinoDoc.AddRhinoObject += RhinoDoc_AddRhinoObject; - // RhinoDoc.UndeleteRhinoObject += RhinoDoc_UndeleteRhinoObject; - // RhinoDoc.LayerTableEvent += RhinoDoc_LayerTableEvent; - //} - - //public void UnsetRhinoEvents( ) - //{ - // RhinoDoc.ModifyObjectAttributes -= RhinoDoc_ModifyObjectAttributes; - // RhinoDoc.DeleteRhinoObject -= RhinoDoc_DeleteRhinoObject; - // RhinoDoc.AddRhinoObject -= RhinoDoc_AddRhinoObject; - // RhinoDoc.UndeleteRhinoObject -= RhinoDoc_UndeleteRhinoObject; - // RhinoDoc.LayerTableEvent -= RhinoDoc_LayerTableEvent; - //} - - //private void RhinoDoc_LayerTableEvent( object sender, Rhino.DocObjects.Tables.LayerTableEventArgs e ) - //{ - // DataSender.Start(); - //} - - //private void RhinoDoc_UndeleteRhinoObject( object sender, RhinoObjectEventArgs e ) - //{ - // //Debug.WriteLine("UNDELETE Event"); - // if ( Paused ) - // { - // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); - // return; - // } - // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) - // { - // DataSender.Start(); - // } - //} - - //private void RhinoDoc_AddRhinoObject( object sender, RhinoObjectEventArgs e ) - //{ - // //Debug.WriteLine("ADD Event"); - // if ( Paused ) - // { - // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); - // return; - // } - // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) - // { - // DataSender.Start(); - // } - //} - - //private void RhinoDoc_DeleteRhinoObject( object sender, RhinoObjectEventArgs e ) - //{ - // if ( Paused ) - // { - // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); - // return; - // } - // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) - // { - // DataSender.Start(); - // } - //} - - //private void RhinoDoc_ModifyObjectAttributes( object sender, RhinoModifyObjectAttributesEventArgs e ) - //{ - // //Debug.WriteLine("MODIFY Event"); - // //Prevents https://github.com/speckleworks/SpeckleRhino/issues/51 from happening - // if ( Converter.getBase64( e.NewAttributes ) == Converter.getBase64( e.OldAttributes ) ) return; - - // if ( Paused ) - // { - // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); - // return; - // } - // if ( e.RhinoObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) - // { - // DataSender.Start(); - // } - //} - - public void SetClientEvents( ) - { - Client.OnError += Client_OnError; - Client.OnLogData += Client_OnLogData; - Client.OnWsMessage += Client_OnWsMessage; - Client.OnReady += Client_OnReady; - } + InitTrackedObjects(InitPayload); + DataSender.Start(); + }); - //public void SetTimers( ) - //{ - // MetadataSender = new System.Timers.Timer( 500 ) { AutoReset = false, Enabled = false }; - // MetadataSender.Elapsed += MetadataSender_Elapsed; + } - // DataSender = new System.Timers.Timer( 2000 ) { AutoReset = false, Enabled = false }; - // DataSender.Elapsed += DataSender_Elapsed; - //} + public void InitTrackedObjects(dynamic payload) + { + //TODO: implement + //foreach (string guid in payload.selection) + // RhinoDoc.ActiveDoc.Objects.Find(new Guid(guid)).Attributes.SetUserString("spk_" + StreamId, StreamId); + } - private void Client_OnReady( object source, SpeckleEventArgs e ) - { - Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Ready Event." ) ); - } + //public void AddTrackedObjects( string[ ] guids ) + //{ + // foreach ( string guid in guids ) + // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId ); - //private void DataSender_Elapsed( object sender, ElapsedEventArgs e ) - //{ - // Debug.WriteLine( "Boing! Boing!" ); - // DataSender.Stop(); - // SendStaggeredUpdate(); - // Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Update Sent." ) ); - //} + // DataSender.Start(); + //} - private void MetadataSender_Elapsed( object sender, ElapsedEventArgs e ) - { - Debug.WriteLine( "Ping! Ping!" ); - MetadataSender.Stop(); - Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "Update Sent." ) ); - } + //public void RemoveTrackedObjects( string[ ] guids ) + //{ + // foreach ( string guid in guids ) + // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, null ); - private void Client_OnWsMessage( object source, SpeckleEventArgs e ) - { - Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( "WS message received and ignored." ) ); - } + // DataSender.Start(); + //} - private void Client_OnLogData( object source, SpeckleEventArgs e ) - { - Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( e.EventData ) ); - } + //public void SetRhinoEvents( ) + //{ + // RhinoDoc.ModifyObjectAttributes += RhinoDoc_ModifyObjectAttributes; + // RhinoDoc.DeleteRhinoObject += RhinoDoc_DeleteRhinoObject; + // RhinoDoc.AddRhinoObject += RhinoDoc_AddRhinoObject; + // RhinoDoc.UndeleteRhinoObject += RhinoDoc_UndeleteRhinoObject; + // RhinoDoc.LayerTableEvent += RhinoDoc_LayerTableEvent; + //} - private void Client_OnError( object source, SpeckleEventArgs e ) - { - Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( e.EventData ) ); - } + //public void UnsetRhinoEvents( ) + //{ + // RhinoDoc.ModifyObjectAttributes -= RhinoDoc_ModifyObjectAttributes; + // RhinoDoc.DeleteRhinoObject -= RhinoDoc_DeleteRhinoObject; + // RhinoDoc.AddRhinoObject -= RhinoDoc_AddRhinoObject; + // RhinoDoc.UndeleteRhinoObject -= RhinoDoc_UndeleteRhinoObject; + // RhinoDoc.LayerTableEvent -= RhinoDoc_LayerTableEvent; + //} - //public void ForceUpdate( ) - //{ - // SendStaggeredUpdate( true ); - //} - - // TODO: This method, or an abstracted version of it, should move to Speckle Core. - //public async void SendStaggeredUpdate( bool force = false ) - //{ - - // if ( Paused && !force ) - // { - // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); - // return; - // } - - // if ( IsSendingUpdate ) - // { - // Expired = true; - // return; - // } - - // IsSendingUpdate = true; - - // Context.NotifySpeckleFrame( "client-is-loading", StreamId, "" ); - - // var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString( "spk_" + this.StreamId, "*", false ).OrderBy( obj => obj.Attributes.LayerIndex ); - - // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Converting " + objs.Count() + " objects..." ); - - // List pLayers = new List(); - // List convertedObjects = new List(); - // List> objectUpdatePayloads = new List>(); - - // long totalBucketSize = 0; - // long currentBucketSize = 0; - // List currentBucketObjects = new List(); - // List allObjects = new List(); - - // int lindex = -1, count = 0, orderIndex = 0; - // foreach ( RhinoObject obj in objs ) - // { - // // layer list creation - // Rhino.DocObjects.Layer layer = RhinoDoc.ActiveDoc.Layers[ obj.Attributes.LayerIndex ]; - // if ( lindex != obj.Attributes.LayerIndex ) - // { - // var spkLayer = new SpeckleCore.Layer() - // { - // Name = layer.FullPath, - // Guid = layer.Id.ToString(), - // ObjectCount = 1, - // StartIndex = count, - // OrderIndex = orderIndex++, - // Properties = new LayerProperties() { Color = new SpeckleCore.SpeckleBaseColor() { A = 1, Hex = System.Drawing.ColorTranslator.ToHtml( layer.Color ) }, } - // }; - - // pLayers.Add( spkLayer ); - // lindex = obj.Attributes.LayerIndex; - // } - // else - // { - // var spkl = pLayers.FirstOrDefault( pl => pl.Name == layer.FullPath ); - // spkl.ObjectCount++; - // } - - // count++; - - // // object conversion - // SpeckleObject convertedObject; - - // convertedObject = Converter.Serialise( obj.Geometry ); - // convertedObject.ApplicationId = obj.Id.ToString(); - // allObjects.Add( convertedObject ); - - // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Converted " + count + " objects out of " + objs.Count() + "." ); - - // // check cache and see what the response from the server is when sending placeholders - // // in the ObjectCreateBulkAsyncRoute - // if ( Context.SpeckleObjectCache.ContainsKey( convertedObject.Hash ) ) - // { - // convertedObject = new SpecklePlaceholder() { Hash = convertedObject.Hash, _id = Context.SpeckleObjectCache[ convertedObject.Hash ]._id, ApplicationId = Context.SpeckleObjectCache[ convertedObject.Hash ].ApplicationId }; - // } - - // // size checking & bulk object creation payloads creation - // long size = Converter.getBytes( convertedObject ).Length; - // currentBucketSize += size; - // totalBucketSize += size; - // currentBucketObjects.Add( convertedObject ); - - // if ( currentBucketSize > 2e6 ) - // { - // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL - // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This stream contains a super big object. These are not supported yet :(" ) ); - // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); - // IsSendingUpdate = false; - // return; - // } - - // if ( currentBucketSize > 5e5 ) // restrict max to ~500kb; should it be user config? anyway these functions should go into core. at one point. - // { - // Debug.WriteLine( "Reached payload limit. Making a new one, current #: " + objectUpdatePayloads.Count ); - // objectUpdatePayloads.Add( currentBucketObjects ); - // currentBucketObjects = new List(); - // currentBucketSize = 0; - // } - - // // catch overflows early - // if ( totalBucketSize >= 50e6 ) - // { - // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)" ) ); - // IsSendingUpdate = false; - // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); - // return; - // } - // } - - // // last bucket - // if ( currentBucketObjects.Count > 0 ) - // objectUpdatePayloads.Add( currentBucketObjects ); - - // Debug.WriteLine( "Finished, payload object update count is: " + objectUpdatePayloads.Count + " total bucket size is (kb) " + totalBucketSize / 1000 ); - - // if ( objectUpdatePayloads.Count > 100 || totalBucketSize >= 50e6 ) - // { - // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL - // Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( "This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)" ) ); - // IsSendingUpdate = false; - // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); - // return; - // } - - // // create bulk object creation tasks - // int k = 0; - // List responses = new List(); - // foreach ( var payload in objectUpdatePayloads ) - // { - // Context.NotifySpeckleFrame( "client-progress-message", StreamId, String.Format( "Sending payload {0} out of {1}", k++, objectUpdatePayloads.Count ) ); - // try - // { - // responses.Add( await Client.ObjectCreateAsync( payload ) ); - // } - // catch ( Exception err ) - // { - // Context.NotifySpeckleFrame( "client-error", Client.Stream.StreamId, JsonConvert.SerializeObject( err.Message ) ); - // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); - // IsSendingUpdate = false; - // return; - // } - // } - - // Context.NotifySpeckleFrame( "client-progress-message", StreamId, "Updating stream..." ); - - // // finalise layer creation - // foreach ( var layer in pLayers ) - // layer.Topology = "0-" + layer.ObjectCount + " "; - - // // create placeholders for stream update payload - // List placeholders = new List(); - // int m = 0; - // foreach ( var myResponse in responses ) - // foreach ( var obj in myResponse.Resources ) placeholders.Add( new SpecklePlaceholder() { _id = obj._id, ApplicationId = allObjects[ m++ ].ApplicationId } ); - - // // create stream update payload - // SpeckleStream streamUpdatePayload = new SpeckleStream(); - // streamUpdatePayload.Layers = pLayers; - // streamUpdatePayload.Objects = placeholders; - // streamUpdatePayload.Name = Client.Stream.Name; - - // // set some base properties (will be overwritten) - // var baseProps = new Dictionary(); - // baseProps[ "units" ] = RhinoDoc.ActiveDoc.ModelUnitSystem.ToString(); - // baseProps[ "tolerance" ] = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; - // baseProps[ "angleTolerance" ] = RhinoDoc.ActiveDoc.ModelAngleToleranceRadians; - // streamUpdatePayload.BaseProperties = baseProps; - - // // push it to the server yo! - // ResponseBase response = null; - // try - // { - // response = await Client.StreamUpdateAsync( Client.Stream.StreamId, streamUpdatePayload ); - // } - // catch ( Exception err ) - // { - // Context.NotifySpeckleFrame( "client-error", Client.Stream.StreamId, JsonConvert.SerializeObject( err.Message ) ); - // IsSendingUpdate = false; - // return; - // } - - // // put the objects in the cache - // int l = 0; - - // foreach ( var obj in streamUpdatePayload.Objects ) - // { - // obj._id = placeholders[ l ]._id; - // Context.SpeckleObjectCache[ allObjects[ l ].Hash ] = placeholders[ l ]; - // l++; - // } - - // // emit events, etc. - // Client.Stream.Layers = streamUpdatePayload.Layers.ToList(); - // Client.Stream.Objects = placeholders; - - // Context.NotifySpeckleFrame( "client-metadata-update", StreamId, Client.Stream.ToJson() ); - // Context.NotifySpeckleFrame( "client-done-loading", StreamId, "" ); - - // Client.BroadcastMessage( new { eventType = "update-global" } ); - - // IsSendingUpdate = false; - // if ( Expired ) - // { - // DataSender.Start(); - // } - // Expired = false; - //} - - public ClientRole GetRole( ) - { - return ClientRole.Sender; - } + //private void RhinoDoc_LayerTableEvent( object sender, Rhino.DocObjects.Tables.LayerTableEventArgs e ) + //{ + // DataSender.Start(); + //} - public string GetClientId( ) - { - return Client.ClientId; - } + //private void RhinoDoc_UndeleteRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // //Debug.WriteLine("UNDELETE Event"); + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} - public void TogglePaused( bool status ) - { - Paused = status; - } + //private void RhinoDoc_AddRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // //Debug.WriteLine("ADD Event"); + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} - public void ToggleVisibility( bool status ) - { - Visible = status; - } + //private void RhinoDoc_DeleteRhinoObject( object sender, RhinoObjectEventArgs e ) + //{ + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + //private void RhinoDoc_ModifyObjectAttributes( object sender, RhinoModifyObjectAttributesEventArgs e ) + //{ + // //Debug.WriteLine("MODIFY Event"); + // //Prevents https://github.com/speckleworks/SpeckleRhino/issues/51 from happening + // if ( Converter.getBase64( e.NewAttributes ) == Converter.getBase64( e.OldAttributes ) ) return; + + // if ( Paused ) + // { + // Context.NotifySpeckleFrame( "client-expired", StreamId, "" ); + // return; + // } + // if ( e.RhinoObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId ) + // { + // DataSender.Start(); + // } + //} + + public void SetClientEvents( ) + { + Client.OnError += Client_OnError; + Client.OnLogData += Client_OnLogData; + Client.OnWsMessage += Client_OnWsMessage; + Client.OnReady += Client_OnReady; + } + + public void SetTimers() + { + MetadataSender = new Timer(500) { AutoReset = false, Enabled = false }; + MetadataSender.Elapsed += MetadataSender_Elapsed; + + DataSender = new Timer(2000) { AutoReset = false, Enabled = false }; + DataSender.Elapsed += DataSender_Elapsed; + } + + private void Client_OnReady( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Ready Event.")); + } + + private void DataSender_Elapsed(object sender, ElapsedEventArgs e) + { + Debug.WriteLine("Boing! Boing!"); + DataSender.Stop(); + SendStaggeredUpdate(); + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Update Sent.")); + } + + private void MetadataSender_Elapsed( object sender, ElapsedEventArgs e ) + { + Debug.WriteLine("Ping! Ping!"); + MetadataSender.Stop(); + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Update Sent.")); + } + + private void Client_OnWsMessage( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("WS message received and ignored.")); + } + + private void Client_OnLogData( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject(e.EventData)); + } + + private void Client_OnError( object source, SpeckleEventArgs e ) + { + Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject(e.EventData)); + } + + public void ForceUpdate() + { + SendStaggeredUpdate(true); + } + + public async void SendStaggeredUpdate(bool force = false) + { + //TODO: Implement! + //if (Paused && !force) + //{ + // Context.NotifySpeckleFrame("client-expired", StreamId, ""); + // return; + //} + + //if (IsSendingUpdate) + //{ + // Expired = true; + // return; + //} + + //IsSendingUpdate = true; + + //Context.NotifySpeckleFrame("client-is-loading", StreamId, ""); + + ////TODO: I am not sure how we are going to send stuff to Speckle just yet. + ////TODO: I only know that serializing the whole damn Revit Element is not a good idea. + //var objs = new Element[]{}; + ////var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString("spk_" + this.StreamId, "*", false).OrderBy(obj => obj.Attributes.LayerIndex); + + //Context.NotifySpeckleFrame("client-progress-message", StreamId, "Converting " + objs.Count() + " objects..."); + + //List pLayers = new List(); + //List convertedObjects = new List(); + //List> objectUpdatePayloads = new List>(); + + //long totalBucketSize = 0; + //long currentBucketSize = 0; + //List currentBucketObjects = new List(); + //List allObjects = new List(); + + //int lindex = -1, count = 0, orderIndex = 0; + //foreach (RhinoObject obj in objs) + //{ + // // layer list creation + // Rhino.DocObjects.Layer layer = RhinoDoc.ActiveDoc.Layers[obj.Attributes.LayerIndex]; + // if (lindex != obj.Attributes.LayerIndex) + // { + // var spkLayer = new SpeckleCore.Layer() + // { + // Name = layer.FullPath, + // Guid = layer.Id.ToString(), + // ObjectCount = 1, + // StartIndex = count, + // OrderIndex = orderIndex++, + // Properties = new LayerProperties() { Color = new SpeckleCore.SpeckleBaseColor() { A = 1, Hex = System.Drawing.ColorTranslator.ToHtml(layer.Color) }, } + // }; + + // pLayers.Add(spkLayer); + // lindex = obj.Attributes.LayerIndex; + // } + // else + // { + // var spkl = pLayers.FirstOrDefault(pl => pl.Name == layer.FullPath); + // spkl.ObjectCount++; + // } + + // count++; + + // // object conversion + // SpeckleObject convertedObject; + + // convertedObject = Converter.Serialise(obj.Geometry); + // convertedObject.ApplicationId = obj.Id.ToString(); + // allObjects.Add(convertedObject); + + // Context.NotifySpeckleFrame("client-progress-message", StreamId, "Converted " + count + " objects out of " + objs.Count() + "."); + + // // check cache and see what the response from the server is when sending placeholders + // // in the ObjectCreateBulkAsyncRoute + // if (Context.SpeckleObjectCache.ContainsKey(convertedObject.Hash)) + // { + // convertedObject = new SpecklePlaceholder() { Hash = convertedObject.Hash, _id = Context.SpeckleObjectCache[convertedObject.Hash]._id, ApplicationId = Context.SpeckleObjectCache[convertedObject.Hash].ApplicationId }; + // } + + // // size checking & bulk object creation payloads creation + // long size = Converter.getBytes(convertedObject).Length; + // currentBucketSize += size; + // totalBucketSize += size; + // currentBucketObjects.Add(convertedObject); + + // if (currentBucketSize > 2e6) + // { + // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL + // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This stream contains a super big object. These are not supported yet :(")); + // Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + // IsSendingUpdate = false; + // return; + // } + + // if (currentBucketSize > 5e5) // restrict max to ~500kb; should it be user config? anyway these functions should go into core. at one point. + // { + // Debug.WriteLine("Reached payload limit. Making a new one, current #: " + objectUpdatePayloads.Count); + // objectUpdatePayloads.Add(currentBucketObjects); + // currentBucketObjects = new List(); + // currentBucketSize = 0; + // } + + // // catch overflows early + // if (totalBucketSize >= 50e6) + // { + // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)")); + // IsSendingUpdate = false; + // Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + // return; + // } + //} + + //// last bucket + //if (currentBucketObjects.Count > 0) + // objectUpdatePayloads.Add(currentBucketObjects); + + //Debug.WriteLine("Finished, payload object update count is: " + objectUpdatePayloads.Count + " total bucket size is (kb) " + totalBucketSize / 1000); + + //if (objectUpdatePayloads.Count > 100 || totalBucketSize >= 50e6) + //{ + // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL + // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)")); + // IsSendingUpdate = false; + // Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + // return; + //} + + //// create bulk object creation tasks + //int k = 0; + //List responses = new List(); + //foreach (var payload in objectUpdatePayloads) + //{ + // Context.NotifySpeckleFrame("client-progress-message", StreamId, String.Format("Sending payload {0} out of {1}", k++, objectUpdatePayloads.Count)); + // try + // { + // responses.Add(await Client.ObjectCreateAsync(payload)); + // } + // catch (Exception err) + // { + // Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + // Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + // IsSendingUpdate = false; + // return; + // } + //} + + //Context.NotifySpeckleFrame("client-progress-message", StreamId, "Updating stream..."); + + //// finalise layer creation + //foreach (var layer in pLayers) + // layer.Topology = "0-" + layer.ObjectCount + " "; + + //// create placeholders for stream update payload + //List placeholders = new List(); + //int m = 0; + //foreach (var myResponse in responses) + // foreach (var obj in myResponse.Resources) placeholders.Add(new SpecklePlaceholder() { _id = obj._id, ApplicationId = allObjects[m++].ApplicationId }); + + //// create stream update payload + //SpeckleStream streamUpdatePayload = new SpeckleStream(); + //streamUpdatePayload.Layers = pLayers; + //streamUpdatePayload.Objects = placeholders; + //streamUpdatePayload.Name = Client.Stream.Name; + + //// set some base properties (will be overwritten) + //var baseProps = new Dictionary(); + //baseProps["units"] = RhinoDoc.ActiveDoc.ModelUnitSystem.ToString(); + //baseProps["tolerance"] = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; + //baseProps["angleTolerance"] = RhinoDoc.ActiveDoc.ModelAngleToleranceRadians; + //streamUpdatePayload.BaseProperties = baseProps; + + //// push it to the server yo! + //ResponseBase response = null; + //try + //{ + // response = await Client.StreamUpdateAsync(Client.Stream.StreamId, streamUpdatePayload); + //} + //catch (Exception err) + //{ + // Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message)); + // IsSendingUpdate = false; + // return; + //} + + //// put the objects in the cache + //int l = 0; + + //foreach (var obj in streamUpdatePayload.Objects) + //{ + // obj._id = placeholders[l]._id; + // Context.SpeckleObjectCache[allObjects[l].Hash] = placeholders[l]; + // l++; + //} + + //// emit events, etc. + //Client.Stream.Layers = streamUpdatePayload.Layers.ToList(); + //Client.Stream.Objects = placeholders; + + //Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson()); + //Context.NotifySpeckleFrame("client-done-loading", StreamId, ""); + + //Client.BroadcastMessage(new { eventType = "update-global" }); + + //IsSendingUpdate = false; + //if (Expired) + //{ + // DataSender.Start(); + //} + //Expired = false; + } + + public ClientRole GetRole( ) + { + return ClientRole.Sender; + } + + public string GetClientId( ) + { + return Client.ClientId; + } + + public void TogglePaused( bool status ) + { + Paused = status; + } + + public void ToggleVisibility( bool status ) + { + Visible = status; + } //public void ToggleLayerHover( string layerId, bool status ) //{ @@ -491,12 +492,12 @@ public void ToggleVisibility( bool status ) public void ToggleLayerVisibility(string layerId, bool status) { - throw new NotImplementedException(); + //TODO: Implement } public void ToggleLayerHover(string layerId, bool status) { - throw new NotImplementedException(); + //TODO: Implement } public void Dispose(bool delete = false) diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index 74a409e..870b391 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -1,17 +1,17 @@ #region Namespaces -using System.Collections.Generic; + +using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Windows.Media; using System.Windows.Media.Imaging; using Autodesk.Revit.Attributes; -using Autodesk.Revit.DB; using Autodesk.Revit.DB.Events; using Autodesk.Revit.UI; -using SpeckleRevitPlugin.Classes; using SpeckleRevitPlugin.UI; -using SpeckleRevitPlugin.Utilities; + #endregion namespace SpeckleRevitPlugin.Entry @@ -117,7 +117,7 @@ public Result OnShutdown(UIControlledApplication a) return Result.Succeeded; } - #region Utilities + #region Ribbon Utilities /// /// Load an Image Source from File @@ -125,7 +125,7 @@ public Result OnShutdown(UIControlledApplication a) /// /// /// - private ImageSource LoadPngImgSource(string SourceName) + private static ImageSource LoadPngImgSource(string SourceName) { try { @@ -159,7 +159,6 @@ public void AddRibbonPanel(UIControlledApplication a) { try { - // First Create the Tab a.CreateRibbonTab("Speckle"); } catch @@ -169,77 +168,74 @@ public void AddRibbonPanel(UIControlledApplication a) // Tools AddButton("Speckle", - "Plugin\r\nTest", - "Plugin\r\nTest", + "Show\r\nSpeckle", + "Show\r\nSpeckle", "SpeckleRevitPlugin.Resources.Template_16.png", "SpeckleRevitPlugin.Resources.Template_32.png", (m_Path + "\\SpeckleRevitPlugin.dll"), "SpeckleRevitPlugin.Entry.ExtCmd", "Speckle connection test for Revit."); + + AddButton("Tools", + "Speckle\r\nWall", + "Speckle\r\nWall", + "SpeckleRevitPlugin.Resources.Template_16.png", + "SpeckleRevitPlugin.Resources.Template_32.png", + (m_Path + "\\SpeckleRevitPlugin.dll"), + "SpeckleRevitPlugin.Entry.SpeckleWallCmd", + "Make Walls in Revit using Speckle Streams."); } /// /// Add a button to a Ribbon Tab /// - /// The name of the ribbon panel - /// The Name of the Button - /// Command Text - /// Small Image - /// Large Image + /// The name of the ribbon panel + /// The Name of the Button + /// Command Text + /// Small Image + /// Large Image /// Path to the DLL file /// Full qualified class descriptor - /// Tooltip to add to the button + /// Tooltip to add to the button /// /// - private bool AddButton(string Rpanel, string ButtonName, string ButtonText, string ImagePath16, string ImagePath32, string dllPath, string dllClass, string Tooltip) + private void AddButton(string panel, + string bName, string bText, string iPath16, string iPath32, + string dllPath, string dllClass, string tooltip) { try { - // The Ribbon Panel - RibbonPanel ribbonPanel = null; - - // Find the Panel within the Case Tab - var rp = new List(); - rp = uiApp.GetRibbonPanels("Speckle"); - foreach (RibbonPanel x in rp) - { - if (x.Name.ToUpper() == Rpanel.ToUpper()) - { - ribbonPanel = x; - } - } - // Create the Panel if it doesn't Exist - if (ribbonPanel == null) - { - ribbonPanel = uiApp.CreateRibbonPanel("Speckle", Rpanel); - } + var ribbonPanel = + uiApp.GetRibbonPanels("Speckle").FirstOrDefault(x => + string.Equals(x.Name, panel, StringComparison.OrdinalIgnoreCase)) ?? + uiApp.CreateRibbonPanel("Speckle", panel); // Create the Pushbutton Data - var pushButtonData = new PushButtonData(ButtonName, ButtonText, dllPath, dllClass); - if (!string.IsNullOrEmpty(ImagePath16)) + var pushButtonData = new PushButtonData(bName, bText, dllPath, dllClass); + if (!string.IsNullOrEmpty(iPath16)) { try { - pushButtonData.Image = LoadPngImgSource(ImagePath16); + pushButtonData.Image = LoadPngImgSource(iPath16); } catch { Debug.WriteLine("Image not found", "SPK"); } } - if (!string.IsNullOrEmpty(ImagePath32)) + if (!string.IsNullOrEmpty(iPath32)) { try { - pushButtonData.LargeImage = LoadPngImgSource(ImagePath32); + pushButtonData.LargeImage = LoadPngImgSource(iPath32); } catch { Debug.WriteLine("Image not found", "SPK"); } } - pushButtonData.ToolTip = Tooltip; + pushButtonData.ToolTip = tooltip; // Add the button to the tab var unused = (PushButton)ribbonPanel.AddItem(pushButtonData); @@ -248,7 +244,6 @@ private bool AddButton(string Rpanel, string ButtonName, string ButtonText, stri { // ignored } - return true; } /// diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj index 1e015a1..12304ed 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj @@ -66,6 +66,7 @@ True Resources.resx + form_Main.xaml diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs new file mode 100644 index 0000000..ee3d571 --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs @@ -0,0 +1,15 @@ +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using Autodesk.Revit.UI; + +namespace SpeckleRevitPlugin.Tools.WallTool +{ + [Transaction(TransactionMode.Manual)] + public class SpeckleWallCmd : IExternalCommand + { + public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) + { + return Result.Succeeded; + } + } +} diff --git a/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs index 624666d..959089d 100644 --- a/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs +++ b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs @@ -1,9 +1,11 @@ -using System; +#region Namespaces +using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; +#endregion namespace SpeckleRevitPlugin.Utilities { @@ -62,12 +64,14 @@ public SearchAssembliesBinder(Assembly currentAssembly, bool searchInDlls) public override Type BindToType(string assemblyName, string typeName) { - var assemblyNames = new List(); - assemblyNames.Add(_currentAssembly.GetName()); // EXE + var assemblyNames = new List + { + _currentAssembly.GetName() + }; if (_searchInDlls) { - assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies()); // DLLs + assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies()); } foreach (var an in assemblyNames) diff --git a/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs index c4ba9a3..9725e9d 100644 --- a/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs +++ b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs @@ -1,20 +1,20 @@ -using System; +#region Namespaces +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Autodesk.Revit.DB; using Autodesk.Revit.DB.ExtensibleStorage; +#endregion namespace SpeckleRevitPlugin.Utilities { public static class SchemaUtilities { /// - /// + /// Retrieves Schema by its name. /// - /// + /// Schema name. /// public static Schema GetSchema(string schemaName) { @@ -24,15 +24,19 @@ public static Schema GetSchema(string schemaName) } /// - /// + /// Checks if Schema exists in Document. /// - /// + /// Schema name. /// public static bool SchemaExist(string schemaName) { return GetSchema(schemaName) != null; } + /// + /// Creates Speckle Schema. + /// + /// public static Schema CreateSchema() { var schemaGuid = new Guid(Properties.Resources.SchemaId); @@ -53,9 +57,16 @@ public static Schema CreateSchema() return schema; } - public static void AddSchemaEntity(Schema schema, Element e, string fieldName, Dictionary fieldValue) + /// + /// Adds new Speckle Schema Entity to Element. + /// + /// Schema + /// Element + /// Filed name + /// Field value + public static void AddSchemaEntity(Schema s, Element e, string fName, Dictionary fValue) { - if (schema == null) + if (s == null) { throw new NullReferenceException("schema"); } @@ -63,16 +74,16 @@ public static void AddSchemaEntity(Schema schema, Element e, string fieldName, D { throw new NullReferenceException("element"); } - if (string.IsNullOrEmpty(fieldName)) + if (string.IsNullOrEmpty(fName)) { throw new NullReferenceException("fieldName"); } try { - var entity = new Entity(schema); - var settingsField = schema.GetField(fieldName); - entity.Set>(settingsField, fieldValue); + var entity = new Entity(s); + var settingsField = s.GetField(fName); + entity.Set>(settingsField, fValue); e.SetEntity(entity); } @@ -83,9 +94,10 @@ public static void AddSchemaEntity(Schema schema, Element e, string fieldName, D } /// - /// + /// Speckle Schema will always be stored on this Element, + /// so now we have a utility to get to it faster. /// - /// + /// Revit Document /// public static Element GetProjectInfo(Document doc) { @@ -95,9 +107,16 @@ public static Element GetProjectInfo(Document doc) return pInfo; } - public static void UpdateSchemaEntity(Schema schema, Element e, string fieldName, Dictionary fieldValue) + /// + /// Updates existing Speckle Schema Entity on given Element. + /// + /// + /// + /// + /// + public static void UpdateSchemaEntity(Schema s, Element e, string fName, Dictionary fValue) { - if (schema == null) + if (s == null) { throw new NullReferenceException("schema"); } @@ -105,16 +124,16 @@ public static void UpdateSchemaEntity(Schema schema, Element e, string fieldName { throw new NullReferenceException("element"); } - if (string.IsNullOrEmpty(fieldName)) + if (string.IsNullOrEmpty(fName)) { throw new NullReferenceException("fieldName"); } try { - var entity = e.GetEntity(schema); - var field = schema.GetField(fieldName); - entity.Set>(field, fieldValue); + var entity = e.GetEntity(s); + var field = s.GetField(fName); + entity.Set>(field, fValue); e.SetEntity(entity); } From 6c1057e8884dee6f34f473e0c4240b3aadd3f4f8 Mon Sep 17 00:00:00 2001 From: ksobon Date: Wed, 23 May 2018 08:53:03 -0400 Subject: [PATCH 5/6] cleanup of revit events --- SpeckleRevitPlugin/Classes/Interop.cs | 85 ++++++++++++++++++--------- SpeckleRevitPlugin/Entry/AppMain.cs | 48 +++++---------- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs index eff256a..9f53f10 100644 --- a/SpeckleRevitPlugin/Classes/Interop.cs +++ b/SpeckleRevitPlugin/Classes/Interop.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using Autodesk.Revit.DB; +using Autodesk.Revit.DB.Events; using CefSharp; using CefSharp.Wpf; using Newtonsoft.Json; @@ -46,21 +47,60 @@ public Interop(ChromiumWebBrowser originalBrowser) Browser = originalBrowser; ReadUserAccounts(); - AppMain.OnModelSynched += Revit_ModelSynched; + AppMain.uiApp.ControlledApplication.DocumentCreated += Revit_DocumentCreated; + AppMain.uiApp.ControlledApplication.DocumentOpened += Revit_DocumentOpened; + AppMain.uiApp.ControlledApplication.DocumentSaved += Revit_DocumentSaved; + AppMain.uiApp.ControlledApplication.DocumentSynchronizedWithCentral += Revit_DocumentSynchronized; SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved; } - public void SetBrowser(ChromiumWebBrowser browser) + public void Dispose() { - Browser = browser; + AppMain.uiApp.ControlledApplication.DocumentCreated -= Revit_DocumentCreated; + AppMain.uiApp.ControlledApplication.DocumentOpened -= Revit_DocumentOpened; + AppMain.uiApp.ControlledApplication.DocumentSaved -= Revit_DocumentSaved; + AppMain.uiApp.ControlledApplication.DocumentSynchronizedWithCentral -= Revit_DocumentSynchronized; + SpeckleRequestHandler.OnClientsRetrieved -= OnClientsRetrieved; + + RemoveAllClients(); } - public void Dispose() + #region Global Events + + private void Revit_DocumentSaved(object sender, DocumentSavedEventArgs e) + { + SaveFileClients(); + } + + private void Revit_DocumentSynchronized(object sender, DocumentSynchronizedWithCentralEventArgs e) + { + SaveFileClients(); + } + + private void Revit_DocumentCreated(object sender, DocumentCreatedEventArgs e) { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) return; + + NotifySpeckleFrame("purge-clients", "", ""); RemoveAllClients(); + } - AppMain.OnModelSynched -= Revit_ModelSynched; - SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved; + private void Revit_DocumentOpened(object sender, DocumentOpenedEventArgs e) + { + var doc = e.Document; + if (doc == null || doc.IsFamilyDocument) return; + + NotifySpeckleFrame("purge-clients", "", ""); + RemoveAllClients(); + InstantiateFileClients(); + } + + #endregion + + public void SetBrowser(ChromiumWebBrowser browser) + { + Browser = browser; } public void ShowDev() @@ -165,6 +205,18 @@ public bool AddReceiverClient( string payload ) return true; } + /// + /// + /// + public void InstantiateFileClients() + { + // (Konrad) This is the thread safe way of interacting with Revit. + // Also it's possible that the Speckle app initiates before Revit + // Document is open/ready making Extensible Storage inaccessible. + AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients); + AppMain.SpeckleEvent.Raise(); + } + /// /// Handler for an event called by Revit when Clients have been retrived from Schema. /// @@ -191,27 +243,6 @@ private void OnClientsRetrieved(IDictionary receivers, IDictiona //} } - /// - /// Handler for an event called by Revit when Document is either saved/synched. - /// It's a good time to store Clients in an Extensible Storage then. - /// - private void Revit_ModelSynched() - { - SaveFileClients(); - } - - /// - /// - /// - public void InstantiateFileClients() - { - // (Konrad) This is the thread safe way of interacting with Revit. - // Also it's possible that the Speckle app initiates before Revit - // Document is open/ready making Extensible Storage inaccessible. - AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients); - AppMain.SpeckleEvent.Raise(); - } - /// /// It's used by Speckle View to trigger Client storage. /// diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index 870b391..8ee39c6 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -20,17 +20,13 @@ namespace SpeckleRevitPlugin.Entry public class AppMain : IExternalApplication { private static readonly string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - private static UIControlledApplication uiApp; private static AppMain _thisApp; - public static ExternalEvent SpeckleEvent; - public static SpeckleRequestHandler SpeckleHandler = new SpeckleRequestHandler(); - internal static FormMainDock MainDock; internal DockablePaneProviderData DockData; - //internal static SettingsHelper Settings { get; set; } - public delegate void ModelSynched(); - public static event ModelSynched OnModelSynched; + public static UIControlledApplication uiApp; + public static ExternalEvent SpeckleEvent; + public static SpeckleRequestHandler SpeckleHandler = new SpeckleRequestHandler(); public Result OnStartup(UIControlledApplication a) { @@ -61,8 +57,6 @@ public Result OnStartup(UIControlledApplication a) a.ControlledApplication.DocumentCreated += OnDocumentCreated; a.ControlledApplication.DocumentOpened += OnDocumentOpened; - a.ControlledApplication.DocumentSynchronizedWithCentral += OnDocumentSynchronized; - a.ControlledApplication.DocumentSaving += OnDocumentSaving; AddRibbonPanel(a); @@ -74,22 +68,6 @@ public Result OnStartup(UIControlledApplication a) } } - private static void OnDocumentSaving(object sender, DocumentSavingEventArgs e) - { - var doc = e.Document; - if (doc == null || doc.IsFamilyDocument) return; - - OnModelSynched?.Invoke(); - } - - private static void OnDocumentSynchronized(object sender, DocumentSynchronizedWithCentralEventArgs e) - { - var doc = e.Document; - if (doc == null || doc.IsFamilyDocument) return; - - OnModelSynched?.Invoke(); - } - private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e) { var doc = e.Document; @@ -97,8 +75,6 @@ private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e) { HideDockablePane(); } - - // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc } private void OnDocumentCreated(object sender, DocumentCreatedEventArgs e) @@ -108,12 +84,13 @@ private void OnDocumentCreated(object sender, DocumentCreatedEventArgs e) { HideDockablePane(); } - - // TODO: In theory this means that we either opened a new doc or another doc. We need to re-instantiate the speckle panel/clients for a new doc } public Result OnShutdown(UIControlledApplication a) { + a.ControlledApplication.DocumentCreated -= OnDocumentCreated; + a.ControlledApplication.DocumentOpened -= OnDocumentOpened; + return Result.Succeeded; } @@ -136,11 +113,14 @@ private static ImageSource LoadPngImgSource(string SourceName) var icon = assembly.GetManifestResourceStream(SourceName); // Decoder - var decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); + if (icon != null) + { + var decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); - // Source - ImageSource m_source = decoder.Frames[0]; - return (m_source); + // Source + ImageSource m_source = decoder.Frames[0]; + return (m_source); + } } catch { @@ -199,7 +179,7 @@ public void AddRibbonPanel(UIControlledApplication a) /// Tooltip to add to the button /// /// - private void AddButton(string panel, + private static void AddButton(string panel, string bName, string bText, string iPath16, string iPath32, string dllPath, string dllClass, string tooltip) { From f5040709a4598cc667ca84c879f8efd9c0818bb7 Mon Sep 17 00:00:00 2001 From: ksobon Date: Fri, 25 May 2018 14:54:23 -0400 Subject: [PATCH 6/6] some material design ui in wpf --- SpeckleRevitPlugin/Entry/AppMain.cs | 5 +- SpeckleRevitPlugin/SpeckleRevitPlugin.addin | 11 +- SpeckleRevitPlugin/SpeckleRevitPlugin.csproj | 67 +++++++ SpeckleRevitPlugin/Tools/WallTool/Messages.cs | 14 ++ .../Tools/WallTool/SpeckleWallCmd.cs | 28 ++- .../Tools/WallTool/SpeckleWallModel.cs | 42 +++++ .../Tools/WallTool/SpeckleWallView.xaml | 174 ++++++++++++++++++ .../Tools/WallTool/SpeckleWallView.xaml.cs | 29 +++ .../Tools/WallTool/SpeckleWallViewModel.cs | 117 ++++++++++++ .../UI/BooleanSelector/BooleanSelector.xaml | 35 ++++ .../BooleanSelector/BooleanSelector.xaml.cs | 28 +++ .../BooleanSelector/BooleanSelectorModel.cs | 12 ++ .../BooleanSelectorViewModel.cs | 19 ++ SpeckleRevitPlugin/UI/Converters.cs | 53 ++++++ .../UI/ElementSelector/ElementSelector.xaml | 35 ++++ .../ElementSelector/ElementSelector.xaml.cs | 13 ++ .../ElementSelector/ElementSelectorModel.cs | 12 ++ .../ElementSelectorViewModel.cs | 14 ++ SpeckleRevitPlugin/UI/InputControl.xaml | 140 ++++++++++++++ SpeckleRevitPlugin/UI/InputControl.xaml.cs | 13 ++ SpeckleRevitPlugin/UI/InputModel.cs | 12 ++ SpeckleRevitPlugin/UI/InputViewModel.cs | 126 +++++++++++++ SpeckleRevitPlugin/UI/InputWrapper.cs | 90 +++++++++ .../UI/StreamSelector/StreamSelector.xaml | 49 +++++ .../UI/StreamSelector/StreamSelector.xaml.cs | 13 ++ .../UI/StreamSelector/StreamSelectorModel.cs | 39 ++++ .../StreamSelector/StreamSelectorViewModel.cs | 72 ++++++++ SpeckleRevitPlugin/UI/form_MainDock.xaml.cs | 2 +- 28 files changed, 1250 insertions(+), 14 deletions(-) create mode 100644 SpeckleRevitPlugin/Tools/WallTool/Messages.cs create mode 100644 SpeckleRevitPlugin/Tools/WallTool/SpeckleWallModel.cs create mode 100644 SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml create mode 100644 SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs create mode 100644 SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs create mode 100644 SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml create mode 100644 SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs create mode 100644 SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs create mode 100644 SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs create mode 100644 SpeckleRevitPlugin/UI/Converters.cs create mode 100644 SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml create mode 100644 SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs create mode 100644 SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs create mode 100644 SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs create mode 100644 SpeckleRevitPlugin/UI/InputControl.xaml create mode 100644 SpeckleRevitPlugin/UI/InputControl.xaml.cs create mode 100644 SpeckleRevitPlugin/UI/InputModel.cs create mode 100644 SpeckleRevitPlugin/UI/InputViewModel.cs create mode 100644 SpeckleRevitPlugin/UI/InputWrapper.cs create mode 100644 SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml create mode 100644 SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs create mode 100644 SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs create mode 100644 SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs index 8ee39c6..46251b6 100644 --- a/SpeckleRevitPlugin/Entry/AppMain.cs +++ b/SpeckleRevitPlugin/Entry/AppMain.cs @@ -10,6 +10,7 @@ using Autodesk.Revit.Attributes; using Autodesk.Revit.DB.Events; using Autodesk.Revit.UI; +using SpeckleRevitPlugin.Classes; using SpeckleRevitPlugin.UI; #endregion @@ -21,9 +22,9 @@ public class AppMain : IExternalApplication { private static readonly string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); private static AppMain _thisApp; - internal static FormMainDock MainDock; internal DockablePaneProviderData DockData; + public static FormMainDock MainDock; public static UIControlledApplication uiApp; public static ExternalEvent SpeckleEvent; public static SpeckleRequestHandler SpeckleHandler = new SpeckleRequestHandler(); @@ -162,7 +163,7 @@ public void AddRibbonPanel(UIControlledApplication a) "SpeckleRevitPlugin.Resources.Template_16.png", "SpeckleRevitPlugin.Resources.Template_32.png", (m_Path + "\\SpeckleRevitPlugin.dll"), - "SpeckleRevitPlugin.Entry.SpeckleWallCmd", + "SpeckleRevitPlugin.Tools.WallTool.SpeckleWallCmd", "Make Walls in Revit using Speckle Streams."); } diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin index 45dd60a..1b05284 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin @@ -1,17 +1,8 @@  - - Command SpeckleRevitPlugin - Speckle plugin for Revit - .\Speckle\SpeckleRevitPlugin.dll - SpeckleRevitPlugin.Entry.ExtCmd - 76028dcb-3f9b-4a5b-9e8b-36b7a5564a56 - Speckle - http://speckle.works/ - Application SpeckleRevitPlugin - .\Speckle\SpeckleRevitPlugin.dll + Speckle\SpeckleRevitPlugin.dll SpeckleRevitPlugin.Entry.AppMain 0a03f52e-0ee0-4c19-82c4-8181626ec816 Speckle diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj index 12304ed..23cfe1f 100644 --- a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj +++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj @@ -5,6 +5,7 @@ Debug AnyCPU {EA37FC7B-0CD7-407B-A6AD-C594E4BD2043} + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties SpeckleRevitPlugin @@ -66,13 +67,41 @@ True Resources.resx + + + BooleanSelector.xaml + + + + + + ElementSelector.xaml + + + + + + InputControl.xaml + + + + SpeckleWallView.xaml + + form_Main.xaml form_MainDock.xaml + + StreamSelector.xaml + + + + + @@ -92,6 +121,24 @@ 63.0.1 + + 3.0.40218 + + + 1.2.4 + + + 1.1.3 + + + 2.3.0.823 + + + 0.0.10 + + + 5.3.0 + 11.0.1 @@ -100,6 +147,22 @@ + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -108,6 +171,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + diff --git a/SpeckleRevitPlugin/Tools/WallTool/Messages.cs b/SpeckleRevitPlugin/Tools/WallTool/Messages.cs new file mode 100644 index 0000000..fb97a9f --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/Messages.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SpeckleRevitPlugin.UI; + +namespace SpeckleRevitPlugin.Tools.WallTool +{ + public class InputDeleted + { + public InputViewModel InputViewModel { get; set; } + } +} diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs index ee3d571..e379d89 100644 --- a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallCmd.cs @@ -1,4 +1,7 @@ -using Autodesk.Revit.Attributes; +using System; +using System.Diagnostics; +using System.Windows.Interop; +using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; @@ -9,6 +12,29 @@ public class SpeckleWallCmd : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { + try + { + var uiApp = commandData.Application; + var doc = uiApp.ActiveUIDocument.Document; + var m = new SpeckleWallModel(doc); + var vm = new SpeckleWallViewModel(m); + var view = new SpeckleWallView + { + DataContext = vm + }; + + var unused = new WindowInteropHelper(view) + { + Owner = Process.GetCurrentProcess().MainWindowHandle + }; + + view.Show(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } return Result.Succeeded; } } diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallModel.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallModel.cs new file mode 100644 index 0000000..96d22f2 --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallModel.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Autodesk.Revit.DB; +using SpeckleRevitPlugin.UI; + +namespace SpeckleRevitPlugin.Tools.WallTool +{ + public class SpeckleWallModel + { + private Document Doc { get; set; } + + public SpeckleWallModel(Document doc) + { + Doc = doc; + } + + /// + /// + /// + /// + public ObservableCollection GetAllAvailableInputs() + { + var result = new HashSet(); + + foreach (var wt in new FilteredElementCollector(Doc).OfClass(typeof(WallType))) + { + foreach (Parameter p in wt.Parameters) + { + if(p.IsReadOnly) continue; + + var iw = new InputWrapper(p, false); + if(iw.DataType != LocalDataType.Boolean) continue; //TODO: just for now + + result.Add(iw); + } + } + + return new ObservableCollection(result); + } + } +} diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml new file mode 100644 index 0000000..3f9bf1d --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs new file mode 100644 index 0000000..dba78fc --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs @@ -0,0 +1,29 @@ +using MaterialDesignThemes.Wpf; +using MaterialDesignColors; +using MaterialDesignThemes.MahApps; +using MahApps.Metro; +using MahApps.Metro.Controls; + +namespace SpeckleRevitPlugin.Tools.WallTool +{ + /// + /// Interaction logic for SpeckleWallView.xaml + /// + public partial class SpeckleWallView : MetroWindow + { + public SpeckleWallView() + { + InitializeComponent(); + + //TODO: How do we force these assemblies to be resolved properly and loaded properly? + //TODO: We can get an installer and copy these to locations that would enforce this. + // (Konrad) We need to make sure that MaterialDesignThemes and MahApps + // are actually loaded into the context. These DLLs are missing if they are not explicitly + // loaded into the app via using statement. + var unused1 = typeof(FlyoutAssist); + var unused2 = typeof(ThemeManager); + var unused3 = typeof(Hue); + var unused4 = typeof(ShadowAssist); + } + } +} diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs new file mode 100644 index 0000000..a6ff6ed --- /dev/null +++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Autodesk.Revit.DB; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using GalaSoft.MvvmLight.Messaging; +using MahApps.Metro.Controls; +using SpeckleRevitPlugin.UI; + +namespace SpeckleRevitPlugin.Tools.WallTool +{ + public class SpeckleWallViewModel : ViewModelBase + { + #region Parameters + + private SpeckleWallModel Model { get; set; } + public RelayCommand CloseFlyout { get; set; } + public RelayCommand AddInput { get; set; } + public RelayCommand WindowClosed { get; set; } + + private ObservableCollection _inputs; + public ObservableCollection Inputs + { + get { return _inputs; } + set { _inputs = value; RaisePropertyChanged(() => Inputs); } + } + + private ObservableCollection _availableInputs; + public ObservableCollection AvailableInputs + { + get { return _availableInputs; } + set { _availableInputs = value; RaisePropertyChanged(() => AvailableInputs); } + } + + private InputWrapper _selectedInput; + public InputWrapper SelectedInput + { + get { return _selectedInput; } + set { _selectedInput = value; RaisePropertyChanged(() => SelectedInput); } + } + + private bool _addInputChecked; + public bool AddInputChecked + { + get { return _addInputChecked; } + set { _addInputChecked = value; RaisePropertyChanged(() => AddInputChecked); } + } + + #endregion + + public SpeckleWallViewModel(SpeckleWallModel model) + { + Model = model; + AvailableInputs = Model.GetAllAvailableInputs(); + + CloseFlyout = new RelayCommand(OnCloseFlyout); + AddInput = new RelayCommand(OnAddInput); + WindowClosed = new RelayCommand(OnWindowClosed); + + Inputs = new ObservableCollection + { + // (Konrad) Forces the Curve input to only use Speckle Stream as source of data. + new InputViewModel(new InputModel(), new InputWrapper + { + Name = "Curve", + AcceptsLocalData = false, + IsRequired = true + }), + //new InputViewModel(new InputModel(), new InputWrapper + //{ + // Name = "Level", + // AcceptsLocalData = true, + // StorageType = LocalDataType.Element + //}), + //new InputViewModel(new InputModel(), new InputWrapper + //{ + // Name = "Structural", + // AcceptsLocalData = true, + // StorageType = LocalDataType.Boolean + //}) + }; + + Messenger.Default.Register(this, OnInputDeleted); + } + + private void OnWindowClosed(MetroWindow obj) + { + // (Konrad) Removes all Messanger bindings. + Cleanup(); + } + + private void OnInputDeleted(InputDeleted obj) + { + // (Konrad) Restore the input in the dropdown and cleanup + AvailableInputs.Add(obj.InputViewModel.Input); + Inputs.Remove(obj.InputViewModel); + } + + private void OnAddInput() + { + if (SelectedInput == null) return; + + OnCloseFlyout(); + + Inputs.Add(new InputViewModel(new InputModel(), SelectedInput)); + + // (Konrad) Cleanup Available Inputs + AvailableInputs.Remove(SelectedInput); + SelectedInput = null; + } + + private void OnCloseFlyout() + { + AddInputChecked = false; + } + } +} diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml new file mode 100644 index 0000000..3096148 --- /dev/null +++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs new file mode 100644 index 0000000..e0dac50 --- /dev/null +++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace SpeckleRevitPlugin.UI.BooleanSelector +{ + /// + /// Interaction logic for BooleanSelector.xaml + /// + public partial class BooleanSelector : UserControl + { + public BooleanSelector() + { + InitializeComponent(); + } + } +} diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs new file mode 100644 index 0000000..ec7fbaa --- /dev/null +++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpeckleRevitPlugin.UI.BooleanSelector +{ + public class BooleanSelectorModel + { + } +} diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs new file mode 100644 index 0000000..4ce7698 --- /dev/null +++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GalaSoft.MvvmLight; + +namespace SpeckleRevitPlugin.UI.BooleanSelector +{ + public class BooleanSelectorViewModel : ViewModelBase + { + public BooleanSelectorModel Model { get; set; } + + public BooleanSelectorViewModel(BooleanSelectorModel model) + { + Model = model; + } + } +} diff --git a/SpeckleRevitPlugin/UI/Converters.cs b/SpeckleRevitPlugin/UI/Converters.cs new file mode 100644 index 0000000..5b668da --- /dev/null +++ b/SpeckleRevitPlugin/UI/Converters.cs @@ -0,0 +1,53 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using GalaSoft.MvvmLight.Command; + +namespace SpeckleRevitPlugin.UI +{ + /// + /// + /// + public class SelectionChangedToSpeckleStreamConverter : IEventArgsConverter + { + public object Convert(object value, object parameter) + { + var items = ((SelectionChangedEventArgs) value).AddedItems; + return items.Count > 0 ? items[0] : null; + } + } + + /// + /// Inverse of the BooleanToVisibilityConverter. Hides elements when Boolean is True. + /// + public class BooleanToVisibilityInverseConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value != null && (bool)value ? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + /// + /// + /// + public class BooleanInverterConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null || !(bool) value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml new file mode 100644 index 0000000..e7e4881 --- /dev/null +++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs new file mode 100644 index 0000000..aa4055c --- /dev/null +++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs @@ -0,0 +1,13 @@ +namespace SpeckleRevitPlugin.UI.ElementSelector +{ + /// + /// Interaction logic for ElementSelector.xaml + /// + public partial class ElementSelector + { + public ElementSelector() + { + InitializeComponent(); + } + } +} diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs new file mode 100644 index 0000000..005e8c3 --- /dev/null +++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpeckleRevitPlugin.UI.ElementSelector +{ + public class ElementSelectorModel + { + } +} diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs new file mode 100644 index 0000000..d4c81ba --- /dev/null +++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GalaSoft.MvvmLight; + +namespace SpeckleRevitPlugin.UI.ElementSelector +{ + public class ElementSelectorViewModel : ViewModelBase + { + + } +} diff --git a/SpeckleRevitPlugin/UI/InputControl.xaml b/SpeckleRevitPlugin/UI/InputControl.xaml new file mode 100644 index 0000000..a938514 --- /dev/null +++ b/SpeckleRevitPlugin/UI/InputControl.xaml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpeckleRevitPlugin/UI/InputControl.xaml.cs b/SpeckleRevitPlugin/UI/InputControl.xaml.cs new file mode 100644 index 0000000..f250dd2 --- /dev/null +++ b/SpeckleRevitPlugin/UI/InputControl.xaml.cs @@ -0,0 +1,13 @@ +namespace SpeckleRevitPlugin.UI +{ + /// + /// Interaction logic for StreamSelectorView.xaml + /// + public partial class InputControl + { + public InputControl() + { + InitializeComponent(); + } + } +} diff --git a/SpeckleRevitPlugin/UI/InputModel.cs b/SpeckleRevitPlugin/UI/InputModel.cs new file mode 100644 index 0000000..7855b95 --- /dev/null +++ b/SpeckleRevitPlugin/UI/InputModel.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Linq; +using SpeckleCore; +using SpeckleRevitPlugin.Classes; + +namespace SpeckleRevitPlugin.UI +{ + public class InputModel + { + + } +} diff --git a/SpeckleRevitPlugin/UI/InputViewModel.cs b/SpeckleRevitPlugin/UI/InputViewModel.cs new file mode 100644 index 0000000..e785d7d --- /dev/null +++ b/SpeckleRevitPlugin/UI/InputViewModel.cs @@ -0,0 +1,126 @@ +#region Namespaces + +using System; +using Autodesk.Revit.DB; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using GalaSoft.MvvmLight.Messaging; +using SpeckleRevitPlugin.Tools.WallTool; +using SpeckleRevitPlugin.UI.BooleanSelector; +using SpeckleRevitPlugin.UI.ElementSelector; +using SpeckleRevitPlugin.UI.StreamSelector; + +#endregion + +namespace SpeckleRevitPlugin.UI +{ + public class InputViewModel : ViewModelBase + { + #region Properties + + public InputModel Model { get; set; } + public RelayCommand ToggleMenu { get; set; } + public RelayCommand Delete { get; set; } + public RelayCommand ToggleSpeckleInput { get; set; } + + private InputWrapper _input; + public InputWrapper Input + { + get { return _input; } + set { _input = value; RaisePropertyChanged(() => Input); } + } + + private bool _showMenu; + public bool ShowMenu + { + get { return _showMenu; } + set { _showMenu = value; RaisePropertyChanged(() => ShowMenu); } + } + + private bool _showInputs; + public bool ShowInputs + { + get { return _showInputs; } + set { _showInputs = value; RaisePropertyChanged(() => ShowInputs); } + } + + private bool _isSpeckleInput; + public bool IsSpeckleInput + { + get { return _isSpeckleInput; } + set { _isSpeckleInput = value; RaisePropertyChanged(() => IsSpeckleInput); } + } + + private ViewModelBase _selectedDataInput; + public ViewModelBase SelectedDataInput + { + get { return _selectedDataInput; } + set { _selectedDataInput = value; RaisePropertyChanged(() => SelectedDataInput); } + } + + #endregion + + public InputViewModel(InputModel model, InputWrapper input) + { + Model = model; + Input = input; + + ToggleMenu = new RelayCommand(OnToggleMenu); + Delete = new RelayCommand(OnDelete); + ToggleSpeckleInput = new RelayCommand(OnToggleSpeckleInput); + + // (Konrad) Set defaults. In case that input doesn't accept + // locally defined data we need to ensure that Speckle Stream + // input is shown at all times. + if (!Input.AcceptsLocalData) IsSpeckleInput = true; + OnToggleSpeckleInput(IsSpeckleInput); + } + + private void OnDelete() + { + Messenger.Default.Send(new InputDeleted { InputViewModel = this }); + ShowMenu = false; + } + + private void OnToggleMenu() + { + ShowMenu = !ShowMenu; + } + + #region Events + + private void OnToggleSpeckleInput(bool isChecked) + { + if (isChecked) + { + // toggle speckle stream on + SelectedDataInput = new StreamSelectorViewModel(new StreamSelectorModel()); + } + else + { + // toggle local data input + switch (Input.DataType) + { + case LocalDataType.None: + break; + case LocalDataType.Element: + SelectedDataInput = new ElementSelectorViewModel(); + break; + case LocalDataType.Integer: + break; + case LocalDataType.Boolean: + SelectedDataInput = new BooleanSelectorViewModel(new BooleanSelectorModel()); + break; + case LocalDataType.String: + break; + case LocalDataType.Double: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + #endregion + } +} diff --git a/SpeckleRevitPlugin/UI/InputWrapper.cs b/SpeckleRevitPlugin/UI/InputWrapper.cs new file mode 100644 index 0000000..64631e7 --- /dev/null +++ b/SpeckleRevitPlugin/UI/InputWrapper.cs @@ -0,0 +1,90 @@ +using System; +using System.ComponentModel; +using Autodesk.Revit.DB; + +namespace SpeckleRevitPlugin.UI +{ + public sealed class InputWrapper : INotifyPropertyChanged + { + public string Name { get; set; } + public int Id { get; set; } + public bool AcceptsLocalData { get; set; } + public LocalDataType DataType { get; set; } + public bool IsInstance { get; set; } + public bool IsRequired { get; set; } + + public InputWrapper() + { + } + + public InputWrapper(Parameter p, bool isInstance = true, bool isRequired = false) + { + Name = p.Definition.Name; + Id = p.Id.IntegerValue; + IsInstance = isInstance; + IsRequired = isRequired; + DataType = StorageTypeToDataType(p); + + // (Konrad) We would make an assumption that a parameter input + // always accepts a local input: int, bool, element etc. + AcceptsLocalData = true; + } + + public override bool Equals(object obj) + { + var item = obj as InputWrapper; + return item != null && Id.Equals(item.Id); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + #region Methods + + /// + /// + /// + /// + /// + internal static LocalDataType StorageTypeToDataType(Parameter p) + { + switch (p.StorageType) + { + case StorageType.None: + return LocalDataType.None; + case StorageType.Integer: + return p.Definition.ParameterType == ParameterType.YesNo + ? LocalDataType.Boolean + : LocalDataType.Integer; + case StorageType.Double: + return LocalDataType.Double; + case StorageType.String: + return LocalDataType.String; + case StorageType.ElementId: + return LocalDataType.Element; + default: + throw new ArgumentOutOfRangeException(nameof(p.StorageType), p.StorageType, null); + } + } + + #endregion + + public event PropertyChangedEventHandler PropertyChanged; + private void RaisePropertyChanged(string propname) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname)); + } + } + + public enum LocalDataType + { + None, + Element, + Integer, + Boolean, + String, + Double + } +} diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml new file mode 100644 index 0000000..e8a0150 --- /dev/null +++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs new file mode 100644 index 0000000..8191c93 --- /dev/null +++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs @@ -0,0 +1,13 @@ +namespace SpeckleRevitPlugin.UI.StreamSelector +{ + /// + /// Interaction logic for StreamSelector.xaml + /// + public partial class StreamSelector + { + public StreamSelector() + { + InitializeComponent(); + } + } +} diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs new file mode 100644 index 0000000..bd6d74d --- /dev/null +++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SpeckleCore; +using SpeckleRevitPlugin.Classes; + +namespace SpeckleRevitPlugin.UI.StreamSelector +{ + public class StreamSelectorModel + { + /// + /// + /// + /// + public List GetStreams() + { + var streams = FormMainDock.Store.UserClients.Where(x => x.GetRole() == ClientRole.Receiver) + .Cast() + .Select(x => x.Client.Stream) + .OrderBy(x => x.Name) + .ToList(); + return streams.Any() + ? streams + : new List(); + } + + /// + /// + /// + /// + /// + public List GetLayers(SpeckleStream stream) + { + return stream.Layers; + } + } +} diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs new file mode 100644 index 0000000..e81b9ff --- /dev/null +++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs @@ -0,0 +1,72 @@ +#region Namespaces + +using System.Collections.Generic; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using SpeckleCore; + +#endregion + +namespace SpeckleRevitPlugin.UI.StreamSelector +{ + public class StreamSelectorViewModel : ViewModelBase + { + #region Properties + + public StreamSelectorModel Model { get; set; } + public RelayCommand StreamSelected { get; set; } + + private List _streams = new List(); + public List Streams + { + get { return _streams; } + set { _streams = value; RaisePropertyChanged(() => Streams); } + } + + private SpeckleStream _selectedStream; + public SpeckleStream SelectedStream + { + get { return _selectedStream; } + set { _selectedStream = value; RaisePropertyChanged(() => SelectedStream); } + } + + private List _layers = new List(); + public List Layers + { + get { return _layers; } + set { _layers = value; RaisePropertyChanged(() => Layers); } + } + + private Layer _selectedLayer; + public Layer SelectedLayer + { + get { return _selectedLayer; } + set { _selectedLayer = value; RaisePropertyChanged(() => SelectedLayer); } + } + + #endregion + + public StreamSelectorViewModel(StreamSelectorModel model) + { + Model = model; + Streams = Model.GetStreams(); + StreamSelected = new RelayCommand(OnStreamSelected); + } + + #region Event Handlers + + /// + /// + /// + /// + private void OnStreamSelected(SpeckleStream stream) + { + // (Konrad) It's possible for this to be null since we can hide the comboboxes + if (stream == null) return; + + Layers = Model.GetLayers(stream); + } + + #endregion + } +} diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs index 92b91ad..302808e 100644 --- a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs +++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs @@ -99,7 +99,7 @@ public void InitializeChromium() private void Form_MainDock_OnLoaded(object sender, RoutedEventArgs e) { #if DEBUG - Browser.ShowDevTools(); + //Browser.ShowDevTools(); #endif }