/* ----------------------------------------------------------------------
 * Fichier.........: gestion.c
 * Auteur.......: Fabrice Bossaert (bossaert@iutb.univ-paris13.fr)
 * Cree le......: septembre 1999
 * 
 * Description..: Simulation d'allocation de processeur avec plusieurs
 * file d'attente de priorites differentes
 * 
 * *** Histoire des revisions ***
 * 
 * ---------------------------------------------------------------------- */


#include <stdio.h>
#include <string.h>

/*Structure d'une tache
   Nombre Maximum de file d'attente
 */
#define N 100
typedef struct Proce
  {
    int Num_proc;
    int Etat_proc;
    int Temps_ex;
    int Periode_es;
    int Duree_es;
    int Temps_avant_es;
    int Ecoule_es;
    int Quantum_init;
    int Quantum_restant;
    int Temps_file;
  }
proce;

/*Structure des files d'attente */
typedef struct Liste
  {
    proce vala;
    struct Liste *suiv;
  }
liste;

/*Structure pour la gestion d'une file d'attente */
typedef struct ges_Liste
  {
    liste *P_list;
    liste *Fin_liste;
  }
ges_liste;

/*tab  : Tableau de files d'attente
   NBF  : Nombre courant de file d'attente
   CoefDes : Coef multiplicatif du quantum, si baiise de priorite
   gestar : Type de gestion
   CTTL : Coef multiplicatif du quantum pour determiner 
   si le process a trop attendu
   Temps_Ecoule : Compteur du temps
 */
ges_liste *tab[N];
liste *l;
int NBF, CoefDes;
int gestar;
int CTTL, Quantum;
int Temps_Ecoule = 0;
int Temps_Ecoule_Dernier_AFF = -1;
/******************************************************************************/
/* met_proc_file :
   Ajoute un process a la fin de la file t
 */
void
met_proc_file (ges_liste * t, liste * l)
{
  if (t->Fin_liste != 0)
    {
      (t->Fin_liste)->suiv = l;
    }
  t->Fin_liste = l;
  if (t->P_list == 0)
    t->P_list = t->Fin_liste;
}

/******************************************************************************/
/*ajoute_proc :
   Initialise les parametres d'un process et l'insere dans la file t
 */
void
ajoute_proc (ges_liste * t, int Nump, int Tex, int pes, int des)
{
  liste *l;
  l = (liste *) malloc (sizeof (liste));
  l->vala.Quantum_init = Quantum;
  l->vala.Quantum_restant = Quantum;
  l->vala.Num_proc = Nump;
  l->vala.Etat_proc = 'p';
  l->vala.Temps_ex = Tex;
  if (pes == -1)
    {
      pes = Tex + 1;
      des = -1;
    };
  l->vala.Periode_es = pes;
  l->vala.Temps_avant_es = pes;
  l->vala.Duree_es = des;
  l->vala.Ecoule_es = 0;
  l->vala.Temps_file = 0;
  l->suiv = 0;
  met_proc_file (t, l);
};

/******************************************************************************/
/* enleve_proc :
   Renvoie le process en tete de file t et le supprime de celle-ci
 */
liste *
enleve_proc (ges_liste * t)
{
  liste *l1;
  l1 = t->P_list;

  if (l1 == t->Fin_liste)
    {
      t->Fin_liste = 0;
      t->P_list = 0;

    }
  else
    {
      t->P_list = l1->suiv;
    }
  l1->suiv = 0;

  return l1;
}
/******************************************************************************/
/* maj_es_Temps_file :
   Gere le compteur de temps des E|S et repositionne les process en attente 
   dans l'etat pret.
   Si la gestion est dynamique: Incremente le temps passe dans la file 
   d'attente dans l'etat pret
 */

