//==========================================================================
//  OTHERMOD.CC - Simple module definitions for
//		  Discrete System Simulation in OMNeT++
//
//  Simulated system: TUB Northen&Southen FDDI Rings
//
//  Author: Gabor.Lencse@hit.bme.hu
//==========================================================================

/*--------------------------------------------------------------*
  Copyright (C) 1996,97 Gabor Lencse,
  Technical University of Budapest, Dept. of Telecommunications,
  Stoczek u.2, H-1111 Budapest, Hungary.

  This file is distributed WITHOUT ANY WARRANTY. See the file
  `license' for details on this and other legal matters.
*--------------------------------------------------------------*/

#include <ctype.h>
#include <string.h>
#include <omnetpp.h>

#include "otherdef.h"
#include "fddi_def.h"
#include "othermod.h"

Define_Module( Stat )
Define_Module( FDDI_Sink )
//Define_Module( FDDI_Generator )
Define_Module_Like( FDDI_GeneratorFromTraceFile, FDDI_Generator )
Define_Module_Like( FDDI_GeneratorHistogram2x1D, FDDI_Generator )
Define_Module_Like( FDDI_GeneratorPiSquare2x1D, FDDI_Generator )
//Define_Module_Like( FDDI_GeneratorKSplit2x1D, FDDI_Generator )
//Define_Module_Like( FDDI_GeneratorKSplit2D, FDDI_Generator )
Define_Module( FDDI_Address_Generator )
Define_Module( FDDI_Generator4Ring )
Define_Module( FDDI_Generator4Sniffer )
Define_Module( FDDI_Monitor )
Define_Module( LoadControl )

void mystrlwr(char *s)
  {
  while (*s) {*s=(char)tolower(*s); s++;}
  }

char * RingID2prefix(int RingID)
  {
  switch ( RingID )
    {
    case 0:
      return RING_0_PREFIX;
    case 1:
      return RING_1_PREFIX;
    default:
      return 0;
    }
  }

void FDDI_Generator4Ring::activity ()
  {
  char msgname[64];
  int my_station_id = parentModule()->index(); // RO: use only in a ring made by indexing ...

  int no_msg = par("no_msg");
  int no_comps = par("no_comps");
  simtime_t wait_time = par("wait_time");

  for (int i = 1; i <= no_msg; i++)
    {
    // select destination randomly (but not the local station)
    int dest = intrand( no_comps-1 );
    if ( dest == my_station_id )
      dest++;
    strcpy(msgname,"FDDI_FRAME ");
    sprintf(msgname+strlen(msgname),"%d->%d",my_station_id,dest);
    cMessage *msg = new cMessage(msgname, FDDI_FRAME);
    msg->addPar("dest") = (long) dest;
    msg->addPar("source") = (long) my_station_id;
    msg->addPar("gentime") = (double) simTime();
    msg->setLength(50+intrand(4451-8)+idle_bytes); /* min_samples 50, max_samples 4500 */
    /* +idle_bytes: add the (no of idle symbols)/2   (16/2=8) */
    send(msg, "out"); // pass down the message for Token Ring MAC

    wait (wait_time);
    }
  }

void FDDI_Generator::activity ()
  {
  RingID = (short) (long) parentModule()->parentModule()->par("RingID");
  my_station_id = par("StationID");
  if ( my_station_id < 0 )
    return; // StationID < 0 means that this station should not generate traffic
  LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
  char address[12+1+10]; // 12 bytes+EOS, +10 is just for safety
  strcpy(address,par("address"));
  // filename is derived from station address
  char filepathname[100];
  #ifdef __MSDOS__
    strcpy(filepathname,"load\\");
  #else
    strcpy(filepathname,"load/");
  #endif
  strcat(filepathname,RingID2prefix(RingID));
  strcat(filepathname,address+6);
  strcat(filepathname,FileNameEnding());
  mystrlwr(filepathname); // for DOS compatibility
  f=fopen(filepathname,"r");
  if ( !f )
    {
    ev.printf("FDDI_Generator Warning: Cannot open input file %s\n",filepathname);
    ev.printf("No messages will be generated by %s\n",fullPath());
    //endSimulation();
    return;
    }
  InitStatistics(); // also schedules generator message(s)
  char msgname[64];
  int pkt_len,dest;
  while ( RetrieveDestLength(receive(),dest,pkt_len) ) // also schedules the next generator message
    {
    strcpy(msgname,"FDDI_FRAME ");
    sprintf(msgname+strlen(msgname),"%d->%d",my_station_id, dest);
    cMessage *msg = new cMessage(msgname, FDDI_FRAME);
    msg->addPar("dest") = (long) dest;
    msg->addPar("source") = (long) my_station_id;
    msg->addPar("gentime") = simTime();
    msg->setLength(pkt_len+idle_bytes); /* add the (no of idle symbols)/2 */
    send(msg, "out"); // pass down the message for Token Ring MAC
    }
  // the following message is appropiate only in the case
  // if load is taken directly from a trace file
  // in all other cases RetrieveDestLength MUST return true
  ev.printf("FDDI_Generator Warning: Trace input file %s exhausted\n",filepathname);
  ev.printf("No more messages will be generated by %s\n",fullPath());
  //endSimulation();
  }

