Writing a Custom Operation Calculation
You can write a custom C routine for APS to calculate operation duration for selected operations. The system calls your custom calculation routine if the operation's Custom Planner Rule field is selected.
About the Standard Operation Calculation Routine
The standard operation calculation routine is ol_opercalc. By default, it returns the value -1.0, which signals the algorithm to use the standard (that is, non-custom) calculation to determine the operation's duration. The C code for this routine is shown below.
#include "ol_api.h" double ol_opercalc(opercalc) opercalc_s* opercalc; { return(-1.0); }
Input to the Standard Routine
The input to ol_opercalc is a pointer to an opercalc_s structure. As shown below, this structure contains information used to calculate operation duration.
/*------------------------------------------------------------------------*/ typedef struct attid_struct /*------------------------------------------------------------------------*/ { char attid[MAX_OLXFSTR+1]; char attvalue[MAX_OLXFSTR+1]; } attid_s; /*------------------------------------------------------------------------*/ typedef struct reslist_struct /*------------------------------------------------------------------------*/ { char resource[MAX_OLXFSTR+1]; char category[MAX_OLXFSTR+1]; double catbufpre; double catbufpost; long numresattids; attid_s** resattidlist; } reslist_s; /*------------------------------------------------------------------------*/ typedef struct opercalc_struct /*------------------------------------------------------------------------*/ { char operation[MAX_OLXFSTR+1]; char order[MAX_OLXFSTR+1]; char part[MAX_OLXFSTR+1]; double quantity; char startwindow[14]; char endwindow[14]; long oprflags; double oprsetup; double oprcycle; double oprbufpre; double oprbufpost; long oprovltype; double oprovlvalue; long matltag; long numresources; reslist_s** resourcelist; long numoprattids; attid_s** oprattidlist; long numordattids; attid_s** ordattidlist; long numprtattids; attid_s** prtattidlist; } opercalc_s;
Output from the Standard Routine
There are two types of output from ol_opercalc:
- The function's return value. The expected return value from ol_opercalc is either the total operation duration in hours (as type double) or the value -1.0 (or any negative value). A negative value signals the system to use the standard (non-custom) operation calculation.
- A set of three parameters. These parameters are part of the opercalc_s
structure:
- oprsetup - Operation setup time (hours)
- oprovltype - Operation overlap type
- oprovlvalue - Operation overlap value
When the algorithm passes these parameters to ol_opercalc, the parameters contain the user-specified values from the database. You can modify them within ol_opercalc before returning them back to the algorithm. If they are modified, the system will respond as follows:
- oprsetup - The setup time will be changed for this operation.
- oprovltype - The overlap offset will be recalculated for this operation.
- oprovlvalue - The overlap offset will be recalculated for this operation.
You can enable operation overlapping through the original database value or by modifying oprovltype. If it is enabled, and if oprsetup is modified and/or the ol_opercalc return value denotes a different oprcycle value is in effect (considering setting of the operation's Fixed Schedule Hours and Custom Planner Rule fields), the system will recalculate the overlap offset for this operation.
Writing Your Custom Routine
When you write your routine, make sure the return value is of type double. The return value must represent the total required operation duration (in hours), for the specified number of parts using the specified resources, during the time interval available for the production.
The following example ol_opercalc routine determines the operation time for a particular resource.
#include "ol_api.h" double ol_opercalc(opercalc) opercalc_s* opercalc; { FILE* log_fp; int i,j; double dTime; log_fp = fopen("d:\\opercalc50.txt","a"); fprintf(log_fp, "\nopercalc: oper=>%s<\n", opercalc->operation ); fflush(log_fp); fprintf(log_fp, "opercalc: numoprattids=%d\n", opercalc->numoprattids ); fflush(log_fp); for (i=0;i<opercalc->numoprattids;i++) { fprintf(log_fp, "opercalc: opr attid =>%s<\n" "opercalc: opr attvalue=>%s<\n", opercalc->oprattidlist[i]->attid, opercalc->oprattidlist[i]->attvalue ); fflush(log_fp); } fprintf(log_fp, "opercalc: order=>%s<\n", opercalc->order ); fflush(log_fp); fprintf(log_fp, "opercalc: numordattids=%d\n", opercalc->numordattids ); fflush(log_fp); for (i=0;i<opercalc->numordattids;i++) { fprintf(log_fp, "opercalc: ord attid =>%s<\n" "opercalc: ord attvalue=>%s<\n", opercalc->ordattidlist[i]->attid, opercalc->ordattidlist[i]->attvalue ); fflush(log_fp); } fprintf(log_fp, "opercalc: part=>%s<\n", opercalc->part ); fflush(log_fp); fprintf(log_fp, "opercalc: numprtattids=%d\n", opercalc->numprtattids ); fflush(log_fp); for (i=0;i<opercalc->numprtattids;i++) { fprintf(log_fp, "opercalc: prt attid =>%s<\n" "opercalc: prt attvalue=>%s<\n", opercalc->prtattidlist[i]->attid, opercalc->prtattidlist[i]->attvalue ); fflush(log_fp); } fprintf(log_fp, "opercalc: quantity=%f\n", opercalc->quantity ); fflush(log_fp); fprintf(log_fp, "opercalc: start=>%s<\n", opercalc->startwindow ); fflush(log_fp); fprintf(log_fp, "opercalc: end=>%s<\n", opercalc->endwindow ); fflush(log_fp); fprintf(log_fp, "opercalc: oprflags=%d\n", opercalc->oprflags ); fflush(log_fp); fprintf(log_fp, "opercalc: oprsetup=%f\n", opercalc->oprsetup ); fflush(log_fp); fprintf(log_fp, "opercalc: oprcycle=%f\n", opercalc->oprcycle ); fflush(log_fp); fprintf(log_fp, "opercalc: oprbufpre=%f\n", opercalc->oprbufpre ); fflush(log_fp); fprintf(log_fp, "opercalc: oprbufpost=%f\n", opercalc->oprbufpost ); fflush(log_fp); fprintf(log_fp, "opercalc: oprovltype=%d\n", opercalc->oprovltype ); fflush(log_fp); fprintf(log_fp, "opercalc: oprovlvalue=%f\n", opercalc->oprovlvalue ); fflush(log_fp); fprintf(log_fp, "opercalc: numresources=%d\n", opercalc->numresources ); fflush(log_fp); for (i=0;i<opercalc->numresources;i++) { fprintf(log_fp, "opercalc: resource=>%s<\n", opercalc->resourcelist[i]->resource ); fflush(log_fp); fprintf(log_fp, "opercalc: category=>%s<\n", opercalc->resourcelist[i]->category ); fflush(log_fp); fprintf(log_fp, "opercalc: catbufpre=%f\n", opercalc->resourcelist[i]->catbufpre ); fflush(log_fp); fprintf(log_fp, "opercalc: catbufpost=%f\n", opercalc->resourcelist[i]->catbufpost ); fflush(log_fp); fprintf(log_fp, "opercalc: numresattids=%d\n", opercalc->resourcelist[i]->numresattids ); fflush(log_fp); for (j=0;j<opercalc->resourcelist[i]->numresattids;j++) { fprintf(log_fp, "opercalc: res attid =>%s<\n" "opercalc: res attvalue=>%s<\n", opercalc->resourcelist[i]->resattidlist[j]->attid, opercalc->resourcelist[i]->resattidlist[j]->attvalue ); fflush(log_fp); } } dTime = -1.0; if ( (!strcmp(opercalc->part,"SA-50910")) && (!strcmp(opercalc->operation, "SA-50910 SA-5.20")) && (!strcmp(opercalc->resourcelist[0]->resource,"PNT10.1")) ) { if ( opercalc->oprflags == 2 ) { fprintf(log_fp, "opercalc: per cycle\n"); fflush(log_fp); if ( opercalc->quantity > 10.0 ) { opercalc->oprsetup = 0.15; fprintf(log_fp, "opercalc: changing oprsetup\n"); opercalc->oprcycle = 0.25; fprintf(log_fp, "opercalc: changing oprcycle\n"); opercalc->oprovltype = 1; fprintf(log_fp, "opercalc: changing oprovltype\n"); opercalc->oprovlvalue = opercalc->quantity / 2; fprintf(log_fp, "opercalc: changing oprovlvalue\n"); opercalc->oprbufpre = opercalc->quantity / 2; fprintf(log_fp, "opercalc: changing oprbufpre to qty/2=%f\n", opercalc->oprbufpre); opercalc->oprbufpost = opercalc->quantity / 4; fprintf(log_fp, "opercalc: changing oprbufpost to qty/4=%f\n", opercalc->oprbufpost); // dTime = opercalc->oprcycle * opercalc->quantity; fflush(log_fp); } } else { fprintf(log_fp, "opercalc: per load\n"); fflush(log_fp); dTime = 1.0; /* opercalc->oprsetup = 0.0; opercalc->oprcycle = 0.1; opercalc->oprovltype = 1; opercalc->oprovlvalue = 3.0; */ } } fprintf(log_fp, "opercalc: dTime = >%f<\n", dTime ); fflush(log_fp); fclose(log_fp); return(dTime); }
Creating the Opercalc.dll
See Creating the Opercalc DLL for the Custom Operation Calculation Routine.
Invoking Your Custom Routine
When APS runs, it uses your custom routine only if the operation's JOBSTEP.STEPTMRL field is set to 2 (Custom Planner Rule selected, Fixed Schedule Hours contains a value) or 3 (Custom Planner Rule selected, Fixed Schedule Hours blank or 0). An operation with a STEPTMRL value of 2 uses fixed cycle time; a value of 3 uses per-piece cycle time.
If the operation's Custom Planner Rule is cleared, the system will not call the custom ol_opercalc routine.