/*=============================================================================

   Sim++ version 1.0 is the property of Paul A. Fishwick and Robert M. Cubert,
   copyright (c) 1995; see license in the root directory of the distribution.

cpudisk.cpp version 4
-------------------------------------------------------------------------------
This program has substantially the same logic as the program cpudisk.c which
used the simpack c-language API.  The differences are (1) this program is
written in c++; (2) it uses the sim++ ooAPI; (3) the code is functionally
divided into event routines; (4) event categories are identified by event
routines, not as enum or int event_id's; and (5) "ye olde case statement" is
gone from the "main while loop".
-----------------------------------------------------------------------------*/

#include "queuing.h"

const n0 = 6;         // no. class 0 tasks
const n1 = 3;         // no. class 1 tasks
const nt = n0+n1;     // total no. of tasks
const NUM_DISKS = 4;
const double DISK_MEAN_TIME = 30.0;
const double DISK_STDEV     =  2.5;
const double CPU_MEAN_TIMES [2] = {10.0, 5.0};  // task_classes are 0 and 1

struct token
  {
  int cls;         /* task's class (& priority) */
  int un;          /* disk for current IO req.  */
  double ts;       /* tour start time stamp     */
  };



/*=============================================================================
Fn prototype declarations for event routines:
-----------------------------------------------------------------------------*/

void BeginTour         (void);
void RequestCPU        (void);
void RlsCPU_SelectDisk (void);
void RequestDisk       (void);
void RlsDisk_EndTour   (void);



/*=============================================================================
Some symbols become global in order to be available to event routines.
-----------------------------------------------------------------------------*/

int nts = 500;                // # of tours to simulate
int counts [2] = {0, 0};
double elapsed_times [2] = {0.0, 0.0};
Facility * cpu = NULL;
Facility * disk [NUM_DISKS+1];
token * tptr = NULL;          // points to task [tkn_id];
Token a_token;                // temporary used to put things across API



/*=============================================================================
Main program.  After initialization is "main while loop".  main while loop is
controlled by nts.  nts is initialized above.  nts is decremented in
RlsDisk_EndTour().  Main while loop ends when nts reaches 0.  At each iteration
of "main while loop", the next event is fetched and then whatever event routine
it identifies, that event routine is called:  ye olde case statement is gone
from the main while loop.
-----------------------------------------------------------------------------*/

int main()
  {
  token task [nt+1];
  for (int ii=1; ii <= nt; ii++) task[ii].cls = (ii > n0 ? 1 : 0);
  new Future (LINKED);
  cpu = new Facility ("CPU", 1);
  for (ii=1; ii <= NUM_DISKS; ii++) disk [ii] = new Facility ("disk", 1);
  for (ii=1; ii <= nt; ii++)
    {
    a_token.Id (ii);
    Future::Schedule (BeginTour, 0.0, a_token, "Begin tour");
    }
  while (nts > 0) // ye olde main while loop
    {
    Estatus es = Future::NextEvent ();
    a_token = es.token;
    tptr = & task [a_token.Id()]; // used in event routines
    if (es.fn == NULL) ErrXit (0, "NULL event routine!");
    (* es.fn) (); // call the event routine:
    } // end ye olde "main while loop"
  Future::ReportStats();
  cout << "\n\n" << setprecision(2)
       << "class 0 tour time = " << elapsed_times [0] / counts [0] << '\n'
       << "class 1 tour time = " << elapsed_times [1] / counts [1] << '\n';
  return 0;
  }

/*=============================================================================
-----------------------------------------------------------------------------*/

void BeginTour ()
  {
  tptr->ts = Future::SimTime();
  Future::Schedule (RequestCPU, 0.0, a_token, "Request CPU");
  Future::UpdateArrivals();
  }

/*=============================================================================
-----------------------------------------------------------------------------*/

void RequestCPU ()
  {
  int task_class = tptr->cls;
  if (cpu->Preempt (a_token, task_class) == FREE)
    {
    double rn = expntl (CPU_MEAN_TIMES [task_class] );
    Future::Schedule (RlsCPU_SelectDisk, rn, a_token, "Rls CPU, Select disk");
    }
  }

/*=============================================================================
-----------------------------------------------------------------------------*/

void RlsCPU_SelectDisk ()
  {
  cpu->Release (a_token.Id() );
  tptr->un = random (1, NUM_DISKS);
  Future::Schedule (RequestDisk, 0.0, a_token, "Request a disk");
  }

/*=============================================================================
-----------------------------------------------------------------------------*/

void RequestDisk ()
  {
  if (disk[tptr->un]->Request(a_token, 0) == FREE)
    {
    double rn = erlang (DISK_MEAN_TIME, DISK_STDEV);
    Future::Schedule (RlsDisk_EndTour, rn, a_token,
      "Release disk, end tour, start next tour");
    }
  }

/*=============================================================================
-----------------------------------------------------------------------------*/

void RlsDisk_EndTour ()
  {
  disk[tptr->un]->Release (a_token.Id() );
  int task_class = tptr->cls;
  double now = Future::SimTime();
  elapsed_times [task_class] += now - tptr->ts;
  tptr->ts = now;
  (counts [task_class]) ++;
  Future::UpdateDepartures();
  Future::Schedule (BeginTour, 0.0, a_token, "Begin tour");
  nts --;
  }

// end cpudisk.cpp

