//===================================================== file = lb_csim.c =====
//=  A CSIM18 simulation of a byte-based leaky bucket for packet traffic     =
//============================================================================
//=  Notes: 1) Assumes an input file with <delta_time, pkt_len> pairs with   =
//=            delta_time in real seconds and pkt_len in integer bytes       =
//=         2) Assumes MEDIA_RATE is in bits per second                      =
//=         3) The trace file is TRACE_FILE and no error checking is         =
//=            performed on the opened file                                  =
//=         4) Shaped traffic is output to SHAPE_FILE                        =
//=         5) Must manually set MEDIA_RATE, BYTE_RATE, and BUCKET_SIZE      =
//=--------------------------------------------------------------------------=
//=  History:   KJC (06/14/00) - Genesis                                     =
//============================================================================

//----- Includes -------------------------------------------------------------
#include <stdio.h>                // Needed for printf(), feof(), and fscanf()
#include <stdlib.h>               // Needed for exit() and ato*()
#include "csim.h"                 // Needed for CSIM18 stuff

//----- Defines ---------------------------------------------------------------
#define      FALSE            0   // Boolean false
#define       TRUE            1   // Boolean true
#define TRACE_FILE   "trace.dat"  // Name of trace file
#define SHAPE_FILE   "shape.dat"  // Name of shaped traffic file

#define MEDIA_RATE       10.0e6   // Output Media rate in bits per sec
#define BYTE_RATE          1000   // Rate of bytes per sec to token bucket
#define BUCKET_SIZE        2000   // Token bucket size in bytes

//----- Globals ---------------------------------------------------------------
FILE     *Tf;                     // Trace file pointer
FILE     *Sf;                     // Shaped file pointer
FACILITY Server;                  // Server facility
EVENT    Done;                    // Done event
EVENT    Pkt_release_event;       // Packet release event
int      Eof_flag;                // End-of-file flag for trace file
int      Total_count;             // Total count of packets
int      Byte_count;              // Byte counter for token bucket
double   Last_clock;              // Clock value at last transmit

//----- Prototypes ------------------------------------------------------------
void generate(void);              // Trace file traffic generator
void byte_gen(void);              // Byte generator
void leaky_bucket(int pkt_len);   // Leaky bucket shaper

//=============================================================================
//==  Main program                                                           ==
//=============================================================================
void sim(void)
{
  // Create the simulation
  create("sim");

  // Increase max_processes
  max_processes(10000L);

  // Open the trace and shape files (clobbers any existing shape file)
  Tf = fopen(TRACE_FILE, "r");
  if (Tf == NULL)
  {
    printf("  >>> ERROR in opening trace file '%s' \n", TRACE_FILE);
    exit(1);
  }
  Sf = fopen(SHAPE_FILE, "w");
  if (Sf == NULL)
  {
    printf("  >>> ERROR in opening shape file '%s' \n", SHAPE_FILE);
    exit(1);
  }

  // CSIM initializations
  Server = facility("Server");
  Done = event("Done event");
  Pkt_release_event = event("Packet release event");

  // Variable initializations (always start with a full token bucket)
  Total_count = 0;
  Byte_count = BUCKET_SIZE;
  Last_clock = 0.0;

  // Initiate byte_gen() and generate() and wait for Done event to be set
  byte_gen();
  generate();
  wait(Done);

  // Output results
  printf("============================================================= \n");
  printf("==         *** CSIM18 leaky bucket simulation ***          == \n");
  printf("============================================================= \n");
  printf("= >>> Shaped traffic output to file %s \n", SHAPE_FILE);
  printf("= Media rate         = %f bits/sec     \n", MEDIA_RATE);
  printf("= Token byte rate    = %f bytes/sec    \n", BYTE_RATE);
  printf("= Token bucket size  = %f bytes        \n", BUCKET_SIZE);
  printf("============================================================= \n");
  printf("= Total CPU time     = %f sec          \n", cputime());
  printf("= Total sim time     = %f sec          \n", clock);
  printf("= Total packets      = %9d pkts        \n", Total_count);
  printf("============================================================= \n");
  printf("= Mean throughput    = %f pkts/sec     \n", tput(Server));
  printf("= Mean service time  = %f sec          \n", serv(Server));
  printf("= Mean queue len     = %f pkts         \n", qlen(Server));
  printf("= Mean response time = %f sec          \n", resp(Server));
  printf("============================================================= \n");
}

//=============================================================================
//==  Function to generate packets from a trace file                         ==
//=============================================================================
void generate(void)
{
  char     in_string1[256];       // Input string #1
  char     in_string2[256];       // Input string #2
  double   time_stamp;            // Delta time in seconds from trace file
  int      pkt_len;               // Packet length in bytes from trace file

  create("generate");

  // Read and generate packets until end-of-file
  //  - A negative length packet is the artificial Done event packet
  Eof_flag = FALSE;
  while(1)
  {
    fscanf(Tf, "%s %s \n", in_string1, in_string2);
    if (feof(Tf))
    {
      Eof_flag = TRUE;
      break;
    }
    time_stamp = atof(in_string1);
    pkt_len = atoi(in_string2);
    hold(time_stamp);
    leaky_bucket(pkt_len);
  }

  return;
}

//=============================================================================
//==  Leaky bucket process                                                   ==
//=============================================================================
void leaky_bucket(int pkt_len)
{
  create("leaky_bucket");

  // Increment total arriving packets counter
  Total_count++;

  // Reserve the server
  reserve(Server);

  // Do the "token matching" by bytes
  Byte_count = Byte_count - pkt_len;
  if (Byte_count < 0)
  {
    clear(Pkt_release_event);
    queue(Pkt_release_event);
  }

  // Transmit the matched packet
  hold((8.0 * pkt_len) / MEDIA_RATE);

  // Release the server
  release(Server);

  // Output to shape file
  fprintf(Sf, "%f %d \n", (clock - Last_clock), pkt_len);
  Last_clock = clock;

  // Check for Done condition
  if ((Eof_flag == TRUE) && (status(Server) == FREE))
    set(Done);

  return;
}

//=============================================================================
//==  Token generation process                                               ==
//=============================================================================
void byte_gen(void)
{
  create("byte_gen");

  // Increment Byte_count up to BUCKET SIZE
  while(1)
  {
    hold(1.0 / BYTE_RATE);
    if (Byte_count >= 0) set(Pkt_release_event);
    if (Byte_count < BUCKET_SIZE) Byte_count++;
  }

  return;
}

