/*************************************************************
/*
/* evg
/*
/* E. Pasqualucci 11-6-1996
/* This program implements an event
/* generator for the data acquisition test stand.
/* Needs to be linked with utilib functions and Vme package.
/* E. Pasqualucci 11-6-1996
/*
/* Modified to implement trigger number sharing. The first
/* trigger generated is written into a location on Vme
/* mirrored memory, the trigger location is written before
/* the generated lengths.
/* E. Pasqualucci 19-9-1996
/*
/* Modified to implement the effect of the edge of Vic memory.
/* The trigger number is now generated as the maximum trigger
/* number seen by the collectors.
/* E. Pasqualucci 26-9-1996
/*
/* Options modified to allow to write on a fifo at a given address.
/* E. Pasqualucci 26-9-1996
/*
/* Writing on fifo enabled.
/* Parameter names modified (EVG_ prefix inserted).
/* Help updated.
/* E. Pasqualucci 4-10-1996
/*
/* Program debug. Minor bugs fixed and generated length
/* aligned to 256 bytes when the events are written on VIC.
/* E. Pasqualucci 16-10-1996
/*
/* Options regarding the event structure included.
/* E. Pasqualucci 22-10-1996
/*
/* Bug regarding timeouts corrected.
/* E. Pasqualucci 24-10-1996
/*
/* v1 released.
/* E. Pasqualucci 24-10-1996
/*
/*************************************************************/

#define debug
#define program

/* standard include files */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <limits.h>

/* for compatibility with LynxOS */

#ifndef RAND_MAX
#define RAND_MAX INT_MAX
#endif

/* Vme package include file */

#include "Vme.h"

/* specific include files */

#include "evg.h"

/* prototype files */

#include "evg_protos.h"
#include "utilib_protos.h"

/* global variables */

int cid;
int fcid;
int mcid;
int rand_max = RAND_MAX;
int seed = EVG_DEF_SEED;
int tot_len = 0;
int n_chain = EVG_DEF_N_CHAIN;
int n_mm_events = EVG_DEF_N_MM_EVENTS;
int min_datalen = EVG_DEF_MIN_DATALEN;          /* (in 32 bit words) */
int ave_datalen = EVG_DEF_AVE_DATALEN;
int ave_htrigger_time = EVG_DEF_AVE_HTRIG_TIME; /* (in microseconds) */
int retry_htrigger_time = EVG_DEF_RETRY_HTRIG_TIME;
int ave_kloe_rate = EVG_DEF_AVE_KLOE_RATE;
int vic_address = EVG_DEF_VIC_ADDRESS;
int vic_size = EVG_DEF_VIC_SIZE;                /* (in bytes) */
int vic_am = EVG_DEF_VIC_AM;
int vic_offset = EVG_DEF_VIC_OFFSET;
int first_event_offset = 0;
int trigger_loc_offset = EVG_DEF_TRIGGER_LOC_OFFSET;
int fifo_loc = (int) NULL;
int fifo_mark = (int) 0;
int fifo_len = 0;
int fifo_am = EVG_DEF_VIC_AM;
int struct_flag = EVG_DEF_EVENT_STRUCT;

unsigned int final_mask = 0;
unsigned int initial_mask = 0;
unsigned int trigger_number = 0;

unsigned int *nv;
unsigned int *ntrg;
unsigned int *evptr;
unsigned int *lenbuf;
unsigned int *chain_ptr;
unsigned int *chain_tnum;

struct evlen *mm_evlen;

