/*
Copyright (c) 2024 PTC Inc. and/or Its Subsidiary Companies. All Rights Reserved.
*/
#include <ProToolkit.h>
#include <ProMdlChk.h>
#include <ProParameter.h>
#include <ProSolid.h>
#include <ProDrawing.h>
#include <ProFamtable.h>
#include <ProSelection.h>
#include <ProScope.h>
static wchar_t** s_results_table = NULL;
static wchar_t* s_results_url = NULL;
#define MISSING_PARAM (int)999
#define INVALID_PARAM_TYPE (int)9999
/*=====================================================================================================*\
FUNCTION: UserCheckMdlParamNameCompare
PURPOSE: Compare the model name to the value of the given param.
RETURNS: MISSING_PARAM, INVALID_PARAM_TYPE, or the integer results of the string comparison.
\*======================================================================================================*/
static int UserCheckMdlParamNameCompare (ProMdl mdl, ProName param_name)
{
ProError status = PRO_TK_NO_ERROR;
ProMdlName mdl_name;
ProModelitem modelitem;
ProParamvalue param_value;
ProParameter param;
int compare_result;
status = ProMdlMdlnameGet (mdl, mdl_name);
status = ProMdlToModelitem(mdl, &modelitem);
status = ProParameterInit(&modelitem, param_name, ¶m);
/*------------------------------------------------------------------------------------------------------*\
Parameter is missing
\*------------------------------------------------------------------------------------------------------*/
if (status != PRO_TK_NO_ERROR)
{
compare_result = MISSING_PARAM;
}
else
{
ProUnititem units;
status = ProParameterValueWithUnitsGet(¶m,¶m_value, &units);
/*------------------------------------------------------------------------------------------------------*\
Parameter is not a string parameter
\*------------------------------------------------------------------------------------------------------*/
if (status != PRO_TK_NO_ERROR || param_value.type != PRO_PARAM_STRING)
{
compare_result = INVALID_PARAM_TYPE;
}
else
{
/*------------------------------------------------------------------------------------------------------*\
Compare the model name to the parameter value.
\*------------------------------------------------------------------------------------------------------*/
ProWstringCompare (param_value.value.s_val, mdl_name, PRO_VALUE_UNUSED, &compare_result);
}
}
return (compare_result);
}
/*======================================================================================================*\
FUNCTION: UserCustCheckMdlParamName
PURPOSE: Check function for the ModelCheck check. Outputs details of the comparison as a ModelCheck error.
\*======================================================================================================*/
static ProError UserCustCheckMdlParamName (ProCharName name, ProMdl mdl, ProAppData appdata,
int* results_count, wchar_t** results_url, wchar_t*** results_table)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProName* param_name_ptr = (ProName*)appdata;
ProMdlName mdl_name;
int compare_result = UserCheckMdlParamNameCompare (mdl, *param_name_ptr);
ProLine error_msg;
/*------------------------------------------------------------------------------------------------------*\
Parameter is set correctly.
\*------------------------------------------------------------------------------------------------------*/
if (compare_result == 0)
{
*results_count = 0;
*results_url = NULL;
*results_table = NULL;
return (PRO_TK_NO_ERROR);
}
/*------------------------------------------------------------------------------------------------------*\
Parameter is not set correctly. Show an appropriate error message.
\*------------------------------------------------------------------------------------------------------*/
else
{
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
status = ProMdlMdlnameGet (mdl, mdl_name);
if (compare_result == MISSING_PARAM)
{
status = ProMessageToBuffer (error_msg, message_file, "UG CustomCheck: MDL PARAM NOT FOUND",
*param_name_ptr, mdl_name);
}
else if (compare_result == INVALID_PARAM_TYPE)
{
status = ProMessageToBuffer (error_msg, message_file, "UG CustomCheck: MDL PARAM INV TYPE",
*param_name_ptr, mdl_name);
}
else
{
status = ProMessageToBuffer (error_msg, message_file, "UG CustomCheck: MDL PARAM INCORRECT",
*param_name_ptr, mdl_name);
}
status = ProArrayAlloc(1, sizeof(ProWstring), 1, (ProArray*)&s_results_table);
s_results_table [0] = (wchar_t*) calloc (PRO_LINE_SIZE, sizeof (wchar_t));
ProWstringCopy (error_msg, s_results_table[0], PRO_VALUE_UNUSED);
/*------------------------------------------------------------------------------------------------------*\
This URL will be used for the "Check Details" link in the ModelCheck report.
\*------------------------------------------------------------------------------------------------------*/
s_results_url = (wchar_t*) calloc (PRO_PATH_SIZE, sizeof (wchar_t));
ProStringToWstring (s_results_url, "http://www.ptc.com/");
*results_table = s_results_table;
*results_count = 1;
*results_url = s_results_url;
return (PRO_TK_NO_ERROR);
}
}
/*======================================================================================================*\
FUNCTION: UserCustCheckMdlAccType
PURPOSE: Check function for the ModelCheck check for accuracy type. Outputs the accuracy type.
\*======================================================================================================*/
static ProError UserCustCheckMdlAccType (ProCharName name, ProMdl mdl, ProAppData appdata,
int* results_count, wchar_t** results_url, wchar_t*** results_table)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProAccuracyType acc_type;
double acc_value;
ProLine info_msg;
ProCharLine message_key;
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
/*------------------------------------------------------------------------------------------------------*\
Output the model accuracy type as an info check.
\*------------------------------------------------------------------------------------------------------*/
status = ProSolidAccuracyGet (mdl, &acc_type, &acc_value);
switch (acc_type)
{
case PRO_ACCURACY_ABSOLUTE:
strcpy (message_key, "UG CustomCheck: MDL ACC ABS");
break;
case PRO_ACCURACY_RELATIVE:
strcpy (message_key, "UG CustomCheck: MDL ACC REL");
break;
default:
return (PRO_TK_UNSUPPORTED);
}
status = ProMessageToBuffer (info_msg, message_file, message_key);
status = ProArrayAlloc(1, sizeof(ProWstring), 1, (ProArray*)&s_results_table);
s_results_table [0] = (wchar_t*) calloc (PRO_LINE_SIZE, sizeof (wchar_t));
ProWstringCopy (info_msg, s_results_table[0], PRO_VALUE_UNUSED);
*results_table = s_results_table;
*results_count = 1;
*results_url = NULL;
return (PRO_TK_NO_ERROR);
}
/*======================================================================================================*\
FUNCTION: UserCustCheckRefScope
PURPOSE: Check function for the ModelCheck check for reference scope. Outputs the scope permitted for
external refs.
\*======================================================================================================*/
static ProError UserCustCheckRefScope (ProCharName name, ProMdl mdl, ProAppData appdata,
int* results_count, wchar_t** results_url, wchar_t*** results_table)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProExtRefScope scope;
ProInvalidRefBehavior behavior;
ProLine info_msg;
ProCharLine message_key;
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
/*------------------------------------------------------------------------------------------------------*\
Output the model ref control level as an info check.
\*------------------------------------------------------------------------------------------------------*/
status = ProRefCtrlSolidGet (mdl, &scope, &behavior);
switch (scope)
{
case PRO_REFCTRL_ALLOW_ALL:
strcpy (message_key, "UG CustomCheck: MDL REFC ALL");
break;
case PRO_REFCTRL_ALLOW_SUBASSEMBLY:
strcpy (message_key, "UG CustomCheck: MDL REFC SUB");
break;
case PRO_REFCTRL_ALLOW_SKELETON:
strcpy (message_key, "UG CustomCheck: MDL REFC SKEL");
break;
case PRO_REFCTRL_ALLOW_NONE:
strcpy (message_key, "UG CustomCheck: MDL REFC NONE");
break;
default:
return (PRO_TK_UNSUPPORTED);
}
status = ProMessageToBuffer (info_msg, message_file, message_key);
status = ProArrayAlloc(1, sizeof(ProWstring), 1, (ProArray*)&s_results_table);
s_results_table [0] = (wchar_t*) calloc (PRO_LINE_SIZE, sizeof (wchar_t));
ProWstringCopy (info_msg, s_results_table[0], PRO_VALUE_UNUSED);
*results_table = s_results_table;
*results_count = 1;
*results_url = NULL;
return (PRO_TK_NO_ERROR);
}
/*======================================================================================================*\
FUNCTION: UserCustCheckDwgviewGeneric
PURPOSE: Check function for the ModelCheck check for generics in drawing views.
Outputs a list of the view names.
\*======================================================================================================*/
static ProError UserCustCheckDwgviewGeneric (ProCharName name, ProMdl mdl, ProAppData appdata,
int* results_count, wchar_t** results_url, wchar_t*** results_table)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProView* views;
int i, size = 0;
ProSolid solid;
ProFamtable fam_table;
wchar_t* table_entry;
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
/*------------------------------------------------------------------------------------------------------*\
Check the model shown by each view
\*------------------------------------------------------------------------------------------------------*/
status = ProDrawingViewsCollect ((ProDrawing)mdl, &views);
if (status == PRO_TK_NO_ERROR)
{
status = ProArraySizeGet (views, &size);
if (status == PRO_TK_NO_ERROR && size > 0)
{
status = ProArrayAlloc(0, sizeof(ProWstring), 1, (ProArray*)&s_results_table);
for (i = 0; i < size; i ++)
{
status = ProDrawingViewSolidGet ((ProDrawing)mdl, views[i], &solid);
/*------------------------------------------------------------------------------------------------------*\
If ProFamtableCheck() succeeds, this means that the model has a family table with at least one
instance or item in it.
\*------------------------------------------------------------------------------------------------------*/
status = ProFamtableInit (solid, &fam_table);
status = ProFamtableCheck (&fam_table);
if (status == PRO_TK_NO_ERROR)
{
table_entry = (wchar_t*) calloc (PRO_NAME_SIZE, sizeof (wchar_t));
ProDrawingViewNameGet ((ProDrawing)mdl, views [i], table_entry);
ProArrayObjectAdd ((ProArray*)&s_results_table, -1, 1, &table_entry);
}
}
/*------------------------------------------------------------------------------------------------------*\
If no items were found...
\*------------------------------------------------------------------------------------------------------*/
status = ProArraySizeGet (s_results_table, &size);
if (size == 0)
{
ProArrayFree ((ProArray*)&s_results_table);
s_results_table = NULL;
}
}
}
*results_table = s_results_table;
*results_count = size;
*results_url = NULL;
return (PRO_TK_NO_ERROR);
}
/*======================================================================================================*\
FUNCTION: UserCustCheckCleanUtility
PURPOSE: Cleanup function for all of the ModelCheck checks.
\*======================================================================================================*/
static ProError UserCustCheckCleanUtility (ProCharName name, ProMdl mdl, ProAppData appdata)
{
int size, i;
ProError status = PRO_TK_NO_ERROR;
if (s_results_table != NULL)
{
status = ProArraySizeGet (s_results_table, &size);
if (status == PRO_TK_NO_ERROR)
{
for (i = 0; i < size; i++)
free (s_results_table [i]);
ProArrayFree ((ProArray*)&s_results_table);
}
s_results_table = NULL;
}
if (s_results_url != NULL)
{
free (s_results_url);
s_results_url = NULL;
}
return (PRO_TK_NO_ERROR);
}
/*======================================================================================================*\
FUNCTION: UserCustUpdateMdlParamName
PURPOSE: Update function for a ModelCheck error due to an invalid or missing model name parameter.
\*======================================================================================================*/
static ProError UserCustUpdateMdlParamName (ProCharName name, ProMdl mdl, wchar_t* selected_item,
ProAppData appdata)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProName* param_name_ptr = (ProName*)appdata;
ProMdlName mdl_name;
int compare_result = UserCheckMdlParamNameCompare (mdl, *param_name_ptr);
ProModelitem modelitem;
ProParamvalue param_value;
ProParameter param;
if (compare_result == 0)
{
/* Nothing to do */
return (PRO_TK_NO_ERROR);
}
else
{
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
status = ProMdlMdlnameGet (mdl, mdl_name);
param_value.type = PRO_PARAM_STRING;
ProWstringCopy (mdl_name, param_value.value.s_val, PRO_VALUE_UNUSED);
status = ProMdlToModelitem (mdl, &modelitem);
/*------------------------------------------------------------------------------------------------------*\
Create the missing parameter with the correct value.
\*------------------------------------------------------------------------------------------------------*/
if (compare_result == MISSING_PARAM)
{
ProParameterWithUnitsCreate(&modelitem, *param_name_ptr, ¶m_value, NULL, ¶m);
status = ProMessageDisplay (message_file, "UG CustomCheck: MDL PARAM UPDATED",
*param_name_ptr);
}
/*------------------------------------------------------------------------------------------------------*\
Since there is no way to change a parameter's type except by deleting and recreating it, the
check will not attempt to repair this situation. Instead, show a message to the user.
\*------------------------------------------------------------------------------------------------------*/
else if (compare_result == INVALID_PARAM_TYPE)
{
status = ProMessageDisplay (message_file, "UG CustomCheck: MDL PARAM UPDATE TYPE",
*param_name_ptr);
}
/*------------------------------------------------------------------------------------------------------*\
Change the value of the parameter appropriately.
\*------------------------------------------------------------------------------------------------------*/
else
{
ProParameterInit (&modelitem, *param_name_ptr, ¶m);
ProParameterValueWithUnitsSet(¶m, ¶m_value, NULL);
status = ProMessageDisplay (message_file, "UG CustomCheck: MDL PARAM UPDATED",
*param_name_ptr);
}
return (PRO_TK_NO_ERROR);
}
}
/*======================================================================================================*\
FUNCTION: UserDwgviewFilterByName
PURPOSE: Filter function to find drawing views by matching the input name.
\*======================================================================================================*/
static ProError UserDwgviewFilterByName (ProDrawing drawing, ProView view, ProAppData app_data)
{
ProError status = PRO_TK_NO_ERROR;
ProName name;
wchar_t* target_name = (wchar_t*)app_data;
int compare_result;
status = ProDrawingViewNameGet (drawing, view, name);
ProWstringCompare (name, target_name, PRO_VALUE_UNUSED, &compare_result);
if (compare_result == 0)
return (PRO_TK_NO_ERROR);
else
return (PRO_TK_CONTINUE);
}
#define DELTA_X 10.0 /* Screen coordinates */
#define DELTA_Y 10.0 /* Screen coordinates */
/*======================================================================================================*\
FUNCTION: UserDwgviewHighlight
PURPOSE: Action function to highlight the drawing view.
\*======================================================================================================*/
static ProError UserDwgviewHighlight (ProDrawing drawing, ProView view, ProError filter_status,
ProAppData app_data)
{
ProError status = PRO_TK_NO_ERROR;
int sheet;
int win_id;
ProPoint3d outline [2];
ProPoint3d points [5];
ProColor old_color, highlite_color;
ProLinestyle old_ls;
status = ProDrawingViewSheetGet (drawing, view, &sheet);
status = ProDrawingCurrentSheetSet (drawing, sheet);
status = ProWindowCurrentGet (&win_id);
ProWindowRefresh (win_id);
/*------------------------------------------------------------------------------------------------------*\
Draw the outline of the view to highlight it.
\*------------------------------------------------------------------------------------------------------*/
status = ProDrawingViewOutlineGet (drawing, view, outline);
points[0][0] = outline[0][0] - DELTA_X;
points[0][1] = outline[0][1] - DELTA_Y;
points[0][2] = 0.0;
points[1][0] = outline[0][0] - DELTA_X;
points[1][1] = outline[1][1] + DELTA_Y;
points[1][2] = 0.0;
points[2][0] = outline[1][0] + DELTA_X;
points[2][1] = outline[1][1] + DELTA_Y;
points[2][2] = 0.0;
points[3][0] = outline[1][0] + DELTA_X;
points[3][1] = outline[0][1] - DELTA_Y;
points[3][2] = 0.0;
points[4][0] = outline[0][0] - DELTA_X;
points[4][1] = outline[0][1] - DELTA_Y;
points[4][2] = 0.0;
ProLinestyleSet (PRO_LINESTYLE_PHANTOM, &old_ls);
highlite_color.method = PRO_COLOR_METHOD_TYPE;
highlite_color.value.type = PRO_COLOR_HIGHLITE;
ProGraphicsColorModify (&highlite_color, &old_color);
ProGraphicsPolylineDraw(points, 5);
ProGraphicsColorModify (&old_color, NULL);
ProLinestyleSet (old_ls, NULL);
return (PRO_TK_E_FOUND);
}
/*======================================================================================================*\
FUNCTION: UserCustActionDwgviewGeneric
PURPOSE: Action function for the ModelCheck check that locates drawing views using generics.
\*======================================================================================================*/
static ProError UserCustActionDwgviewGeneric (ProCharName name, ProMdl mdl, wchar_t* selected_item,
ProAppData appdata)
{
ProFileName message_file;
ProError status = PRO_TK_NO_ERROR;
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
ProDrawingViewVisit ((ProDrawing)mdl, UserDwgviewHighlight, UserDwgviewFilterByName,
(ProAppData)selected_item);
return (PRO_TK_NO_ERROR);
}
/*======================================================================================================*\
FUNCTION: UserCustomModelChecksDefine
PURPOSE: Registers all of the custom Pro/TOOLKIT ModelCheck checks.
\*======================================================================================================*/
int UserCustomModelChecksDefine()
{
ProFileName message_file;
ProCharName model_check_name;
ProLine check_label, action_label, update_label;
static ProName param_name;
ProError status;
/*--------------------------------------------------------------------*\
Prepare the button labels
\*--------------------------------------------------------------------*/
/* Parameter name that should contain the model owner name */
ProStringToWstring (param_name, "MDL_NAME_PARAM");
ProStringToWstring (message_file, "pt_ug_modelcheck.txt");
/* The name of the check. */
strcpy(model_check_name, "CHKTK_UG_MDLPARAM_NAME");
/* The label for the check */
status = ProMessageToBuffer (check_label, message_file, "UG CustomCheck: MDL PARAM NAME", param_name);
/* Label for the button used to update the model for an
item found by the check */
status = ProMessageToBuffer (update_label, message_file, "UG CustomCheckUpdate: MDL PARAM NAME");
/*--------------------------------------------------------------------*\
Register the custom ModelCheck check. This check allows update
of the incorrect situation.
\*--------------------------------------------------------------------*/
status = ProModelcheckCheckRegister (model_check_name, check_label, NULL,
UserCustCheckMdlParamName,
UserCustCheckCleanUtility,
NULL,
NULL,
update_label,
UserCustUpdateMdlParamName,
(ProAppData) ¶m_name);
/*--------------------------------------------------------------------*\
Prepare the button labels
\*--------------------------------------------------------------------*/
/* The name of the check. */
strcpy(model_check_name, "CHKTK_UG_MDL_ACC_TYPE");
/* The label for the check */
status = ProMessageToBuffer (check_label, message_file, "UG CustomCheck: MDL ACC TYPE");
/*--------------------------------------------------------------------*\
Register the custom ModelCheck check (an info check, so no
action or update is possible).
\*--------------------------------------------------------------------*/
status = ProModelcheckCheckRegister (model_check_name, check_label, NULL,
UserCustCheckMdlAccType,
UserCustCheckCleanUtility,
NULL,
NULL,
NULL,
NULL,
NULL);
/*--------------------------------------------------------------------*\
Prepare the button labels
\*--------------------------------------------------------------------*/
/* The name of the check. */
strcpy(model_check_name, "CHKTK_UG_MDL_REFC_SCOPE");
/* The label for the check */
status = ProMessageToBuffer (check_label, message_file, "UG CustomCheck: MDL REFC SCOPE");
/*--------------------------------------------------------------------*\
Register the custom ModelCheck check (an info check, so no
action or update is possible).
\*--------------------------------------------------------------------*/
status = ProModelcheckCheckRegister (model_check_name, check_label, NULL,
UserCustCheckRefScope,
UserCustCheckCleanUtility,
NULL,
NULL,
NULL,
NULL,
NULL);
/*--------------------------------------------------------------------*\
Prepare the button labels
\*--------------------------------------------------------------------*/
/* The name of the check. */
strcpy(model_check_name, "CHKTK_UG_DWGVIEW_GENERIC");
/* The label for the check */
status = ProMessageToBuffer (check_label, message_file, "UG CustomCheck: DWGVIEW GENERIC", param_name);
/* Label for the button used to update the model for an
item found by the check */
status = ProMessageToBuffer (action_label, message_file, "UG CustomCheckAction: DWGVIEW GENERIC");
/*--------------------------------------------------------------------*\
Register the custom ModelCheck check; this check has a Highlight
action that users can use.
\*--------------------------------------------------------------------*/
status = ProModelcheckCheckRegister (model_check_name, check_label, NULL,
UserCustCheckDwgviewGeneric,
UserCustCheckCleanUtility,
action_label,
UserCustActionDwgviewGeneric,
NULL,
NULL,
NULL);
return (PRO_TK_NO_ERROR);
}