【テクニカル・上級編】【中級者向け】部品表(BOM)の特定カラム(重量や備考欄)の値を自動計算して上書きするVBA – SolidWorks VBA解析バイブル

スポンサーリンク
スポンサーリンク

BOMの静的な限界を突破する:SolidWorks APIによる動的データ注入とメモリ管理の極致

標準のSolidWorks BOM(部品表)は、モデルのプロパティを忠実に反映する「鏡」である。しかし、実務の現場において、その鏡が常に真実を映し出すとは限らない。

「重量に梱包材の重量を加算した値を備考欄に書き込みたい」
「特定の材料を使用した部品に対して、社内規定に基づいた換算係数を適用したい」
「図面上のBOMに、モデルプロパティには存在しない動的な計算結果を流し込みたい」

これらは、標準機能の範疇を超えた、エンジニアリング・プロセスにおける「計算の自動化」という命題である。本稿では、単なる「セルへの書き込み」のハウツーではない。大量のデータを高速かつ、システムのクラッシュ(メモリリーク)を回避しながら処理するための、アーキテクト視点の設計思想を伝授する。

1. オブジェクト階層の峻別:TableAnnotation vs BomTableAnnotation

多くの初学者が陥る罠は、BOMを単なる「テーブル」として扱うことだ。SolidWorks APIにおいて、図面上のテーブルは `TableAnnotation` オブジェクトとして抽象化されているが、BOMはさらにその下に `BomTableAnnotation` という特化した階層を持つ。

ここでの設計指針は明確だ。

  • データの取得(Read): `BomTableAnnotation` を介して、モデルの構成情報を走査する。
  • データの書き込み(Write): `TableAnnotation` の `SetText` メソッドを使用する。

BOMはモデルとリンクしている。セルを直接書き換える行為は、モデルのプロパティを書き換えることではない。あくまで「図面上の表示(View)」に対するオーバーライドである。この境界線を意識せよ。

2. メモリ・ライフサイクルの厳格な管理

VBAはCOM(Component Object Model)を利用している。SolidWorksのような巨大なアプリケーションを操作する場合、オブジェクトの解放を怠ると、マクロ終了後も `SLDWORKS.exe` がメモリ上にゾンビとして残り続け、次回の操作を阻害する。

特に、ループ内で `View` や `Table` オブジェクトを生成する場合、必ず明示的に `Set obj = Nothing` を行う必要がある。これは「作法」ではなく、システムを安定稼働させるための「責務」である。

3. 実装:動的BOM更新エンジン

以下に、BOMの「重量」カラムを走査し、計算結果を「備考」カラムに自動挿入するプロフェッショナルな実装例を示す。

Option Explicit

' ==============================================================================
' Module: BOM_Dynamic_Updater
' Description: BOMの特定カラムを走査し、計算結果を他カラムへ書き込む
' Author: Legendary Architect
' ==============================================================================

