C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

PMillマクロの隠れ型・隠れ関数

PowerMillのマクロを作る際、参考となるものは、インストールフォルダ内に
'Macro Programming Guide' があり(日本語版はサポートに言ってもらおう!)
これと、Web上のフォーラムだけが頼りです。


'Macro Programming Guide'(以下マニュアル) によるとPowerMillマクロの
変数の型は string,int,real,bool があり、それらのコンテナとして
array,list がある(混合させるのはNG) となっています。

マニュアルには記載されていないのですが、実はもう一つ entity型がありました。
ツールパスやバウンダリ等のエンティティ用の変数の型です。
当然コンテナも entity list として利用が可能です。

先日あまりの苦しさで作ったライブラリ的なものを作成する際、entity型の
存在は何となくわかっていたのですが、利用方法が良くわかっていません
でした。 その為、指定したフォルダ内をバンバンループさせて、必要条件を
満たしたものだけを、エンティティ名のみをリストに突っ込んで返しています。
(後々の処理を考えると、エンティティそのものを返したかったのですが・・・。)


更に型のみならず、それらを扱う為の隠れ関数(マニュアルには記載されていない)
が多数存在。

例えばfilter関数(これずっと欲しかった・・・。)
第一引数はListやFolder(ArrayもOKかも)で、第二引数が抜き出す条件です。

ツールパスフォルダ内の全てのツールパスを抜き出す場合は、こんな感じです。

	entity list toolpaths = filter(folder('toolpath'), '')

全ての場合は空の文字列です。
(マニュアルにはフォルダ名を指定する場合のみ、大文字小文字を区別するので注意しろ
 と記載されているのですが、途中から仕様が変更されたようです)


これを、計算済みのツールパスのみのリストとして取得したい場合は

	entity list computedpaths = filter(folder('toolpath'), 'Computed == 1')

第二引数に条件となるものを文字列として渡すようです。


'message info' 等、マクロユーザーに表示させるためにはエンティティ型のままでは、
出来ない為、Tree上に表示させている名前を取得したいところです。

そこでまた隠し関数のextract関数の登場です。
第一引数はListやFolderで、第二引数は抜き出すパラメータ名です。
(他の言語のmapやfancみたいなもの・・・そこまではいかないですが)
先程のfilterと組み合わせると

	entity list computedpaths = filter(folder('toolpath'), 'Computed == 1')
	string list names = extract(computedpaths , 'Name') 

これで、namesには計算済みツールパスの名前が全部取得できます。
又、これは一行で書くことが出来ます。

	string list names = extract(filter(folder('toolpath'), 'Computed == 1'), 'Name') 


又、こちらでフォルダ名を取得していますが、空のフォルダ名は取得できていません
でした。
フォルダ名リストを取得する - C#ATIA

これも、get_folders関数と言う隠し関数が存在しており、あんなメンドクサイ
コードを書かずにこれだけで空フォルダも含めて、全て取得できます。

	string list dirs = get_folders('toolpath')

これらをもっと早く知りたかった。 もうこちらのライブラリ的なものが要らない
気がしてます(涙)
非常に個人的なPowerMillマクロ用ライブラリ - C#ATIA



昨日のGUIやグラフィックもそうですが、マニュアルに記載されていない関数等が
かなり多数存在しているようです。 どうもこれらは開発側が利用する為に
試験的に導入されたもののようで、動作保障が無いようです。
恐らくエラー時に動作が不安定で最悪ソフトが落ちる程度で、エラーにならない
処理での利用であれば大丈夫な気がしてます。

これらを旧Delcamさんのフォーラムには、中の人が沢山記載しており
非常に重宝しています。(特にロイドさんが)
Autodeskさんのところには登場していないようですが・・・。


追記です。
entity型はマニュアル(2017)に記載がありました。手元に全て印刷したものが
2013だったもので、すいません。
また、object型と言う型もあり、get_typename関数で型のタイプも
取得できるようです。 動的なコードが書けると言う事ですね。

