Schedule Regular Jobs

Some applications involve solving a mathematical program regularly, for instance every night or every ten minutes. In this article, we’ll discuss how an AIMMS job can reschedule itself, as depicted in the next picture.

../../_images/DelegationLevel.png

Effectively, this realizes that the job at hand is solved regularly. As you can see from this image:

  • The WebUI session you start via AIMMS PRO has an associated AIMMS data session. This data session has a pro::CurrentDelegationLevel() of 0; there is no delegation of work yet.

  • The AIMMS solver session started from this AIMMS Data Session has a pro::CurrentDelegationLevel() of 1; it is the consequence of one delegation.

  • The AIMMS solver session started from the above AIMMS solver session has a pro::CurrentDelegationLevel() of 1 higher than its predecessor.

To start this sequence, the following code is used.

1Procedure pr_OnButtonStartServerSessions {
2    Body: {
3        ep_PayloadProcedure := StringToElement( AllProcedures, "pr_Friesian", create: 0 );
4        pr_IterativeJobScheduling(
5            maxDelegateLevel   :  4,
6            timeIncrement      :  2[second],
7            epPayloadProcedure :  ep_PayloadProcedure);
8    }
9}

Each line is explained as follows:

  1. Determine the payload procedure that should be executed by each solver session. Here it is the pr_Friesian, as Friesian horses are excellent workhorses.

  2. A call to the procedure that actually starts each solver session.

  3. The number of server sessions started is controlled by maxDelegateLevel

  4. The argument timeIncrement, is the time between sessions measured in seconds.

  5. The procedure to be executed regularly passed via the arguments.

The center piece of the project is the procedure pr_IterativeJobScheduling:

 1Procedure pr_IterativeJobScheduling {
 2    Arguments: (maxDelegateLevel,timeIncrement,epPayloadProcedure);
 3    Body: {
 4        if pro::CurrentDelegationLevel() < maxDelegateLevel then
 5            if pro::DelegateToServer(
 6                    requestDescription :
 7                        formatString("The %i'th iteration of %e",
 8                            pro::CurrentDelegationLevel()+1,
 9                            epPayloadProcedure),
10                    waitForCompletion  :  0,
11                    completionCallback :  'pro::session::EmptyCallback',
12                    delegationOverride :  pro::CurrentDelegationLevel() + 1,
13                    scheduledAt        :
14                        if pro::CurrentDelegationLevel() then
15                            MomentToString( sp_LocalTimeFormat, [second],
16                                CurrentToString(sp_ReferenceTimeformat),
17                                timeIncrement )
18                        else "" endif
19                ) then
20                return 1 ;
21            endif ;
22        endif ;
23
24        Apply( epPayloadProcedure );
25    }
26    Parameter maxDelegateLevel {
27        Property: Input;
28    }
29    Parameter timeIncrement {
30        Unit: second;
31        Property: Input;
32    }
33    ElementParameter epPayloadProcedure {
34        Range: AllProcedures;
35        Default: 'MainExecution';
36        Property: Input;
37    }
38}

Each portion of the procedure code is explained below:

  1. Line 4: Limit the number of recurring jobs. When your application does not have a fixed number of jobs, you can remove this line and the argument maxDelegateLevel.

  2. Line 6: requestDescription: to properly identify each job in job overviews and in the session logs.

  3. Line 9: delegationOverride: necessary to submit jobs from within server sessions.

  4. Line 10: scheduleAt: Construct the next time the job is to be executed. By using the Local timezone, ambiguities regarding daylight saving time are avoided. Here sp_LocalTimFmt = "%c%y-%m-%d %H:%M:%S%TZ('Local')".

  5. Line 16: This will execute the payload for each of the server sessions started. The APPLY operator is used here.

To operate, the example that can be downloaded here.

  1. Create an .aimmspack, publish on your favorite AIMMS PRO system.

  2. Launch it and press the only button

  3. Close the app. Yes, once the sequence of server sessions is started, the WebUI of the enclosed example is no longer of use - it can be closed.

  4. Go to job tab in the AIMMS PRO portal and watch new jobs being created, queued, running, and finished.

../../_images/PROJobs.png

Note

  • When you check the session.log files, you may encounter a line like:

    12:10:46,186 0x7f6389d90700 [INFO] {PRO.Client.Library} pr_Friesian(): At 2018-09-04 12:10:46 (UTC) delegation level is 3
    

    That is because the procedure pr_Friesian uses the procedure call pro::management::LocalLogInfo(...); to log some information about current solver session.

  • When you want to interrupt a sequence of server jobs, please terminate the scheduled session before terminating the running session.

Note

The reference time format is initialized as follows:

 1Procedure PostMainInitialization {
 2    Body: {
 3        if pro::GetPROEndPoint() then
 4             pro::Initialize();
 5        endif ;
 6
 7        block ! Determine the reference time format to be used in MomentToString.
 8            p_getOPG := OptionGetValue(
 9                OptionName :  "use_UTC_forCaseAndStartEndDate",
10                Lower      :  p_lower,
11                Current    :  p_current,
12                Default    :  p_default,
13                Upper      :  p_upper);
14            if p_getOPG > 0 then
15                if p_current > 0 then
16                    sp_ReferenceTimeformat := sp_UTCTimeFormat ;
17                else
18                    sp_ReferenceTimeformat := sp_LocalTimeFormat ;
19                endif ;
20            else
21                ! option not defined.  Assume local time format.
22                sp_ReferenceTimeformat := sp_LocalTimeFormat ;
23            endif ;
24        endblock ;
25    }
26    Comment: {
27        "Add initialization statements here that require that the libraries are already initialized properly,
28        or add statements that require the Data Management module to be initialized."
29         Parameter p_getOPG;
30    Parameter p_lower;
31    Parameter p_current;
32    Parameter p_default;
33    Parameter p_upper;
34}