ncfileop.c 12.8 KB
/*=====================================================================
 *                DD SYSTEM base package
 *                  DD_Server library
 *                    ncfileop.c
 *                      V.6.7
 * Last revision:
 *      Oct 24, 2002  New calculation of the maximum record size V 4.1
 *      work with NC files is now in file ncfileop.c
 *      Nov 19, 2004 Bug in the cach operation.
 *      Aug 16, 2005 Cach improved
 *      Mar 15, 2007 V.4.7 Some errors with cash
 *      Jul 10, 2007 V.5.0 According to Server protocol 4.5.1 Call external database if need.
 *                         If "times" has "NODATA" string as name of the file, return "NOTIME"
 *      Sep 22, 2007 V.6.0 New approach. It tries to get new data between two files
 *      Sep 25, 2007 V.6.1 Variable Minimal Gap
 *      May 06, 2008 V.6.2 Fedorov, Max Records number processing
 *      May 12, 2008 V.6.3 Fedorov, In Max Records error in malloc sizeof(int *)!!! important for 64
 *      May 15, 2008 V.6.4 Fedorov, NoData interval processing. Just go to the next correct data interval.
 *      Apr 08, 2009 V.6.5 Fedorov, Unziped files can be touched by groupe
 *      Apr 14, 2009 V.6.6 Fedorov, nc_sync
 *      Feb 02, 2010 V.6.7 Lena, wait to remove an old file in the cache
 *======================================================================*/
#include <netcdf.h>
#include "DD.h"
#include "DD_comm.h"

/*------------------ GLOBAL VARIABLES --------------------------*/
extern int Verbose;

/*=============================================================
 *                       CloseOldFile
 *===========================================================*/
int CloseOldFile(DD_Var_t *D)
{
    int status;

    if(D->Maxnc_rec > 0)
    {
       status = nc_close(D->ncID);
       Cache_ReleaseDataFileAccess(D);
       if(status < 0) 
       {
          D->LastFileStatus = DATAFILEERR;
          if(Verbose) fprintf(stderr,"CloseOldFile: error while closed, message : \n", nc_strerror(status));
          return DATAFILEERR;
       }
       if(Verbose) fprintf(stderr,"CloseOldFile: file closed\n");
       D->Maxnc_rec = 0;
    }
    else if(Verbose) fprintf(stderr,"CloseOldFile: Nothing To close\n");

    return 1;
}

/*##############################################################*/
/*-----------------------SETNEWFILE-----------------------------*/
/*##############################################################*/