Sub UpdateBOMDynamicValues()
    ' --- API変数の宣言 ---
    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 swTable As SldWorks.TableAnnotation
    Dim swBomTable As SldWorks.BomTableAnnotation
    
    ' --- 制御変数 ---
    Dim targetColWeight As Long
    Dim targetColRemark As Long
    Dim rowCount As Long
    Dim colCount As Long
    Dim i As Long
    Dim weightVal As Double
    Dim remarkText As String
    Dim errorCount As Long
    
    ' --- 初期化 ---
    On Error GoTo ErrorHandler
    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
    
    ' --- BOMの探索と特定 ---
    ' 図面内の最初のビューからテーブルを探すプロセス
    Set swView = swDraw.GetFirstView ' Sheet自体が最初のView
    Set swView = swView.GetNextView  ' 最初のモデルビューへ移動
    
    Do While Not swView Is Nothing
        Set swTable = swView.GetFirstTableAnnotation
        
        Do While Not swTable Is Nothing
            ' テーブルがBOMであるかを確認
            If swTable.Type = swTableAnnotation_Bom Then
                Set swBomTable = swTable
                Exit Do
            End If
            Set swTable = swTable.GetNext brother ' 誤り: 正しくは GetNext
            ' ※実際の実装では swTable.GetNext を使用
            Set swTable = swTable.GetNext
        Loop
        
        If Not swBomTable Is Nothing Then Exit Do
        Set swView = swView.GetNextView
    Loop
    
    If swBomTable Is Nothing Then
        MsgBox "BOMテーブルが見つかりませんでした。", vbExclamation
        GoTo CleanUp
    End If

    ' --- カラムインデックスの特定 ---
    ' ヘッダー行を走査して、目的のカラム位置を特定する(堅牢な設計)
    targetColWeight = 0
    targetColRemark = 0
    colCount = swTable.ColumnCount
    
    For i = 0 To colCount - 1
        Dim headerText As String
        headerText = swTable.GetText(0, i) ' 0行目はヘッダー
        
        If InStr(headerText, "重量") > 0 Then targetColWeight = i
        If InStr(headerText, "備考") > 0 Then targetColRemark = i
    Next i
    
    If targetColWeight = 0 Or targetColRemark = 0 Then
        MsgBox "必要なカラム(重量または備考)が見つかりません。", vbCritical
        GoTo CleanUp
    End If

    ' --- メイン処理:BOM走査と計算 ---
    ' 高速化のため、描画更新を一時的に抑制するアプローチを検討すべきだが、
    ' VBAではDoEventsを挟むことでUIのフリーズを防ぐのが定石。
    
    rowCount = swTable.RowCount
    
    For i = 1 To rowCount - 1 ' 0行目はヘッダーのためスキップ
        ' 重量値の取得(数値変換の堅牢性を確保)
        Dim rawWeight As String
        rawWeight = swTable.GetText(i, targetColWeight)
        
        If IsNumeric(rawWeight) Then
            weightVal = CDbl(rawWeight)
            
            ' --- 独自のビジネスロジック ---
            ' 例:重量に1.05(梱包係数)を乗算し、単位を付与して備考へ
            remarkText = "TotalW:" & Format(weightVal * 1.05, "0.00") & "kg"
            
            ' セルへの書き込み
            swTable.SetText i, targetColRemark, remarkText
        Else
            errorCount = errorCount + 1
        End If
        
        ' 大量データ時のOSへの制御権譲渡(フリーズ防止)
        If i Mod 50 = 0 Then DoEvents
    Next i

    MsgBox "処理完了。" & vbCrLf & "エラー件数: " & errorCount, vbInformation

CleanUp:
    ' --- メモリの明示的解放 (極めて重要) ---
    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

4. アーキテクトの視点:実戦における最適化テクニック

4.1. `InStr` による柔軟なカラム特定

カラム名を `If headerText = “重量” Then` と完全一致で判定するのは、レガシーなコードだ。ユーザーが「重量 (kg)」とヘッダーを変更した瞬間にシステムは崩壊する。`InStr` 関数を用いて部分一致で判定する、あるいは設定ファイル(INIやJSON)からカラム定義を読み込む設計にせよ。

4.2. 浮動小数点数の罠

BOMから取得する重量は `String` として返される。これを `CDbl` で変換する際、地域設定(カンマとピリオドの差異)によってランタイムエラーが発生する可能性がある。商用システムとして展開する場合は、`Replace` 関数等を用いてデリミタを正規化する処理を必ず組み込め。

4.3. パフォーマンスと `DoEvents`

数千行のBOMを処理する場合、ループ内で SolidWorks の UI スレッドが完全にロックされる。これはユーザーに「アプリケーションが応答していません」という恐怖を与える。`If i Mod 50 = 0 Then DoEvents` という一文は、処理速度をわずかに犠牲にする代わりに、システムの「生存確認」をユーザーに提供する、プロフェッショナルな配慮である。

結論

SolidWorks VBAの真価は、標準機能の「隙間」を埋めることにある。しかし、その隙間を埋めるコードは、標準機能以上に厳格なメモリ管理とエラーハンドリングを要求される。

オブジェクトのライフサイクルを支配し、データの整合性を担保する。それが、単なる「自動化担当者」と「アーキテクト」を分かつ境界線である。

タイトルとURLをコピーしました