void
maj_es_Temps_file ()
{
  int nbfile;
  liste *l;
  nbfile = NBF;

  for (nbfile = 0; nbfile < NBF; nbfile++)
    {
      l = tab[nbfile]->P_list;

      while (l != 0)
	{
	  if (l->vala.Etat_proc == 'a')
	    {
	      l->vala.Ecoule_es--;
	      if (l->vala.Ecoule_es == 0)
		{
		  if (Temps_Ecoule != Temps_Ecoule_Dernier_AFF)
		    {
		      printf ("\n%d  T%1d Fin E/S",
			      Temps_Ecoule, l->vala.Num_proc);
		      Temps_Ecoule_Dernier_AFF = Temps_Ecoule;
		    }
		  else
		    {
		      printf ("  T%1d Fin E/S",
			      l->vala.Num_proc);
		    }
		  l->vala.Etat_proc = 'p';

		}
	    }
	  else
	    {
	      if (gestar == 3)
		{
		  l->vala.Temps_file++;
		}
	    };
	  l = l->suiv;
	}
    }
}

/******************************************************************************/
/*aff_file :
   Affichage des files d'attente
 */
void
aff_file ()
{
  int nbfile;
  liste *l;
  nbfile = NBF;

  for (nbfile = NBF - 1; nbfile >= 0; nbfile--)
    {
      l = tab[nbfile]->P_list;
      if (l == 0)
	{
	  printf ("");
	};
      while (l != 0)
	{
	  printf ("%1d%c", l->vala.Num_proc, l->vala.Etat_proc);

	  l = l->suiv;
	}
      printf ("/");
    }
}

/******************************************************************************/
/* Premier pret :
   Passe en tete de la file le premier process pret de la file
 */
void
premier_pret (ges_liste * t)
{
  ges_liste *tt;
  liste *l, *ll;
  tt = (ges_liste *) malloc (sizeof (ges_liste));
  tt->P_list = 0;
  tt->Fin_liste = 0;
  l = 0;
  while (t->P_list != 0)
    {

      if (t->P_list->vala.Etat_proc == 'a')
	{
	  ll = enleve_proc (t);
	  met_proc_file (tt, ll);
	}
      else
	{
	  if (l == 0)
	    {
	      l = enleve_proc (t);
	    }
	  else
	    {
	      ll = enleve_proc (t);
	      met_proc_file (tt, ll);
	    }
	}
    }
  if (l != 0)
    {
      met_proc_file (t, l);
    }
  while (tt->P_list != 0)
    {
      l = enleve_proc (tt);
      met_proc_file (t, l);
    }
  free (tt);

}
/******************************************************************************/
/* election :
   Renvoi le numero de la file la plus prioritaire ou ce trouve un process 
   dans l'etat pret
 */
int
election ()
{
  int nbfile, elu;
  liste *l;
  nbfile = NBF - 1;
  elu = -1;

  while ((elu == -1) && (nbfile != -1))
    {
      if (tab[nbfile]->P_list != 0)
	{
	  premier_pret (tab[nbfile]);
	};
      l = tab[nbfile]->P_list;
      if (l != 0)
	{
	  if (l->vala.Etat_proc == 'p')
	    elu = nbfile;
	};
      nbfile--;
    }

  return elu;
}

/******************************************************************************/
/* destruc_file :
   Libere un file vide
 */
void
destruc_file ()
{
  if (tab[NBF - 1]->P_list == 0)
    {
      free (tab[NBF - 1]);
      NBF--;
    }
}

/******************************************************************************/
/* creation_file :
   Creation d'une file d'attente
 */
void
creation_file ()
{
  tab[NBF] = (ges_liste *) malloc (sizeof (ges_liste));
  tab[NBF]->P_list = 0;
  tab[NBF]->Fin_liste = 0;
  NBF++;
}

/******************************************************************************/
/* maj_temps_trop_long :
   Augmente la priorite d'un process ayant trop attendu
 */
