#include <stdlib.h>
 
#include <stdio.h>
 
#include <stdint.h>
 
#include <string.h>
 
#include <unistd.h>
 
#include <ctype.h>
 
#include <zlib.h>
 
#include <vector>
 
#include <sys/time.h>
 
#include <sys/stat.h>
 
#include <signal.h>
 
#include <string>
 
#include "vme.h"
 
#include "dataio.h"
 
#include "SA02_DEF.h"
 
#include "sa02lib.h"
 
#include "H2D.h"
 
 
 
#define TRUE 1
 
#define FALSE 0
 
#define TIMEOUT 3
 
 
 
 
 
RUNINFO runinfo;
 
EVTREC evtrec;
 
 
 
//const int mux_map[4]={ 1,3,0,2};
 
const int mux_map[4]= { 2,0,3,1};
 
 
 
int verbose=0;
 
int timer_out;
 
struct sigaction oact;
 
int bltreadout=0;
 
int ctrl_c=0;
 
 
 
void SigInt (int sig) {
 
  ctrl_c = 1;
 
  timer_out=TRUE;
 
  sa02TimerOut = TRUE;
 
}
 
 
 
void sa02Timerast (int signumber) {
 
  timer_out = TRUE;
 
  sa02TimerOut = TRUE;
 
  sa02Printf("->>> TIMEOUT !!!\n");
 
}
 
 
 
void tmlnk (int tout) {
 
  timer_out = FALSE;
 
  struct sigaction act;
 
  struct itimerval tdelay;
 
 
 
  act.sa_handler = sa02Timerast;
 
  sigemptyset (&act.sa_mask);
 
  act.sa_flags = 0;
 
  tdelay.it_value.tv_sec = tout / 100;
 
  tdelay.it_value.tv_usec = 10000 * (tout % 100);
 
  tdelay.it_interval.tv_sec = 0;
 
  tdelay.it_interval.tv_usec = 0;
 
  if (sigaction (SIGALRM, &act, &oact) < 0) {
 
  }
 
  if (setitimer (ITIMER_REAL, &tdelay, NULL) < 0) {
 
  }
 
}
 
 
 
void tmulk () {
 
  struct itimerval tdelay;
 
 
 
  tdelay.it_value.tv_sec = 0;
 
  tdelay.it_value.tv_usec = 0;
 
  tdelay.it_interval.tv_sec = 0;
 
  tdelay.it_interval.tv_usec = 0;
 
  if (setitimer (ITIMER_REAL, &tdelay, NULL) < 0) {
 
  }
 
  if (sigaction (SIGALRM, &oact, NULL) < 0) {
 
  }
 
}
 
 
 
void sa02Tmlnk (int tout) {
 
  sa02TimerOut = FALSE;
 
  tmlnk (tout);
 
//  ResetTimer (p1h, P1_DAQ_TIMEOUT);
 
}
 
 
 
void sa02Tmulk () {
 
  tmulk();
 
}
 
 
 
