converted by lore's source converter


//////////////////////////////////////////////////////////////////////////////
//
// MDataX.cpp: Implementation of the MDataX class
//
//////////////////////////////////////////////////////////////////////////////
//
// author:     Eckhard Kantz
// website:    http://wegalink.eu
//
//////////////////////////////////////////////////////////////////////////////
/* 
This is FREE software  

Permission is hereby granted, free of charge,  to any person obtaining  a copy 
of this software and associated documentation files (the "Software"),  to deal 
in the Software without restriction, including without limitation  the  rights 
to use,  copy,  modify,  merge,  publish,  distribute, sublicense, and/or sell 
copies  of  the  Software,   and  to  permit  persons  to  whom  the  Software 
is furnished to do so, subject to the following conditions: 

There are no conditions imposed on the use of this software. 

THE SOFTWARE IS PROVIDED "AS IS",  WITHOUT  WARRANTY  OF ANY KIND,  EXPRESS OR 
IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO  THE  WARRANTIES OF MERCHANTABILITY, 
FITNESS  FOR  A  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM,  DAMAGES OR OTHER 
LIABILITY,  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN  THE 
SOFTWARE. 
*/

#include "MDataX.h"

#define RADIX_216 216

// Initialize time period hierarchy
static struct stTimePeriodHierarchy TimePeriodHierarchy[] = {
   {  MAX_INT32, "%u",       "%u"        },
   {  MAX_INT32, "-%u",      "-%02u"     },
   {   24*60*60, "%uth",     "-%02u"     },     // one file per day
   {     60*60, "utc%02uh", "_utc%02uh" },      // one file per hour
   {        60, "%02um",    "%02um"   },     // one file per minute
   {        1, "%02us",    "%02us"    },     // one file per second
   {           0, "",         "(%u)"      },    // one file for each data chunk
};
// Initialize months table
char* Months[] = {"0","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};


//////////////////////////////////////////////////////////////////////
// MDataX class.
//////////////////////////////////////////////////////////////////////
MDataX::MDataX(int8* szIdentifier,int8* szFileName,int8* szDataXroot)
{
   // External interface initialization
#ifdef WIN32
   DataXroot   = "X:";
    Delimiter   = "\\";
    InitialDir  = "";
#else  //Unix
   DataXroot   = "/home/DataX";
    Delimiter   = "/";
#endif
   DeviceDir   = DataXroot;
   DeviceDir  += Delimiter;
   DeviceDir  += szIdentifier;
   Identifier  = szIdentifier;
   FileName = szFileName;
   FilePeriod  = 60*60;       // data of one hour goes into one file
   Parameters  = "parameters.txt";  // the content of this file will be included after the header line of the DataX file
   LogFile     = "DataXlog.txt"; // default log file for a DataX server
   // Internal initialization
   data_fd     = 0;           // indicate that no data file is open
   TimeIndex   = 0;
   FileIndex   = 0;
    // Initial dir
    char curdir[MAXPATHLEN];
    GETCWD(curdir,MAXPATHLEN);
    InitialDir += curdir;

   // Generate and fill conversion table
   BinXconvert = new uint16[32768];
   for (int i=0;i<32768;i++){
      *(BinXconvert+i)=256*bcode(i/216)+bcode(i%216);
   }
}

MDataX::~MDataX()
{
   // Close data file if still open
   if (0!=data_fd){
      close(data_fd);
   }
}

void MDataX::unitTest()
{
#define BIN_LEN   1421507
#define STR_LEN   30
   uint8* pField  = new uint8[BIN_LEN+1];
   uint8* pField1 = new uint8[BIN_LEN+1];
   uint16* pBinMCL = new uint16[16*BIN_LEN/15];
   MString mBinX;
   MString mBinX1(STR_LEN);
   MString mBinX2(STR_LEN);

   printf("Testing BinX conversion\n");
   for (int i=0;i<BIN_LEN;i++){
      pField[i] = (uint8)(i*43);
   }

   // Convert binary to a BinX string
   bin2BinX(&mBinX,pField,BIN_LEN);
   strncpy(mBinX1.getBuffer(),mBinX.getBuffer(),STR_LEN);
   strncpy(mBinX2.getBuffer(),mBinX.getBuffer()+mBinX.getLength()-STR_LEN,STR_LEN);
   printf("\nBinX (%3d): \"%s.....%s\"\n",mBinX.getLength(),mBinX1.getBuffer(),mBinX2.getBuffer());

   // Convert a BinX string to binary
   BinX2bin(pField1,BIN_LEN,mBinX);

   // Compare
   if (0==memcmp(pField,pField1,BIN_LEN)){
      printf("\nBinX conversion successful for %u bytes.\n\n",BIN_LEN);
   }else{
      printf("BinX conversion ---FAILED---\n\n");
   }

   // Convert binary to a BinMCL string
   bin2BinMCL(BIN_LEN/30,(uint16*)pField,pBinMCL);
   strncpy(mBinX1.getBuffer(),(char*)pBinMCL,STR_LEN);
   printf("\nBinMCL: \"%s\"\n",mBinX1.getBuffer());

   // Convert a BinMCL string to binary
   BinMCL2bin(BIN_LEN/30,pBinMCL,(uint16*)pField1);

   // Compare
   if (0==memcmp(pField,pField1,BIN_LEN-BIN_LEN%2)){
      printf("\nBinMCL conversion successful for %u bytes.\n\n",BIN_LEN);
   }else{
      printf("BinMCL conversion ---FAILED---\n\n");
   }

   // Convert numeric value
   uint64 u64Num = 85;
   uint64 u64Num1;
   uint64 u64Factor =1;
   srand((unsigned)time(NULL));
   for (int i=0;i<9;i++){
      if (i<8){
         u64Num = u64Factor + (rand()*rand())%(u64Factor*RADIX_216);
      }else{
         u64Num += 0xC000000000000000;
      }
      num2BinX(&mBinX,&u64Num);
      u64Num1 = BinX2num(mBinX);
      if (i==7){
         // Extended search for failure
         for (int k=0;k<100000&&u64Num==u64Num1;k++){
            u64Num = u64Factor + (rand()*rand())%(u64Factor*RADIX_216);
            num2BinX(&mBinX,&u64Num);
            u64Num1 = BinX2num(mBinX);
         }
      }
#ifndef WIN32
      printf("Numeric conversion (%20u): %9s len=%u (%20u) %s\n",
         (uint32)u64Num,mBinX.getBuffer(),mBinX.getLength(),(uint32)u64Num1,u64Num==u64Num1?"OK":"failed");
#else
      printf("Numeric conversion (%20I64u): %9s len=%u (%20I64u) %s\n",
         u64Num,mBinX.getBuffer(),mBinX.getLength(),u64Num1,u64Num==u64Num1?"OK":"failed");
#endif
      u64Factor *= RADIX_216;
   }
   MBenchmark   mBenchmark(3);
   try{
      while(mBenchmark.run()){
         bin2BinX(&mBinX,pField,BIN_LEN);
      }
      printf("\n bin2BinX   / Byte: %s\n",mBenchmark.info(850,BIN_LEN).getText());
   }catch(MException E){
      throw MException(E.getMessage(),"Dump");
   }
   try{
      while(mBenchmark.run()){
         bin2BinMCL(BIN_LEN/30,(uint16*)pField,pBinMCL);
      }
      printf("\n bin2BinMCL / Byte: %s\n",mBenchmark.info(850,BIN_LEN).getText());
   }catch(MException E){
      throw MException(E.getMessage(),"Dump");
   }
   return;
}

//--------------------------------------------------
// DataX bin code, byt<=215

unsigned char MDataX::bcode(unsigned char byt)
{
unsigned char b;
switch (byt)
  {
  case 12: b=248;break;      // avoid ,
  case 13: b=249;break;      // avoid -
  case 26: b=250;break;      // avoid :
  case 27: b=251;break;      // avoid ;
  case 29: b=252;break;      // avoid =
  case 32: b=253;break;      // avoid @
  case 64: b=254;break;      // avoid `
  case 95: b=255;break;      // avoid del
  default: b=byt+32;
  }
return b;
}

//--------------------------------------------------
// DataX bin decode

unsigned char MDataX::bdecode(unsigned char byt)
{
unsigned char b;
switch (byt)
  {
  case 248: b=12;break;      // avoid ,
  case 249: b=13;break;      // avoid -
  case 250: b=26;break;      // avoid :
  case 251: b=27;break;      // avoid ;
  case 252: b=29;break;      // avoid =
  case 253: b=32;break;      // avoid @
  case 254: b=64;break;      // avoid `
  case 255: b=95;break;      // avoid del
  default: b=byt-32;
  }
return b;
}

//--------------------------------------------------
// uuu_packer_w converts n groups of 15 words into 32n DataX legal chars

void MDataX::bin2BinMCL(int n, uint16 *inp, uint16 *out)
{
   union shifter {
   uint16 wrd[2];       //[0]=lsw [1]=msw
   unsigned long lwr;
   } shf;

   int i;

   for (i=0;i<n;i++){                           //30270 = 32768*15/16
      shf.lwr = 0;

      shf.wrd[0]=*inp++;                        //get input word
      shf.lwr=shf.lwr<<1;                       //save msb
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1)); //table lookup and store two chars

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      shf.wrd[0]=*inp++;
      shf.lwr=shf.lwr<<1;
      *out++=*(uint16*)(BinXconvert+(shf.wrd[0]>>1));

      *out++=*(uint16*)(BinXconvert+shf.wrd[1]);                   //store the word made of msb's

   }
}