void FDDI_GeneratorFromTraceFile::InitStatistics()
  {
  line = new char[50];
  cMessage *m = new cMessage("GenPack");

  if ( fgets(line,50,f) )
    {
    double t;
    sscanf(line,"%lf,",&t);
    LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
    t /= LoadMultiplier;
    scheduleAt(t,m);
    }
  }

bool FDDI_GeneratorFromTraceFile::RetrieveDestLength(cMessage *m, int &dest, int &pkt_len)
  {
  sscanf(line,"%*f,%d,%d\n",&pkt_len,&dest);
  if ( fgets(line,50,f) )
    {
    double t;
    sscanf(line,"%lf,",&t);
    LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
    t /= LoadMultiplier;
    scheduleAt(t,m);
    return true;
    }
  else
    return false;
  }

void FDDI_GeneratorHistogram2x1D::InitStatistics()
  {
  char line[101];

  fgets(line,101,f); // comment with the ID and address of a source Station
  while ( !feof(f) )
    {
    int dest;
    if ( !fgets(line,101,f) ) // the ID and address of a destination Station
      break; // !! This is a hack: EOF is detected too late...
    sscanf(line,"%i,",&dest);
    cMessage *m = new cMessage("GenPack"); // a self message, it will contain all info
    m->addPar("dest") = (long)dest; // ID of destination station
    cDoubleHistogram *delay = new cDoubleHistogram("inter-arrivial time");
    delay->loadFromFile(f); // read the distribution of the inter-arrival time
    m->addPar("delay").setDoubleValue(delay);
    cLongHistogram *length = new cLongHistogram("packet length");
    length->loadFromFile(f); // read the distribution of the packet length
    m->addPar("length").setDoubleValue(length);
    LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
    scheduleAt(simTime()+delay->random()/LoadMultiplier,m); // the 1st packet to 'dest'
    // Remark:
    // This generator will generate packets destined to those stations only
    // the ID of that was found in the statistical input file
    }
  }

bool FDDI_GeneratorHistogram2x1D::RetrieveDestLength(cMessage *m, int &dest, int &pkt_len)
  {
  pkt_len = (int) m->par("length");
  dest = (int) m->par("dest");
  LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
// debug begin
  double delay = (double) m->par("delay");
  if ( delay < 0 )
    error("Negative inter-arrival time of value %lf in %s", delay, fullPath());
  scheduleAt(simTime()+delay/LoadMultiplier,m);
// original code was:
//  scheduleAt(simTime()+(double)m->par("delay")/LoadMultiplier,m);
  return true;
  }

void FDDI_GeneratorPiSquare2x1D::InitStatistics()
  {
  char line[101];

  fgets(line,101,f); // comment with the ID and address of a source Station
  while ( !feof(f) )
    {
    int dest;
    if ( !fgets(line,101,f) ) // the ID and address of a destination Station
      break; // !! This is a hack: EOF is detected too late...
    sscanf(line,"%i,",&dest);
    cMessage *m = new cMessage("GenPack"); // a self message, it will contain all info
    m->addPar("dest") = (long)dest; // ID of destination station
    cPSquare *delay = new cPSquare;
    delay->loadFromFile(f); // read the distribution of the inter-arrivial time
    m->addPar("delay").setDoubleValue(delay);
    cPSquare *length = new cPSquare;
    length->loadFromFile(f); // read the distribution of the packet length
    m->addPar("length").setDoubleValue(length);
    LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
    scheduleAt(simTime()+delay->random()/LoadMultiplier,m); // the 1st packet to 'dest'
    // Remark:
    // This generator will generate packets destined to those stations only
    // the ID of that was found in the statistical input file
    }
  }

