RevitアドインでDynamoを実行する

# はじめに
この記事では、Revit APIを使用してDynamoスクリプトを実行する方法を紹介します。

:::note
本記事は一部ChatGPTを使用して執筆しています。
本記事では Visual Studio 2022 および Revit 2024 を使用しています。
:::


# 実装
### 参照の追加
NuGetパッケージ "DynamoVisualProgramming.Core" と "DynamoVisualProgramming.Revit" をプロジェクトに追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2059946/3aea6688-58fd-155b-d2bf-89fb2ddc10f0.png)

### 名前空間の追加
まず必要な名前空間をインポートします。
```c#
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Windows;
using System.Collections.Generic;
using Dynamo.Applications;
```

### コマンド作成
次に、TestCommand クラスを定義し、IExternalCommand インターフェースを実装します。これにより、Revitの外部コマンドとして機能します。
```c#
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class TestCommand : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        try
        {
            // ここにDynamoを実行する処理を書く
        }

        catch (Exception ex)
        {
            TaskDialog.Show("ERROR", ex.Message);
            return Result.Failed;
        }
    }
}
```

### ファイルパス取得
Dynamoスクリプトのファイルパスをユーザーに選択させます。ファイルオープンダイアログを使用してファイルパスを取得します。ダイアログの詳細は以下の記事を確認してください。

https://qiita.com/Z0E/items/6981045167284490554b#-revitui

```c#
var dialog = new FileOpenDialog("ファイルを選択してください (*.dyn)|*.dyn");
var result = dialog.Show();
var path = ModelPathUtils.ConvertModelPathToUserVisiblePath(dialog.GetSelectedModelPath());
```

### ジャーナルデータの設定
Dynamoを非表示で自動実行するためのジャーナルデータを設定します。
```c#
var journalData = new Dictionary<string, string>
{
    { JournalKeys.ShowUiKey, false.ToString()},
    { JournalKeys.AutomationModeKey, true.ToString()},
    { JournalKeys.DynPathKey, ""},
    { JournalKeys.DynPathExecuteKey, true.ToString()},
    { JournalKeys.ForceManualRunKey, false.ToString()},
    { JournalKeys.ModelShutDownKey, true.ToString()},
    { JournalKeys.ModelNodesInfo, false.ToString()}
};
```

### 実行
最後に、DynamoRevit クラスのインスタンスを作成し、コマンドデータを渡してDynamoスクリプトを実行します。
```c#
var dynamoCommandData = new DynamoRevitCommandData()
{
    Application = commandData.Application,
    JournalData = journalData
};

var dynamo = new DynamoRevit();
dynamo.ExecuteCommand(dynamoCommandData);
DynamoRevit.RevitDynamoModel.OpenFileFromPath(path, true);
DynamoRevit.RevitDynamoModel.ForceRun();
```

### コード全体
```c#
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Windows;
using System.Collections.Generic;
using Dynamo.Applications;

[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class TestCommand : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        try
        {
            // ファイルパス取得
            var dialog = new FileOpenDialog("ファイルを選択してください (*.dyn)|*.dyn");
            var result = dialog.Show();
            var path = ModelPathUtils.ConvertModelPathToUserVisiblePath(dialog.GetSelectedModelPath());
            
            // ジャーナルデータの設定
            var journalData = new Dictionary<string, string>
            {
                { JournalKeys.ShowUiKey, false.ToString()},
                { JournalKeys.AutomationModeKey, true.ToString()},
                { JournalKeys.DynPathKey, ""},
                { JournalKeys.DynPathExecuteKey, true.ToString()},
                { JournalKeys.ForceManualRunKey, false.ToString()},
                { JournalKeys.ModelShutDownKey, true.ToString()},
                { JournalKeys.ModelNodesInfo, false.ToString()}
            };
            
            // 実行
            var dynamoCommandData = new DynamoRevitCommandData()
            {
                Application = commandData.Application,
                JournalData = journalData
            };
            
            var dynamo = new DynamoRevit();
            dynamo.ExecuteCommand(dynamoCommandData);
            DynamoRevit.RevitDynamoModel.OpenFileFromPath(path, true);
            DynamoRevit.RevitDynamoModel.ForceRun();
        }

        catch (Exception ex)
        {
            TaskDialog.Show("ERROR", ex.Message);
            return Result.Failed;
        }
    }
}
```


# テスト
”Dynamoが実行されました” というダイアログを表示するDynamoを用意して、これをアドインから実行してみます。
![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2059946/43c7edf0-b3d8-0870-26df-fdca4c849473.gif)


# 補足
プロジェクトにNuGetパッケージを追加した際、csprojファイルの `PackageReference` に `ExcludeAssets` タグを記述することで、ビルド時にdllファイルがローカルへコピーされないよう設定できます。
詳細は以下の公式ドキュメントを確認してください。

https://learn.microsoft.com/ja-jp/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets

```diff_xml:csproj
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  
  <!-- その他の設定 -->
  
  <ItemGroup>
    <PackageReference Include="DynamoVisualProgramming.Core">
      <Version>2.17.0.3493</Version>
+     <ExcludeAssets>runtime</ExcludeAssets>
    </PackageReference>
    <PackageReference Include="DynamoVisualProgramming.Revit">
      <Version>2.17.0.7404</Version>
+     <ExcludeAssets>runtime</ExcludeAssets>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
```


# 参考リンク
https://forum.dynamobim.com/t/run-a-dyn-from-a-revit-add-in/14701

https://forum.dynamobim.com/t/open-dynamo-in-background-while-c-add-in-executes/19535

https://forum.dynamobim.com/t/execute-dynamo-script-from-revit-api/60529