Productのインスタンス名をパーツ番号と同じにするマクロ|CATIAマクロの作成方法
今回の記事は「お問い合わせ」でいただいた内容です。
送って頂いた内容は以下のようなマクロです。
以前に掲載されてます「Productのパーツ番号をインスタンス名と同じにするマク
ロ」
こちらの反対バージョンを作成したくマクロ作っているのですが
動作が上手くいかず助言いただければ幸いです。—–中略——
書き換えて動作検証を行ったのですが、
親パーツは動くのですが、子パーツのインスタンス名が変換されませんでした。
CATIAの特性上、子パーツのインスタンス名の書き換えが不可なのかもわかりませんでした。
以前、紹介した「Productのパーツ番号をインスタンス名と同じにするマク
「pro.PartNumber = pro.Name」と書くことで、”パーツ番号”を”インスタンス名”に変換していました。つまり単純に考えればこの1行を「pro.Name = pro.PartNumber」と入れ替えれば”インスタンス名”を”パーツ番号”に変換することができます。
しかし、Productの性質上このやり方ではお問い合わせ頂いた通り、ツリー第1階層のProductのみしか変更することが出来ません。
そこで今回は子Product含めすべてのProductのインスタンス名をパーツ番号に一括で変更するマクロを紹介していきます。少し難しい内容も入っていますが、最悪処理の内容を理解しなくても実行可能なので「そういうものだ」と思ってコードの書き方を覚えてもらうだけでも構いません。
マクロの機能
今回作成したのはProductのインスタンス名をパーツ番号と同じにするマクロです。
具体的な機能は以下のとおりです。
・アクティブドキュメント(CATProduct)内の全プロダクトに適用
・インスタンス名は現在の「.1」「.2」を取得し「パーツ番号.1」「パーツ番号.2」とする
サンプルコード
マクロのコードは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
Option Explicit Sub CATMain() 'アクティブドキュメント定義 If TypeName(CATIA.ActiveDocument) <> "ProductDocument" Then MsgBox "このマクロはProductDocument専用です。" & vbLf & _ "CATProductに切り替えて実行してください。" Exit Sub End If Dim doc As ProductDocument Set doc = CATIA.ActiveDocument 'Selection定義 Dim sel As Selection Set sel = doc.Selection sel.Clear 'Productをすべて取得 sel.Search ("アセンブリー・デザイン.Product,all") 'Productを全選択 sel.Remove (1) '1番親のProductの選択を解除 Dim pros As Collection Set pros = New Collection Dim i As Integer For i = 1 To sel.Count pros.Add sel.Item(i).Value Next i sel.Clear 'インスタンス名をパーツ番号に変更 Dim pro As Product For Each pro In pros pro.ApplyWorkMode (DEFAULT_MODE) 'インスタンス名取得 Dim InstName As String InstName = pro.Name 'インスタンス名の「.」以降の値を取得 Dim No As String Dim cnt As Integer If InStr(InstName, ".") = 0 Then No = ".1" cnt = 1 Else No = Right(InstName, Len(InstName) - InStrRev(InstName, ".") + 1) cnt = Right(InstName, Len(InstName) - InStrRev(InstName, ".")) End If 'proの親プロダクトを取得 Dim parent_pro As Product Set parent_pro = pro.Parent.Parent 'インスタンス名=パーツ番号 On Error Resume Next label: Err.Clear parent_pro.ReferenceProduct.Products.Item(pro.Name).Name = pro.PartNumber & No 'エラーが出た場合「No」の値を増やしてもう1度名称変更を試みる If Err.Number <> 0 Then cnt = cnt + 1 No = "." & Replace(Str(cnt), " ", "") GoTo label 'labelに戻る End If Next pro End Sub |
コード解説
アクティブドキュメント定義
1 2 3 4 5 6 7 8 9 |
'アクティブドキュメント定義 If TypeName(CATIA.ActiveDocument) <> "ProductDocument" Then MsgBox "このマクロはProductDocument専用です。" & vbLf & _ "CATProductに切り替えて実行してください。" Exit Sub End If Dim doc As ProductDocument Set doc = CATIA.ActiveDocument |
まずはじめにアクティブドキュメントの定義をします。
今回のマクロはCATProductでのみ有効なものなので、アクティブドキュメントがCATProduct以外の場合はTypeName関数を使った条件分岐でマクロを終了するようにしています。
つまり、アクティブドキュメントがCATProductの場合のみ変数「doc」にアクティブドキュメントを代入し、マクロの処理を続けます。
Selection定義
1 2 3 4 |
'Selection定義 Dim sel As Selection Set sel = doc.Selection sel.Clear |
アクティブドキュメントが定義できたら、そのドキュメント内のSelectionを定義します。
この時、念の為「Clearメソッド」で現在の選択を全て解除しておきます。
Productをすべて取得
1 2 3 4 5 6 7 8 9 10 11 12 |
'Productをすべて取得 sel.Search ("アセンブリー・デザイン.Product,all") 'Productを全選択 sel.Remove (1) '1番親のProductの選択を解除 Dim pros As Collection Set pros = New Collection Dim i As Integer For i = 1 To sel.Count pros.Add sel.Item(i).Value Next i sel.Clear |
次にアクティブドキュメント内にあるProductを全て取得します。
「pros」というコレクションを用意して、その中に格納していきます。
Selectionオブジェクトの「Searchメソッド」を使って、ドキュメント内のProductを全て選択、選択状態のオブジェクトを全て「pros」に追加という処理を行うことで「pros」に全てのProductが格納されます。
このとき、ドキュメント内のProductを全て選択という処理を行うと、ドキュメント内の一番親のProduct(RootProductともいう)も選択状態になります。RootProductは無視したいので処理から除外するため、「Removeメソッド」を使ってあらかじめ選択を解除しておきます。
インスタンス名をパーツ番号に設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
'インスタンス名をパーツ番号に変更 Dim pro As Product For Each pro In pros pro.ApplyWorkMode (DEFAULT_MODE) 'インスタンス名取得 Dim InstName As String InstName = pro.Name 'インスタンス名の「.」以降の値を取得 Dim No As String Dim cnt As Integer If InStr(InstName, ".") = 0 Then No = ".1" cnt = 1 Else No = Right(InstName, Len(InstName) - InStrRev(InstName, ".") + 1) cnt = Right(InstName, Len(InstName) - InStrRev(InstName, ".")) End If 'proの親プロダクトを取得 Dim parent_pro As Product Set parent_pro = pro.Parent.Parent 'インスタンス名=パーツ番号 On Error Resume Next label: Err.Clear parent_pro.ReferenceProduct.Products.Item(pro.Name).Name = pro.PartNumber & No 'エラーが出た場合「No」の値を増やしてもう1度名称変更を試みる If Err.Number <> 0 Then cnt = cnt + 1 No = "." & Replace(Str(cnt), " ", "") GoTo label 'labelに戻る End If Next pro |
最後に「pros」内ループを行い、インスタンス名をパーツ番号に変更していきます。
この時、Productの構成要素であるCATPartが表示モードだとエラーが発生します。
ただ、設計モードに切り替えると、データによっては処理に時間がかかってしまいます。
そこで、ここでは「デフォルトモード」に切り替えます。
デフォルトモードにはヘルプに下記のように書かれています。
-
-
The representation is in Default Work mode.Default mode is a mode where the Product Container is loaded. It’s an intermediate mode between Visualization Mode and Design Mode.
Default Work mode.Default modeで、Product Containerが読み込まれるモードです。
表示モードと設計モードの中間のモードです。
-
上記の通り、表示モードと設計モードの中間のモードです。
モードの切り替えは下記のようなコードで行うことができます。
Productオブジェクト.ApplyWorkMode (モード)
※設計=DESIGN_MODE 表示=VISUALIZATION_MODE デフォルト=DEFAULT_MODE
パーツ番号をVBAで変更する方法
あとはProductのインスタンス名をパーツ番号に変更するだけです。
冒頭でもいったとおり「pro.Name = pro.PartNumber」ではうまく変更ができません。
実はCATProduct内のリンクは第1階層のProductしか紐づいていません。
([編集]>[リンク]より確認できます)
上画像の構成の「AAAAAAAA」を「新しいウィンドウで開く」で開きリンクを確認すると下画像のようになっています。先ほどと同じくツリー第1階層のProductしか紐づいていないことが確認できます。
上記のことから単純に「pro.Name = pro.PartNumber」と書くと、アクティブなCATProduct内のツリー第1階層のProductしか処理が対応されないという結果になってしまいます。
これを回避するには「参照元のドキュメント」を取得する必要があります。
通常、リンクを確認すればわかりますが「リンクタイプ」は「インスタンス」になっています。これは「リンクタイプ」の左側の項「参照元のドキュメント」をインスタンス(複製)したものがツリー上に表示されていることを表しています。
これまでのコードで取得したProductたち(prosの中身)はすべてこのインスタンスされたProductたちです。インスタンスされたProduct内の構成要素となっているProductは変更すること(インスタンス名を変更したり、構成を変更することなど)が出来ません。
たとえば下画像の「DDDDDDDD」のインスタンス名を変更したいとします。
しかし「DDDDDDDD」は「AAAAAAAA」の構成の一部であり、ここで表示されている「DDDDDDDD」はあくまでもインスタンスされた「AAAAAAAA」に”ついてきた”だけの存在です。
そのため「DDDDDDDD」を編集したいのであればインスタンスされた「AAAAAAAA」ではなく、インスタンス元となっている「AAAAAAAA」にアクセスする必要があるという訳です。
インスタンス元となっているProductを取得するには「ReferenceProductプロパティ」を使います。
Productオブジェクト.ReferenceProduct
今回取得したいインスタンス元のProductは“インスタンス名を変更したいProductの1つ親のProduct”です。VBAでいうとProdcutオブジェクトの1つ親には「Products」が挟まるので2つ親にあたります。
色々説明してきましたが結論、以下のように書くことで「pro」のインスタンス名を「文字列」に変更することが可能になります。
‘proの親プロダクトを取得
Dim parent_pro As Product
Set parent_pro = pro.Parent.Parent
‘インスタンス名を変更
parent_pro.ReferenceProduct.Products.Item(pro.Name).Name = 文字列
今回のサンプルコードの場合は文字列部分をインスタンス名+[.X]としています。
インスタンス名が同じになってしまう場合のエラー処理
たとえばパーツ番号が「Part1」でインスタンス名が下記のような場合、マクロの機能としてはインスタンス名の「.1」を取得して「AAA」や「BBB」の部分を「Part1」に変更しようとします。
その場合同じインスタンス名が複数できてしまいますが、ツリーの同階層で同じインスタンス名が共存することはできないためエラーが発生してしまいます。
このエラーを回避するためサンプルコードではエラーの状態を取得して同じインスタンス名が発生していないかを確認しています。
On Error Resume Next
label:
Err.Clear
‘処理①
If Err.Number <> 0 Then
‘処理②
GoTo label
End If
上記コードではまず「On Error Resume Next」を使い、エラーが発生したとしても無視して処理を進める状態にしています。
この状態でエラーが発生すると「Err.Number」にエラーに応じたエラー番号がセットされます。
エラーが発生していない状態は「Err.Number」が「0」なので、「Err.Number <> 0」としておけばエラーが発生した場合にこのIf文内の処理②を行うというような条件分岐ができるわけです。
そしてエラーが発生した場合(言い換えれば同一のインスタンス名が発生した場合)は「.1」の数字部分をカウントアップしていくような処理を処理②の部分に書いておけば同一インスタンス名が共存することはなくなります。
※エラーによる条件分岐はそのほかの予期せぬエラー時にも適用されてしまうので、今回のようなちょっとしたマクロの場合はあまり気にしなくてもいいかもしれませんが、使うときはしっかりと発生するエラーのことを考えながら注意して使用しましょう。
まとめ
今回はProductのインスタンス名をパーツ番号と同じにするマクロについての内容でした。
対の内容である「Productのパーツ番号をインスタンス名と同じにするマク
(VPMナビゲータでProductの登録や構成変更などの経験がある方はインスタンスと参照[リファレンス]の”ロック”と同じような考えなので理解できると思います)
結論でいえば、一番最後に出てきた下記コードを書けばパーツ番号の変更が可能になります。
‘proの親プロダクトを取得
Dim parent_pro As Product
Set parent_pro = pro.Parent.Parent
‘インスタンス名を変更
parent_pro.ReferenceProduct.Products.Item(pro.Name).Name = 文字列
動けば「ヨシッ!」という場合は何も考えず上記コードを書いておけば基本的には問題ないはずです。