Tcl for Creo

Difference between version 371 and 375 - Previous - Next
**p-Shell - A Tcl Programming Interface (API) to PTC's Creo Parametric**

'''Scripting, Customization and Automation using Tcl scripts in Creo'''

p-Shell is a http://www.ptc.com/cad/creo%|%Creo%|% Toolkit Application which runs synchronous or a-synchronous with Creo 4.0 - 810.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 lamPowerShell 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. 
   * For a '''synchronous''' application you will setup a menu once during Creo start, and specify a file to be sourced if the user picked this menu button. 
   * In '''a-synchronous''' mode you start Creo (or connect to an existing session), and after the connection is established you can call any p-Shell command or whatever may required for your application.

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 
https://www.youtube.com/channel/UC97mLPJak54nCYnMJ7xhiig/videos%|%YouTube Channel%|%

----




***Code Samples and more Info follows here***

<<discussion>> Access to Parameter and a sample output



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} ]

======




<<discussion>> Parameter objects usage


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

======

<<discussion>> Access to Dimensions

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
}

======


<<discussion>> Tcl Attribute Viewer for Creo/Attributes in a multi column listbox 


The first image on top is from Creo, the bottom one is done by the script below.

[p-Shell Simple Attribute Viewer%|%width=696 height=444]

======

# 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.







----




<<discussion>> Command Syntax - Syntax, access other models, '-help'

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
======

   1. the main command '''ps_draw'''
   2. the command switch '''set_format'''
   3. Command options for this switch '''-drawing drawing.drw'''
   4. '''--''' mark the end of options
   5. The arguments for the command switch, the name of the format, and at which sheet number '''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.




----



<<discussion>> Simple button which saves the current model


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} ]

======




----



<<discussion>> Search an assembly recursive to extract data from a Csys name matching a pattern


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 

======




----



<<discussion>> Create a new menu entry in Creo - Callback Menu

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.




<<discussion>> Creo scripted feature creation - Create and redefine a datum point


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




======


***Mass properties***

<<discussion>> Mass properties - Traverse an assembly and export mass properties

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
 
======




----


***Welding Component***

<<discussion>> Welding - Find Component Reference for Welding Feature


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

======




----



<<discussion>> Create a Shrink Wrap

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 *

======



***Sheet Metal***

<<discussion>> Sheet Metal - Basic info like surface area, perimeter


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]

======



----






<<discussion>> Drawing Code snippets - Deal with sheet and drawing models


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
}


======



----




<<discussion>> Family Table - Access data in a family table, and delete or add new instances


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


======



----







<<discussion>> Asynchronous mode - connect, start Creo, connect to Windchill


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

======

***Asyn & Windchill (Sample)***

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

======




----




***Assemblies***

<<discussion>> Assembly - information and assembling components


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





****Assemble a Component****



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


****Replace Component(s)****

======

#
# 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
}
======



****Component Path****

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


======



----



***Export to file***

<<discussion>> Export Data


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



======


----




<<discussion>> Geometric Data - Access geometrical data (x,y,z value) 


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



----


<<discussion>> Pattern

======
# 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

======


***Matrices***



<<discussion>> Matrix and Point (Vector) object, collinear axis within an assembly


======
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



----



<<discussion>> Feature - Access basic feature information


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]
 


======


----


***Files***

<<discussion>> Files - Open, save, erase Creo files


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
        }
}

======

----


***Selection of Creo geometric elements***

<<discussion>> Selection - Ask user to select, or fill model item info or component path info, highlight



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 

} 

======



----



<<discussion>> External Data - Write your own Data into a model file (like a SQLite DB)

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
}

======


<<discussion>> Save As - Write Solid Data to file 


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

======



<<discussion>> Worker - Create DXF and STEP files by monitor a folder


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
}

}





======





<<discussion>> Mapkeys - Use Mapkey in Asynchronous mode 


======

#
# 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

======

<<categories>> CAD