//------------------------------------------
//unpacks n 32 byte groups into n 15 word groups

void MDataX::BinMCL2bin(int n, uint16 *inp, uint16 *out)
{
uint16 w,wb[15];

for (int i=0;i<n;i++)
  {
  for (int j=0;j<15;j++)
        {
        w=*inp++;
        wb[j]=216*bdecode(w/256)+bdecode(w%256);
        }
        w=*inp++;
        w=216*bdecode(w/256)+bdecode(w%256);
        w=w<<1;
  for (int j=0;j<15;j++)
        {
        *out++=(wb[j])+(w&32768);
        w=w<<1;
        }
  }
}

MString* MDataX::num2BinX(MString* pBinX,uint64 u64Num)
{
   return num2BinX(pBinX,&u64Num);
}

MString* MDataX::num2BinX(MString* pBinX,uint64* p64Num)
{
   uint64 u64Num  = *p64Num;
   int32  nDigits;
   if (0 == (u64Num & 0xFFFFFFFF80000000)){
      for (nDigits=1;u64Num >= RADIX_216;nDigits++){
         u64Num /= RADIX_216;
      }
   }else if (0 == (u64Num & 0xC000000000000000)){
      u64Num >>= 31;
      for (nDigits=5;u64Num >= RADIX_216;nDigits++){
         u64Num /= RADIX_216;
      }
   }else{
      nDigits = 9;
   }
   return bin2BinX(pBinX,(uint8*)p64Num,8,nDigits);
}