bool FDDI_GeneratorPiSquare2x1D::RetrieveDestLength(cMessage *m, int &dest, int &pkt_len)
  {
  pkt_len = (int) m->par("length");
  dest = (int) m->par("dest");
  LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
  scheduleAt(simTime()+(double)m->par("delay")/LoadMultiplier,m);
  return true;
  }

void FDDI_Address_Generator::activity ()
  {
  RingID = (short) (long) parentModule()->parentModule()->par("RingID");
  my_station_id = par("StationID");
  if ( my_station_id < 0 )
    return; // StationID < 0 means that this station should not generate traffic
  //LoadMultiplier = (double) parentModule()->parentModule()->par("LoadMultiplier");
  char address[12+1+10]; // 12 bytes+EOS, +10 is just for safety
  strcpy(address,par("address"));
  // filename is derived from station address
  char filepathname[100];
  #ifdef __MSDOS__
    strcpy(filepathname,"load\\");
  #else
    strcpy(filepathname,"load/");
  #endif
  strcat(filepathname,RingID2prefix(RingID));
  strcat(filepathname,address+6);
  strcat(filepathname,FileNameEnding());
  mystrlwr(filepathname); // for DOS compatibility
  f=fopen(filepathname,"r");
  if ( !f )
    {
    ev.printf("FDDI_Address_Generator Warning: Cannot open input file %s\n",filepathname);
    ev.printf("No messages will be routed by %s\n",fullPath());
    //endSimulation();
    return;
    }
  InitStatistics(); // just reads the histograms, makes no schedule
  char msgname[64];
  int pkt_len,dest;
  cMessage * m;
  for ( ; ; )
    {
    m = receive();
    pkt_len=m->length()-idle_bytes;
    dest = RetrieveNewAddress(pkt_len);
    strcpy(msgname,"FDDI_FRAME ");
    sprintf(msgname+strlen(msgname),"%d->%d",my_station_id, dest);
    cMessage *msg = new cMessage(msgname, FDDI_FRAME);
    msg->addPar("dest") = (long) dest;
    msg->addPar("source") = (long) my_station_id;
    msg->addPar("gentime") = simTime();
    msg->setLength(pkt_len+idle_bytes); /* add the (no of idle symbols)/2 */
    send(msg, "out"); // pass down the message for Token Ring MAC
    delete m;
    }
  }

void FDDI_Address_Generator::InitStatistics()
  {
  char line[101];

  fgets(line,101,f); // comment with the ID and address of a source Station
  while ( !feof(f) )
    {
    histogram_plus_address * hist_plus_addr = new histogram_plus_address;
    if ( !fgets(line,101,f) ) // the ID and address of a destination Station
      break; // !! This is a hack: EOF is detected too late...
    sscanf(line,"%i,",&hist_plus_addr->dest); // ID of destination station
    cDoubleHistogram *delay = new cDoubleHistogram;
    delay->loadFromFile(f); // read the distribution of the inter-arrivial time
    delete delay; // we do not use it!
    hist_plus_addr->length.loadFromFile(f); // read the distribution of the packet length
    length_histograms.add(&hist_plus_addr);
    // Remark:
    // This address generator will route packets to those stations only
    // the ID of that was found in the statistical input file
    }
  }

int FDDI_Address_Generator::RetrieveNewAddress(int pkt_len)
  {
  int i;
  double sum, random;
  cLongHistogram * hist;

  for ( i=0, sum=0; i < length_histograms.items(); i++)
    {
    hist = &((*( (histogram_plus_address **)(length_histograms[i]) ))->length);
    sum += hist->pdf(pkt_len)*hist->samples();
    }
  random = dblrand()*sum;
  for ( i=0, sum=0; sum<random; i++)
    {
    hist = &((*( (histogram_plus_address **)(length_histograms[i]) ))->length);
    sum += hist->pdf(pkt_len)*hist->samples();
    }
  return i-1;
  }

