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&LT\n",
                opercalc->operation );
        fflush(log_fp);
  fprintf(log_fp,
                "opercalc:  numoprattids=%d\n",
                opercalc->numoprattids );
        fflush(log_fp);
  for (i=0;i&LTopercalc->numoprattids;i++)
        {
   fprintf(log_fp,
    "opercalc:  opr attid   =>%s&LT\n"
    "opercalc:  opr attvalue=>%s&LT\n",
                opercalc->oprattidlist[i]->attid,
    opercalc->oprattidlist[i]->attvalue );
            fflush(log_fp);
        }
  fprintf(log_fp,
                "opercalc:  order=>%s&LT\n",
                opercalc->order );
        fflush(log_fp);
        fprintf(log_fp,
                "opercalc:  numordattids=%d\n",
                opercalc->numordattids );
        fflush(log_fp);
  for (i=0;i&LTopercalc->numordattids;i++)
        {
   fprintf(log_fp,
                "opercalc:  ord attid   =>%s&LT\n"
    "opercalc:  ord attvalue=>%s&LT\n",
                opercalc->ordattidlist[i]->attid,
    opercalc->ordattidlist[i]->attvalue );
            fflush(log_fp);
        }
  fprintf(log_fp,
                "opercalc:  part=>%s&LT\n",
                opercalc->part );
        fflush(log_fp);
  fprintf(log_fp,
                "opercalc:  numprtattids=%d\n",
                opercalc->numprtattids );
        fflush(log_fp);
  for (i=0;i&LTopercalc->numprtattids;i++)
        {
   fprintf(log_fp,
          "opercalc:  prt attid   =>%s&LT\n"
    "opercalc:  prt attvalue=>%s&LT\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&LT\n",
                opercalc->startwindow );
        fflush(log_fp);
        fprintf(log_fp,
                "opercalc:  end=>%s&LT\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&LTopercalc->numresources;i++)
        {
   fprintf(log_fp,
                "opercalc:  resource=>%s&LT\n",
                opercalc->resourcelist[i]->resource );
            fflush(log_fp);
   fprintf(log_fp,
                "opercalc:  category=>%s&LT\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&LTopercalc->resourcelist[i]->numresattids;j++)
            {
       fprintf(log_fp,
        "opercalc:  res attid   =>%s&LT\n"
        "opercalc:  res attvalue=>%s&LT\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&LT\n", dTime );
        fflush(log_fp);
  fclose(log_fp);
  return(dTime);
}

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.