/* 
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

/* Simple schuduling test for RKs....					*/
/*			By Luca Abeni					*/

/*
 * This program is similar to simple.examples/schedtest.c, but uses two
 * periodic taks. It is currently used for generating CPU load competing
 * with the disk server.
 * In order to synchronize tasks and reserves, the execution is assumed to
 * start at time 3000
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>

#include <rk/rk.h>
#include <rk/rk_error.h>
#include <rk/posix_timers.h>
#include <rk/timespec.h>

/* Reserves Parameters */
#define C1 200 
#define T1 450
#define P1 450
#define C2 100
#define T2 1000

/* Number of iteration per task */
#define ITER 2000


unsigned long int timevect[10000];

/* Program start time */
unsigned long int firstime;
struct timespec fnow;


/* Destroy the resource sets... end exit!!! */
void shutdown(rk_resource_set_t rs1, rk_resource_set_t rs2)
{
  int res;

  res = rk_resource_set_destroy(rs1);
  if (res != 0) {
    printf("rk_resource_set_destroy failed destroying RS %p", rs1);
  }

  res = rk_resource_set_destroy(rs2);
  if (res != 0) {
    printf("rk_resource_set_destroy failed destroying RS %p", rs2);
  }

  printf("Everithing is finished... I am going to exit!\n");
  exit(0);
}

/* This is the body of the time consuming tasks */
void childbody(int childnum)
{
  pid_t whoami;
  int i, j, count;
  struct timespec now;
  struct timespec per;
  char filename[256];
  FILE *f;

  whoami = getpid();
  printf("Child %d starting\n", whoami);
  sprintf(filename, "res%d", whoami);
  f = fopen(filename, "w");

  /* Synchronize with the other child */
  fnow.tv_sec += 3;
 

  sleep(1);
  /*
   * The use of periodic tasks is a little tricky here...
   * The task becomes periodic only to wait for the start time.
   */
  if (childnum == 1) {
    per.tv_sec = P1 / 1000;
    per.tv_nsec = (P1 % 1000) * 1000000;
  } else {
    per.tv_sec = T2 / 1000;
    per.tv_nsec = (T2 % 1000) * 1000000;
  }
  printf("Task %d: period %lu %lu\n", childnum, (unsigned long) per.tv_sec, (unsigned long) per.tv_nsec);
  rt_make_periodic(&per, &fnow);
  rt_wait_for_start_time();

  /* And this is the time-consuming loop... */
  count = 10000;
  for (i = 0; i < ITER; i++) {
    for (j = 0; j < count; j++) {
      clock_gettime(CLOCK_REALTIME, &now);
      timevect[i] = now.tv_nsec / 1000000 + now.tv_sec * 1000;
    }
    if ((i % 9) == 0) rt_wait_for_next_period();
  }
 
  printf("Child %d saving data\n", whoami);
  for (i = 0; i < ITER; i++) {
    fprintf(f, "%lu\t%d\n", timevect[i] - firstime, childnum);
  }

  fclose(f);
  
  printf("Child %d diing\n", whoami);
  exit(0);
}

void print_usage(char *cmdname)
{
  printf("usage: %s n (ms/sec) \n", cmdname);
}

