UltraScan III
us_fit_meniscus.cpp
Go to the documentation of this file.
1 #include <QApplication>
3 
4 #include "us_fit_meniscus.h"
5 #include "us_license_t.h"
6 #include "us_license.h"
7 #include "us_gui_settings.h"
8 #include "us_settings.h"
9 #include "us_db2.h"
10 #include "us_passwd.h"
11 #include "us_investigator.h"
12 #include "us_math2.h"
13 #include "us_matrix.h"
14 #include "us_model.h"
15 #include "us_noise.h"
16 
18 // the class US_FitMeniscus.
19 
20 int main( int argc, char* argv[] )
21 {
22  QApplication application( argc, argv );
23 
24  #include "main1.inc"
25 
26  // License is OK. Start up.
27 
29  w.show();
30  return application.exec();
31 }
32 
34 {
35  setWindowTitle( tr( "Fit Meniscus from 2DSA Data" ) );
36  setPalette( US_GuiSettings::frameColor() );
38 
39  // Main layout
40  QBoxLayout* mainLayout = new QVBoxLayout( this );
41  mainLayout->setSpacing ( 2 );
42  mainLayout->setContentsMargins ( 2, 2, 2, 2 );
43 
44  // Component layouts
45  QHBoxLayout* topLayout = new QHBoxLayout;
46  QHBoxLayout* bottomLayout = new QHBoxLayout;
47  QGridLayout* leftLayout = new QGridLayout;
48  QGridLayout* rightLayout = new QGridLayout;
49  QGridLayout* cntrlsLayout = new QGridLayout;
50 
51  // Lay out the meniscus,rmsd text box
52  te_data = new US_Editor( US_Editor::LOAD, false,
53  "results/*-fm*.fit*dat;;*.dat;;*.*" );
54  connect( te_data, SIGNAL( US_EditorLoadComplete( QString ) ),
55  SLOT ( file_loaded( QString ) ) );
56 
57  QFontMetrics fm( te_data->e->font() );
58 
59  te_data->setMinimumHeight( fm.height() * 20 );
60  te_data->setMinimumWidth ( fm.width( "11 : 6.34567, 0.00567890 " ) );
61  te_data->e->setToolTip( tr( "Loaded, editable meniscus,rmsd table" ) );
62 
63  leftLayout->addWidget( te_data, 0, 0, 20, 1 );
64 
65  // Lay out the plot
66  QBoxLayout* plot = new US_Plot( meniscus_plot,
67  tr( "Meniscus Fit" ),
68  tr( "Radius" ), tr( "2DSA Meniscus RMSD Value" ) );
69 
70  QwtPlotPicker* pick = new US_PlotPicker( meniscus_plot );
71  QwtPlotGrid* grid = us_grid( meniscus_plot );
72  pick->setRubberBand( QwtPicker::VLineRubberBand );
73  //connect( pick, SIGNAL( moved ( const QwtDoublePoint& ) ),
74  // this, SLOT( new_value( const QwtDoublePoint& ) ) );
75  grid->attach( meniscus_plot );
76 
77  meniscus_plot->setMinimumSize( 400, 400 );
78  meniscus_plot->setAxisScale( QwtPlot::xBottom, 5.7, 6.8 );
79  meniscus_plot->setToolTip( tr( "Fitted meniscus,rmsd plot" ) );
80 
81  rightLayout->addLayout( plot, 0, 1, 20, 1 );
82 
83  // Lay out the controls
84  QLabel* lb_status = us_label( tr( "Status:" ) );
85 
86  le_status = us_lineedit( tr( "No data loaded" ), -1, true );
87  le_status->setToolTip(
88  tr( "Results of the last action performed" ) );
89 
90  QLabel* lb_order = us_label( tr( "Fit Order:" ) );
91 
92  sb_order = new QSpinBox();
93  sb_order->setRange( 2, 9 );
94  sb_order->setValue( 2 );
95  sb_order->setPalette( US_GuiSettings::editColor() );
96  sb_order->setToolTip( tr( "Order of fitting curve" ) );
97  connect( sb_order, SIGNAL( valueChanged( int ) ), SLOT( plot_data( int ) ) );
98 
99  QLabel* lb_fit = us_label( tr( "Meniscus selected:" ) );
100 
101  le_fit = us_lineedit( "", -1, false );
102  le_fit->setToolTip(
103  tr( "Selected-minimum/Editable meniscus radius value" ) );
104 
105  QLabel* lb_rms_error = us_label( tr( "RMS Error:" ) );
106 
107  le_rms_error = us_lineedit( "", -1, true );
108  le_rms_error->setToolTip(
109  tr( "RMS error of curve to meniscus,rmsd points" ) );
110 
113  connect( dkdb_cntrls, SIGNAL( changed( bool ) ),
114  this, SLOT( update_disk_db( bool ) ) );
115 
116  pb_update = us_pushbutton( tr( "Update Edit" ) );
117  connect( pb_update, SIGNAL( clicked() ), SLOT( edit_update() ) );
118  pb_update->setEnabled( false );
119  pb_update->setToolTip(
120  tr( "Update edit record with meniscus; remove non-chosen models" ) );
121 
122  pb_scandb = us_pushbutton( tr( "Scan Database" ) );
123  connect( pb_scandb, SIGNAL( clicked() ), SLOT( scan_dbase() ) );
124  pb_scandb->setEnabled( dkdb_cntrls->db() );
125  pb_scandb->setToolTip(
126  tr( "Scan fit-meniscus models in DB; create local table files" ) );
127 
128  us_checkbox( tr( "Confirm Each Update Step" ), ck_confirm, true );
129  us_checkbox( tr( "Apply to All Wavelengths" ), ck_applymwl, true );
130 // ck_applymwl->setVisible( false );
131  ck_confirm ->setToolTip(
132  tr( "Pop up confirmation dialogs at each update step" ) );
133  ck_applymwl->setToolTip(
134  tr( "Apply the meniscus update to all wavelengths of"
135  " the current cell/channel" ) );
136 
137  pb_plot = us_pushbutton( tr( "Plot" ) );
138  connect( pb_plot, SIGNAL( clicked() ), SLOT( plot_data() ) );
139  pb_plot->setToolTip(
140  tr( "Plot,analyze meniscus,rmsd from current text" ) );
141 
142  pb_reset = us_pushbutton( tr( "Reset" ) );
143  connect( pb_reset, SIGNAL( clicked() ), SLOT( reset() ) );
144  pb_reset->setToolTip(
145  tr( "Clear text,plot and various other controls" ) );
146 
147  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
148  connect( pb_help, SIGNAL( clicked() ), SLOT( help() ) );
149  pb_help->setToolTip(
150  tr( "Open a dialog with detailed documentation" ) );
151 
152  QPushButton* pb_accept = us_pushbutton( tr( "Close" ) );
153  connect( pb_accept, SIGNAL( clicked() ), SLOT( close() ) );
154  pb_accept->setToolTip(
155  tr( "Close this dialog and exit the program" ) );
156 
157  // Do detailed layout of the controls
158  int row = 0;
159  cntrlsLayout->addWidget( lb_status, row, 0, 1, 1 );
160  cntrlsLayout->addWidget( le_status, row++, 1, 1, 15 );
161  cntrlsLayout->addWidget( lb_order, row, 0, 1, 2 );
162  cntrlsLayout->addWidget( sb_order, row, 2, 1, 1 );
163  cntrlsLayout->addWidget( lb_fit, row, 3, 1, 5 );
164  cntrlsLayout->addWidget( le_fit, row, 8, 1, 3 );
165  cntrlsLayout->addWidget( lb_rms_error, row, 11, 1, 2 );
166  cntrlsLayout->addWidget( le_rms_error, row++, 13, 1, 3 );
167  cntrlsLayout->addLayout( dkdb_cntrls, row, 0, 1, 6 );
168  cntrlsLayout->addWidget( pb_update, row, 6, 1, 5 );
169  cntrlsLayout->addWidget( pb_scandb, row++, 11, 1, 5 );
170  cntrlsLayout->addWidget( ck_confirm, row, 0, 1, 8 );
171  cntrlsLayout->addWidget( ck_applymwl, row++, 8, 1, 8 );
172  cntrlsLayout->addWidget( pb_plot, row, 0, 1, 4 );
173  cntrlsLayout->addWidget( pb_reset, row, 4, 1, 4 );
174  cntrlsLayout->addWidget( pb_help, row, 8, 1, 4 );
175  cntrlsLayout->addWidget( pb_accept, row, 12, 1, 4 );
176 
177  // Define final layout
178  topLayout ->addLayout( leftLayout );
179  topLayout ->addLayout( rightLayout );
180  topLayout ->setStretchFactor( leftLayout, 1 );
181  topLayout ->setStretchFactor( rightLayout, 2 );
182  bottomLayout->addLayout( cntrlsLayout );
183 
184  mainLayout ->addLayout( topLayout );
185  mainLayout ->addLayout( bottomLayout );
186  mainLayout ->setStretchFactor( topLayout, 2 );
187  mainLayout ->setStretchFactor( bottomLayout, 0 );
188 }
189 
190 // Clear the plot, m-r table text, and other elements
192 {
193  meniscus_plot->clear();
194  meniscus_plot->replot();
195 
196  te_data->e ->setPlainText( "" );
197  sb_order ->setValue( 2 );
198  le_fit ->setText( "" );
199  le_rms_error ->setText( "" );
200 }
201 
202 // Plot the data
204 {
205  plot_data();
206 }
207 
208 // Plot the data
210 {
211  meniscus_plot->clear();
212 
213  QString contents = te_data->e->toPlainText();
214  contents.replace( QRegExp( "[^0-9eE\\.\\n\\+\\-]+" ), " " );
215 
216  QStringList lines = contents.split( "\n", QString::SkipEmptyParts );
217  QStringList parsed;
218 
219  QVector< double > vradi( lines.size() );
220  QVector< double > vrmsd( lines.size() );
221  double* radius_values = vradi.data();
222  double* rmsd_values = vrmsd.data();
223 
224  int count = 0;
225 
226  double minx = 1e20;
227  double maxx = 0.0;
228 
229  double miny = 1e20;
230  double maxy = 0.0;
231 
232  // Remove any non-data lines and put values in arrays
233  for ( int ii = 0; ii < lines.size(); ii++ )
234  {
235  QStringList values = lines[ ii ].split( ' ', QString::SkipEmptyParts );
236 
237  if ( values.size() > 1 )
238  {
239  if ( values.size() > 2 ) values.removeFirst();
240 
241  double radius = values[ 0 ].toDouble();
242  if ( radius < 5.7 || radius > 7.3 ) continue;
243 
244  radius_values[ count ] = radius;
245  rmsd_values [ count ] = values[ 1 ].toDouble();
246 
247  // Find min and max
248  minx = min( minx, radius_values[ count ] );
249  maxx = max( maxx, radius_values[ count ] );
250 
251  miny = min( miny, rmsd_values[ count ] );
252  maxy = max( maxy, rmsd_values[ count ] );
253 
254  // Reformat
255  //parsed << QString::number( radius_values[ count ], 'e', 6 ) + ", " +
256  // QString::number( rmsd_values [ count ], 'e', 6 );
257  parsed << QString().sprintf( "%2d : ", ii + 1 ) +
258  QString::number( radius_values[ count ], 'f', 5 ) + ", " +
259  QString::number( rmsd_values [ count ], 'f', 8 );
260 
261  count++;
262  }
263  }
264 
265  if ( count < 3 ) return;
266 
267  te_data->e->setPlainText( parsed.join( "\n" ) );
268 
269  double overscan = ( maxx - minx ) * 0.10; // 10% overscan
270 
271  meniscus_plot->setAxisScale( QwtPlot::xBottom,
272  minx - overscan, maxx + overscan );
273 
274  // Adjust y axis to scale all the data
275  double dy = fabs( maxy - miny ) / 10.0;
276 
277  meniscus_plot->setAxisScale( QwtPlot::yLeft, miny - dy, maxy + dy );
278 
279  raw_curve = us_curve( meniscus_plot, tr( "Raw Data" ) );
280  raw_curve->setPen( QPen( Qt::yellow ) );
281 
282  raw_curve->setData( radius_values, rmsd_values, count );
283 
284  // Do the fit and get the minimum
285 
286  double c[ 10 ];
287 
288  int order = sb_order->value();
289 
290  if ( ! US_Matrix::lsfit( c, radius_values, rmsd_values, count, order + 1 ) )
291  {
292  QMessageBox::warning( this,
293  tr( "Data Problem" ),
294  tr( "The data is inadequate for this fit order" ) );
295 
296  le_fit ->clear();
297  le_rms_error->clear();
298  meniscus_plot->replot();
299 
300  return;
301  }
302 
303  int fit_count = (int) ( ( maxx - minx + 2 * overscan ) / 0.001 );
304 
305  QVector< double > vfitx( fit_count );
306  QVector< double > vfity( fit_count );
307  double* fit_x = vfitx.data();
308  double* fit_y = vfity.data();
309  double x = minx - overscan;
310  double minimum;
311 
312  for ( int i = 0; i < fit_count; i++, x += 0.001 )
313  {
314  fit_x[ i ] = x;
315  fit_y[ i ] = c[ 0 ];
316 
317  for ( int j = 1; j <= order; j++ )
318  fit_y[ i ] += c[ j ] * pow( x, j );
319  }
320 
321  // Calculate Root Mean Square Error
322  double rms_err = 0.0;
323 
324  for ( int i = 0; i < count; i++ )
325  {
326  double x = radius_values[ i ];
327  double y = rmsd_values [ i ];
328 
329  double y_calc = c[ 0 ];
330 
331  for ( int j = 1; j <= order; j++ )
332  y_calc += c[ j ] * pow( x, j );
333 
334  rms_err += sq ( fabs ( y_calc - y ) );
335  }
336 
337  le_rms_error->setText( QString::number( sqrt( rms_err / count ), 'e', 5 ) );
338 
339  // Find the minimum
340  if ( order == 2 )
341  {
342  // Take the derivitive and get the minimum
343  // c1 + 2 * c2 * x = 0
344  minimum = - c[ 1 ] / ( 2.0 * c[ 2 ] );
345  }
346  else
347  {
348  // Find the zero of the derivitive
349  double dxdy [ 9 ];
350  double d2xdy2[ 8 ];
351 
352  // First take the derivitive
353  for ( int i = 0; i < order; i++ )
354  dxdy[ i ] = c[ i + 1 ] * ( i + 1 );
355 
356  // And we'll need the 2nd derivitive
357  for ( int i = 0; i < order - 1; i++ )
358  d2xdy2[ i ] = dxdy[ i + 1 ] * ( i + 1 );
359 
360  // We'll do a quadratic fit for the initial estimate
361  double q[ 3 ];
362  US_Matrix::lsfit( q, radius_values, rmsd_values, count, 3 );
363  minimum = - q[ 1 ] / ( 2.0 * q[ 2 ] );
364 
365  const double epsilon = 1.0e-4;
366 
367  int k = 0;
368  double f;
369  double f_prime;
370  do
371  {
372  // f is the 1st derivitive
373  f = dxdy[ 0 ];
374  for ( int i = 1; i < order; i++ ) f += dxdy[ i ] * pow( minimum, i );
375 
376  // f_prime is the 2nd derivitive
377  f_prime = d2xdy2[ 0 ];
378  for ( int i = 1; i < order - 1; i++ )
379  f_prime += d2xdy2[ i ] * pow( minimum, i );
380 
381  if ( fabs( f ) < epsilon ) break;
382  if ( k++ > 10 ) break;
383 
384  // Get the next estimate
385  minimum -= f / f_prime;
386 
387  } while ( true );
388  }
389 
390  fit_curve = us_curve( meniscus_plot, tr( "Fitted Data" ) );
391  fit_curve->setPen( QPen( Qt::red ) );
392  fit_curve->setData( fit_x, fit_y, fit_count );
393 
394  // Plot the minimum
395 
396  minimum_curve = us_curve( meniscus_plot, tr( "Minimum Pointer" ) );
397  minimum_curve->setPen( QPen( QBrush( Qt::cyan ), 3.0 ) );
398 
399  double radius_min[ 2 ];
400  double rmsd_min [ 2 ];
401 
402  radius_min[ 0 ] = minimum;
403  radius_min[ 1 ] = minimum;
404 
405  rmsd_min [ 0 ] = miny - 1.0 * dy;
406  rmsd_min [ 1 ] = miny + 2.0 * dy;
407 
408  minimum_curve->setData( radius_min, rmsd_min, 2 );
409 
410  // Put the minimum in the line edit box also
411  le_fit->setText( QString::number( minimum, 'f', 5 ) );
412 
413  // Add the marker label -- bold, font size default + 1, lines 3 pixels wide
414  QPen markerPen( QBrush( Qt::white ), 3.0 );
415  markerPen.setWidth( 3 );
416 
417  QwtPlotMarker* pm = new QwtPlotMarker();
418  QwtText label( QString::number( minimum, 'f', 5 ) );
419  QFont font( pm->label().font() );
420 
421  font.setBold( true );
422  font.setPointSize( font.pointSize() + 1 );
423  label.setFont( font );
424 
425  pm->setValue( minimum, miny + 3.0 * dy );
426  pm->setSymbol( QwtSymbol( QwtSymbol::Cross,
427  QBrush( Qt::white ), markerPen, QSize( 9, 9 ) ) );
428  pm->setLabel( label );
429  pm->setLabelAlignment( Qt::AlignTop );
430 
431  pm->attach( meniscus_plot );
432 
433  meniscus_plot->replot();
434 }
435 
436 // Update an edit file with a new meniscus radius value
438 {
439  QString fn = filedir + "/" + fname_edit;
440  idEdit = 0;
441  QFile filei( fn );
442  QString edtext;
443  QStringList edtexts;
444 
445  if ( ! filei.open( QIODevice::ReadOnly | QIODevice::Text ) )
446  {
447  return;
448  }
449 
450  bool confirm = ck_confirm->isChecked();
451  bool all_wvl = ( nedtfs > 1 && ck_applymwl->isChecked() );
452  bool rmv_mdls = true;
453  bool db_upd = dkdb_cntrls->db();
454  int mlsx = 0;
455  int meqx = 0;
456  int mvsx = 0;
457  int mvcn = 0;
458  int mlnn = 0;
459  int llsx = 0;
460  int leqx = 0;
461  int lvsx = 0;
462  int lvcn = 0;
463  double menv = 0.0;
464  double lefv = 0.0;
465  QString mmsg = "";
466 
467  if ( ! all_wvl )
468  { // Apply to a single triple
469  QTextStream ts( &filei );
470  while ( !ts.atEnd() )
471  edtext += ts.readLine() + "\n";
472  filei.close();
473 
474  menv = le_fit->text().toDouble();
475  mlsx = edtext.indexOf( "<meniscus radius=" );
476  if ( mlsx < 0 ) return;
477  meqx = edtext.indexOf( "=\"", mlsx );
478  mvsx = meqx + 2;
479  mvcn = edtext.indexOf( "\"", mvsx + 1 ) - mvsx;
480  llsx = edtext.indexOf( "<data_range left=" );
481  if ( llsx < 0 ) return;
482  leqx = edtext.indexOf( "=\"", llsx );
483  lvsx = leqx + 2;
484  lvcn = edtext.indexOf( "\"", lvsx + 1 ) - lvsx;
485  lefv = edtext.mid( lvsx, lvcn ).toDouble();
486 DbgLv(1) << " eupd: menv" << menv << "lefv" << lefv;
487 
488  if ( menv >= lefv )
489  {
490  QMessageBox::warning( this, tr( "Meniscus within Data Range" ),
491  tr( "The selected Meniscus value, %1 , extends into the data"
492  " range whose left-side value is %2 . This Edit update"
493  " cannot be performed!" ).arg( menv ).arg( lefv ) );
494  return;
495  }
496 
497  edtext = edtext.replace( mvsx, mvcn, le_fit->text() );
498  mlnn = edtext.indexOf( ">", mlsx ) - mlsx + 1;
499 
500  QFile fileo( fn );
501 
502  if ( ! fileo.open( QIODevice::WriteOnly | QIODevice::Text ) )
503  {
504  return;
505  }
506 
507  QTextStream tso( &fileo );
508  tso << edtext;
509  fileo.close();
510 
511  mmsg = tr( "In file directory\n " ) + filedir + " ,\n" +
512  tr( "file\n " ) + fname_edit + "\n" +
513  tr( "has been modified with the line:\n " ) +
514  edtext.mid( mlsx, mlnn );
515 
516  // If using DB, update the edit record there
517 
518  if ( db_upd )
519  {
520  update_db_edit( edtext, fn, mmsg );
521  }
522 
523  if ( confirm )
524  { // Confirm at each update step
525  mmsg += tr( "\n\nDo you want to remove all fit-meniscus models"
526  " (and associated noises) except for the one"
527  " associated with the nearest meniscus value?" );
528 
529  int response = QMessageBox::question( this, tr( "Edit File Updated" ),
530  mmsg, QMessageBox::Yes, QMessageBox::Cancel );
531  rmv_mdls = ( response == QMessageBox::Yes );
532  }
533  } // END: apply to single triple
534 
535  else
536  { // Apply to all wavelengths in a cell/channel
537  QString dmsg = "";
538  menv = le_fit->text().toDouble();
539 DbgLv(1) << " eupd: AppWvl: nedtfs" << nedtfs;
540  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
541 
542  for ( int jj = 0; jj < nedtfs; jj++ )
543  {
544  QString fn = filedir + "/" + edtfiles.at( jj );
545 DbgLv(1) << " eupd: jj" << jj << "fn" << fn;
546  QFile filei( fn );
547 
548  if ( ! filei.open( QIODevice::ReadOnly | QIODevice::Text ) )
549  continue;
550 
551  QTextStream ts( &filei );
552  edtext = "";
553  while ( !ts.atEnd() )
554  edtext += ts.readLine() + "\n";
555  filei.close();
556 
557  mlsx = edtext.indexOf( "<meniscus radius=" );
558  if ( mlsx < 0 ) continue;
559  meqx = edtext.indexOf( "=\"", mlsx );
560  mvsx = meqx + 2;
561  mvcn = edtext.indexOf( "\"", mvsx + 1 ) - mvsx;
562  llsx = edtext.indexOf( "<data_range left=" );
563  if ( llsx < 0 ) continue;
564  leqx = edtext.indexOf( "=\"", llsx );
565  lvsx = leqx + 2;
566  lvcn = edtext.indexOf( "\"", lvsx + 1 ) - lvsx;
567  lefv = edtext.mid( lvsx, lvcn ).toDouble();
568 
569  if ( menv >= lefv )
570  {
571  QMessageBox::warning( this, tr( "Meniscus within Data Range" ),
572  tr( "The selected Meniscus value, %1 , extends into the data"
573  " range whose left-side value is %2 . This Edit update"
574  " cannot be performed!" ).arg( menv ).arg( lefv ) );
575  continue;
576  }
577 
578  edtext = edtext.replace( mvsx, mvcn, le_fit->text() );
579  mlnn = edtext.indexOf( ">", mlsx ) - mlsx + 1;
580 
581 DbgLv(1) << " eupd: mlsx mlnn" << mlsx << mlnn;
582  QFile fileo( fn );
583 
584  if ( ! fileo.open( QIODevice::WriteOnly | QIODevice::Text ) )
585  continue;
586 
587  QTextStream tso( &fileo );
588  tso << edtext;
589  fileo.close();
590 
591  // If using DB, update the edit record there
592 
593  if ( db_upd )
594  {
595  update_db_edit( edtext, fn, dmsg );
596  }
597 
598  } // END: wavelengths loop
599 
600  QApplication::restoreOverrideCursor();
601  QApplication::restoreOverrideCursor();
602  mmsg = tr( "In file directory\n " ) + filedir + " ,\n" +
603  tr( "file\n " ) + fname_edit + "\n" +
604  tr( "has been modified with the line:\n " ) +
605  edtext.mid( mlsx, mlnn ) +
606  tr( "\n\n%1 other cell/channel files were"
607  " similarly modified." ).arg( nedtfs - 1 ) + dmsg;
608 
609  if ( confirm )
610  { // Confirm at each update step
611  mmsg += tr( "\n\nDo you want to remove all fit-meniscus models"
612  " (and associated noises) except for the one"
613  " associated with the nearest meniscus value?" );
614 
615  int response = QMessageBox::question( this, tr( "Edit File Updated" ),
616  mmsg, QMessageBox::Yes, QMessageBox::Cancel );
617 
618  rmv_mdls = ( response == QMessageBox::Yes );
619  }
620 
621  idEdit = 0;
622  } // END: apply to all wavelengths
623 
624  if ( rmv_mdls )
625  {
626 DbgLv(1) << " call Remove Models";
627  remove_models();
628  }
629 
630  if ( ! confirm )
631  {
632  mmsg += tr( "\n\nAll fit-meniscus models (and associated noises),"
633  " except for the one set associated with the nearest"
634  " meniscus value, were removed." );
635 
636  QMessageBox::information( this, tr( "Edit File Updated" ), mmsg );
637  }
638 }
639 
640 // Slot for handling a loaded file: set the name of loaded,edit files
641 void US_FitMeniscus::file_loaded( QString fn )
642 {
643  filedir = fn.section( "/", 0, -2 );
644  fname_load = fn.section( "/", -1, -1 );
645 
646  QString edittrip = fname_load.section( ".", -3, -3 );
647  QString editID = edittrip.section( "-", 0, -2 ).mid( 1 );
648  QString tripnode = edittrip.section( "-", -1, -1 );
649  QString runID = filedir.section( "/", -1, -1 );
650  QString celchn = tripnode.left( 1 ) + "." +
651  tripnode.mid( 1, 1 );
652  QString tripl = tripnode.left( 1 ) + "." +
653  tripnode.mid( 1, 1 ) + "." +
654  tripnode.mid( 2 );
655  QStringList edtfilt;
656  edtfilt << runID + "." + editID + ".*." + celchn + ".*.xml";
657 
658  fname_edit = "";
659  edtfiles = QDir( filedir ).entryList( edtfilt, QDir::Files, QDir::Name );
660 DbgLv(1) << "EDITFILT" << edtfilt;
661  nedtfs = edtfiles.size();
662 
663  if ( nedtfs > 0 )
664  {
665  for ( int jj = 0; jj < nedtfs; jj++ )
666  {
667  if ( edtfiles.at( jj ).contains( tripl ) )
668  fname_edit = edtfiles.at( jj );
669  }
670  pb_update->setEnabled( true );
671 DbgLv(1) << " nedtfs" << nedtfs << "fname_edit" << fname_edit;
672 DbgLv(1) << " f0" << edtfiles.at(0);
673 if(nedtfs>1) DbgLv(1) << " f1" << edtfiles.at(1);
674  }
675 
676  else
677  { // Could not find edit file, so inform the user
678  pb_update->setEnabled( false );
679 
680  QMessageBox::warning( this,
681  tr( "Missing Local Edit" ),
682  tr( "Update Edit is not possible\n"
683  "without a local copy of the Edit file\n"
684  "corresponding to the FM models.\n"
685  "Use\n Convert Legacy Data\nand\n Manage Data\n"
686  "to create a local copy of an Edit file for\n " )
687  + fname_load + tr( "\nof run\n " ) + runID );
688  }
689 
690  plot_data();
691 
692  le_status->setText( tr( "Data loaded: " ) + runID + "/" + fname_load );
693 }
694 
695 // Scan the database for models to use to write local fit table files
697 {
698  QList< ModelDesc > mDescrs; // List of model description objects
699  US_Passwd pw; // DB password
700  US_DB2 db( pw.getPasswd() ); // DB control
701  QStringList query; // DB query string list
702  QStringList mfnams; // List of FM model fit file names
703  QStringList ufnams; // List of unique model fit file names
704  QStringList uantms; // List of unique model fit analysis times
705  QStringList tmodels; // List: IDs of models with truncated descrs
706  QStringList tedGIDs; // List: edit GUIDs of models w/ trunc descrs
707  QStringList tedIDs; // List: edit IDs of models w/ trunc descrs
708 
709  int nfmods = 0; // Number of fit-meniscus models
710  int nfsets = 0; // Number of fit-meniscus analysis sets
711  int nfrpls = 0; // Number of fit file replacements
712  int nfadds = 0; // Number of fit file additions
713  int nfexss = 0; // Number of fit files left as they existed
714 
715  QString invID = QString::number( US_Settings::us_inv_ID() );
716  QRegExp fmIter = QRegExp( "i\?\?-m*",
717  Qt::CaseSensitive, QRegExp::Wildcard );
718 
719  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
720 
721  // Scan the database and find fit-meniscus models
722 
723  le_status->setText(
724  tr( "Scanning DB fit-meniscus models ..." ) );
725  query << "get_model_desc" << invID;
726  db.query( query );
727 
728  while( db.next() )
729  {
730  ModelDesc mdescr;
731  QString modelID = db.value( 0 ).toString();
732  QString modelGUID = db.value( 1 ).toString();
733  QString descript = db.value( 2 ).toString();
734  QString editGUID = db.value( 5 ).toString();
735  QString editID = db.value( 6 ).toString();
736 
737  if ( descript.length() == 80 )
738  { // Truncated description: save ID and skip update for now
739 //DbgLv(1) << "DbSc: TRUNC: modelID" << modelID;
740  tmodels << modelID;
741  tedGIDs << editGUID;
742  tedIDs << editID;
743  continue;
744  }
745 
746  double variance = db.value( 3 ).toString().toDouble();
747  double meniscus = db.value( 4 ).toString().toDouble();
748  QDateTime lmtime = db.value( 7 ).toDateTime();
749  lmtime.setTimeSpec( Qt::UTC );
750  QString ansysID = descript.section( '.', -2, -2 );
751  QString iterID = ansysID .section( '_', -1, -1 );
752 DbgLv(1) << "DbSc: modelID vari meni" << modelID << variance << meniscus
753  << "ansysID" << ansysID << "iterID" << iterID;
754 
755  if ( ansysID.contains( "2DSA-FM" ) || iterID.contains( fmIter ) )
756  { // Model from meniscus fit, so save information
757 DbgLv(1) << "DbSc: *FIT* " << descript;
758 
759  // Format and save the potential fit table file name
760  QString runID = descript.section( '.', 0, -4 );
761  QString tripleID = descript.section( '.', -3, -3 );
762  QString editLabel = ansysID .section( '_', 0, -5 );
763  QString ftfname = runID + "/2dsa-fm." + editLabel +
764  "-" + tripleID + ".fitmen.dat";
765  mdescr.description = descript;
766  mdescr.baseDescr = runID + "." + tripleID + "."
767  + ansysID.section( "-", 0, 3 );
768  mdescr.fitfname = ftfname;
769  mdescr.modelID = modelID;
770  mdescr.modelGUID = modelGUID;
771  mdescr.editID = editID;
772  mdescr.editGUID = editGUID;
773  mdescr.variance = variance;
774  mdescr.meniscus = meniscus;
775  mdescr.antime = descript.section( '.', -2, -2 )
776  .section( '_', 1, 1 ).mid( 1 );
777  mdescr.lmtime = lmtime;
778 
779  mDescrs << mdescr;
780  }
781  }
782 DbgLv(1) << "DbSc: tmodels size" << tmodels.size() << "ted sizes"
783  << tedGIDs.size() << tedIDs.size();
784 
785  for ( int ii = 0; ii < tmodels.size(); ii++ )
786  { // Review models with truncated descriptions
787  QString modelID = tmodels[ ii ];
788  query.clear();
789  query << "get_model_info" << modelID;
790  db.query( query );
791 
792  if ( db.lastErrno() != US_DB2::OK ) continue;
793 
794  db.next();
795 
796  QString modelGUID = db.value( 0 ).toString();
797  QString descript1 = db.value( 1 ).toString();
798  QString contents = db.value( 2 ).toString();
799  int jdx = contents.indexOf( "description=" );
800 //DbgLv(1) << "DbSc: ii jdtx" << ii << jdtx << "modelID" << modelID
801 // << " dsc1" << descript1 << " cont" << contents.left( 20 );
802 
803  if ( jdx < 1 ) continue;
804 
805 //DbgLv(1) << "DbSc: jdx lend" << jdx << lend;
806  QString descript = contents.mid( jdx ).section( '"', 1, 1 );
807  double variance = db.value( 3 ).toString().toDouble();
808  double meniscus = db.value( 4 ).toString().toDouble();
809  QString editGUID = tedGIDs[ ii ];
810  QString editID = tedIDs [ ii ];
811 
812  QDateTime lmtime = db.value( 6 ).toDateTime();
813  lmtime.setTimeSpec( Qt::UTC );
814  QString ansysID = descript.section( '.', -2, -2 );
815  QString iterID = ansysID .section( '_', -1, -1 );
816 //DbgLv(1) << "DbSc: dscr1" << descript1 << "dcs" << descript;
817 
818  if ( ansysID.contains( "2DSA-FM" ) || iterID.contains( fmIter ) )
819  { // Model from meniscus fit, so save information
820 DbgLv(1) << "DbSc: *FIT* " << descript;
821  ModelDesc mdescr;
822 
823  // Format and save the potential fit table file name
824  QString runID = descript.section( '.', 0, -4 );
825  QString tripleID = descript.section( '.', -3, -3 );
826  QString editLabel = ansysID .section( '_', 0, -5 );
827  QString ftfname = runID + "/2dsa-fm." + editLabel +
828  "-" + tripleID + ".fitmen.dat";
829  mdescr.description = descript;
830  mdescr.baseDescr = runID + "." + tripleID + "."
831  + ansysID.section( "-", 0, 3 );
832  mdescr.fitfname = ftfname;
833  mdescr.modelID = modelID;
834  mdescr.modelGUID = modelGUID;
835  mdescr.editID = editID;
836  mdescr.editGUID = editGUID;
837  mdescr.variance = variance;
838  mdescr.meniscus = meniscus;
839  mdescr.antime = descript.section( '.', -2, -2 )
840  .section( '_', 1, 1 ).mid( 1 );
841  mdescr.lmtime = lmtime;
842 
843  mDescrs << mdescr;
844  }
845  }
846 
847  nfmods = mDescrs.size();
848 DbgLv(1) << "Number of FM models found: " << nfmods;
849 if(nfmods>0) {
850 DbgLv(1) << " pre:D0" << mDescrs[0].description;
851 DbgLv(1) << " pre:Dn" << mDescrs[nfmods-1].description; }
852  qSort( mDescrs );
853 if(nfmods>0) {
854 DbgLv(1) << " sorted:D0" << mDescrs[0].description;
855 DbgLv(1) << " sorted:Dn" << mDescrs[nfmods-1].description; }
856 
857  // Scan local files to see what fit table files already exist
858 
859  le_status->setText(
860  tr( "Comparing to existing local meniscus,rmsd table files ..." ) );
861  mfnams.clear();
862  ufnams.clear();
863  uantms.clear();
864 
865  for ( int ii = 0; ii < nfmods; ii++ )
866  { // Find unique file names in order to create sets
867  QString ftfname = mDescrs[ ii ].fitfname;
868  QString antime = mDescrs[ ii ].antime;
869 
870  if ( ! ufnams.contains( ftfname ) )
871  { // This is a new fit-file name, so new analysis set
872  ufnams << ftfname;
873  uantms << antime;
874  }
875 
876  else if ( ! uantms.contains( antime ) )
877  { // Already seen fit-file, but new analysis time, so duplicate
878  uantms << antime;
879  }
880 
881  mfnams << mDescrs[ ii ].fitfname;
882  }
883 
884  nfsets = ufnams.size();
885  int nantm = uantms.size();
886  int ndupl = nantm - nfsets;
887 DbgLv(1) << "Number of FM analysis sets: " << nfsets;
888 DbgLv(1) << "Number of FM analysis set duplicates: " << ndupl;
889  int kfsets = nfsets;
890  QString rdir = US_Settings::resultDir().replace( "\\", "/" ) + "/";
891  QString fnamesv;
892 
893  for ( int ii = 0; ii < kfsets; ii++ )
894  { // Find out for each set whether a corresponding fit file exists
895  QString ftfname = ufnams.at( ii );
896 
897  if ( mfnams.count( ftfname ) == 1 )
898  { // Not really a set; single fit model after previous fm run
899  nfsets--;
900  continue;
901  }
902 
903  QString ftfpath = rdir + ftfname;
904  QFile ftfile( ftfpath );
905 
906  if ( ftfile.exists() )
907  { // File exists, so we must check the need to replace it
908  QString ftfpath = rdir + ftfname;
909  QDateTime fdate = QFileInfo( ftfile ).lastModified().toUTC();
910  int jj = mfnams.lastIndexOf( ftfname );
911  QDateTime rdate = mDescrs[ jj ].lmtime;
912 DbgLv(1) << " ii rdate fdate" << ii << rdate << fdate << " ftfname"
913  << ftfname << " fdate.msecsTo(rdate)" << fdate.msecsTo(rdate);
914 DbgLv(1) << " jj desc" << jj << mDescrs[jj].description
915  << "antime meniscus" << mDescrs[jj].antime << mDescrs[jj].meniscus;
916 
917  if ( fdate.msecsTo( rdate ) > 0 )
918  { // DB record date is later than file date, so must replace file
919  nfrpls++;
920  ftfile.remove();
921  }
922 
923  else
924  { // DB record date is not later than file date, so leave file as is
925  nfexss++;
926  continue;
927  }
928  }
929 
930  else
931  { // File does not exist, so we definitely need to create it
932  nfadds++;
933  QString ftfpath = QString( rdir + ftfname ).section( "/", 0, -2 );
934  QDir().mkpath( ftfpath );
935  }
936 
937  if ( ! ftfile.open( QIODevice::WriteOnly | QIODevice::Text ) )
938  { // Problem!!!
939  qDebug() << "*ERROR* Unable to open file" << ftfname;
940  continue;
941  }
942 
943  // Creating a new or replacement file: build list of meniscus,rmsd pairs
944  int jfirst = mfnams.indexOf( ftfname );
945  int jlast = mfnams.lastIndexOf( ftfname ) + 1;
946  QString antiml = mDescrs[ jlast - 1 ].antime;
947  QStringList mrpairs;
948 
949 DbgLv(1) << " Creating" << ftfname << "jf,jl" << jfirst << jlast;
950  for ( int jj = jfirst; jj < jlast; jj++ )
951  { // First build the pairs list
952  double meniscus = mDescrs[ jj ].meniscus;
953  double variance = mDescrs[ jj ].variance;
954  double rmsd = sqrt( variance );
955  QString antime = mDescrs[ jj ].antime;
956 DbgLv(1) << " jj desc" << jj << mDescrs[jj].description;
957  if ( antime == antiml )
958  mrpairs << QString::number( meniscus, 'f', 6 ) + " "
959  + QString::number( rmsd, 'e', 6 );
960  }
961 
962  mrpairs.sort();
963  QTextStream ts( &ftfile );
964 
965  // Output the pairs to the file
966  for ( int jj = 0; jj < mrpairs.size(); jj++ )
967  ts << mrpairs.at( jj ) + "\n";
968 
969  ftfile.close();
970 
971  fnamesv = fnamesv.isEmpty() ? ftfname : fnamesv;
972  }
973 
974 DbgLv(1) << "Number of FM REPLACE sets: " << nfrpls;
975 DbgLv(1) << "Number of FM ADD sets: " << nfadds;
976 DbgLv(1) << "Number of FM EXISTING sets: " << nfexss;
977 
978  // Report
979  QString msg = tr( "File" );
980  int nftota = nfadds + nfrpls;
981 
982  if ( nfadds == 1 && nfrpls == 0 )
983  msg += tr( " added: " );
984 
985  else if ( nfadds == 0 && nfrpls == 1 )
986  msg += tr( " updated: " );
987 
988  else if ( nfadds == 0 && nfrpls == 0 )
989  msg = tr( "No new fit files were created." );
990 
991  else
992  msg = tr( "Last of %1 added/updated: " ).arg( nftota );
993 
994  if ( nftota > 0 )
995  msg += fnamesv;
996 
997  le_status->setText( msg );
998  QApplication::restoreOverrideCursor();
999  QApplication::restoreOverrideCursor();
1000 }
1001 
1002 // Reset state of database scan button based on DB/Disk choice
1004 {
1005  pb_scandb->setEnabled( isDB );
1006 }
1007 
1008 // Update the DB edit record with a new meniscus value
1009 void US_FitMeniscus::update_db_edit( QString edtext, QString efilepath,
1010  QString& msg )
1011 {
1012  int elnx = edtext.indexOf( "<editGUID " );
1013  int esvx = edtext.indexOf( "\"", elnx ) + 1;
1014  int nvch = edtext.indexOf( "\"", esvx ) - esvx;
1015  QString edGUID = edtext.mid( esvx, nvch );
1016 DbgLv(1) << "updDbEd: edGUID" << edGUID;
1017 
1018  US_Passwd pw;
1019  US_DB2 db( pw.getPasswd() );
1020  QStringList query;
1021  query << "get_editID" << edGUID;
1022  db.query( query );
1023  db.next();
1024  idEdit = db.value( 0 ).toString().toInt();
1025 DbgLv(1) << "updDbEd: idEdit" << idEdit;
1026  db.writeBlobToDB( efilepath, "upload_editData", idEdit );
1027 
1028  if ( nedtfs == 1 || ! ck_applymwl->isChecked() )
1029  {
1030  msg += tr( "\n\nThe meniscus value was also updated for the"
1031  " corresponding edit record in the database." );
1032  }
1033 
1034  else
1035  {
1036  QString fn = efilepath.section( "/", -1, -1 );
1037  int lstfx = nedtfs - 1;
1038  if ( fn == edtfiles[ lstfx ] )
1039  {
1040  msg += tr( "\n\nThe meniscus value was also updated for the"
1041  " corresponding edit records in the database." );
1042  }
1043  }
1044 
1045  return;
1046 }
1047 
1048 // Remove f-m models (and associated noise) except for the single chosen one
1050 {
1051  QString srchRun = filedir.section( "/", -1, -1 );
1052  QString srchEdit = fname_load.section( ".", -3, -3 );
1053  QString srchTrip = srchEdit.section( "-", -1, -1 );
1054  srchEdit = srchEdit.section( "-", 0, -2 );
1055 DbgLv(1) << "RmvMod: scn1 srchRun srchEdit srchTrip"
1056  << srchRun << srchEdit << srchTrip;
1057 
1058  // Scan models files; get list of fit-meniscus type matching run/edit/triple
1059  QStringList modfilt;
1060  modfilt << "M*.xml";
1061  QString moddir = US_Settings::dataDir() + "/models";
1062  QStringList modfiles = QDir( moddir ).entryList(
1063  modfilt, QDir::Files, QDir::Name );
1064  moddir = moddir + "/";
1065  double cmeniscus = le_fit->text().toDouble();
1066 
1067  QList< ModelDesc > lMDescrs;
1068  QList< ModelDesc > dMDescrs;
1069 
1070  QStringList lmodFnams; // local model full path file names
1071  QStringList lmodGUIDs; // Local model GUIDs
1072  QList< double > lmodVaris; // Local variance values
1073  QList< double > lmodMenis; // Local meniscus values
1074  QStringList lmodDescs; // Local descriptions
1075 
1076  QStringList dmodIDs; // Database model IDs
1077  QStringList dmodGUIDs; // Database model GUIDs
1078  QList< double > dmodVaris; // Database variance values
1079  QList< double > dmodMenis; // Database meniscus values
1080  QStringList dmodDescs; // Database descriptions
1081  int nlmods = 0;
1082  int ndmods = 0;
1083  int nlnois = 0;
1084  int ndnois = 0;
1085  int lArTime = 0;
1086  int dArTime = 0;
1087  int lkModx = -1;
1088  int dkModx = -1;
1089  bool db_upd = dkdb_cntrls->db();
1090 
1091  for ( int ii = 0; ii < modfiles.size(); ii++ )
1092  {
1093  ModelDesc lmodd;
1094  QString modfname = modfiles.at( ii );
1095  QString modpath = moddir + modfname;
1096  US_Model model;
1097 
1098  if ( model.load( modpath ) != US_DB2::OK )
1099  continue; // Can't use if can't load
1100 
1101  QString descript = model.description;
1102  QString runID = descript.section( '.', 0, -4 );
1103  QString tripID = descript.section( '.', -3, -3 );
1104  QString anRunID = descript.section( '.', -2, -2 );
1105  QString editLabl = anRunID .section( '_', 0, -5 );
1106 //DbgLv(1) << "RmvMod: scn1 ii runID editLabl tripID"
1107 // << ii << runID << editLabl << tripID;
1108 
1109  if ( runID != srchRun || editLabl != srchEdit || tripID != srchTrip )
1110  continue; // Can't use if from a different runID or edit or triple
1111 
1112  QString iterID = anRunID .section( '_', -1, -1 );
1113 //DbgLv(1) << "RmvMod: iterID" << iterID;
1114 
1115  if ( iterID.length() != 10 || ! iterID.contains( "-m" ) )
1116  continue; // Can't use if not a fit-meniscus type
1117 
1118  // Probably a file from the right set, but let's check for other sets
1119  int arTime = anRunID .section( '_', -4, -4 ).mid( 1 ).toInt();
1120 DbgLv(1) << "RmvMod: arTime lArTime" << arTime << lArTime;
1121 
1122  if ( arTime > lArTime )
1123  { // If first set or new one younger than previous, start lists
1124  lmodFnams.clear();
1125  lmodGUIDs.clear();
1126  lmodVaris.clear();
1127  lmodMenis.clear();
1128  lmodDescs.clear();
1129  lMDescrs .clear();
1130  lArTime = arTime;
1131  }
1132 
1133  else if ( arTime < lArTime )
1134  { // If new one older than previous, skip it
1135  continue;
1136  }
1137 
1138  lmodFnams << modpath; // Save full path file name
1139  lmodGUIDs << model.modelGUID; // Save model GUID
1140  lmodVaris << model.variance; // Save variance
1141  lmodMenis << model.meniscus; // Save meniscus
1142  lmodDescs << model.description; // Save description
1143 
1144  lmodd.description = model.description;
1145  lmodd.modelGUID = model.modelGUID;
1146  lmodd.modelID = "-1";
1147  lmodd.variance = model.variance;
1148  lmodd.meniscus = model.meniscus;
1149  lMDescrs << lmodd;
1150  }
1151 
1152  nlmods = lMDescrs.size();
1153  qSort( lMDescrs );
1154 DbgLv(1) << "RmvMod: nlmods" << nlmods;
1155  double minMdif = 99e+10;
1156 
1157  for ( int ii = 0; ii < nlmods; ii++ )
1158  { // Scan to identify model in set with lowest variance
1159  ModelDesc lmodd = lMDescrs[ ii ];
1160  double diffMen = qAbs( lmodd.meniscus - cmeniscus );
1161 DbgLv(1) << "low Mdif scan: ii vari meni desc" << ii << lmodd.variance
1162  << lmodd.meniscus << lmodd.description.right( 22 );
1163 
1164  if ( diffMen < minMdif )
1165  {
1166  minMdif = diffMen;
1167  lkModx = ii;
1168 //DbgLv(1) << "low Mdif scan: minMdif lkModx" << minMdif << lkModx;
1169  }
1170  }
1171 DbgLv(1) << "RmvMod: minMdif lkModx" << minMdif << lkModx;
1172  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1173 
1174  // Make a list of f-m models that match for DB, if possible
1175  if ( db_upd )
1176  {
1177  ModelDesc dmodd;
1178  QString invID = QString::number( US_Settings::us_inv_ID() );
1179  QString edtID = QString::number( idEdit );
1180  US_Passwd pw;
1181  US_DB2 db( pw.getPasswd() );
1182  QStringList query;
1183  QStringList modIDs;
1184 
1185  if ( idEdit > 0 )
1186  query << "get_model_desc_by_editID" << invID << edtID;
1187 
1188  else
1189  query << "get_model_desc" << invID;
1190 
1191 DbgLv(1) << "RmvMod: idEdit" << idEdit << "query" << query;
1192  db.query( query );
1193 
1194  while( db.next() )
1195  {
1196  QString modelID = db.value( 0 ).toString();
1197  QString descript = db.value( 2 ).toString();
1198  QString runID = descript.section( '.', 0, 0 );
1199  if ( runID == srchRun )
1200  {
1201  modIDs << modelID;
1202  }
1203  }
1204 
1205  for ( int ii = 0; ii < modIDs.size(); ii++ )
1206  {
1207  QString modelID = modIDs.at( ii );
1208  query.clear();
1209  query << "get_model_info" << modelID;
1210  db.query( query );
1211 
1212  if ( db.lastErrno() != US_DB2::OK ) continue;
1213 
1214  db.next();
1215 
1216  QString modelGUID = db.value( 0 ).toString();
1217  QString descript1 = db.value( 1 ).toString();
1218  QString contents = db.value( 2 ).toString();
1219  int jdtx = contents.indexOf( "description=" );
1220 
1221  if ( jdtx < 1 ) continue;
1222 
1223  int jdx = contents.indexOf( "\"", jdtx ) + 1;
1224  int lend = contents.indexOf( "\"", jdx ) - jdx;
1225  QString descript = contents.mid( jdx, lend );
1226  double variance = db.value( 3 ).toString().toDouble();
1227  double meniscus = db.value( 4 ).toString().toDouble();
1228  QString runID = descript.section( '.', 0, -4 );
1229  QString tripID = descript.section( '.', -3, -3 );
1230  QString anRunID = descript.section( '.', -2, -2 );
1231  QString editLabl = anRunID .section( '_', 0, -5 );
1232 //DbgLv(1) << "RmvMod: scn1 ii runID editLabl tripID"
1233 // << ii << runID << editLabl << tripID;
1234 
1235  if ( runID != srchRun || editLabl != srchEdit
1236  || tripID != srchTrip )
1237  continue; // Can't use if from a different runID or edit or triple
1238 
1239  QString iterID = anRunID .section( '_', -1, -1 );
1240 //DbgLv(1) << "RmvMod: iterID" << iterID;
1241 
1242  if ( iterID.length() != 10 || ! iterID.contains( "-m" ) )
1243  continue; // Can't use if not a fit-meniscus type
1244 
1245  // Probably a file from the right set, but let's check for other sets
1246  int arTime = anRunID .section( '_', -4, -4 ).mid( 1 ).toInt();
1247 
1248  if ( arTime > dArTime )
1249  { // If first set or new one younger than previous, start lists
1250  dmodIDs .clear();
1251  dmodGUIDs.clear();
1252  dmodVaris.clear();
1253  dmodMenis.clear();
1254  dmodDescs.clear();
1255  dMDescrs .clear();
1256  dArTime = arTime;
1257  }
1258 
1259  else if ( arTime < dArTime )
1260  { // If new one older than previous, skip it
1261  continue;
1262  }
1263 
1264  dmodIDs << modelID; // Save model DB ID
1265  dmodGUIDs << modelGUID; // Save model GUID
1266  dmodVaris << variance; // Save variance
1267  dmodMenis << meniscus; // Save meniscus
1268  dmodDescs << descript; // Save description
1269 
1270  dmodd.description = descript;
1271  dmodd.modelGUID = modelGUID;
1272  dmodd.modelID = modelID;
1273  dmodd.variance = variance;
1274  dmodd.meniscus = meniscus;
1275  dMDescrs << dmodd;
1276 DbgLv(1) << "RmvMod: scn2 ii dmodDesc" << descript;
1277  }
1278 
1279  ndmods = dMDescrs.size();
1280  qSort( dMDescrs );
1281 
1282  if ( dArTime > lArTime ) // Don't count any older group
1283  nlmods = 0;
1284  else if ( lArTime > dArTime )
1285  ndmods = 0;
1286 
1287 DbgLv(1) << "RmvMod: ndmods" << ndmods;
1288  double minMdif = 99e+10;
1289 
1290  for ( int ii = 0; ii < ndmods; ii++ )
1291  { // Scan to identify model in set with lowest variance
1292  ModelDesc dmodd = dMDescrs[ ii ];
1293  double diffMen = qAbs( dmodd.meniscus - cmeniscus );
1294 DbgLv(1) << "low Mdif scan: ii vari meni desc" << ii << dmodd.variance
1295  << dmodd.meniscus << dmodd.description.right( 22 );
1296 
1297  if ( diffMen < minMdif )
1298  {
1299  minMdif = diffMen;
1300  dkModx = ii;
1301 DbgLv(1) << "low Mdif scan: minMdif dkModx" << minMdif << dkModx;
1302  }
1303  }
1304 
1305  // Now, compare the findings for local versus database
1306  if ( nlmods == ndmods || ( ndmods > 0 && nlmods == 0 ) )
1307  {
1308  int nmatch = 0;
1309 
1310  for ( int jj = 0; jj < nlmods; jj++ )
1311  {
1312  ModelDesc lmodd = lMDescrs[ jj ];
1313  ModelDesc dmodd = dMDescrs[ jj ];
1314 
1315  if ( lmodd.modelGUID == dmodd.modelGUID &&
1316  lmodd.description == dmodd.description )
1317  nmatch++;
1318 
1319  lmodGUIDs[ jj ] = lmodd.modelGUID;
1320  lmodVaris[ jj ] = lmodd.variance;
1321  lmodMenis[ jj ] = lmodd.meniscus;
1322  lmodDescs[ jj ] = lmodd.description;
1323  }
1324 
1325  for ( int jj = 0; jj < ndmods; jj++ )
1326  {
1327  ModelDesc dmodd = dMDescrs[ jj ];
1328  dmodIDs [ jj ] = dmodd.modelID;
1329  dmodGUIDs[ jj ] = dmodd.modelGUID;
1330  dmodVaris[ jj ] = dmodd.variance;
1331  dmodMenis[ jj ] = dmodd.meniscus;
1332  dmodDescs[ jj ] = dmodd.description;
1333  }
1334 
1335  if ( nmatch == nlmods )
1336  { // OK if they match or local only
1337 DbgLv(1) << "++local/dbase match, or local only";
1338  }
1339 
1340  else
1341  { // Not good if they do not match
1342 DbgLv(1) << "**local/dbase DO NOT MATCH";
1343 DbgLv(1) << " nmatch ndmods nlmods" << nmatch << ndmods << nlmods;
1344  return;
1345  }
1346  }
1347 
1348  else if ( nlmods == 0 )
1349  { // It is OK if there are no local records, when DB ones were found
1350 DbgLv(1) << "++only dbase records exist";
1351  }
1352 
1353  else if ( ndmods == 0 )
1354  { // It is OK if there are only local records, when local ones found
1355 DbgLv(1) << "++only local records exist";
1356  }
1357 
1358  else
1359  { // Non-zero local & DB, but they do not match
1360 DbgLv(1) << "**local/dbase DO NOT MATCH in count";
1361 DbgLv(1) << " nlmods ndmods" << nlmods << ndmods;
1362  return;
1363  }
1364  }
1365 
1366 DbgLv(1) << " nlmods ndmods" << nlmods << ndmods;
1367  if ( ndmods > 0 || nlmods > 0 )
1368  { // There are models to scan, so build a list of models,noises to remove
1369  ModelDesc rmodDescrs;
1370  NoiseDesc rnoiDescrs;
1371  QStringList rmodIDs;
1372  QStringList rmodDescs;
1373  QStringList rmodFnams;
1374  QStringList rnoiIDs;
1375  QStringList rnoiFnams;
1376  QStringList rnoiDescs;
1377  QStringList nieDescs;
1378  QStringList nieIDs;
1379  QStringList nieFnams;
1380  int nlrmod = 0;
1381  int ndrmod = 0;
1382  int nlrnoi = 0;
1383  int ndrnoi = 0;
1384  int ntmods = ( ndmods > 0 ) ? ndmods : nlmods;
1385  int ikModx = ( ndmods > 0 ) ? dkModx : lkModx;
1386 DbgLv(1) << " ntmods ikModx" << ntmods << ikModx;
1387 
1388  QString modDesc = "";
1389 
1390  for ( int jj = 0; jj < ntmods; jj++ )
1391  { // Build the list of model files and DB ids for removal
1392  if ( jj != ikModx )
1393  {
1394  int itix;
1395  int irix;
1396  QString fname;
1397  QString mDesc;
1398  QString mID;
1399  QString tiDesc;
1400  QString riDesc;
1401  QString noiID;
1402  QString noiFname;
1403 
1404  if ( nlmods > 0 )
1405  {
1406  fname = lmodFnams[ jj ];
1407  mDesc = lmodDescs[ jj ];
1408  nlrmod++;
1409  if ( ndmods == 0 )
1410  mID = "-1";
1411  }
1412 
1413  if ( ndmods > 0 )
1414  {
1415  mID = dmodIDs [ jj ];
1416  mDesc = dmodDescs[ jj ];
1417  ndrmod++;
1418  if ( nlmods == 0 )
1419  fname = "";
1420  }
1421  rmodIDs << mID;
1422  rmodFnams << fname;
1423  rmodDescs << mDesc;
1424 
1425  if ( modDesc.isEmpty() )
1426  {
1427  modDesc = mDesc; // Save 1st model's description
1428 DbgLv(1) << "RmvMod: 1st rmv-mod: jj modDesc" << jj << modDesc;
1429  // Build noises-in-edit lists for database and local
1430  noises_in_edit( modDesc, nieDescs, nieIDs, nieFnams );
1431  }
1432 
1433  tiDesc = QString( mDesc ).replace( ".model", ".ti_noise" );
1434  riDesc = QString( mDesc ).replace( ".model", ".ri_noise" );
1435  itix = nieDescs.indexOf( tiDesc );
1436  irix = nieDescs.indexOf( riDesc );
1437 
1438  if ( itix >= 0 )
1439  { // There is a TI noise to remove
1440  noiID = nieIDs [ itix ];
1441  noiFname = nieFnams[ itix ];
1442 
1443  if ( noiID != "-1" )
1444  ndrnoi++;
1445 
1446  if ( ! noiFname.isEmpty() )
1447  nlrnoi++;
1448  else
1449  noiFname = "";
1450 
1451  rnoiIDs << noiID;
1452  rnoiFnams << noiFname;
1453  rnoiDescs << tiDesc;
1454  }
1455 
1456  if ( irix >= 0 )
1457  { // There is an RI noise to remove
1458  noiID = nieIDs [ irix ];
1459  noiFname = nieFnams[ irix ];
1460 
1461  if ( noiID != "-1" )
1462  ndrnoi++;
1463 
1464  if ( ! noiFname.isEmpty() )
1465  nlrnoi++;
1466  else
1467  noiFname = "";
1468 
1469  rnoiIDs << noiID;
1470  rnoiFnams << noiFname;
1471  rnoiDescs << riDesc;
1472  }
1473  }
1474  }
1475  QApplication::restoreOverrideCursor();
1476  QApplication::restoreOverrideCursor();
1477 
1478  nlnois = nlrnoi + ( nlrnoi > nlrmod ? 2 : 1 );
1479  ndnois = ndrnoi + ( ndrnoi > ndrmod ? 2 : 1 );
1480  bool rmv_mdls = true;
1481 DbgLv(1) << "RmvMod: nlrmod ndrmod nlrnoi ndrnoi nlnois ndnois"
1482  << nlrmod << ndrmod << nlrnoi << ndrnoi << nlnois << ndnois;
1483  if ( ck_confirm->isChecked() )
1484  {
1485  QString msg = tr( "%1 local model files;\n"
1486  "%2 database model files;\n"
1487  "%3 local noise files;\n"
1488  "%4 database noise files;\n"
1489  "have been identified for removal.\n\n"
1490  "Do you really want to delete them?" )
1491  .arg( nlrmod ).arg( ndrmod ).arg( nlrnoi ).arg( ndrnoi );
1492 
1493  int response = QMessageBox::question( this,
1494  tr( "Remove Models and Noises?" ),
1495  msg, QMessageBox::Yes, QMessageBox::Cancel );
1496  rmv_mdls = ( response == QMessageBox::Yes );
1497  }
1498 
1499  if ( rmv_mdls )
1500  {
1501  US_Passwd pw;
1502  US_DB2* dbP = db_upd ? new US_DB2( pw.getPasswd() ) : NULL;
1503 
1504  QStringList query;
1505  QString recID;
1506  QString recFname;
1507  QString recDesc;
1508 DbgLv(1) << " Remove Models and Noises";
1509  for ( int ii = 0; ii < rmodIDs.size(); ii++ )
1510  { // Remove models and db noises
1511  recID = rmodIDs [ ii ];
1512  recDesc = rmodDescs[ ii ];
1513  recFname = rmodFnams[ ii ];
1514 DbgLv(1) << " Delete: " << recID << recFname.section("/",-1,-1) << recDesc;
1515 
1516  if ( ! recFname.isEmpty() )
1517  { // Delete local file model
1518  QFile recf( recFname );
1519  if ( recf.exists() )
1520  {
1521  if ( recf.remove() )
1522 { DbgLv(1) << " local file removed"; }
1523  else { qDebug() << "*ERROR* removing" << recFname; }
1524  }
1525  else { qDebug() << "*ERROR* does not exist:" << recFname; }
1526  }
1527 
1528  if ( recID != "-1" && dbP != NULL )
1529  { // Delete model (and any child noise) from DB
1530  query.clear();
1531  query << "delete_model" << recID;
1532  int stat = dbP->statusQuery( query );
1533  if ( stat != 0 )
1534  qDebug() << "delete_model error" << stat;
1535 else DbgLv(1) << " DB record deleted";
1536  }
1537  }
1538 
1539  if ( dbP != NULL )
1540  {
1541  delete dbP;
1542  dbP = NULL;
1543  }
1544 
1545  for ( int ii = 0; ii < rnoiIDs.size(); ii++ )
1546  { // Remove local noises
1547  recID = rnoiIDs [ ii ];
1548  recDesc = rnoiDescs[ ii ];
1549  recFname = rnoiFnams[ ii ];
1550 DbgLv(1) << " Delete: " << recID << recFname.section("/",-1,-1) << recDesc;
1551 
1552  if ( ! recFname.isEmpty() )
1553  { // Delete local file noise
1554  QFile recf( recFname );
1555  if ( recf.exists() )
1556  {
1557  if ( recf.remove() )
1558 { DbgLv(1) << " local file removed"; }
1559  else { qDebug() << "*ERROR* removing" << recFname; }
1560  }
1561  else { qDebug() << "*ERROR* does not exist:" << recFname; }
1562  }
1563 
1564  // No need to remove noises from DB; model remove did that
1565  }
1566  }
1567  }
1568 
1569  else
1570  { // No models were found!!! (huh!!!)
1571 DbgLv(1) << "**NO local/dbase models-to-remove were found!!!!";
1572  }
1573 
1574  QApplication::restoreOverrideCursor();
1575  return;
1576 }
1577 
1578 // Create lists of information for noises that match a sample model from a set
1579 void US_FitMeniscus::noises_in_edit( QString modDesc, QStringList& nieDescs,
1580  QStringList& nieIDs, QStringList& nieFnams )
1581 {
1582  QString msetBase = modDesc.section( ".", 0, -3 ) + "." +
1583  modDesc.section( ".", -2, -2 ).section( "_", 0, 3 );
1584  QString srchTlab = msetBase.section( ".", -2, -2 );
1585  QString srchTrip = srchTlab.left( 1 ) + "." + srchTlab.mid( 1, 1 ) + "." +
1586  srchTlab.mid( 2 ) + ".xml";
1587  QString srchRun = msetBase.section( ".", 0, -3 ) + "." +
1588  msetBase.section( ".", -1, -1 )
1589  .section( "_", 0, 0 ).mid( 1 );
1590 DbgLv(1) << "NIE: msetBase" << msetBase;
1591  QStringList query;
1592  QString fname;
1593  QString noiID;
1594  int nlnois = 0;
1595  bool usesDB = dkdb_cntrls->db();
1596 
1597  QStringList noifilt;
1598  noifilt << "N*.xml";
1599  QString noidir = US_Settings::dataDir() + "/noises";
1600  QStringList noifiles = QDir( noidir ).entryList(
1601  noifilt, QDir::Files, QDir::Name );
1602  noidir = noidir + "/";
1603 DbgLv(1) << "NIE: noise-files-size" << noifiles.size();
1604 
1605  for ( int ii = 0; ii < noifiles.size(); ii++ )
1606  {
1607  QString noiFname = noifiles.at( ii );
1608  QString noiPath = noidir + noiFname;
1609  US_Noise noise;
1610 
1611  if ( noise.load( noiPath ) != US_DB2::OK )
1612  continue; // Can't use if can't load
1613 
1614  QString noiDesc = noise.description;
1615 DbgLv(1) << "NIE: ii noiDesc" << ii << noiDesc;
1616 
1617  if ( ! noiDesc.startsWith( msetBase ) )
1618  continue; // Can't use if not from the model set
1619 
1620  nlnois++;
1621 
1622  nieDescs << noiDesc;
1623  nieFnams << noiPath;
1624 DbgLv(1) << "NIE: noiFname" << noiFname;
1625 
1626  if ( ! usesDB )
1627  nieIDs << "-1";
1628  }
1629 
1630  if ( usesDB )
1631  {
1632  US_Passwd pw;
1633  US_DB2 db( pw.getPasswd() );
1634  QStringList nIDs;
1635  QString invID = QString::number( US_Settings::us_inv_ID() );
1636 
1637  QStringList edtIDs;
1638  QStringList edtNams;
1639 
1640  query.clear();
1641  query << "all_editedDataIDs" << invID;
1642  db.query( query );
1643 
1644  while( db.next() )
1645  {
1646  QString edtID = db.value( 0 ).toString();
1647  QString edtName = db.value( 2 ).toString();
1648 
1649  if ( edtName.startsWith( srchRun ) &&
1650  edtName.endsWith( srchTrip ) )
1651  {
1652  edtIDs << edtID;
1653  edtNams << edtName;
1654 DbgLv(1) << "NIE: edtID edtName" << edtID << edtName;
1655  }
1656  }
1657 DbgLv(1) << "NIE: edtIDs-size" << edtIDs.size();
1658 if ( edtIDs.size() > 0 ) DbgLv(1) << "NIE: edtName0" << edtNams[0];
1659 
1660  query.clear();
1661 
1662  if ( edtIDs.size() == 1 )
1663  query << "get_noise_desc_by_editID" << invID << edtIDs[ 0 ];
1664 
1665  else
1666  query << "get_noise_desc" << invID;
1667 
1668  db.query( query );
1669 
1670  while( db.next() )
1671  {
1672  QString noiID = db.value( 0 ).toString();
1673  QString edtID = db.value( 2 ).toString();
1674 DbgLv(1) << "NIE: noiID edtID" << noiID << edtID;
1675 
1676  if ( edtIDs.contains( edtID ) )
1677  nIDs << noiID;
1678  }
1679 DbgLv(1) << "NIE: nIDs-size" << nIDs.size() << "msetBase" << msetBase;
1680 
1681  for ( int ii = 0; ii < nIDs.size(); ii++ )
1682  {
1683  QString noiID = nIDs[ ii ];
1684  US_Noise noise;
1685 
1686  if ( noise.load( noiID, &db ) != US_DB2::OK )
1687  continue; // Can't use if can't load
1688 
1689  QString noiDesc = noise.description;
1690 DbgLv(1) << "NIE: ii noiID noiDesc" << ii << noiID << noiDesc;
1691 
1692  if ( ! noiDesc.startsWith( msetBase ) )
1693  continue; // Can't use if not from the model set
1694 
1695  nieIDs << noiID;
1696 
1697  if ( nlnois == 0 )
1698  {
1699  nieFnams << "";
1700  nieDescs << noiDesc;
1701  }
1702  }
1703  }
1704 DbgLv(1) << "NIE: usesDB" << usesDB << "nlnois" << nlnois
1705  << "nieDescs-size" << nieDescs.size() << "nieIDs-size" << nieIDs.size();
1706 
1707  return;
1708 }
1709