BOM制御の覇権:図面自動化における「決定論的」なテーブル操作の極意
図面自動化の領域において、多くのエンジニアが「とりあえずBOM(部品表)が表示される」レベルで満足し、その先にある「制御」の壁に突き当たって挫折する。
アセンブリ図面におけるBOMは、単なるデータの羅列ではない。それは設計意図、製造要件、そしてサプライチェーンへの指示書が凝縮された、図面内における最も動的かつ重要な「情報構造体」である。列の並び順が一つ違うだけで、製造現場の誤認を招き、システム間連携におけるデータ整合性を破壊する。
本稿では、単なる「BOMの挿入」といった初歩的な解説は一切排除する。シニアエンジニアが直面する、「テンプレートの変更に耐えうる堅牢な列制御」、「メモリリークを許さないオブジェクトのライフサイクル管理」、そして「描画更新コストを最小化するパフォーマンス最適化」に焦点を当て、実務における極限の知見を伝授する。
1. オブジェクト・ライフサイクルの厳格な管理
SolidWorks VBAにおいて、最も忌避すべきは「不完全なオブジェクト解放」である。特に、図面(Drawing)とアセンブリ(Assembly)を跨ぐ操作を行う際、参照カウントが適切に管理されていないと、SolidWorksのプロセスがメモリ上に澱(おり)のように残り、大量の図面をバッチ処理する際に不可避なクラッシュを引き起こす。
`Set swBomTable = Nothing` を書くのは当然だが、真に重要なのは、「どの階層のオブジェクトを、どのタイミングで解放するか」という設計思想だ。
' 破滅的なメモリリークを防ぐための基本原則
' 1. 階層の深いオブジェクトから順に、明示的にNothingを代入する
' 2. Error Handler内で必ず解放処理を通す
' 3. ループ内でのオブジェクト生成は最小限に留める
2. 決定論的アプローチ:インデックス依存からの脱却
初級者が書くコードは、`Column(1)`、`Column(2)` のように列インデックスを直接指定する。これは、設計者がテンプレートの列を一つ追加した瞬間に、システム全体を崩壊させる「時限爆弾」となる。
プロフェッショナルが採用すべきは、「プロパティ名(またはヘッダー文字列)による探索」である。BOMの各列が保持するプロパティ名を走査し、目的の列を特定するロジックを組み込むことで、テンプレートの変更に対して自己修復的な堅牢性を確保する。
3. 実装:BOM完全制御エンジン
以下に、実務レベルで耐えうるBOM制御のコア・モジュールを示す。このコードは、BOMの挿入、特定のカスタムプロパティに基づく列の再配置、不要な列の非表示化、および列幅の最適化を一気通貫で行う。
Option Explicit
''' <summary>
''' 図面内のBOMを高度に制御するメインルーチン
''' </summary>
Public Sub ExecuteAdvancedBomControl()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swSheet As SldWorks.Sheet
Dim swView As SldWorks.View
Dim swBomTable As SldWorks.BomTableAnnotation
Dim swTable As SldWorks.TableAnnotation
On Error GoTo ErrorHandler
' 1. SolidWorksへの接続とドキュメントの検証
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then Exit Sub
If swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いてください。", vbCritical
Exit Sub
End If
Set swDraw = swModel
Set swSheet = swDraw.GetCurrentSheet
' 2. 描画更新の停止(パフォーマンス最適化の要)
' 大規模アセンブリでは、描画更新を抑制することで処理速度が数倍変わる
swModel.LockGraphics = True
' 3. BOMの挿入(既存のBOMがある場合は取得、なければ挿入)
' ここでは簡略化のため、最初のビューにBOMを挿入するロジックとする
Set swView = swDraw.GetFirstView ' Sheet自体が最初のビュー
Set swView = swView.GetNextView ' 最初のモデルビュー
If swView Is Nothing Then GoTo CleanUp
' BOMの挿入 (InsertBomTable3)
' 引数は構成、配置、配置形式など。実務ではテンプレート指定が肝
Dim bomConfig As String
bomConfig = swModel.GetConfigurationNames(0) ' アクティブな構成を使用
' BOM挿入の試行
Set swBomTable = swView.InsertBomTable3(swBomConfigurationOptions_e.swBomConfigurationAll, _
bomConfig, 0, 0, swTableAnnotation_e.swTableAnnotation_Right, False)
If swBomTable Is Nothing Then
' 既にBOMが存在する場合の取得ロジック
Set swBomTable = GetExistingBom(swView)
End If
If Not swBomTable Is Nothing Then
Set swTable = swBomTable
' 4. 列の完全制御ロジック
ControlBomColumns swTable
End If
CleanUp:
' 5. リソースの解放と状態の復元
swModel.LockGraphics = False
swModel.ForceRebuild3 True
' オブジェクトの明示的解放(重要)
Set swBomTable = Nothing
Set swTable = Nothing
Set swView = Nothing
Set swSheet = Nothing
Set swDraw = Nothing
Set swModel = Nothing
Set swApp = Nothing
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
Resume CleanUp
End Sub
''' <summary>
''' プロパティ名に基づき、列の順序・表示・幅を制御する
''' </summary>
Private Sub ControlBomColumns(swTable As SldWorks.TableAnnotation)
Dim i As Integer
Dim colCount As Integer
Dim colPropName As String
Dim targetProp As String
Dim targetOrder As Variant
Dim currentOrderIdx As Integer
colCount = swTable.ColumnCount
' --- 設定セクション: ここをマスターデータから読み込む設計にすべき ---
' 表示したいプロパティの順序を定義
targetOrder = Array("PartNo", "Description", "Quantity", "Material")
' ------------------------------------------------------------------
' A. 不要な列の非表示化と列幅の初期化
For i = 0 To colCount - 1
' 列のプロパティ名を取得(ヘッダーではなく、内部的なデータソース名)
' 注: APIの仕様により、ヘッダー文字列で判定する必要がある場合もある
colPropName = swTable.GetColumnName(i)
' 定義されたリストにないプロパティは非表示にする
If Not IsInArray(colPropName, targetOrder) Then
swTable.SetColumnVisible i, False
Else
swTable.SetColumnVisible i, True
' 列幅の最適化(実務では固定値、または文字数に基づく計算を行う)
swTable.SetColumnWidth i, 3000 ' 0.001m単位
End If
Next i
' B. 列の並び替え(再配置)
' 注意: SolidWorks APIのTableAnnotationには直接的な「MoveColumn」がない場合が多い。
' その場合、列のデータのコピーと削除、あるいはテンプレート側での制御が推奨されるが、
' ここでは「目的の列を特定して制御する」という論理構造を示す。
' 実際の実務では、テンプレートの列順を固定し、コードからは「表示/非表示」のみを制御するのが最も安全である。
End Sub
''' <summary>
''' 配列内に値が存在するか判定するヘルパー関数
''' </summary>
Private Function IsInArray(val As String, arr As Variant) As Boolean
Dim i As Long
For i = LBound(arr) To UBound(arr)
If arr(i) = val Then
IsInArray = True
Exit Function
End If
Next i
IsInArray = False
End Function
''' <summary>
''' 既存のBOMテーブルを探索する
''' </summary>
Private Function GetExistingBom(swView As SldWorks.View) As SldWorks.BomTableAnnotation
Dim swAnn As SldWorks.Annotation
Dim swTableAnn As SldWorks.TableAnnotation
Set swAnn = swView.GetFirstAnnotation2
Do While Not swAnn Is Nothing
If swAnn.GetType = swAnnotationType_e.swTableAnnotation Then
Set swTableAnn = swAnn
Set GetExistingBom = swTableAnn.GetBomTable
End If
Set swAnn = swAnn.GetNext
Loop
End Function
4. アーキテクトの視点:保守性と拡張性
このコードを実務のシステムに組み込む際、以下の3点を考慮せよ。
4.1. テンプレート依存性の排除
コード内にプロパティ名(`”PartNo”`, `”Material”` 等)をハードコードするのは、小規模なスクリプトでは許容されるが、エンタープライズ環境では罪悪である。これらは必ず外部のCSV、Excel、あるいはシステムのデータベースから取得するように設計せよ。
4.2. エラーハンドリングの階層化
`On Error GoTo ErrorHandler` は最低限の作法だ。真のプロフェッショナルは、ネットワーク切断、ファイルロック、メモリ不足といった「予期せぬ事態」を想定し、エラーが発生した際にも `swModel.LockGraphics = False` を確実に実行して、SolidWorksのユーザーインターフェースを「使用不能状態」にさせない設計を行う。
4.3. Windows APIとの連携(極限の領域)
もし、図面生成中にファイル共有サーバーのネットワーク遅延がボトルネックになる場合は、VBAの限界を超え、Windows API (`kernel32.dll`) を用いたファイルロックの確認や、プロセス優先度の制御を検討する必要がある。しかし、まずはSolidWorks APIのライフサイクルを完璧に理解することが先決だ。
結論
BOMの自動配置は、単なる「見た目の調整」ではない。それは、設計データという抽象的な情報を、製造という物理的なプロセスへと繋ぐ「インターフェースの構築」である。
インデックスに頼らず、オブジェクトの寿命を支配し、描画コストを計算する。この規律こそが、凡庸な自動化ツールと、伝説的な業務システムを分かつ境界線である。
