//to compile use: gcc -o elvlog elvlog.c tcfp_parser.c -lusb-1.0 -lc

//SIGUSR1 (10) causes the program to take a reading and record it to the data base
//SIGUSR2 (12) casues the program to take a reading and write it to stdout
//SIGHUP (1) causes the program to reload the configuration file
//SIGTERM (15) casues the program to quit
//and to send the signal from the command line: killall elvlog -s (the number)

//What to DO
#include "elvlog.h"

configurationData confData; //this is the structure where the stuff from the conf file is held

FILE *errlog;
FILE *origstderr;


//================================================
int readKey(void *container, int containSize, int containType, void *defaultValue, char key[], char delim, TCFP_config_data *cd)
{

#define INTEGER 0
#define REAL 1
#define CHARACTER 2
#define STRING 3

 TCFP_config_value *rawKey;
 int length;

 rawKey = TCFP_get_value(cd, key, delim);
 if (rawKey == NULL)
  {//not found so just fill with default
   switch (containType)
    {
     case INTEGER:*(int *)container = *(int *)defaultValue;
                  break;
     case REAL:*(double *)container = *(double *)defaultValue;
                  break;
     case CHARACTER:*(char *)container = *(char *)defaultValue;
                  break;
     case STRING:length = strlen((char *)defaultValue)+1;
                 if (length <= 1) memset(container, 0, containSize);
                 else
                  {
                   if (length > containSize) length = containSize;
                   memcpy(container, defaultValue, length);
                  }
                 break;
     default: memcpy(container, defaultValue, containSize);
    }
  }
 else
  {//found so convert
   switch (containType)
    {
     case INTEGER:*(int *)container = atoi(rawKey);
                  break;
     case REAL:*(double *)container = atof(rawKey);
                  break;
     case CHARACTER:*(char *)container = *rawKey;
                  break;
     case STRING:length = strlen(rawKey)+1;
                 if (length > containSize) length = containSize;
                 memcpy(container, rawKey, length);
                 break;
     default: memcpy(container, defaultValue, containSize);
    }
   TCFP_free_config_value(rawKey);
  }
 return 0; 

}//End of readKey

//================================================
char *convertRange(char rangeStr[])
{
 if (!strcmp(rangeStr, BIP1V)) return bip1v;
 if (!strcmp(rangeStr, BIP1PT25V)) return bip1pt25v;
 if (!strcmp(rangeStr, BIP2V)) return bip2v;
 if (!strcmp(rangeStr, BIP2PT5V)) return bip2pt5v;
 if (!strcmp(rangeStr, BIP4V)) return bip4v;
 if (!strcmp(rangeStr, BIP5V)) return bip5v;
 if (!strcmp(rangeStr, BIP10V)) return bip10v;
 if (!strcmp(rangeStr, BIP20V)) return bip20v;
 return NULL;
}//End of convertRange