uint64   MDataX::BinX2num(MString& mBinX)
{
   uint64 u64Num;
   BinX2bin((uint8*)&u64Num,8,mBinX,true);
   return u64Num;
}

MString* MDataX::bin2BinX(MString* pBinX,uint8* pBuf,int32 nBinLen,int32 nDigits)
{
   int64 n64Bits  = 8*nBinLen;
   int32 nBinXLen = nDigits>0?nDigits:(int32)(4*((n64Bits)/31)+((n64Bits)%31)/8+((n64Bits)%31>0?1:0));
   int64 n64Used;
   int32 nShift;
   int32 nPos = 0;
   int64 n64Factor;
   uint8 uchCode;
   uint32 uQuadrupel;
   uint64 u64Register;

   // Determine length of the resulting BinX string
   pBinX->setLength(nBinXLen);

   // Split input bit field into 31-bit chunks
   for (n64Used=0;n64Used<n64Bits;n64Used+=31){
      //Load register
      if (n64Used/8+8<=nBinLen){
         u64Register = *((uint64*)(pBuf+(int32)(n64Used/8)));
      }else{
         u64Register = 0;
         n64Factor   = 1;
         for (int i=(int32)(n64Used/8);i<nBinLen;i++){
            u64Register += *(pBuf+i) * n64Factor;
            n64Factor   *= 256;
         }
      }
      // Bit shift
      nShift = (int32)(n64Used%8);
      if (0==nShift){
         uQuadrupel = (uint32)u64Register;
      }else{
         u64Register >>= nShift;
         uQuadrupel = (uint32)u64Register;
      }
      uQuadrupel &= 0x7FFFFFFF;
      // Code generation
      for (int i=0;i<4 && nPos<nBinXLen;i++){
         uchCode = uQuadrupel%RADIX_216;
         uQuadrupel /= RADIX_216;
         uchCode = *(uint8*)(BinXconvert+uchCode);
         pBinX->setAt(nPos++,uchCode);
      }
   }
   return pBinX;
}