void
maj_temps_trop_long ()
{
  int nbfile;
  liste *l, *ltraiter;
  ges_liste *t;
  nbfile = NBF;

  for (nbfile = 0; nbfile < NBF; nbfile++)
    {
      t = (ges_liste *) malloc (sizeof (ges_liste));
      t->P_list = 0;
      t->Fin_liste = 0;
      while (tab[nbfile]->P_list != 0)
	{
	  ltraiter = enleve_proc (tab[nbfile]);
	  if ((ltraiter->vala.Etat_proc == 'a')
	      || ((ltraiter->vala.Quantum_restant * CTTL)
		  >= (ltraiter->vala.Temps_file)))
	    {
	      met_proc_file (t, ltraiter);
	    }
	  else
	    {
	      if ((ltraiter->vala.Quantum_restant * CTTL)
		  < (ltraiter->vala.Temps_file))
		{

		  ltraiter->vala.Temps_file = 0;
		  if (((nbfile + 1) == NBF) && (t->P_list != 0)
		      && (tab[nbfile]->P_list != 0))
		    creation_file ();
		  if ((nbfile + 1) != NBF)
		    {
		      met_proc_file (tab[nbfile + 1], ltraiter);
		    }
		  else
		    {
		      met_proc_file (t, ltraiter);
		    };
		}
	    }
	}
      while (t->P_list != 0)
	{
	  ltraiter = enleve_proc (t);
	  met_proc_file (tab[nbfile], ltraiter);
	}
      free (t);
    }
}

/******************************************************************************/
/* gestion :
   Gestion proprement dite des process en file d'attente
 */
void
gestion ()
{
  int File_sel, File_sel_proch, NEW_prior;
  int Temps_avant_es;
  liste *proc_cpu;


  while (NBF > 0)
    {

      File_sel = election ();

      if (File_sel == -1)
	{
	  Temps_Ecoule++;
	  maj_es_Temps_file ();

	  if (gestar == 3)
	    maj_temps_trop_long ();
	}
      else
	{

	  proc_cpu = enleve_proc (tab[File_sel]);
	  if (Temps_Ecoule != Temps_Ecoule_Dernier_AFF)
	    {
	      printf ("\n%d ", Temps_Ecoule);
	      Temps_Ecoule_Dernier_AFF = Temps_Ecoule;
	    };

	  File_sel_proch = File_sel;


	  printf (" T%1d C ", proc_cpu->vala.Num_proc);

	  aff_file ();


	  while ((proc_cpu->vala.Temps_avant_es != 0)
		 && (proc_cpu->vala.Temps_ex != 0) &&
		 (proc_cpu->vala.Quantum_restant != 0))
	    {
	      Temps_Ecoule++;
	      proc_cpu->vala.Temps_avant_es--;
	      proc_cpu->vala.Temps_ex--;
	      proc_cpu->vala.Quantum_restant--;
	      maj_es_Temps_file ();
	      if (gestar == 3)
		maj_temps_trop_long ();
	    }

	  if (proc_cpu->vala.Temps_avant_es == 0)
	    {
	      printf ("\n%d  T%1d Deb E/S", Temps_Ecoule,
		      proc_cpu->vala.Num_proc);
	      Temps_Ecoule_Dernier_AFF = Temps_Ecoule;
	      proc_cpu->vala.Ecoule_es = proc_cpu->vala.Duree_es;
	      proc_cpu->vala.Temps_avant_es = proc_cpu->vala.Periode_es;
	      proc_cpu->vala.Etat_proc = 'a';
	      NEW_prior = NBF;
	      if (proc_cpu->vala.Quantum_restant != 0)
		NEW_prior = (int) (proc_cpu->vala.Quantum_init
				   / (proc_cpu->vala.Quantum_init - proc_cpu->vala.Quantum_restant));

	      if (NEW_prior < File_sel)
		NEW_prior = File_sel;
	      proc_cpu->vala.Quantum_restant = proc_cpu->vala.Quantum_init;
	      if (gestar != 3)
		{
		  met_proc_file (tab[File_sel], proc_cpu);
		}
	      else
		{
		  if (NEW_prior < NBF)
		    {
		      met_proc_file (tab[NEW_prior], proc_cpu);
		    }
		  else
		    {
		      if (tab[NBF - 1]->P_list != 0)
			{
			  creation_file ();
			};
		      met_proc_file (tab[NBF - 1], proc_cpu);
		    }
		}
	    }
	  else
	    {
	      if (proc_cpu->vala.Temps_ex == 0)
		{
		  printf (" T%1d fin",
			  proc_cpu->vala.Num_proc);
		  free (proc_cpu);
		}
	      else
		{
		  if (proc_cpu->vala.Quantum_restant == 0)
		    {
		      if ((File_sel == 0) || (gestar != 3))
			{
			  proc_cpu->vala.Quantum_restant =
			    proc_cpu->vala.Quantum_init;
			  met_proc_file (tab[File_sel], proc_cpu);

			}
		      else
			{
			  proc_cpu->vala.Quantum_init *= CoefDes;
			  proc_cpu->vala.Quantum_restant
			    = proc_cpu->vala.Quantum_init;
			  met_proc_file (tab[File_sel - 1], proc_cpu);
			}
		    }
		}
	    }
	}


      destruc_file ();

    }


}