//================================================
int loadConfData(configurationData *cd,  char *cdFilename)
{
 int err;
 char tempStr[MAXSTRING];
 char chRange[] = "ch0range";
 char chSlope[] = "ch0slope";
 char chIntercept[] = "ch0intercept";
 char chHeader[] = "ch0header";
 char chFormat[] = "ch0format";
 char gfHeader[] = "gf0header";
 char gfFormat[] = "gf0format";
 char gfSettleTime[] = "gf0settleTime";
 int tempValue;
 int loop;

//first open up and read the conf file
  cd->configDataValue = TCFP_load_config(cdFilename);
  if (cd->configDataValue == NULL)
   {
    fprintf(errlog,"Config file read failed\n");
    fflush(errlog);
    return -1;
   }  

//now get config values

 err = readKey(cd->logFilename, MAXSTRING, STRING, "", "logFile", '=', cd->configDataValue);

 err = readKey(cd->dbFilename, MAXSTRING, STRING, "", "dbFile", '=', cd->configDataValue);

 err = readKey(cd->metaname, MAXTABLE, STRING, "meta", "meta", '=', cd->configDataValue);

 tempValue = 1;
 err = readKey((void *)&cd->secPerSamp,(const int)sizeof(cd->secPerSamp), INTEGER, (void *)(&tempValue), "SecPerSamp", '=', cd->configDataValue);

//Get analog front end mode
 err = readKey(cd->mode, MODELENGTH, STRING, "DSE", "AinMode", '=', cd->configDataValue);
 if(strcmp(cd->mode,SEMODE)) 
   if(strcmp(cd->mode,DIFFMODE)) 
    {//Bad mode option
     fprintf(errlog,"Unrecognized mode, default to SE\n");
     fflush(errlog);
     strcpy(cd->mode, SEMODE);
    }

//load channel data
for (loop=0;loop<CHANNELS;loop++)
{
 chRange[2] = chSlope[2] = chIntercept[2] = chHeader[2] = chFormat[2] = (char)loop + '0';
// printf("Looking for key:%s\n",chRange);
 err = readKey(tempStr, MAXSTRING, STRING, "", chFormat, '=', cd->configDataValue);
 err = readKey(tempStr, MAXSTRING, STRING, "", chRange, '=', cd->configDataValue);
 cd->channel[loop].range = convertRange(tempStr);
 cd->channel[loop].slope = 1.0;
 cd->channel[loop].intercept = 0.0;
 err = readKey((void *)&cd->channel[loop].slope, (const int)sizeof(cd->channel[loop].slope), REAL, &(cd->channel[loop].slope), chSlope, '=', cd->configDataValue);
 err = readKey((void *)&cd->channel[loop].intercept, (const int)sizeof(cd->channel[loop].intercept), REAL, &(cd->channel[loop].intercept), chIntercept, '=', cd->configDataValue);

 err = readKey(cd->channel[loop].header, MAXHEADER, STRING, "", chHeader, '=', cd->configDataValue);
 err = readKey(cd->channel[loop].format, MAXSTRING, STRING, "", chFormat, '=', cd->configDataValue);
  
// printf("channel[%d].range is:%s\n",loop,cd->channel[loop].range);
// printf("channel[%d].slope is:%f\n",loop,cd->channel[loop].slope);
// printf("channel[%d].intercept is:%f\n",loop,cd->channel[loop].intercept);
}

//read gf channel data
for (loop=0;loop<GFCHANNELS;loop++)
{
 gfHeader[2] = gfFormat[2] = gfSettleTime[2] = (char)loop + '0';
 err = readKey(cd->gfchannel[loop].header, MAXHEADER, STRING, "", gfHeader, '=', cd->configDataValue);
 err = readKey(cd->gfchannel[loop].format, MAXHEADER, STRING, "", gfFormat, '=', cd->configDataValue);
 err = readKey(&(cd->gfchannel[loop].settleTime), sizeof(int), INTEGER, "", gfSettleTime, '=', cd->configDataValue);
}
//free up config file data  
  TCFP_free_config_data(cd->configDataValue);

return 0;
 
}//End of loadConfData

//================================================
int openUSB( libusb_context **cont, unsigned int idVendor, unsigned int idProduct, libusb_device_handle **handle)
 {
  libusb_device **list;
  struct libusb_device_descriptor device;

  int err, cnt, x;
 
  //Save the context that this opend in
  err=libusb_init(cont); 
  if (err != 0)
    {
     fprintf(errlog,"linusb_init failed with %d\n",err);
     fflush(errlog);
     return -1;
    }

  // discover devices
  cnt = libusb_get_device_list(*cont, &list);

  if (cnt < 0)
   {
     fprintf(errlog,"libusb_get_device_list returned: %d\n",cnt);
     fflush(errlog);
     return(-1);
   }

  for (x=0;x<cnt;x++)
   {
    libusb_get_device_descriptor(list[x],&device);
    if ((device.idVendor == idVendor)&&(device.idProduct==idProduct)) break;
   }
  if (x< cnt)
   {
   err = libusb_open(list[x],handle);
   if (err != 0)
    {
     fprintf(errlog,"linusb_open failed with %d\n",err);
     fflush(errlog);
     libusb_free_device_list(list, 1);
     return -1;
    }
   err = libusb_claim_interface(*handle, 0);
   if (err != 0)
    {
     fprintf(errlog,"linusb_claim_interface failed with %d\n",err);
     fflush(errlog);
     libusb_free_device_list(list, 1);
     return -1;
    }
   }
  else 
   {
    fprintf(errlog,"Device not Found\n");
    fflush(errlog);
    libusb_free_device_list(list, 1);
    return -1;
   }
  libusb_free_device_list(list, 1);
  return 0;

}// End of openUSB