void evg_help (void)
{
    printf (" Usage:\n\t evg [options]\n\n Options:\n\n");
    printf ("[-h]\t\tHelp\n");
    printf ("[-c] nchain\tNumber of Vme chains (default = %d)\n",
            EVG_DEF_N_CHAIN);
    printf ("[-f] loc len\t%s",
            "Fifo location and length (in bytes - hex format)\n");
    printf ("[-s] seed\tRandom seed (default = %d)\n",
            EVG_DEF_SEED);
    printf ("[-e] nev\tNumber of events in the VIC memory or fifo\n",
            EVG_DEF_N_MM_EVENTS);
    printf ("[-t] time\t%s%d%s",
            "Average time between generations (default = ",
            EVG_DEF_AVE_HTRIG_TIME, " usec)\n");
    printf ("[-r] time\t%s%d%s",
            "Time between two write attempts on VIC (default = ",
            EVG_DEF_RETRY_HTRIG_TIME, " usec)\n");
    printf ("[-k] time\t%s%d%s",
            "Average time between two triggers (default = ",
            EVG_DEF_AVE_KLOE_RATE, " usec)\n");
    printf ("[-d] average\t%s%d%s",
            "Average data length (in 32 bit words, default = ",
            EVG_DEF_AVE_DATALEN, ")\n");
    printf ("[-w] flag\t%s%s%s",
            "Event structure flag (w = word num. /e = event num.,",
            " default = e",
            ")\n");
    printf ("[-dm] min\t%s%d%s",
            "Minimum data length (in 32 bit words, default = ",
            EVG_DEF_MIN_DATALEN, ")\n");
    printf ("\n VIC options (hex format):\n\n");
    printf ("[-va] add\tVIC address to map (default = 0x%x)\n",
            EVG_DEF_VIC_ADDRESS);
    printf ("[-vs] space\tVIC space to map (default = 0x%x bytes)\n",
            EVG_DEF_VIC_SIZE);
    printf ("[-vo] offs\tVIC offset from base (default = 0x%x bytes)\n",
            EVG_DEF_VIC_OFFSET);
    printf ("[-vm] mode\tVIC address modifier (default = ox%x)\n",
            EVG_DEF_VIC_AM);
}

void evg_opt (int argc, char **argv)
{
    register int i;
    register int arg;

    int idummy;
    char sdummy[80];

    for (arg = 1; arg < argc; arg++)
        if (argv[arg][0] == '-')
            switch (argv[arg][1])
            {
                case 'd':
                    if (argv[arg][2] == 'm')
                        min_datalen = atoi (argv[++arg]);
                    else
                        ave_datalen = atoi (argv[++arg]);
                    break;
                case 'v':
                    switch (argv[arg][2])
		    {
		        case 'a':
                            sscanf (argv[++arg], "%x", &vic_address);
                            break;
		        case 's':
                            sscanf (argv[++arg], "%x", &vic_size);
                            break;
		        case 'o':
                            sscanf (argv[++arg], "%x", &vic_offset);
                            break;
		        case 'm':
                            sscanf (argv[++arg], "%x", &vic_am);
                            break;
		        default:
                            printf (" please select a VIC option:\n");
                            printf ("\t -va -vs -vo -vm\n");
                            exit (0);
                            break;
                    }
                    break;
                case 'w':
                    switch (argv[++arg][0])
		    {
		        case 'e':
                            struct_flag = EVG_EVN_EVENT_STRUCT;
                            break;
		        case 'w':
                            struct_flag = EVG_WDN_EVENT_STRUCT;
                            break;
		        default:
                            printf (" invalid event structure\n");
                            printf (" please specify :\t e/w\n");
                            exit (0);
                            break;
                    }
                    break;
		case 'c':
                    n_chain = atoi (argv[++arg]);
                    break;
		case 's':
                    seed = atoi (argv[++arg]);
                    break;
		case 'e':
                    n_mm_events = atoi (argv[++arg]);
                    break;
		case 't':
                    ave_htrigger_time = atoi (argv[++arg]);
                    break;
		case 'f':
                    sscanf (argv[++arg], "%x", &fifo_loc);
                    sscanf (argv[++arg], "%x", &fifo_len);
                    fifo_mark = fifo_loc + EVG_DEF_MARK_OFFS;
                    break;
		case 'r':
                    retry_htrigger_time = atoi (argv[++arg]);
                    break;
		case 'k':
                    ave_kloe_rate = atoi (argv[++arg]);
                    break;
		case 'h':
                    evg_help ();
                    exit (0);
	        case 'n':
                    ++arg;
                    break;
		default:
                    printf ("invalid option: -%c\n", argv[arg][1]);
                    break;
            }
}