更に追記です。
filter・extract・get_folders何れも、マニュアルに記載がありました。すいません

PowerMillマクロを高速化?

何時も忘れて何度もフォーラムを検索するのでメモ書きです。

何れもPowerMillの処理を高速化したり、マクロの停止を防ぐものです。

〇メッセージダイアログ
DIALOGS MESSAGE ON
DIALOGS MESSAGE OFF

〇エラーダイアログ
DIALOGS ERROR ON
DIALOGS ERROR OFF

〇エコーコマンド デバッグトレース
ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT

〇グラフィックス
GRAPHICS LOCK
GRAPHICS UKLOCK

〇GUI
GUI ON
GUI OFF

最初の2つのダイアログOFFはメニューからも可能です。
エコーコマンドのやつは、以前書きましたが効果がよくわかっていないです。
グラフィック Lock/UnLock は、3D画面側の更新を止めてしまうものです。
GUI On/Off は、ソフトを非表示にするわけではなく、通常操作している
Treeの表示をするタブから、ブラウザのタブに切り替えます。


GUIは手動(エコーコマンドから)で入力すると、通常の状態に戻せない為
PowerMillを強制終了する以外方法が無い様なので、やらない方が
良いでしょう。 (外部から操作すれば戻るかも)

グラフィックについては、Excelのマクロでも高速化の為の手法と同じだろうと
思います。 こちらも手動でも可能でツールパスの計算時間も若干短くなる
ようです。(バックグランドに投げるようなものかも・・・)


先日のライブラリっぽいものを利用し、アペンドテストを書き換えた上で
上記の物を全部利用した動画がこちらです。

味気無い・・・Autodeskの宣伝みたいになってますね。

非常に個人的なPowerMillマクロ用ライブラリ

あまりに苦しいので、ライブラリ的なものを作りました。
PowerMillのマクロは、マクロの実行ファイル(.mac)以外に拡張子「.inc」に
することでライブラリとして使用できるんです。(エライ!)

//pmill macro Kantoku_PowerMill_Library ver0.0.2
//kpl.inc
//r2017で開発

// **** フォルダ ****
//指定した組み込みフォルダからエンティティの存在するフォルダネームリスト取得
//param : DirName (string)
//param : Name (string)
//param : OptIsNew(bool) - 指定の名前のフォルダが既存にあった場合新作するか? 0_新作しない 1_新作
//param : OptIsAct(bool) - 実行後フォルダをアクティブにするか?  0_非アクティブ1_アクティブ
//return : Outstr(string) - 新たに出来たフォルダ名
function Create_Folder(string DirName , string Name , bool OptIsNew , bool OptIsAct , output string Outstr) {
	string fullname = $DirName + '\' + $Name
	string list Before = get_folders($DirName)
	
	if member($Before, $fullname) {
		if not OptIsNew {
			if OptIsAct {
				ACTIVATE FOLDER $fullname
			}
			return
		} else {
			call Get_UnOverlap_Name($fullname , $Before, $fullname)
		}
	}
	
	CREATE FOLDER $DirName ; 
	string list After = get_folders($DirName)
	string list lst= subtract($After, $Before)
	string autoname = $lst[0]
	$Outstr = basename($fullname)
	RENAME FOLDER $autoname  $Outstr
	if OptIsAct {
		ACTIVATE FOLDER $fullname
	}
}

//重複しない名前を取得
//param : Name(string) - 候補名
//param : Lst(string list) - 比較リスト
//return : Outstr(string) - 重複の無い名前
function Get_UnOverlap_Name(string Name , string list Lst , output string Outstr) {
	string tmpname = $Name
	int cnt = 0
	bool fg = 0
	do {
		$fg = member($Lst, $tmpname) 
		if not $fg {
			break
		}
		$cnt = $cnt + 1
		$tmpname = $Name + '_' + String($cnt)
	} white 1
	$Outstr = $tmpname
}

// **** エンティティ ****