//================================================
int messageOut(libusb_device_handle *handle, char *msg)
{
 int response;
 int msglen;

 msglen = strlen(msg);
 if (msglen != 0)
  {
   response = libusb_control_transfer(handle, VENDOR_CONTROL_OUT, MESSAGE_REQUEST, WVALUE, WINDEX, msg, msglen, 1000);
   if (response != msglen)
     {
       fprintf(errlog,"in messageOut libusb_control_transfer returned: %d\n",response);
       fprintf(errlog,"msg is: %s\n",msg);
       fprintf(errlog,"msglen is: %d\n",msglen);
       fflush(errlog);

     }
   return response;
  }
return -1;
}//End of messageOut

//================================================
int messageIn(libusb_device_handle *handle, char *msg, int len)
{
 int response;

 response = libusb_control_transfer(handle, VENDOR_CONTROL_IN, MESSAGE_REQUEST, WVALUE, WINDEX, msg, len, 1000);
 if (response <= 0)
  {
   fprintf(errlog,"in messageIn libusb_control_transfer Nothing read: %d\n",response);
   fflush(errlog);
  }
 return response;
}//End of messageIn

//================================================
int setMode(libusb_device_handle *handle, char *mode)
{
  char message[] = "AI:CHMODE=DIFF";
  strcpy(&message[10],mode);
  return (messageOut(handle, message));  
}//End of setMode

//================================================
int setRanges(libusb_device_handle *handle, configurationData *cd)
{
 int loop;

 if (!strcmp(cd->mode,DIFFMODE))
 {
  for (loop=0;loop < 4; loop++)
  {
   if (cd->channel[loop].range != NULL)
   {//A range is set so send it
    cd->channel[loop].range[CHCHAR] = '0' +loop;
//printf("setting: %s\n",cd->channel[loop].range);
    messageOut(handle,cd->channel[loop].range);
   }
  }
 }
}//End of SetRanges


//================================================
int readRealValue(libusb_device_handle *handle, int channel, double *value)
{
 char message[MAXBUF];
 char *substring;

 if ((channel < 0) || (channel > 7)) return -1;

 strcpy(message, AI0READ);
 message[4] = channel + '0';
 if (messageOut(handle, message) < 0) return -4;

 if (messageIn(handle, message, MAXBUF)<0) return -4;

 substring = strstr(message, "=");
 if (substring != NULL) 
  {
   *value = atof(&substring[1]);
   return 0;
  }
 else return -3;

}//End of readRealValue    

//===============================================
int setDIODirection(libusb_device_handle *handle, char *direction)
{
  messageOut(handle, direction);

}//End of setDIODirection

//===============================================
int runGFchannel(libusb_device_handle *handle, int gfchan, unsigned int gfDIO)
{
 #define GFCH 5
  char gfstr[]=DIOVALUE;
  sprintf(&gfstr[13],"%3u",gfDIO);
  if (messageOut(handle, gfstr ) <0) return -1;
  sleep(confData.gfchannel[gfchan].settleTime);
  if (readRealValue(handle, GFCH, &(confData.gfchannel[gfchan].reading))<0) return -1;
  confData.gfchannel[gfchan].reading = confData.gfchannel[gfchan].reading * confData.channel[GFCH].slope + confData.channel[GFCH].intercept;
  return 0;
}//End of runGFchannel

//===============================================

