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:

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:

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

Creating the Opercalc.dll for the Custom Routine

The SyteLine setup program installs the standard version of the opercalc50.dll into your \winnt\system32 directory. This dll contains a dummy version of the ol_opercalc routine. You must create a customized opercalc50.dll containing your custom ol_opercalc routine that will be used instead of the standard opercalc50.dll.

To create this dll, follow these steps:

  1. Create a new empty Win32 Dynamic Link Library project in Visual C++.
  2. Move your Microsoft C source code file into the project directory.
  3. Add the source code file to the Source Files folder of the project. 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.
  4. Move the definition file (shown below) into the project directory. If there is a Definition Files folder, add the definition file to that folder. You may have to create the Definition Files folder.
             ;--------------------------------------------------
             ; OPERCALC50.DEF DLL library module definition file
             ;--------------------------------------------------
             LIBRARY OPERCALC50 INITINSTANCE
             DESCRIPTION 'SyteAPS V5.0 OperCalc', Copyright © 2010 Infor. All rights reserved. www.infor.com'
             ;
             EXPORTS
             ol_opercalc = _ol_opercalc@4
             _ol_opercalc = _ol_opercalc@4
             
  5. Make adjustments to the project settings. For example, you must:

    You must compile the code with the following code generation options to match those expected by the system's executable call:

  6. Build the project in either the debug or release form (there are separate settings for each). Messages should appear in the Build tab if there are any problems. If there are problems, modify the source code or project settings to fix them.
  7. Move the new opercalc50.dll file to the folder for the associated Planner project on the Planning server. For example, if the project is named Alt000, place the dll in \Planner\Projects\Alt000.

    NOTE: When you delete an APS Project, all of its folders and files are deleted, including the opercalc50.dll. Therefore, be careful never to put your Visual C++ source code (as mentioned in step 1) in the project folder.

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.


Related Topics

Current Operations