//指定した組み込みフォルダからエンティティの存在するフォルダネームリスト取得
//param : DirName (string)
//param : Calculate(int) - 計算FG -1以下_全て 0_未計算のみ 1以上_計算済のみ
//param : Leval (int) - フォルダの階層 組み込みフォルダは1 指定無しは0以下
//param : RemoveLst(string list) - 除外するフォルダ名リスト
//return : Outlst(string list)
function Get_Entitys_Dir(string Dirname , int Calculate , int Level , string list RemoveLst , output string list Outlst) {
	string list paths = {}
	call Get_Entitys_Path($Dirname , $Calculate , $Level , $paths ) 
	
	string list lst = {}
	string dirtxt = ''
	foreach itm in $paths  {
		$dirtxt = basename(dirname($itm ) ) 
		if not member($RemoveLst , $dirtxt) {
			int dmy = add_last($lst, $dirtxt)
		}
	}
	int dmy = remove_duplicates($lst)
	$Outlst = $lst
}

//指定した組み込みフォルダからベースネームリスト取得
//param : DirName (string)
//param : Calculate(int) - 計算FG -1以下_全て 0_未計算のみ 1以上_計算済のみ
//param : Leval (int) - フォルダの階層 組み込みフォルダは1 指定無しは0以下
//return : Outlst(string list)
function Get_Entitys_Base(string Dirname  , int Calculate , int Level  , output string list Outlst) {
	string list paths = {}
	call Get_Entitys_Path($Dirname , $Calculate , $Level , $paths ) 
	
	string list lst = {}
	string txt = ''
	foreach itm in $paths  {
		$txt = string(basename($itm))
		int dmy = add_last($lst, $txt )
	}
	$Outlst = $lst
}