void FDDI_Generator4Sniffer::activity()
  {
  int my_station_id = par("StationID");
  char address[12+1+10]; // 12 bytes, +10 is just for safety
  strcpy(address,par("address"));

  if ( my_station_id < 0 )
    return; // StationID < 0 means that this station should not generate traffic

  // the following lines may be used to generate traffic e.g. for measurement purposes
  // cMessage *m = new cMessage("GenPack");
  //
  // while ( 1 )
  //   {
  //   scheduleAt(simTime()+uniform(0,100e-6),m);
  //   m=receive();
  //
  //   strcpy(msgname,"FDDI_FRAME from Sniffer to self");
  //   //sprintf(msgname+strlen(msgname),"%d->%d",my_station_id,dest);
  //   cMessage *msg = new cMessage(msgname, FDDI_FRAME);
  //   msg->addPar("dest") = (long) my_station_id;
  //   msg->addPar("source") = (long) my_station_id;
  //   msg->addPar("gentime") = simTime();
  //   msg->setLength(12); // just to be a short frame not to make much load
  //   // this length is inpossible in a real FDDI network!
  //   send(msg, "out");
  //   }
  }

void Stat::activity()
  {
  for(;;)
    {
    cMessage *msg = receive();
    delete msg;
    }
  }

void FDDI_Sink::activity()
  {
//  cOutVector queuing_time("queuing-time",1);
//  cOutVector transm_time("transmission-time",1);
  for(;;)
    {
    cMessage *msg = receive();
    // extract statistics and write out to vector file
//    simtime_t generated = msg->par("gentime"),
//	      sent	= msg->par("sendtime"),
//	      arrived	= msg->arrivalTime();
//    queuing_time.record( sent - generated );
//    transm_time.record( arrived - sent );
//    ev.printf("Time between creation and arrival: %lf\n", arrived-generated);
    delete msg;
    }
  }

void FDDI_Monitor::activity()
  {
  char outvectorname[100];
  sprintf(outvectorname,"packet-length in ring #%i",
    (short) (long) parentModule()->parentModule()->par("RingID"));
  cOutVector packet_length(outvectorname,1);
//  cOutVector queuing_time("queuing-time",1);
//  cOutVector transm_time("transmission-time",1);
  cBag queueing_time;
  for(;;)
    {
    cMessage *msg = receive();
    int length = msg->length()-idle_bytes;
/*  queueing time recording was commented out to fasten postprocessing
    simtime_t generated = msg->par("gentime"),
	      sent	= msg->par("sendtime"),
	      arrived	= msg->arrivalTime();
    int source = (long) msg->par("source");
    if ( queueing_time.isUsed(source) )
      outvect = * ((cOutVector **) (queueing_time[source]) );
    else
      {
      sprintf(outvectorname,"Queueing time in ring #%i, from station #%i",
	(short) (long) parentModule()->parentModule()->par("RingID"),source);
      outvect = new cOutVector(outvectorname,1);
      queueing_time.addAt(source,&outvect);
      }
    outvect->record( sent - generated );
*/
//    transm_time.record( arrived - sent );
//    ev.printf("Time between creation and arrival: %lf\n", arrived-generated);
    packet_length.record(length);
    delete msg;
    }
  }

void LoadControl::activity()
  {
  char filepathname[100];
  strcpy(filepathname,par("LoadControlFile"));
  FILE *f=fopen(filepathname,"r");
  if ( !f )
    {
    ev.printf("Load Control Warning: Cannot open input file %s\n",filepathname);
    ev.printf("No Load Control will be done by %s\n",fullPath());
    //endSimulation();
    return;
    }
  char line[101];
  fgets(line,101,f); // comment with the ID and namestr of the ring
  while ( !feof(f) )
    {
    double t, LoadMultiplier;
    if ( !fgets(line,101,f) ) // time and LoadMultiplier
      break; // !! This is a hack: EOF is detected too late...
    sscanf(line,"%lf %lf",&t,&LoadMultiplier);
    cMessage *m = new cMessage("LoadControlSelfMsg",LOAD_CONTROL_SELFMSG);
    scheduleAt(t,m);
    m = receive();
    delete m;
    parentModule()->par("LoadMultiplier")=LoadMultiplier;
    }
  }


syntax highlighted by Code2HTML, v. 0.9.1