/******************************************************************************/
/* init_process :
   Creation des files d'attente et process, en fonction du fichier nom
 */
void
init_process (char *nom)
{
  FILE *f;
  int priorite, Nump, Temps_ex, Period_es, Duree_es;
  char *fin;
  printf ("%s\n", nom);
  f = fopen (nom, "r+");
  if (f == 0)
    {
      printf ("Lecture %s impossible \n", nom);
    }
  else
    {
      switch (gestar)
	{

	case 1:
	  fin = (char *)malloc(100*sizeof(char));
	  creation_file ();
	  break;
	case 2:

	  break;
	case 3:
	  creation_file ();
	  creation_file ();

	  break;
	}
      while (!feof (f))
	{


	  if (!fscanf (f, "%d", &Nump))
	    {
	      printf ("Erreur dans la structure du fichier %s\n", nom);
	      exit (-1);
	    };
	  if (!feof (f))
	    {
	      if (!fscanf (f, "%d", &Temps_ex))
		{
		  printf ("Erreur dans la structure du fichier %s\n", nom);
		  exit (-1);
		};
	      if (!fscanf (f, "%d", &Period_es))
		{
		  Period_es = -1;
		};
	      if (!fscanf (f, "%d", &Duree_es))
		{
		  Duree_es = -1;
		};
	      if ((gestar == 2) || (gestar == 3))
		{
		  if (!fscanf (f, "%d", &priorite))
		    {
		      printf (" Erreur dans la structure du fichier %s\n", nom);
		      exit (-1);
		    };
		  while (priorite >= NBF)
		    creation_file ();
		}
		else 
		{
		
		fgets(fin,100,f);
		};
		
	      switch (gestar)
		{

		case 1:
		  ajoute_proc (tab[0], Nump, Temps_ex, Period_es, Duree_es);
		  break;
		case 2:

		  ajoute_proc (tab[priorite], Nump, Temps_ex, Period_es, Duree_es);
		  break;
		case 3:
		  ajoute_proc (tab[priorite], Nump, Temps_ex, Period_es, Duree_es);

		  break;
		};
	    }
	};
    if (gestar == 1){free(fin);};
    }
}

/******************************************************************************/
/* gest_param :
   Gestion des parametres de la ligne de commande
 */
