Skip to content

Commit

Permalink
Update DesignApplication to fix PackageContents and ActiveAddInId
Browse files Browse the repository at this point in the history
  • Loading branch information
ricaun committed Jan 20, 2025
1 parent 6e65d74 commit 4505dd9
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [1.0.0] / 2025-01-20
### Features
- Initial release.
### DesignApplication
- Add `DesignApplicationLoader` to fix [Design Automation for Revit ignores PackageContents.xml configuration.](https://github.com/ricaun-io/RevitAddin.DA.Tester/issues/7)
- Add `ExternalServer` to fix [Design Automation for Revit ActiveAddInId is null](https://github.com/ricaun-io/RevitAddin.DA.Tester/issues/9)
### Application
- Sample to execute `ricaun.Revit.DA` library.
### Tests
- Test `ricaun.Revit.DA` library.
- Update `TestCase` with expected results. (`Reference`, `TargetFramework` and `ActiveAddInId`)

[vNext]: ../../compare/1.0.0...HEAD
[1.0.0]: ../../compare/1.0.0
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>1.0.0-alpha</Version>
<Version>1.0.0-beta</Version>
</PropertyGroup>
</Project>
6 changes: 1 addition & 5 deletions ricaun.Revit.DA.Example/Revit/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ public class App : DesignApplication
{
public override bool Execute(Application application, string filePath, Document document)
{
Console.WriteLine($"Execute: \t{application}");
Console.WriteLine($"ActiveAddInId: \t{application.ActiveAddInId?.GetAddInName()}");

var output = RevitOutputModel.Create(application);
output.Save();

return true;
}

public override void OnStartup()
{
Console.WriteLine($"ActiveAddInId: \t{ControlledApplication.ActiveAddInId?.GetAddInName()}");

}

public override void OnShutdown()
Expand Down
14 changes: 8 additions & 6 deletions ricaun.Revit.DA.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ namespace ricaun.Revit.DA.Tests
{
public class Tests : BundleFileTests
{
[TestCase("2024", "4.7", "19", null)]
[TestCase("2025", "4.7", "19", null)]
//[TestCase("2024", "4.7", "19", null)]
//[TestCase("2025", "4.7", "19", null)]
[TestCase("2024", "4.8", "21", "Example")]
[TestCase("2025", "8.0", "25", "Example")]
public async Task ExecuteDesignAutomation(string engine, string frameworkNameContain, string referenceContain, string addInNameContain)
{
var bundlePaths = GetBundles();
Expand All @@ -22,12 +24,12 @@ public async Task ExecuteDesignAutomation(string engine, string frameworkNameCon

Console.WriteLine(output.ToJson());

Assert.IsTrue(output.VersionName.Contains(engine), $"VersionName contains engine {engine}");
Assert.IsTrue(output.FrameworkName.Contains(frameworkNameContain), $"FrameworkName contains framework {frameworkNameContain}");
Assert.IsTrue(output.Reference.Contains(referenceContain), $"Reference contains reference {referenceContain}");
Assert.IsTrue(output.VersionName.Contains(engine), $"VersionName {output.VersionName} not contains engine {engine}");
Assert.IsTrue(output.FrameworkName.Contains(frameworkNameContain), $"FrameworkName {output.FrameworkName} not contains framework {frameworkNameContain}");
Assert.IsTrue(output.Reference.Contains(referenceContain), $"Reference {output.Reference} not contains reference {referenceContain}");

if (output.AddInName is not null)
Assert.IsTrue(output.AddInName.Contains(addInNameContain), $"AddinName contains addin {addInNameContain}");
Assert.IsTrue(output.AddInName.Contains(addInNameContain), $"AddinName {output.AddInName} not contains addin {addInNameContain}");
else
Assert.AreEqual(addInNameContain, output.AddInName);
}
Expand Down
63 changes: 58 additions & 5 deletions ricaun.Revit.DA/DesignApplication.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,50 @@
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using DesignAutomationFramework;
using ricaun.Revit.DA.ExternalServer;
using ricaun.Revit.DA.Loader;
using System;

namespace ricaun.Revit.DA
{
public abstract class DesignApplication : IExternalDBApplication, IDesignAutomation
{
/// <summary>
/// Use ExternalService to execute the IDesignAutomation.Execute, this make the Execute run in the AddIn context.
/// </summary>
public virtual bool UseExternalService => true;
public ControlledApplication ControlledApplication { get; private set; }
public abstract void OnStartup();
public abstract void OnShutdown();
public abstract bool Execute(Application application, string filePath, Document document);

private IExternalDBApplication designApplication;
private DesignAutomationSingleExternalServer externalServer;
public ExternalDBApplicationResult OnStartup(ControlledApplication application)
{
ControlledApplication = application;

designApplication = DesignApplicationLoader.LoadVersion(this);

if (designApplication is IExternalDBApplication)
{
return designApplication.OnStartup(application);
}

Console.WriteLine("----------------------------------------");
Console.WriteLine($"FullName: \t{GetType().Assembly.FullName}");
Console.WriteLine($"AddInName: \t{ControlledApplication.ActiveAddInId?.GetAddInName()}");
Console.WriteLine("----------------------------------------");

try
{
externalServer = new DesignAutomationSingleExternalServer(this).Register();
}
catch (Exception ex)
{
Console.WriteLine($"DesignAutomationSingleExternalServer: \t{ex.Message}");
}

OnStartup();
DesignAutomationBridge.DesignAutomationReadyEvent += DesignAutomationReadyEvent;

Expand All @@ -22,24 +54,45 @@ public ExternalDBApplicationResult OnStartup(ControlledApplication application)
public ExternalDBApplicationResult OnShutdown(ControlledApplication application)
{
ControlledApplication = application;

if (designApplication is IExternalDBApplication)
{
try
{
return designApplication.OnShutdown(application);
}
finally
{
DesignApplicationLoader.Dispose();
}
}

externalServer?.RemoveServer();

OnShutdown();
DesignAutomationBridge.DesignAutomationReadyEvent -= DesignAutomationReadyEvent;

return ExternalDBApplicationResult.Succeeded;
}


private void DesignAutomationReadyEvent(object sender, DesignAutomationReadyEventArgs e)
{
DesignAutomationBridge.DesignAutomationReadyEvent -= DesignAutomationReadyEvent;

var data = e.DesignAutomationData;

Console.WriteLine("--------------------------------------------------");
Console.WriteLine($"RevitApp: {data.RevitApp} \tFilePath: {data.FilePath} \tRevitDoc: {data.RevitDoc} \tAddInName:{data.RevitApp.ActiveAddInId?.GetAddInName()}");
Console.WriteLine("--------------------------------------------------");

if (externalServer is not null && UseExternalService)
{
e.Succeeded = externalServer.ExecuteService(data.RevitApp, data.FilePath, data.RevitDoc);
return;
}

e.Succeeded = Execute(data.RevitApp, data.FilePath, data.RevitDoc);
}
}

public interface IDesignAutomation
{
bool Execute(Application application, string filePath, Document document);
}
}
13 changes: 13 additions & 0 deletions ricaun.Revit.DA/ExternalServer/DesignAutomationExternalData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;

namespace ricaun.Revit.DA.ExternalServer
{
public class DesignAutomationExternalData : IExternalData
{
public Application Application { get; set; }
public string FilePath { get; set; }
public Document Document { get; set; }
}
}
123 changes: 123 additions & 0 deletions ricaun.Revit.DA/ExternalServer/DesignAutomationSingleExternalServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;
using System;
using System.Collections.Generic;

namespace ricaun.Revit.DA.ExternalServer
{
/// <summary>
/// DesignAutomationSingleExternalServer is a single server with a default single service.
/// </summary>
/// <remarks>
/// This external server is used to execute IDesignAutomation when DesignAutomationReadyEvent is triggered.
/// Because the external server is registered before the Revit finish initialize the executed service run in the same ActiveAddIn when the external service is registered.
/// Fix the issue that DesignAutomationReadyEvent triggers without the ActiveAddIn context.
/// </remarks>
public class DesignAutomationSingleExternalServer : ISingleServerService, IDesignAutomationExternalServer
{
private readonly IDesignAutomation designAutomation;
public DesignAutomationSingleExternalServer(IDesignAutomation designAutomation)
{
this.designAutomation = designAutomation;
}
public ExternalServiceId ServiceId { get; } = new ExternalServiceId(Guid.NewGuid());
public Guid ServerId { get; } = Guid.NewGuid();

#region ExecuteService

public bool ExecuteService(Application application, string filePath, Document document)
{
var externalData = new DesignAutomationExternalData()
{
Application = application,
FilePath = filePath,
Document = document,
};
return ExecuteService(externalData);
}

public bool ExecuteService(DesignAutomationExternalData externalData)
{
var service = ExternalServiceRegistry.GetService(ServiceId) as SingleServerService;
var result = ExternalServiceRegistry.ExecuteService(service.GetPublicAccessKey(), externalData);
Console.WriteLine($"ExecuteService: \t{result} \t{externalData}");
return result == ExternalServiceResult.Succeeded;
}

#endregion

#region Register/Unregister
public DesignAutomationSingleExternalServer Register()
{
var options = new ExternalServiceOptions()
{
IsPublic = true,
};
var privateKeyExecute = ExternalServiceRegistry.RegisterService(this, options);
return this.AddServer();
}
public DesignAutomationSingleExternalServer AddServer(IDesignAutomationExternalServer designAutomationExternalServer = null)
{
designAutomationExternalServer ??= this;
var service = ExternalServiceRegistry.GetService(ServiceId) as SingleServerService;
if (!service.IsRegisteredServerId(designAutomationExternalServer.GetServerId()))
{
service.AddServer(designAutomationExternalServer);
service.SetActiveServer(designAutomationExternalServer.GetServerId());
}
return this;
}
public DesignAutomationSingleExternalServer RemoveServer()
{
var service = ExternalServiceRegistry.GetService(ServiceId) as SingleServerService;
foreach (var guid in service.GetRegisteredServerIds())
{
service.RemoveServer(guid);
}
return this;
}
#endregion

#region ISingleServerService
public bool Execute(IExternalServer server, Document document, IExternalData data)
{
if (server is IDesignAutomationExternalServer designAutomationExternalServer)
{
return designAutomationExternalServer.Execute(data as DesignAutomationExternalData);
}
return false;
}
public bool IsValidServer(IExternalServer server)
{
return server is IDesignAutomationExternalServer;
}
#endregion

#region IDesignAutomationExternalServer
public bool Execute(DesignAutomationExternalData data)
{
return designAutomation.Execute(data.Application, data.FilePath, data.Document);
}

public Guid GetServerId() => ServerId;
#endregion

#region IExternalService
public string GetDescription() => GetType().Name;
public string GetName() => GetType().Name;
public ExternalServiceId GetServiceId() => ServiceId;
public string GetVendorId() => "ricaun";

/// <summary>
/// This method may only be invoked in a recordable service. Services registered as non-recordable never receive this call.
/// </summary>
public void OnServersChanged(Document document, ServerChangeCause cause, IList<Guid> oldServers) { }

/// <summary>
/// This method may only be invoked in a recordable service. Services registered as non-recordable never receive this call.
/// </summary>
public DisparityResponse OnServersDisparity(Document document, IList<Guid> oldServers) => DisparityResponse.ApplyDefaults;
#endregion
}
}
10 changes: 10 additions & 0 deletions ricaun.Revit.DA/ExternalServer/IDesignAutomationExternalServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Autodesk.Revit.DB.ExternalService;
using ricaun.Revit.DA.ExternalServer;

namespace ricaun.Revit.DA.ExternalServer
{
public interface IDesignAutomationExternalServer : IExternalServer
{
public bool Execute(DesignAutomationExternalData data);
}
}
10 changes: 10 additions & 0 deletions ricaun.Revit.DA/IDesignAutomation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;

namespace ricaun.Revit.DA
{
public interface IDesignAutomation
{
bool Execute(Application application, string filePath, Document document);
}
}
Loading

0 comments on commit 4505dd9

Please sign in to comment.