【テクニカル・上級編】【実務中級者向け】図面内の「特定ブロック」の有無で保存先を自動振り分け:属性情報を利用したフォルダ整理術 – AutoCAD VBA解析バイブル

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

「図面はデータである」――属性情報に基づく自動保存・フォルダ整理の極致

設計現場において、図面ファイル(DWG)は単なる「絵」ではない。それは、プロジェクトコード、設計者、版数、施工場所といった多層的なメタデータが封じ込められた「構造化データ」である。

しかし、多くの現場では、この貴重なデータが「ファイル名」という極めて脆弱で、かつ人間によるヒューマンエラーが介在しやすい場所に依存している。ファイル名が間違っていれば、データは迷子になり、プロジェクトの整合性は崩壊する。

真の自動化エンジニアが目指すべきは、ファイル名に頼る管理ではない。図面内部の「属性情報(Attribute)」を唯一の真実(Single Source of Truth)とし、それに基づいてファイルシステムを動的に制御するアーキテクチャの構築である。

本稿では、AutoCAD VBAを用い、図面内のタイトルブロックから属性を抽出し、ネットワーク上の適切なプロジェクトディレクトリへ自動的にリネーム・保存する、実務に耐えうる堅牢な実装手法を詳説する。

1. アーキテクチャの設計思想:メタデータの抽出と整合性

本システムの設計において、最も重要なのは「情報の取得」と「ファイル操作」の分離である。

図面内のタイトルブロックは、`BlockReference` オブジェクトとして存在する。その中に含まれる `Attribute` は `AttributeCollection` に格納されている。我々がすべきことは、特定のタグ名(例: `PROJECT_ID` や `DESIGNER`)をキーとして、その値を正確に、かつ高速に走査することだ。

ここで、初学者が陥る罠がある。それは「すべてのエンティティをループで回す」という無駄な計算コストの増大だ。大規模な図面において、全オブジェクトの走査はパフォーマンスを著しく低下させる。本稿では、特定のブロック名を持つオブジェクトにのみフォーカスする手法をとる。

2. 実装:堅牢な属性抽出と自動保存エンジン

以下に、実務レベルの設計思想を反映したVBAコードを示す。単なるコードの提示ではない。エラーハンドリング、メモリ管理、そしてWindows APIの概念を組み込んだ、プロフェッショナル向けのテンプレートである。

Option Explicit

' Windows API: ディレクトリ作成の堅牢性を高めるための宣言(必要に応じて拡張)
' 本実装では、FileSystemObjectによる抽象化を利用するが、
' 極限のパフォーマンスを求める場合は Kernel32.dll の CreateDirectory を検討せよ。

Private Declare PtrSafe Function Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) As Long

''' <summary>
''' 図面内の属性情報を解析し、プロジェクト規定のパスへ保存するメインルーチン
''' </summary>
Public Sub ExecuteSmartAutoSave()
    Dim acadDoc As AcadDocument
    Set acadDoc = ThisDrawing
    
    Dim targetBlockName As String
    targetBlockName = "TITLE_BLOCK_PROJECT" ' ターゲットとするブロック名
    
    Dim projID As String
    Dim designer As String
    Dim newFileName As String
    Dim baseDirPath As String
    Dim finalPath As String
    
    On Error GoTo ErrorHandler

    ' 1. 属性情報の抽出
    ' 探索範囲を限定することで、メモリ消費と計算時間を最小化する
    Dim extractedData As Variant
    extractedData = GetTitleBlockAttributes(acadDoc, targetBlockName)
    
    If IsEmpty(extractedData) Then
        MsgBox "エラー: 指定されたタイトルブロック、または必要な属性が見つかりません。", vbCritical
        Exit Sub
    End If
    
    projID = extractedData(0)    ' Index 0: PROJECT_ID
    designer = extractedData(1)  ' Index 1: DESIGNER_NAME
    
    If projID = "" Or designer = "" Then
        MsgBox "エラー: 属性値が空です。図面情報を確認してください。", vbExclamation
        Exit Sub
    End If

    ' 2. 保存先パスの構築 (ネットワークパスのUNC形式を想定)
    ' 例: \\Server\Projects\P2023-001\Design\P2023-001_Sato.dwg
    baseDirPath = "\\Server\Projects\" & projID & "\Design\"
    newFileName = projID & "_" & designer & ".dwg"
    finalPath = baseDirPath & newFileName

    ' 3. ディレクトリの存在確認と作成
    ' 階層構造が未作成の場合を考慮した再帰的処理が必要
    If Not DirectoryExists(baseDirPath) Then
        If Not CreateDirectoryRecursive(baseDirPath) Then
            Err.Raise vbObjectError + 513, "FileSystem", "ディレクトリの作成に失敗しました。"
        End If
    End If

    ' 4. 保存実行 (SaveAs)
    ' 既存ファイルがある場合の衝突回避ロジックは、実務ではタイムスタンプ等を付与する
    acadDoc.SaveAs finalPath
    
    MsgBox "保存完了: " & finalPath, vbInformation

CleanUp:
    ' オブジェクトの明示的な解放 (Memory Management)
    ' VBAのガベージコレクションに頼らず、COMオブジェクトのライフサイクルを制御する
    Set acadDoc = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

''' <summary>
''' 特定のブロックから属性値を抽出する。メモリ最適化のため、必要な値のみを配列で返す。
''' </summary>
Private Function GetTitleBlockAttributes(doc As AcadDocument, bName As String) As Variant
    Dim ent As AcadEntity
    Dim blkRef As AcadBlockReference
    Dim att As AcadAttribute
    Dim attrDict As Object
    Set attrDict = CreateObject("Scripting.Dictionary")
    
    ' ModelSpaceを走査
    For Each ent In doc.ModelSpace
        ' インスタンスの型判定
        If TypeOf ent Is AcadBlockReference Then
            Set blkRef = ent
            ' ブロック名の一致確認 (EffectiveNameを使用することでダイナミックブロックにも対応)
            If blkRef.EffectiveName = bName Then
                ' 属性の走査
                For Each att In blkRef.GetAttributes
                    ' タグ名に基づき辞書に格納
                    attrDict.Add UCase(att.TagString), att.TextString
                Next att
                
                ' 必要な属性が存在するか検証
                If attrDict.Exists("PROJECT_ID") And attrDict.Exists("DESIGNER_NAME") Then
                    GetTitleBlockAttributes = Array(attrDict("PROJECT_ID"), attrDict("DESIGNER_NAME"))
                    Exit Function
                End If
            End If
        End If
    Next ent
    
    GetTitleBlockAttributes = Empty