void
gest_param (int argc, char *argv[])
{
  char *gestop;


  if (argc == 2)
    {
      if ((argv[1][0] == '-') && (argv[1][1] == 'h'))
	{
	  printf ("Version du 17 09 98\n");
	  printf ("Ce programme a pour but de simuler 3 types d'ordonnancement de taches\n");
	  printf ("a savoir : Ordonnancement circulaire a une seule file d'attente, ordonnancement\n");
	  printf ("circulaire a plusieur files d'attente avec priorite statique et \n");
	  printf ("pour finir un ordonnancement  a plusieur files d'attente avec gestion dynamique des priorites.\n");
	  printf ("La syntaxe de la commande est la suivante :\n\n");
	  printf ("%s nom_fic c|s|(d Coef_Baisse Coef_Limit) Quantum\n\n", argv[0]);
	  printf ("nom_fic : Nom du fichier des process \n");
	  printf ("c : Ordonnancement circulaire a une file d'attente\n");
	  printf ("s : Idem \"c\" avec plusieur files d'attente\n");
	  printf ("d : Idem \"s\" avec gestion des priorites dynamique\n");
	  printf ("    Coef_Baisse etant un coef multiplicateur du quantum en cas\n");
	  printf ("    de baisse de priorite\n");
	  printf ("    Coef_Limit multiplie par le quantum est un seuil ou l' on decide\n");
	  printf ("    que le temps pour lequel une tache a attendu est trop\n");
	  printf ("    long.\n");
	  printf ("La structure du fichier process est la suivante :\n");
	  printf ("Pour l'Ordonnancement c :\n");
	  printf ("Numero de la tache Temps d'execution Periodicite de l'E/S Duree de l'E/S\n");
	  printf ("exemple :\n");
	  printf ("1	100	20	60\n");
	  printf ("Pour l'Ordonnancement s et d :\n");
	  printf ("Numero de la tache  Temps d'execution  Periodicite de l'E/S Duree E/S Priorite\n");
	  printf ("exemple :\n");
	  printf ("1	100	20	60	3\n");
	  printf ("remarque s'il n'y a pas d'E/S on poura mettre - comme symbole exemple :\n");
	  printf ("1	100	-	-	3\n");
	  printf ("Bon TRAVAIL\n");
	  printf ("Fabrice BOSSAERT\n");

	  exit (-1);
	};
    }
  if ((argc != 4) && (argc != 6))
    {
      printf ("%s nom_fic c|s|(d Coef_Baisse Coef_Limit) Quantum\n\n", argv[0]);
      printf ("nom_fic : Nom du fichier des process \n");
      printf ("c : Ordonnancement circulaire a une file d'attente\n");
      printf ("s : Idem \"c\" avec plusieur files d'attente\n");
      printf ("d : Idem \"s\" avec gestion des priorites dynamique\n");
      printf ("    Coef_Baisse etant un coef multiplicateur du quantum en cas\n");
      printf ("    de baisse de priorite\n");
      printf ("    Coef_Limit multiplie par le quantum est un seuil ou l' on decide\n");
      printf ("    que le temps pour lequel une tache a attendu est trop\n");
      printf ("    long.\n");
      exit (0);
    }
  if (argc == 4)
    {

      Quantum = atoi (argv[3]);

    }
  else
    {
      Quantum = atoi (argv[5]);
      CoefDes = atoi (argv[3]);
      CTTL = atoi (argv[4]);
    };
  if (Quantum == 0)
    {
      printf ("Quantum nul!!");
      exit (-1);
    };
  gestop = (char *) malloc (strlen (argv[2]) * sizeof (char));
  strcpy (gestop, argv[2]);

  switch (gestop[0])
    {
    case 'c':
      gestar = 1;
      break;
    case 's':
      gestar = 2;
      break;
    case 'd':
      gestar = 3;
      break;
    default:
      printf ("Erreur de gestion c|s|d\n");
      exit (-1);
    };
  free (gestop);
  if ((argc == 4) && (gestar == 3))
    {
      printf ("Erreur d Coef_Baisse Coef_Limit Quantum\n");
      exit (-1);
    };
  init_process (argv[1]);
}

/******************************************************************************/
/* main */
main (int argc, char *argv[])
{
  NBF = 0;
  CoefDes = 0;
  CTTL = 0;

  gest_param (argc, argv);

  gestion ();
  printf ("\n");
};
