【上級プロへの道】SolidWorksアセンブリ構造をJSONで完全復元!~ツリー階層・合致・マトリクス座標をテキスト化する独自エンジン~
こんにちは!SolidWorks VBAの世界へようこそ。長年この世界で色々な自動化に携わってきましたが、今回は皆さんの「マクロの記録」からの卒業を強力に後押しし、さらにその先へと導く、ちょっと野心的なテーマに挑戦したいと思います。
「アセンブリ構造をJSONで完全復元する独自エンジン」
…なんだか難しそう?大丈夫です。このブログでは、SolidWorks VBAの基本的な使い方から、なぜこの「JSON化」が強力なのか、そしてそれを実現するための具体的なコードまで、優しく、そして丁寧に解説していきます。まるで、経験豊富な先輩エンジニアが隣で一緒にコードを書いているような感覚で、読み進めていただけると嬉しいです。
さあ、SolidWorks VBAの奥深い世界へ、一緒に飛び込みましょう!
—
なぜ「JSON化」がアセンブリ管理の究極なのか?
SolidWorksでアセンブリを作成する時、私たちは部品を配置し、それらを「合致(Mate)」で固定していきますよね。部品の階層構造(ツリー)を考え、それぞれの合致条件を定義していく。これは、設計者にとっては非常に直感的で強力な方法です。
しかし、このアセンブリ情報を「データ」として扱うとなると、話は少し変わってきます。
- マクロの記録の限界: マクロの記録は、個々の操作を記録するには便利ですが、アセンブリ全体の構造や合致関係を汎用的に保存・再利用するには限界があります。毎回同じようなアセンブリを「記録したマクロで」再現するのは、効率的とは言えません。
- 設計変更への対応: アセンブリが複雑化するにつれて、設計変更のたびに手作業で修正するのは大変です。もし、アセンブリの「設計図」をデータとして持っていれば、それを元に自動で再構築できたら…?
- 外部システムとの連携: 他のシステム(生産管理、PDM、シミュレーションツールなど)とSolidWorksアセンブリの情報を連携させたい場合、人間が読めるだけでなく、コンピュータが理解できる「構造化されたデータ」が必要です。
そこで登場するのが、JSON(JavaScript Object Notation)です。JSONは、人間にも読みやすく、コンピュータにとっても扱いやすい、軽量なデータ交換フォーマットです。
このJSONを使って、SolidWorksアセンブリの「ツリー階層」「合致関係」「部品の配置(マトリクス座標)」をテキストデータとして保存し、それを元に全く同じアセンブリをコードから再構築する…これが、今回ご紹介する「独自エンジン」の目指すところです。
この技術をマスターすれば、あなたはSolidWorks VBAの単なる操作者から、「SolidWorksアセンブリの設計思想をデータとして操るエンジニア」へと進化できるはずです!
—
SolidWorks VBAの「キホン」を再確認しよう
JSON化の前に、まずはSolidWorks VBAの基本的な考え方と、アセンブリを操作する上で不可欠な要素を再確認しておきましょう。
1. SolidWorks APIオブジェクトモデル:SolidWorksの「脳」にアクセスする
SolidWorks VBAは、SolidWorksの各機能にアクセスするための「API(Application Programming Interface)」を利用しています。このAPIは、まるでSolidWorksという巨大なシステムの「脳」であり、様々な「オブジェクト」や「メソッド(命令)」、そして「プロパティ(属性)」で構成されています。
私たちがVBAコードを書くとき、これらのオブジェクトを介してSolidWorksに指示を出しているのです。
2. アセンブリを操作するための主要オブジェクト
アセンブリを扱う上で、特に重要になるオブジェクトをいくつかご紹介しましょう。
- `SldWorks` オブジェクト: SolidWorksアプリケーションそのものを表します。VBAコードの出発点となることが多いです。
- `ModelDoc2` オブジェクト: 現在開いているドキュメント(Part, Assembly, Drawing)を表します。アセンブリドキュメントであれば、これがアセンブリの「本体」です。
- `AssemblyDoc` オブジェクト: `ModelDoc2` オブジェクトがアセンブリドキュメントである場合に、アセンブリ固有の機能にアクセスするためのオブジェクトです。
- `Component2` オブジェクト: アセンブリ内の個々の部品(またはサブアセンブリ)を表します。アセンブリツリーの各ノードに対応します。
- `Mate` オブジェクト: アセンブリ内の部品間に定義される「合致」を表します。
3. コード例:アセンブリ内のコンポーネントを列挙してみる
まずは簡単な例として、現在開いているアセンブリのルートレベルにあるコンポーネント名をすべて列挙するVBAコードを見てみましょう。
Sub ListAssemblyComponents()
Dim swApp As SldWorks
Dim swModel As ModelDoc2
Dim swAssy As AssemblyDoc
Dim vComponents As Variant
Dim swComp As Component2
Dim i As Long
‘ SolidWorksアプリケーションを取得
Set swApp = Application.SldWorks
‘ 現在アクティブなドキュメントを取得
Set swModel = swApp.ActiveDoc
‘ アクティブドキュメントがアセンブリかどうかを確認
If swModel Is Nothing Then
MsgBox “開いているドキュメントがありません。”, vbExclamation
Exit Sub
End If
If swModel.GetType <> swDocumentTypes_e.swDocASSEMBLY Then
MsgBox “アクティブなドキュメントはアセンブリではありません。”, vbExclamation
Exit Sub
End If
‘ アセンブリドキュメントとしてキャスト
Set swAssy = swModel
‘ アセンブリ内のすべてのコンポーネントを取得(ルートレベルのみ)
‘ Component2オブジェクトの配列が返る
vComponents = swAssy.GetComponents(True) ‘ Trueはルートレベルのみを取得するフラグ
If IsEmpty(vComponents) Then
MsgBox “アセンブリにコンポーネントが見つかりません。”, vbInformation
Exit Sub
End If
Debug.Print “— アセンブリコンポーネント一覧(ルートレベル) —”
‘ 取得したコンポーネントをループ処理
For i = LBound(vComponents) To UBound(vComponents)
Set swComp = vComponents(i)
‘ コンポーネント名(インスタンス名)を表示
Debug.Print “コンポーネント名: ” & swComp.Name2
‘ コンポーネントのモデル名(ファイル名)を表示
‘ Component2.GetModelDoc2.GetPathName でフルパスを取得し、ファイル名部分だけを抽出
Dim modelDoc As ModelDoc2
Set modelDoc = swComp.GetModelDoc2
If Not modelDoc Is Nothing Then
Debug.Print ” モデル名: ” & modelDoc.GetPathName
End If
‘ ここで、コンポーネントのトランスフォーメーション(位置・姿勢)を取得することも可能
‘ Dim vTransform As Variant
‘ vTransform = swComp.GetTransform ‘ Variant(0 to 15) で返ってくる
‘ Debug.Print ” トランスフォーメーション行列の最初の要素: ” & vTransform(0)
Next i
Debug.Print “———————————————-”
MsgBox “コンポーネント一覧をイミディエイトウィンドウに出力しました。”, vbInformation
End Sub
コードのポイント解説:
- `Application.SldWorks`: SolidWorksアプリケーション本体への参照を取得します。
- `swApp.ActiveDoc`: 現在アクティブなドキュメント(開いているウィンドウ)を取得します。
- `swModel.GetType`: ドキュメントの種類(Part, Assembly, Drawing)を判定します。`swDocASSEMBLY` と比較していますね。
- `swAssy.GetComponents(True)`: アセンブリのルートレベルにあるコンポーネントの配列を取得します。`False` を指定すると、ネストされたサブアセンブリ内のコンポーネントもすべて再帰的に取得します。今回はJSON化の基礎として、まずはルートレベルで十分です。
- `swComp.Name2`: コンポーネントのインスタンス名(例: `Part1@Assembly1/1`)を取得します。
- `swComp.GetModelDoc2`: そのコンポーネントが参照しているドキュメント(PartやSubAssembly)の`ModelDoc2`オブジェクトを取得します。
- `modelDoc.GetPathName`: そのドキュメントのフルパスを取得します。
このコードを実行すると、SolidWorksの「イミディエイトウィンドウ」(Ctrl+Gで表示)に、アセンブリ内のコンポーネント名がリストアップされます。
—
陥りやすいエラーとその回避策
VBAでSolidWorks APIを扱う上で、初心者がつまずきやすいポイントがいくつかあります。
1. オブジェクトが`Nothing`になっている:
- 原因: APIから取得したオブジェクトが、期待通りに取得できなかったり、参照が切れてしまったりした場合に発生します。例えば、アクティブなドキュメントがないのに`swApp.ActiveDoc`を使おうとしたり、存在しないコンポーネントを取得しようとしたり。
- 回避策: コードの冒頭や、オブジェクトを取得した直後に `If obj Is Nothing Then …` のようなチェックを必ず入れましょう。エラーメッセージで「オブジェクト変数またはWithブロック変数が設定されていません」と出たときは、この原因が最も疑われます。
2. データ型の不一致:
- 原因: APIが返すデータ型と、VBAで受け取る変数のデータ型が合っていない場合にエラーになります。例えば、配列を期待しているところに単一の値を代入しようとしたり。
- 回避策: APIドキュメント(SolidWorksのヘルプメニューから参照できます)で、各メソッドやプロパティが返すデータ型をしっかり確認しましょう。`Variant`型は汎用性が高いですが、後で型変換が必要になることもあります。
3. コンポーネントの参照パスの問題:
- 原因: アセンブリを別の場所に移動したり、部品ファイルを削除したりすると、コンポーネントが「ロスト」してしまいます。`swComp.GetModelDoc2`が`Nothing`を返す原因になります。
- 回避策: JSON化してアセンブリ構造を保存・復元する際には、部品ファイルのフルパスを一緒に保存することが重要です。復元時に、そのパスにファイルが存在するかどうかのチェックも入れるとより堅牢になります。
—
JSON化への挑戦:アセンブリ構造を「見える化」する
さて、いよいよ本題です。SolidWorksアセンブリの構造をJSON形式で表現し、それを保存・復元する仕組みを考えていきましょう。
1. 独自JSONスキーマの設計
どのような情報が必要かを洗い出し、JSONの構造を設計します。今回は以下の要素を含めることを考えます。
- `assemblyName`: アセンブリファイルの名前
- `components`: コンポーネントのリスト(配列)
- 各コンポーネントは以下を持つ
- `name`: コンポーネントのインスタンス名(例: `Part1@Assembly1/1`)
- `path`: 参照している部品ファイルのフルパス
- `transform`: 部品を配置する際の4×3マトリクス座標(12個の要素)
- `children`: サブアセンブリの場合、その中のコンポーネントリスト(再帰構造)
- `mates`: 合致情報のリスト(配列)
- 各合致は以下を持つ
- `name`: 合致の名前(例: `COINCIDENT`)
- `entity1`: 合致させる最初のエンティティ情報
- `componentName`: どのコンポーネントの
- `entity`: どのジオメトリ(例: `Face1`, `Edge1`, `Vertex1`)
- `entity2`: 合致させる2番目のエンティティ情報
- `componentName`: どのコンポーネントの
- `entity`: どのジオメトリ
- `alignment`: 合致の向き(例: `swMateAlignment_e.swMateAlignStandard`)
- `isReversed`: 合致が反転されているか(ブーリアン)
JSONのイメージ:
{
“assemblyName”: “MyAssembly.sldasm”,
“components”: [
{
“name”: “Part1@MyAssembly/1”,
“path”: “C:\\Designs\\Parts\\Part1.sldprt”,
“transform”: [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ],
“children”: []
},
{
“name”: “SubAssembly1@MyAssembly/2”,
“path”: “C:\\Designs\\Assemblies\\SubAssembly1.sldasm”,
“transform”: [ … ],
“children”: [
{
“name”: “Part2@SubAssembly1/1”,
“path”: “C:\\Designs\\Parts\\Part2.sldprt”,
“transform”: [ … ],
“children”: []
}
]
}
],
“mates”: [
{
“name”: “COINCIDENT”,
“entity1”: {
“componentName”: “Part1@MyAssembly/1”,
“entity”: “Face1”
},
“entity2”: {
“componentName”: “SubAssembly1@MyAssembly/2”,
“entity”: “Face10”
},
“alignment”: 0, // swMateAlignStandard
“isReversed”: false
}
// … 他の合致情報
]
}
2. JSONへのシリアライズ(出力)VBAコード例
このJSON構造を生成するVBAコードは、再帰処理がカギとなります。
‘
‘ SolidWorksアセンブリ情報をJSON形式で出力するマクロ
‘
‘ — 定数定義 —
‘ SolidWorks APIの列挙型をVBAで利用可能にするための定義(一部)
‘ 実際には、SolidWorksの参照設定でVBAエディタから直接参照できますが、
‘ ここでは例として一部記載します。
‘ 例: If swModel.GetType <> swDocumentTypes_e.swDocASSEMBLY Then …
‘ Const swDocASSEMBLY As Long = 2 ‘ (参考値)
‘ Const swMateAlignStandard As Long = 0 ‘ (参考値)
‘ — グローバル変数 —
‘ JSON文字列を構築するための一時的な変数
Private jsonOutput As String
Private indentLevel As Integer
‘
‘ メイン処理:アセンブリ情報をJSON化してイミディエイトウィンドウに出力
‘
Sub SerializeAssemblyToJson()
Dim swApp As SldWorks
Dim swModel As ModelDoc2
Dim swAssy As AssemblyDoc
Dim jsonObject As Object ‘ Dictionaryオブジェクト(後述)
‘ SolidWorksアプリケーションとアクティブドキュメントを取得
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
‘ ドキュメントの存在とタイプをチェック
If swModel Is Nothing Then
MsgBox “SolidWorksが開かれていないか、アクティブなドキュメントがありません。”, vbCritical
Exit Sub
End If
If swModel.GetType <> swDocumentTypes_e.swDocASSEMBLY Then
MsgBox “アクティブなドキュメントはアセンブリではありません。”, vbExclamation
Exit Sub
End If
Set swAssy = swModel
‘ JSON構築のためのDictionaryオブジェクトを初期化
‘ Microsoft Scripting Runtime の参照設定が必要
On Error Resume Next
Set jsonObject = CreateObject(“Scripting.Dictionary”)
On Error GoTo 0
If jsonObject Is Nothing Then
MsgBox “Microsoft Scripting Runtime の参照設定がされていません。” & vbCrLf & _
“VBAエディタの[ツール] > [参照設定]で、” & vbCrLf & _
“‘Microsoft Scripting Runtime’ にチェックを入れてください。”, vbCritical
Exit Sub
End If
‘ — JSONのルートオブジェクトを構築 —
jsonObject(“assemblyName”) = swModel.GetPathName ‘ フルパスからファイル名のみを取得するのが望ましいが、ここでは簡略化
Dim componentsDict As Object
Set componentsDict = CreateObject(“Scripting.Dictionary”)
jsonObject(“components”) = componentsDict ‘ コンポーネントのDictionaryを格納
Dim matesDict As Object
Set matesDict = CreateObject(“Scripting.Dictionary”)
jsonObject(“mates”) = matesDict ‘ 合致のDictionaryを格納
‘ — コンポーネントツリーを再帰的に処理 —
Dim vRootComponents As Variant
vRootComponents = swAssy.GetComponents(True) ‘ ルートレベルのコンポーネントを取得
Dim componentArray As Variant
ReDim componentArray(LBound(vRootComponents) To UBound(vRootComponents))
Dim i As Long
For i = LBound(vRootComponents) To UBound(vRootComponents)
componentArray(i) = ProcessComponent(vRootComponents(i)) ‘ 再帰処理関数を呼び出し
Next i
componentsDict(“items”) = componentArray ‘ 配列をDictionaryの’items’キーに格納
‘ — 合致情報を処理 —
Dim vMates As Variant
vMates = swAssy.GetMates() ‘ すべての合致を取得
Dim mateArray As Variant
If Not IsEmpty(vMates) Then
ReDim mateArray(LBound(vMates) To UBound(vMates))
For i = LBound(vMates) To UBound(vMates)
mateArray(i) = ProcessMate(vMates(i)) ‘ 合致処理関数を呼び出し
Next i
matesDict(“items”) = mateArray
Else
matesDict(“items”) = Array() ‘ 合致がない場合は空の配列
End If
‘ — JSON文字列を生成 —
jsonOutput = DictToJson(jsonObject) ‘ DictionaryをJSON文字列に変換
‘ イミディエイトウィンドウに出力
Debug.Print jsonOutput
MsgBox “アセンブリ情報がJSON形式でイミディエイトウィンドウに出力されました。”, vbInformation
‘ オブジェクト解放
Set jsonObject = Nothing
Set swAssy = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub
‘
‘ コンポーネントを再帰的に処理し、Dictionary形式で返す関数
‘
Function ProcessComponent(swComp As Component2) As Object
Dim compDict As Object
Set compDict = CreateObject(“Scripting.Dictionary”)
‘ コンポーネント名(インスタンス名)
compDict(“name”) = swComp.Name2
‘ 部品ファイルのパス
Dim modelDoc As ModelDoc2
Set modelDoc = swComp.GetModelDoc2
If Not modelDoc Is Nothing Then
compDict(“path”) = modelDoc.GetPathName
Else
compDict(“path”) = “N/A – Not Loaded or Missing” ‘ モデルがロードされていない場合
End If
‘ コンポーネントのトランスフォーメーション(位置・姿勢)を取得
Dim vTransform As Variant
vTransform = swComp.GetTransform ‘ 4×3行列 (12要素のVariant配列)
If Not IsEmpty(vTransform) Then
‘ 12要素の配列をJSONで扱いやすいようにVector/Matrix形式に変換して格納
‘ ここでは簡略化のため、そのまま12要素の配列として格納します。
‘ 実際には、JSONライブラリによっては、より構造化された形式で格納することを推奨します。
compDict(“transform”) = vTransform
Else
compDict(“transform”) = Array() ‘ 取得できなかった場合
End If
‘ 子コンポーネントの処理(再帰)
Dim vChildComponents As Variant
vChildComponents = swComp.GetChildren() ‘ 子コンポーネントを取得
Dim childArray As Variant
If Not IsEmpty(vChildComponents) Then
ReDim childArray(LBound(vChildComponents) To UBound(vChildComponents))
Dim j As Long
For j = LBound(vChildComponents) To UBound(vChildComponents)
childArray(j) = ProcessComponent(vChildComponents(j)) ‘ 再帰呼び出し
Next j
compDict(“children”) = childArray
Else
compDict(“children”) = Array() ‘ 子コンポーネントがない場合
End If
Set ProcessComponent = compDict ‘ 構築したDictionaryを返す
End Function
‘
‘ 合致情報を処理し、Dictionary形式で返す関数
‘
Function ProcessMate(swMate As Mate) As Object
Dim mateDict As Object
Set mateDict = CreateObject(“Scripting.Dictionary”)
‘ 合致の名前
mateDict(“name”) = swMate.Type ‘ Typeプロパティで合致の種類を取得 (例: 0 = swMateTypeDistance, 1 = swMateTypeCoincident)
‘ より分かりやすくするために、Typeを文字列に変換する処理を追加すると良いでしょう。
‘ 例: MateTypeToString(swMate.Type)
‘ 合致のエンティティ情報
Dim entity1Dict As Object
Set entity1Dict = CreateObject(“Scripting.Dictionary”)
Dim entity2Dict As Object
Set entity2Dict = CreateObject(“Scripting.Dictionary”)
Dim entity1 As MateEntity
Dim entity2 As MateEntity
Set entity1 = swMate.Entity1
Set entity2 = swMate.Entity2
‘ エンティティ1の情報
entity1Dict(“componentName”) = entity1.ComponentName ‘ コンポーネント名
entity1Dict(“entity”) = EntityToString(entity1.Entity) ‘ エンティティ(ジオメトリ)を文字列に変換
‘ エンティティ2の情報
entity2Dict(“componentName”) = entity2.ComponentName
entity2Dict(“entity”) = EntityToString(entity2.Entity)
mateDict(“entity1”) = entity1Dict
mateDict(“entity2”) = entity2Dict
‘ 合致の向き
mateDict(“alignment”) = swMate.Alignment
mateDict(“isReversed”) = swMate.IsReversed
Set ProcessMate = mateDict
End Function
‘
‘ エンティティオブジェクトを文字列に変換するヘルパー関数 (簡易版)
‘
Function EntityToString(entity As Object) As String
‘ 実際には、entityのタイプ(Face, Edge, Vertexなど)を判別し、
‘ SolidWorks APIの対応するメソッドで名前やIDを取得する必要があります。
‘ 例: If TypeOf entity Is Face Then EntityToString = “Face” & FaceID
‘ ここでは簡易的に、オブジェクトの型名と一部の情報を返します。
On Error Resume Next
Dim entityType As String
Dim entityName As String
If entity Is Nothing Then
EntityToString = “Nothing”
Exit Function
End If
Select Case TypeName(entity)
Case “Face”
entityType = “Face”
‘ Face.GetID などで固有のIDを取得できる場合がある
entityName = “Face_” & CStr(entity.GetID) ‘ GetID()は存在しない可能性あり、要確認
Case “Edge”
entityType = “Edge”
entityName = “Edge_” & CStr(entity.GetID)
Case “Vertex”
entityType = “Vertex”
entityName = “Vertex_” & CStr(entity.GetID)
Case Else
entityType = TypeName(entity)
entityName = “Unknown”
End Select
On Error GoTo 0
‘ 実際には、コンポーネント内のジオメトリを一意に識別できるような文字列(例: Feature名やカスタムプロパティなど)を生成するのが理想です。
‘ ここでは単純に型名とID(取得できれば)を返します。
EntityToString = entityType & “:” & entityName
End Function
‘
‘ Microsoft Scripting DictionaryオブジェクトをJSON文字列に変換する関数
‘ (簡易版 – 実際のライブラリを使うとより堅牢です)
‘
Private Function DictToJson(dict As Object) As String
Dim json As String
Dim key As Variant
Dim value As Variant
Dim firstItem As Boolean
json = “{”
firstItem = True
‘ Key-Valueペアを処理
For Each key In dict.Keys
value = dict(key)
If Not firstItem Then json = json & “,” & vbCrLf ‘ 区切り文字
‘ キーをJSON文字列に変換 (ダブルクォートで囲む)
json = json & Chr(34) & key & Chr(34) & “:”
‘ 値をJSON文字列に変換
json = json & ValueToJson(value)
firstItem = False
Next key
json = json & “}”
DictToJson = json
End Function
‘
‘ Variant型の値をJSON文字列に変換するヘルパー関数
‘
Private Function ValueToJson(value As Variant) As String
Dim json As String
Select Case VarType(value)
Case vbObject ‘ Dictionary オブジェクトの場合
If TypeName(value) = “Dictionary” Then
json = DictToJson(value) ‘ 再帰的にDictToJsonを呼び出し
Else
json = “null” ‘ サポート外のオブジェクト
End If
Case vbArray ‘ 配列の場合
json = ArrayToJson(value)
Case vbString ‘ 文字列の場合
json = Chr(34) & Replace(value, Chr(34), “\” & Chr(34)) & Chr(34) ‘ ダブルクォートをエスケープ
Case vbBoolean ‘ ブーリアン値の場合
If value Then json = “true” Else json = “false”
Case vbInteger, vbLong, vbByte, vbSByte, vbInteger, vbLongLong, vbCurrency, vbSingle, vbDouble, vbDecimal ‘ 数値の場合
json = CStr(value)
Case vbDate
json = Chr(34) & CStr(value) & Chr(34) ‘ ISO 8601形式に変換するのが望ましい
Case vbEmpty, vbNull
json = “null”
Case Else ‘ その他の型
json = Chr(34) & CStr(value) & Chr(34) ‘ 文字列として扱う
End Select
ValueToJson = json
End Function
‘
‘ Variant型配列をJSON文字列に変換するヘルパー関数
‘
Private Function ArrayToJson(arr As Variant) As String
Dim json As String
Dim i As Long
Dim firstItem As Boolean
json = “[”
firstItem = True
For i = LBound(arr) To UBound(arr)
If Not firstItem Then json = json & “,” & vbCrLf ‘ 区切り文字
json = json & ValueToJson(arr(i)) ‘ 各要素を再帰的に変換
firstItem = False
Next i
json = json & “]”
ArrayToJson = json
End Function
コードのポイント解説:
- `Scripting.Dictionary` の利用: VBAには標準でJSONを扱う機能がないため、`Microsoft Scripting Runtime` ライブラリに含まれる `Dictionary` オブジェクトを駆使してJSON構造を模倣します。
- 重要: VBAエディタの `[ツール]` > `[参照設定]` で `Microsoft Scripting Runtime` にチェックを入れるのを忘れないでください!
- 再帰処理 (`ProcessComponent` 関数): アセンブリは階層構造を持つため、サブアセンブリの中のコンポーネントも処理する必要があります。`ProcessComponent` 関数は、自身を呼び出すことでこの階層をたどります。
- トランスフォーメーション (`swComp.GetTransform`): 部品を配置した際の3D空間での位置と姿勢を表す4×3の行列(12個の数値)を取得します。これが復元時の重要な情報となります。
- 合致情報 (`swAssy.GetMates`): アセンブリ内のすべての合致を取得し、それぞれの種類、合致させるエンティティ(面、エッジ、頂点など)、向きなどを取得します。
- `entity1.ComponentName` / `entity2.ComponentName`: 合致に関わるコンポーネントの名前。
- `entity1.Entity` / `entity2.Entity`: 合致させるジオメトリそのもの。これを一意に識別できる文字列に変換することが、復元時に重要になります(`EntityToString`関数)。
- JSON変換関数群 (`DictToJson`, `ValueToJson`, `ArrayToJson`): `Dictionary` オブジェクトを、最終的に人間が読めるJSON形式の文字列に変換します。これは、JSONライブラリがないVBAでの「簡易実装」です。本格的な利用では、外部のJSONパーサーライブラリ(VB.NETならNewtonsoft.Jsonなど)を使うのが一般的ですが、VBAでもこの方法で十分実現可能です。
注意点:
- エンティティの識別: `EntityToString` 関数は、ジオメトリを識別するための簡易的なものです。実際には、部品ファイル内でジオメトリを一意に特定できるような方法(例: Feature名、カスタムプロパティ、ジオメトリのIDなど)を確立する必要があります。
- 合致タイプ: `swMate.Type` は数値で返ってくるため、`MateTypeToString` のような関数で、`swMateTypeCoincident` (0), `swMateTypeConcentric` (4) のような分かりやすい名前に変換すると、JSONがより読みやすくなります。
- エラーハンドリング: 上記コードは基本的な部分のみを示しています。実際の運用では、ファイルが見つからない、API呼び出しが失敗するなどの状況に対する、より丁寧なエラーハンドリングが必要です。
—
JSONからの復元:アセンブリを「復活」させる
JSONファイルに保存されたアセンブリ情報を元に、全く同じアセンブリをSolidWorks上に再構築するプロセスです。これは、シリアライズの逆の処理になります。
1. JSONファイルの読み込みと解析:
- JSONファイルを読み込み、VBAの `Dictionary` オブジェクト(またはJSONパーサー)で解析します。
2. アセンブリドキュメントの新規作成:
- `swApp.NewDocument` メソッドで、新しいアセンブリドキュメントを作成します。
3. コンポーネントの配置:
- JSONの `components` 配列をループ処理します。
- `swAssy.AddComponent` メソッドを使用して、指定されたパスの部品ファイルをアセンブリに追加します。
- 重要: `swComp.SetTransform` メソッドで、JSONに保存されているトランスフォーメーション行列を設定し、部品の正確な位置と姿勢を再現します。
4. 合致の定義:
- JSONの `mates` 配列をループ処理します。
- `swAssy.AddMate` メソッドを使用して、合致を定義します。
- 重要: `AddMate` メソッドには、合致させるエンティティを正確に指定する必要があります。JSONに保存した `componentName` と `entity` 情報(文字列)を元に、SolidWorks APIで該当するエンティティオブジェクトを検索・特定する処理が必要です。これが最も複雑な部分の一つです。
復元VBAコードの概要(概念的なイメージ):
‘
‘ JSONファイルからSolidWorksアセンブリを再構築するマクロ(概念)
‘
Sub DeserializeAssemblyFromJson(jsonFilePath As String)
Dim swApp As SldWorks
Dim swModel As ModelDoc2
Dim swAssy As AssemblyDoc
Dim jsonObject As Object ‘ Dictionary オブジェクト
‘ — 1. JSONファイルの読み込みと解析 —
Dim jsonString As String
Dim fileNum As Integer
fileNum = FreeFile
Open jsonFilePath For Input As #fileNum
jsonString = Input$(LOF(fileNum), fileNum)
Close #fileNum
‘ JSON文字列をDictionaryオブジェクトに変換(簡易版)
‘ ここで、JSONパーサーライブラリや自作の解析関数を使用します。
‘ Set jsonObject = ParseJsonToDict(jsonString) ‘ 仮の関数
‘ 実際には、上記Serializeコードで使ったDictToJsonの逆処理が必要
‘ 例として、ここでは直接Dictionaryにアクセスすると仮定します。
‘ 実際には、IniFile Parserのようなものか、WebからJSONパーサのVBAコードを探してくるのが現実的です。
‘ — 仮のDictionaryオブジェクトを作成(本来はJSONから解析) —
‘ この部分は、実際にはJSON文字列を解析して生成されるべきです。
Set jsonObject = CreateObject(“Scripting.Dictionary”)
jsonObject(“assemblyName”) = “RestoredAssembly.sldasm” ‘ JSONから取得
‘ … Component, Mate 情報をJSONから読み込む処理 …
MsgBox “JSON解析処理は省略しています。実際のコードでは、JSON文字列を解析して ‘jsonObject’ に格納してください。”, vbExclamation
‘ 実際の解析処理は複雑なため、ここでは省略します。
‘ — 2. 新しいアセンブリドキュメントを作成 —
Set swApp = Application.SldWorks
Set swModel = swApp.NewDocument(swApp.GetUserPreferenceString(swUserPreferenceStringValue_e.swDefaultTemplateAssembly), 0, 0, 0) ‘ デフォルトテンプレートを使用
If swModel Is Nothing Then
MsgBox “新しいアセンブリドキュメントの作成に失敗しました。”, vbCritical
Exit Sub
End If
Set swAssy = swModel
swModel.Extension.SaveAs swApp.︱GetDocumentPath(swModel) & “\” & jsonObject(“assemblyName”), swSaveAsVersion_e.swSaveAsCurrentVersion, swSaveAsOptions_e.swSaveAsOptions_Silent, 0, 0, 0 ‘ ファイル名で保存
‘ — 3. コンポーネントの配置 —
Dim componentsData As Object ‘ JSONから読み込んだコンポーネントデータ (Dictionary or Array)
componentsData = jsonObject(“components”)(“items”) ‘ 仮にitems配列として取得
Dim addedComponents As Object ‘ 配置したコンポーネントを管理するDictionary
Set addedComponents = CreateObject(“Scripting.Dictionary”)
Dim compData As Variant
For Each compData In componentsData
Dim componentPath As String
componentPath = compData(“path”) ‘ JSONから取得
‘ 部品ファイルが存在するかチェック (重要!)
If Dir(componentPath) = “” Then
MsgBox “部品ファイルが見つかりません: ” & componentPath & vbCrLf & “アセンブリの復元をスキップします。”, vbExclamation
‘ ここで処理を中断するか、続行するか判断
GoTo Cleanup ‘ クリーンアップ処理へ
End If
Dim swComp As Component2
‘ AddComponent2 を使うと、トランスフォーメーションも同時に指定できる
Dim vTransform As Variant
vTransform = compData(“transform”) ‘ JSONから取得したトランスフォーメーション行列
‘ 4×3行列を16要素のMatrix形式に変換する必要がある場合がある
‘ Dim swMatrix As Object ‘ MathUtil.CreateMatrix など
‘ Set swMatrix = … (vTransformからMatrixオブジェクトを作成)
‘ Set swComp = swAssy.AddComponent5(componentPath, swAddComponentOptions_e.swAddComponentOption_NoOption, “”, 0, 0, 0) ‘ 簡略化
‘ swComp.SetTransform swMatrix.ArrayData ‘ Matrixオブジェクトの配列データを設定
‘ 簡略化のため、ここではAddComponentとSetTransformを別々に行う例を示します。
‘ 実際には AddComponent2 を使う方が効率的です。
Dim componentName As String
componentName = compData(“name”) ‘ JSONから取得
Dim componentInstanceName As String
componentInstanceName = Left(componentName, InStrRev(componentName, “@”) – 1) ‘ 例: Part1@Assembly1/1 -> Part1
‘ AddComponent2 (より新しいAPI) を使う例
Dim swComp2 As Component2
Dim transformArray(0 To 15) As Double ‘ 4×4行列用
‘ vTransform (12要素) から transformArray (16要素) を構築する処理
‘ 例: transformArray(0) = vTransform(0), transformArray(1) = vTransform(1), … transformArray(12) = vTransform(11)
‘ 残りは単位行列の要素を設定
‘ … (この変換処理は省略)
‘ ここで Set swComp2 = swAssy.AddComponent2(…) を呼び出す
‘ 例: Set swComp2 = swAssy.AddComponent2(componentPath, swAddComponentOptions_e.swAddComponentOption_LoadReferencedDocuments, “”, transformArray(0), transformArray(4), transformArray(8))
‘ SetTransform を使う場合
Set swComp2 = swAssy.AddComponent(componentPath, swAddComponentOptions_e.swAddComponentOption_LoadReferencedDocuments, “”, 0, 0, 0) ‘ まず配置
If Not swComp2 Is Nothing Then
‘ コンポーネントのトランスフォーメーションを設定
‘ swComp2.SetTransform vTransform ‘ 直接設定できる場合もあるが、API仕様を確認
‘ 実際には、4×4行列への変換が必要な場合が多い
‘ 例: Call SetComponentTransform(swComp2, vTransform)
‘ Call SetComponentTransform(swComp2, compData(“transform”))
MsgBox “トランスフォーメーション設定処理は省略しています。”, vbExclamation
Else
MsgBox “コンポーネントの配置に失敗しました: ” & componentPath, vbExclamation
GoTo Cleanup
End If
‘ 配置したコンポーネントをDictionaryに格納(合致定義で必要)
addedComponents(componentName) = swComp2
Next compData
‘ — 4. 合致の定義 —
Dim matesData As Object ‘ JSONから読み込んだ合致データ
matesData = jsonObject(“mates”)(“items”) ‘ 仮にitems配列として取得
Dim mateData As Variant
For Each mateData In matesData
Dim mateType As Long
‘ MateTypeStringToLong (mateData(“name”)) ‘ 文字列から合致タイプ定数に変換
mateType = CLng(mateData(“name”)) ‘ 簡易的に数値として取得
Dim component1 As Component2
Dim component2 As Component2
Dim entity1 As Object ‘ MateEntityオブジェクト
Dim entity2 As Object ‘ MateEntityオブジェクト
Dim entity1Info As Object: Set entity1Info = mateData(“entity1”)
Dim entity2Info As Object: Set entity2Info = mateData(“entity2”)
‘ コンポーネントオブジェクトを取得
If addedComponents.Exists(entity1Info(“componentName”)) Then
Set component1 = addedComponents(entity1Info(“componentName”))
Else
MsgBox “合致定義エラー: コンポーネントが見つかりません – ” & entity1Info(“componentName”), vbExclamation
GoTo Cleanup
End If
If addedComponents.Exists(entity2Info(“componentName”)) Then
Set component2 = addedComponents(entity2Info(“componentName”))
Else
MsgBox “合致定義エラー: コンポーネントが見つかりません – ” & entity2Info(“componentName”), vbExclamation
GoTo Cleanup
End If
‘ エンティティオブジェクトを検索・取得(これが一番難しい!)
‘ JSONの “entity” 文字列(例: “Face:Face_1″)を元に、
‘ Component.GetSpecificEntityBy XYZ メソッドなどで検索する
‘ Set entity1 = FindEntity(component1, entity1Info(“entity”))
‘ Set entity2 = FindEntity(component2, entity2Info(“entity”))
‘ … (FindEntity関数は自作が必要)
‘ 仮のエンティティ設定
‘ Set entity1 = component1.GetSpecificEntityBy XYZ(…) ‘ 実際の検索処理
‘ Set entity2 = component2.GetSpecificEntityBy XYZ(…)
‘ 合致を追加
‘ Dim newMate As Mate
‘ Set newMate = swAssy.AddMate(entity1, entity2, mateType, mateData(“alignment”), mateData(“isReversed”))
‘ — 簡略化のため、合致定義処理は省略 —
‘ 実際には、FindEntity関数とAddMateの呼び出しが必要です。
‘ MsgBox “合致定義処理は省略しています。”
Next mateData
‘ — 最終処理 —
swModel.EditRebuild3 ‘ 再構築
swModel.ViewZoomtofit2 ‘ 全体表示
MsgBox “アセンブリが正常に復元されました!”, vbInformation
Cleanup:
‘ オブジェクト解放
Set addedComponents = Nothing
Set jsonObject = Nothing
Set swAssy = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub
‘ — Helper Function: FindEntity (概念) —
‘ Function FindEntity(comp As Component2, entityString As String) As Object
‘ ‘ entityString (例: “Face:Face_1”) を解析し、
‘ ‘ comp オブジェクト内の対応するジオメトリを検索して返す
‘ ‘ … 実装は非常に複雑 …
‘ End Function
‘ — Helper Function: SetComponentTransform (概念) —
‘ Sub SetComponentTransform(swComp As Component2, transform12 As Variant)
‘ ‘ 12要素のトランスフォーメーション行列を、
‘ ‘ SolidWorks APIが要求する形式(例: 4×4行列)に変換してSetTransformに渡す
‘ ‘ … 実装は複雑 …
‘ End Sub
復元処理の難しさ:
- エンティティの特定: JSONに保存した `entity` 文字列(例: “Face:Face\_1″)から、実際の `Face`, `Edge`, `Vertex` オブジェクトをSolidWorks APIで検索し、特定するのが最も難しい部分です。これには、コンポーネント内のジオメトリをどのように一意に識別するか、という設計が重要になります。
- トランスフォーメーション行列の変換: `swComp.GetTransform` で取得できる12要素の配列を、`swComp.SetTransform` や `swAssy.AddComponent2` で要求される4×4行列の形式に変換する必要があります。
- エラーハンドリング: 部品ファイルが見つからない、エンティティが見つからない、合致の定義が不正などのエラーに備えた、堅牢なエラーハンドリングが不可欠です。
—
まとめ:この技術がもたらす未来
今回ご紹介した「SolidWorksアセンブリ構造のJSONシリアライゼーションと再構築」は、確かに高度な技術です。しかし、これをマスターすることで、あなたは以下のような強力な能力を手に入れることができます。
- アセンブリのバージョン管理の高度化: 設計履歴をJSONデータとして保存し、過去の状態を正確に再現できます。
- 設計変更の自動化: JSONデータを編集・操作することで、アセンブリの構成をプログラムで変更し、再構築できます。
- 設計パラメータの外部管理: JSON内のトランスフォーメーションや合致情報を外部のデータベースや設定ファイルで管理し、アセンブリを動的に生成できます。
- 他システムとの連携強化: SolidWorksアセンブリの構造情報を、他のCAD/CAM/CAEシステムや生産管理システムと容易に連携できるようになります。
最初は難しく感じるかもしれませんが、一つ一つのAPIの挙動を理解し、地道にコードを積み上げていくことで、必ず道は開けます。マクロの記録から一歩踏み出し、SolidWorks VBAの真の力を引き出しましょう。
このブログが、皆さんの「SolidWorks VBAマスターへの道」を照らす一助となれば幸いです。もし質問があれば、遠慮なくコメントしてくださいね!一緒に学び、成長していきましょう!