int runGF(libusb_device_handle *handle)
{
 int step, response;

 for(step=0;step<GFCHANNELS;step++)
 {
  switch(step)
   {
    case 0:
           if(confData.gfchannel[0].header[0] != 0)
            {           
             response = runGFchannel(handle, 0, 0x1);
             break;
            }
           else step++;
    case 1:
           if(confData.gfchannel[1].header[0] != 0)
            {           
             response = runGFchannel(handle, 1, 0x2);
             break;
            }
           else step++;
    case 2:
           if(confData.gfchannel[2].header[0] != 0)
            {           
             response = runGFchannel(handle, 2, 0x4);
             break;
            }
           else step++;
    case 3:
           if(confData.gfchannel[3].header[0] != 0)
            {           
             response = runGFchannel(handle, 3, 0x8);
             break;
            }
           else step++;
    case 4:
           if(confData.gfchannel[4].header[0] != 0)
            {           
             response = runGFchannel(handle, 4, 0x10);
             break;
            }
           else step++;
    case 5:
           if(confData.gfchannel[5].header[0] != 0)
            {           
             response = runGFchannel(handle, 5, 0x20);
             break;
            }
           else step++;
    case 6:
           if(confData.gfchannel[6].header[0] != 0)
            {           
             response = runGFchannel(handle, 6, 0x40);
             break;
            }
           else step++;
    case 7:
           if(confData.gfchannel[7].header[0] != 0)
            {           
             response = runGFchannel(handle, 7, 0xc0);
             break;
            }
           else step++;
   }//End of switch
   if (response<0) return -1;
 }//End for
 if (messageOut(handle, DIOVALUE )<0)return -1;  //switch all the gf channels off
 return 0;
}//End of runGF

//================================================
volatile sig_atomic_t running;
volatile sig_atomic_t sampleReport, sampleRecord, reloadConfig;

void sigQuit(int signum)
 {
  running = 0;
  fprintf(errlog, "User Request to Quit\n");
  fflush(errlog);

  signal(signum, sigQuit);
 }//End of sigQuit

void sigHandler(int signum)
 { 
  switch(signum)
  {
   case SIGUSR1: 
                 fprintf(errlog, "User Request to Record\n");
                 fflush(errlog);
                 sampleRecord = 1;
                 break;
   case SIGUSR2: 
                 fprintf(errlog, "User Request to Report\n");
                 fflush(errlog);
                 sampleReport = 1;
                 break;
   case SIGHUP:  fprintf(errlog, "User Request to Reload Configuration\n");
                 fflush(errlog);
                 reloadConfig = 1;
                 break;
  }
  signal(signum, sigHandler);
 }

//================================================
int openLog(configurationData *cd)
{
  time_t startTime;
  char *timeStr;

 errlog = fopen(cd->logFilename,"a");
 if (errlog == NULL) errlog = origstderr;
 else
 {
  stderr=errlog;

 }
 startTime = time(NULL);
 timeStr = ctime(&startTime);
 fprintf(errlog,"\nLog Opened at:%s",timeStr);
 fflush(errlog);
}//End of openLog

