otsdaq  3.03.00
ConfigurationManagerRW.cc
1 #include "otsdaq/ConfigurationInterface/ConfigurationManagerRW.h"
2 
3 #include <dirent.h>
4 
5 using namespace ots;
6 
7 #undef __MF_SUBJECT__
8 #define __MF_SUBJECT__ "ConfigurationManagerRW"
9 
10 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
11 #define TABLE_INFO_EXT "Info.xml"
12 
13 #define CORE_TABLE_INFO_FILENAME \
14  ((getenv("SERVICE_DATA_PATH") == NULL) \
15  ? (std::string(__ENV__("USER_DATA")) + "/ServiceData") \
16  : (std::string(__ENV__("SERVICE_DATA_PATH")))) + \
17  "/CoreTableInfoNames.dat"
18 
19 std::atomic<bool> ConfigurationManagerRW::firstTimeConstructed_ = true;
20 
21 //==============================================================================
24  : ConfigurationManager(username) // for use as author of new views
25 {
26  __GEN_COUT__ << "Instantiating Config Manager with Write Access! (for " << username
27  << ") time=" << time(0) << " runTimeSeconds()=" << runTimeSeconds()
28  << __E__;
29 
30  theInterface_ = ConfigurationInterface::getInstance(
31  ConfigurationInterface::CONFIGURATION_MODE::
32  ARTDAQ_DATABASE); // false to use artdaq DB
33 
34  //=========================
35  // dump names of core tables (so UpdateOTS.sh can copy core tables for user)
36  // only if table does not exist
37  if(firstTimeConstructed_)
38  {
39  firstTimeConstructed_ = false;
40 
41  // make table group history directory here and at Gateway Supervisor (just in case)
42  mkdir((ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH).c_str(), 0755);
43 
44  const std::set<std::string>& contextMemberNames = getContextMemberNames();
45  const std::set<std::string>& backboneMemberNames = getBackboneMemberNames();
46  const std::set<std::string>& iterateMemberNames = getIterateMemberNames();
47 
48  FILE* fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "r");
49 
50  if(fp) // check for all core table names in file, and force their presence
51  {
52  std::vector<unsigned int> foundVector;
53  char line[100];
54  for(const auto& name : contextMemberNames)
55  {
56  foundVector.push_back(false);
57  rewind(fp);
58  while(fgets(line, 100, fp))
59  {
60  if(strlen(line) < 1)
61  continue;
62  line[strlen(line) - 1] = '\0'; // remove endline
63  if(strcmp(line, ("ContextGroup/" + name).c_str()) == 0) // is match?
64  {
65  foundVector.back() = true;
66  break;
67  }
68  }
69  }
70 
71  for(const auto& name : backboneMemberNames)
72  {
73  foundVector.push_back(false);
74  rewind(fp);
75  while(fgets(line, 100, fp))
76  {
77  if(strlen(line) < 1)
78  continue;
79  line[strlen(line) - 1] = '\0'; // remove endline
80  if(strcmp(line, ("BackboneGroup/" + name).c_str()) == 0) // is match?
81  {
82  foundVector.back() = true;
83  break;
84  }
85  }
86  }
87 
88  for(const auto& name : iterateMemberNames)
89  {
90  foundVector.push_back(false);
91  rewind(fp);
92  while(fgets(line, 100, fp))
93  {
94  if(strlen(line) < 1)
95  continue;
96  line[strlen(line) - 1] = '\0'; // remove endline
97  if(strcmp(line, ("IterateGroup/" + name).c_str()) == 0) // is match?
98  {
99  foundVector.back() = true;
100  break;
101  }
102  }
103  }
104 
105  //look for optional Context table ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
106  {
107  foundVector.push_back(false);
108  rewind(fp);
109  while(fgets(line, 100, fp))
110  {
111  if(strlen(line) < 1)
112  continue;
113  line[strlen(line) - 1] = '\0'; // remove endline
114  if(strcmp(line,
115  ("ContextGroup/" +
116  ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE)
117  .c_str()) == 0) // is match?
118  {
119  foundVector.back() = true;
120  break;
121  }
122  }
123  }
124 
125  fclose(fp);
126 
127  // open file for appending the missing names
128  fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "a");
129  if(fp)
130  {
131  unsigned int i = 0;
132  for(const auto& name : contextMemberNames)
133  {
134  if(!foundVector[i])
135  fprintf(fp, "\nContextGroup/%s", name.c_str());
136 
137  ++i;
138  }
139  for(const auto& name : backboneMemberNames)
140  {
141  if(!foundVector[i])
142  fprintf(fp, "\nBackboneGroup/%s", name.c_str());
143 
144  ++i;
145  }
146  for(const auto& name : iterateMemberNames)
147  {
148  if(!foundVector[i])
149  fprintf(fp, "\nIterateGroup/%s", name.c_str());
150 
151  ++i;
152  }
153 
154  //last is optional Context table ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
155  if(!foundVector[i])
156  fprintf(
157  fp,
158  "\nContextGroup/%s",
159  ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE.c_str());
160 
161  fclose(fp);
162  }
163  else
164  {
165  __SS__ << "Failed to open core table info file for appending: "
166  << CORE_TABLE_INFO_FILENAME << __E__;
167  __SS_THROW__;
168  }
169  }
170  else
171  {
172  fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "w");
173  if(fp)
174  {
175  fprintf(fp, "ARTDAQ/*");
176  fprintf(fp, "\nConfigCore/*");
177  for(const auto& name : contextMemberNames)
178  fprintf(fp, "\nContextGroup/%s", name.c_str());
179  for(const auto& name : backboneMemberNames)
180  fprintf(fp, "\nBackboneGroup/%s", name.c_str());
181  for(const auto& name : iterateMemberNames)
182  fprintf(fp, "\nIterateGroup/%s", name.c_str());
183  fclose(fp);
184  }
185  else
186  {
187  __SS__ << "Failed to open core table info file: "
188  << CORE_TABLE_INFO_FILENAME << __E__;
189  __SS_THROW__;
190  }
191  }
192  } // end dump names of core tables
193 
194  __GEN_COUTV__(runTimeSeconds());
195 } // end constructor
196 
197 //==============================================================================
204 const std::map<std::string, TableInfo>& ConfigurationManagerRW::getAllTableInfo(
205  bool refresh /* = false */,
206  std::string* accumulatedWarnings /* = 0 */,
207  const std::string& errorFilterName /* = "" */,
208  bool getGroupKeys /* = false */,
209  bool getGroupInfo /* = false */,
210  bool initializeActiveGroups /* = false */)
211 {
212  // allTableInfo_ is container to be returned
213 
214  if(!refresh)
215  return allTableInfo_;
216 
217  // else refresh!
218  allTableInfo_.clear();
219 
220  TableBase* table;
221 
222  // existing configurations are defined by which infos are in TABLE_INFO_PATH
223  // can test that the class exists based on this
224  // and then which versions
225  __GEN_COUT__ << "======================================================== "
226  "getAllTableInfo start runTimeSeconds()="
227  << runTimeSeconds() << __E__;
228  {
229  __GEN_COUT__ << "Refreshing all! Extracting list of tables..." << __E__;
230  DIR* pDIR;
231  struct dirent* entry;
232  std::string path = TABLE_INFO_PATH;
233  char fileExt[] = TABLE_INFO_EXT;
234  const unsigned char MIN_TABLE_NAME_SZ = 3;
235 
236  const int numOfThreads = PROCESSOR_COUNT / 2;
237  __GEN_COUT__ << " PROCESSOR_COUNT " << PROCESSOR_COUNT << " ==> " << numOfThreads
238  << " threads." << __E__;
239  if(numOfThreads < 2) // no multi-threading
240  {
241  if((pDIR = opendir(path.c_str())) != 0)
242  {
243  while((entry = readdir(pDIR)) != 0)
244  {
245  // enforce table name length
246  if(strlen(entry->d_name) < strlen(fileExt) + MIN_TABLE_NAME_SZ)
247  continue;
248 
249  // find file names with correct file extenstion
250  if(strcmp(&(entry->d_name[strlen(entry->d_name) - strlen(fileExt)]),
251  fileExt) != 0)
252  continue; // skip different extentions
253 
254  entry->d_name[strlen(entry->d_name) - strlen(fileExt)] =
255  '\0'; // remove file extension to get table name
256 
257  // 0 will force the creation of new instance (and reload from Info)
258  table = 0;
259 
260  try // only add valid table instances to maps
261  {
262  theInterface_->get(table,
263  entry->d_name,
264  0,
265  0,
266  true); // dont fill
267  }
268  catch(cet::exception const&)
269  {
270  if(table)
271  delete table;
272  table = 0;
273 
274  __GEN_COUT__ << "Skipping! No valid class found for... "
275  << entry->d_name << "\n";
276  continue;
277  }
278  catch(std::runtime_error& e)
279  {
280  if(table)
281  delete table;
282  table = 0;
283 
284  __GEN_COUT__ << "Skipping! No valid class found for... "
285  << entry->d_name << "\n";
286  __GEN_COUT__ << "Error: " << e.what() << __E__;
287 
288  // for a runtime_error, it is likely that columns are the problem
289  // the Table Editor needs to still fix these.. so attempt to
290  // proceed.
291  if(accumulatedWarnings)
292  {
293  if(errorFilterName == "" || errorFilterName == entry->d_name)
294  {
295  *accumulatedWarnings += std::string("\nIn table '") +
296  entry->d_name + "'..." +
297  e.what(); // global accumulate
298 
299  __SS__ << "Attempting to allow illegal columns!" << __E__;
300  *accumulatedWarnings += ss.str();
301  }
302 
303  // attempt to recover and build a mock-up
304  __GEN_COUT__ << "Attempting to allow illegal columns!"
305  << __E__;
306 
307  std::string returnedAccumulatedErrors;
308  try
309  {
310  table = new TableBase(entry->d_name,
311  &returnedAccumulatedErrors);
312  }
313  catch(...)
314  {
315  __GEN_COUT__ << "Skipping! Allowing illegal columns "
316  "didn't work either... "
317  << entry->d_name << "\n";
318  continue;
319  }
320  __GEN_COUT__
321  << "Error (but allowed): " << returnedAccumulatedErrors
322  << __E__;
323 
324  if(errorFilterName == "" || errorFilterName == entry->d_name)
325  *accumulatedWarnings +=
326  std::string("\nIn table '") + entry->d_name + "'..." +
327  returnedAccumulatedErrors; // global accumulate
328  }
329  else
330  continue;
331  }
332 
333  if(nameToTableMap_[entry->d_name]) // handle if instance existed
334  {
335  // copy the existing temporary versions! (or else all is lost)
336  std::set<TableVersion> versions =
337  nameToTableMap_[entry->d_name]->getStoredVersions();
338  for(auto& version : versions)
339  if(version.isTemporaryVersion())
340  {
341  try // do NOT let TableView::init() throw here
342  {
343  nameToTableMap_[entry->d_name]->setActiveView(
344  version);
345  table->copyView( // this calls TableView::init()
346  nameToTableMap_[entry->d_name]->getView(),
347  version,
348  username_);
349  }
350  catch(
351  ...) // do NOT let invalid temporary version throw at this
352  // point
353  {
354  } // just trust configurationBase throws out the failed version
355  }
356 
357  delete nameToTableMap_[entry->d_name];
358  nameToTableMap_[entry->d_name] = 0;
359  }
360 
361  nameToTableMap_[entry->d_name] = table;
362 
363  allTableInfo_[entry->d_name].tablePtr_ = table;
364  allTableInfo_[entry->d_name].versions_ =
365  theInterface_->getVersions(table);
366 
367  // also add any existing temporary versions to all table info
368  // because the interface wont find those versions
369  std::set<TableVersion> versions =
370  nameToTableMap_[entry->d_name]->getStoredVersions();
371  for(auto& version : versions)
372  if(version.isTemporaryVersion())
373  {
374  allTableInfo_[entry->d_name].versions_.emplace(version);
375  }
376  } //end table name handling from directory
377  closedir(pDIR);
378  }
379  }
380  else //multi-threading
381  {
382  int threadsLaunched = 0;
383  int foundThreadIndex = 0;
384  std::string tableName;
385 
386  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
387  for(int i = 0; i < numOfThreads; ++i)
388  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
389 
390  std::vector<std::shared_ptr<ots::TableInfo>> sharedTableInfoPtrs;
391 
392  if((pDIR = opendir(path.c_str())) != 0)
393  {
394  while((entry = readdir(pDIR)) != 0)
395  {
396  // enforce table name length
397  if(strlen(entry->d_name) < strlen(fileExt) + MIN_TABLE_NAME_SZ)
398  continue;
399 
400  // find file names with correct file extenstion
401  if(strcmp(&(entry->d_name[strlen(entry->d_name) - strlen(fileExt)]),
402  fileExt) != 0)
403  continue; // skip different extentions
404 
405  entry->d_name[strlen(entry->d_name) - strlen(fileExt)] =
406  '\0'; // remove file extension to get table name
407  tableName =
408  entry
409  ->d_name; //copy immediate, thread does not like using pointer to char*, corrupts immediately on wraparound
410 
411  //make temporary table info for thread
412  sharedTableInfoPtrs.push_back(std::make_shared<ots::TableInfo>());
413  sharedTableInfoPtrs.back()->accumulatedWarnings_ =
414  accumulatedWarnings ? "ALLOW"
415  : ""; //mark to allow accumulated warnings
416 
417  if(threadsLaunched >= numOfThreads)
418  {
419  //find availableThreadIndex
420  foundThreadIndex = -1;
421  size_t ii = 0;
422  while(foundThreadIndex == -1)
423  {
424  for(int i = 0; i < numOfThreads; ++i)
425  if(*(threadDone[i]))
426  {
427  foundThreadIndex = i;
428  break;
429  }
430  if(foundThreadIndex == -1)
431  {
432  __GEN_COUT_TYPE__(TLVL_DEBUG + 2)
433  << __COUT_HDR__
434  << "Waiting for available thread... iteration # "
435  << ii << __E__;
436  if(++ii > 100 /* 1s */ * 10)
437  {
438  __GEN_SS__ << "Threads seem to be stuck getting "
439  "table info! Timeout while waiting..."
440  << __E__;
441  __GEN_SS_THROW__;
442  }
443  usleep(10000);
444  }
445  } //end thread search loop
446  threadsLaunched = numOfThreads - 1;
447  }
448  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex
449  << " for table " << tableName << __E__;
450 
451  *(threadDone[foundThreadIndex]) = false;
452  std::thread(
453  [](ConfigurationManagerRW* theCfgMgr,
454  std::string theTableName,
455  TableBase* existingTable,
456  std::shared_ptr<ots::TableInfo> theTableInfo,
457  std::shared_ptr<std::atomic<bool>> theThreadDone) {
459  theTableName,
460  existingTable,
461  theTableInfo,
462  theThreadDone);
463  },
464  this,
465  tableName,
466  nameToTableMap_[tableName],
467  sharedTableInfoPtrs.back(),
468  threadDone[foundThreadIndex])
469  .detach();
470 
471  ++threadsLaunched;
472  ++foundThreadIndex;
473  } //end table name handling from directory
474  closedir(pDIR);
475  } //end tableInfo thread loop
476 
477  //check for all threads done
478  do
479  {
480  foundThreadIndex = -1;
481  for(int i = 0; i < numOfThreads; ++i)
482  if(!*(threadDone[i]))
483  {
484  foundThreadIndex = i;
485  break;
486  }
487  if(foundThreadIndex != -1)
488  {
489  __GEN_COUTT__ << "Waiting for thread to finish... "
490  << foundThreadIndex << __E__;
491  usleep(10000);
492  }
493  } while(foundThreadIndex != -1); //end thread done search loop
494 
495  //threads done now, so copy table info
496  for(auto& tableInfo : sharedTableInfoPtrs)
497  {
498  __GEN_COUT_TYPE__(TLVL_DEBUG + 3)
499  << __COUT_HDR__ << "Copying table info for "
500  << tableInfo->tablePtr_->getTableName() << __E__;
501  nameToTableMap_[tableInfo->tablePtr_->getTableName()] =
502  tableInfo->tablePtr_;
503  allTableInfo_[tableInfo->tablePtr_->getTableName()].tablePtr_ =
504  tableInfo->tablePtr_;
505  allTableInfo_[tableInfo->tablePtr_->getTableName()].versions_ =
506  tableInfo->versions_;
507  } //end copy group info loop
508  }
509  __GEN_COUT__ << "Extracting list of tables complete." << __E__;
510  } //end Extracting list of tables
511 
512  // call init to load active versions by default, activate with warnings allowed (assuming development going on)
513  if(initializeActiveGroups)
514  {
515  __GEN_COUT__ << "Now initializing..." << __E__;
516  // if there is a filter name, do not include init warnings (it just scares people in the table editor)
517  std::string tmpAccumulateWarnings;
518  init(0 /*accumulatedErrors*/,
519  false /*initForWriteAccess*/,
520  accumulatedWarnings ? &tmpAccumulateWarnings : nullptr);
521 
522  if(accumulatedWarnings && errorFilterName == "")
523  *accumulatedWarnings += tmpAccumulateWarnings;
524  }
525  __GEN_COUT__ << "======================================================== "
526  "getAllTableInfo end runTimeSeconds()="
527  << runTimeSeconds() << __E__;
528 
529  // get Group Info too!
530  if(getGroupKeys || getGroupInfo)
531  {
532  allGroupInfo_.clear();
533  try
534  {
535  // build allGroupInfo_ for the ConfigurationManagerRW
536 
537  std::set<std::string /*name*/> tableGroups =
538  theInterface_->getAllTableGroupNames();
539  __GEN_COUT__ << "Number of Groups: " << tableGroups.size() << __E__;
540 
541  __GEN_COUTT__ << "Group Info start runTimeSeconds()=" << runTimeSeconds()
542  << __E__;
543 
544  TableGroupKey key;
545  std::string name;
546  for(const auto& fullName : tableGroups)
547  {
548  TableGroupKey::getGroupNameAndKey(fullName, name, key);
549  cacheGroupKey(name, key);
550  }
551 
552  __GEN_COUTT__ << "Group Keys end runTimeSeconds()=" << runTimeSeconds()
553  << __E__;
554 
555  // for each group get member map & comment, author, time, and type for latest key
556  if(getGroupInfo)
557  {
558  const int numOfThreads = PROCESSOR_COUNT / 2;
559  __GEN_COUT__ << " PROCESSOR_COUNT " << PROCESSOR_COUNT << " ==> "
560  << numOfThreads << " threads." << __E__;
561  if(numOfThreads < 2) // no multi-threading
562  for(auto& groupInfo : allGroupInfo_)
563  {
564  try
565  {
567  groupInfo.first /*groupName*/,
568  groupInfo.second.getLatestKey(),
569  false /*doActivate*/,
570  &groupInfo.second.latestKeyMemberMap_ /*groupMembers*/,
571  0 /*progressBar*/,
572  0 /*accumulateErrors*/,
573  &groupInfo.second.latestKeyGroupComment_,
574  &groupInfo.second.latestKeyGroupAuthor_,
575  &groupInfo.second.latestKeyGroupCreationTime_,
576  true /*doNotLoadMember*/,
577  &groupInfo.second.latestKeyGroupTypeString_);
578  }
579  catch(...)
580  {
581  __GEN_COUT_WARN__ << "Error occurred loading latest group "
582  "info into cache for '"
583  << groupInfo.first << "("
584  << groupInfo.second.getLatestKey()
585  << ")'..." << __E__;
586  groupInfo.second.latestKeyGroupComment_ =
587  ConfigurationManager::UNKNOWN_INFO;
588  groupInfo.second.latestKeyGroupAuthor_ =
589  ConfigurationManager::UNKNOWN_INFO;
590  groupInfo.second.latestKeyGroupCreationTime_ =
591  ConfigurationManager::UNKNOWN_TIME;
592  groupInfo.second.latestKeyGroupTypeString_ =
593  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
594  groupInfo.second.latestKeyMemberMap_ = {};
595  }
596  } // end group info loop
597  else //multi-threading
598  {
599  int threadsLaunched = 0;
600  int foundThreadIndex = 0;
601 
602  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
603  for(int i = 0; i < numOfThreads; ++i)
604  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
605 
606  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
607 
608  for(auto& groupInfo : allGroupInfo_)
609  {
610  //make temporary group info for thread
611  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
612 
613  if(threadsLaunched >= numOfThreads)
614  {
615  //find availableThreadIndex
616  foundThreadIndex = -1;
617  while(foundThreadIndex == -1)
618  {
619  for(int i = 0; i < numOfThreads; ++i)
620  if(*(threadDone[i]))
621  {
622  foundThreadIndex = i;
623  break;
624  }
625  if(foundThreadIndex == -1)
626  {
627  __GEN_COUTT__ << "Waiting for available thread..."
628  << __E__;
629  usleep(10000);
630  }
631  } //end thread search loop
632  threadsLaunched = numOfThreads - 1;
633  }
634  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex
635  << " for " << groupInfo.first << "("
636  << groupInfo.second.getLatestKey() << ")" << __E__;
637 
638  *(threadDone[foundThreadIndex]) = false;
639 
640  std::thread(
641  [](ConfigurationManagerRW* theCfgMgr,
642  std::string theGroupName,
643  ots::TableGroupKey theGroupKey,
644  std::shared_ptr<ots::GroupInfo> theGroupInfo,
645  std::shared_ptr<std::atomic<bool>> theThreadDone) {
647  theCfgMgr,
648  theGroupName,
649  theGroupKey,
650  theGroupInfo,
651  theThreadDone);
652  },
653  this,
654  groupInfo.first,
655  groupInfo.second.getLatestKey(),
656  sharedGroupInfoPtrs.back(),
657  threadDone[foundThreadIndex])
658  .detach();
659 
660  ++threadsLaunched;
661  ++foundThreadIndex;
662  } //end groupInfo thread loop
663 
664  //check for all threads done
665  do
666  {
667  foundThreadIndex = -1;
668  for(int i = 0; i < numOfThreads; ++i)
669  if(!*(threadDone[i]))
670  {
671  foundThreadIndex = i;
672  break;
673  }
674  if(foundThreadIndex != -1)
675  {
676  __GEN_COUTT__ << "Waiting for thread to finish... "
677  << foundThreadIndex << __E__;
678  usleep(10000);
679  }
680  } while(foundThreadIndex != -1); //end thread done search loop
681 
682  //threads done now, so copy group info
683  size_t i = 0;
684  for(auto& groupInfo : allGroupInfo_)
685  {
686  groupInfo.second.latestKeyGroupComment_ =
687  sharedGroupInfoPtrs[i]->latestKeyGroupComment_;
688  groupInfo.second.latestKeyGroupAuthor_ =
689  sharedGroupInfoPtrs[i]->latestKeyGroupAuthor_;
690  groupInfo.second.latestKeyGroupCreationTime_ =
691  sharedGroupInfoPtrs[i]->latestKeyGroupCreationTime_;
692  groupInfo.second.latestKeyGroupTypeString_ =
693  sharedGroupInfoPtrs[i]->latestKeyGroupTypeString_;
694  groupInfo.second.latestKeyMemberMap_ =
695  sharedGroupInfoPtrs[i]->latestKeyMemberMap_;
696  ++i;
697  } //end copy group info loop
698 
699  } //end multi-thread handling
700  }
701  } // end get group info
702  catch(const std::runtime_error& e)
703  {
704  __SS__
705  << "A fatal error occurred reading the info for all table groups. Error: "
706  << e.what() << __E__;
707  __GEN_COUT_ERR__ << "\n" << ss.str();
708  if(accumulatedWarnings)
709  *accumulatedWarnings += ss.str();
710  else
711  throw;
712  }
713  catch(...)
714  {
715  __SS__ << "An unknown fatal error occurred reading the info for all table "
716  "groups."
717  << __E__;
718  try
719  {
720  throw;
721  } //one more try to printout extra info
722  catch(const std::exception& e)
723  {
724  ss << "Exception message: " << e.what();
725  }
726  catch(...)
727  {
728  }
729  __GEN_COUT_ERR__ << "\n" << ss.str();
730  if(accumulatedWarnings)
731  *accumulatedWarnings += ss.str();
732  else
733  throw;
734  }
735  __GEN_COUTT__ << "Group Info end runTimeSeconds()=" << runTimeSeconds() << __E__;
736  } //end getGroupInfo
737  else
738  __GEN_COUTT__ << "Table Info end runTimeSeconds()=" << runTimeSeconds() << __E__;
739 
740  return allTableInfo_;
741 } // end getAllTableInfo()
742 
743 //==============================================================================
746  ConfigurationManagerRW* cfgMgr,
747  std::string tableName,
748  TableBase* existingTable,
749  std::shared_ptr<ots::TableInfo> tableInfo,
750  std::shared_ptr<std::atomic<bool>> threadDone)
751 try
752 {
753  __COUTT__ << "Thread started... table " << tableName << __E__;
754 
755  // 0 will force the creation of new instance (and reload from Info)
756  tableInfo->tablePtr_ = 0;
757 
758  try // only add valid table instances to maps
759  {
760  cfgMgr->theInterface_->get(tableInfo->tablePtr_,
761  tableName,
762  0,
763  0,
764  true); // dont fill
765  }
766  catch(cet::exception const&)
767  {
768  if(tableInfo->tablePtr_)
769  delete tableInfo->tablePtr_;
770  tableInfo->tablePtr_ = 0;
771 
772  __COUT__ << "Skipping! No valid class found for... " << tableName << "\n";
773  *(threadDone) = true;
774  return;
775  }
776  catch(std::runtime_error& e)
777  {
778  if(tableInfo->tablePtr_)
779  delete tableInfo->tablePtr_;
780  tableInfo->tablePtr_ = 0;
781 
782  __COUT__ << "Skipping! No valid class found for... " << tableName << "\n";
783  __COUTT__ << "Error: " << e.what() << __E__;
784 
785  // for a runtime_error, it is likely that columns are the problem
786  // the Table Editor needs to still fix these.. so attempt to
787  // proceed.
788  if(tableInfo->accumulatedWarnings_ == "ALLOW")
789  {
790  tableInfo->accumulatedWarnings_ = "";
791  if(1) //errorFilterName == "" || errorFilterName == tableName)
792  {
793  tableInfo->accumulatedWarnings_ += std::string("\nIn table '") +
794  tableName + "'..." +
795  e.what(); // global accumulate
796 
797  __SS__ << "Attempting to allow illegal columns!" << __E__;
798  tableInfo->accumulatedWarnings_ += ss.str();
799  }
800 
801  // attempt to recover and build a mock-up
802  __COUT__ << "Attempting to allow illegal columns!" << __E__;
803 
804  std::string returnedAccumulatedErrors;
805  try
806  {
807  tableInfo->tablePtr_ =
808  new TableBase(tableName, &returnedAccumulatedErrors);
809  }
810  catch(...)
811  {
812  __COUT__ << "Skipping! Allowing illegal columns didn't work either... "
813  << tableName << "\n";
814  *(threadDone) = true;
815  return;
816  }
817  __COUT_WARN__ << "Error (but allowed): " << returnedAccumulatedErrors
818  << __E__;
819 
820  if(1) //errorFilterName == "" || errorFilterName == entry->d_name)
821  tableInfo->accumulatedWarnings_ +=
822  std::string("\nIn table '") + tableName + "'..." +
823  returnedAccumulatedErrors; // global accumulate
824  }
825  else
826  {
827  tableInfo->accumulatedWarnings_ = "";
828  *(threadDone) = true;
829  return;
830  }
831  }
832 
833  if(existingTable) // handle if instance existed
834  {
835  __COUTT__ << "Copying temporary version from existing table object for "
836  << tableName << __E__;
837  // copy the existing temporary versions! (or else all is lost)
838  std::set<TableVersion> versions = existingTable->getStoredVersions();
839  for(auto& version : versions)
840  if(version.isTemporaryVersion())
841  {
842  try // do NOT let TableView::init() throw here
843  {
844  existingTable->setActiveView(version);
845  tableInfo->tablePtr_->copyView( // this calls TableView::init()
846  existingTable->getView(),
847  version,
848  cfgMgr->username_);
849  }
850  catch(...) // do NOT let invalid temporary version throw at this
851  // point
852  {
853  } // just trust configurationBase throws out the failed version
854  }
855 
856  delete existingTable;
857  existingTable = 0;
858  }
859 
860  tableInfo->versions_ = cfgMgr->theInterface_->getVersions(tableInfo->tablePtr_);
861 
862  // also add any existing temporary versions to all table info
863  // because the interface wont find those versions
864  std::set<TableVersion> versions = tableInfo->tablePtr_->getStoredVersions();
865  for(auto& version : versions)
866  if(version.isTemporaryVersion())
867  {
868  tableInfo->versions_.emplace(version);
869  }
870 
871  __COUTT__ << "Thread done... table " << tableName << __E__;
872  *(threadDone) = true;
873 } // end loadTableInfoThread
874 catch(...)
875 {
876  __COUT_ERR__ << "Error occurred loading latest table info into cache for '"
877  << tableName << "'..." << __E__;
878  *(threadDone) = true;
879 } // end loadTableInfoThread catch
880 
881 //==============================================================================
884  ConfigurationManagerRW* cfgMgr,
885  std::string groupName,
886  ots::TableGroupKey groupKey,
887  std::shared_ptr<ots::GroupInfo> groupInfo,
888  std::shared_ptr<std::atomic<bool>> threadDone)
889 try
890 {
891  __COUTT__ << "Thread started... " << groupName << "(" << groupKey << ")" << __E__;
892 
893  cfgMgr->loadTableGroup(groupName /*groupName*/,
894  groupKey, //groupInfo->getLatestKey(),
895  false /*doActivate*/,
896  &(groupInfo->latestKeyMemberMap_) /*groupMembers*/,
897  0 /*progressBar*/,
898  0 /*accumulateErrors*/,
899  &(groupInfo->latestKeyGroupComment_),
900  &(groupInfo->latestKeyGroupAuthor_),
901  &(groupInfo->latestKeyGroupCreationTime_),
902  true /*doNotLoadMember*/,
903  &(groupInfo->latestKeyGroupTypeString_));
904 
905  *(threadDone) = true;
906 } // end loadTableGroupThread
907 catch(...)
908 {
909  __COUT_WARN__ << "Error occurred loading latest group info into cache for '"
910  << groupName << "(" << groupInfo->getLatestKey() << ")'..." << __E__;
911  groupInfo->latestKeyGroupComment_ = ConfigurationManager::UNKNOWN_INFO;
912  groupInfo->latestKeyGroupAuthor_ = ConfigurationManager::UNKNOWN_INFO;
913  groupInfo->latestKeyGroupCreationTime_ = ConfigurationManager::UNKNOWN_TIME;
914  groupInfo->latestKeyGroupTypeString_ = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
915  groupInfo->latestKeyMemberMap_ = {};
916  *(threadDone) = true;
917 } // end loadTableGroupThread catch
918 
919 //==============================================================================
924  ConfigurationManagerRW* cfgMgr,
925  std::string groupName,
926  ots::TableGroupKey groupKeyToCompare,
927  const std::map<std::string, TableVersion>& groupMemberMap,
928  const std::map<std::string /*name*/, std::string /*alias*/>& memberTableAliases,
929  std::atomic<bool>* foundIdentical,
930  ots::TableGroupKey* identicalKey,
931  std::mutex* threadMutex,
932  std::shared_ptr<std::atomic<bool>> threadDone)
933 try
934 {
935  std::map<std::string /*name*/, TableVersion /*version*/> compareToMemberMap;
936  std::map<std::string /*name*/, std::string /*alias*/> compareToMemberTableAliases;
937  std::map<std::string /*name*/, std::string /*alias*/>*
938  compareToMemberTableAliasesPtr = nullptr;
939  if(memberTableAliases
940  .size()) //only give pointer if necessary, without will load group faster
941  compareToMemberTableAliasesPtr = &compareToMemberTableAliases;
942 
943  cfgMgr->loadTableGroup(groupName,
944  groupKeyToCompare,
945  false /*doActivate*/,
946  &compareToMemberMap /*memberMap*/,
947  0, /*progressBar*/
948  0, /*accumulatedWarnings*/
949  0, /*groupComment*/
950  0,
951  0, /*null pointers*/
952  true /*doNotLoadMember*/,
953  0 /*groupTypeString*/,
954  compareToMemberTableAliasesPtr);
955 
956  bool debug = false;
957  if(TTEST(9)) // = DEBUG+9
958  {
959  debug = true;
960  for(auto& memberPair : groupMemberMap)
961  __COUTS__(9) << "member " << memberPair.first << " (" << memberPair.second
962  << ")" << __E__;
963  for(auto& memberPair : compareToMemberMap)
964  __COUTS__(9) << "compare " << groupName << " (" << groupKeyToCompare
965  << ") member:" << memberPair.first << " (" << memberPair.second
966  << ")" << __E__;
967  }
968 
969  bool isDifferent = false;
970  for(auto& memberPair : groupMemberMap)
971  {
972  if(memberTableAliases.find(memberPair.first) != memberTableAliases.end())
973  {
974  // handle this table as alias, not version
975  if(compareToMemberTableAliases.find(memberPair.first) ==
976  compareToMemberTableAliases.end() || // alias is missing
977  memberTableAliases.at(memberPair.first) !=
978  compareToMemberTableAliases.at(memberPair.first))
979  { // then different
980  isDifferent = true;
981  if(debug)
982  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
983  << ") on alias " << memberPair.first << __E__;
984  break;
985  }
986  else
987  continue;
988  } // else check if compareTo group is using an alias for table
989  else if(compareToMemberTableAliases.find(memberPair.first) !=
990  compareToMemberTableAliases.end())
991  {
992  // then different
993  isDifferent = true;
994  if(debug)
995  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
996  << ") on reverse alias " << memberPair.first << __E__;
997  break;
998 
999  } // else handle as table version comparison
1000  else if(compareToMemberMap.find(memberPair.first) ==
1001  compareToMemberMap.end() || // name is missing
1002  memberPair.second !=
1003  compareToMemberMap.at(memberPair.first)) // or version mismatch
1004  {
1005  // then different
1006  isDifferent = true;
1007  if(debug)
1008  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
1009  << ") on mismatch " << memberPair.first << __E__;
1010  break;
1011  }
1012  }
1013 
1014  // check member size for exact match
1015  if(!isDifferent &&
1016  groupMemberMap.size() !=
1017  compareToMemberMap
1018  .size()) // different size, so not same (groupMemberMap is a subset of memberPairs)
1019  {
1020  isDifferent = true;
1021 
1022  if(debug)
1023  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare << ") on size "
1024  << __E__;
1025  }
1026 
1027  if(!isDifferent) //found an exact match!
1028  {
1029  *foundIdentical = true;
1030  __COUT__ << "=====> Found exact match with key: " << groupKeyToCompare << __E__;
1031 
1032  std::lock_guard<std::mutex> lock(*threadMutex);
1033  *identicalKey = groupKeyToCompare;
1034  }
1035 
1036  *(threadDone) = true;
1037 } // end compareTableGroupThread
1038 catch(...)
1039 {
1040  __COUT_WARN__ << "Error occurred comparing group '" << groupName << "("
1041  << groupKeyToCompare << ")'..." << __E__;
1042 
1043  *(threadDone) = true;
1044 } // end compareTableGroupThread catch
1045 
1046 //==============================================================================
1050 std::map<std::string /*table name*/,
1051  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
1053 {
1054  std::map<std::string /*table name*/,
1055  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
1057 
1058  // always have scratch alias for each table that has a scratch version
1059  // overwrite map entry if necessary
1060  if(!ConfigurationInterface::isVersionTrackingEnabled())
1061  for(const auto& tableInfo : allTableInfo_)
1062  for(const auto& version : tableInfo.second.versions_)
1063  if(version.isScratchVersion())
1064  retMap[tableInfo.first][ConfigurationManager::SCRATCH_VERSION_ALIAS] =
1065  TableVersion(TableVersion::SCRATCH);
1066 
1067  return retMap;
1068 } // end getVersionAliases()
1069 
1070 //==============================================================================
1074 void ConfigurationManagerRW::activateTableGroup(const std::string& tableGroupName,
1075  TableGroupKey tableGroupKey,
1076  std::string* accumulatedTreeErrors,
1077  std::string* groupTypeString)
1078 {
1079  try
1080  {
1081  loadTableGroup(tableGroupName,
1082  tableGroupKey,
1083  true, // loads and activates
1084  0, // no members needed
1085  0, // no progress bar
1086  accumulatedTreeErrors, // accumulate warnings or not
1087  0 /* groupComment */,
1088  0 /* groupAuthor */,
1089  0 /* groupCreateTime */,
1090  false /* doNotLoadMember */,
1091  groupTypeString);
1092  }
1093  catch(...)
1094  {
1095  __GEN_COUT_ERR__ << "There were errors, so de-activating group: "
1096  << tableGroupName << " (" << tableGroupKey << ")" << __E__;
1097  try // just in case any lingering pieces, let's deactivate
1098  {
1099  destroyTableGroup(tableGroupName, true);
1100  }
1101  catch(...)
1102  {
1103  }
1104  throw; // re-throw original exception
1105  }
1106 
1107  __GEN_COUT_INFO__ << "Updating persistent active groups to "
1108  << ConfigurationManager::ACTIVE_GROUPS_FILENAME << " ..." << __E__;
1109 
1110  __COUT_INFO__ << "Active Context table group: " << theContextTableGroup_ << "("
1111  << (theContextTableGroupKey_
1112  ? theContextTableGroupKey_->toString().c_str()
1113  : "-1")
1114  << ")" << __E__;
1115  __COUT_INFO__ << "Active Backbone table group: " << theBackboneTableGroup_ << "("
1116  << (theBackboneTableGroupKey_
1117  ? theBackboneTableGroupKey_->toString().c_str()
1118  : "-1")
1119  << ")" << __E__;
1120  __COUT_INFO__ << "Active Iterate table group: " << theIterateTableGroup_ << "("
1121  << (theIterateTableGroupKey_
1122  ? theIterateTableGroupKey_->toString().c_str()
1123  : "-1")
1124  << ")" << __E__;
1125  __COUT_INFO__ << "Active Configuration table group: " << theConfigurationTableGroup_
1126  << "("
1127  << (theConfigurationTableGroupKey_
1128  ? theConfigurationTableGroupKey_->toString().c_str()
1129  : "-1")
1130  << ")" << __E__;
1131 
1133  FILE* fp = fopen(fn.c_str(), "w");
1134  if(!fp)
1135  {
1136  __SS__ << "Fatal Error! Unable to open the file "
1138  << " for editing! Is there a permissions problem?" << __E__;
1139  __GEN_COUT_ERR__ << ss.str();
1140  __SS_THROW__;
1141  return;
1142  }
1143  fprintf(fp, "%s\n", theContextTableGroup_.c_str());
1144  fprintf(
1145  fp,
1146  "%s\n",
1147  theContextTableGroupKey_ ? theContextTableGroupKey_->toString().c_str() : "-1");
1148  fprintf(fp, "%s\n", theBackboneTableGroup_.c_str());
1149  fprintf(
1150  fp,
1151  "%s\n",
1152  theBackboneTableGroupKey_ ? theBackboneTableGroupKey_->toString().c_str() : "-1");
1153  fprintf(fp, "%s\n", theIterateTableGroup_.c_str());
1154  fprintf(
1155  fp,
1156  "%s\n",
1157  theIterateTableGroupKey_ ? theIterateTableGroupKey_->toString().c_str() : "-1");
1158  fprintf(fp, "%s\n", theConfigurationTableGroup_.c_str());
1159  fprintf(fp,
1160  "%s\n",
1161  theConfigurationTableGroupKey_
1162  ? theConfigurationTableGroupKey_->toString().c_str()
1163  : "-1");
1164  fclose(fp);
1165 
1166  // save last activated group
1167  {
1168  std::pair<std::string /*group name*/, TableGroupKey> activatedGroup(
1169  std::string(tableGroupName), tableGroupKey);
1170  if(theConfigurationTableGroupKey_ &&
1171  theConfigurationTableGroup_ == tableGroupName &&
1172  *theConfigurationTableGroupKey_ == tableGroupKey)
1173  ConfigurationManager::saveGroupNameAndKey(activatedGroup,
1174  LAST_ACTIVATED_CONFIG_GROUP_FILE);
1175  else if(theContextTableGroupKey_ && theContextTableGroup_ == tableGroupName &&
1176  *theContextTableGroupKey_ == tableGroupKey)
1177  ConfigurationManager::saveGroupNameAndKey(activatedGroup,
1178  LAST_ACTIVATED_CONTEXT_GROUP_FILE);
1179  else if(theBackboneTableGroupKey_ && theBackboneTableGroup_ == tableGroupName &&
1180  *theBackboneTableGroupKey_ == tableGroupKey)
1181  ConfigurationManager::saveGroupNameAndKey(activatedGroup,
1182  LAST_ACTIVATED_BACKBONE_GROUP_FILE);
1183  else if(theIterateTableGroupKey_ && theIterateTableGroup_ == tableGroupName &&
1184  *theIterateTableGroupKey_ == tableGroupKey)
1185  ConfigurationManager::saveGroupNameAndKey(activatedGroup,
1186  LAST_ACTIVATED_ITERATOR_GROUP_FILE);
1187  } // end save last activated group
1188 
1189 } // end activateTableGroup()
1190 
1191 //==============================================================================
1196  TableVersion sourceViewVersion)
1197 {
1198  __GEN_COUT_INFO__ << "Creating temporary backbone view from version "
1199  << sourceViewVersion << __E__;
1200 
1201  // find common available temporary version among backbone members
1202  TableVersion tmpVersion =
1203  TableVersion::getNextTemporaryVersion(); // get the default temporary version
1204  TableVersion retTmpVersion;
1205  auto backboneMemberNames = ConfigurationManager::getBackboneMemberNames();
1206  for(auto& name : backboneMemberNames)
1207  {
1208  retTmpVersion =
1210  if(retTmpVersion < tmpVersion)
1211  tmpVersion = retTmpVersion;
1212  }
1213 
1214  __GEN_COUT__ << "Common temporary backbone version found as " << tmpVersion << __E__;
1215 
1216  // create temporary views from source version to destination temporary version
1217  for(auto& name : backboneMemberNames)
1218  {
1219  retTmpVersion =
1220  getTableByName(name)->createTemporaryView(sourceViewVersion, tmpVersion);
1221  if(retTmpVersion != tmpVersion)
1222  {
1223  __SS__ << "Failure! Temporary view requested was " << tmpVersion
1224  << ". Mismatched temporary view created: " << retTmpVersion << __E__;
1225  __GEN_COUT_ERR__ << ss.str();
1226  __SS_THROW__;
1227  }
1228  }
1229 
1230  return tmpVersion;
1231 } // end createTemporaryBackboneView()
1232 
1233 //==============================================================================
1234 TableBase* ConfigurationManagerRW::getTableByName(const std::string& tableName)
1235 {
1236  if(nameToTableMap_.find(tableName) == nameToTableMap_.end())
1237  {
1238  if(tableName == ConfigurationManager::ARTDAQ_TOP_TABLE_NAME)
1239  {
1240  __GEN_COUT_WARN__
1241  << "Since target table was the artdaq top configuration level, "
1242  "attempting to help user by appending to core tables file: "
1243  << CORE_TABLE_INFO_FILENAME << __E__;
1244  FILE* fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "a");
1245  if(fp)
1246  {
1247  fprintf(fp, "\nARTDAQ/*");
1248  fclose(fp);
1249  }
1250  }
1251 
1252  __SS__ << "Table not found with name: " << tableName << __E__;
1253  size_t f;
1254  if((f = tableName.find(' ')) != std::string::npos)
1255  ss << "There was a space character found in the table name needle at "
1256  "position "
1257  << f << " in the string (was this intended?). " << __E__;
1258 
1259  ss << "\nIf you think this table should exist in the core set of tables, try "
1260  "running 'UpdateOTS.sh --tables' to update your tables, then relaunch ots."
1261  << __E__;
1262  ss << "\nTables must be defined at path $USER_DATA/TableInfo/ to exist in ots. "
1263  "Please verify your table definitions, and then restart ots."
1264  << __E__;
1265  __GEN_COUT_ERR__ << "\n" << ss.str();
1266  __SS_THROW__;
1267  }
1268  return nameToTableMap_[tableName];
1269 } // end getTableByName()
1270 
1271 //==============================================================================
1277  const std::string& tableName,
1278  TableVersion version,
1279  bool looseColumnMatching /* = false */,
1280  std::string* accumulatedErrors /* = 0 */,
1281  bool getRawData /* = false */)
1282 {
1283  auto it = nameToTableMap_.find(tableName);
1284  if(it == nameToTableMap_.end())
1285  {
1286  __SS__ << "\nCan not find table named '" << tableName
1287  << "'\n\n\n\nYou need to load the table before it can be used."
1288  << "It probably is missing from the member list of the Table "
1289  "Group that was loaded?\n\n\n\n\n"
1290  << __E__;
1291  __SS_THROW__;
1292  }
1293  TableBase* table = it->second;
1294 
1295  if(version.isTemporaryVersion())
1296  {
1297  table->setActiveView(version);
1298 
1299  if(getRawData)
1300  {
1301  std::stringstream jsonSs;
1302  table->getViewP()->printJSON(jsonSs);
1303  table->getViewP()->doGetSourceRawData(true);
1304  table->getViewP()->fillFromJSON(jsonSs.str());
1305  }
1306  }
1307  else
1308  {
1309  theInterface_->get(table,
1310  tableName,
1311  0 /* groupKey */,
1312  0 /* groupName */,
1313  false /* dontFill */, // false to fill w/version
1314  version,
1315  false /* resetConfiguration*/, // false to not reset
1316  looseColumnMatching,
1317  getRawData,
1318  accumulatedErrors);
1319  }
1320  return table;
1321 } // end getVersionedTableByName()
1322 
1323 //==============================================================================
1327  TableVersion temporaryVersion,
1328  bool makeTemporary) //,
1330 {
1331  TableVersion newVersion(temporaryVersion);
1332 
1333  // set author of version
1334  TableBase* table = getTableByName(tableName);
1335  table->getTemporaryView(temporaryVersion)->setAuthor(username_);
1336  // NOTE: author is assigned to permanent versions when saved to DBI
1337 
1338  if(!makeTemporary) // saveNewVersion makes the new version the active version
1339  newVersion = theInterface_->saveNewVersion(table, temporaryVersion);
1340  else // make the temporary version active
1341  table->setActiveView(newVersion);
1342 
1343  // if there is a problem, try to recover
1344  while(!makeTemporary && !newVersion.isScratchVersion() &&
1345  allTableInfo_[tableName].versions_.find(newVersion) !=
1346  allTableInfo_[tableName].versions_.end())
1347  {
1348  __GEN_COUT_ERR__
1349  << "What happenened!?? ERROR::: new persistent version v" << newVersion
1350  << " already exists!? How is it possible? Retrace your steps and "
1351  "tell an admin."
1352  << __E__;
1353 
1354  // create a new temporary version of the target view
1355  temporaryVersion = table->createTemporaryView(newVersion);
1356 
1357  if(newVersion.isTemporaryVersion())
1358  newVersion = temporaryVersion;
1359  else
1360  newVersion = TableVersion::getNextVersion(newVersion);
1361 
1362  __GEN_COUT_WARN__ << "Attempting to recover and use v" << newVersion << __E__;
1363 
1364  if(!makeTemporary) // saveNewVersion makes the new version the active version
1365  newVersion =
1366  theInterface_->saveNewVersion(table, temporaryVersion, newVersion);
1367  else // make the temporary version active
1368  table->setActiveView(newVersion);
1369  }
1370 
1371  if(newVersion.isInvalid())
1372  {
1373  __SS__ << "Something went wrong saving the new version v" << newVersion
1374  << ". What happened?! (duplicates? database error?)" << __E__;
1375  __GEN_COUT_ERR__ << "\n" << ss.str();
1376  __SS_THROW__;
1377  }
1378 
1379  // update allTableInfo_ with the new version
1380  allTableInfo_[tableName].versions_.insert(newVersion);
1381 
1382  // table->getView().print();
1383  return newVersion;
1384 } // end saveNewTable()
1385 
1386 //==============================================================================
1391 void ConfigurationManagerRW::eraseTemporaryVersion(const std::string& tableName,
1392  TableVersion targetVersion)
1393 {
1394  TableBase* table = getTableByName(tableName);
1395 
1396  table->trimTemporary(targetVersion);
1397 
1398  // if allTableInfo_ is not setup, then done
1399  if(allTableInfo_.find(tableName) == allTableInfo_.end())
1400  return;
1401  // else cleanup table info
1402 
1403  if(targetVersion.isInvalid())
1404  {
1405  // erase all temporary versions!
1406  for(auto it = allTableInfo_[tableName].versions_.begin();
1407  it != allTableInfo_[tableName].versions_.end();
1408  /*no increment*/)
1409  {
1410  if(it->isTemporaryVersion())
1411  {
1412  __GEN_COUT__ << "Removing '" << tableName << "' version info: " << *it
1413  << __E__;
1414  allTableInfo_[tableName].versions_.erase(it++);
1415  }
1416  else
1417  ++it;
1418  }
1419  }
1420  else // erase target version only
1421  {
1422  //__GEN_COUT__ << "Removing '" << tableName << "' version info: " << targetVersion << __E__;
1423  auto it = allTableInfo_[tableName].versions_.find(targetVersion);
1424  if(it == allTableInfo_[tableName].versions_.end())
1425  {
1426  __GEN_COUT__ << "Target '" << tableName << "' version v" << targetVersion
1427  << " was not found in info versions..." << __E__;
1428  return;
1429  }
1430  allTableInfo_[tableName].versions_.erase(
1431  allTableInfo_[tableName].versions_.find(targetVersion));
1432  }
1433 } // end eraseTemporaryVersion()
1434 
1435 //==============================================================================
1440 void ConfigurationManagerRW::clearCachedVersions(const std::string& tableName)
1441 {
1442  TableBase* table = getTableByName(tableName);
1443 
1444  table->trimCache(0);
1445 } // end clearCachedVersions()
1446 
1447 //==============================================================================
1453 {
1454  for(auto configInfo : allTableInfo_)
1455  configInfo.second.tablePtr_->trimCache(0);
1456 } // end clearAllCachedVersions()
1457 
1458 //==============================================================================
1461  const std::string& tableName, TableVersion sourceVersion)
1462 {
1463  getTableByName(tableName)->reset();
1464 
1465  // make sure source version is loaded
1466  // need to load with loose column rules!
1467  TableBase* table =
1468  getVersionedTableByName(tableName, TableVersion(sourceVersion), true);
1469 
1470  // copy from source version to a new temporary version
1471  TableVersion newTemporaryVersion =
1472  table->copyView(table->getView(), TableVersion(), username_);
1473 
1474  // update allTableInfo_ with the new version
1475  allTableInfo_[tableName].versions_.insert(newTemporaryVersion);
1476 
1477  return newTemporaryVersion;
1478 } // end copyViewToCurrentColumns()
1479 
1480 //==============================================================================
1482 void ConfigurationManagerRW::cacheGroupKey(const std::string& groupName,
1483  TableGroupKey key)
1484 {
1485  allGroupInfo_[groupName].keys_.emplace(key);
1486 
1487  // __SS__ << "Now keys are: " << __E__;
1488  // for(auto& key:allGroupInfo_[groupName].keys_)
1489  // ss << "\t" << key << __E__;
1490  // __GEN_COUT__ << ss.str() << __E__;
1491 } // end cacheGroupKey()
1492 
1493 //==============================================================================
1497 const GroupInfo& ConfigurationManagerRW::getGroupInfo(const std::string& groupName)
1498 {
1499  // //NOTE: seems like this filter is taking the long amount of time
1500  // std::set<std::string /*name*/> fullGroupNames =
1501  // theInterface_->getAllTableGroupNames(groupName); //db filter by group name
1502 
1503  // so instead caching ourselves...
1504  auto it = allGroupInfo_.find(groupName);
1505  if(it == allGroupInfo_.end())
1506  {
1507  __SS__ << "Group name '" << groupName
1508  << "' not found in group info! (creating empty info)" << __E__;
1509  __GEN_COUT_WARN__ << ss.str();
1510  //__SS_THROW__;
1511  return allGroupInfo_[groupName];
1512  }
1513  return it->second;
1514 } // end getGroupInfo()
1515 
1516 //==============================================================================
1527  const std::string& groupName,
1528  const std::map<std::string, TableVersion>& groupMemberMap,
1529  const std::map<std::string /*name*/, std::string /*alias*/>& memberTableAliases)
1530 {
1531  // //NOTE: seems like this filter is taking the long amount of time
1532  // std::set<std::string /*name*/> fullGroupNames =
1533  // theInterface_->getAllTableGroupNames(groupName); //db filter bygroup name
1534  // const GroupInfo& groupInfo = getGroupInfo(groupName); // Note this also seems to take too long because requires a pre-cache load!
1535  std::set<TableGroupKey> keys;
1536  { //so instead load keys from special db group cache (this avoids pre-cache filling and avoids long db lookup, unless speed table cache missing for this group)
1537  //attempt to use cache first! (potentially way faster .04 s vs 4 s)
1538  bool cacheFailed = false;
1539  try
1540  {
1541  TableBase localGroupMemberCacheLoader(
1542  true /*special table*/
1543  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),,
1544  TableBase::GROUP_CACHE_PREPEND + groupName);
1545  auto versions = theInterface_->getVersions(&localGroupMemberCacheLoader);
1546  for(const auto& version : versions)
1547  keys.emplace(TableGroupKey(version.version()));
1548  }
1549  catch(...)
1550  {
1551  __COUT__ << "Ignoring cache loading error. Doing full load of keys..."
1552  << __E__;
1553  cacheFailed = true;
1554  }
1555 
1556  if(cacheFailed) //since cache failed, do full load
1557  keys = theInterface_->getKeys(groupName);
1558  }
1559 
1560  __COUTTV__(StringMacros::setToString(keys));
1561 
1562  const unsigned int MAX_DEPTH_TO_CHECK = 20;
1563  unsigned int keyMinToCheck = 0;
1564 
1565  if(keys.size())
1566  keyMinToCheck = keys.rbegin()->key();
1567  if(keyMinToCheck > MAX_DEPTH_TO_CHECK)
1568  {
1569  keyMinToCheck -= MAX_DEPTH_TO_CHECK;
1570  __GEN_COUT__ << "Checking groups back to key... " << keyMinToCheck << __E__;
1571  }
1572  else
1573  {
1574  keyMinToCheck = 0;
1575  __GEN_COUT__ << "Checking all groups." << __E__;
1576  }
1577 
1578  __GEN_COUTTV__(StringMacros::mapToString(groupMemberMap));
1579 
1580  // have min key to check, now loop through and check groups
1581 
1582  const int numOfThreads = PROCESSOR_COUNT / 2;
1583  __GEN_COUT__ << " PROCESSOR_COUNT " << PROCESSOR_COUNT << " ==> " << numOfThreads
1584  << " threads." << __E__;
1585  if(numOfThreads < 2) // no multi-threading
1586  {
1587  std::map<std::string /*name*/, TableVersion /*version*/> compareToMemberMap;
1588  std::map<std::string /*name*/, std::string /*alias*/> compareToMemberTableAliases;
1589  std::map<std::string /*name*/, std::string /*alias*/>*
1590  compareToMemberTableAliasesPtr = nullptr;
1591  if(memberTableAliases.size())
1592  compareToMemberTableAliasesPtr = &compareToMemberTableAliases;
1593 
1594  bool isDifferent;
1595  for(const auto& key : keys)
1596  {
1597  if(key.key() < keyMinToCheck)
1598  continue; // skip keys that are too old
1599 
1600  loadTableGroup(groupName,
1601  key,
1602  false /*doActivate*/,
1603  &compareToMemberMap /*memberMap*/,
1604  0, /*progressBar*/
1605  0, /*accumulatedWarnings*/
1606  0, /*groupComment*/
1607  0, /*groupAuthor*/
1608  0, /*groupCreateTime*/
1609  true /*doNotLoadMember*/,
1610  0 /*groupTypeString*/,
1611  compareToMemberTableAliasesPtr);
1612 
1613  isDifferent = false;
1614  for(auto& memberPair : groupMemberMap)
1615  {
1616  if(memberTableAliases.find(memberPair.first) != memberTableAliases.end())
1617  {
1618  // handle this table as alias, not version
1619  if(compareToMemberTableAliases.find(memberPair.first) ==
1620  compareToMemberTableAliases.end() || // alias is missing
1621  memberTableAliases.at(memberPair.first) !=
1622  compareToMemberTableAliases.at(memberPair.first))
1623  { // then different
1624  isDifferent = true;
1625  break;
1626  }
1627  else
1628  continue;
1629  } // else check if compareTo group is using an alias for table
1630  else if(compareToMemberTableAliases.find(memberPair.first) !=
1631  compareToMemberTableAliases.end())
1632  {
1633  // then different
1634  isDifferent = true;
1635  break;
1636 
1637  } // else handle as table version comparison
1638  else if(compareToMemberMap.find(memberPair.first) ==
1639  compareToMemberMap.end() || // name is missing
1640  memberPair.second !=
1641  compareToMemberMap.at(
1642  memberPair.first)) // or version mismatch
1643  {
1644  // then different
1645  isDifferent = true;
1646  break;
1647  }
1648  }
1649  if(isDifferent)
1650  continue;
1651 
1652  // check member size for exact match
1653  if(groupMemberMap.size() != compareToMemberMap.size())
1654  continue; // different size, so not same (groupMemberMap is a subset of
1655  // memberPairs)
1656 
1657  __GEN_COUT__ << "Found exact match with key: " << key << __E__;
1658  // else found an exact match!
1659  return key;
1660  }
1661  __GEN_COUT__ << "No match found - this group is new!" << __E__;
1662  // if here, then no match found
1663  return TableGroupKey(); // return invalid key
1664  }
1665  else //multi-threading
1666  {
1667  int threadsLaunched = 0;
1668  int foundThreadIndex = 0;
1669  std::atomic<bool> foundIdentical = false;
1670  ots::TableGroupKey identicalKey;
1671  std::mutex threadMutex;
1672 
1673  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
1674  for(int i = 0; i < numOfThreads; ++i)
1675  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
1676 
1677  for(const auto& key : keys)
1678  {
1679  if(foundIdentical)
1680  break;
1681  if(key.key() < keyMinToCheck)
1682  continue; // skip keys that are too old
1683 
1684  if(threadsLaunched >= numOfThreads)
1685  {
1686  //find availableThreadIndex
1687  foundThreadIndex = -1;
1688  while(foundThreadIndex == -1)
1689  {
1690  if(foundIdentical)
1691  break;
1692 
1693  for(int i = 0; i < numOfThreads; ++i)
1694  if(*(threadDone[i]))
1695  {
1696  foundThreadIndex = i;
1697  break;
1698  }
1699  if(foundThreadIndex == -1)
1700  {
1701  __GEN_COUTT__ << "Waiting for available thread..." << __E__;
1702  usleep(10000);
1703  }
1704  } //end thread search loop
1705  threadsLaunched = numOfThreads - 1;
1706  }
1707  if(foundIdentical)
1708  break;
1709 
1710  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex << __E__;
1711  *(threadDone[foundThreadIndex]) = false;
1712 
1713  std::thread(
1714  [](ConfigurationManagerRW* cfgMgr,
1715  std::string theGroupName,
1716  ots::TableGroupKey groupKeyToCompare,
1717  const std::map<std::string, TableVersion>& groupMemberMap,
1718  const std::map<std::string /*name*/, std::string /*alias*/>&
1719  memberTableAliases,
1720  std::atomic<bool>* theFoundIdentical,
1721  ots::TableGroupKey* theIdenticalKey,
1722  std::mutex* theThreadMutex,
1723  std::shared_ptr<std::atomic<bool>> theThreadDone) {
1725  theGroupName,
1726  groupKeyToCompare,
1727  groupMemberMap,
1728  memberTableAliases,
1729  theFoundIdentical,
1730  theIdenticalKey,
1731  theThreadMutex,
1732  theThreadDone);
1733  },
1734  this,
1735  groupName,
1736  key,
1737  groupMemberMap,
1738  memberTableAliases,
1739  &foundIdentical,
1740  &identicalKey,
1741  &threadMutex,
1742  threadDone[foundThreadIndex])
1743  .detach();
1744 
1745  ++threadsLaunched;
1746  ++foundThreadIndex;
1747  } //end group key check thread loop
1748 
1749  //check for all threads done
1750  do
1751  {
1752  foundThreadIndex = -1;
1753  for(int i = 0; i < numOfThreads; ++i)
1754  if(!*(threadDone[i]))
1755  {
1756  foundThreadIndex = i;
1757  break;
1758  }
1759  if(foundThreadIndex != -1)
1760  {
1761  __GEN_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
1762  << __E__;
1763  usleep(10000);
1764  }
1765  } while(foundThreadIndex != -1); //end thread done search loop
1766 
1767  if(foundIdentical)
1768  {
1769  __GEN_COUT__ << "Found exact match with key: " << identicalKey << __E__;
1770  return identicalKey;
1771  }
1772 
1773  // if here, then no match found
1774  return TableGroupKey(); // return invalid key
1775  } //end multi-thread handling
1776 } // end findTableGroup()
1777 
1778 //==============================================================================
1784  TableVersion fillVersion /* = TableVersion()*/)
1785 {
1786  if(fillVersion.isInvalid())
1787  return &groupMetadataTable_;
1788  //else load specified fill version
1789 
1790  //only lock metadata table since it is shared by all group accesses
1791  std::lock_guard<std::mutex> lock(metaDataTableMutex_);
1792 
1793  // clear table
1794  while(groupMetadataTable_.getView().getNumberOfRows())
1795  groupMetadataTable_.getViewP()->deleteRow(0);
1796 
1797  // retrieve metadata from database
1798  try
1799  {
1800  theInterface_->fill(&groupMetadataTable_, fillVersion);
1801  }
1802  catch(const std::runtime_error& e)
1803  {
1804  __GEN_COUT_WARN__ << "Failed to load " << groupMetadataTable_.getTableName()
1805  << "-v" << fillVersion << ". Metadata error: " << e.what()
1806  << __E__;
1807  }
1808  catch(...)
1809  {
1810  __GEN_COUT_WARN__ << "Failed to load " << groupMetadataTable_.getTableName()
1811  << "-v" << fillVersion << ". Ignoring unknown metadata error. "
1812  << __E__;
1813  }
1814 
1815  // check that there is only 1 row
1816  if(groupMetadataTable_.getView().getNumberOfRows() != 1)
1817  {
1818  groupMetadataTable_.print();
1819  __GEN_COUT_ERR__ << "Ignoring that groupMetadataTable_ v" << fillVersion
1820  << " has wrong "
1821  "number of rows!' Must "
1822  "be 1. Going with anonymous defaults."
1823  << __E__;
1824 
1825  // fix metadata table
1826  while(groupMetadataTable_.getViewP()->getNumberOfRows() > 1)
1827  groupMetadataTable_.getViewP()->deleteRow(0);
1828  if(groupMetadataTable_.getViewP()->getNumberOfRows() == 0)
1829  groupMetadataTable_.getViewP()->addRow();
1830  }
1831 
1832  return &groupMetadataTable_;
1833 } // end getMetadataTable()
1834 
1835 //==============================================================================
1843  const std::string& groupName,
1844  std::map<std::string, TableVersion>& groupMembers,
1845  const std::string& groupComment,
1846  std::map<std::string /*table*/, std::string /*alias*/>* groupAliases)
1847 {
1848  // steps:
1849  // determine new group key
1850  // verify group members
1851  // verify groupNameWithKey
1852  // verify store
1853 
1854  if(groupMembers.size() == 0) // do not allow empty groups
1855  {
1856  __SS__ << "Empty group member list. Can not create a group without members!"
1857  << __E__;
1858  __SS_THROW__;
1859  }
1860 
1861  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
1862 
1863  // verify group members
1864  // - use all table info
1865  std::map<std::string, TableInfo> allCfgInfo = getAllTableInfo();
1866  for(auto& memberPair : groupMembers)
1867  {
1868  // check member name
1869  if(allCfgInfo.find(memberPair.first) == allCfgInfo.end())
1870  {
1871  __GEN_COUT_ERR__ << "Group member \"" << memberPair.first
1872  << "\" not found in database!";
1873 
1874  if(groupMetadataTable_.getTableName() == memberPair.first)
1875  {
1876  __GEN_COUT_WARN__
1877  << "Looks like this is the groupMetadataTable_ '"
1878  << TableBase::GROUP_METADATA_TABLE_NAME
1879  << ".' Note that this table is added to the member map when groups "
1880  "are saved."
1881  << "It should not be part of member map when calling this function."
1882  << __E__;
1883  __GEN_COUT__ << "Attempting to recover." << __E__;
1884  groupMembers.erase(groupMembers.find(memberPair.first));
1885  }
1886  else
1887  {
1888  __SS__ << ("Group member not found!") << __E__;
1889  __SS_THROW__;
1890  }
1891  }
1892  // check member version
1893  if(allCfgInfo[memberPair.first].versions_.find(memberPair.second) ==
1894  allCfgInfo[memberPair.first].versions_.end())
1895  {
1896  __SS__ << "Group member \"" << memberPair.first << "\" version \""
1897  << memberPair.second << "\" not found in database!";
1898  __SS_THROW__;
1899  }
1900  } // end verify members
1901 
1902  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
1903 
1904  // verify group aliases
1905  if(groupAliases)
1906  {
1907  for(auto& aliasPair : *groupAliases)
1908  {
1909  // check for alias table in member names
1910  if(groupMembers.find(aliasPair.first) == groupMembers.end())
1911  {
1912  __GEN_COUT_ERR__ << "Group member \"" << aliasPair.first
1913  << "\" not found in group member map!";
1914 
1915  __SS__ << ("Alias table not found in member list!") << __E__;
1916  __SS_THROW__;
1917  }
1918  }
1919  } // end verify group aliases
1920 
1921  TableGroupKey newKey =
1922  TableGroupKey::getNextKey(theInterface_->findLatestGroupKey(groupName));
1923  __GEN_COUT__ << "New Key for group: " << groupName << " found as " << newKey << __E__;
1924  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
1925 
1926  // verify groupNameWithKey and attempt to store
1927  try
1928  {
1929  // save meta data for group; reuse groupMetadataTable_
1930  std::string groupAliasesString = "";
1931  if(groupAliases)
1932  groupAliasesString = StringMacros::mapToString(
1933  *groupAliases, "," /*primary delimeter*/, ":" /*secondary delimeter*/);
1934  __GEN_COUT__ << "Metadata: " << username_ << " " << time(0) << " " << groupComment
1935  << " " << groupAliasesString << __E__;
1936 
1937  // to compensate for unusual errors upstream, make sure the metadata table has one
1938  // row
1939  while(groupMetadataTable_.getViewP()->getNumberOfRows() > 1)
1940  groupMetadataTable_.getViewP()->deleteRow(0);
1941  if(groupMetadataTable_.getViewP()->getNumberOfRows() == 0)
1942  groupMetadataTable_.getViewP()->addRow();
1943 
1944  // columns are uid,comment,author,time
1945  groupMetadataTable_.getViewP()->setValue(
1946  groupAliasesString, 0, ConfigurationManager::METADATA_COL_ALIASES);
1947  groupMetadataTable_.getViewP()->setValue(
1948  groupComment, 0, ConfigurationManager::METADATA_COL_COMMENT);
1949  groupMetadataTable_.getViewP()->setValue(
1950  username_, 0, ConfigurationManager::METADATA_COL_AUTHOR);
1951  groupMetadataTable_.getViewP()->setValue(
1952  time(0), 0, ConfigurationManager::METADATA_COL_TIMESTAMP);
1953 
1954  if(TTEST(2))
1955  {
1956  std::stringstream ss;
1957  groupMetadataTable_.print(ss);
1958  __COUT_MULTI__(2, ss.str());
1959  }
1960 
1961  // save table, and retry on save collision
1962  {
1963  // set version to first available persistent version
1965  theInterface_->findLatestVersion(&groupMetadataTable_));
1966  groupMetadataTable_.getViewP()->setVersion(newVersion);
1967 
1968  uint16_t retries = 0;
1969  while(1)
1970  {
1971  try
1972  {
1973  theInterface_->saveActiveVersion(&groupMetadataTable_);
1974  }
1975  catch(const std::runtime_error& e)
1976  {
1977  __GEN_COUT__ << "Caught runtime_error exception during table save."
1978  << __E__;
1979  if(std::string(e.what()).find("there was a collision") !=
1980  std::string::npos)
1981  {
1982  __GEN_COUT_WARN__
1983  << "There was a collision saving the new table "
1984  << groupMetadataTable_ << "(" << newVersion
1985  << "), trying incremented table version... retries="
1986  << retries << __E__;
1987  if(++retries > 3) //give up
1988  throw;
1989  newVersion = TableVersion::getNextVersion(
1990  newVersion); //increment table version
1991  groupMetadataTable_.getViewP()->setVersion(newVersion);
1992  __GEN_COUT__ << "New version for table: " << groupMetadataTable_
1993  << " found as " << newVersion << __E__;
1994  continue;
1995  }
1996  else
1997  throw;
1998  }
1999 
2000  __GEN_COUT__ << "Created table: " << groupMetadataTable_ << "-v"
2001  << newVersion << __E__;
2002  break;
2003  } //end collission retry loop
2004  }
2005 
2006  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2007  << __E__;
2008 
2009  // force groupMetadataTable_ to be a member for the group
2010  groupMembers[groupMetadataTable_.getTableName()] =
2011  groupMetadataTable_.getViewVersion();
2012 
2013  // save group, and retry on save collision
2014  {
2015  uint16_t retries = 0;
2016  while(1)
2017  {
2018  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2019  << __E__;
2020 
2021  try
2022  {
2023  theInterface_->saveTableGroup(
2024  groupMembers,
2025  TableGroupKey::getFullGroupString(groupName, newKey));
2026  }
2027  catch(const std::runtime_error& e)
2028  {
2029  __GEN_COUT__ << "Caught runtime_error exception during group save."
2030  << __E__;
2031  if(std::string(e.what()).find("there was a collision") !=
2032  std::string::npos)
2033  {
2034  __GEN_COUT_WARN__
2035  << "There was a collision saving the new group " << groupName
2036  << "(" << newKey
2037  << "), trying incremented group key... retries=" << retries
2038  << __E__;
2039  if(++retries > 3) //give up
2040  throw;
2041  newKey = TableGroupKey::getNextKey(newKey); //increment group key
2042  __GEN_COUT__ << "New Key for group: " << groupName << " found as "
2043  << newKey << __E__;
2044  continue;
2045  }
2046  else
2047  throw;
2048  }
2049 
2050  __GEN_COUT__ << "Created table group: " << groupName << "(" << newKey
2051  << ")" << __E__;
2052  break;
2053  } //end collission retry loop
2054  }
2055 
2056  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2057  << __E__;
2058  }
2059  catch(std::runtime_error& e)
2060  {
2061  __GEN_COUT_ERR__ << "Failed to create table group: " << groupName << "(" << newKey
2062  << ")" << __E__;
2063  __GEN_COUT_ERR__ << "\n\n" << e.what() << __E__;
2064  throw;
2065  }
2066  catch(...)
2067  {
2068  __GEN_COUT_ERR__ << "Failed to create table group: " << groupName << ":" << newKey
2069  << __E__;
2070  throw;
2071  }
2072 
2073  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2074 
2075  // store cache of recent groups
2076  cacheGroupKey(groupName, newKey);
2077 
2078  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2079 
2080  // at this point succeeded!
2081  return newKey;
2082 } // end saveNewTableGroup()
2083 
2084 //==============================================================================
2089 {
2090  __GEN_COUT_INFO__ << "Creating new backbone from temporary version "
2091  << temporaryVersion << __E__;
2092 
2093  // find common available temporary version among backbone members
2094  TableVersion newVersion(TableVersion::DEFAULT);
2095  TableVersion retNewVersion;
2096  auto backboneMemberNames = ConfigurationManager::getBackboneMemberNames();
2097  for(auto& name : backboneMemberNames)
2098  {
2099  retNewVersion = ConfigurationManager::getTableByName(name)->getNextVersion();
2100  __GEN_COUT__ << "New version for backbone member (" << name
2101  << "): " << retNewVersion << __E__;
2102  if(retNewVersion > newVersion)
2103  newVersion = retNewVersion;
2104  }
2105 
2106  __GEN_COUT__ << "Common new backbone version found as " << newVersion << __E__;
2107 
2108  // create new views from source temporary version
2109  for(auto& name : backboneMemberNames)
2110  {
2111  // saveNewVersion makes the new version the active version
2112  retNewVersion = getConfigurationInterface()->saveNewVersion(
2113  getTableByName(name), temporaryVersion, newVersion);
2114  if(retNewVersion != newVersion)
2115  {
2116  __SS__ << "Failure! New view requested was " << newVersion
2117  << ". Mismatched new view created: " << retNewVersion << __E__;
2118  __GEN_COUT_ERR__ << ss.str();
2119  __SS_THROW__;
2120  }
2121  }
2122 
2123  return newVersion;
2124 } // end saveNewBackbone()
2125 
2126 //==============================================================================
2132  const std::string& tableName,
2133  TableVersion originalVersion,
2134  bool makeTemporary,
2135  TableBase* table,
2136  TableVersion temporaryModifiedVersion,
2137  bool ignoreDuplicates /*= false*/,
2138  bool lookForEquivalent /*= false*/,
2139  bool* foundEquivalent /*= nullptr*/)
2140 {
2141  bool needToEraseTemporarySource =
2142  (originalVersion.isTemporaryVersion() && !makeTemporary);
2143 
2144  if(foundEquivalent)
2145  *foundEquivalent = false; // initialize
2146 
2147  // check for duplicate tables already in cache, plus highest version numbers not in cache
2148  if(!ignoreDuplicates)
2149  {
2150  __GEN_COUT__ << "Checking for duplicate '" << tableName << "' tables..." << __E__;
2151 
2152  TableVersion duplicateVersion;
2153 
2154  {
2155  //"DEEP" checking
2156  // load into cache 'recent' versions for this table
2157  // 'recent' := those already in cache, plus highest version numbers not in cache
2158  const std::map<std::string, TableInfo>& allTableInfo =
2159  getAllTableInfo(); // do not refresh
2160 
2161  auto versionReverseIterator =
2162  allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator
2163  __GEN_COUT__ << "Filling up '" << tableName << "' cache from "
2164  << table->getNumberOfStoredViews() << " to max count of "
2165  << table->MAX_VIEWS_IN_CACHE << __E__;
2166  for(; table->getNumberOfStoredViews() < table->MAX_VIEWS_IN_CACHE &&
2167  versionReverseIterator != allTableInfo.at(tableName).versions_.rend();
2168  ++versionReverseIterator)
2169  {
2170  __GEN_COUTT__ << "'" << tableName << "' versions in reverse order "
2171  << *versionReverseIterator << __E__;
2172  try
2173  {
2174  getVersionedTableByName(tableName,
2175  *versionReverseIterator); // load to cache
2176  }
2177  catch(const std::runtime_error& e)
2178  {
2179  // ignore error
2180  __COUTT__ << "'" << tableName
2181  << "' version failed to load: " << *versionReverseIterator
2182  << __E__;
2183  }
2184  }
2185  }
2186 
2187  __GEN_COUT__ << "Checking '" << tableName << "' for duplicate..." << __E__;
2188 
2189  duplicateVersion = table->checkForDuplicate(
2190  temporaryModifiedVersion,
2191  (!originalVersion.isTemporaryVersion() && !makeTemporary)
2192  ? TableVersion()
2193  : // if from persistent to persistent, then include original version in search
2194  originalVersion);
2195 
2196  if(lookForEquivalent && !duplicateVersion.isInvalid())
2197  {
2198  // found an equivalent!
2199  __GEN_COUT__ << "Equivalent '" << tableName << "' table found in version v"
2200  << duplicateVersion << __E__;
2201 
2202  // if duplicate version was temporary, do not use
2203  if(duplicateVersion.isTemporaryVersion() && !makeTemporary)
2204  {
2205  __GEN_COUT__ << "Need persistent. Duplicate '" << tableName
2206  << "' version was temporary. "
2207  "Abandoning duplicate."
2208  << __E__;
2209  duplicateVersion = TableVersion(); // set invalid
2210  }
2211  else
2212  {
2213  // erase and return equivalent version
2214 
2215  // erase modified equivalent version
2216  eraseTemporaryVersion(tableName, temporaryModifiedVersion);
2217 
2218  // erase original if needed
2219  if(needToEraseTemporarySource)
2220  eraseTemporaryVersion(tableName, originalVersion);
2221 
2222  if(foundEquivalent)
2223  *foundEquivalent = true;
2224 
2225  __GEN_COUT__ << "\t\t Equivalent '" << tableName
2226  << "' assigned version: " << duplicateVersion << __E__;
2227 
2228  return duplicateVersion;
2229  }
2230  }
2231 
2232  if(!duplicateVersion.isInvalid())
2233  {
2234  __SS__ << "This version of table '" << tableName
2235  << "' is identical to another version currently cached v"
2236  << duplicateVersion << ". No reason to save a duplicate." << __E__;
2237  __GEN_COUT_ERR__ << "\n" << ss.str();
2238 
2239  // delete temporaryModifiedVersion
2240  table->eraseView(temporaryModifiedVersion);
2241  __SS_THROW__;
2242  }
2243 
2244  __GEN_COUT__ << "Check for duplicate '" << tableName << "' tables complete."
2245  << __E__;
2246  }
2247 
2248  if(makeTemporary)
2249  __GEN_COUT__ << "\t\t**************************** Save as temporary '"
2250  << tableName << "' table version" << __E__;
2251  else
2252  __GEN_COUT__ << "\t\t**************************** Save as new '" << tableName
2253  << "' table version" << __E__;
2254 
2255  TableVersion newAssignedVersion =
2256  saveNewTable(tableName, temporaryModifiedVersion, makeTemporary);
2257 
2258  __GEN_COUTTV__(table->getView().getComment());
2259 
2260  if(needToEraseTemporarySource)
2261  eraseTemporaryVersion(tableName, originalVersion);
2262 
2263  __GEN_COUT__ << "\t\t '" << tableName
2264  << "' new assigned version: " << newAssignedVersion << __E__;
2265  return newAssignedVersion;
2266 } // end saveModifiedVersion()
2267 
2268 //==============================================================================
2269 GroupEditStruct::GroupEditStruct(const ConfigurationManager::GroupType& groupType,
2270  ConfigurationManagerRW* cfgMgr)
2271  : groupType_(groupType)
2272  , originalGroupName_(cfgMgr->getActiveGroupName(groupType))
2273  , originalGroupKey_(cfgMgr->getActiveGroupKey(groupType))
2274  , cfgMgr_(cfgMgr)
2275  , mfSubject_(cfgMgr->getUsername())
2276 {
2277  if(originalGroupName_ == "" || originalGroupKey_.isInvalid())
2278  {
2279  __SS__ << "Error! No active group found for type '"
2281  << ".' There must be an active group to edit the group." << __E__ << __E__
2282  << StringMacros::stackTrace() << __E__;
2283  __SS_THROW__;
2284  }
2285 
2286  __GEN_COUT__ << "Extracting Group-Edit Struct for type "
2287  << ConfigurationManager::convertGroupTypeToName(groupType) << __E__;
2288 
2289  std::map<std::string, TableVersion> activeTables = cfgMgr->getActiveVersions();
2290 
2291  const std::set<std::string>& memberNames =
2292  groupType == ConfigurationManager::GroupType::CONTEXT_TYPE
2293  ? ConfigurationManager::getContextMemberNames()
2294  : (groupType == ConfigurationManager::GroupType::BACKBONE_TYPE
2295  ? ConfigurationManager::getBackboneMemberNames()
2296  : (groupType == ConfigurationManager::GroupType::ITERATE_TYPE
2297  ? ConfigurationManager::getIterateMemberNames()
2298  : cfgMgr->getConfigurationMemberNames()));
2299 
2300  for(auto& memberName : memberNames)
2301  try
2302  {
2303  groupMembers_.emplace(
2304  std::make_pair(memberName, activeTables.at(memberName)));
2305  groupTables_.emplace(std::make_pair(
2306  memberName,
2307  TableEditStruct(memberName, cfgMgr))); // Table ready for editing!
2308  }
2309  catch(...)
2310  {
2311  __GEN_COUTV__(StringMacros::mapToString(activeTables));
2312  __SS__ << "Error! Could not find group member table '" << memberName
2313  << "' for group type '"
2315  << ".' All group members must be present to create the group editing "
2316  "structure."
2317  << __E__ << __E__ << StringMacros::stackTrace() << __E__;
2318  __SS_THROW__;
2319  }
2320 
2321 } // end GroupEditStruct constructor()
2322 
2323 //==============================================================================
2324 GroupEditStruct::~GroupEditStruct()
2325 {
2326  __GEN_COUT__ << "GroupEditStruct from editing '" << originalGroupName_ << "("
2327  << originalGroupKey_ << ")' Destructing..." << __E__;
2328  dropChanges();
2329  __GEN_COUT__ << "GroupEditStruct from editing '" << originalGroupName_ << "("
2330  << originalGroupKey_ << ")' Desctructed." << __E__;
2331 } // end GroupEditStruct destructor()
2332 
2333 //==============================================================================
2336  bool markModified /*= false*/)
2337 {
2338  auto it = groupTables_.find(tableName);
2339  if(it == groupTables_.end())
2340  {
2341  if(groupType_ == ConfigurationManager::GroupType::CONFIGURATION_TYPE &&
2342  markModified)
2343  {
2344  __GEN_COUT__ << "Table '" << tableName
2345  << "' not found in configuration table members from editing '"
2346  << originalGroupName_ << "(" << originalGroupKey_ << ")..."
2347  << " Attempting to add it!" << __E__;
2348 
2349  // emplace returns pair<object,bool wasAdded>
2350  auto newIt = groupTables_.emplace(std::make_pair(
2351  tableName,
2352  TableEditStruct(tableName, cfgMgr_))); // Table ready for editing!
2353  if(newIt.second)
2354  {
2355  newIt.first->second.modified_ =
2356  markModified; // could indicate 'dirty' immediately in user code, which will cause a save of table
2357  groupMembers_.emplace(
2358  std::make_pair(tableName, newIt.first->second.temporaryVersion_));
2359  return newIt.first->second;
2360  }
2361  __GEN_COUT_ERR__ << "Failed to emplace new table..." << __E__;
2362  }
2363 
2364  __SS__ << "Table '" << tableName << "' not found in table members from editing '"
2365  << originalGroupName_ << "(" << originalGroupKey_ << ")!'" << __E__;
2366  __SS_THROW__;
2367  }
2368  it->second.modified_ =
2369  markModified; // could indicate 'dirty' immediately in user code, which will cause a save of table
2370  return it->second;
2371 } // end getTableEditStruct()
2372 
2373 //==============================================================================
2374 void GroupEditStruct::dropChanges()
2375 {
2376  __GEN_COUT__ << "Dropping unsaved changes from editing '" << originalGroupName_ << "("
2377  << originalGroupKey_ << ")'..." << __E__;
2378 
2379  ConfigurationManagerRW* cfgMgr = cfgMgr_;
2380 
2381  // drop all temporary versions
2382  for(auto& groupTable : groupTables_)
2383  if(groupTable.second
2384  .createdTemporaryVersion_) // if temporary version created here
2385  {
2386  // erase with proper version management
2387  cfgMgr->eraseTemporaryVersion(groupTable.second.tableName_,
2388  groupTable.second.temporaryVersion_);
2389  groupTable.second.createdTemporaryVersion_ = false;
2390  groupTable.second.modified_ = false;
2391  }
2392 
2393  __GEN_COUT__ << "Unsaved changes dropped from editing '" << originalGroupName_ << "("
2394  << originalGroupKey_ << ").'" << __E__;
2395 } // end GroupEditStruct::dropChanges()
2396 
2397 //==============================================================================
2398 void GroupEditStruct::saveChanges(const std::string& groupNameToSave,
2399  TableGroupKey& newGroupKey,
2400  bool* foundEquivalentGroupKey /*= nullptr*/,
2401  bool activateNewGroup /*= false*/,
2402  bool updateGroupAliases /*= false*/,
2403  bool updateTableAliases /*= false*/,
2404  TableGroupKey* newBackboneKey /*= nullptr*/,
2405  bool* foundEquivalentBackboneKey /*= nullptr*/,
2406  std::string* accumulatedWarnings /*= nullptr*/)
2407 {
2408  __GEN_COUT__ << "Saving changes..." << __E__;
2409 
2410  newGroupKey = TableGroupKey(); // invalidate reference parameter
2411  if(newBackboneKey)
2412  *newBackboneKey = TableGroupKey(); // invalidate reference parameter
2413  if(foundEquivalentBackboneKey)
2414  *foundEquivalentBackboneKey = false; // clear to start
2415  ConfigurationManagerRW* cfgMgr = cfgMgr_;
2416 
2417  // save all temporary modified versions
2418  bool anyTableNew = false;
2419  for(auto& groupTable : groupTables_)
2420  {
2421  if(!groupTable.second.modified_)
2422  continue; // skip if not modified
2423 
2424  __GEN_COUT__ << "Original version is " << groupTable.second.tableName_ << "-v"
2425  << groupTable.second.originalVersion_ << __E__;
2426 
2427  groupMembers_.at(groupTable.first) = cfgMgr->saveModifiedVersion(
2428  groupTable.second.tableName_,
2429  groupTable.second.originalVersion_,
2430  true /*make temporary*/,
2431  groupTable.second.table_,
2432  groupTable.second.temporaryVersion_,
2433  true /*ignoreDuplicates*/); // make temporary version to save persistent version properly
2434 
2435  __GEN_COUT__ << "Temporary target version is " << groupTable.second.tableName_
2436  << "-v" << groupMembers_.at(groupTable.first) << "-v"
2437  << groupTable.second.temporaryVersion_ << __E__;
2438 
2439  groupMembers_.at(groupTable.first) = cfgMgr->saveModifiedVersion(
2440  groupTable.second.tableName_,
2441  groupTable.second.originalVersion_,
2442  false /*make temporary*/,
2443  groupTable.second.table_,
2444  groupTable.second.temporaryVersion_,
2445  false /*ignoreDuplicates*/,
2446  true /*lookForEquivalent*/); // save persistent version properly
2447 
2448  if(groupTable.second.originalVersion_ != groupMembers_.at(groupTable.first))
2449  {
2450  anyTableNew = true;
2451  __GEN_COUT__ << "Final NEW target version is " << groupTable.second.tableName_
2452  << "-v" << groupMembers_.at(groupTable.first) << __E__;
2453  }
2454  else
2455  __GEN_COUT__ << "Final target version is " << groupTable.second.tableName_
2456  << "-v" << groupMembers_.at(groupTable.first) << __E__;
2457 
2458  groupTable.second.modified_ = false; // clear modified flag
2459  groupTable.second.createdTemporaryVersion_ = false; // modified version is gone
2460  } // loop through table edit structs
2461 
2462  for(auto& table : groupMembers_)
2463  {
2464  __GEN_COUT__ << table.first << " v" << table.second << __E__;
2465  }
2466 
2467  if(!anyTableNew) //then could be duplicate group
2468  {
2469  __GEN_COUT__ << "Checking for duplicate groups..." << __E__;
2470  newGroupKey = cfgMgr->findTableGroup(groupNameToSave, groupMembers_);
2471  }
2472  else
2473  __GEN_COUT__ << "New table found, so no need to check duplicate groups." << __E__;
2474 
2475  if(!newGroupKey.isInvalid())
2476  {
2477  __GEN_COUT__ << "Found equivalent group key (" << newGroupKey << ") for "
2478  << groupNameToSave << "." << __E__;
2479  if(foundEquivalentGroupKey)
2480  *foundEquivalentGroupKey = true;
2481  }
2482  else
2483  {
2484  newGroupKey = cfgMgr->saveNewTableGroup(groupNameToSave, groupMembers_);
2485  __GEN_COUT__ << "Saved new Context group key (" << newGroupKey << ") for "
2486  << groupNameToSave << "." << __E__;
2487  }
2488 
2489  bool groupAliasChange = false;
2490  bool tableAliasChange = false;
2491 
2492  if(groupType_ !=
2493  ConfigurationManager::GroupType::
2494  BACKBONE_TYPE) //if not backbone group save, consider changing aliases (not if it is backbone group, tables not necessarily active yet, which causes error in GroupEditStruct backboneGroupEdit)
2495  {
2496  GroupEditStruct backboneGroupEdit(ConfigurationManager::GroupType::BACKBONE_TYPE,
2497  cfgMgr);
2498 
2499  if(groupType_ != ConfigurationManager::GroupType::BACKBONE_TYPE &&
2500  updateGroupAliases)
2501  {
2502  // check group aliases ... a la
2503  // ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML
2504 
2505  TableEditStruct& groupAliasTable = backboneGroupEdit.getTableEditStruct(
2506  ConfigurationManager::GROUP_ALIASES_TABLE_NAME, true /*markModified*/);
2507  TableView* tableView = groupAliasTable.tableView_;
2508 
2509  // unsigned int col;
2510  unsigned int row = 0;
2511 
2512  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
2513  cfgMgr->getNode(ConfigurationManager::GROUP_ALIASES_TABLE_NAME)
2514  .getChildren();
2515  std::string groupName, groupKey;
2516  for(auto& aliasNodePair : aliasNodePairs)
2517  {
2518  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
2519  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
2520 
2521  __GEN_COUT__ << "Group Alias: " << aliasNodePair.first << " => "
2522  << groupName << "(" << groupKey << "); row=" << row << __E__;
2523 
2524  if(groupName == originalGroupName_ &&
2525  TableGroupKey(groupKey) == originalGroupKey_)
2526  {
2527  __GEN_COUT__ << "Found alias! Changing group key from ("
2528  << originalGroupKey_ << ") to (" << newGroupKey << ")"
2529  << __E__;
2530 
2531  groupAliasChange = true;
2532 
2533  tableView->setValueAsString(
2534  newGroupKey.toString(), row, tableView->findCol("GroupKey"));
2535  }
2536 
2537  ++row;
2538  }
2539 
2540  if(groupAliasChange)
2541  {
2542  std::stringstream ss;
2543  tableView->print(ss);
2544  __GEN_COUT__ << ss.str();
2545  }
2546  } // end updateGroupAliases handling
2547 
2548  if(groupType_ != ConfigurationManager::GroupType::BACKBONE_TYPE &&
2549  updateTableAliases)
2550  {
2551  // update all table version aliases
2552  TableView* tableView =
2553  backboneGroupEdit
2554  .getTableEditStruct(ConfigurationManager::VERSION_ALIASES_TABLE_NAME,
2555  true /*markModified*/)
2556  .tableView_;
2557 
2558  for(auto& groupTable : groupTables_)
2559  {
2560  if(groupTable.second.originalVersion_ ==
2561  groupMembers_.at(groupTable.second.tableName_))
2562  continue; // skip if no change
2563 
2564  __GEN_COUT__ << "Checking alias... original version is "
2565  << groupTable.second.tableName_ << "-v"
2566  << groupTable.second.originalVersion_
2567  << " and new version is v"
2568  << groupMembers_.at(groupTable.second.tableName_) << __E__;
2569 
2570  // unsigned int col;
2571  unsigned int row = 0;
2572 
2573  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
2574  cfgMgr->getNode(ConfigurationManager::VERSION_ALIASES_TABLE_NAME)
2575  .getChildren();
2576  std::string tableName, tableVersion;
2577  for(auto& aliasNodePair : aliasNodePairs)
2578  {
2579  tableName =
2580  aliasNodePair.second.getNode("TableName").getValueAsString();
2581  tableVersion =
2582  aliasNodePair.second.getNode("Version").getValueAsString();
2583 
2584  __GEN_COUT__ << "Table Alias: " << aliasNodePair.first << " => "
2585  << tableName << "-v" << tableVersion << "" << __E__;
2586 
2587  if(tableName == groupTable.second.tableName_ &&
2588  TableVersion(tableVersion) == groupTable.second.originalVersion_)
2589  {
2590  __GEN_COUT__ << "Found alias! Changing icon table version alias."
2591  << __E__;
2592 
2593  tableAliasChange = true;
2594 
2595  tableView->setValueAsString(
2596  groupMembers_.at(groupTable.second.tableName_).toString(),
2597  row,
2598  tableView->findCol("Version"));
2599  }
2600 
2601  ++row;
2602  }
2603  }
2604 
2605  if(tableAliasChange)
2606  {
2607  std::stringstream ss;
2608  tableView->print(ss);
2609  __GEN_COUT__ << ss.str();
2610  }
2611  } // end updateTableAliases handling
2612 
2613  TableGroupKey localNewBackboneKey;
2614  // if backbone modified, save group and activate it
2615  if(groupAliasChange || tableAliasChange)
2616  {
2617  for(auto& table : backboneGroupEdit.groupMembers_)
2618  {
2619  __GEN_COUT__ << table.first << " v" << table.second << __E__;
2620  }
2621  backboneGroupEdit.saveChanges(
2622  backboneGroupEdit.originalGroupName_,
2623  localNewBackboneKey,
2624  foundEquivalentBackboneKey ? foundEquivalentBackboneKey : nullptr);
2625 
2626  if(newBackboneKey)
2627  *newBackboneKey = localNewBackboneKey;
2628  }
2629 
2630  // acquire all active groups and ignore errors, so that activateTableGroup does not
2631  // erase other active groups
2632  {
2633  __GEN_COUT__
2634  << "Restoring active table groups, before activating new groups..."
2635  << __E__;
2636 
2637  std::string localAccumulatedWarnings;
2638  cfgMgr->restoreActiveTableGroups(
2639  false /*throwErrors*/,
2640  "" /*pathToActiveGroupsFile*/,
2641  ConfigurationManager::LoadGroupType::
2642  ALL_TYPES /*onlyLoadIfBackboneOrContext*/,
2643  &localAccumulatedWarnings);
2644  }
2645 
2646  // activate new groups
2647  if(!localNewBackboneKey.isInvalid())
2648  cfgMgr->activateTableGroup(
2649  backboneGroupEdit.originalGroupName_,
2650  localNewBackboneKey,
2651  accumulatedWarnings ? accumulatedWarnings : nullptr);
2652 
2653  } //end non-backbone save type handling
2654  else //is backbone save type
2655  {
2656  // acquire all active groups and ignore errors, so that activateTableGroup does not
2657  // erase other active groups
2658  {
2659  __GEN_COUT__
2660  << "Restoring active table groups, before activating new groups..."
2661  << __E__;
2662 
2663  std::string localAccumulatedWarnings;
2664  cfgMgr->restoreActiveTableGroups(
2665  false /*throwErrors*/,
2666  "" /*pathToActiveGroupsFile*/,
2667  ConfigurationManager::LoadGroupType::
2668  ALL_TYPES /*onlyLoadIfBackboneOrContext*/,
2669  &localAccumulatedWarnings);
2670  }
2671  } //end backbone save type handling
2672 
2673  if(activateNewGroup)
2674  cfgMgr->activateTableGroup(groupNameToSave,
2675  newGroupKey,
2676  accumulatedWarnings ? accumulatedWarnings : nullptr);
2677 
2678  __GEN_COUT__ << "Changes saved." << __E__;
2679 } // end GroupEditStruct::saveChanges()
2680 
2681 //==============================================================================
2684 {
2685  if(1)
2686  return; //if 0 to debug
2687  __GEN_COUTV__(runTimeSeconds());
2688 
2689  std::string accumulatedWarningsStr;
2690  std::string* accumulatedWarnings = &accumulatedWarningsStr;
2691 
2692  // get Group Info too!
2693  try
2694  {
2695  std::string debugGroupName = "Mu2eHWEmulatorContext";
2696 
2697  //final solution demo of getting latest group key:
2698  {
2699  TableGroupKey latestGroupKey =
2700  theInterface_->findLatestGroupKey(debugGroupName);
2701  __GEN_COUTV__(latestGroupKey);
2702 
2703  __GEN_COUTV__(runTimeSeconds());
2704  }
2705 
2706  //steps to do time comparison for getting last group key and table key:
2707 
2708  // build allGroupInfo_ for the ConfigurationManagerRW
2709 
2710  std::set<std::string /*name*/> tableGroups =
2711  theInterface_->getAllTableGroupNames();
2712  __GEN_COUT__ << "Number of Groups: " << tableGroups.size() << __E__;
2713 
2714  __GEN_COUTV__(runTimeSeconds());
2715  // return;
2716 
2717  TableGroupKey key;
2718  std::string name;
2719  for(const auto& fullName : tableGroups)
2720  {
2721  TableGroupKey::getGroupNameAndKey(fullName, name, key);
2722  cacheGroupKey(name, key);
2723 
2724  if(name == debugGroupName)
2725  {
2726  __GEN_COUTV__(key);
2727  }
2728  }
2729  __GEN_COUTV__(runTimeSeconds());
2730 
2731  std::set<std::string /*name*/> tableNames = theInterface_->getAllTableNames();
2732  __GEN_COUT__ << "Number of Tables: " << tableNames.size() << __E__;
2733 
2734  __GEN_COUTV__(runTimeSeconds());
2735 
2736  for(const auto& fullName : tableNames)
2737  {
2738  if(fullName.find(debugGroupName) != std::string::npos)
2739  {
2740  __GEN_COUTV__(fullName);
2741  }
2742  }
2743  __GEN_COUTV__(runTimeSeconds());
2744 
2745  TableGroupKey latestGroupKey = theInterface_->findLatestGroupKey(debugGroupName);
2746  __GEN_COUTV__(latestGroupKey);
2747 
2748  __GEN_COUTV__(runTimeSeconds());
2749 
2750  TableBase localGroupMemberCacheSaver(
2751  true /*special table*/
2752  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),
2753  TableBase::GROUP_CACHE_PREPEND + debugGroupName);
2754  TableVersion lastestGroupCacheKey =
2755  theInterface_->findLatestVersion(&localGroupMemberCacheSaver);
2756  __GEN_COUTV__(lastestGroupCacheKey);
2757 
2758  __GEN_COUTV__(runTimeSeconds());
2759 
2760  //test a group save that already exists
2761  try
2762  {
2763  TableGroupKey groupKey(int(0));
2764  __GEN_COUT__ << "Testing group save of pre-existing " << debugGroupName << "("
2765  << groupKey << ")" << __E__;
2766  std::map<std::string, TableVersion> groupMembers;
2767  groupMembers["DesktopIconTable"] = TableVersion(123);
2768  theInterface_->saveTableGroup(
2769  groupMembers,
2770  TableGroupKey::getFullGroupString(debugGroupName, groupKey));
2771  }
2772  catch(...)
2773  {
2774  __GEN_COUT__ << "Exception during group save." << __E__;
2775  }
2776  __GEN_COUTV__(runTimeSeconds());
2777 
2778  //test a group save that does not already exists
2779  try
2780  {
2781  std::string debugGroupName = "testGroupSave";
2782  TableGroupKey groupKey(int(1));
2783  __GEN_COUT__ << "Testing group save of non-existing " << debugGroupName << "("
2784  << groupKey << ")" << __E__;
2785  std::map<std::string, TableVersion> groupMembers;
2786  groupMembers["DesktopIconTable"] = TableVersion(123);
2787  groupMembers["MessageFacilityTable"] = TableVersion(7);
2788  theInterface_->saveTableGroup(
2789  groupMembers,
2790  TableGroupKey::getFullGroupString(debugGroupName, groupKey));
2791  }
2792  catch(...)
2793  {
2794  __GEN_COUT__ << "Exception during new group save." << __E__;
2795  }
2796  __GEN_COUTV__(runTimeSeconds());
2797 
2798  //test a table save that already exists
2799  {
2800  std::string documentNameToLoad = "XDAQApplicationTable";
2801  TableVersion documentVersionToLoad(134);
2802 
2803  __GEN_COUT__ << "Testing table save of pre-existing " << documentNameToLoad
2804  << __E__;
2805 
2806  { //load to prove it exists
2807  TableBase localDocLoader(
2808  documentNameToLoad); //can not use special table when filling
2809  localDocLoader.changeVersionAndActivateView(
2810  localDocLoader.createTemporaryView(), documentVersionToLoad);
2811  theInterface_->fill(&localDocLoader, documentVersionToLoad);
2812  __SS__;
2813  localDocLoader.print(ss);
2814  __GEN_COUTV__(ss.str());
2815  }
2816  __GEN_COUTV__(runTimeSeconds());
2817 
2818  try
2819  { //attempt to save over existing version
2820  std::string documentNameToSave = documentNameToLoad;
2821  TableBase
2822  localDocSaver( //true /*special table*/, //special table only allows 1 view in cache and does not load schema (which is perfect for this check),
2823  documentNameToSave); //can not use special table when filling
2824  localDocSaver.changeVersionAndActivateView(
2825  localDocSaver.createTemporaryView(), documentVersionToLoad);
2826 
2827  std::string json = "{ }";
2828  localDocSaver.getViewP()->setCustomStorageData(json);
2829 
2830  __COUTT__ << "Saving JSON string: "
2831  << localDocSaver.getViewP()->getCustomStorageData() << __E__;
2832 
2833  __COUTT__ << "Saving JSON doc as "
2834  << localDocSaver.getView().getTableName() << "("
2835  << localDocSaver.getView().getVersion().toString() << ")"
2836  << __E__;
2837 
2838  // save to db, and do not allow overwrite
2839  theInterface_->saveActiveVersion(&localDocSaver, false /* overwrite */);
2840  }
2841  catch(...)
2842  {
2843  __GEN_COUT__ << "Exception during table save." << __E__;
2844  }
2845  __GEN_COUTV__(runTimeSeconds());
2846 
2847  { //load to prove it exists
2848  TableBase localDocLoader(
2849  documentNameToLoad); //can not use special table when filling
2850  localDocLoader.changeVersionAndActivateView(
2851  localDocLoader.createTemporaryView(), documentVersionToLoad);
2852  theInterface_->fill(&localDocLoader, documentVersionToLoad);
2853  __SS__;
2854  localDocLoader.print(ss);
2855  __GEN_COUTV__(ss.str());
2856  }
2857  __GEN_COUTV__(runTimeSeconds());
2858  }
2859  __GEN_COUTV__(runTimeSeconds());
2860 
2861  //test a table save that does not already exist
2862  {
2863  std::string documentNameToLoad = "MessageFacilityTable";
2864  TableVersion documentVersionToLoad(7);
2865  TableBase localDocLoader(
2866  documentNameToLoad); //can not use special table when filling
2867 
2868  __GEN_COUT__ << "Testing table save of non-existing " << documentNameToLoad
2869  << __E__;
2870 
2871  { //load to prove it exists
2872  localDocLoader.changeVersionAndActivateView(
2873  localDocLoader.createTemporaryView(), documentVersionToLoad);
2874  theInterface_->fill(&localDocLoader, documentVersionToLoad);
2875  __SS__;
2876  localDocLoader.print(ss);
2877  __GEN_COUTV__(ss.str());
2878  __GEN_COUTV__(runTimeSeconds());
2879  }
2880  __GEN_COUTV__(runTimeSeconds());
2881 
2882  try
2883  { //attempt to save new version
2884 
2885  // modify it
2887  theInterface_->findLatestVersion(&localDocLoader));
2888  localDocLoader.getViewP()->setVersion(newVersion);
2889 
2890  __GEN_COUTT__ << "Saving new table as "
2891  << localDocLoader.getView().getTableName() << "("
2892  << localDocLoader.getView().getVersion().toString() << ")"
2893  << __E__;
2894 
2895  localDocLoader.getViewP()->setValueAsString(
2896  "10.226.9.17", 0, 4); //modify value that is 10.226.9.16
2897 
2898  __SS__;
2899  localDocLoader.print(ss);
2900  __GEN_COUTV__(ss.str());
2901 
2902  // save to db, and do not allow overwrite
2903  // theInterface_->saveActiveVersion(&localDocLoader, false /* overwrite */);
2904  }
2905  catch(...)
2906  {
2907  __GEN_COUT__ << "Exception during new table save." << __E__;
2908  }
2909  __GEN_COUTV__(runTimeSeconds());
2910  }
2911  __GEN_COUTV__(runTimeSeconds());
2912  return;
2913 
2914  // for each group get member map & comment, author, time, and type for latest key
2915  for(auto& groupInfo : allGroupInfo_)
2916  {
2917  try
2918  {
2919  loadTableGroup(groupInfo.first /*groupName*/,
2920  groupInfo.second.getLatestKey(),
2921  false /*doActivate*/,
2922  &groupInfo.second.latestKeyMemberMap_ /*groupMembers*/,
2923  0 /*progressBar*/,
2924  0 /*accumulateErrors*/,
2925  &groupInfo.second.latestKeyGroupComment_,
2926  &groupInfo.second.latestKeyGroupAuthor_,
2927  &groupInfo.second.latestKeyGroupCreationTime_,
2928  true /*doNotLoadMember*/,
2929  &groupInfo.second.latestKeyGroupTypeString_);
2930  }
2931  catch(const std::runtime_error& e)
2932  {
2933  __GEN_COUT_WARN__
2934  << "Error occurred loading latest group info into cache for '"
2935  << groupInfo.first << "(" << groupInfo.second.getLatestKey()
2936  << ")': \n"
2937  << e.what() << __E__;
2938 
2939  groupInfo.second.latestKeyGroupComment_ =
2940  ConfigurationManager::UNKNOWN_INFO;
2941  groupInfo.second.latestKeyGroupAuthor_ =
2942  ConfigurationManager::UNKNOWN_INFO;
2943  groupInfo.second.latestKeyGroupCreationTime_ =
2944  ConfigurationManager::UNKNOWN_TIME;
2945  groupInfo.second.latestKeyGroupTypeString_ =
2946  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
2947  groupInfo.second.latestKeyMemberMap_ = {};
2948  }
2949  catch(...)
2950  {
2951  __GEN_COUT_WARN__
2952  << "Error occurred loading latest group info into cache for '"
2953  << groupInfo.first << "(" << groupInfo.second.getLatestKey()
2954  << ")'..." << __E__;
2955  groupInfo.second.latestKeyGroupComment_ =
2956  ConfigurationManager::UNKNOWN_INFO;
2957  groupInfo.second.latestKeyGroupAuthor_ =
2958  ConfigurationManager::UNKNOWN_INFO;
2959  groupInfo.second.latestKeyGroupCreationTime_ =
2960  ConfigurationManager::UNKNOWN_TIME;
2961  groupInfo.second.latestKeyGroupTypeString_ =
2962  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
2963  groupInfo.second.latestKeyMemberMap_ = {};
2964  }
2965  } // end group info loop
2966  __GEN_COUTV__(runTimeSeconds());
2967  } // end get group info
2968  catch(const std::runtime_error& e)
2969  {
2970  __SS__ << "A fatal error occurred reading the info for all table groups. Error: "
2971  << e.what() << __E__;
2972  __GEN_COUT_ERR__ << "\n" << ss.str();
2973  if(accumulatedWarnings)
2974  *accumulatedWarnings += ss.str();
2975  else
2976  throw;
2977  }
2978  catch(...)
2979  {
2980  __SS__ << "An unknown fatal error occurred reading the info for all table groups."
2981  << __E__;
2982  __GEN_COUT_ERR__ << "\n" << ss.str();
2983  if(accumulatedWarnings)
2984  *accumulatedWarnings += ss.str();
2985  else
2986  throw;
2987  } //end catch
2988 
2989  __GEN_COUT__ << "testXDAQContext() end runTimeSeconds()=" << runTimeSeconds()
2990  << __E__;
2991  return;
2992 
2993  try
2994  {
2995  __GEN_COUT__ << "Loading table..." << __E__;
2996  loadTableGroup("FETest", TableGroupKey(2)); // Context_1
2997  ConfigurationTree t = getNode("/FETable/DEFAULT/FrontEndType");
2998 
2999  std::string v;
3000 
3001  __GEN_COUT__ << __E__;
3002  t.getValue(v);
3003  __GEN_COUT__ << "Value: " << v << __E__;
3004  __GEN_COUT__ << "Value index: " << t.getValue<int>() << __E__;
3005 
3006  return;
3007  }
3008  catch(...)
3009  {
3010  __GEN_COUT__ << "Failed to load table..." << __E__;
3011  }
3012 } //end testXDAQContext()
TableVersion saveNewVersion(TableBase *configuration, TableVersion temporaryVersion, TableVersion newVersion=TableVersion())
TableVersion saveNewTable(const std::string &tableName, TableVersion temporaryVersion=TableVersion(), bool makeTemporary=false)
modifiers of generic TableBase
TableGroupKey findTableGroup(const std::string &groupName, const std::map< std::string, TableVersion > &groupMembers, const std::map< std::string, std::string > &groupAliases=std::map< std::string, std::string >())
void testXDAQContext(void)
for debugging
TableVersion saveNewBackbone(TableVersion temporaryVersion=TableVersion())
const std::map< std::string, TableInfo > & getAllTableInfo(bool refresh=false, std::string *accumulatedWarnings=0, const std::string &errorFilterName="", bool getGroupKeys=false, bool getGroupInfo=false, bool initializeActiveGroups=false)
TableVersion copyViewToCurrentColumns(const std::string &tableName, TableVersion sourceVersion)
copyViewToCurrentColumns
TableGroupKey saveNewTableGroup(const std::string &groupName, std::map< std::string, TableVersion > &groupMembers, const std::string &groupComment=TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT, std::map< std::string, std::string > *groupAliases=0)
modifiers of a table group based on alias, e.g. "Physics"
void activateTableGroup(const std::string &tableGroupName, TableGroupKey tableGroupKey, std::string *accumulatedTreeErrors=0, std::string *groupTypeString=0)
modifiers of table groups
TableBase * getMetadataTable(TableVersion fillVersion=TableVersion())
created for use in otsdaq_flatten_system_aliases and otsdaq_export_system_aliases,...
TableVersion saveModifiedVersion(const std::string &tableName, TableVersion originalVersion, bool makeTemporary, TableBase *config, TableVersion temporaryModifiedVersion, bool ignoreDuplicates=false, bool lookForEquivalent=false, bool *foundEquivalent=nullptr)
TableVersion createTemporaryBackboneView(TableVersion sourceViewVersion=TableVersion())
-1, from MockUp, else from valid backbone view version
const GroupInfo & getGroupInfo(const std::string &groupName)
public group cache handling
void clearCachedVersions(const std::string &tableName)
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
void eraseTemporaryVersion(const std::string &tableName, TableVersion targetVersion=TableVersion())
TableBase * getVersionedTableByName(const std::string &tableName, TableVersion version, bool looseColumnMatching=false, std::string *accumulatedErrors=0, bool getRawData=false)
static void compareTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKeyToCompare, const std::map< std::string, TableVersion > &groupMemberMap, const std::map< std::string, std::string > &memberTableAliases, std::atomic< bool > *theFoundIdentical, ots::TableGroupKey *theIdenticalKey, std::mutex *theThreadMutex, std::shared_ptr< std::atomic< bool >> theThreadDone)
static void loadTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKey, std::shared_ptr< ots::GroupInfo > theGroupInfo, std::shared_ptr< std::atomic< bool >> theThreadDone)
loadTableGroupThread()
static void loadTableInfoThread(ConfigurationManagerRW *cfgMgr, std::string tableName, TableBase *existingTable, std::shared_ptr< ots::TableInfo > tableInfo, std::shared_ptr< std::atomic< bool >> threadDone)
loadTableInfoThread()
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType onlyLoadIfBackboneOrContext=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false)
static const unsigned int PROCESSOR_COUNT
Static members.
static const std::string & convertGroupTypeToName(const ConfigurationManager::GroupType &groupTypeId)
void restoreActiveTableGroups(bool throwErrors=false, const std::string &pathToActiveGroupsFile="", ConfigurationManager::LoadGroupType onlyLoadIfBackboneOrContext=ConfigurationManager::LoadGroupType::ALL_TYPES, std::string *accumulatedWarnings=0)
std::map< std::string, TableVersion > getActiveVersions(void) const
getActiveVersions
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
void init(std::string *accumulatedErrors=0, bool initForWriteAccess=false, std::string *accumulatedWarnings=0)
void destroyTableGroup(const std::string &theGroup="", bool onlyDeactivate=false)
static const std::string ACTIVE_GROUPS_FILENAME
added env check for otsdaq_flatten_active_to_version to function
const TableBase * getTableByName(const std::string &configurationName) const
void getValue(T &value) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
TableVersion createTemporaryView(TableVersion sourceViewVersion=TableVersion(), TableVersion destTemporaryViewVersion=TableVersion::getNextTemporaryVersion())
source of -1, from MockUp, else from valid view version
Definition: TableBase.cc:1734
void trimTemporary(TableVersion targetVersion=TableVersion())
Definition: TableBase.cc:362
unsigned int getNumberOfStoredViews(void) const
Definition: TableBase.cc:856
TableVersion checkForDuplicate(TableVersion needleVersion, TableVersion ignoreVersion=TableVersion()) const
Definition: TableBase.cc:404
TableView * getTemporaryView(TableVersion temporaryVersion)
Definition: TableBase.cc:1845
const unsigned int MAX_VIEWS_IN_CACHE
Definition: TableBase.h:22
TableVersion getNextVersion(void) const
Definition: TableBase.cc:1821
TableVersion copyView(const TableView &sourceView, TableVersion destinationVersion, const std::string &author, bool looseColumnMatching=false)
Definition: TableBase.cc:1675
void print(std::ostream &out=std::cout) const
always prints active view
Definition: TableBase.cc:277
TableVersion getNextTemporaryVersion(void) const
Definition: TableBase.cc:1798
void trimCache(unsigned int trimSize=-1)
Definition: TableBase.cc:322
std::string toString(void) const
toString
static TableGroupKey getNextKey(const TableGroupKey &key=TableGroupKey())
static void getGroupNameAndKey(const std::string &fullGroupString, std::string &groupName, TableGroupKey &key)
requires fullGroupString created as name + "_v" + key + ""
bool isInvalid(void) const
isInvalid
static std::string getFullGroupString(const std::string &groupName, const TableGroupKey &key, const std::string &preKey="_v", const std::string &postKey="")
std::string toString(void) const
toString
Definition: TableVersion.cc:33
bool isInvalid(void) const
isInvalid
static TableVersion getNextVersion(const TableVersion &version=TableVersion())
bool isScratchVersion(void) const
bool isTemporaryVersion(void) const
static TableVersion getNextTemporaryVersion(const TableVersion &version=TableVersion())
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1090
void setVersion(const T &version)
< in included .icc source
const std::string & getCustomStorageData(void) const
Getters.
Definition: TableView.h:71
int fillFromJSON(const std::string &json)
Definition: TableView.cc:2401
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1952
void setCustomStorageData(const std::string &storageData)
Definition: TableView.h:168
TableEditStruct & getTableEditStruct(const std::string &tableName, bool markModified=false)
Note: if markModified, and table not found in group, this function will try to add it to group.
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static std::string stackTrace(void)