#include "FileWriterCDF.h" #include #include #include #include "../Common/Toolbox.h" #include "../TimeManager/TimeManager.h" using namespace TREPS::Common; using namespace TREPS::TimeManager; namespace TREPS { namespace File { FileWriterCDFClass::FileWriterCDFClass(void):FileWriterAbstractClass(), cdfid(0), filePath("") { memset(&cdfbuffer,0,(CDF_STATUSTEXT_LEN+1)*sizeof(char)); } FileWriterCDFClass::~FileWriterCDFClass(void) { this->close(); } bool FileWriterCDFClass::init(const char *file_path, const map *attributes) { if (!this->close()) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot close previous document"); return false; } this->filePath = file_path; // string tmpPath = this->filePath; tmpPath += ".cdf"; //delete previous file (CDFcreateCDF no overwrite existing file) if (isFileExist(tmpPath.c_str())) deleteFile(tmpPath.c_str()); //CDFcreateCDF add automatically ".cdf" extension CDFstatus status = CDFcreateCDF((char*)this->filePath.c_str(),&this->cdfid); if (status != CDF_OK) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create file " << file_path); CDFerror (status, this->cdfbuffer); return false; } //write attributes for (map::const_iterator att = attributes->begin(); att != attributes->end(); ++att) { if (!this->addAttribute(att->first.c_str(), att->second.c_str(), GLOBAL_SCOPE)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to add global attribut"); return false; } } return true; } bool FileWriterCDFClass::writeData(const DataRecordListClass *data, t_TimeFormat timeFormat, const char *timePattern, const t_FieldList *fields) { //write time fields if (timeFormat != TF_NONE) { long timeVarNum; t_Field timeField; timeField.id = "TREPS_TIME"; timeField.type = FT_TIME; timeField.dims.clear(); timeField.timeformat = timeFormat; timeField.timepattern = timePattern; if (!this->writeField(&timeField, data, timeVarNum)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to create time field"); return false; } } //write data fields if (!fields->empty()) { for (t_FieldList::const_iterator field = fields->begin(); field != fields->end(); ++field) { long varNum; if (!this->writeField(&(*field), data, varNum)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to create field : " << (*field).id); return false; } } } return true; } bool FileWriterCDFClass::writeField(const t_Field *field, const DataRecordListClass *data, long &varNum) { long dataType; long numElts; //create field CDFstatus status; if (!this->getCDFVarTypeDef(field, data, dataType, numElts)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to get variable definition : " << field->id); return false; } long numDims = field->dims.size(); if (numDims == 0) { //for 0-dimensional zVariables this argument is ignored (but must be present) long dimSizes[1] = {0}; long recVary = {VARY}; long dimVarys[1] = {VARY}; status = CDFcreatezVar (this->cdfid, field->id.c_str(), dataType, numElts, 0L, dimSizes, recVary, dimVarys, &varNum); } else { long dimSizes[numDims]; long recVary = {VARY}; long dimVarys[numDims]; int index = 0; for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) { dimSizes[index] = (*dim).size; dimVarys[index] = VARY; } status = CDFcreatezVar (this->cdfid, field->id.c_str(), dataType, numElts, numDims, dimSizes, recVary, dimVarys, &varNum); } if (status != CDF_OK) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to create field : " << field->id); return false; } //write data if (!this->writeFieldData(field, data, varNum, dataType, numElts)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to write field data : " << field->id); return false; } //add field attributes for (map::const_iterator att = field->attributes.begin(); att != field->attributes.end(); ++att) { if (att->second.empty()) continue; if (!this->addAttribute(att->first.c_str(), att->second.c_str(), VARIABLE_SCOPE, varNum)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to add variable attribut"); return false; } } return true;; } bool FileWriterCDFClass::writeFieldData(const t_Field *field, const DataRecordListClass *data, long varNum, long cdfType, long numElts) { CDFstatus status; DataRecordClass *crtRecord = data->getFirstRecord(); int recNum = 0; //create buffer use to set value long dataSize; status = CDFgetDataTypeSize(cdfType,&dataSize); if (status != CDF_OK) return false; while (crtRecord != NULL) { if (field->dims.empty()) { //scalar void *buffer = malloc(dataSize*numElts); memset(buffer,0,dataSize*numElts); if (!this->convertData(field, crtRecord, field->id.c_str(), cdfType, numElts, buffer)) { free(buffer); return false; } status = CDFputzVarRecordData(this->cdfid, varNum, recNum, buffer); if (status != CDF_OK) { free(buffer); return false; } free(buffer); } else { //no scalar int nbValByRec = 1; for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) nbValByRec *= (*dim).size; void *buffer = malloc(nbValByRec * dataSize * numElts); memset(buffer,0,nbValByRec * dataSize * numElts); void *pos = buffer; for (int i = 0; i < nbValByRec; ++i) { //value key in data record string key = field->id; key += "_TREPSID"; key += intToStr(i); //convert data for cdf if (!this->convertData(field, crtRecord, key.c_str(), cdfType, numElts, pos)) { free(buffer); return false; } pos = (void *)((long)pos + (dataSize * numElts)); } status = CDFputzVarRecordData(this->cdfid, varNum, recNum, buffer); free(buffer); } ++recNum; crtRecord = crtRecord->getNextRecord(); } return true; } bool FileWriterCDFClass::getCDFVarTypeDef(const t_Field *field, const DataRecordListClass *data, long &cdfType, long &numElts) { //get time manager instance TimeManagerClass *timeMgr = TimeManagerClass::getInstance(); //always 1 except for var type CDF_CHAR numElts = 1; //compute nb val by record int nbValByRec = 1; for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) nbValByRec *= (*dim).size; // switch (field->type) { case FT_UNKNOWN : { cdfType = CDF_CHAR; //get numElts ie max string size if (nbValByRec == 1) numElts = data->getMaxStringValueSize(field->id.c_str()); else { for (int i = 0; i < nbValByRec; ++i) { string crtId = field->id; crtId += "_TREPSID"; crtId += intToStr(i); int crtNumElts = data->getMaxStringValueSize(crtId.c_str()); numElts = ((crtNumElts > numElts) ? crtNumElts : numElts); } } break; } case FT_FLOAT : cdfType = CDF_FLOAT; break; case FT_DOUBLE : cdfType = CDF_DOUBLE; break; case FT_SHORT : cdfType = CDF_INT2; break; case FT_INT : cdfType = CDF_INT4; break; case FT_LONG : cdfType = CDF_INT8; break; case FT_TIME : { switch (field->timeformat) { case TF_NONE : return false; case TF_PATTERN : cdfType = CDF_CHAR; timeMgr->setCurrentPattern(field->timepattern.c_str()); numElts = timeMgr->getCurrentPatternSize(); break; case TF_TIMESTAMP : cdfType = CDF_INT4; break; case TF_DECIMAL : cdfType = CDF_DOUBLE; break; case TF_EPOCH : cdfType = CDF_EPOCH; break; case TF_TT2000 : cdfType = CDF_TIME_TT2000; break; case TF_DDTIME : cdfType = CDF_CHAR; numElts = 16; break; default : return false; } break; } default : return false; } return true; } bool FileWriterCDFClass::convertData(const t_Field *field, DataRecordClass *record, const char *key, long cdfType, long numElts, void *buffer) { //get time manager instance TimeManagerClass *timeMgr = TimeManagerClass::getInstance(); CDFstatus status; long dataSize; status = CDFgetDataTypeSize(cdfType,&dataSize); if (status != CDF_OK) return false; switch (cdfType) { case CDF_CHAR : { string val; if (field->type != FT_TIME) val = record->getValue(key); else { t_Time time = record->getTime(); val = timeMgr->to_string(time, field->timeformat, field->timepattern.c_str()); } int valLen = val.size(); if (valLen > numElts) return false; memcpy(buffer,val.c_str(),valLen * dataSize); return true; } case CDF_FLOAT : { float f = record->getFloatValue(key); memcpy(buffer,&f,sizeof(float)); return true; } case CDF_DOUBLE : case CDF_EPOCH : { double d; if (field->type != FT_TIME) d = record->getDoubleValue(key); else { t_Time time = record->getTime(); switch (field->timeformat) { case TF_DECIMAL : d = timeMgr->to_DECIMAL(time); break; case TF_EPOCH : d = timeMgr->to_EPOCH(time); break; default : //error d = 0.; } } memcpy(buffer,&d,sizeof(double)); return true; } case CDF_INT2 : { short int s = record->getShortValue(key); memcpy(buffer,&s,sizeof(short int)); return true; } case CDF_INT4 : { long int i; if (field->type != FT_TIME) i = record->getLongValue(key); else { t_Time time = record->getTime(); switch (field->timeformat) { case TF_TIMESTAMP : i = timeMgr->to_TIMESTAMP(time); break; default : //error i = 0; } } memcpy(buffer,&i,sizeof(long int)); return true; } case CDF_INT8 : case CDF_TIME_TT2000 : { long long int l; if (field->type != FT_TIME) l = record->getLongLongValue(key); else { t_Time time = record->getTime(); switch (field->timeformat) { case TF_TT2000 : l = timeMgr->to_TT2000(time); break; default : //error l = 0; } } memcpy(buffer,&l,sizeof(long long int)); return true; } } return false; } bool FileWriterCDFClass::addAttribute(const char *attName, const char *attVal, long scope, long varNum) { if (!this->isOpened()) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"File not initialized - Cannot create general description attribute"); return false; } CDFstatus status; long attNum = CDFgetAttrNum(this->cdfid, (char*)attName); if (attNum < 0) { status = CDFcreateAttr(this->cdfid, attName, scope, &attNum); if (status != CDF_OK) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create description attribute " << attName); return false; } } if (scope == GLOBAL_SCOPE) status = CDFputAttrgEntry (this->cdfid, attNum, 0, CDF_CHAR, strlen(attVal), (void *)attVal); else status = CDFputAttrzEntry (this->cdfid, attNum, varNum, CDF_CHAR, strlen(attVal), (void *)attVal); if (status != CDF_OK) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot put attribute value " << attName); return false; } return true; } bool FileWriterCDFClass::save(void) { if (!this->isOpened() || this->filePath.compare("") == 0) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot save file"); return false; } string tmpPath = this->filePath; tmpPath += ".cdf"; if (!isFileExist(tmpPath.c_str())) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot save file"); return false; } //we need to close the cdf file before to copy it string newPath = this->filePath; this->close(); //copy tmp file to filePath ifstream source(tmpPath.c_str(), ios::binary); if (!source.good()) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot save file"); return false; } ofstream dest(newPath.c_str(), ios::binary); dest << source.rdbuf(); source.close(); dest.close(); return true; } bool FileWriterCDFClass::close(void) { if (this->isOpened()) { CDFstatus status = CDFcloseCDF(this->cdfid); if (status != CDF_OK) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error detected during the close of the CDF file"); } this->cdfid = 0; this->filePath = ""; return (status == CDF_OK); } return !this->isOpened(); } bool FileWriterCDFClass::isOpened(void) { return (this->cdfid > 0); } } }