int main(int argc, char *argv[])
{
  pid_t child1, child2;
  rk_resource_set_t rs1, rs2;
  rk_reserve_t	cpu_rsv1, cpu_rsv2;
  rk_reserve_param_data_t p;
  struct timespec c, t;
  cpu_reserve_attr_data_t attr;
  char name[256];
  int res;

  /* create resource sets */
  sprintf(name, "RS 1");
  rs1 = rk_resource_set_create(name);
  if (((int)rs1 == -1) || (rs1 == NULL)) {
    printf("Cannot create resource set 1!!!\n");
    exit(-1);
  }
  sprintf(name, "RS 2");
  rs2 = rk_resource_set_create(name);
  if (((int)rs2 == -1) || (rs2 == NULL)) {
    printf("Cannot create resource set 2!!!\n");
    shutdown(rs1, 0);
  }
 
  /* And now, the reserves... */
  clock_gettime(CLOCK_REALTIME, &fnow);
  firstime= fnow.tv_nsec / 1000000 + fnow.tv_sec * 1000;
  
  c.tv_sec = C1 / 1000, c.tv_nsec = (C1 % 1000) * 1000 * 1000;
  t.tv_sec = T1 / 1000, t.tv_nsec = (T1 % 1000) * 1000 * 1000;
  
  p.enf_mode = RSV_HARD;
  //  p.enf_mode = RSV_SOFT;
  p.sch_mode = p.rep_mode = RSV_HARD; 
  attr.compute_time = c;
  attr.period = t;
  attr.deadline = t;
  attr.blocking_time.tv_sec = 0 ; attr.blocking_time.tv_nsec = 0;
  memcpy(&attr.start_time, &fnow, sizeof(struct timespec));
  attr.start_time.tv_sec += 1;
  attr.reserve_type = p;

  cpu_rsv1 = rk_cpu_reserve_create(rs1, &attr);
  if (cpu_rsv1 == 0) {
    printf("Cannot create CPU reserve # 1\n");
    shutdown(rs1,rs2);
  }
  
  c.tv_sec = C2 / 1000, c.tv_nsec = (C2 % 1000) * 1000 * 1000;
  t.tv_sec = T2 / 1000, t.tv_nsec = (T2 % 1000) * 1000 * 1000;
  
  p.enf_mode = RSV_HARD;
  //  p.enf_mode = RSV_SOFT;
  p.sch_mode = p.rep_mode = RSV_HARD; 
  attr.compute_time = c;
  attr.period = t;
  attr.deadline = t;
  attr.blocking_time.tv_sec = 0 ; attr.blocking_time.tv_nsec = 0;
  memcpy(&attr.start_time, &fnow, sizeof(struct timespec));
  attr.start_time.tv_sec += 1;
  attr.reserve_type = p;

  cpu_rsv2 = rk_cpu_reserve_create(rs2, &attr);
  if (cpu_rsv2 == 0) {
    printf("Cannot create CPU reserve # 2\n");
    /* Well, we have created cpu resv #1, but it isn't attached to a
    	resource set yet... In order to destroy it, we need to wait that
	it is attached to its RS!!!
    */
    while (rk_resource_set_get_cpu_rsv(rs1) == 0);
    shutdown(rs1,rs2);
  }
  printf("LOAD CPU Competitor!!!!! \n");
  /* Let's wait that the reserves are attached to their resource sets */
  while (rk_resource_set_get_cpu_rsv(rs1) == 0);
  while (rk_resource_set_get_cpu_rsv(rs2) == 0);

  /* Create child 1 & attach it to RS 1 */
  child1 = fork();
  if (child1 < 0) {
    perror("Error in fork()");
    shutdown(rs1, rs2);
  }
  if (child1 == 0) {
    childbody(1);
    
    /* The child exits, so execution cannot arrive here */
    printf("ARRRG!!!! Why am I here?\n");
    exit(-1);
  }
  
  /* Create child 2 & attach it to RS 2 */
  child2 = fork();
  if (child2 < 0) {
    perror("Error in fork()");
    shutdown(rs1, rs2);
  }
  if (child2 == 0) {
    childbody(2);

    /* The child exits, so execution cannot arrive here */
    printf("ARRRG!!!! Why am I here?\n");
    exit(-1);
  }
 

  res = rk_resource_set_attach_process(rs1, child1);
  if (res != 0) {
    printf("rk_resource_set_attach_proces failed for Resource Set 1...\n");
    shutdown(rs1, rs2);
  }
  res = rk_resource_set_attach_process(rs2, child2);
  if (res != 0) {
    printf("rk_resource_set_attach_proces failed for Resource Set 2...\n");
    shutdown(rs1, rs2);
  }

  printf("Done...\n");

  /* And now, as a good father, wait for the children to die... */
  wait(&res);
  wait(&res);

  /* ...and destroy the resource sets!!! */
  shutdown(rs1, rs2);
  return 1;
}