int help() {
 
  printf ("*********************************************************************************:\n");  
  printf ("Usage: Performs single/multiple read or write access to SA02 via Belle PTS multipurpose IO board:\n\n");  
  printf ("-i input parameter filename // can be specified multiple times  \n");  
  printf ("-o output filename \n");  
  printf ("-f output filename - append data to file\n");  
  printf ("-w write command (FE TP DLY0 MUX MUXASIC VTH0 VTH1 TPLVL0 TPLVL1 GREG CREG CMON TMON0 TMON1 DMON) \n");  
  printf ("-a vme base address 0x32100000 \n");  
  printf ("-c asic chip number (0..3) \n");  
  printf ("-x asic channel number (0..36) \n");  
  printf ("-t sleep time bewteen events (sec) \n");  
  printf ("-b dataoff - scan over the asic channels instead of the parameter, data step is the channel step, chip and channel are initial chip and  channel. Should apper after -p or-w\n");  
  printf ("-s data step or in case of -b the channel step \n");  
  printf ("-r number of readouts for each event\n");  
  printf ("-n number of events\n");  
  printf ("-h <command> at each event execute external command after readouts are done");  
  printf ("-q  set status register bit TPENB of the CAEN1495 IO before event loop\n");  
  printf ("-e send software trigger before reading the data\n");  
  printf ("-p asicparameter ( PHASECMPS GAIN SHAPINGTIME COMPARATOR VRDRIVE MONITOR ID)\n");  
  printf ("-p asicparameter ( DECAYTIME OFFSET FINEADJ_UNIPOL FINEADJ_DIFF TPENB KILL)\n\n");  
  printf ("-u value ... send test SEU software trigger \n");  
  printf ("-z value ... finish the execution of the program in <value> s \n");  
  printf ("-m fixeddata : test fixed data, if error occurs, abort \n");  
  printf ("-l board number   \n");  
  printf ("*********************************************************************************:\n");  
  printf ("100 events TMON0 reading and 10 readings of the data for each event:\n");  
  printf ("./sa02_ctrl  -a 0x32100000  -w TMON0 -n 100 -r 10 -t 10000 -v 1\n\n");  
  printf ("scan 9 points TPLVL0 inital data 111 stepsize 11, send 1 test pulse, read data :\n");  
  printf ("./sa02_ctrl  -a 0x32100000  -w TPLVL0 -d 111 -s 11 -n 9 -e 1 -r 1 -v 1\n\n");  
  printf ("Initialize the board from input.param, Read 10000 events of data. the result is saved in the output.dat\n");  
  printf ("./sa02_ctrl  -a 0x32100000  -i input.param -n 10000 -r 1 -o output.dat \n\n");  
  printf ("Initialize the board from input.param, Read 10000 events of data. scan TPENB from 0 to 144 channels, send test pulse before the data acquisition,\n ");  
  printf ("set TPENB to b after the acquisition, the result is saved in the output.dat\n");  
  printf ("./sa02_ctrl  -a 0x32100000  -i input.param -p TPENB -d 1 -b 0 -s 1 -n 144 -c 0 -x 0 -e 1 -r 10000 -o output.dat \n\n");  
  return 0;
 
}
 
 
 
int module_header(int recid,uint32_t *data,int len) {
 
  data[0] = recid;
 
  data[1] = (len >0)? len : 0 ;
 
  return data[1]+2;
 
}
 
 
 
//-------------------------------------------------------------------------
 
int PrintData(uint32_t *rdata, int count) {
 
  for (int j=0; j<count; j++) {
 
    uint32_t recid   = rdata[j++];
 
    uint32_t len     = rdata[j++];
 
    printf(" recid=0x%0x len=%d pos=%d(maxpos %d) val=",recid
, len
, j
, count
);  
    for (uint32_t i=0; i<len; i++) {
 
      if (j
< count
) printf("0x%0x\t", rdata
[j
]);  
      j++;
 
    }
 
    if (len) j--;
 
  }
 
  return 0;
 
}
 
 
 
//-------------------------------------------------------------------------
 
