Scripting, Customization and Automation using Tcl scripts in Creo
p-Shell is a Creo Toolkit Application which runs synchronous or a-synchronous with Creo 4.0 - 10.0 as a Binary Tcl Extensions, and enables you to access Creo Files and their Database via Tcl commands within a running Tcl/Tk environment.
This API is not a PowerShell or JSON interface, where a server processes is required. It is a true mapping of Creo Toolkit calls in C for Tcl. The only script base interface to Creo Parametric with direct communication.
This pages shows some of many p-Shell built-In commands. This allows scripting in Creo for administrative or automation jobs and application development.
p-Shell Environment is available for Tcl/Tk 8.6
A console in synchronous mode is available to test interactive p-Shell commands before using it in a program, or to execute admin tasks. In a-synchronous mode, start wish, load the dll, connect to Creo (see further below, how to do) and start typing the commands, and of course by sourcing Tcl files.
You can develop your application without restarting Creo over and over, source your code again, and simply validate your changes! Or evaluate commands in a shell before using.
More info: Tcl4Creo (at) gmail (dot) com or view YouTube Channel
This section shows how to work with parameter/attributes in Creo using the p-Shell command 'ps_param'
Sample: Get the list of all parameter in a Creo Model. Simple output or export of all parameter within the current Creo CAD model to a csv file.
# write all parameter from a given model # into a csv file with the name $model.csv like box.prt.csv # if delimeter is not given, use “,” proc ExportModelParameter {model {del ,} } { # open the file for writing set fp [open $model.csv w] # write the header puts $fp [join [list Name Type Value] $del] # sorted param names output, get all parameter from the given model foreach paramObj [lsort -dictionary [ps_param list -model $model] ] { # from the paramObj get name, value and the type set nam [$paramObj cget -name ] set val [$paramObj cget -value ] set typ [$paramObj cget -type ] # Write name, type and value into the file puts $fp [join [list $nam $typ $val] $del] } # close the file and return close $fp } # # call the export function for models names where the pattern match # # Get parameter for each Part model in session # proc ExportParameter {pattern} { # Loop through all files found by the given pattern foreach model [ps_glob $pattern] { # Call to export the parameter from one model ExportModelParameter $model } } # # Call the export procedure with the pattern “*.prt” # ExportParameter *.prt exit
Or by a button call (without exit of course)
pack [button .pexp -text „App Exp“. -command [list ExportParameter *.prt]]
Sample: Create new parameter (or modify if exists) and change/set the value
# # Create a parameter # set paramCmd [ps_param set MY_Param String {Hello World} ] # # Configure the value # $paramCmd configure -value "Hello wiki.tcl.tk" -designate Yes -description "Don't change me manually" # #Create two string parameter at the first feature with the ID 1 # ps_modelitem miObj -model box.prt -type Feature -id 1 set paramObjs [ps_param set -at_item miObj -- [list MY_Param1 MY_Param2] String {Unknown} ]
Show some commands and usage. Assume the parameter name is 'IDENT' and the final value should be "A-123-K-009", enable export to Windchill.
Note: Range and restricted values are also implemented.
# # Set a parameter by name, type and value # Make sure we have a parameter which is a STRING # # Check if exists # if ![ps_param exists IDENT] { # # Okay create it # set paramCmd [ps_param set IDENT String "This is a Identnumber, please change me." ] } else { # # Paramter exists. Is parameter a STRING type ? # If locked create an error # set paramCmd [ps_param list IDENT] if [$paramCmd islocked] { error "Error: Parameter IDENT is locked" } if ![string equal [$paramCmd cget -type] STRING ] { # # Wrong type # # Get val and create a string type # set val [$paramCmd cget -value] # # Delete the param # $paramCmd delete # # Create as a STRING type with old value # set paramCmd [ps_param set IDENT String $val ] } } # # Change to the final value and enable with designate the export to Windchill # $paramCmd configure -value "A-123-K-009" -designate Yes -description "This Parameter will hold the Article Number"
Create a Component Parameter (Name PS_TEST_PARAMETER as a String with the Value 'TEST' at a selected component
ps_sel selObj -modelitem miObj selObj select part ps_param set -at miObj PS_TEST_PARAMETER String TEST
Console Help
ps_dim -help Options for ps_dim are: model exists DimID by_feat_ids FeatIDs from_ids DimIDs list thickness set ID_Val_List -help
An DimObj is created by calling
# get all from an ID, with a call to ‘ps_dim list’ upfront to loop through ID, Symbol, Value # you can give a pattern as well by just calling ‘ps_dim list -pattern hei*’ for example ps_dim from_id 1 # or get all dims from the feature with the ID 5 ps_dim by_feat_id 5 # the standard, to get all dim Objects from a Model is ps_dim model # to specify a different model use the -model option eq. from the motor.asm use ps_dim model -model motor.asm # for the ID or Feature Id ps_dim from_id -model box.prt —- 1 # or ps_dim by_feat_id -model motor.asm —- 5
This will return an dimObj command like ::PS::BOX.PRT.DIM.D0
Other object specific commands:
Options for ::PS::BOX.PRT.DIM.D0 are: cget Option configure ?Option Value? reset model exists show noshow refresh drawing position move pointObj -help
To get the current dimension value call:
set value [::PS::BOX.PRT.DIM.D0 cget -value]
Read Write (RW) for cget and configure or Read Only (RO, e.g. you can’t change the type) options for cget only.
Other cget/configure options follow here:
::PS::BOX.PRT.DIM.D0 configure may return: {-value Value RW-Dim 0.0 360.0} {-bck_value BackupValue RO-Dim 0.0 360.0} {-symbol Symbol RW-Dim {} d0} {-is_ref_dim IsRefDim RO-Dim 0 0} {-is_iso_din ModelIsIsoDIN RO-Dim 1 0} {-type Type RO-Dim {} ANGLE} {-id DimID RO-Dim -1 0} {-featid FeatID RO-Dim 0 12} {-user_data UserData RW-Parameter {} {Model not ISO/DIN}} {-tolerance Tolerance RW-Dim {} {0.500000 0.500000}} {-symtext SymbolText RO-Dim {} {{{0:@D}}}} {-prefix DimPreFix RW-Dim {} {}} {-suffix DimSufFix RW-Dim {} {}} {-decimals Decimals RW-Dim 2 2} {-denominator Denominator RW-Dim -1 0} {-can_regen_driven_dim CanRegenDrivenDim RO-Dim -1 0} {-is_regen_negativ IsRegenNegativ RO-Dim -1 0} {-is_rel_driven IsRelationDriven RO-Dim -1 0} {-is_fractional IsFractional RO-Dim -1 0} {-inspection Inspection RW-Dim -1 0} {-basic Basic RW-Dim -1 0} {-comppath asmCompPath RW-AsmCompPath {} {}} {-view_id ViewID RW-Dim -1 -1} {-drawing Drawing RW-Dim {} {}} {-nominal NominalValue RO-Dim 0.0 360.0} {-bound Bound RW-Dim NOMINAL NOMINAL} {-label TolLabel RW-Dim {} {Model not ISO/DIN}} {-text Text RW-Dim {} {{{0:@D}}}}
Some sample code
# Just Do It for the current active model # with hardcoded seperator # export Id, symbol and value to csv proc QuickAndDim {} { set out [file join [ps_pwd] Dim-[ps_model cur].csv] set fp [open $out w] foreach item [ps_dim list] { puts $fp [join $item ,] } close $fp } # Call it QuickAndDim # with some note and a file header proc SimpleOut {model {sep {,}} } { # prepare the output filename # use the current working dir of Creo get by 'ps_pwd' # # and use the model name within the file name # if our current folder is c:/test and the model name is box.prt # out would be c:/test/SimpleDim-box.prt.csv # set out [file join [ps_pwd] SimpleDim-$model.csv] # open the file for write set fp [open $out w] # prepare a header, if not skip the next 2 lines of code # could be done in one line as well set COLS [list DimID DimSymbol DimValue] puts $fp [join $COLS $sep] # ps_dim list will return a list, each list item has 3 information # the dimension ID, the symbol name, and the value # e.g. # ps_dim list will return {6 d6 4.25} {10 boxlen 60.0} ... foreach item [ps_dim list -model $model] { puts $fp [join $item $sep] } # close the open file close $fp } # Call it with the current model SimpleOut [ps_model current] # # export only dims for active features # Note: The pattern counter value (integer) is not a dimension # # FeatID,DimID,DimSymbol,DimValue # 39,2,d2,32.0 # 39,3,d3,66.0 # 39,5,d5,7.5 proc ActiveOut {model {sep {,}} } { # prepare the output filename # use the current working dir of Creo get by 'ps_pwd' # # and use the model name within the file name # if our current folder is c:/Creo/Work and the model name is box.prt # out would be c:/Creo/Work/SimpleDim-box.prt.csv # set out [file join [ps_pwd] ActiveSimpleDim-$model.csv] # open the file for write set fp [open $out w] # prepare a header, if not skip the next 2 lines of code # could be done in one line as well set COLS [list FeatID DimID DimSymbol DimValue] puts $fp [join $COLS $sep] # Get all active feature IDs, skip suppressed ... set ActiveFeats [ps_feat list -model $model -active true] # this will return a nested list # foreach ActiveFeats ID a list of dims or an empty list set dimObjLists [ps_dim by_feat -model $model -- $ActiveFeats] # now check the list for each feat id foreach dimObjList $dimObjLists ActiveFeat $ActiveFeats { # if we have no dim for this feature continue if {[llength $dimObjList] == 0} continue # walk through the list of dimObj foreach dimObj $dimObjList { # get id, symbol and value set id [$dimObj cget -id] set sym [$dimObj cget -symbol] set val [$dimObj cget -value] # write to file puts $fp [join [list $ActiveFeat $id $sym $val] $sep] } } close $fp } # Call it with the current model ActiveOut [ps_model cur] # # export only a dimension value from type diameter # add tolernce values and inspection basic info # sample out (of course out is without the '#') # Symbol,Value,TolL,TolH,Inspection,Basic # d2,32.000,0.005334,0.005334,No,No # d12,5.334,0.005334,0.005334,No,No # d27,10.667,0.005334,0.005334,No,No proc SimpleByType {model {sep {,}}} { set B(0) No set B(1) Yes set dims [ps_dim list] set out [file join [ps_pwd] dim-info-by-type--$model.csv] set fp [open $out w] puts $fp "Symbol,Value,TolL,TolH,Inspection,Basic" foreach item $dims { # assign id sym val to a var from the list item foreach {id sym val} $item break # get the dim Object handle set dimObj [ps_dim from_ids -model $model -- $id] if {[string equal [$dimObj cget -type ] DIAMETER ] } { # get the tolerance set tol [$dimObj cget -tolerance ] # assign lower, upper value set toll [lindex $tol 0 ] set tolu [lindex $tol 1 ] #export if inspection or basic set insp [$dimObj cget -inspection ] set basic [$dimObj cget -basic ] set data [join [list $sym [format %.3f $val] $toll $tolu $B($insp) $B($basic)] $sep] puts $fp $data } } # close the file close $fp } # # Call it with the current model # SimpleByType [ps_model cur] # to call in a loop for each prt model # use ‘set PartModels [ps_glob *.prt]’ and use ‘foreach’ like: foreach prtmdl [ps_glob *.prt] { SimpleOut $prtmdl }
The first image on top is from Creo, the bottom one is done by the script below.
# SimpleAttrViewr -- # # Most of this code is from tree demonstration script # AttrDataInsert contains the code to get model parameter # and fill the data into the tree widget # proc AttrInit {} { set cols {name type valu desi desc} set names {Name Type Value Designate Description} set tree [AttrBuild .mclist $cols $names] set model [ps_model cur] AttrDataInsert $tree $cols $model } proc AttrDataInsert {tree cols model} { set rows [list] foreach paramObj [lsort -dictionary [ps_param list -model $model] ] { set name [$paramObj cget -name ] set valu [$paramObj cget -value ] set type [$paramObj cget -type ] set desi [$paramObj cget -desi ] set desc [$paramObj cget -descr ] lappend rows [list $name $type $valu $desi $desc] } # Instead of 0/1 display True/False set DES(1) True set DES(0) False foreach row $rows { foreach $cols $row {} $tree insert {} end -values [list $name [string totitle $type] $valu $DES($desi) $desc] } } # # Code for sort removed # to make it easier to read # proc AttrBuild {tl cols names} { set w $tl catch {destroy $w} toplevel $w wm title $w "p-Shell Multi Colums Parameter List" wm iconname $w "mclist" ttk::frame $w.container ttk::treeview $w.tree -columns $cols -show headings -yscroll "$w.vsb set" -xscroll "$w.hsb set" ttk::scrollbar $w.vsb -orient vertical -command "$w.tree yview" ttk::scrollbar $w.hsb -orient horizontal -command "$w.tree xview" pack $w.container -fill both -expand 1 grid $w.tree $w.vsb -in $w.container -sticky nsew grid $w.hsb -in $w.container -sticky nsew grid column $w.container 0 -weight 1 grid row $w.container 0 -weight 1 # Configure the header foreach col $cols name $names { $w.tree heading $col -text $name -image noArrow -anchor w $w.tree column $col -width 40 } # return the tree widget return $w.tree } # # Call the Init proc # AttrInit
Note: Working with dimensions is similar, a dimObj will contain more info (Tolerance, Tolerance Label, owning FeatId, the value,...) which can be extracted with the 'cget' option.
Each p-Shell command follows the same rule, here a short explanation.
The task: For the drawing drawing.drw set the format std_a3.frm at sheet number 1.
ps_draw set_format -drawing drawing.drw -- std_a3.frm 1
Output of 'ps_draw -help' list_models ?Pattern? set_format FormatName ?SheetNumber? ?FormatSheetNumber? format_get matrix SheetNumber matrixObj overlay_view SheetNumber views scale SheetNumber ?Value? add_model delete_model regen_draft display_status View Option ?Value? replace sourceModel targetModel ?unrepresent_ok? add_view positionPoint orientationMatrix Scale SheetNumber ?exploded? add_project_view positionPoint ParentView ?exploded? create_from_template templateName newName ?Display? notes current ?NewCurrentModel? srep_add_asm SREP_Name getsize setsize Size ?xval yval? detail_option_get Option ?NewValue? format_is_shown hide_format show_format -help'
If your active model is the requested drawing.
ps_draw set_format std_a3.frm 1
If the drawing is not active but in session.
ps_draw set_format -drawing drawing.drw -- std_a3.frm 1
You can omit --
ps_draw set_format -drawing drawing.drw std_a3.frm 1
Note: Each command has a '-help' option.
Short example to create a "Save Button" (file save) for the current active model in Creo. The label contains the name of the active model.
ps_model current will return the name of the active model, e.g. K-AB-123.PRT, or an empty string if no model is active.
ps_file save will save the active model to disk, or ps_file save box.drw will save the drawing 'box.drw' to disk.
pack [button .mdlsave -text "Save [ps_model current]" -command {ps_file save} ]
Short example show how to walk through an assembly, and get data from a Csys.
The whole working code within less than 40 lines of code (excludes comments).
#---------------------------------------------------------------------- # # Get_Assy_Csys_Match -- # # Root command to get csys data from an assembyl. # This will walk through the given assembyl to find # the required csys # # Parameters: # FP the handle to an open file # ASM the assembyl name # Pat the pattern to match, e.g A* match ACSYS # #---------------------------------------------------------------------- proc Get_Assy_Csys_Match {FP ASM PAT} { # Get it for the assembyl as well Get_Solid_Csys_Match $FP $ASM $PAT # Vist the given assembly, get all feature IDs # where the feature type is a component foreach FID [ps_visit type -model $ASM -- COMPONENT] { # Get the model name from this feature ID set MDL [ps_assy component_name -model $ASM -- $FID ] # Now get all Geom ID from each CSYS in this Solid # No filter for sub types like skeleton, ... Get_Solid_Csys_Match $FP $MDL $PAT # Recursive Call # If the model is an assembly, # search in this assembly as well if { [ps_model is_assembly -model $MDL]} { Get_Assy_Csys_Match $FP $MDL $PAT } } # Done and return return } #---------------------------------------------------------------------- # # Get_Solid_Csys_Match -- # # Root command to get csys data from the given solid. # First get a list of all geom IDs # From this geom ids get the feature IDs # From the feature get the name and chack for the pattern match # if the patter match, extract all data without a transformaion # to the top level assembly # a model item can be used for this request as well # # Parameters: # FP the handle to an open file # MDL the assembyl or part name # Pat the pattern to match, e.g A* match ACSYS # #---------------------------------------------------------------------- proc Get_Solid_Csys_Match {FP MDL PAT} { # This will visit a given model and return all Csys IDs # you can do this as well by type and get the feature ID # like this -> ps_visit type csys # but for extracting the csys data we need the geom id # set GEOM_IDS [ps_visit csys -model $MDL] # This is a one to one relation faster if we have a list of csys set FEAT_IDS [ps_geom to_feat_ids -model $MDL -- CSYS $GEOM_IDS] foreach GEOM_ID $GEOM_IDS FEAT_ID $FEAT_IDS { # Get the Feature Name set FEAT_NAME [ps_feat name -model $MDL -- $FEAT_ID] # Now the Match check # Here do a match of the csys name by the given pattern # if not match, continue if {![string match -nocase $PAT $FEAT_NAME]} { continue } # Prepare the Output set Record [list $MDL $FEAT_NAME] # Get the Csys Data to an csysObj ps_data csys -model $MDL -- $GEOM_ID CsysData # extract each vector, skip origin and other data foreach vector [list xvector yvector zvector origin] { # from the csysObj extract the 3D point data to an pointObj CsysData $vector Point # lappend the 3D Values by expanding to the output record list lappend Record {*}[Point 3d_values] } # write the data in our file, use ',' as an seperator puts $FP [join $Record ,] } } proc CSYS_Test {} { # The Pattern to search for set PAT A* # use the current active Model set MODEL [ps_model cur] # Prepare the filename set outname [format "%s_csys_data.csv" $MODEL] # open the file set FP [open $outname w] # write the header, use ',' as an seperator puts $FP [join [list Model CsysName xvecx xvecy xvecz yvecx yvecy yvecz zvecx zvecy zvecz ovecx ovecy ovecz] ,] # get the data Get_Assy_Csys_Match $FP $MODEL $PAT # close the file close $FP # Done and return return } # Just call it CSYS_Test
Provide a Callback menu in Creo.
Based on the installation, there is a 'program' folder, each folder may provide a Tcl Callback program if you place a 'pshell_init.tcl' file within this folder. By this, a simple copy of a folder into your program structure will enable the Tcl Scripts to run in Creo.
Sample 'pshell_init.tcl' file, argv0 is the path to the init file. The 'program' folder are parsed once, recursively on Creo Start. The command may supply additional arguments.
proc PShell_Init {dir} { ps_auto_menu add -root {My Attributes} \ -text {Model Attributes} \ -command [list $dir/MyAttributes.tcl] } PShell_Init [file dirname $argv0]
If the user select now the menu "Tools->My Attributes->Model Attributes" in Creo the file '$dir/MyAttributes.tcl' in the same dir of the 'pshell_init.tcl' file gets sourced an executed. Note: All required files for Creo (menu files) will be created automatically on run time. To end the Tcl program call 'exit', which will only end the running Tcl program.
Create a feature (tree) in Creo. Assume you would like to create a datum point as an intersection of a plane with one axis.
# First get the feature tree by creating the feature by yourself. # Assume Feature ID 56 is a Datum Point created as an intersection of a plane with an axis ps_ftree ftObj -id 56 ftObj get
This is the output of the 'get' command
.1 -type FEATURE_TYPE -map_integer DATUM_POINT .2 -type DPOINT_TYPE -integer -1 .3 -type STD_FEATURE_NAME -wstring APNT12 .4 -type DPOINT_POINTS_ARRAY -array .4.1 -type DPOINT_POINT -compound .4.1.1 -type DPOINT_POINT_NAME -wstring APNT12 .4.1.2 -type DPOINT_PLA_CONSTRAINTS -array .4.1.2.1 -type DPOINT_PLA_CONSTRAINT -compound .4.1.2.1.1 -type DPOINT_PLA_CONSTR_REF -selection ::PS::FTREE::SEL.1 .4.1.2.1.2 -type DPOINT_PLA_CONSTR_TYPE -integer 0 .4.1.2.1.3 -type DPOINT_PLA_CONSTR_VAL -double 0.0 .4.1.2.2 -type DPOINT_PLA_CONSTRAINT -compound .4.1.2.2.1 -type DPOINT_PLA_CONSTR_REF -selection ::PS::FTREE::SEL.2 .4.1.2.2.2 -type DPOINT_PLA_CONSTR_TYPE -integer 0 .4.1.2.2.3 -type DPOINT_PLA_CONSTR_VAL -double 0.0 .4.1.3 -type DPOINT_DIM_CONSTRAINTS -array
Now create your own
# Create the Object, give the feature and the point a name 'PSPNT1' ps_ftree ftObjDtmPnt # # if a selection is inserted and the selection does not exists # the selObj will be created # here in this sample ::PS::FTREE::SEL.Axis and ::PS::FTREE::SEL.Surface # ftObjDtmPnt insert .1 -type FEATURE_TYPE -map_integer DATUM_POINT ftObjDtmPnt insert .2 -type DPOINT_TYPE -integer -1 ftObjDtmPnt insert .3 -type STD_FEATURE_NAME -wstring PSPNT1 ftObjDtmPnt insert .4 -type DPOINT_POINTS_ARRAY -array ftObjDtmPnt insert .4.1 -type DPOINT_POINT -compound ftObjDtmPnt insert .4.1.1 -type DPOINT_POINT_NAME -wstring PSPNT1 ftObjDtmPnt insert .4.1.2 -type DPOINT_PLA_CONSTRAINTS -array ftObjDtmPnt insert .4.1.2.1 -type DPOINT_PLA_CONSTRAINT -compound ftObjDtmPnt insert .4.1.2.1.1 -type DPOINT_PLA_CONSTR_REF -selection ::PS::FTREE::SEL.Axis ftObjDtmPnt insert .4.1.2.1.2 -type DPOINT_PLA_CONSTR_TYPE -integer 0 ftObjDtmPnt insert .4.1.2.1.3 -type DPOINT_PLA_CONSTR_VAL -double 0.0 ftObjDtmPnt insert .4.1.2.2 -type DPOINT_PLA_CONSTRAINT -compound ftObjDtmPnt insert .4.1.2.2.1 -type DPOINT_PLA_CONSTR_REF -selection ::PS::FTREE::SEL.Surface ftObjDtmPnt insert .4.1.2.2.2 -type DPOINT_PLA_CONSTR_TYPE -integer 0 ftObjDtmPnt insert .4.1.2.2.3 -type DPOINT_PLA_CONSTR_VAL -double 0.0 ftObjDtmPnt insert .4.1.3 -type DPOINT_DIM_CONSTRAINTS -array # # Ask the user for the axis and the surface # you can also manually configure the selection obj to specify the axis and the surface # Here to make it easy ask the user ::PS::FTREE::SEL.Axis select axis ::PS::FTREE::SEL.Surface select surface # Now create the datum point ftObjDtmPnt create # get the feature ID set FID [ftObjDtmPnt cget -id] # now change the selected plane by asking again ::PS::FTREE::SEL.Surface select surface # Now create the datum point by using the new plane # with the same feature tree ftObjDtmPnt create # you can also configure all elements # here we change the selection ftObjDtmPnt itemconfigure .4.1.2.1.1 -selection ::PS::FTREE::SEL.NewAxis # now get the new axis :PS::FTREE::SEL.NewAxis select axis # Now redefine the datum point by using the new axis # with the same feature tree ftObjDtmPnt redefine
Get all mass properties from an active assembly and all sub components, traverse (walk through) and use the first CSYS or the system default, to calculate the mass prop data.
proc PS_Mass_Prop_Assy {} { # Get current model set CurModel [ps_model cur] set ext [ps_model ext -model $CurModel] if {![string equal $ext ASM]} { Debug "Expect an assembly 'ASM' but got $CurModel '$EXT'" return } PS_Mass_From_Model $CurModel PS_Mass_Traverse $CurModel } proc PS_Mass_Traverse {model} { Debug "Traverse Model $model" # Get Component ID's set CompIDs [ps_visit type -model $model component] foreach CompID $CompIDs { # get component name set CompName [ps_assy component_name -model $model $CompID] Debug "Assembly $model Component ID $CompID CompName $CompName" # get the extension set ext [ps_model ext -model $CompName] switch $ext { PRT { # # proceed each subtype ? # SKELETONS may not have a weight # Skip everything if not a SOLID # set SUBTYPE [ps_model subtype -model $CompName] switch $SUBTYPE { SOLID { } default { Debug "Skip Model $CompName Subtype $SUBTYPE for mass prop calc" red continue } } # Get Mass Prop PS_Mass_From_Model $CompName } ASM { # Get Mass Prop PS_Mass_From_Model $CompName # rekursive call PS_Mass_Traverse $CompName } default { Debug "Unknow extension '$ext' in assy '$model' found" return } } } } proc PS_Mass_From_Model {m} { Debug "PS_Mass_From_Part '$m'" set CSysIDs [ps_visit csys -model $m] Debug "CSysIDs Geom Ids '$CSysIDs'" set num [llength $CSysIDs] if {$num == 0} { Debug "Calc MProp based on default, no CSYS found" set dict1 [ps_solid basic_mass_prop -model $m ] set dict2 [ps_solid math_mass_prop -model $m ] } else { Debug "ps_geom names -model $m CSYS $CSysIDs" set names [ps_geom names -model $m CSYS $CSysIDs] Debug "names $names" set use [lindex $names 0] Debug "For calculation take the first one '$use' " set dict1 [ps_solid basic_mass_prop -model $m -csys $use] set dict2 [ps_solid math_mass_prop -model $m -csys $use] } # or all indicies in one arry array set basic $dict1 array set extend $dict2 # output or join and write to a csv file foreach item [lsort [array names basic]] { Debug "basic $item $basic($item)" } # output or join and write to a csv file foreach item [lsort [array names extend]] { Debug "extend $item $extend($item)" } } # Call Main Function PS_Mass_Prop_Assy
Debug Output (cut only for toplevl assembly)
Debug : proc PS_Mass_From_Model PS_Mass_From_Part 'ENGINE.ASM' CSysIDs Geom Ids '8' ps_geom names -model ENGINE.ASM CSYS 8 names ASM_DEF_CSYS For calculation take the first one 'ASM_DEF_CSYS' basic CENTER_OF_GRAVITY.X 0.013103156721395044 basic CENTER_OF_GRAVITY.Y 18.48877898819197 basic CENTER_OF_GRAVITY.Z 25.652370056147102 basic DENSITY 1000.0000000000003 basic MASS 41422192.0786966 basic SURFACE_AREA 30802.63214818563 basic UNITSYSTEMTYPE MASS_LEN_TIME basic UNITTYPE_LENGTH mm basic UNITTYPE_MASS kg basic VOLUME 41422.192078696586 extend INERTIA_MATRIX.IXX_IXY_IXZ.X 2974291992.942244 extend INERTIA_MATRIX.IXX_IXY_IXZ.Y -13421519.761119707 extend INERTIA_MATRIX.IXX_IXY_IXZ.Z 31272945.442605354 extend INERTIA_MATRIX.IYX_IYY_IYZ.X -13421519.761119707 extend INERTIA_MATRIX.IYX_IYY_IYZ.Y 31640536282.290863 extend INERTIA_MATRIX.IYX_IYY_IYZ.Z 13752835676.919683 extend INERTIA_MATRIX.IZX_IZY_IZZ.X 31272945.442605354 extend INERTIA_MATRIX.IZX_IZY_IZZ.Y 13752835676.919683 extend INERTIA_MATRIX.IZX_IZY_IZZ.Z 37105163761.98315 extend INERTIA_TENSOR.IXX_IXY_IXZ.X 68745700044.27402 extend INERTIA_TENSOR.IXX_IXY_IXZ.Y 13421519.761119707 extend INERTIA_TENSOR.IXX_IXY_IXZ.Z -31272945.442605354 extend INERTIA_TENSOR.IYX_IYY_IYZ.X 13421519.761119707 extend INERTIA_TENSOR.IYX_IYY_IYZ.Y 40079455754.92539 extend INERTIA_TENSOR.IYX_IYY_IYZ.Z -13752835676.919683 extend INERTIA_TENSOR.IZX_IZY_IZZ.X -31272945.442605354 extend INERTIA_TENSOR.IZX_IZY_IZZ.Y -13752835676.919683 extend INERTIA_TENSOR.IZX_IZY_IZZ.Z 34614828275.23311 extend PRINCIPAL_AXES.1.X -0.001631475347182119 extend PRINCIPAL_AXES.1.Y 0.8785253011946432 extend PRINCIPAL_AXES.1.Z -0.47769303265701174 extend PRINCIPAL_AXES.2.X 0.001100349292419445 extend PRINCIPAL_AXES.2.Y 0.47769495633622155 extend PRINCIPAL_AXES.2.Z 0.8785250809865192 extend PRINCIPAL_AXES.3.X 0.9999980637579385 extend PRINCIPAL_AXES.3.Y 0.0009076628210328389 extend PRINCIPAL_AXES.3.Z -0.00174603223827789 extend PRINCIPAL_MOMENTS.X 9617533725.350153 extend PRINCIPAL_MOMENTS.Y 23659502930.638294 extend PRINCIPAL_MOMENTS.Z 27328570062.012695
In the next example, you have an active Drawing, and you want to update parameter in a Repeat Region. The attached drawing model must be an assembly (not checked).
The feature Parameter for Welding Feature should contain 2 parameter, Article_1 and Article_2. Assumption: This 2 Parameter are the contact/welded components given by the feature parents of the identified weld.
If you do a weld, and you have welded sheet1.prt and sheet2.prt by one weld, the value of the weld feature parameter 'Article_1' will be set to sheet1.prt or sheet2.prt based on the order of the selection. This two parameter are include in the repeat region.
proc Run_Script {} { # We need a modelitem for the feature parameter catch {ps_modelitem miObj} # Get the attached Model set CurAssy [ps_draw current] # Get the list of all Weld_fillet feature ID's set Welds [ps_visit type -model $CurAssy WELD_FILLET] # now proceed for each welding foreach Weld $Welds { set ParentIDs [ps_feat parents -model $CurAssy $Weld] Debug "Weld $Weld Parents $ParentIDs" set refIDs [list] foreach ParentID $ParentIDs { set type [ps_feat type -model $CurAssy -- $ParentID] if {[string equal $type COMPONENT]} { lappend refIDs $ParentID } else { Debug "Expect a Component got $type for Parent ID $ParentID" } } # ignore in this sample if we don't have 2 Parents which are Components if { [llength $refIDs] != 2} { Debug "For FeatID $Weld expect two component references got '$refIDs' from Parent IDs '$ParentIDs'" continue } set Comp1 [lindex $refIDs 0] set Comp2 [lindex $refIDs 1] # get the component name from the FeatID set CompName1 [ps_assy component_name -model $CurAssy $Comp1] set CompName2 [ps_assy component_name -model $CurAssy $Comp2] # configure modelitem to point to the Weld Feature ID Debug "Modelitem conf -type feat -model $CurAssy -id $Weld" miObj conf -type feat -model $CurAssy -id $Weld # # Create or update the Parameter 'Article_1' and 'Article_2' # Instead of Component_Name. Ext write only the Component Name Update_Parameter miObj Article_1 [file root $CompName1] Update_Parameter miObj Article_2 [file root $CompName2] } } # # This procedure will create or update a feature Parameter # proc Update_Parameter {item name value} { if {![ps_param exist -at $item $name]} { # Ok, not existing, create it Debug "Created param $item $name '$value'" ps_param set -at $item $name String $value } else { # ok, exists get the handle and configure the value set Param [ps_param list -at $item $name] Debug "Modify $name '$value'" $Param config -value $value } } Run_Script
Sample code to create a ShrinkWrap Model Output.
# Get the current model FOO.ASM set cur [ps_model cur] # Create ShrinkWrap Obj ps_shrinkwrap swObj # Create 2 modelitems Objs ps_modelitem CsysMiObj ps_modelitem PlanMiObj # Config 2 modelitems by name, one is a csys, one is a datum plane CsysMiObj byname $cur csys acs0 PlanMiObj byname $cur surface a-top # use this modelitems to create 2 Selection Objs ps_sel CsysSelObj -modelitem CsysMiObj ps_sel PlanSelObj -modelitem PlanMiObj # Create a random output filename (sec by sec) set outfile swrapout[clock sec].prt # Create this model in session ps_file create $outfile # config the shrinkwrap Output, Method, Target Solid swObj config -output_filename shrink_test_out -creationmethod merged -target_solid $outfile # Add the 2 Datum Selections swObj config -datumrefs [list CsysSelObj PlanSelObj] # Create the ShrinkWrap swObj export # Save it to disk ps_file save $outfile # clean all created objs ps_obj destroy *
Simple Sheet Metal Stuff, get the surface area for one side from an active sheet metal part.
'ps_visit surf' will return all surface ID's for the given model ps_geom smt_surf_type' will return a list of Type and ID, like 'GREEN 123 WHITE 2333 SIDE 3434', the input is a list of Surface ID's
# for this sample I use the 'switch' statement, not string ..., ignoring the other types WHITE ...
proc PS_SMT_Surface {} { set data [ps_geom smt_surf_type [ps_visit surf]] set vals [list] foreach {COL ID} $data { switch $COL { GREEN { set AREA [ps_geom area $ID] lappend vals $AREA } } } set total [expr [join $vals + ]] return $total } # # Get the surface area from the green side from the current active model # set Area [PS_SMT_Surface]
Simple Drawing Stuff, add a sheet and set a format. Use the same format as for sheet number 1. Assume drawing.drw is in memory but not active.
# # Delete all Drawing Tables which are belonging to the current Format # and sheet number Prior to replace the Format # Purpose is to remove the old Title Block # proc Delete_Format_Table_From_Sheet {DRAWING sheet} { set TABIDS [ps_table list -drawing $DRAWING ] foreach TABID $TABIDS { set is_from_format [ps_table is_from_format -drawing $DRAWING -- $TABID] if {$is_from_format} { set seg_count [ps_table segcount -drawing $DRAWING -- $TABID] # # Title Block should have a Segment Count of 1 # if {$seg_count == 1} { set seg_sheet [ps_table segsheetget -drawing $DRAWING -- $TABID -1] if {$seg_sheet == $sheet} { ps_table delete -drawing $DRAWING -- $TABID } } } } } # How many sheets we have set num [ps_sheet number -drawing drawing.drw] if {$num == 1 } { # get format name from sheet number one set myfrm [ps_sheet format -drawing drawing.drw -- 1] # add a new sheet ps_sheet add -drawing drawing.drw # set the format at sheet 2 (default format sheet number is 1) ps_draw set_format -drawing drawing.drw -- $myfrm 2 # activate sheet number 2 ps_sheet set 2 }
Assume you would like to add a column to our current model, the column should contain the Parameter Material, add an instance and assign Aluminum.
# Default is the current Model # else you can configure the command # with the option "-model ModelName" # if you try to manage the same model # twice, an error will be raised # # Get the handle to our family table ps_famtab myFT # Insert the column myFT insert Parameter Material # Insert a new Instance mfFT add PS12345 # Set the Value for the new instance mfFT set PS12345 Material Aluminum
Some sample task if you want to extract or set Family Table Data.
Sample family Table Data Name F126 d9 Material PS78047 Y 14.800 Steel 0007564187 * * * 0007564185 N 15.000 Copper 0007564186 * 20.000 Aluminium # Get the ObjectCommand ps_famtab ft # Get all names ft names 0007564187 0007564185 0007564186 # get header info ft header -> {F126 FEATURE PS78047.PRT BOOL} {d9 DIMENSION PS78047.PRT DOUBLE} {MATERIAL PARAMETER PS78047.PRT STRING} # get ids ft column_ids -> F126 D9 MATERIAL # get data by column ft get_column_data MATERIAL -> * Copper Aluminium # or ft get_column_data d9 -> * 15.0 20.0 # get the whole list ft get_col -> {* N *} {* 15.0 20.0} {* Copper Aluminium} # count the number of instances ft nins -> 3 # get the number of rows ft ncol -> 3 # get all data instance by instance ft get_instance_data -> {* * *} {N 15.0 Copper} {* 20.0 Aluminium} # get data for one instance ft get_instance_data 0007564186 -> * 20.0 Aluminium # set by instance ft set_instance_data 0007564186 [list Y 22.2 Gold] # delete an instance ft delete 0007564187 # remove a columns ft remove_column d9 # delete the whole table ft erase
In Asynchronous mode you start Creo or connect to an existing session. After this you can drive Creo without having a callback in a Creo Menu.
# # Simple asynchrounes Creo Session # to load a Creo Drawing and export an DXF file # Execute in Tcl as a callback or just in plain Tcl # # # Setup to callback functions # # Invoked after connection is establish # proc pShell_Init {} {puts "Connected"} # # Invoked after connection is closed # proc pShell_Exit {} { puts "Session closed"} # # In Tcl get the DLL # package req pshell_asyn # # Configure and Start the Creo Session # ps_session ::PS::SessionCmd \ -exit pShell_Exit \ -init pShell_Init \ -timeout 20 \ -text C:/p-Shell/programs/text \ -pro_comm_msg [file native "C:/Creo 2/Common Files/M200/x86e_win64/ob/pro_comm_msg.exe"] # # Start Creo # after connect 'pShell_Init' is called # ::PS:SessionCmd start [file native "C:/Creo 2/Parametric/bin/creo2.bat"] # # Note: This script continues if Creo is running and connection is established # # # Make sure that all Datums are not displayed # Load a config file wich will turn on/off what we want # ps_import config c:/Release/release.pro # # Open the Drawing from your Project Directory # ps_file open C:/ProjectData/CreoFiles/P12KU88/box.drw # # Get the referenced model # No error check here (None or more than one) # set refMdl [ps_draw list -drawing box.drw] # # Make sure not to display a certain layer, turn off a layer where we have only curves # No error check here (Layer exists, current status) ps_layer set -model $refMdl -- ALL_CURVES Blank # # Update the title block to display the current day (This is Based on the used Format) # assume parameter DRW_RELASE_DATE is new or exists as a string # set ClockVal [clock format [clock seconds] -format "%m/%d/%y"] ps_param set -model box.drw -- DRW_RELEASE_DATE String $ClockVal # # Display the Drawing in a Window # ps_wind set box.drw # # Create the DXF file # ps_export dxf -model box.drw -- box.dxf # # Save the drawing with the updated parameter # ps_file save box.drw # # Stop the session # This will eval 'pShell_Exit' and delete all p-Shell commands # ::PS:SessionCmd stop
Another example to export Creo Objects to a local folder from Windchill
# # Setup to callback functions # # Invoked after connection is establish # proc pShell_Init {} {puts "Connected"} # # Invoked after connection is closed # proc pShell_Exit {} { puts "Session closed"} # # In Tcl get the DLL # package req pshell_asyn # # Configure and Start the Creo Session # ps_session ::PS::SessionCmd \ -exit pShell_Exit \ -init pShell_Init \ -timeout 20 \ -text C:/p-Shell/programs/text \ -pro_comm_msg [file native "C:/Creo 2/Common Files/M200/x86e_win64/ob/pro_comm_msg.exe"] # # Start Creo # after connect 'pShell_Init' is called # ::PS:SessionCmd start [file native "C:/Creo 2/Parametric/bin/creo2.bat"] # # Create the Server Command # ps_server serverObj # # Configure the Server Command # serverObj configure -serverurl http://MyWindchill/windchill -alias MyConnectionName # # Prepare Username and Password for http://MyWindchill/windchill # serverObj login MyUserName MyPassword # # Register an existing Workspace 'MyWorkspace' # serverObj register MyWorkspace # # Add both assemblies into the Workspace MyWorkspace # After add to workspace the objects are not "Checked Out" # serverObj multiobjcheckout [list assy1.asm assy2.asm] false # # Open both assemblies in Creo # ps_file open assy1.asm ps_file open assy2.asm # # Save both assemblies in an existing folder named 'c:/temp/export/assyX' # ps_file backup assy1.asm c:/temp/export/assy1 ps_file backup assy2.asm c:/temp/export/assy2 # If you CheckOut models # and next modified in Session or # saved to an active Workspace # use: # serverObj checkin ?Modelname? # to checkin the data to Windchill # eg: serverObj checkin assy1.asm # if Modelname is not given, # the whole Workspace will be checked in # # Stop the session # ::PS:SessionCmd stop
Command options for ps_assy are:
component_name FeatID is_bulk_item FeatID type FeatID matrix_get comppathObj Matrix matrix_set comppathObj Matrix explode ?NewState? interchange IDs ReplaceModel create NAME.EXT Unplaced ?Template? dynamic_position ?NewState? assemble Component Matrix modelItemObjReturn setconstraints ModelItem Constraints ?CompPath? getconstraints ModelItem Constraints numofconstraints ModelItem -help
Example: Assembly by CSYS
If you assemble by Coordinate system, you can use the name of the CSYS to identify the model item. Like in Creo you have to specify the assembly reference and the component reference.
For placing Components (single model or another assembly) in one assembly you need to specify the target assembly, the component/assembly to assembly and an initial matrix.
The return value on placing a component is a model item, which contains the component info, for feature id and type.
We assembly the component 'comp.prt' by using the coordinate system with the name 'CS0' into the assembly 'assy.asm' by using the coordinate system with the name 'ACS0'.
You must supply the same info, as if you would do this in Creo manually.
Basically a selection object contains a component path and a model item reference, this must be supplied by your calls. Component Path is not required in the following example.
You need to supply a component path if you would like to use a reference in the target assembly, which is for example a csys of an already assembled component.
Note: A component can be assembled multiple times, the component path will uniquely identify the model item in an assembly.
# # Vars for the models to be used # model handle # set ASSY assy.asm set PART comp.prt # Create two selection objects # including named model items # one for the assembly reference # one for the component reference ps_sel AssySelObj -modelitem AssyMiObj ps_sel CompSelObj -modelitem CompMiObj # A csys for sure has a name # Note: The Feature ID of the CSYS is not the ID, which is used later # The Geometric ID will be used # set data, fill csys geometric id, type and owner. # # one for the assembly # one for the component # This two calls will setup the model items with valid values # if the given name and type is valid AssyMiObj byname $ASSY csys ACS0 CompMiObj byname $PART csys CS0 # # create the constraint Object # ps_asmconstraint constraintObj # # setup the type by 'csys' # constraintObj config -constrainttype CSYS # # set the info, same as manually done in Creo after selecting the references # Could be done in one statement as well # constraintObj config -asmselection AssySelObj -asmdatumside none constraintObj config -compselection CompSelObj -compdatumside NONE # # Create a default matrix for initial position # ps_matrix matrixObj # # Assemble the component first without any constraints # You only have to take care that # 'PlacedMiObj' does not exists as a command with another type. # You can create it upfront as well, same for the Matrix Object # ps_assy assemble -model $ASSY -- $PART matrixObj PlacedMiObj # Final call to set the constraints # We don't need to specify a component path in this sample # Only 2 (not 3) args given to the function # 'PlacedMiObj' is the result from assembling without constraints # this uniquely identifies the component to configure # 'list' is used to keep in mind that you may need to specify more the one constraint # If you assembe by datum planes you need to supply normally 3 constraints # # If this fail, you may want to delete the placed component with ps_feat delete ?ID? # The feature id you get with: 'PlacedMiObj cget -id' # # Configure the placed component to make if fully parametric # ps_assy setconstraints PlacedMiObj [list asmconObj] # # Component is now fully constraint and assembled #
Assume you have have an active assembly test.asm where feature ID 43 is the subassembly sub.asm and contains a component with the name bolt.prt at feature id 30.
# # Assembly test.asm is active # '0' the path is invalid or # '1' the path is valid. # set member1 [ps_assy component_name 43 ] set member2 [ps_assy component_name -model sub.asm -- 30 ]
member1 will be set to SUB.ASM
member2 will be set to BOLT.PRT
# # Some! notes and assumption for a component interchange # # Your current active model is an assembly # The model name given by CurModel is part of the assembly # The model name given by NewModel is loaded and already in session # Note: # The components are replaced only in the top level # of the given assembly (not recursive) # # If the CurModel is an instance or the genric, # NewModel must be in the same family table or the generic # It will Fail if CurModel is equal to RepModel # If the CurModel is part of an interchange assembyl, # NewModel must be in the same interchange assembly # ID Table Map still valid # You must have a PTC lizense (AdvAssembly) to do an interchange # ... # # A proc just to call our small test function # proc ReplaceModelTest {} { set Assy [ps_model cur] set CurModel PSL053.PRT set RepModel PSL060.PRT ReplaceModel $Assy $CurModel $RepModel } # # This is the basic task # More check are required # to make it save # e.g. # RepIDs is not initialized # Component feature status may invalid (suppressed, name retrieve may fail) # RepModel may not in session # Is the RepModel valid for Replace (same table, interchange assembly) # ... proc ReplaceModel {Assy CurModel RepModel} { # Visit by Component will return all # component IDs in the given assembly foreach ID [ps_visit type -model $Assy component] { # Get the model Name for the given ID in this assembly set M [ps_assy comp -model $Assy $ID] #If the component name is equal to CurModel add to our list if {[string equal -nocase $M $CurModel]} {lappend RepIDs $ID} } # Do the Interchange ps_assy interchange -model $Assy -- $RepIDs $RepModel return }
Replace by Family Table with some checks
# For the given Assembly # Get the component IDs # for the given Model Name # # Args: # # Assy - The Assembly where to replace the model # Model - The Assembled Model to Replace # ReplaceModel - The Target Model Name after Replace # Assumption: # If Model is not an Instance it is a Generic # ... proc ReplaceByFamTab {Assy Model ReplaceModel} { set IDs [IDsByModelName $Assy $Model] if {[llength $IDs] == 0} { Rep_Error "Model $Model not found in Assembly" return 1 } set GenModel $Model # If the given model is an instance get the generic if { [ps_model is_instance -model $Model] == 1} { set GenModel [ps_model generic_get -model $Model] } # Get access to the Family Table ps_famtab FTObj -model $GenModel # Init the list by the name with out extension set InstNames [file root $GenModel] # Add all other instance names foreach Inst [FTObj names] { lappend InstNames $Inst } # Check that the model to replace is part # of the instance list, hithout the Extension check if {[lsearch $InstNames [file root $ReplaceModel]] < 0} { Rep_Error "Replace Model $ReplaceModel not found in the generic model $GenModel" return 1 } # Make sure the model is in session if {![ps_model exists $ReplaceModel]} { Rep_Warn "Open Instance $ReplaceModel" FTObj open [file root $ReplaceModel ] } # Do the Interchange ps_assy interchange -model $Assy -- $IDs $ReplaceModel return 0 } # # Get Component IDs # in a given assemby # which match the given # 'Model' name # further checks for suppressed IDs # are required # proc IDsByModelName {Assy Model} { set RetIDs [list] foreach ID [ps_visit type -model $Assy component] { # Get the model Name for the given ID in this assembly set M [ps_assy comp -model $Assy $ID] if {[string equal -nocase $M $Model]} {lappend RetIDs $ID} } return $RetIDs } proc Rep_Error {mess} { ps_mess -icon err $mess } proc Rep_Warn {mess} { ps_mess -icon warn $mess } proc Rep_Mess {mess} { ps_mess $mess }
Create a component path object. A component path object identifies a unique component in one assembly.
# # Note: creation or configuration return 0 or 1. # '0' the path is invalid or # '1' the path is valid. # ps_comppath myPath -model test.asm -path {43 30} -component bolt.prt
Options for ps_export are:
relation Filename model_info Filename program Filename iges_2d Filename dxf Filename render Filename sla_binary Filename sla_ascii Filename catiafacets Filename bom Filename dwg_setup Filename feature_info Filename mfg_oper_cl Filename mfg_feat_cl Filename material Filename iges_3d Filename step Filename vda Filename set Filename cgm Filename inventor Filename fiat Filename connector_params Filename catia Filename cable_params Filename optegra_vis Filename dwg Filename -help
Quick sample:
# # Simple Creo Export Task # read from an input file the drawing name # Line by Line # # export the Drawing as DXF # and the attached models to STEP # Check if the the object was already export # # Export STEP and DXF files in different folder # # Main Call to export ddata from Creo files # proc DO_Export {} { global env global APP_GLOB if {![info exists env(WORKDIR)]} { error "env var WORKDIR not found" } destroy .log toplevel .log pack [text .log.t] set EXP_DIR [file join $env(WORKDIR) export] set APP_GLOB(EXP.ROOT) $EXP_DIR set APP_GLOB(EXP.DXF) [file join $EXP_DIR DXF] set APP_GLOB(EXP.DXF.LOG) [file join $APP_GLOB(EXP.DXF) Log] set APP_GLOB(EXP.STEP.DIR) [file join $EXP_DIR STEP] set APP_GLOB(EXP.STEP.LOG) [file join $APP_GLOB(EXP.STEP.DIR) Log] set APP_GLOB(EXP.INPUT.FILE) [file join $EXP_DIR Input input.csv] set APP_GLOB(EXP.DONE.FILE) [file join $EXP_DIR Input done.csv] set INPUT $APP_GLOB(EXP.INPUT.FILE) set DONE $APP_GLOB(EXP.DONE.FILE) Status_Set INF "Import from $INPUT, log in $DONE" # Check the required export folder, dirty hack if {![file isdir $APP_GLOB(EXP.ROOT)]} { file mkdir $APP_GLOB(EXP.ROOT) } if {![file isdir $APP_GLOB(EXP.DXF)]} { file mkdir $APP_GLOB(EXP.DXF) } if {![file isdir $APP_GLOB(EXP.DXF.LOG)]} { file mkdir $APP_GLOB(EXP.DXF.LOG) } if {![file isdir $APP_GLOB(EXP.STEP.DIR)]} { file mkdir $APP_GLOB(EXP.STEP.DIR) } if {![file isdir $APP_GLOB(EXP.STEP.LOG)]} { file mkdir $APP_GLOB(EXP.STEP.LOG) } if {![file exists $INPUT]} { Status_Set INF "Input file not exists '$INPUT'" return } # Read what was already don Creo_Export_Read_Done $DONE # Start the export Creo_Export_Start $INPUT } # Run proc Creo_Export_Start {INPUT} { set fp [open $INPUT] set data [split [read $fp] \n] close $fp set len [llength $data] foreach model $data { set MODEL [string toupper [string trim $model]] if {[string len $MODEL] == 0 } { continue } if {[string equal [string index $MODEL 0] #]} { Status_Set INFO "Skip '$MODEL' ($i/$len)" continue } Status_Set INFO "Export '$MODEL' ($i/$len)" after 500 Creo_Export_Action $MODEL } } proc Creo_Export_Action {MODEL} { global DONE_DATA if {[info exists DONE_DATA($MODEL) ]} { Status_Set INF "Already Done '$MODEL'" return } set EXT [string range $MODEL end-2 end] switch $EXT { DRW { if {[Creo_Export_Action_DRW $MODEL] == 1} { Creo_Export_Read_Mark $MODEL } } ASM - PRT { if {[Creo_Export_Action_ASM_PRT $MODEL] == 1} { Creo_Export_Read_Mark $MODEL } } } } proc Creo_Export_Action_DRW {MODEL} { global APP_GLOB set InSession [ps_model exists $MODEL] if {$InSession == 0} { set ok [Creo_Open_File $MODEL] if {$ok == 0} { return 0 } } set root [file rootname $MODEL] set Rev X-X # Export DXF if {[ps_param exists -model $MODEL PTC_WM_VERSION]} { set POBJ [ps_param list -model $MODEL PTC_WM_VERSION] set Rev [$POBJ cget -val] } set Rev [string map {. {-}} $Rev] set filename [format "%s_%s.%s" $root $Rev dxf] set dirname $APP_GLOB(EXP.DXF) set export [file join $dirname $filename] # Create the DXF file set fail [catch {ps_export dxf -model $MODEL -- $export}] if { $fail == 1} { Status_Set ERR "DXF Export for '$MODEL' fail" return 0 } set logfiles [glob -nocomplain -tail -dir $dirname *_dxf__out.log*] foreach logfile $logfiles { set src [file join $dirname $logfile] set tar [file join $APP_GLOB(EXP.DXF.LOG) $logfile] if {[file exists $tar]} { catch {file delete $tar} } catch { file rename $src $tar Status_Set INF "Move Log Info to log folder" } } Status_Set INF "DXF Export for '$MODEL' success" set AttachedModels [ps_draw list -drawing $MODEL] set ret 1 foreach Model $AttachedModels { if {[Creo_Export_Action_STEP $MODEL] == 0} { set ret 0 } } return 1 } # Open a file # search path must be set or from Windchill # proc Creo_Open_File {TAR_FILE} { if { [ catch {ps_file open $TAR_FILE } ] } { Status_Set ERR "Can't open $TAR_FILE" return 0 } Status_Set DEB "Successfully loaded '$TAR_FILE'" return 1 } # # Export Action for a 3D Model # proc Creo_Export_Action_ASM_PRT {MODEL} { global APP_GLOB Status_Set INF "STEP Export for '$MODEL' ..." set InSession [ps_model exists $MODEL] if {$InSession == 0} { set ok [Creo_Open_File $MODEL] if {$ok == 0} { return 0 } } set ret [Creo_Export_Action_STEP $MODEL] return $ret } # # export 3D model as a STEP File # Check for Windchill Revision Info # Use the Rev in the output filename # proc Creo_Export_Action_STEP {MODEL} { set root [file rootname $MODEL] set Rev X.X # Export DXF if {[ps_param exists -model $MODEL PTC_WM_VERSION]} { set POBJ [ps_param list -model $MODEL PTC_WM_VERSION] set Rev [$POBJ cget -val] } set Rev [string map {. {-}} $Rev] set filename [format "%s_%s.%s" $root $Rev stp] set dirname $::APP_GLOB(EXP.STEP.DIR) set export [file join $dirname $filename] set fail [catch {ps_export step -model $MODEL -- $export}] if { $fail == 1} { Status_Set ERR "STEP Export for '$MODEL' fail" return 0 } set logfiles [glob -nocomplain -tail -dir $dirname *_out.log*] foreach logfile $logfiles { set src [file join $dirname $logfile] set tar [file join $::APP_GLOB(EXP.STEP.LOG) $logfile] if {[file exists $tar]} { catch {file delete $tar} } catch { file rename $src $tar Status_Set INF "Move Log Info to log folder" } } Status_Set INF "STEP Export for '$MODEL' success" return 1 } # # Mark already done proc Creo_Export_Read_Mark {MODEL} { set fp [open $::APP_GLOB(EXP.DONE.FILE) a+ ] puts $fp $MODEL close $fp } proc Creo_Export_Read_Done {DONE} { global DONE_DATA unset -nocomplain DONE_DATA if {![file exists $DONE]} { Status_Set INF "Already Done Info not exists" return } Status_Set INF "Read Already Done Info ..." set fp [open $DONE] set data [split [read $fp] \n] close $fp foreach model $data { set MODEL [string toupper [string trim $model]] if {[string len $MODEL] == 0 } { continue } set DONE_DATA($MODEL) 1 Status_Set INF "Mark as Done '$MODEL'" } } # Simple logger proc Status_Set {type message} { .log.t insert end "$type $message\n" update } # run for it DO_Export
Options for ps_data are:
axis Geom_ID lineObj edge Geom_ID lineObj arc Geom_ID arcObj csys Geom_ID csysObj surface Geom_ID csysObj point Geom_ID pointObj outline lineObj eval_surface Geom_ID UVpointObj lineObj extremes Geom_ID pointObj lineObj edge_eval_xyz IDs Params curve_eval_xyz IDs Params edge_dir SurfID EdgeID curve Geom_ID lineObj tessellation Geom_ID Tolerance -help
# Assume you have a point table pattern # and you want to export the x,y,z coords for each point # Note: 3d values measured in default transformation of the model # # if 235 is a point pattern feature ID in the current model # open file for export set fp [open export.txt w] # Get all Feature IDs part of the pattern foreach member [ps_pattern member 235] { # Get the geom point ID set geomId [ps_visit feature point $member] # Get the coords in a point Object ps_data point $geomId pntObj # Write x y z to the open file puts $fp [pntObj 3d] } # close the open file close $fp
ps_matrix matrixObj
This command will create a 4x4 identity matrixObj
1.0 | 0.0 | 0.0 | 0.0 |
0.0 | 1.0 | 0.0 | 0.0 |
0.0 | 0.0 | 1.0 | 0.0 |
0.0 | 0.0 | 0.0 | 1.0 |
Options for matrixObj are:
cget Option configure Option ?Value? rot2d angle invert normalize ?matrixOut? reset identity copy matrixOut product matrixObj ?matrixOut? x_to_vector pointObj y_to_vector pointObj z_to_vector pointObj origin_to_vector pointObj x_from_vector pointObj y_from_vector pointObj z_from_vector pointObj origin_from_vector pointObj shift_to_vector pointObj shift_from_vector pointObj scale_xyz ScaleValue
Sample command
# copy the matrix matrixObj copy bckMatrix
Common problem to get collinear axis within an assembly
# # Seach for collinear axis # Input: # a) The Selection for the Axis to search for selAxis # b) The Selection which gives us the part where we need to search in # From the axis we get Matrix up to root # Next the Matrix Down to our target # Multiplay this two matices # Transform with this matrix the given axis # Search this in our target model # # For planes, if the z vector is parallel # it should have the same orientation # we can use also the z-vector and one axis # proc Collinear_Axis_Get {selAxis selModel} { # # set up object vars # # Matrix obj's set MAT1 ::PS::TMP::MAT_1 set MAT2 ::PS::TMP::MAT_2 set MAT3 ::PS::TMP::MAT_3 # for axis data set AxisData1 ::PS::TMP::AXISDATA1 set AxisDataTmp ::PS::TMP::AXISDATATMP set AxisTrans ::PS::TMP::AXISTRANS # # get model item from selection # set mi1 [$selAxis cget -modelitem] set mi2 [$selModel cget -modelitem] # # get comp path from selection # set cp1 [$selAxis cget -comppath] set cp2 [$selModel cget -comppath] # # '$cp1 cget -model' and '$cp2 cget -model' should be the same assy # ps_assy matrix_get -model [$cp1 cget -model] -bottom_up yes $cp1 $MAT1 ps_assy matrix_get -model [$cp2 cget -model] -bottom_up no $cp2 $MAT2 # # Get target matrix # $MAT1 product $MAT2 $MAT3 # # get model item data # set MODEL1 [$mi1 cget -model] set TYPE1 [$mi1 cget -type] set ID1 [$mi1 cget -id] set MODEL2 [$mi2 cget -model] # # Input axis data # ps_data axis -model $MODEL1 -- $ID1 $AxisData1 # # Transform this into the target model # $AxisData1 transform $MAT3 $AxisTrans # # init list # set collAxis [list] # # get all axis in target # set AllAxisInModel2 [ps_visit axis -model $MODEL2] foreach Axis $AllAxisInModel2 { ps_data axis -model $MODEL2 $Axis $AxisDataTmp set is_coll [$AxisTrans is_coll $AxisDataTmp] if {$is_coll == 1} { lappend collAxis $Axis } } # # return all colinear axis # return $collAxis } # create selection objects ps_sel sel1 ps_sel sel2 # Ask for an axis sel1 select axis # ask for a part sel2 sel part # # get Collinear Axis based on sel1 and sel2 # Collinear_Axis_Get sel1 sel2
Similar commands available for arcs, coordinate systems, and single line (2 Points)
Options for pointObj are:
cget Option configure ?Option? transform matrixObj pointObj rotate matrixObj pointObj copy pointObj product pointObj angle pointObj lenght normalize cross_product point2Obj resultPointObj dircos add point2Obj resultPointObj move point2Obj resultPointObj diff point2Obj resultPointObj 2d_values XY_List 3d_values XYZ_List multiply matrixObj pointObj draw ?Color? -help
Command options for ps_feat are:
count name FeatIDs type FeatIDs list exists FeatIDs children FeatID parents FeatID status FeatIDs visible FeatIDs active FeatIDs number FeatIDs has_geom_checks FeatIDs suppress FeatIDs delete FeatIDs resume FeatIDs protype FeatIDs subtype FeatIDs show_param selObj ParamType set_name FeatID NewName is_read_only FeatIDs unset_read_only set_read_only FeatID ungroup GroupIDs groupstatus FeatIDs isgroup FeatIDs num_section FeatID section_copy FeatID secNumber sectionObjName cancel_insert_mode ?BoolResume? is_insert_mode insert_mode_activate FeatID -help
# # Children of a feature in the active model # ps_feat child 23 # # Parents from FeatID 45 in model box.prt # ps_feat parents -model box.prt -- 45 # # get the status for all features in the active model # set stat [ps_feat stat [ps_feat list]] # # Set the first csys equal to model name (one line) # ps_feat set_name [lindex [ps_visit type csys] 0] [file root [ps_model cur]] # # better do a small procedure # the call 'ps_visit type csys' will return all IDs in the model which are a csys # In this sample only the first one needs to be checked # If we don't have a csys the first element of the list is empty as well # proc FirstCsysNameToModelName { Model } { if {![ps_model is_solid -model $Model] } { return } set FirstCsys [lindex [ps_visit type -model $Model csys] 0 ] if { [llength $FirstCsys ] == 0} { return } set NewName [file root $Model] set CurName [ps_feat name -model $Model -- $FirstCsys] if {[string compare $CurName $NewName] } { ps_feat set_name -model $Model -- $FirstCsys $NewName } } # # Now call it with current model # FirstCsysNameToModelName [ps_model cur] # # Walk through an assembly # proc Traverse {model} { set CompIDs [ps_visit type -model $model component] ;# Get all Component Feat ID's foreach CompID $CompIDs { # get component name set CompName [ps_assy component_name -model $model $CompID] ;# Get the model name by Feat ID # get the extension set ext [ps_model ext -model $CompName] switch $ext { PRT { FirstCsysNameToModelName $CompName } ASM { FirstCsysNameToModelName $CompName Traverse $CompName } default { error "Invalid extension '$ext' in assy '$model' found" return } } } return } # # Now call it with current assembly model # Traverse [ps_model cur]
Options for ps_file are:
open Filename erase Filename exists Filename save Filename delete Filename display Filename copy Source Target rename Source Target backup Filename NewDir create Filename get ?Filter? ?InitPath? ?Label? ?PreSelect? -help
Note: Get working directory with 'ps_pwd' and change with 'ps_cd TargetDir'
# # open file from disc into memory # ps_file open c:/workdir/box.prt
# # open files from a text file # model names one per line # # Note: If you work with Windchill # NAME.EXT is enough in a connected session # # If you work with search path # you can use the full path as well # # # read the file # set fp [open models.txt] set models [split [read $fp] \n] close $fp # # Open the models in session # if already in session skip to open the model # foreach model $models { if {![ps_model exist $model]} ps_file open $model } }
Invoke a user selection
# # create the selection object # ps_sel selObj # # The usr should select an edge # Wirite something into the Creo message area # ps_mess "Please select an edge" # # Control goes to Creo # selObj select edge # Back in p-Shell: # Get the model item from the selection object # Note: You can also get the status if a user abort. # selObj modelitem miObj # # It's an edge - get the edge data into an lineObj # Note: the line object will contains more info, calculate length is only one option # ps_data edge -model [miObj get -model] -- [miObj get -id ] lineObj # # get the vector length # set len [lineObj len]
Just highlight assembly components at the first level for the active assembly
ps_sel sel -modelitem mi -color warning # In this case it works without setting the model item type # The default is 'UNUSED' foreach featid [ps_visit type component] { mi config -id $featid sel show }
Write a test string and a whole SQLite DB (or any other file) to the model.
Note: You can write tcl scripts to the model as well and use 'eval' later.
This example shows how to read and write in one call, so this is only an example. An Application may read first, and later write the updated database back to the Creo file. Only on Init a new Database may writen to the file or if the structure of the DB have changed.
You can use any 'Class' or 'Slot' name (max 32 char), it up to you. The max size per slot is 524288 bytes and you can have not more than 11800 slots.
Available commands (without options)
ps_exdata class_create ClassName ps_exdata class_delete ClassName ps_exdata class_list ps_exdata create Slot ps_exdata delete Slot ps_exdata list ps_exdata read Slot ps_exdata write Slot Data
Example code
proc Write_Read_SQLITE_Slot {} { # The current Model set M [ps_model current] # Write a Text to the model ps_exdata write -model $M -class MyClass -- MySlot "This is my Data" # Read it back set mydata [ps_exdata read -model $M -class MyClass -- MySlot] # Read the SQLite DB set fp [open [file join c:/MyApps DB mydb.sqlite ]] fconfigure $fp -translation {binary binary} set data [read $fp] close $fp # Encode set encdata [base64::encode $data] # Write into the model ps_exdata write -model $M -class MyClass -- MyFileSlot $encdata # Read Back set encdata [ps_exdata read -model $M -class MyClass -- MyFileSlot] # Decode set decdata [base64::decode $encdata] # Open a model name based SQLite file # Write the content back to disc set fp [open [file join c:/MyApps DB Model.$M.sqlite ] w] fconfigure $fp -translation {binary binary} puts $fp $decdata close $fp }
Write all Part Files in Session to c:/temp as a Facet Model.
BOX.PRT will become BOX.PRT.dxf
proc Write_Session_PRT_To_DXF {} { # Create the SaveAs Obj Handle ps_saveas SaveObj -type dxf # Loop through foreach MODEl [ps_glob *.prt] { SaveObj config -model $MODEL -output c:/temp/$MODEl.dxf SaveObj byprofile } } # Call it Write_Session_PRT_To_DXF
This Worker will create STEP and DXF output files from part and assembly files. Creo will be started with no graphics and use an rpc input. If no more files needs to be processed, Creo will be automatically stopped. The Tcl Worker code is available in the Tcl’ers Wiki.
For this Demo, the Worker uses the WORKER namespace, which is entered on Applications creation.
This Creo parameter MODEL_VERSION with a value like '3.9' will hold the version info of the model, this may help to identify the output if you export a model in different develop stages. Sample filename box3-9.dxf (Dots not allowed for the filename created in Creo)
For the YouTube Demo the paramater value was created randomly, to show that this is live, no fake.
Some procs used here, like Log and Debug* may need an other environment.
See https://youtu.be/hwN600UR5cA
# Mini p-Shell asyn environment, see text widget for log info # make sure ce-<AppName>.bat has valid vars set # I started using a queue, but for a professional task # I would use a SQLite DB # Some Global Helper # Just a source # set m chooser_complete.asm # ps_file backup $m C:/Tcl4Creo/Worker/Export/$m # log a message proc Log {txt} { $::LOGTEXT insert end "[PS::CLOCK::Time] " FGSECO $::LOGTEXT insert end "$txt\n" $::LOGTEXT see end Wait 5 } # Just make a folder if not existz proc Make_Dir {dir} { if {[file isdir $dir]} { Debug "Dir '$dir' exists" return } Log "Create Dir $dir" file mkdir $dir } # struct::queue Mon # -> '::Mon' # ::Mon size # -> '0' # ::Mon put box.prt # -> '' # ::Mon put zyl.prt # -> '' # ::Mon get # -> 'box.prt' # ::Mon size # -> '1' # our namespace namespace eval ::WORKER { # Init Worker vars proc Main {} { variable Priv package req twapi package require struct::queue set Priv(MONITOR.DIR) C:/Tcl4Creo/Worker set Priv(MONITOR.EXPORT.DIR) [file join $Priv(MONITOR.DIR) Export] set Priv(MONITOR.OUTPUT.DIR) [file join $Priv(MONITOR.DIR) Output] set Priv(QUEUE.WORK) ::WORKER::QueueWork set Priv(QUEUE.DONE) ::WORKER::QueueDone set Priv(QUEUE.LOCKED) 0 set Priv(CREO.INVOKE.AFTER) {} set Priv(CREO.STOP.AFTER) {} set Priv(SESOBJ) sesObj set Priv(LOOP.BUSY) 0 set Priv(VERSION.PARAM) MODEL_VERSION set Priv(START_DELAY) 4000 # On Debug and Restart if { [string len [info command $Priv(QUEUE.WORK)] ]} { Log "Clear existing Queue" $Priv(QUEUE.WORK) clear $Priv(QUEUE.DONE) clear } else { struct::queue $Priv(QUEUE.WORK) struct::queue $Priv(QUEUE.DONE) } Debug "In Main" # Sample GUI ::WORKER::WIND::Init Worker_Setup Make_Dir $Priv(MONITOR.DIR) Make_Dir $Priv(MONITOR.EXPORT.DIR) Make_Dir $Priv(MONITOR.OUTPUT.DIR) # The Drop Target Monitor_Start $Priv(MONITOR.EXPORT.DIR) Log "App '$::APP_GLOB(APP_NAME)' is running" Log "Start Creo Delay is [expr $Priv(START_DELAY)/1000] sec" } # ::WORKER::Monitor_Clean_Export_Dir proc Monitor_Clean_Export_Dir {} { variable Priv foreach {TYPE} {STEP DXF} { set expDir [file join $Priv(MONITOR.OUTPUT.DIR) $TYPE] file delete {*}[glob $expDir/*] } } # Start the folder monitor # Note that the file names being reported may be either in longname or shortname (8.3) format. Moreover, # depending on the buffering and caching by the operating system, # a single write may result in one or more notifications. proc Monitor_Start {dir} { variable Priv catch { twapi::cancel_filesystem_monitor $Priv(MONITOR.ID) Log "Running Monitor canceld" } set notifier [twapi::begin_filesystem_monitor $dir [NS]::Monitor_Logger \ -secd true \ -access true \ -attr true \ -subtree true \ -size true \ -write true \ -patterns {*.prt* *.asm*} \ ] set Priv(MONITOR.ID) $notifier set Priv(MONITOR.EXPORT.DIR) $dir Log "Monitor started in '$dir', handle is '$notifier'" } # the Main callback after a dir change # make sure to handle the right event proc Monitor_Logger {HINFO Event} { foreach {Type File} $Event {} Status_Set DEB "Monitor_Logger HINFO $HINFO Event $Event" switch $Type { modified { Log "New File Event '$File', add to our queue ..." Monito_Queue_Add_Item $File } default { Log "Event Type $Type for the file '$File' is ignored" return } } return 1 } # # Callback on folder change # if locked, do it again later # after 2 seconds idle time start Creo # proc Monito_Queue_Add_Item {file} { variable Priv Cancel_Stop_Request if $Priv(QUEUE.LOCKED) { after 500 [NS]::Monito_Queue_Add_Item $file return } if { [Queue_Add_Unique_Item $Priv(QUEUE.WORK) $file] } { after cancel $Priv(CREO.INVOKE.AFTER) if { [ Is_Connected ] == 0 } { set Priv(CREO.INVOKE.AFTER) [after $Priv(START_DELAY) [NS]::Start_Creo ] # for debug # Queue_Get_Info $Priv(QUEUE.WORK) } } } # Start Creo proc Start_Creo {} { variable Priv after cancel $Priv(CREO.INVOKE.AFTER) Debug Start_Creo red Log "Start Creo with '$::env(PSE_CREO_START_BAT)', please wait..." set batch "$::env(PSE_CREO_START_BAT)" catch {$Priv(SESOBJ) start "$batch -g:no_graphics -i:rpc_input" } res Log "start result '$res'" if { [Is_Connected] == 1} { Log "Creo Loop will start in a minute .." } else { Log "Failed to start" } } # event may fired more then once # make sure to add the file only once proc Queue_Add_Unique_Item {q newfile} { set qsize [$q size] if {$qsize == 0} { Log "Add '$newfile' to Queue $q ..." $q put $newfile return 1 } set files [$q peek $qsize] set i 0 foreach file $files { Debug "file $file" if {[string equal $file $newfile]} { Debug "queue item $i file '$file' already added" red return 0 } incr i } Log "Add '$newfile' to Queue $q ..." $q put $newfile return 1 } # Cancel the Stop Request proc Cancel_Stop_Request {} { variable Priv if { [ Is_Connected ] == 1 } { after cancel $Priv(CREO.STOP.AFTER) } } # # Simple Boolean Check # proc Is_Connected {} { variable Priv # Log "Connected [$Priv(SESOBJ) status]" return [$Priv(SESOBJ) status] } # for debug the queue proc Queue_Get_Info {q} { Log "Current Queue $q ..." set qsize [$q size] set items [$q peek $qsize] set i 0 foreach item $items { Log "queue item $i '$item'..." incr i } } # # This is just a Debug proc to # create new file with a random name # for testing the export # proc File_Creation_Test {args} { variable Priv set alphabet "abcdefghijklmnopqrstuvwxyz" set len [string len $alphabet] # Create a random prefix for the tar name set rnd_char {} for {set i 0} {$i<3} {incr i} { set rnd [expr rand()] set pos [expr $rnd * $len] set idx [expr int($pos)] append rnd_char [string index $alphabet $idx] } Debug_Var alphabet len rnd pos idx rnd_char # from the sample part file folder # get a random file set sample_dir [file join $::APP_GLOB(APP_DIR) files part_files] set rnd [expr rand()] set files [glob -nocomplain -tail -dir $sample_dir *.prt*] set len [llength $files] set pos [expr $rnd * $len] set idx [expr int($pos)] # # prepare tar and src file name # tar is equal to src but random char are added at the beginning # set src_name [lindex $files $idx] set tar_name [format %s%s $rnd_char [string range $src_name 3 end]] set src [file join $sample_dir $src_name] set tar [file join $Priv(MONITOR.EXPORT.DIR) $tar_name] if {[file exists $tar]} { file delete $tar } file copy $src $tar return } # # if the ps_session command exist # we are already done # proc Worker_Setup {} { variable Priv if {[string len [info comm $Priv(SESOBJ)]]} { Log "Session Command already created" return } Log "Package Load done ps_session = '[info comm ps_session]'" Log "Get a Session Object" ps_session $Priv(SESOBJ) Log "On connect call [NS]:On_Connect, on exit call [NS]:On_Exit" set Priv(WORK_DIR) [file join $::APP_GLOB(APP_DIR) work] set Priv(TEXT_DIR) [file join $::APP_GLOB(APP_DIR) text] Log "text is '$Priv(TEXT_DIR)'" $Priv(SESOBJ) config -init [NS]::On_Connect -exit [NS]::On_Exit -text $Priv(TEXT_DIR) Make_Dir $Priv(WORK_DIR) cd $Priv(WORK_DIR) Log "Working Dir is '[pwd]'" Dev_Config return } # Create a config with windows scale 0.6 # skip if exists proc Dev_Config {} { variable Priv set cfg [file join $Priv(WORK_DIR) config.pro] if { [file exists $cfg] } { Log "Working Config exists" return } set fp [open $cfg w] puts $fp "windows_scale 0.6" close $fp Log "Working Config created" } # # Get back to the caller # so use 'after' # proc On_Connect {} { variable Priv Log "In On Connect ..." after 1000 [NS]::Creo_Process_Queue $Priv(QUEUE.WORK) return } # Create the saveasObj and # work on the queue # Do it in a loop proc Creo_Process_Queue {q} { variable Priv Log "Process Queue ..." set saveObj [ps_saveas saveObj] while 1 { Creo_Loop_Queue $q $saveObj Wait 2000 if { [ Is_Connected ] == 0 } { return } } } # No op if Creo is stopped proc On_Exit {} { Log "In On_Exit" } # ::WORKER::Creo_Loop_Queue # if size is 0 stop Creo after 8 sec proc Creo_Loop_Queue {q saveObj} { variable Priv set qsize [$q size] if {$qsize == 0} { if { $Priv(LOOP.BUSY) == 1 } { set Priv(CREO.STOP.AFTER) [after 8000 [NS]::Stop_Creo ] set Priv(LOOP.BUSY) 0 } return } after cancel $Priv(CREO.STOP.AFTER) set Priv(LOOP.BUSY) 1 set files [$q peek $qsize] foreach file $files { Debug "Process file '$file' " set file [$q get] if { [Creo_Process_File $file $saveObj] } { $Priv(QUEUE.DONE) put $file Debug "file '$file' processed" } else { App_Log "file '$file' process fail" } } } # Check that we don't have # a new job before exit proc Stop_Creo {} { variable Priv if {$Priv(LOOP.BUSY) == 1} { Log "Can't Stop Creo, still busy"# return } Log "Stop Creo" $Priv(SESOBJ) stop Creo_File_CleanUp } # # Remove the files from the Export folder # Lock/Release the Queue proc Creo_File_CleanUp {} { variable Priv set Priv(QUEUE.LOCKED) 1 set qd $Priv(QUEUE.DONE) set qw $Priv(QUEUE.WORK) Log "Clean Start Queue Work Size [$qw size] Work Done Size [$qd size] .." while 1 { if {[$qd size] == 0} { Log "Queue is empty now" break } set file [$qd get] set fullpath [file join $Priv(MONITOR.EXPORT.DIR) $file] if {[file exists $fullpath]} { Log "Delete '$fullpath'" file delete $fullpath } else { Log "Not found '$fullpath'" } } Log "Clean Up done Queue Work Size [$qw size] Work Done Size [$qd size] .." set Priv(QUEUE.LOCKED) 0 return } # # No Checks here, just take the value if exists # A Dot used in the version may lead to an error # if specified for the output file proc Get_Version_Text {MODEL} { variable Priv set VerTxt X-Y set PName $Priv(VERSION.PARAM) if {[ps_param exist -model $MODEL -- $PName]} { set Value [lindex [ps_param value -model $MODEL -- $PName] end] set VerTxt V[string map {. -} $Value] } return $VerTxt } # # Now work to create STEP and DXF output # for a single Creo File # For Demo, display the model # this is not required for this type of an export # Add the version info to the file name # proc Creo_Process_File {file saveObj} { variable Priv set fullpath [file join $Priv(MONITOR.EXPORT.DIR) $file] Debug "open file $fullpath" set MODEL [ps_file open $fullpath] Debug_Var MODEL ps_wind set $MODEL set VerTxt [Get_Version_Text $MODEL] # set VerTxt 1-1 $saveObj config -model $MODEL foreach {TYPE ext} {STEP stp DXF dxf} { if {[ps_model is_assembly -model $MODEL]} { if {[string equal $TYPE DXF]} { Log "Model $MODEL No DXF Export for Type Assembly" continue } } set expDir [file join $Priv(MONITOR.OUTPUT.DIR) $TYPE] Make_Dir $expDir set expName [format %s-%s.%s [string tolower [file root $MODEL]] $VerTxt $ext] set expFile [file join $expDir $expName] Debug "$saveObj config -model $MODEL -type $TYPE -output $expFile" Log "Model $MODEL Export TYPE $TYPE -> '$expFile'" $saveObj config -type $TYPE -output $expFile $saveObj export } Wait 1000 set filesInSession [ps_glob] set num [llength $filesInSession] Log "#$num files in session before erase" ps_wind close # ps_file erase $MODEL ps_file erase -all $MODEL set filesInSession [ps_glob] set num [llength $filesInSession] Log "#$num files in session after erase" return 1 } }
# # In synchronous mode mapkey’s will be executed # after the control returns to Creo # # In a-synchronous mode you can write complete # programs using map key’s. # The macro is immediately executed # # # A-Synchronous Mode # There are three programming options # to use Mapkey’s (which I would prefer) # # 1. Call the Mapkey directly # 2. Create a config file on the fly, then read the file and execute the key # 3. Use substr and replace all your variables in a string # Next save/or execute # This sample shows option 1 and a commented version of option 2 # # Mode 1 # # In the mode you can easily create a proc’s where the arguments # are the variable you need in your mapkey # for example on ‚file open‘ the name of the file may your argument # # Use ‚tail -f‘ (get a windows version ) on your current trail file # If you record a mapkey a lot of stuff is not needed # Watch the statements # Write your code and Test your sub routines proc Run {} { package req pshell_asyn ps_session sessionObj sessionObj connect # For some logs pack [text .t] -expand yes -fill both ps_cd [pwd] # Main Call MK_Action } # # Main Action, Hardcoded here # Input where ever you get the full path from # proc MK_Action {} { set files [list] lappend files c:/CreoTickle/work/models/blower.asm lappend files c:/CreoTickle/work/models/frame.prt lappend files c:/CreoTickle/work/models/nut.prt lappend files c:/CreoTickle/work/models/piston_pin.prt foreach file $files { MK_Quit_Window # Run the Mapkey MK_Change_Dir [file dirname $file] # Or write on the fly a config file # MK_Change_Dir_By_Config_File [file dirname $file] MK_File_Open [file tail $file] MK_File_Save_As STEP } # MK_Quit_Window } # Execute the Mapkey # log what we do here # proc MapKey {MK} { .t insert end [split $MK \;] .t see end # Next Call will invoke the mapkey ps_mapkey $MK } # # Write a Config File # proc MK_Write {CFG sequence} { set fp [open $CFG w] puts $fp $sequence close $fp } # Read a Config File proc MK_Read {CFG} { ps_import config $CFG } # Write/Read and Execute proc MK_Change_Dir_By_Config_File {dir} { # Name of the MapKey set MK ps_cd set CFG [file join [pwd] mapkey.pro] set sequence " mapkey $MK ~ Close `main_dlg_cur` `appl_casc`;\\ mapkey(continued) ~ Command `ProCmdSessionChangeDir` ;\\ mapkey(continued) ~ Trail `UI Desktop` `UI Desktop` `DLG_PREVIEW_POST` `file_open`;\\ mapkey(continued) ~ Update `file_open` `Inputname` `$dir`;\\ mapkey(continued) ~ Command `ProFileSelPushOpen@context_dlg_open_cmd`; " # Write to a config file MK_Write $CFG $sequence # Read it MK_Read $CFG # Execute it MapKey %$MK return } # Close Active Window proc MK_Quit_Window {} { MapKey "~ Command `ProCmdWinCloseGroup`" } # Close Diaplod proc MK_Close {} { set sequence "~ Close `main_dlg_cur` `appl_casc`;" MapKey $sequence } # # Save current Model either IGES or STEP # proc MK_File_Save_As {Type} { switch -glob -nocase -- $Type { STEP { set DB_Key db_539 } IGES { set DB_Key db_134 } default { error "Invalid Type '$Type', use STEP or IGES" } } append sequence "~ Close `main_dlg_cur` `appl_casc`;" append sequence "~ Command `ProCmdModelSaveAs`;" append sequence "~ Open `file_saveas` `type_option`;" append sequence "~ Close `file_saveas` `type_option`;" append sequence "~ Select `file_saveas` `type_option` 1 `$DB_Key`;" append sequence "~ Activate `file_saveas` `OK`;" append sequence "~ Activate `UI Message Dialog` `ok`;" MapKey $sequence return } # # Open a file # Note: Ths will not work for an instance # proc MK_File_Open {MODEL} { append sequence "~ Command `ProCmdModelOpen`;" append sequence "~ Update `file_open` `Inputname` `$MODEL`;" append sequence "~ Activate `file_open` `Inputname`;" MapKey $sequence MapKey "~ Command `ProFileSelPushOpen_Standard@context_dlg_open_cmd`;" } # # Simply change the current # working folder # proc MK_Change_Dir {dir} { append sequence "~ Close `main_dlg_cur` `appl_casc`;" append sequence "~ Command `ProCmdSessionChangeDir` ;" append sequence "~ Trail `UI Desktop` `UI Desktop` `DLG_PREVIEW_POST` `file_open`;" append sequence "~ Update `file_open` `Inputname` `$dir`;" append sequence "~ Command `ProFileSelPushOpen@context_dlg_open_cmd`;" MapKey $sequence return } Run