こんにちは。開発プロジェクトを率いるチーフアーキテクトの私だ。
これまで数多くの大規模CAD自動化プロジェクトを見てきたが、現場から最も多く寄せられる悲鳴の一つが、「サブアセンブリの動的挿入と合致(Mate)の自動化でプログラムが破綻する」というトラブルだ。
単体部品(Part)の挿入であれば、`AddComponents`メソッドを呼んで適当に合致をつければ動く。しかし、すでに独自の内部階層を持つ「サブアセンブリ」をメインアセンブリに組み込もうとした瞬間、多くのプログラマが絶望的なバグに直面する。
「下位構成部品の参照が取れない」「合致の方向が突然反転する」「コンポーネントのインスタンス名が衝突してエラーになる」――これらはすべて、SolidWorks APIのライフサイクルと、アセンブリの階層構造(Component2ツリー)に対する理解不足が原因だ。
今回は、実務の現場で即座に通用する、堅牢で保守性の高いサブアセンブリ動的挿入と合致定義の極意を授けよう。
—
なぜ「素朴なコード」は実務で破綻するのか?
多くの解説書やネットの記事では、アセンブリにコンポーネントを挿入する際、以下のような安易なコードが紹介されている。
‘ 【悪例】実務で使ってはならないフラット思考のコード
Dim swComp As ModelDoc2
Set swComp = swApp.OpenDoc6(“SubAssembly.sldasm”, …)
swAssembly.AddComponent5 “SubAssembly.sldasm”, …
‘ その後、直感的に面を取得して合致させる
このアプローチがなぜ非効率で危険なのか。理由は3つある。
1. ファイルパスとコンポーネント名の混同: `AddComponent5`は「ファイル」を読み込んでインスタンス(コンポーネント)を生成する。同一のサブアセンブリを2つ以上配置した場合、名前の衝突やポインタの取り違えが起きる。
2. 階層の無視: サブアセンブリ内部の部品(Leaf Component)にアクセスするには、ルートから一気に下るのではなく、`Component2`オブジェクトのツリー構造を正しく辿る必要がある。
3. エラーハンドリングの欠如: ファイルの存在確認や、コンポーネント追加直後の再構築(Rebuild)漏れにより、合致定義時に「面が見つからない(NULLポインタ例外)」でVBAがクラッシュする。
プロのエンジニアであれば、「どのインスタンスの、どのコンポーネントの、どのエンティティ(面や線)か」を完全に制御下に置く設計にしなければならない。
—
堅牢なサブアセンブリ挿入のアーキテクチャ設計
実務で破綻しない自動化ツールを作るための鉄則は以下の通りだ。
- 完全修飾パスの利用: 読み込むファイルは必ず絶対パスで指定し、ワーキングディレクトリの迷子を防ぐ。
- 追加直後のポインタ取得: `AddComponent5`の戻り値、またはアセンブリの最新コンポーネントから確実に`Component2`オブジェクトを取得する。
- コンテキストの明確化: サブアセンブリ内部の構成部品を操作する場合、ルートアセンブリ側から見たパス(ComponentPath)を意識する。
—
【プロダクションコード】サブアセンブリ動的挿入&合致定義マクロ
それでは、実務の現場でそのまま組み込める完全版のVBAコードを公開する。
このコードは、メインアセンブリに対し、指定したサブアセンブリを動的に配置し、基準平面同士を一致(Coincident)させるロジックを含んでいる。
‘ ==============================================================================
‘ ódulo名: modSubAssemblyAssembler
‘ 概要 : サブアセンブリの動的挿入と階層を考慮した合致定義を行うプロダクションコード
‘ 依存 : SolidWorks 202X以降
‘ ==============================================================================
Option Explicit
Sub Main()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swAssDoc As SldWorks.AssemblyDoc
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
‘ 1. アクティブドキュメントがアセンブリか検証
If swModel Is Nothing Then
MsgBox “ドキュメントが開かれていません。”, vbCritical
Exit Sub
End If
If swModel.GetType <> swDocASSEMBLY Then
MsgBox “アクティブなドキュメントはアセンブリではありません。”, vbCritical
Exit Sub
End If
Set swAssDoc = swModel
‘ — 設定値(実務ではデータベースやINIファイルから読み込むこと) —
Dim subAssemblyPath As String
subAssemblyPath = “C:\MyData\Assemblies\SubUnit_A.sldasm”
‘ 配置座標(X, Y, Z)
Dim posX As Double, posY As Double, posZ As Double
posX = 0.1: posY = 0.0: posZ = 0.0
‘ 2. サブアセンブリの挿入
Dim swInsertedComp As SldWorks.Component2
Set swInsertedComp = InsertSubAssembly(swAssDoc, subAssemblyPath, posX, posY, posZ)
If swInsertedComp Is Nothing Then
MsgBox “サブアセンブリの挿入に失敗しました。”, vbCritical
Exit Sub
End If
‘ 3. 階層構造を意識した合致(Mate)の定義例
‘ メインアセンブリの「前面」と、サブアセンブリ側の特定の平面を一致させる
Dim result As Boolean
result = DefineMates(swModel, swAssDoc, swInsertedComp)
If result Then
MsgBox “サブアセンブリの挿入と合致定義が正常に完了しました。”, vbInformation
Else
MsgBox “合致の定義中にエラーが発生しました。”, vbExclamation
End If
End Sub
‘ ——————————————————————————
‘ 関数名: InsertSubAssembly
‘ 概要 : 指定したパスのサブアセンブリをアセンブリ内に配置する
‘ ——————————————————————————
Private Function InsertSubAssembly(swAssDoc As SldWorks.AssemblyDoc, filePath As String, x As Double, y As Double, z As Double) As SldWorks.Component2
Dim swComp As SldWorks.Component2
Dim transform(15) As Double
‘ ファイル存在チェック
If Dir(filePath) = “” Then
MsgBox “ファイルが見つかりません: ” & filePath, vbCritical
Set InsertSubAssembly = Nothing
Exit Function
End If
‘ 単位変換(ミリメートルの場合は内部単位系(メートル)への換算が必要な場合があるが、AddComponent5は座標指定に注意)
‘ ここでは簡易的にトランスフォーム行列の平行移動成分(12, 13, 14番目)を設定
transform(0) = 1: transform(1) = 0: transform(2) = 0
transform(3) = 0: transform(4) = 1: transform(5) = 0
transform(6) = 0: transform(7) = 0: transform(8) = 1
transform(9) = 0: transform(10) = 0: transform(11) = 0
transform(12) = x: transform(13) = y: transform(14) = z
transform(15) = 1
‘ コンポーネントの追加
‘ 引数: FileName, Option, X, Y, Z, Config, X, Y, Z (回転等)
Set swComp = swAssDoc.AddComponent5(filePath, 0, “”, False, “”, 0, 0, 0)
If Not swComp Is Nothing Then
‘ 位置と向きを適用
swComp.SetTransformAndOverridePersistTransform swApp.MathUtility.CreateTransform(transform)
‘ アセンブリの強制再構築
swAssDoc.EditRebuild
End If
Set InsertSubAssembly = swComp
End Function
‘ ——————————————————————————
‘ 関数名: DefineMates
‘ 概要 : 挿入したサブアセンブリとメイン側のエンティティで合致を作成する
‘ ——————————————————————————
Private Function DefineMates(swModel As SldWorks.ModelDoc2, swAssDoc As SldWorks.AssemblyDoc, swTargetComp As SldWorks.Component2) As Boolean
Dim swMateRet As Long
‘ 選択クリア
swModel.ClearSelection2 True
‘ 【重要】メインアセンブリ側の基準面を選択
‘ ここでは例として「Front Plane(前面)」を選択状態にする
Dim bRet As Boolean
bRet = swModel.Extension.SelectByID2(“前面”, “PLANE”, 0, 0, 0, True, 1, Nothing, 0)
If Not bRet Then
DefineMates = False
Exit Function
End If
‘ 【重要】サブアセンブリ側の面、またはサブアセンブリ内構成部品の面を選択
‘ サブアセンブリ自体のルート平面、または下位コンポーネントの面を指定する
‘ ここではサブアセンブリ内の特定部品の面をコンポーネントパス付きで選択する例
‘ 例: “SubUnit_A-1/Part1-1” の “Face@Part1”
Dim targetEntityName As String
targetEntityName = swTargetComp.Name2 & “/BasePlate-1@<1>” ‘ 実際の構成に合わせて書き換えてください
bRet = swModel.Extension.SelectByID2(targetEntityName, “FACE”, 0, 0, 0, True, 1, Nothing, 0)
If Not bRet Then
‘ フォールバックとしてサブアセンブリ自体の原点や平面を指定する場合の処理をここに記述
swModel.ClearSelection2 True
DefineMates = False
Exit Function
End If
‘ 一致(Coincident)合致の追加
‘ AddMate5(MateType, MateAlignment, Flip, Distance, DistanceUpperBound, DistanceLowerBound, …)
Dim swMate As SldWorks.Mate2
Set swMate = swAssDoc.AddMate5(swMateType_Coincident, swMateAlign_CLOSEST, False, 0, 0, 0, 0, 0, 0, 0, 0, False, swMateError_NoError, swMateRet)
If swMateRet = 0 Then
DefineMates = True
Else
DefineMates = False
End If
swModel.ClearSelection2 True
swModel.EditRebuild
End Function
—
チーフアーキテクトからの実務アドバイス:保守性とデータベース連携
このコードを実際の業務システム(PDM連携や、Excelからパラメータを読み込んで自動生成するジェネレータ)に組み込む際は、以下の2点を必ず守ってほしい。
1. コンポーネント名のハードコーディング厳禁
プロダクション環境では、サブアセンブリ内の部品名(例: `BasePlate-1` など)が設計変更によってリネームされるリスクがある。面名や部品名で選択するのではなく、カスタムプロパティや特定IDをキーにしてAPI経由でトラバーサル(走査)し、動的にエンティティを取得する設計に昇華させるべきだ。
2. トランザクション的思考(ロールバックの担保)
巨大なアセンブリの自動構築中にエラーが発生した場合、中途半端にコンポーネントが配置された壊れたアセンブリが残されることになる。ファイルを開く際は変更を破棄できるようにし、エラー時は適切にセッションをクローズする防衛的プログラミングを徹底してほしい。
基盤の設計が正しければ、SolidWorks VBAはあなたの最強の武器となる。泥臭いコードから脱却し、エレガントで拡張性の高い自動化システムを構築してくれ。健闘を祈る。