int main( int argc , char ** argv) {
 
  // intercept routine
 
  if (signal (SIGINT, SigInt) == SIG_ERR) {
 
  }
 
 
 
  uint32_t fixeddata =0;
 
  int testfixeddata =0;
 
  uint32_t data    =0;
 
  uint32_t dataoff =0;
 
  uint32_t address =0;
 
  uint32_t cmd     =0;
 
  uint32_t chip    =0;
 
  uint32_t asicpar =0;
 
  uint32_t asicshft=0;
 
  uint32_t dstep   =0;
 
  uint32_t sendswtrg  =0;
 
  uint32_t tpenb   =0;
 
  uint32_t sendseutrg  =0;
 
 
 
  int aborttimeout =0;
 
  int append   =0;
 
  int remap    =0;
 
  int dt0      =0;
 
  int neve     =0;
 
  int towrite  =0;
 
  int toread   =0;
 
  int ch       =0;
 
  int output   =0;
 
  int externalcmd   =0;
 
  int getstatus   =0;
 
  int writeevent   =1;
 
  int c;
 
 
 
  char filename[0xFF]="";
 
  char externalcommand[0xFF]="";
 
  int sa02BoardMask = 0xF;
 
  opterr = 0;
 
  std::vector<std::pair<std::string,uint32_t> > inputfiles;
 
  while ((c = getopt (argc, argv, "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:z:x:w:y:")) != -1)
 
    switch (c) {
 
      case 'i':
 
        inputfiles.push_back(make_pair(std::string(optarg), sa02BoardMask));
 
        break;       // input file
 
      case 'f':
 
        append=1;    // append the data to the file filename
 
      case 'o':
 
        break;       // output
 
      case 'z':
 
        aborttimeout
=atoi (optarg
);    // abort timeout in s 
        break;
 
      case 'h':
 
        sprintf(externalcommand
,"%s", optarg 
);  
        externalcmd=1;
 
        break;       // output
 
      case 'm':
 
        fixeddata 
=  strtoul (optarg
,NULL
,0); 
        testfixeddata=1;
 
        break;             // address
 
      case 'a':
 
        break;             // address
 
      case 'r':
 
        break;             // number of readings per event
 
      case 'v':
 
        break;             // verbosity
 
      case 't':
 
        break;                // sec sleep between events
 
      case 's':
 
        break;                // step size in data or channel increase
 
      case 'g':
 
        bltreadout 
=  strtoul (optarg
,NULL
,0); 
        break;                // bltreadout
 
      case 'l':
 
        sa02BoardMask 
=  strtoul (optarg
,NULL
,0); 
        if ( sa02BoardMask > 0xF) {
 
          printf("Error! Board Mask 0..15 . Set to 0.");  
          sa02BoardMask = 0xF;
 
        }
 
        break;                // board mask
 
      case 'j':
 
        sa02BoardType 
=  strtoul (optarg
,NULL
,0); 
        if ( sa02BoardType > 3) {
 
          printf("Error! Board type sa02 0 / sa03 1 / FEB1_0 2 / FEB1_1 3 . Set to 3");  
          sa02BoardType = 3;
 
        }
 
        break;                // board number
 
      case 'n':
 
        break;                          // number of events or number of scan points
 
      case 'e':
 
        sendswtrg 
=  strtoul (optarg
,NULL
,0); 
        break;         // send sotware trigger before reading out the data
 
      case 'u':
 
        sendseutrg 
=  strtoul (optarg
,NULL
,0); 
        break;         // send software SEU trigger before reading out the data
 
      case 'w':
 
        towrite=1;
 
        if (strcmp(optarg
,"MUXASIC")==0)  remap
=1;  
        cmd =sa02GetCmdCode(optarg);
 
        break;         // write command
 
      case 'p':
 
        towrite=2;
 
        cmd = sa02GetAsicCode(optarg, &asicpar, &asicshft);
 
        break;  // asic parameter
 
      case 'b':
 
        towrite=3;
 
        break;    // scan channels instead of parameter; the value is the off value of the channel
 
      case 'c':
 
        break;                // chip number  or initial channel number in the case of -b option
 
      case 'x':
 
        break;                 // channel number or initial channel in the case of -b option
 
      case 'd':
 
        if (remap) {
 
          data = mux_map[data];
 
        }
 
        break;                 // data or initial data in the case of scan
 
      case 'q':
 
        break;                 // set TPENB bit of the CAEN1495 IO
 
      case 'y':
 
        writeevent  
= strtoul (optarg
,NULL
,0); 
        break;                 // write event data
 
      case 'k':
 
        getstatus  
= strtoul (optarg
,NULL
,0); 
        break;                 //  print board status
 
      case '?':
 
        if (optopt == 'c')
 
          fprintf (stderr
, "Option -%c requires an argument.\n", optopt
);  
          fprintf (stderr
, "Unknown option `-%c'.\n", optopt
);  
        else
 
                   "Unknown option character `\\x%x'.\n",
 
                   optopt);
 
        return 1;
 
      default:
 
    }
 
 
 
  for (int i
=optind
; i
<argc
; i
++) data 
= strtoul (argv
[i
],NULL
,0);  
 
 
  if (argc <2) {
 
    help();
 
  }
 
 
 
  // printf ("[neve=%d] a=0x%08x cmd= 0x%08x Data 0x%0x ",neve,address, cmd,data);
 
 
 
  //VME_START(CAEN_V1718);
 
  VME_START(ZYNQ_XILLINUX);
 
  int daqmode =3;
 
  int trglen = 1000;
 
  Sa02DaqMode (daqmode);
 
  Sa02SelectTriggerWithMaskAndLength (sendswtrg, sa02BoardMask , trglen);
 
 
 
  for (unsigned int i=0; i<inputfiles.size(); i++) {
 
   
 
    sa02LoadParametersFromFile(inputfiles[i].first.c_str(), inputfiles[i].second);
 
  }
 
  
 
  if (getstatus){
 
    char saddress[0xFF];
 
    double sdata[20];
 
    for (int board=0;board<4;board++){
 
      if (sa02BoardMask &(1<<board)) {
 
        printf("sa02Status => board %d\n",board
);  
        sa02Status(board, saddress,sdata);
 
      } 
 
    } 
 
  }
 
  if (sendseutrg)  Sa02SEUTrigger();
 