int32 MDataX::BinX2bin(uint8* pBuf,int32 nBufSize,MString& mBinX,bool fTail)
{
   int32 nBinXLen  = mBinX.getLength();
   int32 nBinXbits = 31*(nBinXLen/4)+((nBinXLen%4)==0?0:8*(nBinXLen%4)-1);
   int32 nBinLen   = nBinXbits/8 + ((fTail==true) && (nBinXbits<64) && (nBinXbits%8>0) ? 1 : 0);
   int32 nPos;
   uint8 uchMerge;
   int64 n64Used = 0;
   int32 nShift;
   int32 nFactor;
   uint8 uchCode;
   bool  fMerge;
   uint64 uQuadrupel;

   // Check if buffer size is sufficient
   if (nBufSize<nBinLen){
      throw MException("Unsufficient binary buffer size",nBufSize);
   }
   memset(pBuf,0,nBufSize);
   for (nPos=0;nPos<nBinXLen;nPos+=4){
      //Decode BinX characters
      uQuadrupel = 0;
      nFactor    = 1;
      for (int i=nPos;i<nPos+4 && i<nBinXLen;i++){
         uchCode = *(mBinX.getBuffer()+i);
         switch (uchCode){
            case 248: uchCode = 12; break;      //avoid 44 - comma
            case 249: uchCode = 13; break;      //avoid 45 - minus sign
            case 250: uchCode = 26; break;      //avoid 58 - colon
            case 251: uchCode = 27; break;      //avoid 59 - semicolon
            case 252: uchCode = 29; break;      //avoid 61 - equal sign
            case 253: uchCode = 32; break;      //avoid  64 - @ sign
            case 254: uchCode = 64; break;      //avoid 96 - back apostroph
            case 255: uchCode = 95; break;      //avoid 127- del
            default:  uchCode -= 32;
         }
         uQuadrupel += uchCode * nFactor;
         nFactor    *= RADIX_216;
      }
      // Generate bit sequence
      nShift = (int32)(n64Used%8);
      fMerge = true;
      if (0==nShift){
         fMerge = false;
      }else{
         uQuadrupel <<= nShift;
      }
      if (true==fMerge){
         uchMerge = *(pBuf+(n64Used/8));
         uQuadrupel += uchMerge;
      }
      // Fill buffer
      if (n64Used/8+4 < nBinLen){
         *(int32*)(pBuf+(n64Used/8)) = (int32)uQuadrupel;
         if (nShift > 1){
            *(pBuf+(n64Used/8)+4) = (int8)(uQuadrupel>>32);
         }
      }else{
         for (int32 i=(int32)(n64Used/8);i<nBinLen;i++){
            *(pBuf+i) = (int8)(uQuadrupel);
            uQuadrupel >>= 8;
         }
      }
      n64Used += 31;
   }

   return nBinLen;
}

