UltraScan III
us_time_state.cpp
Go to the documentation of this file.
1 #include "us_time_state.h"
3 #include "us_settings.h"
4 
5 #ifndef _TR_
6 #define _TR_(a) QObject::tr(a)
7 #endif
8 #ifndef DbgLv
9 #define DbgLv(a) if(dbg_level>=a)qDebug()
10 #endif
11 
12 // Constructor of the US_TimeState object
14 {
15  filename = QString( "" );
17  fvers = QString( _TMST_VERS_ );
18  fileo = NULL;
19  filei = NULL;
20  dso = NULL;
21  dsi = NULL;
22  error_msg = QString( "" );
23  wr_open = false;
24  rd_open = false;
25  const_ti = false;
26  ntimes = 0;
27  nvalues = 0;
28  timex = -1;
29  fhdr_size = 0;
30  rec_size = 0;
31  file_size = (qint64)0;
33  cdata = (char*)cwork;
34 
35  keys.clear();
36  fmts.clear();
37  offs.clear();
38 
39  // Determine little-endian flag and integer-size for conversion utilities
40  unsigned char vbuf[ 8 ];
41  int* vint = (int*)vbuf;
42  *vint = 1;
43  lit_endian = ( vbuf[ 0 ] != '\0' ); // Flag if machine is little-endian
44  int_size = sizeof( int ); // Save word size of integer
45 }
46 
47 // Open a file to write TMST data
48 int US_TimeState::open_write_data( QString fpath,
49  double timeinc, double ftime )
50 {
51  int status = 0;
52 
53  filepath = fpath;
54  filename = filepath.section( "/", -1, -1 );
55  fvers = QString( _TMST_VERS_ );
56  time_inc = timeinc;
57  time_first = ftime;
58  const_ti = ( timeinc > 0.0 );
59 
60  fileo = new QFile( filepath );
61 
62  if ( ! fileo->open( QIODevice::WriteOnly ) )
63  {
64  status = 100;
65  set_error( status );
67  return status;
68  }
69 
70  dso = new QDataStream( fileo );
71  wr_open = true;
72  rd_open = false;
73  ntimes = 0;
74  nvalues = 0;
75  rec_size = 0;
76 
77  strncpy( cdata, _TMST_MAGI_, 4 ); // "USTS"
78  strncpy( cdata + 4, _TMST_VERS_, 3 ); // "1.0"
79  cdata[ 5 ] = cdata[ 6 ]; // '1','0'
80  fhdr_size = 6;
81 
82  keys.clear();
83  fmts.clear();
84  offs.clear();
85 
86  // Write out the file header
87  dso->writeRawData( cdata, fhdr_size );
88  file_size = (qint64)fhdr_size;
89 
90  return status;
91 }
92 
93 // Specify key and format of a data field
94 int US_TimeState::set_key( QString key, QString format )
95 {
96  int status = 0;
97 
98  QString fl1 = QString( format ).left( 1 );
99 
100  if ( QString( "FIC" ).indexOf( fl1 ) < 0 )
101  {
102  status = 101;
103  set_error( status );
104  error_msg += format;
105 DbgLv(1) << "TS: key format" << key << format << "stat errm" << status << error_msg;
106  return status;
107  }
108 
109  int flen = QString( format ).mid( 1 ).toInt();
110  int offset = rec_size;
111  keys << key;
112  fmts << format;
113  offs << offset;
114  nvalues++;
115  rec_size = offset + flen;
116 DbgLv(1) << "TS: nval" << nvalues << "key format" << key << format
117  << "offs flen" << offset << flen << "rsize" << rec_size;
118 
119  return status;
120 }
121 
122 // Specify keys and formats for all data fields
123 int US_TimeState::set_keys( QStringList& fkeys, QStringList& ffmts )
124 {
125  int status = 0;
126 
127  nvalues = keys.count();
128 
129  if ( ffmts.count() != nvalues )
130  {
131  nvalues = 0;
132  return set_error( 102 );
133  }
134 
135  for ( int jj = 0; jj < nvalues; jj++ )
136  {
137  status = set_key( fkeys[ jj ], ffmts[ jj ] );
138  if ( status != 0 )
139  break;
140  }
141 
142  return status;
143 }
144 
145 // Specify keys and formats for all data fields
146 int US_TimeState::set_keys( QStringList& fkeys )
147 {
148  QStringList wkeys;
149  QStringList wfmts;
150 
151  for ( int jj = 0; jj < fkeys.count(); jj++ )
152  {
153  QString fkey = fkeys[ jj ];
154  QString ffmt = fkey.section( " ", -1, -1 ).simplified();
155  fkey = fkey.section( " ", 0, -2 ).simplified();
156 
157  wkeys << fkey;
158  wfmts << ffmt;
159  }
160 
161  return set_keys( wkeys, wfmts );
162 }
163 
164 // Set an integer value for the current record
165 int US_TimeState::set_value( QString key, int ivalue )
166 {
167  int status = 0;
168 DbgLv(1) << "DtsF: sival: key ival" << key << ivalue;
169  int rfmt = 0;
170  int rlen = 4;
171  int roff = 0;
172  double dvalue;
173 
174  status = key_parameters( key, &rfmt, &rlen, &roff );
175 
176  if ( status == 0 )
177  {
178  char cwrk[ 256 ];
179  char* cval = (char*)cwrk;
180  int coff = 0;
181 
182  switch( rfmt )
183  {
184  case 0: // I4
185  store_iword( cdata + roff, ivalue );
186  break;
187  case 1: // I2
188  store_hword( cdata + roff, ivalue );
189  break;
190  case 2: // I1
191  sprintf( cval, "%d", ivalue );
192  cwrk[ 0 ] = cwrk[ strlen( cval ) - 1 ] && 15;
193  memcpy ( cdata + roff, cval + coff, 1 );
194  break;
195  case 3: // R4
196  dvalue = (double)ivalue;
197  store_fword( cdata + roff, dvalue );
198  break;
199  case 4: // R8
200  dvalue = (double)ivalue;
201  store_dword( cdata + roff, dvalue );
202  break;
203  case 5: // Cnnn
204  sprintf( cval, "%d", ivalue );
205  coff = 0;
206  memcpy ( cdata + roff, cval + coff, rlen );
207  break;
208  default: // UNKNOWN
209  break;
210  }
211  }
212 
213  return status;
214 }
215 
216 // Set a float value for the current record
217 int US_TimeState::set_value( QString key, double dvalue )
218 {
219  int status = 0;
220 DbgLv(1) << "DtsF: sdval: key dval" << key << dvalue;
221  int rfmt = 0;
222  int rlen = 4;
223  int roff = 0;
224  int jvalue;
225 
226  status = key_parameters( key, &rfmt, &rlen, &roff );
227 
228  if ( status == 0 )
229  {
230  char cwrk[ 256 ];
231  char* cval = (char*)cwrk;
232  int coff = 0;
233 
234  switch( rfmt )
235  {
236  case 0: // I4
237  jvalue = qRound( dvalue );
238  store_iword( cdata + roff, jvalue );
239  break;
240  case 1: // I2
241  jvalue = qRound( dvalue );
242  store_hword( cdata + roff, jvalue );
243  break;
244  case 2: // I1
245  jvalue = qRound( dvalue );
246  sprintf( cval, "%d", jvalue );
247  cwrk[ 0 ] = cwrk[ strlen( cval ) - 1 ] && 15;
248  memcpy ( cdata + roff, cval + coff, 1 );
249  break;
250  case 3: // R4
251  store_fword( cdata + roff, dvalue );
252  break;
253  case 4: // R8
254  store_dword( cdata + roff, dvalue );
255  break;
256  case 5: // Cnnn
257  sprintf( cval, "%f", dvalue );
258  coff = 0;
259  memcpy ( cdata + roff, cval + coff, rlen );
260  break;
261  default: // UNKNOWN
262  break;
263  }
264  }
265 
266  return status;
267 }
268 
269 // Set a character string value for the current record
270 int US_TimeState::set_value( QString key, QString svalue )
271 {
272  int status = 0;
273 DbgLv(1) << "DtsF: ssval: key sval" << key << svalue;
274  int rfmt = 0;
275  int rlen = 4;
276  int roff = 0;
277  int jvalue;
278  double dvalue;
279  QString wstr;
280 
281  status = key_parameters( key, &rfmt, &rlen, &roff );
282 
283  if ( status == 0 )
284  {
285  char cwrk[ 256 ];
286  char* cval = (char*)cwrk;
287  int coff = 0;
288 
289  switch( rfmt )
290  {
291  case 0: // I4
292  jvalue = svalue.toInt();
293  store_iword( cdata + roff, jvalue );
294  break;
295  case 1: // I2
296  jvalue = svalue.toInt();
297  store_hword( cdata + roff, jvalue );
298  break;
299  case 2: // I1
300  jvalue = svalue.toInt();
301  sprintf( cval, "%d", jvalue );
302  cwrk[ 0 ] = cwrk[ strlen( cval ) - 1 ] && 15;
303  memcpy ( cdata + roff, cval + coff, 1 );
304  break;
305  case 3: // R4
306  dvalue = svalue.toDouble();
307  store_fword( cdata + roff, dvalue );
308  break;
309  case 4: // R8
310  dvalue = svalue.toDouble();
311  store_dword( cdata + roff, dvalue );
312  break;
313  case 5: // Cnnn
314  wstr = svalue;
315 
316  if ( svalue.length() < rlen )
317  { // Pad with blanks
318  for ( int jj = svalue.length(); jj < rlen; jj++ )
319  wstr += QString( " " );
320  }
321 
322  memcpy( cdata + roff, wstr.toAscii().constData(), rlen );
323  break;
324  default: // UNKNOWN
325  break;
326  }
327  }
328 
329  return status;
330 }
331 
332 // Flush the current data record (write to file)
334 {
335  int status = 0;
336 
337  dso->writeRawData( cdata, rec_size );
338  timex++;
339  ntimes++;
340  memset( cdata, 0, rec_size );
341  file_size += (qint64)rec_size;
342 
343  return status;
344 }
345 
346 // Close the output data file
348 {
349  int status = 0;
350 
351  fileo->close();
352  fileo = NULL;
353  dso = NULL;
354  wr_open = false;
355 
356  return status;
357 }
358 
359 // Write the definitions (XML) file for the last opened output data file
360 int US_TimeState::write_defs( double timeinc )
361 {
362  int status = 0;
363  time_inc = timeinc < 0.0 ? time_inc : timeinc;
364 
365  QString xfname = QString( filename ).section( ".", 0, -2 ) + ".xml";
366  QString xfpath = QString( filepath ).section( ".", 0, -2 ) + ".xml";
367 
368  QFile xfo( xfpath );
369 
370  if ( !xfo.open( QIODevice::WriteOnly | QIODevice::Text ) )
371  { // Error opening the file for write
372  status = 700;
373  set_error( status );
374  return status;
375  }
376 
377  QXmlStreamWriter xml( &xfo );
378  xml.setAutoFormatting( true );
379  xml.writeStartDocument();
380  xml.writeDTD ( "<!DOCTYPE US_TimeState>" );
381  xml.writeStartElement( "TimeState" ); // <TimeState version=...>
382  xml.writeAttribute ( "version", QString( _TMST_VERS_ ) );
383 
384  xml.writeStartElement( "file" ); // <file time_count=...>
385  xml.writeAttribute ( "time_count", QString::number( ntimes ) );
386  xml.writeAttribute ( "constant_incr", ( const_ti ? "1" : "0" ) );
387  xml.writeAttribute ( "time_increment", QString::number( time_inc ) );
388  xml.writeAttribute ( "first_time", QString::number( time_first ) );
389 
390  for ( int jj = 0; jj < nvalues; jj++ )
391  { // Key,Format for each value field in the records
392  xml.writeStartElement( "value" ); // <value key=...format=.../>
393  xml.writeAttribute ( "key", keys[ jj ] );
394  xml.writeAttribute ( "format", fmts[ jj ] );
395  xml.writeEndElement ( );
396  }
397 
398  xml.writeEndElement ( ); // </file>
399  xml.writeEndElement ( ); // </TimeState>
400  xml.writeEndDocument ( );
401  xfo.close();
402 
403  return status;
404 }
405 
406 // Read in TMST data from a file
407 int US_TimeState::open_read_data( QString fpath )
408 {
409  int status = 0;
410  file_size = (qint64)0;
411  filei = new QFile( filepath );
412 
413  if ( ! filei->open( QIODevice::ReadOnly ) )
414  { // Error opening file for read
415  status = 500;
416  set_error( status );
417  error_msg += filepath;
418  return status;
419  }
420 
421  file_size = filei->size();
422  filepath = fpath;
423  filename = filepath.section( "/", -1, -1 );
424  dsi = new QDataStream( filei );
425  fvers = QString( _TMST_VERS_ );
426 
427  rd_open = true;
428  wr_open = false;
429  fhdr_size = 6;
430 
431  // Read in the file header
432  dsi->readRawData( cdata, fhdr_size );
433 
434  if ( strncmp( cdata, _TMST_MAGI_, 4 ) != 0 )
435  { // Error in magic number (wrong kind of file?)
436  status = 501;
437  set_error( status );
438  error_msg += QString( _TMST_MAGI_ ) + " "
439  + QString( cdata ).left( 4 );
440  return status;
441  }
442 
443  cdata[ 6 ] = cdata[ 4 ];
444  cdata[ 7 ] = '.';
445  cdata[ 8 ] = cdata[ 5 ];
446  cdata[ 9 ] = '\0';
447  if ( strncmp( cdata+6, _TMST_VERS_, 3 ) )
448  { // Error in version of file
449  status = 502;
450  set_error( status );
451  error_msg += fvers + " " + QString( cdata+6 ).left( 3 );
452  return status;
453  }
454 
455  ntimes = 0; // Initialize counts and size
456  nvalues = 0;
457  rec_size = 0;
458 
459  strncpy( cdata, _TMST_MAGI_, 4 ); // "USTS"
460  strncpy( cdata+4, _TMST_VERS_, 3 ); // "1.0"
461  cdata[ 5 ] = cdata[ 6 ]; // '1','0'
462 
463  keys.clear(); // Initialize field attributes
464  fmts.clear();
465  offs.clear();
466  int koff = 0;
467 
468  // Read the associated XML definitions file
469  QString xfname = QString( filename ).section( ".", 0, -2 ) + ".xml";
470  QString xfpath = QString( filepath ).section( ".", 0, -2 ) + ".xml";
471 
472  QFile xfi( xfpath );
473 
474  if ( !xfi.open( QIODevice::ReadOnly | QIODevice::Text ) )
475  { // Error opening xml definitions for read
476  status = 505;
477  set_error( status );
478  return status;
479  }
480 
481  QXmlStreamReader xml( &xfi );
482 
483  while( ! xml.atEnd() )
484  { // Read definition elements
485  QString xname;
486  QString attv;
487  QXmlStreamAttributes attr;
488  xml.readNext();
489 
490  if ( xml.isStartElement() )
491  {
492  xname = xml.name().toString();
493  attr = xml.attributes();
494 
495  if ( xname == "TimeState" )
496  { // Parse file/object version
497  fvers = attr.value( "version" ).toString();
498  }
499 
500  else if ( xname == "file" )
501  { // Parse overall file attributes
502  attv = attr.value( "time_count" ).toString();
503  ntimes = attv.isEmpty() ? ntimes : attv.toInt();
504 
505  attv = attr.value( "constant_incr" ).toString();
506  const_ti = attv.isEmpty() ? const_ti : ( attv == "1" );
507 
508  attv = attr.value( "time_increment" ).toString();
509  time_inc = attv.isEmpty() ? time_inc : attv.toDouble();
510 
511  attv = attr.value( "first_time" ).toString();
512  time_first = attv.isEmpty() ? time_first : attv.toDouble();
513  }
514 
515  else if ( xname == "value" )
516  { // Parse the attributes of a record field
517  QString ky = attr.value( "key" ).toString();
518  QString fm = attr.value( "format" ).toString();
519 
520  keys << ky; // Add key to list
521  fmts << fm; // Add format
522  offs << koff; // Add offset in record
523  koff += fm.mid( 1 ).toInt(); // Bump offset by value length
524 
525  nvalues++; // Bump count of values
526  }
527  } // End: is-start-element
528  } // End: element loop
529 
530  xfi.close();
531 
532  return status;
533 }
534 
535 // Get the count of time value records
537 {
538  return ntimes;
539 }
540 
541 // Get the character and parameters of the time range
542 int US_TimeState::time_range( bool* constti, double* timeinc, double* ftime )
543 {
544  if ( constti != NULL ) *constti = const_ti;
545  if ( timeinc != NULL ) *timeinc = time_inc;
546  if ( ftime != NULL ) *ftime = time_first;
547 
548  return ntimes;
549 }
550 
551 // Get record field keys and formats
552 int US_TimeState::field_keys( QStringList* keysP, QStringList* fmtsP )
553 {
554  if ( keysP != NULL )
555  *keysP = keys;
556 
557  if ( fmtsP != NULL )
558  *fmtsP = fmts;
559 
560  return nvalues;
561 }
562 
563 // Read in the next or a specified data record
564 int US_TimeState::read_record( int rtimex )
565 {
566  int status = 0;
567 
568  if ( timex < 0 )
569  { // If current index indicates still in header, complete header calcs
570  int lstv = nvalues - 1;
571  QString fm = fmts[ lstv ];
572  rec_size = offs[ lstv ] + fm.mid( 1 ).toInt();
573  }
574 
575  if ( rtimex < 0 || ( rtimex - timex ) == 1 )
576  { // Set to next and read it in
577  timex++;
578  dsi->readRawData( cdata, rec_size );
579  }
580 
581  else if ( rtimex > timex )
582  { // Read in records until we have reached the designated one
583  while ( timex < rtimex )
584  {
585  timex++;
586  dsi->readRawData( cdata, rec_size );
587  }
588  }
589 
590  else
591  { // Error if designated time index is less than current
592  status = 510;
593  return set_error( status );
594  }
595 
596  return status;
597 }
598 
599 // Get a time integer value for a given key from the current record
600 int US_TimeState::time_ivalue( const QString key, int* stat )
601 {
602  int ivalue = 0;
603  int rfmt = 0;
604  int rlen = 4;
605  int roff = 0;
606 
607  // Fetch attributes of the specified key; point to its record position
608  int status = key_parameters( key, &rfmt, &rlen, &roff );
609  char* rdata = cdata + roff;
610 
611  if ( status == 0 )
612  {
613  char cwrk[ 256 ];
614  char* cval = (char*)cwrk;
615  char* eval = cval + rlen;
616  double dvalue;
617 
618  switch( rfmt )
619  { // Fetch the value in this key's format; get integer output value
620  case 0: // I4
621  ivalue = iword( rdata );
622  break;
623  case 1: // I2
624  ivalue = hword( rdata );
625  break;
626  case 2: // I1
627  ivalue = (unsigned int)rdata[ 0 ];
628  break;
629  case 3: // R4
630  dvalue = dword( rdata );
631  ivalue = qRound( dvalue );
632  break;
633  case 4: // R8
634  dvalue = d8word( rdata );
635  ivalue = qRound( dvalue );
636  break;
637  case 5: // Cnnn
638  memcpy( cval, rdata, rlen );
639  *eval = '\0';
640  ivalue = QString( cval ).left( rlen ).toInt();
641  break;
642  default: // UNKNOWN
643  break;
644  }
645  }
646 
647  if ( stat != NULL )
648  *stat = status;
649  return ivalue;
650 }
651 
652 // Get a time double value for a given key from the current record
653 double US_TimeState::time_dvalue( const QString key, int* stat )
654 {
655  double dvalue = 0.0;
656  int rfmt = 0;
657  int rlen = 4;
658  int roff = 0;
659 
660  // Fetch attributes of the specified key; point to its record position
661  int status = key_parameters( key, &rfmt, &rlen, &roff );
662  char* rdata = cdata + roff;
663 
664  if ( status == 0 )
665  {
666  char cwrk[ 256 ];
667  char* cval = (char*)cwrk;
668  char* eval = cval + rlen;
669  double ivalue;
670 
671  switch( rfmt )
672  { // Fetch the value in this key's format; get double output value
673  case 0: // I4
674  ivalue = iword( rdata );
675  dvalue = (double)ivalue;
676  break;
677  case 1: // I2
678  ivalue = hword( rdata );
679  dvalue = (double)ivalue;
680  break;
681  case 2: // I1
682  ivalue = (unsigned int)rdata[ 0 ];
683  dvalue = (double)ivalue;
684  break;
685  case 3: // R4
686  dvalue = dword( rdata );
687  break;
688  case 4: // R8
689  dvalue = d8word( rdata );
690  break;
691  case 5: // Cnnn
692  memcpy( cval, rdata, rlen );
693  *eval = '\0';
694  dvalue = QString( cval ).left( rlen ).toDouble();
695  break;
696  default: // UNKNOWN
697  break;
698  }
699  }
700 
701  if ( stat != NULL )
702  *stat = status;
703  return dvalue;
704 }
705 
706 // Get a time string value for a given key from the current record
707 QString US_TimeState::time_svalue( QString key, int* stat )
708 {
709  QString svalue;
710  int ivalue;
711  double dvalue;
712  int rfmt = 0;
713  int rlen = 4;
714  int roff = 0;
715 
716  // Fetch attributes of the specified key; point to its record position
717  int status = key_parameters( key, &rfmt, &rlen, &roff );
718  char* rdata = cdata + roff;
719 
720  if ( status == 0 )
721  {
722  switch( rfmt )
723  { // Fetch the value in this key's format; get string output value
724  case 0: // I4
725  ivalue = iword( rdata );
726  svalue = QString::number( ivalue );
727  break;
728  case 1: // I2
729  ivalue = hword( rdata );
730  svalue = QString::number( ivalue );
731  break;
732  case 2: // I1
733  ivalue = (unsigned int)rdata[ 0 ];
734  svalue = QString::number( ivalue );
735  break;
736  case 3: // R4
737  dvalue = dword( rdata );
738  svalue = QString::number( dvalue );
739  break;
740  case 4: // R8
741  dvalue = d8word( rdata );
742  svalue = QString::number( dvalue );
743  break;
744  case 5: // Cnnn
745  svalue = QString( rdata ).left( rlen );
746  break;
747  default: // UNKNOWN
748  break;
749  }
750  }
751 
752  if ( stat != NULL )
753  *stat = status;
754  return svalue;
755 }
756 
757 // Close the input data file
759 {
760  int status = 0;
761 
762  filei->close(); // Close the input file
763  filei = NULL; // Clear the file pointer
764  dsi = NULL; // Clear the data stream pointer
765  rd_open = false; // Flag a closed file
766 
767  return status;
768 }
769 
770 // Get the error message for a given status
771 QString US_TimeState::error_message( int status )
772 {
773  struct errmap // Error status,message entry
774  {
775  int estat;
776  QString emsg;
777  };
778 
779  const errmap emap[] = // Error status-to-message mappings
780  {
781  { 100, _TR_( "Write-file open error: " ) },
782  { 101, _TR_( "Invalid field format: " ) },
783  { 102, _TR_( "Keys count out of sync." ) },
784  { 700, _TR_( "Write-XML-file open error" ) },
785  { 500, _TR_( "Read-file open error: " ) },
786  { 501, _TR_( "Not the TMST file magic number: " ) },
787  { 502, _TR_( "Incompatible file format version: " ) },
788  { 505, _TR_( "Read-XML-file open error" ) },
789  { 510, _TR_( "Attempt to access previous time record" ) },
790  { 901, _TR_( "Invalid key parameters (key,fmt,len,off): " ) },
791  { 999, _TR_( "UNKNOWN" ) }
792  };
793 
794  const int netypes = sizeof( emap ) / sizeof( emap[ 0 ] );
795  QString errmsg = _TR_( "UNKNOWN" );
796 
797  for ( int ii = 0; ii < netypes; ii++ )
798  { // Find a match to the specified error status code
799  if ( emap[ ii ].estat == status )
800  { // A status code match is found, so get the corresponding message
801  errmsg = emap[ ii ].emsg;
802  break;
803  }
804  }
805 
806 DbgLv(1) << "DtsF: egmsg: status" << status << "message" << errmsg;
807  return errmsg;
808 }
809 
810 // Get the error message for the last error that occurred
812 {
813  return error_msg;
814 }
815 
816 // Static function to create a TMST record in the DB from a local file set
818  const int expID, const QString fpath )
819 {
820  QStringList query;
821  int tmstID = -1;
822  if ( dbP == NULL || fpath.isEmpty() || expID < 1 )
823  return tmstID; // Invalid arguments
824 
825  QString tmst_fpath = fpath;
826  QString defs_fpath = QString( fpath ).section( ".", 0, -2 ) + ".xml";
827 
828  QFile dfi( tmst_fpath );
829  QFile xfi( defs_fpath );
830  if ( !dfi.exists() || !xfi.exists() )
831  return -2; // Local files do not both exist
832 
833  if ( ! xfi.open( QIODevice::ReadOnly ) )
834  return -3; // Cannot open definitions file
835 
836  // Get attributes of the local files and test for existing DB record
837  QString tmst_cksm = US_Util::md5sum_file( tmst_fpath );
838  QString defs_cksm = US_Util::md5sum_file( defs_fpath );
839  QString tmst_fname = QString( fpath ).section( "/", -1, -1 );
841 DbgLv(1) << "dbCreate: dbP fn ck" << dbP << tmst_fname << tmst_cksm;
842 
843  QByteArray defs_da = xfi.readAll();
844  xfi.close();
845  QString idTmst = QString::number( tmstID );
846  QString idExp = QString::number( expID );
847  QString fnamedb;
848  QString xdefs;
849  QString cksumdb;
850  QByteArray defs_ld;
851  tmstID = 0;
852  int expIDdb = expID;
853 
854  int stat = dbExamine( dbP, &tmstID, &expIDdb, &fnamedb,
855  &xdefs, &cksumdb );
856 DbgLv(1) << "dbCreate: dbExam stat" << stat << "tmstID expID"
857  << tmstID << expIDdb << "cksumdb" << cksumdb;
858  if ( stat == US_DB2::OK )
859  { // We have an already existing DB record, so check how it matches local
860  if ( cksumdb == tmst_cksm )
861  { // Binary data appears to match, so check xml defs
862  QByteArray defsdb = xdefs.toUtf8();
863  QString cksumxd = QString( QCryptographicHash::hash(
864  defsdb, QCryptographicHash::Md5 ).toHex() )
865  + " " + QString::number( defsdb.size() );
866 
867  if ( tmstID > 0 && cksumxd == defs_cksm )
868  { // Xml definitions characters also match, return with existing DB
869  if ( fnamedb != tmst_fname )
870  { // If the file name differs, we must update the record
871  dbP->mysqlEscapeString( defs_ld, defs_da, defs_da.size() );
872  query.clear();
873  query << "update_timestate" << idTmst << idExp
874  << tmst_fname << defs_ld;
875  dbP->statusQuery( query );
876  }
877 
878  return tmstID;
879  }
880  }
881 
882  // Not a complete match, so we must delete the DB record and re-create it
883  if ( tmstID > 0 )
884  {
885  dbDelete( dbP, tmstID );
886  }
887  }
888 
889  // Escape definitions xml string, then create a new DB record
890  dbP->mysqlEscapeString( defs_ld, defs_da, defs_da.size() );
891  query.clear();
892  query << "new_timestate" << idExp << tmst_fname << defs_ld;
893  stat = dbP->statusQuery( query );
894  int nsrtID = dbP->lastInsertID();
895  tmstID = ( stat == US_DB2::OK || stat == US_DB2::NOROWS )
896  ? nsrtID : -4;
897 DbgLv(1) << "dbCreate: new_timestate status" << stat
898  << "idExp tmstID nsrtID dsiz" << idExp << tmstID << nsrtID << defs_da.size();
899 
900  return tmstID;
901 }
902 
903 // Static function to delete a TMST record from the DB
904 int US_TimeState::dbDelete( US_DB2* dbP, const int tmstID )
905 {
907 DbgLv(1) << "dbDelete: dbP tmstID" << dbP << tmstID;
908  QStringList query;
909  query << "delete_timestate" << QString::number( tmstID );
910  int status = dbP->statusQuery( query );
911 
912  return status;
913 }
914 
915 // Static function to examine a TMST record from the DB
916 int US_TimeState::dbExamine( US_DB2* dbP, int* tmstIdP, int* expIdP,
917  QString* fnameP, QString* xdefsP, QString* cksumP, QDateTime* lastupdP )
918 {
920 DbgLv(1) << "dbExamine: dbP tmstID expID fname xdefs cksum lastupd"
921  << dbP << tmstIdP << expIdP << fnameP << xdefsP << cksumP << lastupdP;
922  QStringList query;
923  int tmstID = ( tmstIdP == NULL ) ? 0 : *tmstIdP;
924  int expID = ( expIdP == NULL ) ? 0 : *expIdP;
925  int status = US_DB2::OK;
926 DbgLv(1) << "dbExamine: tmstID expID" << tmstID << expID;
927 
928  if ( tmstID > 0 )
929  { // TimeState ID is given, get experimentID; then fall thru for the rest
930  query.clear();
931  query << "get_timestate" << QString::number( tmstID );
932  dbP->query( query );
933  status = dbP->lastErrno();
934 DbgLv(1) << "dbExamine: get_timestate tmstID" << tmstID << "status" << status;
935  if ( status != US_DB2::OK )
936  return status;
937 
938  int nrows = dbP->numRows();
939  dbP->next();
940  expID = dbP->value( 0 ).toString().toInt();
941 DbgLv(1) << "dbExamine: get_timestate expID" << expID << "nrows" << nrows;
942  if ( expIdP != NULL )
943  *expIdP = expID;
944  }
945 
946  else if ( expID > 0 )
947  { // Experiment ID is given, get timestateID; then fall thru for the rest
948 //query.clear();
949 //query << "get_experiment2_timestate" << QString::number( expID );
950 //dbP->query( query );
951 //status=dbP->lastErrno();
952 //DbgLv(1) << "dbExamine: get_experiment2_timestate expID status" << expID << status;
953  query.clear();
954  query << "get_experiment_timestate" << QString::number( expID );
955  dbP->query( query );
956  status = dbP->lastErrno();
957 DbgLv(1) << "dbExamine: get_experiment_timestate expID status" << expID << status;
958  if ( status != US_DB2::OK )
959  return status;
960 
961  int nrows = dbP->numRows();
962 DbgLv(1) << "dbExamine: get_experiment_timestate nrows" << nrows;
963 // dbP->next();
964 bool havenx=dbP->next();
965  status = dbP->lastErrno();
966 DbgLv(1) << "dbExamine: get_experiment_timestate next status" << status
967  << "have_next" << havenx;
968  tmstID = dbP->value( 0 ).toString().toInt();
969 DbgLv(1) << "dbExamine: get_experiment_timestate tmstID" << tmstID
970  << dbP->value(0).toString() << dbP->value(1).toString();
971  if ( tmstIdP != NULL )
972  *tmstIdP = tmstID;
973  }
974 
975  else
976  { // Neither tmstID nor expID given
977  return US_DB2::NO_EXPERIMENT;
978  }
979 
980  if ( fnameP != NULL ) // Return file name if requested
981  *fnameP = dbP->value( 1 ).toString();
982 
983  if ( xdefsP != NULL ) // Return xml definitions string if requested
984  *xdefsP = dbP->value( 2 ).toString();
985 
986  if ( cksumP != NULL ) // Return cksum+size string if requested
987  *cksumP = dbP->value( 3 ).toString() + " " +
988  dbP->value( 4 ).toString();
989 if(cksumP!=NULL)
990 DbgLv(1) << "dbExamine: cksum-db" << *cksumP;
991 
992  if ( lastupdP != NULL ) // Return last-updated datetime if requested
993  *lastupdP = QDateTime::fromString( dbP->value( 5 ).toString(),
994  Qt::ISODate ).toUTC();
995 
996  return status;
997 }
998 
999 // Static function to download a TMST binary data record from the DB
1000 int US_TimeState::dbDownload( US_DB2* dbP, const int tmstID,
1001  const QString fpath )
1002 {
1004 DbgLv(1) << "dbDownload: dbP tmstID fpath" << dbP << tmstID << fpath;
1005  int status = dbP->readBlobFromDB( fpath, QString( "download_timestate" ),
1006  tmstID );
1007  return status;
1008 }
1009 
1010 // Static function to upload a TMST binary data record to the DB
1011 int US_TimeState::dbUpload( US_DB2* dbP, const int tmstID,
1012  const QString fpath )
1013 {
1015 DbgLv(1) << "dbUpload: dbP tmstID fpath" << dbP << tmstID << fpath;
1016  int status = dbP->writeBlobToDB( fpath, QString( "upload_timestate" ),
1017  tmstID );
1018  return status;
1019 }
1020 
1021 
1023 // Private slots. Internal utilities like fetch/store half,full,float
1025 
1026 // Utility to extract an unsigned half-word (I2) from a data byte array.
1027 int US_TimeState::uhword( char* dba )
1028 {
1029  unsigned char* cbuf = (unsigned char*)dba;
1030  int j0 = (int)cbuf[ 0 ] & 255;
1031  int j1 = (int)cbuf[ 1 ] & 255;
1032  return ( ( j0 << 8 ) | j1 );
1033 }
1034 
1035 // Utility to extract a signed half-word (I2) from a data byte array.
1036 int US_TimeState::hword( char* dba )
1037 {
1038  int iword = uhword( dba );
1039  return ( ( iword < 32768 ) ? iword : ( iword - 65536 ) );
1040 }
1041 
1042 // Utility to extract a full-word (I4) from a data byte array.
1043 int US_TimeState::iword( char* cbuf )
1044 {
1045  int j0 = uhword( cbuf );
1046  int j1 = uhword( cbuf + 2 );
1047  return ( ( j0 << 16 ) | j1 );
1048 }
1049 
1050 // Utility to extract a float (F4) from a data byte array.
1051 float US_TimeState::fword( char* cbuf )
1052 {
1053  int ival = iword( cbuf );
1054  int* iptr = &ival;
1055  float* fptr = (float*)iptr;
1056  return *fptr;
1057 }
1058 
1059 // Utility to extract a double (F4) from a data byte array.
1060 double US_TimeState::dword( char* cbuf )
1061 {
1062  float fval = fword( cbuf );
1063  return (double)fval;
1064 }
1065 
1066 // Utility to extract a double (F8) from a data byte array.
1067 double US_TimeState::d8word( char* cbuf )
1068 {
1069  int iwork[ 2 ];
1070  double *dptr = (double*)iwork;
1071  int ival = iword( cbuf );
1072  int hx = lit_endian ? 1 : 0;
1073  int lx = 1 - hx;
1074  int ratio = sizeof( double ) / int_size;
1075  if ( ratio == 2 )
1076  {
1077  iwork[ hx ] = ival;
1078  iwork[ lx ] = iword( cbuf + 4 );
1079  }
1080  else
1081  iwork[ 0 ] = ival;
1082 
1083  return *dptr;
1084 }
1085 
1086 // Utility to store an integer as a half-word (I2) in a data byte array.
1087 void US_TimeState::store_hword( char* dba, int ival )
1088 {
1089  unsigned char* cbuf = (unsigned char*)dba;
1090  unsigned char vbuf[ 8 ];
1091  int* vint = (int*)vbuf;
1092  *vint = ival;
1093 
1094  if ( lit_endian ) // Test machine byte order
1095  { // Expected little-endian: lo,hi order
1096  cbuf[ 0 ] = vbuf[ 1 ];
1097  cbuf[ 1 ] = vbuf[ 0 ];
1098  }
1099 
1100  else
1101  { // Surprise! big-endian: hi,lo order
1102  cbuf[ 0 ] = vbuf[ int_size - 2 ]; // 2
1103  cbuf[ 1 ] = vbuf[ int_size - 1 ]; // 3
1104  }
1105 }
1106 
1107 // Utility to store an integer as a full-word (I4) in a data byte array.
1108 void US_TimeState::store_iword( char* dba, int ival )
1109 {
1110  unsigned char* cbuf = (unsigned char*)dba;
1111  unsigned char vbuf[ 8 ];
1112  int* vint = (int*)vbuf;
1113  *vint = ival;
1114 
1115  if ( lit_endian ) // Test machine byte order
1116  { // Expected little-endian: lo,hi order
1117  cbuf[ 0 ] = vbuf[ 3 ];
1118  cbuf[ 1 ] = vbuf[ 2 ];
1119  cbuf[ 2 ] = vbuf[ 1 ];
1120  cbuf[ 3 ] = vbuf[ 0 ];
1121  }
1122 
1123  else
1124  { // Surprise! big-endian: hi,lo order
1125  cbuf[ 0 ] = vbuf[ int_size - 4 ]; // 0
1126  cbuf[ 1 ] = vbuf[ int_size - 3 ]; // 1
1127  cbuf[ 2 ] = vbuf[ int_size - 2 ]; // 2
1128  cbuf[ 3 ] = vbuf[ int_size - 1 ]; // 3
1129  }
1130 }
1131 
1132 // Utility to store a double as a float (F4) in a data byte array.
1133 void US_TimeState::store_fword( char* dba, double dval )
1134 {
1135  float fwrk = (float)dval;
1136  float* fptr = (float*)&fwrk;
1137  int* iptr = (int*)fptr;
1138 
1139  store_iword( dba, *iptr );
1140 }
1141 
1142 // Utility to store a double as a double (F8) in a data byte array.
1143 void US_TimeState::store_dword( char* dba, double dval )
1144 {
1145  double dwrk = dval;
1146  int* iptr = (int*)&dwrk;
1147 
1148  store_iword( dba, *iptr );
1149  int hx = lit_endian ? 1 : 0;
1150  int lx = 1 - hx;
1151  int ratio = sizeof( double ) / int_size;
1152  if ( ratio == 2 )
1153  {
1154  store_iword( dba, *(iptr + hx) );
1155  store_iword( dba + 4, *(iptr + lx) );
1156  }
1157  else
1158  {
1159  store_iword( dba, *iptr );
1160  }
1161 }
1162 
1163 // Utility to store an array of half-word integers
1164 void US_TimeState::store_hwords( char* dba, int* ivals, int nvals )
1165 {
1166  char* cptr = dba;
1167 
1168  for ( int ii = 0; ii < nvals; ii++, cptr+=2 )
1169  {
1170  store_hword( cptr, ivals[ ii ] );
1171  }
1172 }
1173 
1174 // Utility to store an array of full-word integers
1175 void US_TimeState::store_iwords( char* dba, int* ivals, int nvals )
1176 {
1177  char* cptr = dba;
1178 
1179  for ( int ii = 0; ii < nvals; ii++, cptr+=4 )
1180  {
1181  store_iword( cptr, ivals[ ii ] );
1182  }
1183 }
1184 
1185 // Utility to store an array of floats
1186 void US_TimeState::store_fwords( char* dba, double* dvals, int nvals )
1187 {
1188  char* cptr = dba;
1189 
1190  for ( int ii = 0; ii < nvals; ii++, cptr+=4 )
1191  {
1192  store_fword( cptr, dvals[ ii ] );
1193  }
1194 }
1195 
1196 // Utility to set an error status value and its corresponding message
1197 int US_TimeState::set_error( int status )
1198 {
1199  error_msg = error_message( status );
1200  return status;
1201 }
1202 
1203 // Utility to return format,length,offset parameters for a given key
1204 // key = input key string to examine
1205 // rfmtP = optional pointer for format flag (0-5 for I4,I2,I1,F4,F8,Cn)
1206 // rlenP = optional pointer for field length (1,2,4,8 for F,I; 1-127 for C)
1207 // roffP = optional pointer for offset of field in data record
1208 int US_TimeState::key_parameters( const QString key,
1209  int* rfmtP, int* rlenP, int* roffP )
1210 {
1211  int status = 0;
1212 
1213  const QString sfmts( "I4I2I2F4F8C1C2C3C4C5C6C7C8C9" );
1214 
1215  int keyx = keys.indexOf( key ); // Key index
1216  QString fmt = ( keyx >= 0 ) ? fmts[ keyx ] : ""; // Associated format
1217  QString ftst = fmt.left( 2 );
1218  int rfmt = sfmts.indexOf( ftst ) / 2; // Format flag
1219  rfmt = qMin( rfmt, 5 );
1220  int rlen = QString( fmt ).mid( 1 ).toInt(); // Length
1221  int roff = ( keyx >= 0 ) ? offs[ keyx ] : -1;
1222 
1223  if ( rfmt < 0 || rlen < 1 || roff < 0 )
1224  {
1225  status = 901;
1226  set_error( status );
1227  error_msg += key + QString().sprintf( " %d %d %d", rfmt, rlen, roff );
1228  }
1229 
1230  if ( rfmtP != NULL ) *rfmtP = rfmt;
1231  if ( rlenP != NULL ) *rlenP = rlen;
1232  if ( roffP != NULL ) *roffP = roff;
1233 
1234  return status;
1235 }
1236