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

   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 3
-------------------------------------------------------------------------------
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; and, (3) the code is functionally
divided into event routines.
-----------------------------------------------------------------------------*/

#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

enum EventTypes {UNUSED, BEGIN_TOUR, REQUEST_CPU, RELEASE_CPU,
                 REQUEST_DISK, RELEASE_DISK};

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", characterized by its
"case statement".  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 case it turns out to be, each case is handled by an event
routine.
-----------------------------------------------------------------------------*/

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 (BEGIN_TOUR, 0.0, a_token); }
  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
    switch (es.event_id)
      {
      case BEGIN_TOUR  : BeginTour         (); break;
      case REQUEST_CPU : RequestCPU        (); break;
      case RELEASE_CPU : RlsCPU_SelectDisk (); break;
      case REQUEST_DISK: RequestDisk       (); break;
      case RELEASE_DISK: RlsDisk_EndTour   (); break;
      default: cout << "PULLED SOMETHING UNEXPECTED OUT OF THE HAT!\n";
      } //end ye olde "case statement"
    } // 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 (REQUEST_CPU, 0.0, a_token);
  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 (RELEASE_CPU, rn, a_token);
    }
  }

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

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

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

void RequestDisk ()
  {
  if (disk[tptr->un]->Request(a_token, 0) == FREE)
    {
    double rn = erlang (DISK_MEAN_TIME, DISK_STDEV);
    Future::Schedule (RELEASE_DISK, rn, a_token);
    }
  }

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

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 (BEGIN_TOUR, 0.0, a_token);
  nts --;
  }

// end cpudisk.cpp