//指定した組み込みフォルダからパスリスト取得
//param : DirName (string)
//param : Calculate(int) - 計算FG -1以下_全て 0_未計算のみ 1以上_計算済のみ
//param : Leval (int) - フォルダの階層 組み込みフォルダは1 指定無しは0以下
//return : Outlst(string list)
function Get_Entitys_Path(string Dirname , int Calculate , int Level , output string list Outlst) {
	string list ents = {}
	string ent = ''
	int cal = -1
	foreach itm in folder($Dirname)  {
		$ent = pathname($itm)
		if $Level > 0 {
			if size(tokens($ent, "\")) != $Level + 1 {
				continue
			}
		}
		if $Calculate > -1 {
			switch $itm.computed {
				case 0
					if Calculate > 0 {
						continue
					}
					break
				case 1
					if Calculate == 0 {
						continue
					}
					break
			}
		}
		int dmy = add_last($ents, $ent)
	}
	$Outlst = $ents
}

// **** その他 ****
//リストから文字列
//param : Lst (string list)
//param : Delimiter (string)
//return : Outtxt(string)
function Lst_ToString(string list Lst , string Delimiter , output string Outtxt) {
	string txt = ''
	foreach itm in $Lst {
		$txt = $txt + $itm + $Delimiter
	}
	int lng = length($txt) - length($Delimiter)
	$Outtxt = substring($txt , 0 , $lng)
	return
}

//ダンプ
function Dump(string list Lst) {
	string txt = ''
	call Lst_ToString($Lst , crlf , $txt )
	print = $txt
}

//ダイアログ類オン
function DialogOn() {
	DIALOGS MESSAGE ON
	DIALOGS ERROR ON
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
}

//ダイアログ類オフ
function DialogOff() {
	DIALOGS MESSAGE OFF
	DIALOGS ERROR OFF
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
}

ver0.0.1 - 完成
ver0.0.2 - Get_Entitys_Dir関数に除外リスト追加



エンティティの名前の重複が許されない為、先日のマクロも何度も何度もリストを
取得しなくてはならない為、その辺中心に作っています。

これを利用するテストマクロがこちら。

//pmill macro クズユニットテスト

function Main() {
	call DialogOff()
	
	// **** エンティティ ****
	string list lst = {}
	print = "**path**"
	call Get_Entitys_Path('Toolpath' , 1, 0, $lst ) 
	call Dump($Lst )
	call Get_Entitys_Path('Boundary' , 1 , 0 , $lst ) 
	call Dump($Lst )
	
	print = "**base**"
	call Get_Entitys_Path('Toolpath' , 1,0 , $lst ) 
	call Dump($Lst )
	call Get_Entitys_Base('Boundary' , 1 , 0 , $lst ) 
	call Dump($Lst )
	
	print = "**dir**"
	call Get_Entitys_Dir('Toolpath' , 1 , 0 , $lst ) 
	call Dump($Lst )
	call Get_Entitys_Dir('Boundary' , 1 , 2 , $lst ) 
	call Dump($Lst )
	
	//**** フォルダ ****
	string Dirname = ''
	call Create_Folder('Toolpath' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	call Create_Folder('Toolpath' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	call Create_Folder('Boundary' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	call Create_Folder('Boundary' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	call Create_Folder('Tool' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	call Create_Folder('Tool' , 'Hoge' , 1 , 1 , $Dirname)
	print = $Dirname
	
	call DialogOn()
}

include kpl.inc

最後に記載した

 ・・・
include kpl.inc

で読み込んでます。最初じゃなくても大丈夫なんです。(エライ)
(最初だとエラーメッセージに表示された行がずれちゃうんです)

国内だとPowerMillユーザー少なそうだから、使う人いないだろうなぁ
(日本語のフォーラム作って欲しい・・・)

ダイアログオフが思った程、機能していない

上手く行かない と思っていたらこんな記述を発見。

PowerMILL 2017 Dialogs On\Off error when run macro. - Autodesk Community

要はこんな感じでコードを書いておくと、ダイアログが表示されなくなります。
・・・されなくなる、はずでした。

  DIALOGS MESSAGE OFF


困っていたのは、マクロの処理中にダイアログが出てきちゃって、マクロ自体が止まっちゃうの
です。こっちとしては処理中に他の作業を行いたいので、まとめてマクロで処理させたい
のに離れられなくなっちゃうんです。
10年ぐらいの前のバージョンのPowerMill7でも、機能していたのに・・・。

と、イロイロ探しながら悩んでいたら、マクロの記録をとったらしっかりと記載されて
いましたw


あまりに汚いコードなので、一部のみの公開です。
ダイアログが出て困っていたのは、ツールパスをアペンド(連結)させた際に出てくる
確認用のダイアログです。上手く以下なかった際の処理はこんな感じ。

コード的には

	DIALOGS MESSAGE OFF
	foreach Itm in $AppendLst {
		//アペンド処理
		EDIT TOOLPATH $CopyedName APPEND CONFIRM $Itm
	}
	DIALOGS MESSAGE ON

記録をとった上、修正したコードがこちら。

	foreach Itm in $AppendLst {
		EDIT TOOLPATH $CopyedName APPEND CONFIRM $Itm
		Yes //悩んだ末の解決策がこれだけ
	}

実際に上手く処理できるようになった動画はこちらです。

さっさと記録とれば良かった・・・。

計算済みコーナーペンシル仕上げを逆方向にする

PowerMillの機能の中でちょっとだけ不満なのが、コーナーペンシル仕上げ加工。

大体の3DCAMソフトには付いている機能なのですが、今まで経験した中で
唯一PowerMillだけ、下から上にツールパスを作成してくれません。
(使いたい理由は秘密)

サポートさんには機能をつけて欲しいと要望を出しましたが、所詮小市民の
戯言なので、期待は出来ないと思ってます。

上から下へのツールパスを手動で方向逆転させられるので、作れなくはないの
ですが、この操作がメニューの深いところにあるため非常に面倒です。

その為、マクロを作成しまとめて処理させる事にしました。

//pm2017 macro sample_All_Pencil_corner_Rev.mac
//全ての計算済みコーナーペンシル仕上げ加工の方向を逆にする

function Main() {
	//ペンシル検索
	STRING LIST PathLst = {}
	call GetPencilLIST($PathLst)
	
	//チェック
	STRING msg = ''
	if is_empty($PathLst) {
		$msg = '処理可能なペンシルパスがありません!!'
		message error $msg
		return
	}
	
	//メッセージ
	BOOL yesno = 0
	call ListToString(PathLst, $msg)
	$msg = '以下のペンシルパスを逆方向にします。宜しいですか?' + crlf + $msg
	$yesno = QUERY $msg
	IF not yesno {
		return
	}
	
	//リバース
	STRING ActPathName = TOOLPATH.NAME
	call SetReverse(PathLst)
	
	//アクティブツールパスを実行前に戻す
	call ReActToolPath($PathLst, $ActPathName)
}

//実行前アクティブツールパスに戻す
function ReActToolPath(STRING LIST lst, STRING PathName) {
	STRING ReActName = ''
	if member($lst, $PathName) {
		call GetAfterPathName($PathName, $ReActName)
	} else {
		$ReActName = $PathName
	}
	ACTIVATE TOOLPATH $ReActName
}

//変換後のツールパス名取得
function GetAfterPathName(STRING PathName, output STRING NewName) {
	STRING Key = '_Rev'
	STRING Before = $PathName
	INT RevPos = position($Before, $Key)
	STRING After = ''
	if $RevPos == -1 {
		$After = $Before + $Key
	} else {
		$After = replace($Before, $Key, '')		
	}
	$NewName = $After
}

//パス名修正
function RenamePath(STRING PathName) {
	STRING After = ''
	call GetAfterPathName($PathName, $After)
	RENAME Toolpath $PathName $After
}

//逆方向
function SetReverse(STRING LIST lst) {
	foreach itm in lst {
		ACTIVATE TOOLPATH $itm
		EDIT TOOLPATH REVERSE
		call RenamePath(itm)
	}
}

//計算済みペンシル
function GetPencilLIST(output STRING LIST Out_lst) {
	STRING LIST lst = {}
	foreach itm in folder('Toolpath') {
		if itm.computed {
			if itm.Strategy == 'pencil_corner' {
				INT dmy = add_last(lst, itm.name)
			}
		}
	}
	INT dmy = remove_duplicates(lst)
	$Out_lst = lst
	return
}

//リストの文字化
function ListToString(STRING LIST lst, output STRING Out_msg) {
	STRING msg = ""
	foreach itm in lst {
		$msg = msg + itm + crlf
	}
	$Out_msg = msg
}

ツリーのツールパスフォルダ内の計算済みコーナーペンシル仕上げ加工を全て探し出し、
逆転させます。その際、ツールパスの名称を逆転させたものには、
"ツールパス名" + "_Rev" の名称に変更し、再度実行した際は "_Rev"
の名称を削除します。
(手動でゴチョゴチョした際は、もちろんそうなりません・・・)

実際に使用した感じです。 最初に通常に作成したシミュレーションを
実行し、マクロ実行後のシミュレーションを実行してみました。


"Macro Programming Guide" に計算済みツールパスを判断するためのサンプル
が記載されているのですが、パラメータ部分が

IF entity(‘toolpath’,Name) .Calculated {

となっているのですが、正しくは

IF entity(‘toolpath’,Name).computed {

です。 参考に出来るものが少ないだけに誤記は勘弁して欲しい。
(他にも幾つかあります)

PowerMillに同位置の作業平面を作る

加工する際、世の中の方々は加工原点をどうやって決めているんでしょうか?
部品の原点が決まっているようであれば、そこを原点にするのかも知れない
のですが、基準まで加工して作る場合は・・・バイスを使用していたら材料の
上側の左奥(か、右奥・・・わかりにくいですね)になるのだろうと思いますが、
社内で使用している治具の関係で、うちの場合は材料底面の中心部分にする
と言う、暗黙なルールが見え隠れしています。

こんな感じです。
f:id:kandennti:20170210190831p:plain

悩むのが、CADの原点と加工原点が違う際にどうするのか?
世間の皆様はどうしているのでしょう???
恐らくCAM側で加工原点を作成して作業をするのだろうと思うのですが、
個人的にPowerMillの作業平面(加工原点)の操作が、思ったより
やりにくい・・・(うちが材料の中心部にしているせいです)

なので、今まではCATIAで座標変換させてIgesエクスポートさせていたのですが、
後々に確認したい場合に結構不便で困っていました。



PowerMillのマクロは独自言語なのですが、外部からマクロの実行が出来ます。
VBAでも出来ちゃうので、CATIAの座標系情報を元にPowerMillの作業平面を
作成してしまおう! と思いつき、マクロを作ってみました。

'vba sample_PM_Push_AxisSystem Ver0.0.1
'using-'KCL0.0.10' 'PowerMill'
Option Explicit

Sub CATMain()
    'ドキュメントのチェック
    If Not CanExecute("PartDocument") Then Exit Sub
    
    'パワーミル取得
    Dim Pm As Object: Set Pm = GetPowerMill()
    If KCL.IsNothing(Pm) Then
        MsgBox "パワーミルを起動して下さい!"
        Exit Sub
    End If
    
    'エクスポートする座標系選択
    Dim Axis As AxisSystem
    Set Axis = KCL.SelectItem("座標系を選択して下さい", "AxisSystem")
    If KCL.IsNothing(Axis) Then Exit Sub

    '座標系データ取得
    Dim AxisCoord As Variant: AxisCoord = GetAxisCoord(Axis)
    
    'パワーミル 作業平面作成
    Call CreateWorkPlane_Pm(Pm, AxisCoord)
End Sub


' *** Catia ***
'座標系データ取得
Private Function GetAxisCoord(ByVal Axis As AxisSystem) As Variant
    Dim Ax: Set Ax = Axis
    Dim Ori(2), VecX(2), VecY(2)
    Call Ax.GetOrigin(Ori)
    Call Ax.GetVectors(VecX, VecY)
    
    GetAxisCoord = Array(Axis.Name, _
                         Ori(0), Ori(1), Ori(2), _
                         VecX(0), VecX(1), VecX(2), _
                         VecY(0), VecY(1), VecY(2))
End Function


'*** PowerMill ***
'パワーミル取得
Private Function GetPowerMill() As Object
    On Error Resume Next
        Set GetPowerMill = GetObject(, "PMILL.Document")
    On Error GoTo 0
End Function

'ダイアログ類オン
Private Sub DialogOn_Pm(ByVal Pm As Object)
    With Pm
        Call .Docommand("DIALOGS MESSAGE ON")
        Call .Docommand("DIALOGS ERROR ON")
        Call .Docommand("ECHO ON DCPDEBUG TRACE COMMAND ACCEPT")
    End With
End Sub

'ダイアログ類オフ
Private Sub DialogOff_Pm(ByVal Pm As Object)
    With Pm
        Call .Docommand("DIALOGS MESSAGE OFF")
        Call .Docommand("DIALOGS ERROR OFF")
        Call .Docommand("ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT")
    End With
End Sub

'パワーミルに作業平面作成
Private Sub CreateWorkPlane_Pm(ByVal Pm As Object, ByVal Axcoord As Variant)
    Call DialogOff_Pm(Pm)
    
    With Pm
        '新エンティティ名
        Call .Execute("string NewName = new_entity_name('Workplane')", 1)
        
        '作業平面作成・編集開始
        Call .Execute("CREATE WORKPLANE ; EDITOR", 1)
        Call .Execute("ACTIVATE WORKPLANE $NewName", 1)
        Call .Execute("MODE WORKPLANE_EDIT START $NewName", 1)
        
        'リネーム
        Dim ReName$: ReName$ = Get_NewWPName_Pm(Pm, Axcoord(0))
        If Not ReName$ = vbNullString Then
            Call .Execute("MODE WORKPLANE_EDIT NAME """ & ReName & """ ", 1)
        End If
    
        '原点
        Call .Execute("MODE WORKPLANE_EDIT POSITION", 1)
        Call .Execute("MODE POSITION WORKSPACE WORLD", 1)
        Call .Execute("MODE POSITION PLANE XY", 1)
        Call .Execute("MODE POSITION CARTESIAN X """ & CStr(Axcoord(1)) & """ ", 1)
        Call .Execute("MODE POSITION CARTESIAN Y """ & CStr(Axcoord(2)) & """ ", 1)
        Call .Execute("MODE POSITION CARTESIAN Z """ & CStr(Axcoord(3)) & """ ", 1)
        Call .Execute("POSITION APPLY", 1)
        
        'X軸
        Call .Execute("MODE WORKPLANE_EDIT DIRECTION X", 1)
        Call .Execute("MODE DIRECTION WORKSPACE WORLD", 1)
        Call .Execute("MODE DIRECTION COMPONENT I """ & CStr(Axcoord(4)) & """ ", 1)
        Call .Execute("MODE DIRECTION COMPONENT J """ & CStr(Axcoord(5)) & """ ", 1)
        Call .Execute("MODE DIRECTION COMPONENT K """ & CStr(Axcoord(6)) & """ ", 1)
        Call .Execute("DIRECTION ACCEPT", 1)
        
        'Y軸
        Call .Execute("MODE WORKPLANE_EDIT DIRECTION Y", 1)
        Call .Execute("MODE DIRECTION WORKSPACE WORLD", 1)
        Call .Execute("MODE DIRECTION COMPONENT I """ & CStr(Axcoord(7)) & """ ", 1)
        Call .Execute("MODE DIRECTION COMPONENT J """ & CStr(Axcoord(8)) & """ ", 1)
        Call .Execute("MODE DIRECTION COMPONENT K """ & CStr(Axcoord(9)) & """ ", 1)
        Call .Execute("DIRECTION ACCEPT", 1)
        
        '編集終了
        Call .Execute("MODE WORKPLANE_EDIT FINISH ACCEPT", 1)
    End With
    
    Call DialogOn_Pm(Pm)
End Sub

'重複を避けた作業平面名を取得
Private Function Get_NewWPName_Pm(ByVal Pm As Object, ByVal Name As String) As String
    Get_NewWPName_Pm = Name
    
    If Name = vbNullString Then Exit Function
    
    Dim Ret, CallBackMsg$
    With Pm
        Call .Docommand("STRING LIST $lst={}")
        Ret = .Execute("$lst = extract(folder('Workplane'), 'name')", 1)
        Ret = .ExecuteEx("PRINT PAR '$lst'", 0, CallBackMsg)
    End With
    
    Dim WPNameDic As Object: Set WPNameDic = Get_WPNameList_Pm(CallBackMsg)
    If WPNameDic.Count < 1 Then Exit Function
    
    Dim TmpName$: TmpName = Name
    Dim Count&: Count = 0
    Do
        If Not WPNameDic.Exists(TmpName) Then Exit Do
        Count = Count + 1
        TmpName = Name & "_" & CStr(Count)
    Loop
    
    Get_NewWPName_Pm = TmpName
End Function

'パワーミルの作業平面名辞書
Private Function Get_WPNameList_Pm(ByVal Txt As String) As Object
    Set Get_WPNameList_Pm = Nothing
    Const Key = "(STRING) "
    Dim KeyLng: KeyLng = Len(Key)
    
    Dim Ary: Ary = Split(Txt, vbNewLine)
    Dim Dic As Object: Set Dic = KCL.InitDic()
    
    Dim i&, KeyIdx&
    For i = 0 To UBound(Ary)
        KeyIdx = InStr(Ary(i), Key)
        If KeyIdx > 0 Then
            Call Dic.Add(Mid(Ary(i), KeyIdx + KeyLng), 1)
        End If
    Next
    
    Set Get_WPNameList_Pm = Dic
End Function

CreateWorkPlane_Pm関数が、ビックリするぐらい酷い状況・・・。
結果的に、GetObjectでPowerMillを所得しマクロのコードを垂れ流しです。



実際に試してみた動画がこちら
(予め、CADデータはインポートしてあります)

作業平面を作る過程を止める事も出来るようですが、動画に箔がつくのでそのままです。
本当は、作成した作業平面をアクティブにしたかったのですが・・・上手く行ってません。
それでも便利です。 悩みが一個解消しました。


忘れちゃいそうなので、覚書としてのコード的な部分です。

外部からの操作で、関数の戻り値が取得できるものかどうか疑問でしたが、
フォーラムの記載を参考にしました。 作業平面名の重複を避けるため作業平面フォルダ
から全作業平面名を取得する必要が有りました。
Get_NewWPName_Pm・Get_WPNameList_Pm関数でその辺の処理をしています。

こちらのコードはエコーコマンドの表示を止めるもののようで、
中にはこの表示を止めないとマクロが上手く動作しないものがあるようです。
(よくわかっていないのですが、エラーがらみだと思います)

ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT

ところが逆のエコーコマンドに表示させる為のコードが、探しても探しても
見つかりませんでした。 何となく感で打ち込んだところ
こちらで再表示させられるようになりました。

ECHO ON DCPDEBUG TRACE COMMAND ACCEPT

ひょっとしたら何処かに記載されているかも。

フォルダ名リストを取得する

想像以上に苦しく、進まない PowerMillマクロです。
でも、メンドクサイ類似作業の繰り返しはコリゴリなので、進めてます。

以前はメモ帳で作っていたのですがw AutodeskのForumでこんな記述を発見。
Notepad++ Language for editing PowerMill macros. - Autodesk Community

Notepad++と言うエディタの、PowerMillマクロ言語用のものがあるなんてありがたいです。
確かにマニュアルにもチラチラ出てくる名前のエディタなので、使おうと思ったのですが
日本語が未対応っぽい。

探すとこちらがHitしました。
Notepad++ ja-pack 1 (日本語パック) を公開してみた。 | OFF-SOFT.net

ん~よく読んでないせいか、画面が日本語になっていない・・・。 "Shift-JIS" は
使えているので、まぁこのままで行きます。



で、本題の方ですが、僕の場合Treeが長くなるのがイヤなので、
ツールパスや工具フォルダ内にフォルダを作って作業してます。 こんな感じです。
f:id:kandennti:20170209191356p:plain

面倒なんですが、ダッダーっと長いのはイヤなんです。 何れはマクロやテンプレートで
作るし。

後々に利用する事になりそうなので、組み込みフォルダ内のフォルダ名リストを取得する
マクロです。

//pm2017 macro
//フォルダ名取得 クズテスト

function Main() {
	STRING LIST lst = {}
	//ツールパス
	call GetFolderNameLIST('Toolpath', $lst)
	call Dump(lst)
	
	//工具
	call GetFolderNameLIST('Tool', $lst)
	call Dump(lst)
}

//指定した組み込みルートフォルダ内のフォルダ名を取得
function GetFolderNameLIST(STRING Lst_name, output STRING LIST Folder_lst ) {
	STRING LIST lst = {}
	foreach itm in folder(Lst_name) {
		INT dmy = add_last(lst, basename(dirname(pathname(itm))))
	}
	INT dmy = remove_duplicates(lst)
	$Folder_lst = lst
	return
}

function Dump(STRING LIST lst) {
	STRING msg = ""
	foreach itm in lst {
		$msg = msg + itm + crlf
	}
	print = msg
}


エコーコマンドで出力させてます。 結果はこんな感じ。

 ・・・

Process Command : [\tprint = msg\n]

r2s_base
r1s_base
r05s_base

 ・・・

Process Command : [\tprint = msg\n]

path
ref

 ・・・

ん~ 理由はわかっているのですが、空のフォルダ名が取得できていないです。
ネストしているフォルダ名も実は取れないんですけど、それは禁止にしよう・・・。