source: XIOS/dev/dev_olga/src/node/file.cpp @ 1201

Last change on this file since 1201 was 1201, checked in by oabramkina, 7 years ago

Two server levels: merging trunk r1200 (except for non-contiguous zoom) into dev. Tested on Curie. Todo: non-contiguous zoom.

  • 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: 37.2 KB
Line 
1#include "file.hpp"
2
3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
6#include "object_factory.hpp"
7#include "context.hpp"
8#include "context_server.hpp"
9#include "nc4_data_output.hpp"
10#include "nc4_data_input.hpp"
11#include "calendar_util.hpp"
12#include "date.hpp"
13#include "message.hpp"
14#include "type.hpp"
15#include "xios_spl.hpp"
16#include "context_client.hpp"
17#include "mpi.hpp"
18#include "timer.hpp"
19
20namespace xios {
21
22   /// ////////////////////// Dfinitions ////////////////////// ///
23
24   CFile::CFile(void)
25      : CObjectTemplate<CFile>(), CFileAttributes()
26      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
27      , allDomainEmpty(false), isOpen(false)
28   {
29     setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
30     setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
31   }
32
33   CFile::CFile(const StdString & id)
34      : CObjectTemplate<CFile>(id), CFileAttributes()
35      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
36      , allDomainEmpty(false), isOpen(false)
37    {
38      setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
39      setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
40    }
41
42   CFile::~CFile(void)
43   { /* Ne rien faire de plus */ }
44
45   ///---------------------------------------------------------------
46  //! Get name of file
47   StdString CFile::GetName(void)   { return (StdString("file")); }
48   StdString CFile::GetDefName(void){ return (CFile::GetName()); }
49   ENodeType CFile::GetType(void)   { return (eFile); }
50
51   //----------------------------------------------------------------
52
53   const StdString CFile::getFileOutputName(void) const
54   {
55     return (name.isEmpty() ? getId() : name) + (name_suffix.isEmpty() ? StdString("") :  name_suffix.getValue());
56   }
57
58   //----------------------------------------------------------------
59   /*!
60   \brief Get data writer object.
61   Each enabled file in xml represents a physical netcdf file.
62   This function allows to access the data writer object.
63   \return data writer object.
64   */
65   boost::shared_ptr<CDataOutput> CFile::getDataOutput(void) const
66   {
67      return data_out;
68   }
69
70   /*!
71   \brief Get data reader object.
72   Each enabled file in xml represents a physical netcdf file.
73   This function allows to access the data reader object.
74   \return data reader object.
75   */
76   boost::shared_ptr<CDataInput> CFile::getDataInput(void) const
77   {
78      return data_in;
79   }
80
81   /*!
82   \brief Get virtual field group
83      In each file, there always exists a field group which is the ancestor of all
84   fields in the file. This is considered be virtual because it is created automatically during
85   file initialization and it normally doesn't appear on xml file
86   \return Pointer to field group
87   */
88   CFieldGroup* CFile::getVirtualFieldGroup(void) const
89   {
90      return (this->vFieldGroup);
91   }
92
93   /*!
94   \brief Get virtual variable group
95      In each file, there always exists a variable group which is the ancestor of all
96   variable in the file. This is considered be virtual because it is created automatically during
97   file initialization and it normally doesn't appear on xml file
98   \return Pointer to variable group
99   */
100   CVariableGroup* CFile::getVirtualVariableGroup(void) const
101   {
102      return (this->vVariableGroup);
103   }
104
105   //! Get all fields of a file
106   std::vector<CField*> CFile::getAllFields(void) const
107   {
108      return (this->vFieldGroup->getAllChildren());
109   }
110
111   //! Get all variables of a file
112   std::vector<CVariable*> CFile::getAllVariables(void) const
113   {
114      return (this->vVariableGroup->getAllChildren());
115   }
116
117   //----------------------------------------------------------------
118   /*!
119   \brief Get all enabled fields of file
120      A field is considered to be enabled if it fullfil these conditions: it is enabled, inside a enabled file
121   and its own level is not larger than file output level.
122   \param [in] default_outputlevel default value output level of file
123   \param [in] default_level default value level of field
124   \param [in] default_enabled flag determine by default if field is enabled
125   \return Vector of pointers of enabled fields
126   */
127   std::vector<CField*> CFile::getEnabledFields(int default_outputlevel,
128                                                int default_level,
129                                                bool default_enabled)
130   {
131      if (!this->enabledFields.empty())
132         return (this->enabledFields);
133
134      const int _outputlevel =
135         (!output_level.isEmpty()) ? output_level.getValue() : default_outputlevel;
136      std::vector<CField*>::iterator it;
137      this->enabledFields = this->getAllFields();
138
139      std::vector<CField*> newEnabledFields;
140
141      for ( it = this->enabledFields.begin(); it != this->enabledFields.end(); it++ )
142      {
143         if (!(*it)->enabled.isEmpty()) // Si l'attribut 'enabled' est dfini ...
144         {
145            if (! (*it)->enabled.getValue()) continue;
146//            { it--; this->enabledFields.erase(it+1); continue; }
147         }
148         else // Si l'attribut 'enabled' n'est pas dfini ...
149         {
150            if (!default_enabled) continue;
151//            { it--; this->enabledFields.erase(it+1); continue; }
152         }
153
154         if (!(*it)->level.isEmpty()) // Si l'attribut 'level' est dfini ...
155         {
156            if ((*it)->level.getValue() > _outputlevel) continue;
157//            { it--; this->enabledFields.erase(it+1); continue; }
158         }
159         else // Si l'attribut 'level' n'est pas dfini ...
160         {
161            if (default_level > _outputlevel) continue;
162//            { it--; this->enabledFields.erase(it+1); continue; }
163         }
164
165//         CField* field_tmp=(*it).get();
166//         shared_ptr<CField> sptfield=*it;
167//         field_tmp->refObject.push_back(sptfield);
168         newEnabledFields.push_back(*it);
169         // Le champ est finalement actif, on y ajoute sa propre reference.
170//         (*it)->refObject.push_back(*it);
171         // Le champ est finalement actif, on y ajoute la rfrence au champ de base.
172         (*it)->setRelFile(CFile::get(this));
173      }
174      enabledFields = newEnabledFields;
175
176      return (this->enabledFields);
177   }
178
179   //----------------------------------------------------------------
180   //! Change virtual field group to a new one
181   void CFile::setVirtualFieldGroup(CFieldGroup* newVFieldGroup)
182   {
183      this->vFieldGroup = newVFieldGroup;
184   }
185
186   //! Change virtual variable group to new one
187   void CFile::setVirtualVariableGroup(CVariableGroup* newVVariableGroup)
188   {
189      this->vVariableGroup = newVVariableGroup;
190   }
191
192   //----------------------------------------------------------------
193   bool CFile::isSyncTime(void)
194   {
195     CContext* context = CContext::getCurrent();
196     const CDate& currentDate = context->calendar->getCurrentDate();
197     if (!sync_freq.isEmpty())
198     {
199       if (lastSync + sync_freq.getValue() < currentDate)
200       {
201         lastSync = currentDate;
202         return true;
203        }
204      }
205      return false;
206    }
207
208   //! Initialize a file in order to write into it
209   void CFile::initFile(void)
210   {
211      CContext* context = CContext::getCurrent();
212      const CDate& currentDate = context->calendar->getCurrentDate();
213      CContextServer* server = context->server;
214
215      lastSync  = currentDate;
216      lastSplit = currentDate;
217      if (!split_freq.isEmpty())
218      {
219        StdString keySuffix("CContext_"+CContext::getCurrent()->getId()+"::CFile_"+getFileOutputName()+"::") ; 
220        if (context->registryIn->foundKey(keySuffix+"splitStart") && context->registryIn->foundKey(keySuffix+"splitEnd"))
221        {
222          CDate savedSplitStart(*context->getCalendar()), savedSplitEnd(*context->getCalendar());
223          context->registryIn->getKey(keySuffix+"splitStart", savedSplitStart);
224          context->registryIn->getKey(keySuffix+"splitEnd",   savedSplitEnd);
225
226          if (savedSplitStart <= lastSplit && lastSplit <= savedSplitEnd)
227            lastSplit = savedSplitStart;
228        }
229      }
230      isOpen = false;
231
232      allDomainEmpty = true;
233
234//      if (!record_offset.isEmpty() && record_offset < 0)
235//        ERROR("void CFile::initFile(void)",
236//              "Invalid 'record_offset', this attribute cannot be negative.");
237      const int recordOffset = record_offset.isEmpty() ? 0 : record_offset;
238
239      set<StdString> setAxis;
240      set<StdString> setDomains;
241
242      std::vector<CField*>::iterator it, end = this->enabledFields.end();
243      for (it = this->enabledFields.begin(); it != end; it++)
244      {
245         CField* field = *it;
246         allDomainEmpty &= !field->grid->doGridHaveDataToWrite();
247         std::vector<CAxis*> vecAxis = field->grid->getAxis();
248         for (size_t i = 0; i < vecAxis.size(); ++i)
249           setAxis.insert(vecAxis[i]->getAxisOutputName());
250//            setAxis.insert(vecAxis[i]);
251         std::vector<CDomain*> vecDomains = field->grid->getDomains();
252         for (size_t i = 0; i < vecDomains.size(); ++i)
253           setDomains.insert(vecDomains[i]->getDomainOutputName());
254//            setDomains.insert(vecDomains[i]);
255
256         field->resetNStep(recordOffset);
257      }
258      nbAxis = setAxis.size();
259      nbDomains = setDomains.size();
260
261      // create sub communicator for file
262      int color = allDomainEmpty ? 0 : 1;
263      MPI_Comm_split(server->intraComm, color, server->intraCommRank, &fileComm);
264      if (allDomainEmpty) MPI_Comm_free(&fileComm);
265
266      // if (time_counter.isEmpty()) time_counter.setValue(time_counter_attr::centered);
267      if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
268    }
269
270    /*
271       Check condition to write into a file
272       For now, we only use the level-2 server to write files (if this mode is activated)
273       or classical server to do this job.
274    */
275    void CFile::checkWriteFile(void)
276    {
277      CContext* context = CContext::getCurrent();
278      // Done by classical server or secondary server
279      // This condition should be changed soon
280      if (!CXios::usingServer2 || (CXios::usingServer2 && !context->hasClient))         
281      {
282        if (mode.isEmpty() || mode.getValue() == mode_attr::write)
283        {
284          CTimer::get("Files : create headers").resume();
285          if (!isOpen) createHeader();
286          CTimer::get("Files : create headers").suspend();
287          checkSync();
288        }       
289        checkSplit(); // REally need this?
290      }
291    }
292
293    /*
294       Check condition to read from a file
295       For now, we only use the level-1 server to write files (if this mode is activated)
296       or classical server to do this job.
297       This function can be used by client for reading metadata
298    */
299    void CFile::checkReadFile(void)
300    {
301      CContext* context = CContext::getCurrent();
302      // Done by classical server or secondary server
303      // TODO: This condition should be changed soon. It only works with maximum number of level as 2
304      if (!CXios::usingServer2 || (CXios::usingServer2 && context->hasClient))
305      {
306        if (!mode.isEmpty() && mode.getValue() == mode_attr::read)
307        {
308          CTimer::get("Files : open headers").resume();
309          if (!isOpen) openInReadMode(&(context->server->intraComm));
310          CTimer::get("Files : open headers").suspend();
311        }
312        //checkSplit(); // Really need for reading?
313      }
314    }
315
316    /*!
317    \brief Verify if synchronisation should be done
318        If syn option is enabled, syn frequence and current time will be used to
319    calculate the moment to syn file(s)
320    \return True if it is the moment to synchronize file, otherwise false
321    */
322   bool CFile::checkSync(void)
323   {
324     CContext* context = CContext::getCurrent();
325     const CDate& currentDate = context->calendar->getCurrentDate();
326     if (!sync_freq.isEmpty())
327     {
328       if (lastSync + sync_freq.getValue() <= currentDate)
329       {
330         lastSync = currentDate;
331         data_out->syncFile();
332         return true;
333        }
334      }
335      return false;
336    }
337
338    /*!
339    \brief Verify if splitting should be done
340        If split option is enabled, split frequence and current time will be used to
341    calculate the moment to split file
342    \return True if it is the moment to split file, otherwise false
343    */
344    bool CFile::checkSplit(void)
345    {
346      CContext* context = CContext::getCurrent();
347      const CDate& currentDate = context->calendar->getCurrentDate();
348      if (!split_freq.isEmpty())
349      {
350        if (currentDate > lastSplit + split_freq.getValue())
351        {
352          lastSplit = lastSplit + split_freq.getValue();
353          std::vector<CField*>::iterator it, end = this->enabledFields.end();
354          for (it = this->enabledFields.begin(); it != end; it++)
355          {
356            (*it)->resetNStep();
357            (*it)->resetNStepMax();
358          }
359          if (mode.isEmpty() || mode.getValue() == mode_attr::write)
360            createHeader();
361          else
362            openInReadMode();
363          return true;
364        }
365      }
366      return false;
367    }
368
369   /*!
370   \brief Create header of netcdf file
371   There are some information to fill in header of each netcdf.
372   */
373   void CFile::createHeader(void)
374   {
375      CContext* context = CContext::getCurrent();
376      CContextServer* server = context->server;
377
378      if (!allDomainEmpty)
379      {
380         StdString filename = getFileOutputName();
381
382// determine splitting format in the file name  : firstPart%start_date%middlePart%end_date%lastPart
383
384         std::string strStartDate="%start_date%" ;
385         std::string strEndDate="%end_date%" ;
386
387         std::string firstPart ;
388         std::string middlePart ;
389         std::string lastPart ;
390         size_t pos1, pos2 ;
391         bool hasStartDate=false ;
392         bool hasEndDate=false ;
393         bool hasSplit = (!split_freq.isEmpty());
394                 
395         pos1=filename.find(strStartDate) ;
396         if (pos1!=std::string::npos)
397         {
398           firstPart=filename.substr(0,pos1) ;
399           pos1+=strStartDate.size() ;
400           hasStartDate=true ;
401         }
402         else pos1=0 ;
403
404         pos2=filename.find(strEndDate,pos1) ;
405         if (pos2!=std::string::npos)
406         {
407           middlePart=filename.substr(pos1,pos2-pos1) ;
408           pos2+=strEndDate.size() ;
409           lastPart=filename.substr(pos2,filename.size()-pos2) ;
410           hasEndDate=true ;
411         }
412         else middlePart=filename.substr(pos1,filename.size()) ;
413
414         if (!hasStartDate && !hasEndDate)
415         {
416           hasStartDate=true ;
417           hasEndDate=true;
418           firstPart=middlePart ;
419           if (hasSplit) firstPart +="_";
420           middlePart="-" ;
421         }
422   
423         StdOStringStream oss;
424
425         if (!split_freq.isEmpty())
426         {
427           CDate split_start ;
428           CDate splitEnd ;
429           if (!split_start_offset.isEmpty()) split_start=lastSplit + split_start_offset ;
430           else split_start=lastSplit ;
431
432           splitEnd = lastSplit + split_freq ;
433           if (!split_last_date.isEmpty())
434           {
435             CDate splitLastDate=CDate::FromString(split_last_date,*CContext::getCurrent()->getCalendar()) ;
436             if( splitLastDate < splitEnd)  splitEnd=splitLastDate ;
437           }
438           
439           if (!split_end_offset.isEmpty()) splitEnd = splitEnd + split_end_offset;
440           else splitEnd = splitEnd - 1 * Second;
441
442           string splitFormat;
443           if (split_freq_format.isEmpty())
444           {
445             if (split_freq.getValue().second != 0) splitFormat = "%y%mo%d%h%mi%s";
446             else if (split_freq.getValue().minute != 0) splitFormat = "%y%mo%d%h%mi";
447             else if (split_freq.getValue().hour != 0) splitFormat = "%y%mo%d%h";
448             else if (split_freq.getValue().day != 0) splitFormat = "%y%mo%d";
449             else if (split_freq.getValue().month != 0) splitFormat = "%y%mo";
450             else splitFormat = "%y";
451           }
452           else splitFormat = split_freq_format;
453
454           oss << firstPart ;
455           if (hasStartDate) oss << split_start.getStr(splitFormat) ;
456           oss << middlePart ;
457           if (hasEndDate) oss << splitEnd.getStr(splitFormat);
458           oss << lastPart ;
459
460           StdString keySuffix("CContext_"+CContext::getCurrent()->getId()+"::CFile_"+getFileOutputName()+"::") ; 
461           context->registryOut->setKey(keySuffix+"splitStart", lastSplit);
462           context->registryOut->setKey(keySuffix+"splitEnd",   splitEnd);
463         }
464         else oss<<firstPart<<lastPart ;
465
466        bool append = !this->append.isEmpty() && this->append.getValue();
467
468         bool useClassicFormat = !format.isEmpty() && format == format_attr::netcdf4_classic;
469         bool useCFConvention = convention.isEmpty() || convention == convention_attr::CF;
470
471         bool multifile = true;
472         if (!type.isEmpty())
473         {
474           if (type == type_attr::one_file) multifile = false;
475           else if (type == type_attr::multiple_file) multifile = true;
476
477         }
478#ifndef USING_NETCDF_PAR
479         if (!multifile)
480         {
481            info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
482            multifile = true;
483          }
484#endif
485         if (multifile)
486         {
487            int commSize, commRank;
488            MPI_Comm_size(fileComm, &commSize);
489            MPI_Comm_rank(fileComm, &commRank);
490
491            if (server->intraCommSize > 1)
492            {
493              oss << "_" ;
494              int width=0; int n = commSize-1;
495              while (n != 0) { n = n / 10; width++;}
496              if (!min_digits.isEmpty())
497                if (width < min_digits) width = min_digits;
498              oss.width(width);
499              oss.fill('0');
500              oss << right << commRank;
501            }
502         }
503         oss << ".nc";
504
505         bool isCollective = par_access.isEmpty() ||  par_access == par_access_attr::collective;
506
507         if (isOpen) data_out->closeFile();
508
509        data_out = shared_ptr<CDataOutput>(new CNc4DataOutput(this, oss.str(), append, useClassicFormat, useCFConvention,
510                                                              fileComm, multifile, isCollective, time_counter_name));
511        isOpen = true;
512
513        data_out->writeFile(CFile::get(this));
514
515        // Do not recreate the file structure if opening an existing file
516        if (!data_out->IsInAppendMode())
517        {
518          std::vector<CField*>::iterator it, end = this->enabledFields.end();
519          for (it = this->enabledFields.begin(); it != end; it++)
520          {
521            CField* field = *it;
522            this->data_out->writeFieldGrid(field);
523          }
524          this->data_out->writeTimeDimension();
525
526          for (it = this->enabledFields.begin(); it != end; it++)
527          {
528            CField* field = *it;
529            this->data_out->writeFieldTimeAxis(field);
530          }
531         
532          for (it = this->enabledFields.begin(); it != end; it++)
533          {
534            CField* field = *it;
535            this->data_out->writeField(field);
536          }
537
538          vector<CVariable*> listVars = getAllVariables();
539          for (vector<CVariable*>::iterator it = listVars.begin(); it != listVars.end(); it++)
540            this->data_out->writeAttribute(*it);
541
542          this->data_out->definition_end();
543        }
544        else
545        {
546          // check time axis even in append mode
547          std::vector<CField*>::iterator it, end = this->enabledFields.end();
548          for (it = this->enabledFields.begin(); it != end; it++)
549          {
550            CField* field = *it;
551            this->data_out->writeFieldTimeAxis(field);
552          }
553        }
554      }
555   }
556
557  /*!
558  \brief Open an existing NetCDF file in read-only mode
559  */
560  void CFile::openInReadMode(MPI_Comm* comm)
561  {
562    CContext* context = CContext::getCurrent();
563    CContextServer* server = context->server;
564    MPI_Comm readComm = *comm;
565
566    if (!allDomainEmpty)
567    {
568      StdString filename = getFileOutputName();
569      StdOStringStream oss;
570      oss << filename;
571
572      if (!split_freq.isEmpty())
573      {
574        string splitFormat;
575        if (split_freq_format.isEmpty())
576        {
577          if (split_freq.getValue().second != 0) splitFormat = "%y%mo%d%h%mi%s";
578          else if (split_freq.getValue().minute != 0) splitFormat = "%y%mo%d%h%mi";
579          else if (split_freq.getValue().hour != 0) splitFormat = "%y%mo%d%h";
580          else if (split_freq.getValue().day != 0) splitFormat = "%y%mo%d";
581          else if (split_freq.getValue().month != 0) splitFormat = "%y%mo";
582          else splitFormat = "%y";
583        }
584        else splitFormat = split_freq_format;
585        oss << "_" << lastSplit.getStr(splitFormat)
586        << "-" << (lastSplit + split_freq.getValue() - 1 * Second).getStr(splitFormat);
587      }
588
589      bool multifile = true;
590      if (!type.isEmpty())
591      {
592        if (type == type_attr::one_file) multifile = false;
593        else if (type == type_attr::multiple_file) multifile = true;
594      }
595  #ifndef USING_NETCDF_PAR
596      if (!multifile)
597      {
598        info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
599        multifile = true;
600      }
601  #endif
602      if (multifile)
603      {
604        int commSize, commRank;
605        MPI_Comm_size(readComm, &commSize);
606        MPI_Comm_rank(readComm, &commRank);
607
608        if (server->intraCommSize > 1)
609        {
610          oss << "_";
611          int width = 0, n = commSize - 1;
612          while (n != 0) { n = n / 10; width++; }
613          if (!min_digits.isEmpty() && width < min_digits)
614            width = min_digits;
615          oss.width(width);
616          oss.fill('0');
617          oss << right << commRank;
618        }
619      }
620      oss << ".nc";
621
622      bool isCollective = par_access.isEmpty() || par_access == par_access_attr::collective;
623
624      if (isOpen) data_out->closeFile();
625      if (time_counter_name.isEmpty()) data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective));
626      else data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, time_counter_name));
627      isOpen = true;
628    }
629  }
630
631   //! Close file
632   void CFile::close(void)
633   {
634     if (!allDomainEmpty)
635       if (isOpen)
636       {
637         if (mode.isEmpty() || mode.getValue() == mode_attr::write)
638          this->data_out->closeFile();
639         else
640          this->data_in->closeFile();
641       }
642      if (fileComm != MPI_COMM_NULL) MPI_Comm_free(&fileComm);
643   }
644   //----------------------------------------------------------------
645
646   void CFile::readAttributesOfEnabledFieldsInReadMode()
647   {
648     if (enabledFields.empty()) return;
649
650     // Just check file and try to open it
651     if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
652
653     checkReadFile();
654
655     for (int idx = 0; idx < enabledFields.size(); ++idx)
656     {
657        // First of all, find out which domain and axis associated with this field
658        enabledFields[idx]->solveGridReference();
659
660        // Read attributes of domain and axis from this file
661        this->data_in->readFieldAttributesMetaData(enabledFields[idx]);
662
663        // Now complete domain and axis associated with this field
664        enabledFields[idx]->solveGenerateGrid();
665
666        // Read necessary value from file
667        this->data_in->readFieldAttributesValues(enabledFields[idx]);
668
669        // Fill attributes for base reference
670        enabledFields[idx]->solveGridDomainAxisBaseRef();
671     }
672
673     // Now everything is ok, close it
674     close();
675   }
676
677
678   /*!
679   \brief Parse xml file and write information into file object
680   \param [in] node xmld node corresponding in xml file
681   */
682   void CFile::parse(xml::CXMLNode & node)
683   {
684      SuperClass::parse(node);
685
686      if (node.goToChildElement())
687      {
688        do
689        {
690           if (node.getElementName()=="field" || node.getElementName()=="field_group") this->getVirtualFieldGroup()->parseChild(node);
691           else if (node.getElementName()=="variable" || node.getElementName()=="variable_group") this->getVirtualVariableGroup()->parseChild(node);
692        } while (node.goToNextElement());
693        node.goToParentElement();
694      }
695
696   }
697   //----------------------------------------------------------------
698
699   /*!
700   \brief Represent a file in form of string with all its info
701   \return String
702   */
703   StdString CFile::toString(void) const
704   {
705      StdOStringStream oss;
706
707      oss << "<" << CFile::GetName() << " ";
708      if (this->hasId())
709         oss << " id=\"" << this->getId() << "\" ";
710      oss << SuperClassAttribute::toString() << ">" << std::endl;
711      if (this->getVirtualFieldGroup() != NULL)
712         oss << *this->getVirtualFieldGroup() << std::endl;
713      oss << "</" << CFile::GetName() << " >";
714      return (oss.str());
715   }
716
717   //----------------------------------------------------------------
718
719   /*!
720   \brief Find all inheritace among objects in a file.
721   \param [in] apply (true) write attributes of parent into ones of child if they are empty
722                     (false) write attributes of parent into a new container of child
723   \param [in] parent
724   */
725   void CFile::solveDescInheritance(bool apply, const CAttributeMap * const parent)
726   {
727      SuperClassAttribute::setAttributes(parent,apply);
728      this->getVirtualFieldGroup()->solveDescInheritance(apply, NULL);
729      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
730   }
731
732   //----------------------------------------------------------------
733
734   /*!
735   \brief Resolve all reference of active fields.
736      In order to know exactly which data each active field has, a search for all its
737   reference to find its parents or/and its base reference object must be done. Moreover
738   during this search, there are some information that can only be sent to server AFTER
739   all information of active fields are created on server side, e.g: checking mask or index
740   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
741   */
742   void CFile::solveOnlyRefOfEnabledFields(bool sendToServer)
743   {
744     int size = this->enabledFields.size();
745     for (int i = 0; i < size; ++i)
746     {
747       this->enabledFields[i]->solveOnlyReferenceEnabledField(sendToServer);
748     }
749   }
750
751   void CFile::checkGridOfEnabledFields()
752   { 
753     int size = this->enabledFields.size();
754     for (int i = 0; i < size; ++i)
755     {
756       this->enabledFields[i]->checkGridOfEnabledFields();
757     }
758   }
759
760   void CFile::sendGridOfEnabledFields()
761   { 
762     int size = this->enabledFields.size();
763     for (int i = 0; i < size; ++i)
764     {
765       this->enabledFields[i]->sendGridOfEnabledFields();
766     }
767   }
768
769   void CFile::generateNewTransformationGridDest()
770   {
771     int size = this->enabledFields.size();
772     for (int i = 0; i < size; ++i)
773     {
774       this->enabledFields[i]->generateNewTransformationGridDest();
775     }
776   }
777
778   /*!
779   \brief Resolve all reference of active fields.
780      In order to know exactly which data each active field has, a search for all its
781   reference to find its parents or/and its base reference object must be done. Moreover
782   during this search, there are some information that can only be sent to server AFTER
783   all information of active fields are created on server side, e.g: checking mask or index
784   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
785   */
786   void CFile::solveAllRefOfEnabledFieldsAndTransform(bool sendToServer)
787   {
788     int size = this->enabledFields.size();
789     for (int i = 0; i < size; ++i)
790     {       
791      this->enabledFields[i]->solveAllEnabledFieldsAndTransform();
792     }
793   }
794
795   /*!
796    * Constructs the filter graph for each active field.
797    *
798    * \param gc the garbage collector to use when building the filter graph
799    */
800   void CFile::buildFilterGraphOfEnabledFields(CGarbageCollector& gc)
801   {
802     int size = this->enabledFields.size();
803     for (int i = 0; i < size; ++i)
804     {
805       this->enabledFields[i]->buildFilterGraph(gc, true);
806     }
807   }
808
809   /*!
810     Prefetching the data for enabled fields read from file.
811   */
812   void CFile::prefetchEnabledReadModeFields(void)
813   {
814     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
815       return;
816
817     int size = this->enabledFields.size();
818     for (int i = 0; i < size; ++i)
819       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate());
820   }
821
822   /*!
823     Prefetching the data for enabled fields read from file whose data is out-of-date.
824   */
825   void CFile::prefetchEnabledReadModeFieldsIfNeeded(void)
826   {
827     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
828       return;
829
830     int size = this->enabledFields.size();
831     for (int i = 0; i < size; ++i)
832       this->enabledFields[i]->sendReadDataRequestIfNeeded();
833   }
834
835   void CFile::solveFieldRefInheritance(bool apply)
836   {
837      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
838      std::vector<CField*> allF = this->getAllFields();
839      for (unsigned int i = 0; i < allF.size(); i++)
840         allF[i]->solveRefInheritance(apply);
841   }
842
843   //----------------------------------------------------------------
844
845   /*!
846   \brief Add a field into file.
847      A field is added into file and it will be written out if the file is enabled and
848   level of this field is smaller than level_output. A new field won't be created if one
849   with id has already existed
850   \param [in] id String identity of new field
851   \return Pointer to added (or already existed) field
852   */
853   CField* CFile::addField(const string& id)
854   {
855     return vFieldGroup->createChild(id);
856   }
857
858   /*!
859   \brief Add a field group into file.
860      A field group is added into file and it will play a role as parents for fields.
861   A new field group won't be created if one with id has already existed
862   \param [in] id String identity of new field group
863   \return Pointer to added (or already existed) field group
864   */
865   CFieldGroup* CFile::addFieldGroup(const string& id)
866   {
867     return vFieldGroup->createChildGroup(id);
868   }
869
870   /*!
871   \brief Add a variable into file.
872      A variable is added into file and if one with id has already existed, pointer to
873   it will be returned.
874      Variable as long as attributes are information container of file.
875   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
876   to fill in (extra) information for a file.
877   \param [in] id String identity of new variable
878   \return Pointer to added (or already existed) variable
879   */
880   CVariable* CFile::addVariable(const string& id)
881   {
882     return vVariableGroup->createChild(id);
883   }
884
885   /*!
886   \brief Add a variable group into file.
887      A variable group is added into file and it will play a role as parents for variables.
888   A new variable group won't be created if one with id has already existed
889   \param [in] id String identity of new variable group
890   \return Pointer to added (or already existed) variable group
891   */
892   CVariableGroup* CFile::addVariableGroup(const string& id)
893   {
894     return vVariableGroup->createChildGroup(id);
895   }
896
897   void CFile::setContextClient(CContextClient* newContextClient)
898   {
899     client = newContextClient;
900   }
901
902   CContextClient* CFile::getContextClient()
903   {
904     return client;
905   }
906
907   /*!
908   \brief Send a message to create a field on server side
909   \param[in] id String identity of field that will be created on server
910   */
911   void CFile::sendAddField(const string& id, CContextClient* client)
912   {
913      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
914   }
915
916   /*!
917   \brief Send a message to create a field group on server side
918   \param[in] id String identity of field group that will be created on server
919   */
920   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
921   {
922      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
923   }
924
925   /*!
926   \brief Receive a message annoucing the creation of a field on server side
927   \param[in] event Received event
928   */
929   void CFile::recvAddField(CEventServer& event)
930   {
931
932      CBufferIn* buffer = event.subEvents.begin()->buffer;
933      string id;
934      *buffer>>id;
935      get(id)->recvAddField(*buffer);
936   }
937
938   /*!
939   \brief Receive a message annoucing the creation of a field on server side
940   \param[in] buffer Buffer containing message
941   */
942   void CFile::recvAddField(CBufferIn& buffer)
943   {
944      string id;
945      buffer>>id;
946      addField(id);
947   }
948
949   /*!
950   \brief Receive a message annoucing the creation of a field group on server side
951   \param[in] event Received event
952   */
953   void CFile::recvAddFieldGroup(CEventServer& event)
954   {
955
956      CBufferIn* buffer = event.subEvents.begin()->buffer;
957      string id;
958      *buffer>>id;
959      get(id)->recvAddFieldGroup(*buffer);
960   }
961
962   /*!
963   \brief Receive a message annoucing the creation of a field group on server side
964   \param[in] buffer Buffer containing message
965   */
966   void CFile::recvAddFieldGroup(CBufferIn& buffer)
967   {
968      string id;
969      buffer>>id;
970      addFieldGroup(id);
971   }
972
973   /*!
974   \brief Send messages to duplicate all variables on server side
975      Because each variable has also its attributes. So first thing to do is replicate
976   all these attributes on server side. Because variable can have a value, the second thing
977   is to duplicate this value on server, too.
978   */
979   void CFile::sendAddAllVariables(CContextClient* client)
980   {
981     std::vector<CVariable*> allVar = getAllVariables();
982     std::vector<CVariable*>::const_iterator it = allVar.begin();
983     std::vector<CVariable*>::const_iterator itE = allVar.end();
984
985     for (; it != itE; ++it)
986     {
987       this->sendAddVariable((*it)->getId(), client);
988       (*it)->sendAllAttributesToServer(client);
989       (*it)->sendValue(client);
990     }
991   }
992
993   /*!
994   \brief Send a message to create a variable group on server side
995   \param[in] id String identity of variable group that will be created on server
996   \param [in] client client to which we will send this adding action
997   */
998   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
999   {
1000      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1001   }
1002
1003   /*
1004     Send message to add a variable into a file within a certain client
1005     \param [in] id String identity of a variable
1006     \param [in] client client to which we will send this adding action
1007   */
1008   void CFile::sendAddVariable(const string& id, CContextClient* client)
1009   {
1010      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1011   }
1012
1013   /*!
1014   \brief Receive a message annoucing the creation of a variable on server side
1015   \param[in] event Received event
1016   */
1017   void CFile::recvAddVariable(CEventServer& event)
1018   {
1019
1020      CBufferIn* buffer = event.subEvents.begin()->buffer;
1021      string id;
1022      *buffer>>id;
1023      get(id)->recvAddVariable(*buffer);
1024   }
1025
1026   /*!
1027   \brief Receive a message annoucing the creation of a variable on server side
1028   \param[in] buffer Buffer containing message
1029   */
1030   void CFile::recvAddVariable(CBufferIn& buffer)
1031   {
1032      string id;
1033      buffer>>id;
1034      addVariable(id);
1035   }
1036
1037   /*!
1038   \brief Receive a message annoucing the creation of a variable group on server side
1039   \param[in] event Received event
1040   */
1041   void CFile::recvAddVariableGroup(CEventServer& event)
1042   {
1043
1044      CBufferIn* buffer = event.subEvents.begin()->buffer;
1045      string id;
1046      *buffer>>id;
1047      get(id)->recvAddVariableGroup(*buffer);
1048   }
1049
1050   /*!
1051   \brief Receive a message annoucing the creation of a variable group on server side
1052   \param[in] buffer Buffer containing message
1053   */
1054   void CFile::recvAddVariableGroup(CBufferIn& buffer)
1055   {
1056      string id;
1057      buffer>>id;
1058      addVariableGroup(id);
1059   }
1060
1061   /*!
1062     \brief Sending all active (enabled) fields from client to server.
1063   Each field is identified uniquely by its string identity. Not only should we
1064   send the id to server but also we need to send ids of reference domain and reference axis.
1065   With these two id, it's easier to make reference to grid where all data should be written.
1066   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
1067   */
1068   void CFile::sendEnabledFields(CContextClient* client)
1069   {
1070     size_t size = this->enabledFields.size();
1071     for (size_t i = 0; i < size; ++i)
1072     {
1073       CField* field = this->enabledFields[i];
1074       this->sendAddField(field->getId(), client);
1075       field->sendAllAttributesToServer(client);
1076       field->sendAddAllVariables(client);
1077     }
1078   }
1079
1080
1081   /*!
1082   \brief Dispatch event received from client
1083      Whenever a message is received in buffer of server, it will be processed depending on
1084   its event type. A new event type should be added in the switch list to make sure
1085   it processed on server side.
1086   \param [in] event: Received message
1087   */
1088   bool CFile::dispatchEvent(CEventServer& event)
1089   {
1090      if (SuperClass::dispatchEvent(event)) return true;
1091      else
1092      {
1093        switch(event.type)
1094        {
1095           case EVENT_ID_ADD_FIELD :
1096             recvAddField(event);
1097             return true;
1098             break;
1099
1100           case EVENT_ID_ADD_FIELD_GROUP :
1101             recvAddFieldGroup(event);
1102             return true;
1103             break;
1104
1105            case EVENT_ID_ADD_VARIABLE :
1106             recvAddVariable(event);
1107             return true;
1108             break;
1109
1110           case EVENT_ID_ADD_VARIABLE_GROUP :
1111             recvAddVariableGroup(event);
1112             return true;
1113             break;
1114           default :
1115              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1116           return false;
1117        }
1118      }
1119   }
1120
1121
1122
1123
1124   ///---------------------------------------------------------------
1125
1126} // namespace xios
Note: See TracBrowser for help on using the repository browser.