void MDataX::enterSubDirectory(struct stTimePeriodHierarchy* pTimePeriodHierarchy,
                        unsigned int* pTimeVal,MString& Prefix,bool fCreate)
{
#define S_MASK  S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH
    // Escape period==0
    if (0==pTimePeriodHierarchy->period){
        char Index[100];
        sprintf(Index,pTimePeriodHierarchy->file_format,FileIndex++);
        Prefix += Index;
        return;
    }
   // Generate subsequent names
    char dir[100];
   char prefix[100];
   sprintf(dir,pTimePeriodHierarchy->dir_format,*pTimeVal);
    // Convert month to string
    if (dir[0]=='-'){
        strcpy(dir,Months[*pTimeVal]);
    }
    // Adjust day numeral
    if (!strcmp(dir,"1th")){
        strcpy(dir,"1st");
    }
    if (!strcmp(dir,"2th")){
        strcpy(dir,"2nd");
    }
    if (!strcmp(dir,"3th")){
        strcpy(dir,"3rd");
    }
    if (!strcmp(dir,"21th")){
        strcpy(dir,"21st");
    }
    if (!strcmp(dir,"22th")){
        strcpy(dir,"22nd");
    }
    if (!strcmp(dir,"23th")){
        strcpy(dir,"23rd");
    }
    if (!strcmp(dir,"31th")){
        strcpy(dir,"31st");
    }
   sprintf(prefix,pTimePeriodHierarchy->file_format,*pTimeVal);
   Prefix += prefix;
   // Enter (create) subsequent directory level
   if (-1==chdir(dir)){
      if (true==fCreate){
         if (-1==MKDIR(dir,S_MASK)){
            throw (MException(strerror(errno)));
         }
         if (-1==chdir(dir)){
            throw (MException(strerror(errno)));
         }
      }else{
         throw MException("Folder does not exist",Prefix);
      }
   }
   // Check if another directory level is needed
   if (FilePeriod<pTimePeriodHierarchy->period){
      enterSubDirectory(pTimePeriodHierarchy+1,pTimeVal+1,Prefix,fCreate);
   }
}