//================================================
int main(int argc, char *argv[])
 {
 TCFP_config_data *config_data;
 TCFP_config_value *dbfilename;

 const char *pzTail;
 int loop, end=0;

 FILE *dataFileHandle;
 time_t sampleTime, currentTime, nextTime, sleepTime;
 int sleepleft;
 char *timeStr;
 int firstField;

 char gfstr[]=DIOVALUE;

 char stringcounts[255];
 int stringcountsLength;

  libusb_context *adio;
  libusb_device **list;
  libusb_device *found = NULL;
  libusb_device_handle *handle;

  int err = 0,x;
  ssize_t i = 0;
  ssize_t cnt;

  char *substring;

  char inBuffer[MAXBUF];

  int mlength;
  int response;
  int setupUSB, setupMode, setupRange, setupCreate, setupConfig;

  double avalue;
  double temperature,humidity;

  origstderr = errlog = stderr;
  
  int sockfd, acceptsockfd;
//  struct sockaddr_un servAddr, cli_addr;

  const char pathLocal[] = "/tmp/elvlog";
  int lenLocal;

  char formatstr[MAXFORMAT + 10];

  if (geteuid() != 0) 
   {
    fprintf(errlog, "Must be root to execute\n");
    fflush(errlog);
    return -1;
   }

//if no arguments then print error and exit
  if (argc < 2)
    {
      fprintf(errlog, "No config file specified: elvlog file\n");
      fflush(errlog);
      return -1;
    }
 /*
  sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
  if (sockfd < 0) 
   {
      frintf(errlog,"ERROR opening socket\n");
      fflush(errlog);
      return -1;
   }

  memset(&servAddr,0,sizeof servAddr);

  servAddr.sun_family = AF_LOCAL;
  strncpy(servAddr.sun_path,pathLocal,sizeof servAddr.sun_path-1);
  servAddr[sizeof servAddr.sun_path-1] = 0;

  lenLocal = SUN_LEN(&servAddr);


  serv_addr.sun_port = htons(PORTNO);
  if (bind(sockfd, (struct sockaddr_un *) &servAddr,sizeof(servAddr)) < 0) 
    error("ERROR on binding");
  listen(sockfd,5);
*/
//  printf("The DF file is:%s\n",confData.dbFilename);

  setupUSB = 0;
  setupMode=0;
  setupRange=0;
  setupCreate=0;
  setupConfig=0;
  running = 1;
  sampleRecord = sampleReport = 0;
  reloadConfig = 1;
//ready to go

 signal(SIGUSR1, sigHandler);
 signal(SIGUSR2, sigHandler);
 signal(SIGHUP, sigHandler);
 signal(SIGTERM, sigQuit);
 signal(SIGIO, sigHandler);

 nextTime = time(NULL);   //grab the current time and use it to step forward from;

 while (running)
 {
//now read the config file


  if (reloadConfig)
   {
    if (loadConfData(&confData, argv[1]) < 0) return -1;

    if (isatty(STDERR_FILENO))
     {
      openLog(&confData);
     }
    reloadConfig = 0;
    setupMode = 0;
    setupRange = 0;
    setupCreate = 0;
   }

  if (setupUSB == 0)
   {
    if (openUSB( &adio, MCOMP, USB7204, &handle) < 0)
       {
         printf("Set up to quit\n");
         setupUSB = 0;
         running = 0; //This should cause it to quit!
       }
    else 
       {
         setDIODirection(handle, DIOOUT);
         setupUSB = 1;
       }
   }

  if ((setupMode == 0)&&(setupUSB))
   {
  if (!strcmp(confData.mode,DIFFMODE)) end=4; else end=8;
   setMode(handle,confData.mode);
    setupMode = 1;
   }

  if ((setupRange == 0)&&(setupUSB))
   {
    setRanges(handle, &confData);
    setupRange = 1;
   }
  if (setupCreate == 0)
   {
//Open data file
       dataFileHandle = fopen(confData.dbFilename,"a");
       if (dataFileHandle == NULL) 
         {
          fprintf(errlog,"Failed to open data file:%s\n",confData.dbFilename);
          fflush(errlog);
          running = 0;
          setupCreate = 0;
         }
       else
        {
         sampleTime = time(NULL);

//print out headers for the channels
         for(firstField=0,loop=0;loop<end;loop++)
         {
          if(confData.channel[loop].range != NULL)
           {
             if (firstField==0)
              {
               fprintf(dataFileHandle,"%s,%s(%d)", "Epoch Time",confData.channel[loop].header,loop);
               firstField = 1;
              }
             else fprintf(dataFileHandle,",%s(%d)", confData.channel[loop].header,loop);
           }   
         }
//nothing found at least fill in time
         if((loop>=end)&&(firstField==0)) fprintf(dataFileHandle,"%lu", sampleTime);

//print out headers for the gf channels
         for(loop=0;loop<GFCHANNELS;loop++)
         {
          if(confData.gfchannel[loop].header[0] != 0)
           {
             fprintf(dataFileHandle,",%s(%d)", confData.gfchannel[loop].header,loop);
           }   
         }
         fprintf(dataFileHandle,"\n");
         fflush(dataFileHandle);
         setupCreate = 1;
        } 
   }

 if(setupUSB&&setupMode&&setupRange)
 {
  sampleTime = time(NULL);
  timeStr = ctime(&sampleTime);
  for(loop=0;((loop<end)&&(setupUSB));loop++)
  {
    if(confData.channel[loop].range != NULL)
    {
     if (readRealValue(handle, loop, &(confData.channel[loop].reading))==-4) 
       {
        libusb_release_interface(handle, 0);    
        libusb_close(handle);
        setupUSB = setupMode = setupRange = 0; 
        fprintf(errlog,"Reading Channels: USB device not found, reseting handle\n");
        fflush(errlog);
        break;
       }
     confData.channel[loop].reading = confData.channel[loop].reading * confData.channel[loop].slope + confData.channel[loop].intercept;
    }  
   
  } 
  if(setupUSB&&setupMode&&setupRange)
  {
  if (runGF(handle)<0)
       {
        libusb_release_interface(handle, 0);    
        libusb_close(handle);
        setupUSB = setupMode = setupRange = 0; 
        fprintf(errlog,"Reading GF:USB device not found, reseting handle\n");
        fflush(errlog);
        continue;
       }
  }
 } 

if(sampleReport)
 {
  sampleReport = 0;
  printf("Reading Channels...\n");
  for(loop=0;loop<end;loop++)
  {
    if(confData.channel[loop].range != NULL)
    {
     printf("channel %d is:%f\n",loop, confData.channel[loop].reading);
    }   
  }  
  for(loop=0;loop<GFCHANNELS;loop++)
  {
   printf("GF channel %d is:%f\n", loop, confData.gfchannel[loop].reading);
  }
  
 }

if (sampleRecord)
 {
  sampleRecord = 0;
  for(firstField=0,loop=0;loop<end;loop++)
  {
    if(confData.channel[loop].range != NULL)
    {
     if (firstField==0)
      {
       strcpy(formatstr,"%lu,");
       strcpy(&formatstr[strlen(formatstr)],confData.channel[loop].format);
       fprintf(dataFileHandle,formatstr, sampleTime,confData.channel[loop].reading);
       firstField = 1;
      }
     else
      {
       strcpy(formatstr,",");
       strcpy(&formatstr[1],confData.channel[loop].format);
       fprintf(dataFileHandle,formatstr, confData.channel[loop].reading);
      }
    }   
  }

  if((loop>=end)&&(firstField==0)) fprintf(dataFileHandle,"%lu", sampleTime);

  for(loop=0;loop<GFCHANNELS;loop++)
  {
   if(confData.gfchannel[loop].header[0] != 0)
    {
     strcpy(formatstr,",");
     strcpy(&formatstr[1],confData.gfchannel[loop].format);
     fprintf(dataFileHandle,formatstr, confData.gfchannel[loop].reading);
    }
  }
  fprintf(dataFileHandle,"\n");
  fflush(dataFileHandle);
 }

//Calculate when it sould sample again 
 nextTime += confData.secPerSamp;
//grab the current time
 currentTime = time(NULL);

if (currentTime<nextTime)
 {
//not time yet so sleep until 1 second before
  sleepTime = nextTime-currentTime; //give us one second
//but before sleeping make sure it is suppose to be running and that it is a positive amount of time sleep
  if ((running)&&(sleepTime>0)) sleepleft = sleep(sleepTime);
//since I was woken up than I need to reset to catch it next go around
  if (sleepleft != 0) nextTime -= confData.secPerSamp;
 }
 
sampleRecord = 1;
}

//printf("Recognized SIGTERM and closing to quit\n");
fprintf(errlog, "Closing libusb handle\n");
fflush(errlog);

if (setupUSB)
 {
  libusb_release_interface(handle, 0);    
  libusb_close(handle);
 }

  libusb_exit(adio);

fprintf(errlog, "Closing logfile\n");
fflush(errlog);


  fclose(errlog);

  return (1);
}//End of Main



