Community Tip - Visit the PTCooler (the community lounge) to get to know your fellow community members and check out some of Dale's Friday Humor posts! X
In an assembly I want to visit all the ProCsys with a certain name and then get there coordinates. I have tried several examples which use ProSolidCsysVisit(), including the ProUtilCollectSolidCsys() and ProUtilCsysFind() functions. I know how to get the coordinates with the ProSelection typ by selecting a Csys with ProSelect() but now I have to use the ProCsys typ. But I also think I am not using the functions ProUtilCollectSolidCsys() and ProUtilCsysFind() correctly because I don´t get a list of ProCsys, only one value.
Can someone help me to understand this? There are also some things I didn´t find any explanation, like what is the "handle to the solid" or what does "PRO_TK_E_NOT_FOUND" mean?
Thanks a lot!
Tim
Please use below APIs:
Answers to your additional questions:
1. What is 'handle to the solid'?
Ans.: Handle to solid is pointer to solid i.e. ProSolid
2. What does PRO_TK_E_NOT_FOUND means?
Ans.: If any API returns error PRO_TK_E_NOT_FOUND then, it means that API found 0 objects which that API is supposed to look for in specified model.
Thank you very much, I will try it!
Maybe a stupid question but what exactly is a solid?
Solid is nothing but, the solid model in Creo Parametric. Please read Toolkit User's Guide to understand more in detail about Creo Parametric Toolkit.
Also, please open separate threads or cases for each query so that individual questions would be addressed separately in different threads.
I tried this way and it kind of worked but not the way I wanted. It visit some csys but only a few and I can't even find those csys in my Creo model. I think I have to go recursively in deeper subsystems, but I'm not sure. Here is what I have:
ProError FilterFunction(ProCsys p_csys, ProAppData app_data) {
ProModelitem r_geom_item;
ProName csysName;
ProSolid model = ((UserCsysAppData*)app_data)->model;
ProCsysToGeomitem(model, p_csys, &r_geom_item);
ProModelitemNameGet(&r_geom_item, csysName);
strcpy(((UserCsysAppData*)app_data)->csys_name, csysName);
if(strcmp(csysName,"ENTRY")){
return(PRO_TK_CONTINUE);
}
else {
return(PRO_TK_NO_ERROR);
}
}
ProError VisitActionFunction(ProCsys p_csys, ProError filter_return, ProAppData app_data) {
if (filter_return == PRO_TK_NO_ERROR) {
ProGeomitemdata* csys_data;
ProCsysDataGet(p_csys, &csys_data); // get data
int coor[3];
coor[0] = csys_data->data.p_csys_data->origin[0];
coor[1] = csys_data->data.p_csys_data->origin[1];
coor[2] = csys_data->data.p_csys_data->origin[2];
// write data
FILE* fp;
fp = fopen("Csys_data1.txt", "a");
ProName csysName;
strcpy(csysName, ((UserCsysAppData*)app_data)->csys_name);
char* csysName2 = (char*)calloc(100, sizeof(char));
ProWstringToString(csysName2, csysName);
fprintf(fp, "%s\t\t%d\t%d\t%d\n", csysName2, coor[0], coor[1], coor[2]);
fclose(fp);
return(PRO_TK_NO_ERROR);
}
}
ProMdl model;
UserCsysAppData app_data;
status = ProMdlCurrentGet(&model);
app_data.model = model;
status = ProSolidCsysVisit((ProSolid)model, (ProCsysVisitAction)VisitActionFunction, (ProCsysFilterAction)FilterFunction, (ProAppData)&app_data);
If I understand it correctly, ProSolidCsysVisit() gets the first Csys possible and then calls the filter function. The filter decides if the action function does something with that csys or not. What I don't understand is how ProSolidCsysVisit() navigates through the different Csys and where I need to do a recursive action to get ALL the Csys in the assembly.
Hi @Tim_1996,
Please go through response from @RolandRaytchev to understand how to recursively visit assembly and get assembly component. You can visit CSYS of each component and get its origin.
No, the visit function calls just a filter, only if the filter return PRO_TK_NO_ERROR you will have it in your final data. Without the filter you would have to do this in your code, with the filter you know that the result will already contain what you want. So you look for "ENTRY" in that case you will get a maximum of 1 csys, because names must be unique.
One last note, appdata should contain an array where you save all valid cordinate systems. You add this to the array in your filter.
But where does the ProCsys come from if not from ProSolidCsysVisit()? I just use the ProCsys which is handed over to the filter function.
Okay, I thought "ENTRY" would be the name because it is written at the Csys in the 3D environment. Than I have to filter by type or something. Any idea how I can get this "type" which is displayed at the Csys?
Here is a "Creo Parametric TOOLKIT User’s Guide" for 7.0 and 8.0.2.0 version:
Hi @Tim_1996 ,
because I did nothing the last 5 years with Toolkit possibly I do not remember accurately but so far I remember .
You meant you can select the coordinate system with ProSelect . In this case to understand where is the CSYS you can check selection with ProSelectionAsmcomppathGet() to get the Asmcomppath and print it to see the direct assembly component level.
- is the CSYS an assembly feature or it is a feature in some part which has specific asmcomppath. The mentioned workflow by @rghadge should work pretty good when the csys feature is an assembly feature or you know that it is in part or subassembly and you have the handle of the mdl /solid
But let say if it is inside a component or subassembly ->in this case so far I remember what I did in the past something like this:
I did first call ProSolidFeatVisit and used a filter to check if the feature is form type PRO_FEAT_COMPONENT
IF yes then I will then
-check the workflow suggested by @rghadge to find if the CSYS is inside of the current visited model. If not yes found and stop - if not found yet then continue with :
-check if the model is an assembly and call recursively the ProSolidFeatVisit . So this is one of the workflows what could be used when we search a specific CSYS feature in the assembly but we do not not know where is it -> in Top assembly or in some part /subassembly component on some specific component level depth.
OK let remember a name of a feature could be not unique. Means we could have in different components / part a feature with the same name.
So when I know the component path so I could e.g. take directly the part by ProAsmcomppathMdlGet() and then used the suggested workflow by
PS - the question what is ProSolid.
It is simple casted ProMdl object handle to ProSolid .... some thing like ....ProMdl mdl; (ProSolid) mdl.... - but, please, pay attention you can do this cast only if you are sure that the ProMdl type is PRO_ASSEMBLY or PRO_PART (othewise such casting do not make any sense)- so means before you will cast ProMdl handle to ProSolid you can check the ProMdl type e.g. by the fucntion ProMdlTypeGet()
I tried this way and it kind of worked but not the way I wanted. It visit some csys but only a few and I can't even find those csys in my Creo model. I think I have to go recursively in deeper subsystems, but I'm not sure. Here is what I have:
ProError FilterFunction(ProCsys p_csys, ProAppData app_data) {
ProModelitem r_geom_item;
ProName csysName;
ProSolid model = ((UserCsysAppData*)app_data)->model;
ProCsysToGeomitem(model, p_csys, &r_geom_item);
ProModelitemNameGet(&r_geom_item, csysName);
strcpy(((UserCsysAppData*)app_data)->csys_name, csysName);
if(strcmp(csysName,"ENTRY")){
return(PRO_TK_CONTINUE);
}
else {
return(PRO_TK_NO_ERROR);
}
}
ProError VisitActionFunction(ProCsys p_csys, ProError filter_return, ProAppData app_data) {
if (filter_return == PRO_TK_NO_ERROR) {
ProGeomitemdata* csys_data;
ProCsysDataGet(p_csys, &csys_data); // get data
int coor[3];
coor[0] = csys_data->data.p_csys_data->origin[0];
coor[1] = csys_data->data.p_csys_data->origin[1];
coor[2] = csys_data->data.p_csys_data->origin[2];
// write data
FILE* fp;
fp = fopen("Csys_data1.txt", "a");
ProName csysName;
strcpy(csysName, ((UserCsysAppData*)app_data)->csys_name);
char* csysName2 = (char*)calloc(100, sizeof(char));
ProWstringToString(csysName2, csysName);
fprintf(fp, "%s\t\t%d\t%d\t%d\n", csysName2, coor[0], coor[1], coor[2]);
fclose(fp);
return(PRO_TK_NO_ERROR);
}
}
ProMdl model;
UserCsysAppData app_data;
status = ProMdlCurrentGet(&model);
app_data.model = model;
status = ProSolidCsysVisit((ProSolid)model, (ProCsysVisitAction)VisitActionFunction, (ProCsysFilterAction)FilterFunction, (ProAppData)&app_data);
If I understand it correctly, ProSolidCsysVisit() gets the first Csys possible and then calls the filter function. The filter decides if the action function does something with that csys or not. What I don't understand is how ProSolidCsysVisit() navigates through the different Csys and where I need to do a recursive action to get ALL the Csys in the assembly.
Hey, Thank you first!
You are talking of ProSolidFeatVisit() and PRO_FEAT_COMPONENT, but I am looking for Csys. Is this important or do I have to use the equavilant functions for Csys?
Hi @Tim_1996 ,
here a old example (Creo 3.0) where I did visit assembly and highlighting components - should show the principle. Then name of it is not relevant- bug... coming from my old template. It is old nmake project - files in src subfolder. and the mean program and visit is in PTTestBug.c
Please, let me know if you have question to it. Thanks
... in the mentioned project in previous post the relevant code will show how to visit recursively the assembly. So you will have there a handle of each component /part or sub assembly.
For the question how to visit the CSYS in ProSolid I found a siimple code on old disk (fortunately it did not chrash) form my first attempst to do this many years ago therefore is not perfect but still possible it could help:
....
....
....
....
ProMdl theModel;
int status;
ProName theName;
char dummy[100];
status = ProMdlCurrentGet( &theModel );
JlCheckStatus( status );
status = ProMdlNameGet( theModel, theName );
JlCheckStatus( status );
ProMessageDisplay(msgfile,"user %s", "Hello" );
ProMessageDisplay( msgfile, "user %s %s",
"The name of the current Model is ",
ProWstringToString( dummy, theName ) );
status = UserCSYSTest((ProSolid) theModel);
....
....
....
//=================================
typedef struct surface_visit_data
{
FILE *fp;
ProSolid part;
} AxisVisitData_t;
/*==========================================================================
============================================================================*/
ProError UserDemoCsysAct(
ProCsys Csys,
ProError filt_status,
ProAppData app_data)
{
ProError status;
AxisVisitData_t *p_data = (AxisVisitData_t*)app_data;
ProSolid part = p_data->part;
FILE *fp = p_data->fp;
int id;
ProModelitem modelitem;
ProFeature feature;
ProFeattype ftype;
ProName wname;
char name[PRO_NAME_SIZE];
ProSelection selection;
ProBoolean visible;
/*---------------------------------------------------------------*\
Get the axis identifier.
\*---------------------------------------------------------------*/
status = ProCsysIdGet (Csys, &id);
if (status != PRO_TK_NO_ERROR)
return (status);
/*---------------------------------------------------------------*\
Make a ProModelitem handle for the axis.
\*---------------------------------------------------------------*/
status = ProModelitemInit (part, id, PRO_CSYS, &modelitem);
if (status != PRO_TK_NO_ERROR)
return (status);
/*---------------------------------------------------------------*\
Get the feature to which belongs.
\*---------------------------------------------------------------*/
status = ProGeomitemFeatureGet (&modelitem, &feature);
if (status != PRO_TK_NO_ERROR)
return (status);
/*---------------------------------------------------------------*\
Get the feature type.
\*---------------------------------------------------------------*/
status = ProFeatureTypeGet (&feature, &ftype);
if (status != PRO_TK_NO_ERROR)
return (status);
/*---------------------------------------------------------------*\
If the type was not HOLE, skip it.
\*---------------------------------------------------------------*/
if (ftype != PRO_FEAT_CSYS)
{
printf("\nFTYPE =%d\n",ftype);
return (PRO_TK_NO_ERROR);
}
status = ProFeatureVisibilityGet(&feature,&visible) ;
if(visible !=PRO_B_TRUE)
{
printf("\nNONVISIBLE FEATURE NUMBARO : %d\n", feature.id);
return (PRO_TK_NO_ERROR);
}
/*---------------------------------------------------------------*\
Get the name of the Csys.
\*---------------------------------------------------------------*/
status = ProModelitemNameGet (&modelitem, wname);
if (status != PRO_TK_NO_ERROR)
return (status);
ProWstringToString (name, wname);
/*---------------------------------------------------------------*\
Print out name and identifier.
\*---------------------------------------------------------------*/
fprintf (fp, "CSYS %s belongs to CSYS feature %d\n", name,
feature.id);
/*---------------------------------------------------------------*\
Highlight
\*---------------------------------------------------------------*/
ProSelectionAlloc (NULL, &feature, &selection);
ProSelectionHighlight (selection, PRO_COLOR_HIGHLITE);
ProSelectionFree (&selection);
return (PRO_TK_NO_ERROR);
}
ProError UserDemoCsysFlt(
ProCsys Csys,
ProAppData app_data)
{
AxisVisitData_t *p_data = (AxisVisitData_t*)app_data;
ProSolid part = p_data->part;
FILE *fp = p_data->fp;
ProFeature feature;
ProFeatStatus fstatus;
int id;
ProModelitem modelitem;
printf("\nFILTER FUNCTION\n");
status = ProCsysIdGet (Csys, &id);
if (status != PRO_TK_NO_ERROR)
return (status);
status = ProModelitemInit (part, id, PRO_CSYS, &modelitem);
if (status != PRO_TK_NO_ERROR)
return (status);
/*---------------------------------------------------------------*\
Get the feature to which the Csys belongs.
\*---------------------------------------------------------------*/
status = ProGeomitemFeatureGet (&modelitem, &feature);
if (status != PRO_TK_NO_ERROR)
return (status);
status = ProFeatureStatusGet(&feature,&fstatus);
if(fstatus == PRO_FEAT_SUPPRESSED) return (PRO_TK_CONTINUE);
else
return (PRO_TK_NO_ERROR);
}
//=======================================
where the function definition of
ProError UserCSYSTest(ProSolid part)
{
ProError status;
AxisVisitData_t data;
data.part = part;
/*---------------------------------------------------------------*\
Open the text file.
\*---------------------------------------------------------------*/
data.fp = fopen ("visit_test.dat","w");
/*---------------------------------------------------------------*\
Visit all the axes using the visit and filter functions
above. Pass the owning solid and the text file pointer
using the app_data argument.
\*---------------------------------------------------------------*/
status = ProSolidCsysVisit (part, UserDemoCsysAct,
UserDemoCsysFlt, (ProAppData)&data);
/*---------------------------------------------------------------*\
Close the file
\*---------------------------------------------------------------*/
fclose (data.fp);
return (PRO_TK_NO_ERROR);
}
....
....
....
There some of the object name are misleading -I think I wanted to do some quick test where I did not attapt all name of objects properly...
Hi @Tim_1996 ,
possibly you could ignorre my last post. I did not see that you already did this action ( receiving the csys in the ProSolid). I see that you posted code...
Hi @Tim_1996,
If you are interested to get CSYS features of assembly components (sub levels of assembly) then, yes you need to recursively visit assembly and get ProMdl (or ProSolid) of each component. And, then visit CSYS of each ProSolid.
Okay, so I always call ProSolidCsysVisit() after I got ProSolid and it is of the right type, correct? How can I get the ProSolid?
Use API ProMdlCurrentGet() to get ProMdl of top level assembly model. And, use API ProAsmcompMdlGet() to get ProMdl handle of assembly component.
You can type cast ProMdl as ProSolid.
And what do I hand over the function ProAsmcompMdlGet()? With the ProCsys it is not working.
In an assembly, if you have a component, this is a Feature. ProAsmcompMdlGet () will give you the model handle for that Feature ID, this is for sure a solid 🙂
From the feature ID 235 in MYASSY.ASM
OWNER MyAssy
Type FEATURE
ID 235
to the Model and RunTime ID
OWNER MyPart
Type PART
ID 34
Here you translate the feature ID to a Solid Model ID, because if you have now the Solid Model Handle, you can call CsysVisit for this Model as well, check the Tcl Code, your Logic should be similar. You have very ofter a complex var with Owner, Type and ID (Mostly the Model Item). Think about a Database where you do a 'Select ...' this is the same here. In Creo there are a lot of tables (Arrays) linked via Owner, Type,ID. It's a simple mapping.
A component can be assembled multiple times, but the Feature ID Path to each component is unique.
ProError ProAsmcompMdlGet (ProAsmcomp *p_feat_handle, ProMdl *p_mdl_handle);
Purpose: Retrieves the component model, given a component feature.
Input Arguments:
p_feat_handle - The handle to the component feature Output Arguments:
p_mdl_handle - The model handle to be initialized
I guess you have a synchronous app, you can setup your button to be only active in assembly mode, else the first think you check is that your current active model is an assembly. This is for sure a Solid 😉 und this current model is now your handle to call ProSolidCsysVisit. Your app data should have an empty initialized ProArray, this array is filled with your filter if your app says yes, this is what I need. After that Call to ProSolidCsysVisit you check the ProArray Size, if 0, upps nothing found, else you can loop through the Array or whatever you have setup in your AppData (I guess you have a couple a csys model items in one Array).
Note: If you do this recursive for all members in the assembly keep in mind that the origin data needs to be transformed to the assembly level, that means during parsing the assembly you need to make sure that the model ID path is saved or handled.
You see, there is a lot to consider 😫, but if you are 25 years old, you have plenty of time 😎, have fun!!
If you have an assembly, I thought you would like to search for a csys by a special name within the whole structure. If you only want to search the given assembly, you don't need to visit the assembly by components, in that case ProSolidCsysVisit() for the assembly is enough. And keep in mind, if you call ProCsysIdGet((ProCsys) csys, &ID) in your filter function, the ID is not the feature ID. You get the model item type PRO_CSYS, in the visit function, but this is not a PRO_FEATURE, so you need to get the feature id, to extract the feature name for your match. And only for the whole structure a matrix transform may required.
PRO_TK_E_NOT_FOUND will be returned if the call was successfully but no data was found.
SOLID, I would say a solid is everything where the Creo file extension is either '.PRT' (model type PRO_PART) or '.ASM' (model type PRO_ASSEMBLY)
Tcl Code less than 30 lines (in a colored image, after that clear uncolored code) follows, C logic is very similar > 30 lines 😀, even if you use your own filter function
# Side effects
# Each component is checked
# and the point data is not transformed
# to the top level assembly
#
# Function Input:
# FP : The handle to the open file
# MDL : The Name of the Solid Model
# Pat : The pattern to match, e.g A* match ACSYS
proc Get_Csys_Match {FP MDL PAT} {
foreach GEOM_ID [ps_visit csys -model $MDL] {
# Get the Feature ID for this geom id
set FEAT_ID [ps_geom to_feat_ids -model $MDL -- CSYS $GEOM_ID]
# 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 ,]
}
}
# Function Input:
# FP : The handle to the open file
# ASM : The Name of the Assembly
# 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_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_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
}
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
Sample Out
Have fun 😇