End Function

''' <summary>
''' 再帰的なディレクトリ作成を行う。FileSystemObjectを利用。
''' </summary>
Private Function CreateDirectoryRecursive(ByVal path As String) As Boolean
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    On Error Resume Next
    ' 階層を一段ずつ作成していくロジック
    Dim parts() As String
    parts = Split(path, "\")
    
    Dim currentPath As String
    Dim i As Integer
    
    ' UNCパスの考慮
    If Left(path, 2) = "\\" Then
        currentPath = "\\" & parts(2)
        For i = 3 To UBound(parts)
            currentPath = currentPath & "\" & parts(i)
            If Not fso.FolderExists(currentPath) Then
                fso.CreateFolder currentPath
            End If
            currentPath = currentPath
        Next i
    Else
        ' ローカルパス用の処理(省略)
    End If
    
    CreateDirectoryRecursive = (Err.Number = 0)
    Set fso = Nothing
End Function

''' <summary>
''' ディレクトリの存在確認
''' </summary>
Private Function DirectoryExists(ByVal path As String) As Boolean
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    DirectoryExists = fso.FolderExists(path)
    Set fso = Nothing
End Function

3. 伝説的アーキテクトによる技術解説:極限の知見

このコードを単なる「動くスクリプト」として理解してはいけない。以下の3点は、大規模システムを運用する上で不可欠な設計判断である。

A. `EffectiveName` の採用(ダイナミックブロックへの対応)

多くのエンジニアは `blkRef.Name` を使用する。しかし、AutoCADのダイナミックブロックを使用している場合、`Name` プロパティは内部的な匿名名(`*U…`)を返す。これにより、ブロック特定に失敗するという致命的なバグを誘発する。`EffectiveName` を使用することで、ユーザーが定義したブロック名に基づいた確実な判定が可能となる。これはレガシーな環境から最新のAutoCADへ移行する際の必須知識である。

B. メモリ管理とCOMオブジェクトのライフサイクル

VBAにおける `Set obj = Nothing` は、単なる作法ではない。AutoCADのような巨大なアプリケーションにおいて、COMインターフェースを介したオブジェクトの参照が残存することは、メモリリーク、ひいてはAutoCADのクラッシュに直結する。特に、ループ内で `AcadEntity` や `AcadAttribute` を生成する場合、スコープを最小限に抑え、処理終了時には確実に解放するアーキテクチャを徹底せよ。

C. ネットワーク・レイテンシとトランザクションの整合性

本コードでは `FileSystemObject` を用いてディレクトリ作成を行っているが、実務でネットワークストレージ(NAS/SAN)を相手にする場合、ネットワークの瞬断や遅延が最大の敵となる。
`CreateDirectoryRecursive` の実装において、エラーハンドリングを極めて厳格に行う必要がある。また、`SaveAs` を実行する前に、対象のパスが「書き込み可能(Writeable)」であるかを事前にチェックするロジックを追加することで、不完全な保存によるファイル破損のリスクを最小化できる。

結論

図面管理の自動化は、単なる作業時間の削減ではない。それは、設計プロセスにおける「データの信頼性」を担保するためのインフラ構築である。

属性情報をトリガーとした自動振り分けを実現することは、CADデータをエンタープライズ・リソース・プランニング(ERP)の一部へと昇華させる第一歩である。このレベルの自動化を実装できるエンジニアこそが、次世代の設計アーキテクチャを牽引する存在となるのだ。

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