void MDataX::openTimePeriodFile(time_t SampleTime,bool fCreate)
{
   struct tm* pDateTime = gmtime(&SampleTime);
   unsigned int TimeVal[] = {
         pDateTime->tm_year + 1900,
         pDateTime->tm_mon + 1,
         pDateTime->tm_mday,
         pDateTime->tm_hour,
         pDateTime->tm_min,
         pDateTime->tm_sec,
         0
      };
   MString Prefix("");

   // Enter (create) root directory in data storage
   if (-1==chdir(DeviceDir.getText())){
      if (true==fCreate){
         if (-1==MKDIR(DeviceDir.getText(),S_MASK)){
            throw (MException(strerror(errno)));
         }
         if (-1==chdir(DeviceDir.getText())){
            throw (MException(strerror(errno)));
         }
      }else{
         throw MException("Folder does not exist",DeviceDir.getText());
      }
   }
   // Enter (create) subsequent directories in data storage
   enterSubDirectory(TimePeriodHierarchy,TimeVal,Prefix,fCreate);
   // Complete file name
   Prefix += "_";
   Prefix += FileName;
   if (true==fCreate){
      // Open data file for writing
      if (-1==(data_fd = OPEN(Prefix.getText(),O_RDWR|O_APPEND,S_IREAD|S_IWRITE))){
         // Maybe file does not exist so creat it
         if (-1==(data_fd = OPEN(Prefix.getText(),O_RDWR|      // read/write access
                                        O_CREAT,      // create file
                                        S_IREAD|      // user can read
                                        S_IWRITE|     // user can write
                                        S_IRGRP|      // group can read
                                        S_IWGRP))){   // group can write
            throw (MException(strerror(errno)));
         }
         // Generate identification line
         char achTime[20];
         sprintf(achTime,"%u,duration:%u",(uint32)SampleTime,FilePeriod);
         MString mData(Identifier);
         mData += ",";
         mData += achTime;
         mData += "\n";
         // Write identification line to file
         if (-1==write(data_fd,mData.getBuffer(),mData.getLength())){
            throw (MException(strerror(errno)));
         }
         // Append parameter file content
         MString  mParameter(DeviceDir);
         mParameter += Delimiter;
         mParameter += Parameters;
         try{
            mData.read(mParameter.getBuffer());
            // Write parameters section to file
            if (-1==write(data_fd,mData.getBuffer(),mData.getLength())){
               throw (MException(strerror(errno)));
            }
         }catch(...){
         }
      }
   }else{
      // Open data file for reading
      if (-1==(data_fd = OPEN(Prefix.getText(),O_RDONLY,0))){
         throw (MException("File does not exist"));
      }
   }
   // Remember opened file period
    if (FilePeriod>0){
      TimeIndex = SampleTime/FilePeriod;
    }else{
        TimeIndex = 0;
    }
    // Change back to initial dir
   if (-1==chdir(InitialDir.getText())){
      throw (MException(strerror(errno)));
   }
}

void MDataX::writeData(time_t SampleTime, MString& mData)
{
   writeData(SampleTime, mData.getBuffer(), mData.getLength());
}

void MDataX::writeData(time_t SampleTime, char* Data, int DataLen)
{
   // Check time period index
   if (FilePeriod!=0 && TimeIndex==SampleTime/FilePeriod){
      // Check if the data file needs to be opened
      if (0==data_fd){
         openTimePeriodFile(SampleTime,true);
      }
   }else{
      if (0!=data_fd){
         close(data_fd);
      }
      openTimePeriodFile(SampleTime,true);
   }
   // Append data to file
   if (-1==write(data_fd,Data,DataLen)){
      throw (MException(strerror(errno)));
   }
}

void MDataX::readData(time_t SampleTime,MString* pData)
{
   openTimePeriodFile(SampleTime);
   //Determine file size
   struct stat stat_buf;
   uint32 uFileSize;
   if (0==fstat(data_fd,&stat_buf)){
      uFileSize = stat_buf.st_size;
      pData->setLength(uFileSize+1);
      *(pData->getBuffer()+uFileSize) = 0;
   }else{
      throw MException("File status not accessible:");
   }
   // Get data from file into memory
   read(data_fd,pData->getBuffer(),uFileSize);
   close(data_fd);
}