void evg_delay (int us)
{
    struct timeval tv;
    int status;

    tv.tv_sec = 0;
    tv.tv_usec = us;
    status = select( 1, 0, 0, 0, &tv );
}

unsigned int evg_find_trg (unsigned int ptr)
{
    register unsigned int i;

    for (i=0; i < n_mm_events ; ++i)
        if (evptr[i] > ptr)
            return i;
}

unsigned int evg_trigger_number (void)
{
    register unsigned int i;
    register unsigned int imax = ntrg[0];

    for (i=1 ; i < n_chain ; ++i)
        if (ntrg[i] > imax) imax = ntrg[i];

    return imax;
}

void evg_main_loop ()
{
    register int i;
    int offs;
    int len = (int) sizeof (struct evlen);
    int retry;
    int status;
    int lenmod;
    int prev = 0;
    int modint = EVG_EVOFF_MOD/sizeof(int);
    int time_left;
    float factor;
    struct timeval t0, t1;
    unsigned int mask;
    int nev_htrigger = ave_htrigger_time/ave_kloe_rate;
    int n_max_retry = ave_htrigger_time/retry_htrigger_time;

    factor = 2.*(float)(ave_datalen - min_datalen) *
             (float) nev_htrigger / (float) rand_max;

    for (i=0 ; i < n_chain ; ++i)
    {
        lenbuf[i] = (min_datalen + EVG_NHT) * nev_htrigger +
                    (int) (factor * (float) rand());

        /* If we are not reading from fifo we have to align */
        /* the lengths.                                     */

        if (!fifo_loc)
	{
            lenmod = lenbuf[i] % modint;
            lenbuf[i] -= lenmod;
            lenbuf[i] += lenmod > modint/2 ? modint : 0;
        }

        if ((int) chain_ptr[i] + lenbuf[i] < tot_len)
        {
            chain_ptr[i] += (unsigned int) lenbuf[i];
            ntrg[i] = nv[i] * (unsigned int) n_mm_events +
                      evg_find_trg (chain_ptr[i]);
        }
        else
	{
            ++nv[i];
            chain_ptr[i] = 0;
            ntrg[i] = nv[i] * (unsigned int) n_mm_events;
            lenbuf[i] = tot_len - (int) chain_ptr[i];
	}

        mm_evlen[i].fifolen = lenbuf[i];
        mm_evlen[i].flag = 1;
        offs = vic_offset +
               (int) ((char *) (mm_evlen+i) -
               (char *) mm_evlen);
        VmeWrite (cid, offs, (char *) (mm_evlen+i), len);
    }

    trigger_number = evg_trigger_number ();
    VmeWrite (cid, trigger_loc_offset,
              (char *) &trigger_number, sizeof (unsigned int));

    while (TRUE)
    {
        gettimeofday (&t0, (struct timezone *) 0);
        t1.tv_usec = t0.tv_usec + ave_htrigger_time % SEC_OVER_USEC;
        t1.tv_sec = t0.tv_sec + ave_htrigger_time/SEC_OVER_USEC;
        mask = initial_mask;
        retry = 0;
        time_left = ave_htrigger_time;

        for (i=0 ; i < n_chain ; ++i)
	{
            lenbuf[i] = (min_datalen + EVG_NHT) * nev_htrigger +
                        (int) (factor * (float) rand());

            /* If we are not reading from fifo we have to align */
            /* the lengths.                                     */

            if (!fifo_loc)
            {
                lenmod = lenbuf[i] % modint;
                lenbuf[i] -= lenmod;
                lenbuf[i] += lenmod > modint/2 ? modint : 0;
            }

            if ((int) chain_ptr[i] + lenbuf[i] < tot_len)
            {
                chain_ptr[i] += (unsigned int) lenbuf[i];
                ntrg[i] = nv[i] * (unsigned int) n_mm_events +
                          evg_find_trg (chain_ptr[i]);
            }
            else
            {
                ++nv[i];
                ntrg[i] = nv[i] * (unsigned int) n_mm_events;
                lenbuf[i] = tot_len - (int) chain_ptr[i];
                chain_ptr[i] = 0;
            }
        }

	while (retry < n_max_retry && timercmp (&t1, &t0, >))
        {
            time_left -= retry_htrigger_time;
            evg_delay (retry_htrigger_time);

            for (i=0 ; i < n_chain ; ++i)
                if (mask & (1 << i))
		{
                    offs = vic_offset + (int) ((char *) &(mm_evlen[i].flag) -
                                               (char *) mm_evlen);
                    VmeRead (cid, offs, (char *) &(mm_evlen[i].flag),
                             sizeof (int));

                    if (!mm_evlen[i].flag)
                    {
                        mm_evlen[i].fifolen = lenbuf[i];
                        mm_evlen[i].flag = 1;
                        offs = vic_offset +
                               (int) ((char *) (mm_evlen+i) -
                                      (char *) mm_evlen);
                        VmeWrite (cid, offs, (char *) (mm_evlen+i), len);
                        mask = mask & ~(1 << i);
                    }
		}

            if (mask == final_mask)
                break;
            else
                ++retry;

            gettimeofday (&t0, (struct timezone *) 0);
        }

        if (mask != final_mask)
        {
            fprintf (stderr, " error : chain delay \n");
            for (i=0 ; i < n_chain ; ++i)
                if (mask & (1 << i))
                    fprintf (stderr, " chain n. %d\n", i);
            evg_close ();
            exit (0);
        }

        trigger_number = evg_trigger_number();
        VmeWrite (cid, trigger_loc_offset,
                  (char *) &trigger_number, sizeof (unsigned int));

#ifdef debug
        if (trigger_number-(trigger_number%EVG_DEF_DEB_PRINT_INTERVAL)
            > prev)
	{
            printf ("trigger generated : %d\n", trigger_number);
            prev = trigger_number-(trigger_number%EVG_DEF_DEB_PRINT_INTERVAL);
        }
#endif

        gettimeofday (&t0, (struct timezone *) 0);
        time_left = (t0.tv_sec - t1.tv_sec) * SEC_OVER_USEC +
                    (t0.tv_usec - t1.tv_usec);
        evg_delay (time_left);
    }
}

