//================================================== file = thinClient.c =====
//=  A CSIM model of a thin client + server system (aka cloud computing)     =
//============================================================================
//=  Notes: Loosely adapted from Section 8.4 of M. Molly, "Fundamentals of   =
//=         Performance Modeling," Macmillan Publishing Company, 1989.       =
//=--------------------------------------------------------------------------=
//=  Execution: thinClient a1 a2 a3 a4 a5 a6 a7                              =
//=              - a1 = Simulation time (hours)                              =
//=              - a2 = Number of thin clients                               =
//=              - a3 = Mean think time of clients (seconds)                 =
//=              - a4 = Mean processing time in CPU (seconds)                =
//=              - a5 = Mean disk access time (seconds)                      =
//=              - a6 = Probability a job needs disk access                  =
//=              - a7 = Probability a job needs more processing              =
//=--------------------------------------------------------------------------=
//=  Build: Standard CSIM build                                              =
//=--------------------------------------------------------------------------=
//=  History: KJC (06/27/09) - Genesis                                       =
//=           KJC (07/05/11) - Minor clean-up                                =
//============================================================================
//----- Includes -------------------------------------------------------------
#include "csim.h"               // Needed for CSIM stuff
#include <stdio.h>              // Needed for printf()
#include <stdlib.h>             // Needed for atol() and atof()
#include <assert.h>             // Needed for assert() macro

//----- Globals --------------------------------------------------------------
FACILITY Client;                // Thin clients
FACILITY Cpu;                   // The server CPU
FACILITY Disk;                  // The server disk
TABLE    Resp_table;            // Table to collect response time measurements
EVENT    Job_event;             // Event to create new jobs for thin clients
double   Sim_time;              // Simulation run time (hours)
int      Num_clients;           // Number of thin clients
double   Think_time;            // Mean think time of clients (seconds)
double   Process_time;          // Mean processing time in CPU (millisec)
double   Disk_time;             // Mean disk access time (millisec)
double   Prob_process;          // Probability a job needs more processing
double   Prob_disk;             // Probability a job needs disk access

//----- Prototypes -----------------------------------------------------------
void gen(void);                     // Generate customers to thin clients
void client_process(void);          // Thin client process
void cpu_process(double org_time);  // CPU process
void disk_process(double org_time); // Disk process

//============================================================================
//==  Main program                                                          ==
//============================================================================
void sim(int argc, char *argv[])
{
  int i;                        // Loop index

  // Check if enough arguments
  if (argc != 8)
  {
    printf("Usage is: 'thinClient a1 a2 a3 a4 a5 a6 a7' where:          \n");
    printf("            - a1 = Simulation time (hours)                  \n");
    printf("            - a2 = Number of thin clients                   \n");
    printf("            - a3 = Mean think time of clients (seconds)     \n");
    printf("            - a4 = Mean processing time in CPU (seconds)    \n");
    printf("            - a5 = Mean disk access time (seconds)          \n");
    printf("            - a6 = Probability a job needs disk access      \n");
    printf("            - a7 = Probability a job needs more processing  \n");
    exit(1);
  }

  // Intialize operational parameters from command line entries
  Sim_time  = atof(argv[1]);
  Num_clients = atoi(argv[2]);
  Think_time = atof(argv[3]);
  Process_time = atof(argv[4]);
  Disk_time = atof(argv[5]);
  Prob_disk = atof(argv[6]);
  Prob_process = atof(argv[7]);

  // Create the simulation
  create("sim");

  // Initialize CSIM stuff
  Client = facility_ms("Thin clients", Num_clients);
  Cpu = facility("CPU");
  Disk = facility("Disk");
  Resp_table = table("Response time table");
  Job_event = event("Event to create new jobs");

  // Output begin-of-simulation banner
  printf("*** BEGIN SIMULATION *** \n");

  // Kick-off Num_clients with an initial job
  for (i=0; i<Num_clients; i++)
    client_process();

  // Kick off gen() and hold for Sim_time (in seconds)
  gen();
  hold(3600.0 * Sim_time);

  // Output results
  printf("============================================================= \n");
  printf("==  >>> CSIM cloud computing client/server simulation <<<  == \n");
  printf("============================================================= \n");
  printf("=  Simulation time         = %f hours   \n", Sim_time);
  printf("=  Number of thin clients  = %d clients \n", Num_clients);
  printf("=  Mean client think time  = %f sec     \n", Think_time);
  printf("=  Mean processing time    = %f sec     \n", Process_time);
  printf("=  Mean disk time          = %f sec     \n", Disk_time);
  printf("=  Prob of disk access     = %f         \n", Prob_disk);
  printf("=  Prob of more processing = %f         \n", Prob_process);
  printf("============================================================= \n");
  printf("=  CPU time                = %f sec     \n", cputime());
  printf("=  Numer of jobs completed = %d jobs    \n",
    table_cnt(Resp_table));
  printf("============================================================= \n");
  printf("=  Mean response time      = %f sec     \n",
    table_mean(Resp_table));
  printf("=-----------------------------------------------------------= \n");
  printf("=  Mean client resp time   = %f sec     \n", resp(Client));
  printf("=  Mean CPU resp time      = %f sec     \n", resp(Cpu));
  printf("=  Mean disk resp time     = %f sec     \n", resp(Disk));
  printf("=  Utilization of CPU      = %f %%      \n", 100.0 * util(Cpu));
  printf("=  Utilization of disk     = %f %%      \n", 100.0 * util(Disk));
  printf("============================================================= \n");

  // Output end-of-simulation banner
  printf("*** END SIMULATION *** \n");
}

//=============================================================================
//==  Process to generate new jobs for clients                               ==
//=============================================================================
void gen(void)
{
  create("gen");

  // Wait for job event to create a new job for a client
  clear(Job_event);
  while(1)
  {
    queue(Job_event);
    client_process();
  }
}

//=============================================================================
//==  Process for clients                                                    ==
//=============================================================================
void client_process(void)
{
  create("client");

  // Assert the there is never a queue
  assert(qlength(Client) == 0);

  // Reserve, hold, and release a client
  reserve(Client);
  hold(exponential(Think_time));
  release(Client);

  // Job goes to CPU
  cpu_process(clock);
}

//=============================================================================
//==  Process for CPU                                                        ==
//=============================================================================
void cpu_process(double org_time)
{
  create("cpu");

  // Reserve, hold, and release CPU
  reserve(Cpu);
  hold(exponential(Process_time));
  release(Cpu);

  // Test if job needs disk, more processing, or is done
  if (uniform(0.0, 1.0) < Prob_disk)
    disk_process(org_time);
  else if (uniform(0.0, 1.0) < Prob_process)
    cpu_process(org_time);
  else
  {
    record((clock - org_time), Resp_table);
    set(Job_event);
  }
}

//=============================================================================
//==  Process for disk                                                       ==
//=============================================================================
void disk_process(double org_time)
{
  create("disk");

  // Reserve, hold, and release disk
  reserve(Disk);
  hold(exponential(Disk_time));
  release(Disk);

  // Job always goes back to the CPU
  cpu_process(org_time);
}