int SetNewFile(DD_Var_t *D, int N)
/*
 * Function tries to get new data for the gap beetween files, or just 
 * open a new data file according to offset N referring to current position in the times file.
 * The current position changed if file is succesefully open.
 * Return values: 
 * OK 
 * OUTOFTIME - next interval exceeeds GlobalStart/Stop
 * TIMEINEMPTY - next interval marked as "NODATA"
 * TRYAGAIN - the VI is blocked while new data is arriving
 * WAITEXTCALL - new data is requested
 * CACHTOOREC - now free space in the CACH
 * CHACHERR - unrecovable error in CACH
 * DATAFILEERR - unrecovable error in data file
 *----------------------------------------------------------------
 * At the entry we are assuming that VI is not locked!
 */
{
  static char TimeDimName[] = "Time";
  static size_t TimeCount[2] = {1,TIMELENGTH};

  char Name[MAXFILENAME];               /* Name of data nc file */
  char FullName[PATHLENGTH];            /* Full name of nc file */
  char StartTime[TIMELENGTH];
  char StopTime[TIMELENGTH];
  static int NameID,TimeDimID;               /* NC variables ID for Times file */
  static size_t start[2] = {0,0};                   /* NV start array */
  static size_t FileCount[2] = {1,MAXFILENAME}; /* NC count array for Times file */
  static size_t DimArray[NC_MAX_DIMS];  /* NC_MAX_DIMS is in netcdf.h */
  static int DimIDArray[NC_MAX_DIMS];
  size_t TestNumber;                                /* To test the new file number  */
  int FileNumber, Find, OldestNumber, NewAttempt;
  char command[300];
  int dltt;
  int status;
  time_t CurrentTime;
  double FirstTime, LastTime;
  int More;

/*--------------- Calculate the next file to open ---------------*/
  TestNumber = D->TimeRecNumber + N;
  if(Verbose) fprintf(stderr,"SetNewFile(): Record %d\n",TestNumber);

  D->TimeRecNumber = TestNumber;
  start[0] = TestNumber;
  
/*----------------------------------------------------------------*
 *  Some test and call external archive we have to make only if  N > 0 
 *  Otherwise we just reopen existing file
 *----------------------------------------------------------------*/
  if(N > 0)
  {
     if(TestNumber > D->RValidMax) /* Out of existing data files */
     {
        if(Verbose) fprintf(stderr,"SetNewFile(): TestNumber %d exides D->RValidMax %d\n",TestNumber,D->RValidMax);

        if(D->ExtCallAllowed) 
        {
           More = 1;
           while(More)
           {
            switch(status = ExtDataRequest(D, D->CDTime+D->MinGap))
            {
                case OK: return WAITEXTCALL;
                case NODATAATTIME: /* The last files in the data base are only NODATA, let us try again with StopTime of  TestNumber */
                  start[0] = TestNumber;
                  status = nc_get_vara_text(D->tmID, D->StopID,start,TimeCount,StopTime);
                  D->CDTime = DD_Time2Double(StopTime) + 0.001;
                  if(Verbose) fprintf(stderr,"SetNewFile(): We found no data, Let us try another CDTime = %s\n",Double2DD_Time(D->CDTime));
                  More = 1;
                  break;
                case GAPISSMALL: return TIMEINEMPTY; break;
                case OUTOFTIME: return OUTOFTIME; break;
                default: return DATAFILEERR;
            }
           }
        } else return OUTOFTIME;
     }
  }
  
  /*--------------- Get File Name and start Time  ---------------------*/
  status = nc_get_vara_text(D->tmID,D->NameID,start,FileCount,Name);
  status = nc_get_vara_text(D->tmID, D->StartID,start,TimeCount,StartTime);
  if(status != NC_NOERR)
  {          
     if(Verbose) fprintf(stderr,"SetNewFile(): Get File Name and StartTime  %s\n",nc_strerror(status));
     D->LastFileStatus = DATAFILEERR;
     return DATAFILEERR; 
  }
  FirstTime = DD_Time2Double(StartTime);
  
  if(N > 0)
  {
     if(strncmp(Name,"NODATA",6) == 0) /* Next record marked as NODATA */
     {
        if(D->ExtCallAllowed && (FirstTime - D->CDTime > D->MinGap)) /* Try to fill the gap between the data 
                                                                      * and NODATA segment if there is enough gap
                                                                      */
        {
           if(Verbose) fprintf(stderr,"SetNewFile(): There is a gap  %d\n",FirstTime - D->CDTime);
           switch(status = ExtDataRequest(D, D->CDTime+D->MinGap))
           {
              case OK: return WAITEXTCALL;
              case NODATAATTIME:
              case GAPISSMALL: return TIMEINEMPTY; break;
              case OUTOFTIME: return OUTOFTIME; break;
              default: return DATAFILEERR;
           }
        } 
        else /* Gap to NODATA interval is too small and we wiil try next time interval */
        {
           if(Verbose) fprintf(stderr,"SetNewFile(): Next File is NODATA, try file %d\n",++TestNumber);
           status = nc_get_vara_text(D->tmID, D->StopID,start,TimeCount,StopTime);
           D->CDTime = DD_Time2Double(StopTime);

           switch( status = SetNewFile(D,1) )
           {
               case OK: return OK;
               case WAITEXTCALL: return WAITEXTCALL;
               case TIMEINEMPTY: return TIMEINEMPTY;
               case OUTOFTIME: return OUTOFTIME;
               case CACHERR: return CACHERR;
               default: return DATAFILEERR;
           }
        }
           
     }
     else   /* Next record is valid */
     {
//        sleep(40);
        if(D->ExtCallAllowed && ((FirstTime - D->CDTime) > D->MinGap))
        {
            if(Verbose) fprintf(stderr,"SetNewFile(): File OK, but there is a gap  %d %d\n",FirstTime - D->CDTime, D->MinGap);
           switch(status = ExtDataRequest(D, D->CDTime+D->MinGap))
           {
              case OK: return WAITEXTCALL;
              case NODATAATTIME:
              case GAPISSMALL: return TIMEINEMPTY; break;
              case OUTOFTIME: return OUTOFTIME; break;
              default: return DATAFILEERR;
           }
        }  /* else just continue */
     }
  }

/*----------- Close the old file ----------------------*/
  if((status = CloseOldFile(D)) < 0) return DATAFILEERR;

  strcpy(FullName,D->path);
  strcpy(FullName+strlen(D->path), Name);
//  if(Verbose) fprintf(stderr,"SetNewFile(): New file to open: %s\n",FullName);

  //Request data file access to the cache manager
  if (Cache_RequestDataFileAccess(D, Name) != OK)
	  return (CACHERR);


/*----------------- Open requested file -----------------------------------------------*/

  status = nc_open(FullName,NC_NOWRITE,&(D->ncID));
  if(status != NC_NOERR)
  {
    if(Verbose) fprintf(stderr,"SetNewFile(): Error while File Open: %s, message : %s\n", Name, nc_strerror(status));
    D->LastFileStatus =DATAFILEERR ;
    return DATAFILEERR;
  }
  if(Verbose) fprintf(stderr,"SetNewFile(): %s is open\n",FullName);

/* Recover time  dimension */
  status = nc_inq_dimid(D->ncID,TimeDimName,&TimeDimID);
  if(status != NC_NOERR) 
  {
    if(Verbose) fprintf(stderr,"SetNewFile(): Get Time Dim: %s\n",nc_strerror(status));
    D->LastFileStatus = DATAFILEERR;
    return DATAFILEERR;
  }
  status = nc_inq_dimlen(D->ncID,TimeDimID,&(D->Maxnc_rec));
  if(status != NC_NOERR) 
  {          
    if(Verbose) fprintf(stderr,"SetNewFile: GetFile Length: %s\n",nc_strerror(status));
    D->LastFileStatus = DATAFILEERR;
    return DATAFILEERR;
  }

/* Setup current position in the new open file */
  if(N > 0) D->nc_rec = 0;
  if(N < 0) D->nc_rec = D->Maxnc_rec-1;
  
  D->LastFileStatus = OK;
  return OK;
}
/*------------------------------------------------------------------------------------*/