void evg_init ()
{
    int j;
    int len, lenby, ierr;
    int count;
    int buf;
    int lastadd, size;
    register int i;
    float factor;
    int *vic_ptr;
    int *fifo_ptr;
    int *mark_ptr;
    int *len_table; /* data length for the events (in int) - to be generated */
    unsigned int *ip;
    unsigned int *local_mm_evlen;
    unsigned int *pointer;
    unsigned int *local_mm_events;

    vic_offset += (trigger_loc_offset + sizeof (int));

    factor = 2.*(float)(ave_datalen-min_datalen)/(float)rand_max;

    nv = (unsigned int *) calloc ((size_t) n_chain, sizeof (int));
    ntrg = (unsigned int *) calloc ((size_t) n_chain, sizeof (int));
    lenbuf = (unsigned int *) calloc ((size_t) n_chain, sizeof (int));
    chain_ptr = (unsigned int *) calloc ((size_t) n_chain, sizeof (int));
    chain_tnum = (unsigned int *) calloc ((size_t) n_chain, sizeof (int));

    uti_vzero ((char *) nv, n_chain * (int) sizeof (unsigned int));
    uti_vzero ((char *) ntrg, n_chain * (int) sizeof (unsigned int));
    uti_vzero ((char *) chain_ptr, n_chain * (int) sizeof (unsigned int));
    uti_vzero ((char *) chain_tnum, n_chain * (int) sizeof (unsigned int));

    for (i=0 ; i < n_chain ; ++i)
        initial_mask = initial_mask | (1 << i);

    /* VME initialization */

    if ((cid = VmeOpenChannel ("EventGeneratorVIC","pio")) < 0)
    {
        fprintf (stderr, " error in opening VIC vme channel\n");
        exit (0);
    }

    if (fifo_loc)
    {
        if ((fcid = VmeOpenChannel ("EventGeneratorFifo","pio")) < 0)
        {
            fprintf (stderr, " error in opening fifo vme channel\n");
            VmeCloseChannel (cid);
            exit (0);
        }

        if ((mcid = VmeOpenChannel ("EventGeneratorMark","pio")) < 0)
        {
            fprintf (stderr, " error in opening fifo mark channel\n");
            VmeCloseChannel (cid);
            VmeCloseChannel (fcid);
            exit (0);
        }
    }

    if ((vic_ptr = (int *) VmeMapAddress (cid, vic_address, vic_size, vic_am))
        == (int *) NULL)
    {
        fprintf (stderr, " error in mapping VIC\n");
        evg_close ();
        exit (0);
    }

    if (VmeSetProperty (cid, Vme_SET_COPY_AM, vic_am) < 0)
    {
        fprintf (stderr, " error in setting access mode\n");
        evg_close ();
        exit (0);
    }

    if (fifo_loc)
    {
        if ((fifo_ptr = (int *) VmeMapAddress (fcid, fifo_loc, fifo_len,
                                               fifo_am)) == (int *) NULL)
        {
            fprintf (stderr, " error in mapping fifo\n");
            evg_close ();
            exit (0);
        }

        if ((mark_ptr = (int *) VmeMapAddress (mcid, fifo_mark,
                                               (int) sizeof (int),
                                               fifo_am)) == (int *) NULL)
        {
            fprintf (stderr, " error in mapping fifo mark\n");
            evg_close ();
            exit (0);
        }
    }

    /* Header initialization */

    len = n_chain * (int) sizeof (struct evlen) / (int) sizeof (unsigned int);
    lenby = n_chain * (int) sizeof (struct evlen);
    local_mm_evlen = (unsigned int *) calloc ((size_t) n_chain, (size_t) len);
    mm_evlen = (struct evlen *) local_mm_evlen;
    uti_vzero ((char *) local_mm_evlen, len * (int) sizeof (unsigned int));

    count = VmeWrite (cid, vic_offset, (char *) local_mm_evlen, lenby);

#ifdef debug
    fprintf (stderr, "VmeWrite: count = %d\n", count);
    count = VmeRead (cid, vic_offset, (char *) local_mm_evlen, lenby);
    fprintf (stderr, "VmeRead: count = %d\n", count);
    fprintf (stderr, "%d %d %d %d\n", local_mm_evlen[0],
             local_mm_evlen[1], local_mm_evlen[2], local_mm_evlen[3]);
#endif

    /* Generation of event lengths */

    evptr = (unsigned int *) calloc ((size_t) n_mm_events, sizeof (int));
    len_table = (int *) calloc ((size_t) n_mm_events, sizeof (int));
    srand (seed);

    first_event_offset = (vic_offset + lenby) / EVG_EVOFF_MOD;
    first_event_offset += EVG_EVOFF_MOD;

    size = fifo_loc ? fifo_len : vic_size;
    lastadd = fifo_loc ? 0 : first_event_offset;

    for (i=0 ; i < n_mm_events ; ++i)
    {
        len_table[i] = min_datalen + (int) (factor * (float) rand());
        if (size > lastadd + ((len_table[i] + EVG_NHT) * sizeof (int)))
	{
            evptr[i] = tot_len;
            tot_len += (len_table[i] + EVG_NHT);
            lastadd += (len_table[i] + EVG_NHT) * sizeof (int);
        }
        else
	{
            evptr[i] = tot_len;
            lastadd = size;
            tot_len = (size - (fifo_loc ? 0 : first_event_offset))
                      / sizeof(int);
            len_table[i] = tot_len - evptr[i] - EVG_NHT;
            n_mm_events = ++i;
            fprintf (stderr,
                     " Warning: only %d events generated by evg\n", i);
            break;
        }
    }

    /* Generation of events */

    pointer = local_mm_events = (unsigned int *) calloc ((size_t) tot_len,
                                sizeof (unsigned int));

    for (j=0 ; j < n_mm_events ; ++j , ++pointer)
    {
        *pointer = 0 | (j << EVG_TRIGGER_BIT_SHIFT);

        if (struct_flag == EVG_EVN_EVENT_STRUCT)
            for (i=0 ; i < len_table[j] ; ++i)
                *(++pointer) = 0 | (j << EVG_DATA_BIT_SHIFT);
        else if (struct_flag == EVG_WDN_EVENT_STRUCT)
            for (i=0 ; i < len_table[j] ; ++i)
                *(++pointer) = 0 | (i << EVG_DATA_BIT_SHIFT);

        if (EVG_NHT == 3)
	{
            *(++pointer) = 0 | (len_table[j] << EVG_DATA_BIT_SHIFT);
            *(++pointer) = 1 << EVG_EOB_BIT_SHIFT;
	}
        else
            *(++pointer) = 1 << EVG_EOB_BIT_SHIFT |
                           (len_table[j] << EVG_DATA_BIT_SHIFT);
    }

#ifdef debug
    fprintf (stderr, " Events generated :\n");
    for (i=0 ; i < len_table[0] + EVG_NHT ; ++i)
         fprintf (stderr, "%x\n", local_mm_events[i]);
#endif

    /* Now the events have to be copied to the mirrored memory or the fifo */

    if (fifo_loc)
    {
        for (i=0, ip = local_mm_events ; i < tot_len ; ++i, ++ip)
            count = VmeWrite (fcid, 0,
                              (char *) ip, sizeof (int));

        count = VmeRead (mcid, 0, (char *) &buf, sizeof (int));
    }
    else
    {
        count = VmeWrite (cid, first_event_offset,
                          (char *) local_mm_events, tot_len * sizeof(int));
    }

    /* Init the trigger number */

    count = VmeWrite (cid, trigger_loc_offset,
                      (char *) &trigger_number, sizeof (unsigned int));

#ifdef debug
    count = VmeRead (cid, first_event_offset, (char *) local_mm_events,
                     tot_len * sizeof(int));
    fprintf (stderr, " VmeRead : count = %d\n", count);
    fprintf (stderr, " Events read from VIC :\n");
    for (i=0 ; i < len_table[0]+EVG_NHT ; ++i)
         fprintf (stderr, "%x\n", local_mm_events[i]);

    count = VmeRead (cid, trigger_loc_offset, (char *) &trigger_number,
                     sizeof(unsigned int));
    fprintf (stderr, " VmeRead : count = %d\n", count);
    fprintf (stderr, " trigger offset = %d\n", trigger_loc_offset);
    fprintf (stderr, " trigger number = %d\n", trigger_number);
#endif

    fprintf (stderr, " VIC filled up\n\t from offset %d\n\t to offset %d\n",
             trigger_loc_offset,
             fifo_loc ? vic_offset + lenby :
             first_event_offset + tot_len * sizeof(int));
}

void evg_close ()
{
    VmeCloseChannel (cid);
    if (fifo_loc)
    {
        VmeCloseChannel (fcid);
        VmeCloseChannel (mcid);
    }
}

#ifdef program
main (int argc, char **argv)
{
    evg_opt(argc,argv);
    evg_init();
    evg_main_loop();
    evg_close();
}
#endif