source: XIOS/trunk/src/node/file.cpp @ 2200

Last change on this file since 2200 was 2200, checked in by ymipsl, 3 years ago

Bugs fix for UGRID convention output

  • global indexation was not taking into account
  • coherence problem in connectivity for node, edge and face mesh
  • add UGRID testcase based on sphere cube

YM

  • Property copyright set to
    Software name : XIOS (Xml I/O Server)
    http://forge.ipsl.jussieu.fr/ioserver
    Creation date : January 2009
    Licence : CeCCIL version2
    see license file in root directory : Licence_CeCILL_V2-en.txt
    or http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
    Holder : CEA/LSCE (Laboratoire des Sciences du CLimat et de l'Environnement)
    CNRS/IPSL (Institut Pierre Simon Laplace)
    Project Manager : Yann Meurdesoif
    yann.meurdesoif@cea.fr
File size: 46.0 KB
RevLine 
[219]1#include "file.hpp"
2
[352]3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
[219]6#include "object_factory.hpp"
[300]7#include "context.hpp"
8#include "context_server.hpp"
9#include "nc4_data_output.hpp"
[599]10#include "nc4_data_input.hpp"
[318]11#include "calendar_util.hpp"
12#include "date.hpp"
[352]13#include "message.hpp"
14#include "type.hpp"
[591]15#include "xios_spl.hpp"
[1158]16#include "context_client.hpp"
[382]17#include "mpi.hpp"
[1158]18#include "timer.hpp"
[1234]19#include "server.hpp"
[219]20
[335]21namespace xios {
[509]22
[878]23   /// ////////////////////// Dfinitions ////////////////////// ///
[219]24
25   CFile::CFile(void)
26      : CObjectTemplate<CFile>(), CFileAttributes()
[1639]27      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
[1232]28      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
[509]29   {
[957]30     setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
31     setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
[472]32   }
[219]33
34   CFile::CFile(const StdString & id)
35      : CObjectTemplate<CFile>(id), CFileAttributes()
[1639]36      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
[1232]37      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
[509]38    {
[957]39      setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
40      setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
[472]41    }
[219]42
43   CFile::~CFile(void)
44   { /* Ne rien faire de plus */ }
45
46   ///---------------------------------------------------------------
[509]47  //! Get name of file
[219]48   StdString CFile::GetName(void)   { return (StdString("file")); }
49   StdString CFile::GetDefName(void){ return (CFile::GetName()); }
50   ENodeType CFile::GetType(void)   { return (eFile); }
51
52   //----------------------------------------------------------------
[773]53
[1158]54   const StdString CFile::getFileOutputName(void) const
[1622]55   TRY
[773]56   {
[1158]57     return (name.isEmpty() ? getId() : name) + (name_suffix.isEmpty() ? StdString("") :  name_suffix.getValue());
[773]58   }
[1622]59   CATCH
[773]60
61   //----------------------------------------------------------------
[509]62   /*!
[599]63   \brief Get data writer object.
[509]64   Each enabled file in xml represents a physical netcdf file.
[599]65   This function allows to access the data writer object.
66   \return data writer object.
[509]67   */
[1542]68   std::shared_ptr<CDataOutput> CFile::getDataOutput(void) const
[1622]69   TRY
[219]70   {
[599]71      return data_out;
[219]72   }
[1622]73   CATCH
[219]74
[509]75   /*!
[599]76   \brief Get data reader object.
77   Each enabled file in xml represents a physical netcdf file.
78   This function allows to access the data reader object.
79   \return data reader object.
80   */
[1542]81   std::shared_ptr<CDataInput> CFile::getDataInput(void) const
[1622]82   TRY
[599]83   {
84      return data_in;
85   }
[1622]86   CATCH
[599]87
88   /*!
[509]89   \brief Get virtual field group
90      In each file, there always exists a field group which is the ancestor of all
91   fields in the file. This is considered be virtual because it is created automatically during
92   file initialization and it normally doesn't appear on xml file
93   \return Pointer to field group
94   */
[347]95   CFieldGroup* CFile::getVirtualFieldGroup(void) const
[1622]96   TRY
[219]97   {
98      return (this->vFieldGroup);
99   }
[1622]100   CATCH
[219]101
[509]102   /*!
103   \brief Get virtual variable group
104      In each file, there always exists a variable group which is the ancestor of all
105   variable in the file. This is considered be virtual because it is created automatically during
106   file initialization and it normally doesn't appear on xml file
107   \return Pointer to variable group
108   */
[472]109   CVariableGroup* CFile::getVirtualVariableGroup(void) const
[1622]110   TRY
[472]111   {
112      return (this->vVariableGroup);
113   }
[1622]114   CATCH
[472]115
[509]116   //! Get all fields of a file
[347]117   std::vector<CField*> CFile::getAllFields(void) const
[1622]118   TRY
[219]119   {
120      return (this->vFieldGroup->getAllChildren());
121   }
[1622]122   CATCH
[509]123
124   //! Get all variables of a file
[472]125   std::vector<CVariable*> CFile::getAllVariables(void) const
[1622]126   TRY
[472]127   {
128      return (this->vVariableGroup->getAllChildren());
129   }
[1622]130   CATCH
[219]131
132   //----------------------------------------------------------------
[509]133   /*!
134   \brief Get all enabled fields of file
135      A field is considered to be enabled if it fullfil these conditions: it is enabled, inside a enabled file
136   and its own level is not larger than file output level.
137   \param [in] default_outputlevel default value output level of file
138   \param [in] default_level default value level of field
139   \param [in] default_enabled flag determine by default if field is enabled
140   \return Vector of pointers of enabled fields
141   */
142   std::vector<CField*> CFile::getEnabledFields(int default_outputlevel,
[347]143                                                int default_level,
144                                                bool default_enabled)
[1622]145   TRY
[219]146   {
147      if (!this->enabledFields.empty())
148         return (this->enabledFields);
149
150      const int _outputlevel =
151         (!output_level.isEmpty()) ? output_level.getValue() : default_outputlevel;
[347]152      std::vector<CField*>::iterator it;
[219]153      this->enabledFields = this->getAllFields();
154
[347]155      std::vector<CField*> newEnabledFields;
[509]156
[651]157      for ( it = this->enabledFields.begin(); it != this->enabledFields.end(); it++ )
[219]158      {
[878]159         if (!(*it)->enabled.isEmpty()) // Si l'attribut 'enabled' est dfini ...
[219]160         {
[300]161            if (! (*it)->enabled.getValue()) continue;
[219]162         }
[878]163         else // Si l'attribut 'enabled' n'est pas dfini ...
[219]164         {
[651]165            if (!default_enabled) continue;
[219]166         }
167
[878]168         if (!(*it)->level.isEmpty()) // Si l'attribut 'level' est dfini ...
[219]169         {
[651]170            if ((*it)->level.getValue() > _outputlevel) continue;
[219]171         }
[878]172         else // Si l'attribut 'level' n'est pas dfini ...
[219]173         {
[651]174            if (default_level > _outputlevel) continue;
[219]175         }
[509]176
[651]177         newEnabledFields.push_back(*it);
[878]178         // Le champ est finalement actif, on y ajoute la rfrence au champ de base.
[346]179         (*it)->setRelFile(CFile::get(this));
[219]180      }
[651]181      enabledFields = newEnabledFields;
[219]182
183      return (this->enabledFields);
184   }
[1622]185   CATCH_DUMP_ATTR
[219]186
187   //----------------------------------------------------------------
[509]188   //! Change virtual field group to a new one
[347]189   void CFile::setVirtualFieldGroup(CFieldGroup* newVFieldGroup)
[1622]190   TRY
[509]191   {
192      this->vFieldGroup = newVFieldGroup;
[219]193   }
[1622]194   CATCH_DUMP_ATTR
[219]195
[509]196   //! Change virtual variable group to new one
[472]197   void CFile::setVirtualVariableGroup(CVariableGroup* newVVariableGroup)
[1622]198   TRY
[509]199   {
200      this->vVariableGroup = newVVariableGroup;
[472]201   }
[1622]202   CATCH_DUMP_ATTR
[472]203
[219]204   //----------------------------------------------------------------
[318]205   bool CFile::isSyncTime(void)
[1622]206   TRY
[318]207   {
[651]208     CContext* context = CContext::getCurrent();
209     const CDate& currentDate = context->calendar->getCurrentDate();
[538]210     if (!sync_freq.isEmpty())
[318]211     {
[651]212       if (lastSync + sync_freq.getValue() < currentDate)
[318]213       {
[651]214         lastSync = currentDate;
215         return true;
[318]216        }
217      }
[651]218      return false;
[318]219    }
[1622]220    CATCH_DUMP_ATTR
[509]221
222   //! Initialize a file in order to write into it
[1232]223   void CFile::initWrite(void)
[1622]224   TRY
[321]225   {
[651]226      CContext* context = CContext::getCurrent();
227      const CDate& currentDate = context->calendar->getCurrentDate();
228      CContextServer* server = context->server;
[509]229
[651]230      lastSync  = currentDate;
231      lastSplit = currentDate;
[702]232      if (!split_freq.isEmpty())
233      {
[1316]234        StdString keySuffix("CFile::"+getFileOutputName()+"::") ; 
[1158]235        if (context->registryIn->foundKey(keySuffix+"splitStart") && context->registryIn->foundKey(keySuffix+"splitEnd"))
[702]236        {
237          CDate savedSplitStart(*context->getCalendar()), savedSplitEnd(*context->getCalendar());
[1158]238          context->registryIn->getKey(keySuffix+"splitStart", savedSplitStart);
239          context->registryIn->getKey(keySuffix+"splitEnd",   savedSplitEnd);
[702]240
241          if (savedSplitStart <= lastSplit && lastSplit <= savedSplitEnd)
242            lastSplit = savedSplitStart;
243        }
244      }
[1232]245      isOpen = false;     
[379]246
[952]247//      if (!record_offset.isEmpty() && record_offset < 0)
248//        ERROR("void CFile::initFile(void)",
249//              "Invalid 'record_offset', this attribute cannot be negative.");
250      const int recordOffset = record_offset.isEmpty() ? 0 : record_offset;
[757]251
[1009]252      set<StdString> setAxis;
253      set<StdString> setDomains;
[379]254
255      std::vector<CField*>::iterator it, end = this->enabledFields.end();
[609]256      for (it = this->enabledFields.begin(); it != end; it++)
[379]257      {
[1232]258         CField* field = *it;         
[609]259         std::vector<CAxis*> vecAxis = field->grid->getAxis();
260         for (size_t i = 0; i < vecAxis.size(); ++i)
[1009]261           setAxis.insert(vecAxis[i]->getAxisOutputName());
[609]262         std::vector<CDomain*> vecDomains = field->grid->getDomains();
263         for (size_t i = 0; i < vecDomains.size(); ++i)
[1009]264           setDomains.insert(vecDomains[i]->getDomainOutputName());
[757]265
266         field->resetNStep(recordOffset);
[379]267      }
[609]268      nbAxis = setAxis.size();
269      nbDomains = setDomains.size();
[379]270
[509]271      // create sub communicator for file
[1232]272      createSubComFile();
[692]273
[1158]274      // if (time_counter.isEmpty()) time_counter.setValue(time_counter_attr::centered);
[802]275      if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
[321]276    }
[1622]277    CATCH_DUMP_ATTR
[509]278
[1232]279    //! Initialize a file in order to write into it
280    void CFile::initRead(void)
[1622]281    TRY
[1232]282    {
283      if (checkRead) return;
284      createSubComFile();
285      checkRead = true;
286    }
[1622]287    CATCH_DUMP_ATTR
[1232]288
289    /*!
290      Create a sub communicator in which processes participate in reading/opening file
291    */
292    void CFile::createSubComFile()
[1622]293    TRY
[1232]294    {
295      CContext* context = CContext::getCurrent();
296      CContextServer* server = context->server;
297
298      // create sub communicator for file
299      allZoneEmpty = true;     
300      std::vector<CField*>::iterator it, end = this->enabledFields.end();
301      for (it = this->enabledFields.begin(); it != end; it++)
302      {
303         CField* field = *it;
304         bool nullGrid = (0 == field->grid);
305         allZoneEmpty &= nullGrid ? false : !field->grid->doGridHaveDataToWrite();
306      }
307
308      int color = allZoneEmpty ? 0 : 1;
[1639]309      MPI_Comm_split(server->intraComm, color, server->intraCommRank, &fileComm);
310      if (allZoneEmpty) MPI_Comm_free(&fileComm);
[1232]311    }
[1622]312    CATCH_DUMP_ATTR
[1232]313
[1136]314    /*
315       Check condition to write into a file
316       For now, we only use the level-2 server to write files (if this mode is activated)
317       or classical server to do this job.
318    */
319    void CFile::checkWriteFile(void)
[1622]320    TRY
[321]321    {
[1054]322      CContext* context = CContext::getCurrent();
323      // Done by classical server or secondary server
[1136]324      // This condition should be changed soon
[1234]325      if (CServer::serverLevel == 0 || CServer::serverLevel == 2)
[599]326      {
[1054]327        if (mode.isEmpty() || mode.getValue() == mode_attr::write)
328        {
[1158]329          CTimer::get("Files : create headers").resume();
[1054]330          if (!isOpen) createHeader();
[1158]331          CTimer::get("Files : create headers").suspend();
[1054]332          checkSync();
[1136]333        }       
334        checkSplit(); // REally need this?
335      }
336    }
[1622]337    CATCH_DUMP_ATTR
[1136]338
339    /*
340       Check condition to read from a file
341       For now, we only use the level-1 server to write files (if this mode is activated)
342       or classical server to do this job.
343       This function can be used by client for reading metadata
344    */
345    void CFile::checkReadFile(void)
[1622]346    TRY
[1136]347    {
348      CContext* context = CContext::getCurrent();
349      // Done by classical server or secondary server
350      // TODO: This condition should be changed soon. It only works with maximum number of level as 2
[1239]351      if (CServer::serverLevel == 0 || CServer::serverLevel == 1)
[1136]352      {
353        if (!mode.isEmpty() && mode.getValue() == mode_attr::read)
[1054]354        {
[1158]355          CTimer::get("Files : open headers").resume();
[1232]356         
357          if (!isOpen) openInReadMode();
358
[1158]359          CTimer::get("Files : open headers").suspend();
[1054]360        }
[1136]361        //checkSplit(); // Really need for reading?
[599]362      }
[321]363    }
[1622]364    CATCH_DUMP_ATTR
[509]365
366    /*!
[1232]367      Verify if a process participates in an opening-file communicator
368      \return true if the process doesn't participate in opening file
369    */
370    bool CFile::isEmptyZone()
[1622]371    TRY
[1232]372    {
373      return allZoneEmpty;
374    }
[1622]375    CATCH_DUMP_ATTR
[1232]376
377    /*!
[509]378    \brief Verify if synchronisation should be done
379        If syn option is enabled, syn frequence and current time will be used to
380    calculate the moment to syn file(s)
381    \return True if it is the moment to synchronize file, otherwise false
382    */
[321]383   bool CFile::checkSync(void)
[1622]384   TRY
[321]385   {
[651]386     CContext* context = CContext::getCurrent();
387     const CDate& currentDate = context->calendar->getCurrentDate();
[538]388     if (!sync_freq.isEmpty())
[321]389     {
[651]390       if (lastSync + sync_freq.getValue() <= currentDate)
[321]391       {
[651]392         lastSync = currentDate;
393         data_out->syncFile();
394         return true;
[321]395        }
396      }
[651]397      return false;
[321]398    }
[1622]399   CATCH_DUMP_ATTR
[509]400
401    /*!
402    \brief Verify if splitting should be done
403        If split option is enabled, split frequence and current time will be used to
404    calculate the moment to split file
405    \return True if it is the moment to split file, otherwise false
406    */
[321]407    bool CFile::checkSplit(void)
[1622]408    TRY
[321]409    {
[651]410      CContext* context = CContext::getCurrent();
411      const CDate& currentDate = context->calendar->getCurrentDate();
[538]412      if (!split_freq.isEmpty())
[321]413      {
[651]414        if (currentDate > lastSplit + split_freq.getValue())
[321]415        {
[651]416          lastSplit = lastSplit + split_freq.getValue();
[347]417          std::vector<CField*>::iterator it, end = this->enabledFields.end();
[599]418          for (it = this->enabledFields.begin(); it != end; it++)
419          {
420            (*it)->resetNStep();
421            (*it)->resetNStepMax();
422          }
423          if (mode.isEmpty() || mode.getValue() == mode_attr::write)
[651]424            createHeader();
[599]425          else
426            openInReadMode();
[651]427          return true;
[321]428        }
429      }
[651]430      return false;
[321]431    }
[1622]432    CATCH_DUMP_ATTR
[509]433
434   /*!
435   \brief Create header of netcdf file
436   There are some information to fill in header of each netcdf.
437   */
[300]438   void CFile::createHeader(void)
[1622]439   TRY
[219]440   {
[651]441      CContext* context = CContext::getCurrent();
442      CContextServer* server = context->server;
[509]443
[1232]444      if (!allZoneEmpty)
[219]445      {
[773]446         StdString filename = getFileOutputName();
[875]447
448// determine splitting format in the file name  : firstPart%start_date%middlePart%end_date%lastPart
449
450         std::string strStartDate="%start_date%" ;
451         std::string strEndDate="%end_date%" ;
452
453         std::string firstPart ;
454         std::string middlePart ;
455         std::string lastPart ;
456         size_t pos1, pos2 ;
457         bool hasStartDate=false ;
[920]458         bool hasEndDate=false ;
459         bool hasSplit = (!split_freq.isEmpty());
[875]460                 
461         pos1=filename.find(strStartDate) ;
462         if (pos1!=std::string::npos)
463         {
464           firstPart=filename.substr(0,pos1) ;
465           pos1+=strStartDate.size() ;
466           hasStartDate=true ;
467         }
468         else pos1=0 ;
469
470         pos2=filename.find(strEndDate,pos1) ;
471         if (pos2!=std::string::npos)
472         {
473           middlePart=filename.substr(pos1,pos2-pos1) ;
474           pos2+=strEndDate.size() ;
475           lastPart=filename.substr(pos2,filename.size()-pos2) ;
476           hasEndDate=true ;
477         }
478         else middlePart=filename.substr(pos1,filename.size()) ;
479
480         if (!hasStartDate && !hasEndDate)
481         {
482           hasStartDate=true ;
483           hasEndDate=true;
484           firstPart=middlePart ;
[920]485           if (hasSplit) firstPart +="_";
[875]486           middlePart="-" ;
487         }
[920]488   
[300]489         StdOStringStream oss;
[538]490
[431]491         if (!split_freq.isEmpty())
492         {
[1201]493           CDate split_start ;
494           CDate splitEnd ;
495           if (!split_start_offset.isEmpty()) split_start=lastSplit + split_start_offset ;
496           else split_start=lastSplit ;
[702]497
[1201]498           splitEnd = lastSplit + split_freq ;
499           if (!split_last_date.isEmpty())
500           {
501             CDate splitLastDate=CDate::FromString(split_last_date,*CContext::getCurrent()->getCalendar()) ;
502             if( splitLastDate < splitEnd)  splitEnd=splitLastDate ;
503           }
504           
505           if (!split_end_offset.isEmpty()) splitEnd = splitEnd + split_end_offset;
506           else splitEnd = splitEnd - 1 * Second;
507
[651]508           string splitFormat;
[431]509           if (split_freq_format.isEmpty())
510           {
[1516]511             CDuration splitFreq = split_freq.getValue();
512             splitFreq.solveTimeStep(*CContext::getCurrent()->getCalendar());
513             if (splitFreq.second != 0) splitFormat = "%y%mo%d%h%mi%s";
514             else if (splitFreq.minute != 0) splitFormat = "%y%mo%d%h%mi";
515             else if (splitFreq.hour != 0) splitFormat = "%y%mo%d%h";
516             else if (splitFreq.day != 0) splitFormat = "%y%mo%d";
517             else if (splitFreq.month != 0) splitFormat = "%y%mo";
[538]518             else splitFormat = "%y";
[431]519           }
[651]520           else splitFormat = split_freq_format;
[702]521
[875]522           oss << firstPart ;
[1201]523           if (hasStartDate) oss << split_start.getStr(splitFormat) ;
[875]524           oss << middlePart ;
525           if (hasEndDate) oss << splitEnd.getStr(splitFormat);
526           oss << lastPart ;
[702]527
[1316]528           StdString keySuffix("CFile::"+getFileOutputName()+"::") ; 
[1158]529           context->registryOut->setKey(keySuffix+"splitStart", lastSplit);
530           context->registryOut->setKey(keySuffix+"splitEnd",   splitEnd);
[431]531         }
[875]532         else oss<<firstPart<<lastPart ;
[509]533
[528]534        bool append = !this->append.isEmpty() && this->append.getValue();
[567]535
[517]536         bool useClassicFormat = !format.isEmpty() && format == format_attr::netcdf4_classic;
[878]537         bool useCFConvention = convention.isEmpty() || convention == convention_attr::CF;
[517]538
[651]539         bool multifile = true;
[300]540         if (!type.isEmpty())
541         {
[651]542           if (type == type_attr::one_file) multifile = false;
543           else if (type == type_attr::multiple_file) multifile = true;
[379]544
[509]545         }
[379]546#ifndef USING_NETCDF_PAR
547         if (!multifile)
548         {
[651]549            info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
550            multifile = true;
[379]551          }
552#endif
[509]553         if (multifile)
[300]554         {
[651]555            int commSize, commRank;
[1639]556            MPI_Comm_size(fileComm, &commSize);
557            MPI_Comm_rank(fileComm, &commRank);
[509]558
559            if (server->intraCommSize > 1)
[375]560            {
[651]561              oss << "_" ;
562              int width=0; int n = commSize-1;
563              while (n != 0) { n = n / 10; width++;}
[509]564              if (!min_digits.isEmpty())
[651]565                if (width < min_digits) width = min_digits;
566              oss.width(width);
567              oss.fill('0');
568              oss << right << commRank;
[375]569            }
[300]570         }
571         oss << ".nc";
572
[526]573         bool isCollective = par_access.isEmpty() ||  par_access == par_access_attr::collective;
574
575         if (isOpen) data_out->closeFile();
576
[1542]577        data_out = std::shared_ptr<CDataOutput>(new CNc4DataOutput(this, oss.str(), append, useClassicFormat, useCFConvention,
[802]578                                                              fileComm, multifile, isCollective, time_counter_name));
[528]579        isOpen = true;
[300]580
[528]581        data_out->writeFile(CFile::get(this));
582
[1494]583        if (!useCFConvention) sortEnabledFieldsForUgrid();
584
[528]585        // Do not recreate the file structure if opening an existing file
586        if (!data_out->IsInAppendMode())
587        {
588          std::vector<CField*>::iterator it, end = this->enabledFields.end();
589          for (it = this->enabledFields.begin(); it != end; it++)
590          {
[347]591            CField* field = *it;
[300]592            this->data_out->writeFieldGrid(field);
[528]593          }
594          this->data_out->writeTimeDimension();
[509]595
[528]596          for (it = this->enabledFields.begin(); it != end; it++)
597          {
[347]598            CField* field = *it;
[1158]599            this->data_out->writeFieldTimeAxis(field);
600          }
601         
602          for (it = this->enabledFields.begin(); it != end; it++)
603          {
604            CField* field = *it;
[300]605            this->data_out->writeField(field);
[528]606          }
[509]607
[651]608          vector<CVariable*> listVars = getAllVariables();
[528]609          for (vector<CVariable*>::iterator it = listVars.begin(); it != listVars.end(); it++)
610            this->data_out->writeAttribute(*it);
[509]611
[528]612          this->data_out->definition_end();
613        }
[1158]614        else
615        {
616          // check time axis even in append mode
617          std::vector<CField*>::iterator it, end = this->enabledFields.end();
618          for (it = this->enabledFields.begin(); it != end; it++)
619          {
620            CField* field = *it;
621            this->data_out->writeFieldTimeAxis(field);
622          }
623        }
[219]624      }
625   }
[1622]626   CATCH_DUMP_ATTR
[219]627
[599]628  /*!
629  \brief Open an existing NetCDF file in read-only mode
630  */
[1232]631  void CFile::openInReadMode()
[1622]632  TRY
[599]633  {
634    CContext* context = CContext::getCurrent();
[651]635    CContextServer* server = context->server;
[1639]636    MPI_Comm readComm = this->fileComm;
[599]637
[1232]638    if (!allZoneEmpty)
[599]639    {
[773]640      StdString filename = getFileOutputName();
[599]641      StdOStringStream oss;
642      oss << filename;
643
644      if (!split_freq.isEmpty())
645      {
646        string splitFormat;
647        if (split_freq_format.isEmpty())
648        {
[1516]649          CDuration splitFreq = split_freq.getValue();
650          splitFreq.solveTimeStep(*CContext::getCurrent()->getCalendar());
651          if (splitFreq.second != 0) splitFormat = "%y%mo%d%h%mi%s";
652          else if (splitFreq.minute != 0) splitFormat = "%y%mo%d%h%mi";
653          else if (splitFreq.hour != 0) splitFormat = "%y%mo%d%h";
654          else if (splitFreq.day != 0) splitFormat = "%y%mo%d";
655          else if (splitFreq.month != 0) splitFormat = "%y%mo";
[599]656          else splitFormat = "%y";
657        }
[651]658        else splitFormat = split_freq_format;
659        oss << "_" << lastSplit.getStr(splitFormat)
660        << "-" << (lastSplit + split_freq.getValue() - 1 * Second).getStr(splitFormat);
[599]661      }
662
663      bool multifile = true;
664      if (!type.isEmpty())
665      {
666        if (type == type_attr::one_file) multifile = false;
667        else if (type == type_attr::multiple_file) multifile = true;
668      }
669  #ifndef USING_NETCDF_PAR
670      if (!multifile)
671      {
672        info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
673        multifile = true;
674      }
675  #endif
676      if (multifile)
677      {
678        int commSize, commRank;
[1639]679        MPI_Comm_size(readComm, &commSize);
680        MPI_Comm_rank(readComm, &commRank);
[599]681
682        if (server->intraCommSize > 1)
683        {
[651]684          oss << "_";
[599]685          int width = 0, n = commSize - 1;
686          while (n != 0) { n = n / 10; width++; }
687          if (!min_digits.isEmpty() && width < min_digits)
688            width = min_digits;
689          oss.width(width);
690          oss.fill('0');
691          oss << right << commRank;
692        }
693      }
694      oss << ".nc";
695
696      bool isCollective = par_access.isEmpty() || par_access == par_access_attr::collective;
[1485]697      bool readMetaDataPar = true;
698      if (!context->hasServer) readMetaDataPar = (read_metadata_par.isEmpty()) ? false : read_metadata_par;
[599]699
700      if (isOpen) data_out->closeFile();
[1486]701      bool ugridConvention = !convention.isEmpty() ? (convention == convention_attr::UGRID) : false;
702      if (time_counter_name.isEmpty())
[1542]703        data_in = std::shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar, ugridConvention));
[1486]704      else
[1542]705        data_in = std::shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar, ugridConvention, time_counter_name));
[599]706      isOpen = true;
707    }
708  }
[1622]709  CATCH_DUMP_ATTR
[599]710
[509]711   //! Close file
[286]712   void CFile::close(void)
[1622]713   TRY
[286]714   {
[1232]715     if (!allZoneEmpty)
[509]716       if (isOpen)
[321]717       {
[599]718         if (mode.isEmpty() || mode.getValue() == mode_attr::write)
719          this->data_out->closeFile();
720         else
721          this->data_in->closeFile();
[1232]722        isOpen = false;
[321]723       }
[1639]724      if (fileComm != MPI_COMM_NULL) MPI_Comm_free(&fileComm);
[286]725   }
[1622]726   CATCH_DUMP_ATTR
727
[219]728   //----------------------------------------------------------------
729
[775]730   void CFile::readAttributesOfEnabledFieldsInReadMode()
[1622]731   TRY
[775]732   {
733     if (enabledFields.empty()) return;
734
735     // Just check file and try to open it
[805]736     if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
737
[1136]738     checkReadFile();
[805]739
[775]740     for (int idx = 0; idx < enabledFields.size(); ++idx)
741     {
742        // First of all, find out which domain and axis associated with this field
743        enabledFields[idx]->solveGridReference();
744
745        // Read attributes of domain and axis from this file
746        this->data_in->readFieldAttributesMetaData(enabledFields[idx]);
747
748        // Now complete domain and axis associated with this field
749        enabledFields[idx]->solveGenerateGrid();
750
751        // Read necessary value from file
752        this->data_in->readFieldAttributesValues(enabledFields[idx]);
753
754        // Fill attributes for base reference
755        enabledFields[idx]->solveGridDomainAxisBaseRef();
756     }
757
758     // Now everything is ok, close it
759     close();
760   }
[1622]761   CATCH_DUMP_ATTR
[775]762
[509]763   /*!
764   \brief Parse xml file and write information into file object
765   \param [in] node xmld node corresponding in xml file
766   */
[219]767   void CFile::parse(xml::CXMLNode & node)
[1622]768   TRY
[219]769   {
770      SuperClass::parse(node);
[509]771
[472]772      if (node.goToChildElement())
773      {
774        do
775        {
776           if (node.getElementName()=="field" || node.getElementName()=="field_group") this->getVirtualFieldGroup()->parseChild(node);
777           else if (node.getElementName()=="variable" || node.getElementName()=="variable_group") this->getVirtualVariableGroup()->parseChild(node);
[651]778        } while (node.goToNextElement());
[472]779        node.goToParentElement();
[219]780      }
[1622]781   }
782   CATCH_DUMP_ATTR
[472]783
[219]784   //----------------------------------------------------------------
785
[509]786   /*!
787   \brief Represent a file in form of string with all its info
788   \return String
789   */
[219]790   StdString CFile::toString(void) const
[1622]791   TRY
[219]792   {
793      StdOStringStream oss;
794
795      oss << "<" << CFile::GetName() << " ";
796      if (this->hasId())
797         oss << " id=\"" << this->getId() << "\" ";
798      oss << SuperClassAttribute::toString() << ">" << std::endl;
[347]799      if (this->getVirtualFieldGroup() != NULL)
[219]800         oss << *this->getVirtualFieldGroup() << std::endl;
801      oss << "</" << CFile::GetName() << " >";
802      return (oss.str());
803   }
[1622]804   CATCH
[219]805
806   //----------------------------------------------------------------
[509]807
808   /*!
809   \brief Find all inheritace among objects in a file.
810   \param [in] apply (true) write attributes of parent into ones of child if they are empty
811                     (false) write attributes of parent into a new container of child
812   \param [in] parent
813   */
[445]814   void CFile::solveDescInheritance(bool apply, const CAttributeMap * const parent)
[1622]815   TRY
[219]816   {
[445]817      SuperClassAttribute::setAttributes(parent,apply);
[509]818      this->getVirtualFieldGroup()->solveDescInheritance(apply, NULL);
[472]819      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
[219]820   }
[1622]821   CATCH_DUMP_ATTR
[219]822
823   //----------------------------------------------------------------
824
[509]825   /*!
826   \brief Resolve all reference of active fields.
827      In order to know exactly which data each active field has, a search for all its
828   reference to find its parents or/and its base reference object must be done. Moreover
829   during this search, there are some information that can only be sent to server AFTER
830   all information of active fields are created on server side, e.g: checking mask or index
831   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
832   */
[823]833   void CFile::solveOnlyRefOfEnabledFields(bool sendToServer)
[1622]834   TRY
[823]835   {
836     int size = this->enabledFields.size();
837     for (int i = 0; i < size; ++i)
838     {
[1129]839       this->enabledFields[i]->solveOnlyReferenceEnabledField(sendToServer);
[823]840     }
841   }
[1622]842   CATCH_DUMP_ATTR
[823]843
[1025]844   void CFile::checkGridOfEnabledFields()
[1622]845   TRY
[1025]846   { 
847     int size = this->enabledFields.size();
848     for (int i = 0; i < size; ++i)
849     {
850       this->enabledFields[i]->checkGridOfEnabledFields();
851     }
852   }
[1622]853   CATCH_DUMP_ATTR
[1025]854
[1239]855   void CFile::sendGridComponentOfEnabledFields()
[1622]856   TRY
[1239]857   { 
858     int size = this->enabledFields.size();
859     for (int i = 0; i < size; ++i)
860     {
861       this->enabledFields[i]->sendGridComponentOfEnabledFields();
862     }
863   }
[1622]864   CATCH_DUMP_ATTR
[1239]865
[1494]866   /*!
867   \brief Sorting domains with the same name (= describing the same mesh) in the decreasing order of nvertex for UGRID files.
868   This insures that the domain with the highest nvertex is written first and thus all known mesh connectivity is generated at once by this domain.
[2200]869   Secondly, we associate the UGRID mesh to the domain in incresing order, i.e. nvertex=1 first, nvertex=2 and then nvertex>2.
870   In this case the connectivity of each component of the mesh (node, edge and face) are fully coherent.
[1494]871   */
872   void CFile::sortEnabledFieldsForUgrid()
[1622]873   TRY
[1494]874   {
875     int size = this->enabledFields.size();
876     std::vector<int> domainNvertices;
877     std::vector<StdString> domainNames;
[2200]878     std::map<string, tuple<CDomain*,CDomain*,CDomain*>> registeredDomains ;
[1494]879
880     for (int i = 0; i < size; ++i)
881     {
882       std::vector<CDomain*> domain = this->enabledFields[i]->getRelGrid()->getDomains();
883       if (domain.size() != 1)
884       {
885         ERROR("void CFile::sortEnabledFieldsForUgrid()",
886               "A domain, and only one, should be defined for grid "<< this->enabledFields[i]->getRelGrid()->getId() << ".");
887       }
888       StdString domainName = domain[0]->getDomainOutputName();
889       int nvertex;
890       if (domain[0]->nvertex.isEmpty())
891       {
892         ERROR("void CFile::sortEnabledFieldsForUgrid()",
893               "Attributes nvertex must be defined for domain "<< domain[0]->getDomainOutputName() << ".");
894       }
895       else
896         nvertex = domain[0]->nvertex;
897
898       for (int j = 0; j < i; ++j)
899       {
900         if (domainName == domainNames[j] && nvertex > domainNvertices[j])
901         {
902           CField* tmpSwap = this->enabledFields[j];
903           this->enabledFields[j] = this->enabledFields[i];
904           this->enabledFields[i] = tmpSwap;
905           domainNames.push_back(domainNames[j]);
906           domainNames[j] = domainName;
907           domainNvertices.push_back(domainNvertices[j]);
908           domainNvertices[j] = nvertex;
909         }
910         else
911         {
912           domainNames.push_back(domainName);
913           domainNvertices.push_back(nvertex);
914         }
915       }
916       if (i==0)
917       {
918         domainNames.push_back(domainName);
919         domainNvertices.push_back(nvertex);
920       }
[2200]921
922       if (nvertex==1)  std::get<0>(registeredDomains[domainName])=domain[0] ;
923       else if (nvertex==2) std::get<1>(registeredDomains[domainName])=domain[0] ;
924       else  std::get<2>(registeredDomains[domainName])=domain[0] ;
[1494]925     }
[2200]926
927     for(auto& it:registeredDomains)
928     {
929       list<CDomain*> domains ;
930       string domainName=it.first ;
931
932       if (std::get<0>(it.second)!=nullptr) domains.push_back(std::get<0>(it.second)) ;
933       if (std::get<1>(it.second)!=nullptr) domains.push_back(std::get<1>(it.second)) ; 
934       if (std::get<2>(it.second)!=nullptr) domains.push_back(std::get<2>(it.second)) ; 
935       
936       // for each component of a given mesh (i.e. domains with same name but different number of vertices)
937       // associate the UGRID mesh in increasing order
938       for(auto& domain : domains )
939       {
940         domain-> computeWrittenIndex();
941         CArray<int, 1>& indexToWrite = domain->localIndexToWriteOnServer;
942         int nbWritten = indexToWrite.numElements();
943         CArray<double,1> writtenLat, writtenLon;
944         CArray<double,2> writtenBndsLat, writtenBndsLon;
945
946         writtenLat.resize(nbWritten);
947         writtenLon.resize(nbWritten);
948         for (int idx = 0; idx < nbWritten; ++idx)
949         {
950           writtenLat(idx) = domain->latvalue(indexToWrite(idx));
951           writtenLon(idx) = domain->lonvalue(indexToWrite(idx));
952         }
953   
954         int nvertex = domain->nvertex, idx;
955        if (nvertex>1)
956         {
957           writtenBndsLat.resize(nvertex, nbWritten);
958           writtenBndsLon.resize(nvertex, nbWritten);
959           CArray<double,2>& boundslat = domain->bounds_latvalue;
960           CArray<double,2>& boundslon = domain->bounds_lonvalue;
961           for (idx = 0; idx < nbWritten; ++idx)
962             for (int nv = 0; nv < nvertex; ++nv)
963             {
964                writtenBndsLat(nv, idx) = boundslat(nv, int(indexToWrite(idx)));
965                writtenBndsLon(nv, idx) = boundslon(nv, int(indexToWrite(idx)));
966             }
967         }
968         domain->assignMesh(domainName, domain->nvertex);
969         CContextServer* server=CContext::getCurrent()->server ;
970         domain->mesh->createMeshEpsilon(server->intraComm, writtenLon, writtenLat, writtenBndsLon, writtenBndsLat);
971       }
972
973     }
[1494]974   }
[1622]975   CATCH_DUMP_ATTR
[1494]976
[1025]977   void CFile::sendGridOfEnabledFields()
[1622]978   TRY
[1025]979   { 
980     int size = this->enabledFields.size();
981     for (int i = 0; i < size; ++i)
982     {
983       this->enabledFields[i]->sendGridOfEnabledFields();
984     }
985   }
[1622]986   CATCH_DUMP_ATTR
[1025]987
[823]988   void CFile::generateNewTransformationGridDest()
[1622]989   TRY
[823]990   {
991     int size = this->enabledFields.size();
992     for (int i = 0; i < size; ++i)
993     {
994       this->enabledFields[i]->generateNewTransformationGridDest();
995     }
996   }
[1622]997   CATCH_DUMP_ATTR
[823]998
999   /*!
1000   \brief Resolve all reference of active fields.
1001      In order to know exactly which data each active field has, a search for all its
1002   reference to find its parents or/and its base reference object must be done. Moreover
1003   during this search, there are some information that can only be sent to server AFTER
1004   all information of active fields are created on server side, e.g: checking mask or index
1005   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
1006   */
[1129]1007   void CFile::solveAllRefOfEnabledFieldsAndTransform(bool sendToServer)
[1622]1008   TRY
[459]1009   {
[509]1010     int size = this->enabledFields.size();
1011     for (int i = 0; i < size; ++i)
[1144]1012     {       
[1129]1013      this->enabledFields[i]->solveAllEnabledFieldsAndTransform();
[509]1014     }
[459]1015   }
[1622]1016   CATCH_DUMP_ATTR
[509]1017
1018   /*!
[640]1019    * Constructs the filter graph for each active field.
1020    *
1021    * \param gc the garbage collector to use when building the filter graph
1022    */
1023   void CFile::buildFilterGraphOfEnabledFields(CGarbageCollector& gc)
[1622]1024   TRY
[640]1025   {
1026     int size = this->enabledFields.size();
1027     for (int i = 0; i < size; ++i)
1028     {
1029       this->enabledFields[i]->buildFilterGraph(gc, true);
1030     }
1031   }
[1622]1032   CATCH_DUMP_ATTR
[640]1033
1034   /*!
[1358]1035    * Post-process the filter graph for each active field.
1036    */
1037   void CFile::postProcessFilterGraph()
[1622]1038   TRY
[1358]1039   {
1040     int size = this->enabledFields.size();
1041     for (int i = 0; i < size; ++i)
1042     {
1043       this->enabledFields[i]->checkIfMustAutoTrigger();
1044     }
1045   }
[1622]1046   CATCH_DUMP_ATTR
[1358]1047
1048   /*!
[598]1049     Prefetching the data for enabled fields read from file.
1050   */
1051   void CFile::prefetchEnabledReadModeFields(void)
[1622]1052   TRY
[598]1053   {
1054     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1055       return;
1056
1057     int size = this->enabledFields.size();
1058     for (int i = 0; i < size; ++i)
[1021]1059       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate());
[598]1060   }
[1622]1061   CATCH_DUMP_ATTR
[598]1062
1063   /*!
[1358]1064     Do all pre timestep operations for enabled fields in read mode:
1065      - Check that the data excepted from server has been received
1066      - Check if some filters must auto-trigger
1067   */
1068   void CFile::doPreTimestepOperationsForEnabledReadModeFields(void)
[1622]1069   TRY
[1358]1070   {
1071     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1072       return;
1073
1074     int size = this->enabledFields.size();
1075     for (int i = 0; i < size; ++i)
1076     {
1077       this->enabledFields[i]->checkForLateDataFromServer();
1078       this->enabledFields[i]->autoTriggerIfNeeded();
1079     }
1080   }
[1622]1081   CATCH_DUMP_ATTR
[1358]1082
1083   /*!
[1318]1084     Do all post timestep operations for enabled fields in read mode:
1085      - Prefetch the data read from file when needed
[598]1086   */
[1318]1087   void CFile::doPostTimestepOperationsForEnabledReadModeFields(void)
[1622]1088   TRY
[598]1089   {
1090     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1091       return;
1092
1093     int size = this->enabledFields.size();
1094     for (int i = 0; i < size; ++i)
[1318]1095     {
[598]1096       this->enabledFields[i]->sendReadDataRequestIfNeeded();
[1318]1097     }
[598]1098   }
[1622]1099   CATCH_DUMP_ATTR
[598]1100
[445]1101   void CFile::solveFieldRefInheritance(bool apply)
[1622]1102   TRY
[219]1103   {
[878]1104      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
[347]1105      std::vector<CField*> allF = this->getAllFields();
[219]1106      for (unsigned int i = 0; i < allF.size(); i++)
[445]1107         allF[i]->solveRefInheritance(apply);
[219]1108   }
[1622]1109   CATCH_DUMP_ATTR
[219]1110
1111   //----------------------------------------------------------------
1112
[509]1113   /*!
1114   \brief Add a field into file.
1115      A field is added into file and it will be written out if the file is enabled and
1116   level of this field is smaller than level_output. A new field won't be created if one
1117   with id has already existed
1118   \param [in] id String identity of new field
1119   \return Pointer to added (or already existed) field
1120   */
[347]1121   CField* CFile::addField(const string& id)
[1622]1122   TRY
[300]1123   {
[651]1124     return vFieldGroup->createChild(id);
[300]1125   }
[1622]1126   CATCH_DUMP_ATTR
[219]1127
[509]1128   /*!
1129   \brief Add a field group into file.
1130      A field group is added into file and it will play a role as parents for fields.
1131   A new field group won't be created if one with id has already existed
1132   \param [in] id String identity of new field group
1133   \return Pointer to added (or already existed) field group
1134   */
[347]1135   CFieldGroup* CFile::addFieldGroup(const string& id)
[1622]1136   TRY
[300]1137   {
[651]1138     return vFieldGroup->createChildGroup(id);
[300]1139   }
[1622]1140   CATCH_DUMP_ATTR
[509]1141
1142   /*!
1143   \brief Add a variable into file.
1144      A variable is added into file and if one with id has already existed, pointer to
1145   it will be returned.
1146      Variable as long as attributes are information container of file.
1147   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
1148   to fill in (extra) information for a file.
1149   \param [in] id String identity of new variable
1150   \return Pointer to added (or already existed) variable
1151   */
[472]1152   CVariable* CFile::addVariable(const string& id)
[1622]1153   TRY
[472]1154   {
[651]1155     return vVariableGroup->createChild(id);
[472]1156   }
[1622]1157   CATCH_DUMP_ATTR
[472]1158
[509]1159   /*!
1160   \brief Add a variable group into file.
1161      A variable group is added into file and it will play a role as parents for variables.
1162   A new variable group won't be created if one with id has already existed
1163   \param [in] id String identity of new variable group
1164   \return Pointer to added (or already existed) variable group
1165   */
[472]1166   CVariableGroup* CFile::addVariableGroup(const string& id)
[1622]1167   TRY
[472]1168   {
[651]1169     return vVariableGroup->createChildGroup(id);
[472]1170   }
[1622]1171   CATCH_DUMP_ATTR
[509]1172
[1021]1173   void CFile::setContextClient(CContextClient* newContextClient)
[1622]1174   TRY
[1021]1175   {
1176     client = newContextClient;
[1294]1177     size_t size = this->enabledFields.size();
1178     for (size_t i = 0; i < size; ++i)
1179     {
1180       this->enabledFields[i]->setContextClient(newContextClient);
1181     }
[1021]1182   }
[1622]1183   CATCH_DUMP_ATTR
[1021]1184
1185   CContextClient* CFile::getContextClient()
[1622]1186   TRY
[1021]1187   {
1188     return client;
1189   }
[1622]1190   CATCH_DUMP_ATTR
[1021]1191
[1232]1192   void CFile::setReadContextClient(CContextClient* readContextclient)
[1622]1193   TRY
[1232]1194   {
1195     read_client = readContextclient;
1196   }
[1622]1197   CATCH_DUMP_ATTR
[1232]1198
1199   CContextClient* CFile::getReadContextClient()
[1622]1200   TRY
[1232]1201   {
1202     return read_client;
1203   }
[1622]1204   CATCH_DUMP_ATTR
[1232]1205
[509]1206   /*!
1207   \brief Send a message to create a field on server side
1208   \param[in] id String identity of field that will be created on server
1209   */
[1021]1210   void CFile::sendAddField(const string& id, CContextClient* client)
[1622]1211   TRY
[1009]1212   {
[1021]1213      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
[1009]1214   }
[1622]1215   CATCH_DUMP_ATTR
[1009]1216
[509]1217   /*!
1218   \brief Send a message to create a field group on server side
1219   \param[in] id String identity of field group that will be created on server
1220   */
[1136]1221   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
[1622]1222   TRY
[1136]1223   {
1224      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
1225   }
[1622]1226   CATCH_DUMP_ATTR
[1136]1227
1228   /*!
[509]1229   \brief Receive a message annoucing the creation of a field on server side
1230   \param[in] event Received event
1231   */
[300]1232   void CFile::recvAddField(CEventServer& event)
[1622]1233   TRY
[300]1234   {
[509]1235
[651]1236      CBufferIn* buffer = event.subEvents.begin()->buffer;
[300]1237      string id;
[651]1238      *buffer>>id;
1239      get(id)->recvAddField(*buffer);
[300]1240   }
[1622]1241   CATCH
[509]1242
1243   /*!
1244   \brief Receive a message annoucing the creation of a field on server side
1245   \param[in] buffer Buffer containing message
1246   */
[300]1247   void CFile::recvAddField(CBufferIn& buffer)
[1622]1248   TRY
[300]1249   {
[651]1250      string id;
1251      buffer>>id;
1252      addField(id);
[300]1253   }
[1622]1254   CATCH_DUMP_ATTR
[300]1255
[509]1256   /*!
1257   \brief Receive a message annoucing the creation of a field group on server side
1258   \param[in] event Received event
1259   */
[300]1260   void CFile::recvAddFieldGroup(CEventServer& event)
[1622]1261   TRY
[300]1262   {
[509]1263
[651]1264      CBufferIn* buffer = event.subEvents.begin()->buffer;
[300]1265      string id;
[651]1266      *buffer>>id;
1267      get(id)->recvAddFieldGroup(*buffer);
[300]1268   }
[1622]1269   CATCH
[509]1270
1271   /*!
1272   \brief Receive a message annoucing the creation of a field group on server side
1273   \param[in] buffer Buffer containing message
1274   */
[300]1275   void CFile::recvAddFieldGroup(CBufferIn& buffer)
[1622]1276   TRY
[300]1277   {
[651]1278      string id;
1279      buffer>>id;
1280      addFieldGroup(id);
[300]1281   }
[1622]1282   CATCH_DUMP_ATTR
[300]1283
[509]1284   /*!
1285   \brief Send messages to duplicate all variables on server side
1286      Because each variable has also its attributes. So first thing to do is replicate
1287   all these attributes on server side. Because variable can have a value, the second thing
1288   is to duplicate this value on server, too.
1289   */
[1021]1290   void CFile::sendAddAllVariables(CContextClient* client)
[1622]1291   TRY
[1009]1292   {
1293     std::vector<CVariable*> allVar = getAllVariables();
1294     std::vector<CVariable*>::const_iterator it = allVar.begin();
1295     std::vector<CVariable*>::const_iterator itE = allVar.end();
1296
1297     for (; it != itE; ++it)
1298     {
[1021]1299       this->sendAddVariable((*it)->getId(), client);
1300       (*it)->sendAllAttributesToServer(client);
1301       (*it)->sendValue(client);
[1009]1302     }
1303   }
[1622]1304   CATCH_DUMP_ATTR
[1009]1305
[509]1306   /*!
[1009]1307   \brief Send a message to create a variable group on server side
1308   \param[in] id String identity of variable group that will be created on server
[1132]1309   \param [in] client client to which we will send this adding action
1310   */
1311   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
[1622]1312   TRY
[1132]1313   {
1314      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1315   }
[1622]1316   CATCH_DUMP_ATTR
[1132]1317
1318   /*
1319     Send message to add a variable into a file within a certain client
1320     \param [in] id String identity of a variable
1321     \param [in] client client to which we will send this adding action
1322   */
[1021]1323   void CFile::sendAddVariable(const string& id, CContextClient* client)
[1622]1324   TRY
[472]1325   {
[1130]1326      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
[472]1327   }
[1622]1328   CATCH_DUMP_ATTR
[509]1329
1330   /*!
1331   \brief Receive a message annoucing the creation of a variable on server side
1332   \param[in] event Received event
1333   */
[472]1334   void CFile::recvAddVariable(CEventServer& event)
[1622]1335   TRY
[472]1336   {
[651]1337      CBufferIn* buffer = event.subEvents.begin()->buffer;
[472]1338      string id;
[651]1339      *buffer>>id;
1340      get(id)->recvAddVariable(*buffer);
[472]1341   }
[1622]1342   CATCH
[509]1343
1344   /*!
1345   \brief Receive a message annoucing the creation of a variable on server side
1346   \param[in] buffer Buffer containing message
1347   */
[472]1348   void CFile::recvAddVariable(CBufferIn& buffer)
[1622]1349   TRY
[472]1350   {
[651]1351      string id;
1352      buffer>>id;
1353      addVariable(id);
[472]1354   }
[1622]1355   CATCH_DUMP_ATTR
[472]1356
[509]1357   /*!
1358   \brief Receive a message annoucing the creation of a variable group on server side
1359   \param[in] event Received event
1360   */
[472]1361   void CFile::recvAddVariableGroup(CEventServer& event)
[1622]1362   TRY
[472]1363   {
[509]1364
[651]1365      CBufferIn* buffer = event.subEvents.begin()->buffer;
[472]1366      string id;
[651]1367      *buffer>>id;
1368      get(id)->recvAddVariableGroup(*buffer);
[472]1369   }
[1622]1370   CATCH
[509]1371
1372   /*!
1373   \brief Receive a message annoucing the creation of a variable group on server side
1374   \param[in] buffer Buffer containing message
1375   */
[472]1376   void CFile::recvAddVariableGroup(CBufferIn& buffer)
[1622]1377   TRY
[472]1378   {
[651]1379      string id;
1380      buffer>>id;
1381      addVariableGroup(id);
[472]1382   }
[1622]1383   CATCH_DUMP_ATTR
[472]1384
[509]1385   /*!
1386     \brief Sending all active (enabled) fields from client to server.
1387   Each field is identified uniquely by its string identity. Not only should we
1388   send the id to server but also we need to send ids of reference domain and reference axis.
1389   With these two id, it's easier to make reference to grid where all data should be written.
1390   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
[1136]1391   */
1392   void CFile::sendEnabledFields(CContextClient* client)
[1622]1393   TRY
[1009]1394   {
1395     size_t size = this->enabledFields.size();
1396     for (size_t i = 0; i < size; ++i)
1397     {
1398       CField* field = this->enabledFields[i];
[1021]1399       this->sendAddField(field->getId(), client);
[1405]1400       field->checkTimeAttributes();
[1021]1401       field->sendAllAttributesToServer(client);
1402       field->sendAddAllVariables(client);
[1009]1403     }
1404   }
[1622]1405   CATCH_DUMP_ATTR
[1009]1406
[509]1407   /*!
1408   \brief Dispatch event received from client
1409      Whenever a message is received in buffer of server, it will be processed depending on
1410   its event type. A new event type should be added in the switch list to make sure
1411   it processed on server side.
1412   \param [in] event: Received message
1413   */
[300]1414   bool CFile::dispatchEvent(CEventServer& event)
[1622]1415   TRY
[300]1416   {
[651]1417      if (SuperClass::dispatchEvent(event)) return true;
[300]1418      else
1419      {
1420        switch(event.type)
1421        {
1422           case EVENT_ID_ADD_FIELD :
[651]1423             recvAddField(event);
1424             return true;
1425             break;
[509]1426
[300]1427           case EVENT_ID_ADD_FIELD_GROUP :
[651]1428             recvAddFieldGroup(event);
1429             return true;
1430             break;
[509]1431
[472]1432            case EVENT_ID_ADD_VARIABLE :
[651]1433             recvAddVariable(event);
1434             return true;
1435             break;
[509]1436
[472]1437           case EVENT_ID_ADD_VARIABLE_GROUP :
[651]1438             recvAddVariableGroup(event);
1439             return true;
1440             break;
[300]1441           default :
[651]1442              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1443           return false;
[300]1444        }
1445      }
1446   }
[1622]1447   CATCH
[509]1448
[1622]1449   ///--------------------------------------------------------------
1450   /*!
1451   */
1452   StdString CFile::dumpClassAttributes(void)
1453   {
1454     StdString str;
1455     CContext* context = CContext::getCurrent();
1456     str.append("context=\"");
1457     str.append(context->getId());
1458     str.append("\"");
1459     str.append(" enabled fields=\"");
1460     int size = this->enabledFields.size();
1461     for (int i = 0; i < size; ++i)
1462     {
1463       str.append(this->enabledFields[i]->getId());
1464       str.append(" ");
1465     }
1466     str.append("\"");
1467     return str;
1468   }
[509]1469
[219]1470   ///---------------------------------------------------------------
1471
[335]1472} // namespace xios
Note: See TracBrowser for help on using the repository browser.