/*----------------- VarSize -----------------------------------*/
int  VarSize(nc_type type)
{
   switch(type)
   {
     case NC_BYTE: return sizeof(char);
     case NC_CHAR: return sizeof(char);
     case NC_SHORT: return sizeof(short);
     case NC_INT: return sizeof(int);
     case NC_FLOAT: return sizeof(float);
     case NC_DOUBLE: return sizeof(double);
     default: return 1;
   }
}

/*##############################################################*/
/*----------------------- MaxRecord -----------------------------*/
/*##############################################################*/

size_t MaxRecord(int ncID)
/* ncID - ID of already open NC file */
{
  int timedimid;   /* ID for dimension "Time" */

  int nvars;
  int ndims;
  int *ndims_arr;      /* Array to keep number of dimensions of each variable */
  size_t *dimleng_arr; /* Array to keep all length of dimensions */
  nc_type *vartype_arr; /* Array to keep all types of variables */
  int **dimid_arr; /* array of pointers to arrays of variable dimensions */

  int status;     /* Error indicator */
  int iv, id;
  int cvarsize;    /* size of variable */
  int maxsize;     /* maximal size */
  int recordsize;   /* size by units */
  size_t RecordNumber;

  /*--- general information -------*/
  status = nc_inq_ndims(ncID,&ndims);
  status = nc_inq_nvars(ncID,&nvars);
  status = nc_inq_unlimdim(ncID, &timedimid); /* What is unlimited dimension ID */

  /*---- memory allocation --------*/
  dimleng_arr = (size_t *)malloc(ndims * sizeof(size_t));
  vartype_arr = (nc_type *)malloc(nvars * sizeof(nc_type));
  dimid_arr = (int **)malloc(nvars * sizeof(int *));
  ndims_arr = (int *)malloc(nvars * sizeof(int));

  /* Length of each dimension */
  for(id = 0; id < ndims; id++)
  {
     if(id == timedimid) dimleng_arr[id] = 1;
     else status = nc_inq_dimlen(ncID, id, &(dimleng_arr[id]));
  }

  /* type and dimension of each variable */
  for(iv = 0; iv < nvars; iv++)
  {
      status = nc_inq_vartype(ncID,iv,&(vartype_arr[iv]));
      status = nc_inq_varndims(ncID,iv,&(ndims_arr[iv]));
      dimid_arr[iv] = (int *)malloc(ndims_arr[iv]*sizeof(int)); /* memory for dim ID array */
      status = nc_inq_vardimid(ncID,iv,dimid_arr[iv]);
  }

  /* calculation of size of variable and maximum size of variables */
  maxsize = 0;
  for(iv = 0; iv < nvars; iv++)
  {
     recordsize = 1;
     for(id = 0; id < ndims_arr[iv]; id++)
         recordsize *= dimleng_arr[(dimid_arr[iv])[id]];
     cvarsize  = recordsize * VarSize(vartype_arr[iv]);
     if(cvarsize > maxsize) maxsize = cvarsize;
  }

  /* calculation record number */
  RecordNumber = (size_t)(MAXPACKSIZE / maxsize);
  if(RecordNumber == 0)
  {
     if(Verbose) fprintf(stderr,"MaxRecord(%d): Error in calculation of records number,MAXPACKSIZE = %d, maxsize = %d\n",ncID,MAXPACKSIZE,maxsize);
  }

  /* Free all */
  for(iv = 0; iv < nvars; iv++) free(dimid_arr[iv]);
  free(ndims_arr);
  free(dimid_arr);
  free(vartype_arr);
  free(dimleng_arr);

  return RecordNumber;
}
/*----------------------------------------------------------------------*/