void MDataX::dumpData(time_t SampleTime,MString* pData,MString& mStyle)
{
   uint32   uCount;
   uint64   u64MilliTimeStamp;
   MString  mTimeStamp;
   MString   mFile;
   MString  mPrefix;
   MString  mNum;
   MString  mBin;
   MString  mDataVector;
   MMString mLine(mFile,"\r\n");
   MMString mCollection(mLine,":=");
   MMString mItem(mCollection,",;");
   // Obtain file content
   readData(SampleTime-SampleTime%FilePeriod,&mFile);
   // Generate header
   if (mStyle=="meta"){
      (*pData) += "\nMeta data:\n----------\n";
   }
   // Split file into lines
   mLine.resetPosition();
   while (NULL!=mLine.getNextDelimitedString()){
      // Check for BinXtime 
      if (true==isDataTypeIdentifier(mLine,BINX_TIME)){
         // Proceed to desired timestamp
         for(;;){
            // Obtain next measurement line
            if (NULL!=mLine.getNextDelimitedString()){
               mCollection.resetPosition();
               // Read complete measurement line
               if (NULL!=mCollection.getNextDelimitedString()){
                  mItem.resetPosition();
                  // Read timestamp
                  if (NULL!=mItem.getNextDelimitedString()){
                     // Check for desired timestamp
                     if ((uint32)SampleTime > (uint32)((u64MilliTimeStamp = BinX2num(mItem))/1000)){
                        continue;
                     }
                     // Read data vector
                     if (NULL!=mItem.getNextDelimitedString()){
                        if (mStyle=="binx"){
                           (*pData) += "\nBinX data:\n-----------\n";
                           (*pData) += mItem;
                        }
                        mBin.setLength(mItem.getLength());
                        uCount = BinX2bin((uint8*)mBin.getBuffer(),mBin.getLength(),mItem);
                        if (mStyle=="table"){
                           (*pData) += "\nTable data:\n------------\n";
                        }else if (mStyle=="data"){
                           (*pData) += "\nData:\n-----\n";
                        }
                        for (uint32 i=0;i<uCount;i++){
                           mNum = i;
                           if (mStyle=="table"){
                              mDataVector += mNum;
                              mDataVector += ",";
                              mNum         = (uint32)(*((uint8*)(mBin.getBuffer()+i)));
                              mDataVector += mNum;
                              mDataVector += "\n";
                           }else if (mStyle=="data"){
                              mNum         = (uint32)(*((uint8*)(mBin.getBuffer()+i)));
                              mDataVector += mNum;
                              mDataVector += ",";
                           }
                        }
                        (*pData) += mDataVector;
                        (*pData) += "\nTimestamp: ";
                        mTimeStamp = u64MilliTimeStamp/1000;
                        (*pData) += mTimeStamp;
                        (*pData) += ".";
                        mTimeStamp = u64MilliTimeStamp%1000;
                        (*pData) += mTimeStamp;
                        break;
                     }
                  }
               }
            }
            break;
         }
         return;
      }
      // Meta data
      if (mStyle=="meta"){
         // Adjust path
         mCollection.resetPosition();
         if (NULL!=mCollection.getNextDelimitedString()){
            // Split path into folders
            mPrefix = "";
            mItem.resetPosition();
            while (NULL!=mItem.getNextDelimitedString()){
               (*pData) += mPrefix;
               (*pData) += mItem;
               (*pData) += "\n";
               mPrefix  += "  ";
            }
         }
         // Adjust collection
         if (NULL!=mCollection.getNextDelimitedString()){
            // Split collection into items
            (*pData) += mPrefix;
            mItem.resetPosition();
            while (NULL!=mItem.getNextDelimitedString()){
               (*pData) += " ";
               (*pData) += mItem;
            }
            (*pData) += "\n";
         }
      }
   }
   (*pData) += "\n";
}

bool MDataX::isDataTypeIdentifier(MString& mData,DATAX_DATA_TYPE_IDENTIFIER eDataType)
{
   uint32 uNum = 0;
   // Evaluate first 4 bytes
   if (mData.getLength()>=4){
      for (int i=3;i>=0;i--){
         uNum *= 216;
         uNum += *((uint8*)(mData.getBuffer()+i)) - 32;
      }
      if ((uint32)eDataType==uNum){
         return true;
      }
   }
   return false;
}

void MDataX::logMessage(int8* szHeader,uint32 uNum,int8* szTrailer)
{
   MString mNum;
   mNum = uNum;
   logMessage(szHeader,mNum,szTrailer);
}

void MDataX::logMessage(int8* szHeader,MString& mMsg,int8* szTrailer)
{
   FILE*      pLog;
   MString    mLog;

   mLog  = MDateTime::getTimeUTC();
   mLog += "  ";
   mLog += szHeader;
   mLog += mMsg;
   mLog += szTrailer;

   if (NULL!=(pLog=fopen(LogFile.getText(),"a"))){
      fwrite(mLog.getText(),mLog.getLength(),1,pLog);
      fclose(pLog);
   }
}