#define MAXSIZE 10000
 
  int maxsize = MAXSIZE;
 
  uint32_t *rdata = new uint32_t[maxsize];
 
  FILE * fp=NULL;
 
//  uint32_t hdr[10];
 
  int nbtotal=0;
 
    if (verbose
) printf("Data in the file:%s\n", filename
);  
    if (append
) fp
=fopen(filename
,"ab");  
    else fp
=fopen(filename
,"wb");  
    output=1;
 
    // run header
 
    runinfo.id= RUNINFO_ID; // run record ID
 
    runinfo.len= sizeof(runinfo);
 
    runinfo.cmd= cmd;
 
    runinfo.x0 = data;
 
    runinfo.dx = dstep;
 
    runinfo.nx = neve;
 
    runinfo.chip= chip;
 
    runinfo.ch  = ch;
 
    runinfo.neve= toread;
 
    runinfo.writemode= towrite;
 
    sa02Printf("RUNINFO x0=%d nx=%d dx=%d\n", runinfo.x0,runinfo.dx,runinfo.nx);
 
    nbtotal
+=fwrite(&runinfo
, 1,sizeof(runinfo
),fp
); //gzip 
  } else {
 
    if (verbose
) printf("Data are not written to the file!\n");  
  }
 
 
 
//  hdr[0]= 0x1; // event record ID
 
  if (!neve & (towrite || toread) ) neve=1;
 
 
 
  time_t t,told,tstart;
 
  tstart=t;
 
  int ncount=0;
 
  int nerrors=0;
 
  int readerror=0;
 
  uint32_t response[2]= {0,0};
 
 
 
  Sa02TestPulseEnable(address, tpenb);
 
  ///////////////////////////////////////////////////////////////
 
  int h2=0;
 
  if(neve>1) {
 
    H2DInit(h2, "h2d","Sa02 Chip Scan", 144, 0,1,neve,data, dstep);
 
    H2DSetTitleX(h2,"channel");
 
    H2DSetTitleY(h2,"Scan");
 
  }
 
  for (int i=0; i<neve; i++) {
 
    int nb = sizeof(uint32_t);
 
    int count=0;
 
    switch (towrite) {
 
      case 1: {
 
        rdata[count+2]= data;
 
        rdata[count+3]= sa02Cmd(address,cmd, data, chip, ch , 2, response);
 
        count+=module_header(cmd,&rdata[count],2 );
 
        break;
 
      }
 
      case 2: {
 
        uint32_t datal = asicpar & (data << asicshft);
 
        rdata[count+2] =  data;
 
        rdata[count+3] =  sa02AsicWrite(address, cmd, datal, chip, ch, asicpar,asicshft);
 
        count+=module_header(cmd ,&rdata[count],2); // recid cmd
 
        break;
 
      }
 
      case 3: {
 
        rdata[count+2] =  chip*36+ch;
 
        rdata[count+3] =  sa02AsicWrite(address, cmd, data, chip, ch, asicpar,asicshft);  // switch on the channel
 
        count+=module_header(cmd ,&rdata[count],2); // recid cmd
 
        break;
 
      }
 
    }
 
//    const int dsize = ( FEB_DATA_STOP - FEB_DATA_START) / FEB_DATA_INC;
 
    const int dsize = 144*4;
 
 
 
    Sa02SetNeve(toread); 
 
    sa02Reset();
 
 
 
    for ( int j=0; j< toread; j++) {
 
      int eventerror =  0;
 
      if ((count+2+dsize) >= maxsize) {
 
        int oldsize = maxsize;
 
        maxsize*=2;
 
        printf("Increasing data buffer to %d elements\n", maxsize
);  
        uint32_t *tmpdata = rdata;
 
        rdata = new uint32_t[maxsize];
 
        //memcpy(tmpdata,rdata, oldsize*sizeof(uint32_t));
 
        for (int ki=0; ki<oldsize; ki++) rdata[ki]=tmpdata[ki];
 
        delete tmpdata;
 
      }
 
      Sa02TestPulseEnable(address, tpenb);
 
      if (sendswtrg)  Sa02SoftwareTrigger();
 
      nb  = sa02Read(sa02BoardMask, &rdata[count+2] );
 
      if (neve>1) {
 
        for (int ich=0; ich<144*4; ich++){
 
          int xch = (143 - ich%144) + ich/144*144;
 
          H2DFillBin(h2, xch,i,rdata[count+2+ich]);
 
        }
 
      }
 
      if (testfixeddata && nb>0) {
 
        int len = nb / sizeof(uint32_t);
 
        for (int ir=0; ir<len; ir++) {
 
          if ( rdata[count+2+ir]!=fixeddata ) {
 
            printf("INSERT INTO fixederror VALUES ( '%d', '%d','%d','%d','0x%08x','0x%08x' ) \n", int(t
), int(t
-tstart
), i
*toread
+j
, ir
, rdata
[count
+2+ir
], fixeddata 
);  
            eventerror++;
 
          }
 
        }
 
      }
 
      if (eventerror) {
 
        readerror++;
 
        if (readerror==3) {
 
          ctrl_c = 1;
 
          system("date >> firmware.lock");  
        }
 
      } else {
 
        readerror= 0;
 
      }
 
      if (nb>=0) count+=module_header(0x3,&rdata[count],nb/sizeof(uint32_t));  // recid 0x3
 
      if (ctrl_c) break;
 
      if (timer_out) j--;
 
      if (t
!=told 
) printf("%d events in %2.2f min (%d s) TIMEOUTS=%d %s",ncount
, (double)(t
-tstart
)/60.
,int(t
-tstart
), nerrors
, ctime(&t
));  
      if (aborttimeout && (t-tstart)>aborttimeout) {
 
        printf("Abort timeout reached ....\n");  
        ctrl_c=1;
 
        break;
 
      }
 
      told=t;
 
      if (nb<0) {
 
        nerrors++;
 
        j--;
 
      } else ncount++;
 
    }
 
    if (externalcmd) {
 
      char ecmd[256];
 
      sprintf(ecmd
,"%s %u %u %u %u",externalcommand
,(unsigned int)  tstart
,data
, rdata
[2], rdata
[3]);  
      if (verbose
) printf("Executing external command %s\n",ecmd
);  
    }
 
    if (output && writeevent) {
 
      evtrec.id=EVTREC_ID;
 
      evtrec.len=count*sizeof(uint32_t)+ sizeof(evtrec);
 
      evtrec.nev=i;
 
      nb 
= fwrite( &evtrec
,  1,  sizeof(evtrec
),fp
); //gzip 
      if (count
) nb
+=fwrite(&rdata
[0],1,count
*sizeof(uint32_t),fp
); //gzip  
      if (nb
!= (int) evtrec.
len) printf("Error writing! %d!=%d\n",nb
,evtrec.
len);  
      nbtotal+= nb;
 
    }
 
    if (verbose==1)  {
 
      printf("[%d/%d] %u \t", i
,count
, (unsigned int) time(NULL
));  
      PrintData(rdata,count);
 
    }
 
    if (dt0) sleep(dt0);
 
    if (towrite ==3) {
 
      sa02AsicWrite(address, cmd, dataoff, chip, ch, asicpar, asicshft);
 
      ch += dstep;
 
      if (ch>35) {
 
        ch-=36;
 
        chip++;
 
      }
 
    } else {
 
      data += dstep;
 
    }
 
    if (ctrl_c) {
 
      if (!testfixeddata
) printf("Ctrl+C Program interrupted ....\n");  
      break;
 
    }
 
  }
 
  if (output) {
 
    if (neve>1) H2DWrite2File(h2,fp);
 
  }
 
  if (verbose>1)  {
 
    printf("%d bytes written to file %s\n", nbtotal
, filename
);  
  }
 
  VME_STOP();
 
  if (toread 
&& !testfixeddata
) printf("%d events in %2.2f min  (%d s) TIMEOUTS=%d  %s",ncount
, (double)(t
-tstart
)/60.
,int(t
-tstart
), nerrors
, ctime(&t
));  
  return 0;
 
}