//===================================================== file = lb1_csim.c ====
//=  A CSIM18 simulation of a byte-based leaky bucket for packet traffic     =
//=   - Can be used for shaping or policing (testing for conformance)        =
//============================================================================
//=  Notes: 1) Time unit is the millisecond                                  =
//=         2) Assumes an input trace file with <delta_time, pkt_len> pairs  =
//=            with delta_time in real millisec and pkt_len in integer bytes =
//=         3) Assumes MEDIA_RATE is in bits per millsec                     =
//=         4) The trace input file is TRACE_FILE and the shapted output     =
//=            file is SHAPE_FILE                                            =
//=         5) Must manually set MEDIA_RATE, MEAN_RATE, and MAX_BURST.       =
//=            MAX_BURST must be divisible by 8.                             =
//=         6) Must manually set CUMUL_FLAG (if FALSE then delta time will   =
//=            be output to SHAPE_FILE)                                      =
//=         7) Must manually set POLICE_FLAG.                                =
//=         8) If a packet leaves at the exact time a byte is supposed to    =
//=            increment in the token bucket, a race condition makes the     =
//=            result unpredictable (i.e., whether the byte will increment   =
//=            before or after the packet transmission).                     =
//=--------------------------------------------------------------------------=
//=  History:   KJC (07/04/01) - Genesis (from lb_csim.c)                    =
//============================================================================
#pragma warn -pro                 // Disable function prototype warning
#pragma warn -stu                 // Disable undefined structure warning
#pragma warn -rch                 // Disable unreachable code warning
#pragma warn -ccc                 // Disable condition always true warning

//----- 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.0e3   // Output link rate in bits per millisec
#define MEAN_RATE         5.0e3   // Mean rate in bits per millisec
#define MAX_BURST        120000   // Max burst size in bits (token bucket size)
#define CUMUL_FLAG        FALSE   // Flag for cumulative or delta time
#define POLICE_FLAG        TRUE   // Flag to output non-conformance messages

//----- Globals ---------------------------------------------------------------
FILE     *Tf;                     // Trace (input) file pointer
FILE     *Sf;                     // Shaped (output) file pointer
FACILITY Server;                  // Server facility
TABLE    Bits_sent;               // Bits sent table
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
int      Conf_count;              // Counter for non-conformance events

//----- Prototypes ------------------------------------------------------------
void generate(void);              // Trace file traffic generator
void leaky_bucket(int pkt_len);   // Leaky bucket shaper
void byte_gen(void);              // Byte generator for token bucket

//=============================================================================
//==  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");
  Bits_sent = table("Bits sent table");
  Pkt_release_event = event("Packet release event");

  // Variable initializations (always start with a full token bucket)
  Total_count = 0;
  Byte_count = MAX_BURST / 8;
  Last_clock = 0.0;
  Conf_count = 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("= >>> Traffic trace input from file %s            \n", TRACE_FILE);
  printf("= >>> Shaped traffic output to file %s            \n", SHAPE_FILE);
  if (POLICE_FLAG == TRUE)
    printf("= >>> Policing is TRUE                          \n");
  printf("= Media rate              = %9.2f bits/msc        \n", MEDIA_RATE);
  printf("= Leaky bucket mean rate  = %9.2f bits/msc        \n", MEAN_RATE);
  printf("= Leaky bucket burst size = %9d bits (%d bytes)   \n",
    MAX_BURST, (MAX_BURST / 8));
  printf("============================================================= \n");
  printf("= Total simulation time   = %9.2f msec       \n", clock);
  printf("= Number of packets       = %9d pkts         \n", Total_count);
  printf("= Non-conformance events  = %9d events       \n", Conf_count);
  printf("= Throughput              = %9.2f bits/msec  \n",
    table_sum(Bits_sent) / clock);
  printf("= Mean response time      = %9.2f msec       \n", resp(Server));
  printf("= Mean length             = %9.2f pkts       \n", qlen(Server));
  printf("============================================================= \n");

  return;
}

//=============================================================================
//==  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
  Eof_flag = FALSE;
  while(!feof(Tf))
  {
    fscanf(Tf, "%s %s \n", in_string1, in_string2);
    time_stamp = atof(in_string1);
    pkt_len = atoi(in_string2);

    if ((time_stamp < 0.0) || (pkt_len <= 0))
    {
      printf("  >>> ERROR in input value (time_stamp = %f  pkt_len = %d) \n",
        time_stamp, pkt_len);
      exit(1);
    }

    hold(time_stamp);
    leaky_bucket(pkt_len);

    if (feof(Tf)) Eof_flag = TRUE;
  }

  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);

  // DEBUG...
  printf("%f -- %5d -----> ", clock, Byte_count);

  // Do the "token matching" by bytes
  Byte_count = Byte_count - pkt_len;
  if (Byte_count < 0)
  {
    Conf_count++;
    if (POLICE_FLAG == TRUE)
    {
      fprintf(Sf,"*** non-conformance at %f msec -- ", clock);
      fprintf(Sf,"Byte_count = %d bytes \n", Byte_count);
    }
    clear(Pkt_release_event);
    queue(Pkt_release_event);
  }

  // Transmit the matched packet and record the number of bits sent
  hold((8.0 * pkt_len) / MEDIA_RATE);
  record(8.0 * pkt_len, Bits_sent);

  // Output to shape file
  if (CUMUL_FLAG == TRUE)
    fprintf(Sf, "%f %d \n", clock, pkt_len);
  else
  {
    fprintf(Sf, "%f %d \n", (clock - Last_clock), pkt_len);
    Last_clock = clock;
  }

  // DEBUG...
  printf("%f -- %5d \n", clock, Byte_count);

  // Release the server
  release(Server);

  // Check for Done condition
  if (Eof_flag == TRUE)
  {
    hold(0.0);
    if (status(Server) == FREE) set(Done);
  }

  return;
}

//=============================================================================
//==  Token generation process                                               ==
//==   - Tokens are generated per byte                                       ==
//=============================================================================
void byte_gen(void)
{
  create("byte_gen");

  // Increment Byte_count up to BUCKET SIZE
  while(1)
  {
    hold(8.0 / MEAN_RATE);
    if (Byte_count < (MAX_BURST / 8)) Byte_count++;
    if (Byte_count >= 0) set(Pkt_release_event);
  }
}

