Main Page · Modules · All Classes · Class Hierarchy
MSSessionStorage.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2016 Csaba Kertész (csaba.kertesz@gmail.com)
5  *
6  * AiBO+ is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * AiBO+ is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "MSSessionStorage.hpp"
23 
24 #include "MSContext.hpp"
25 #include "MSData.hpp"
26 #include "MSDefs.hpp"
27 #include "MSElement.hpp"
28 #include "MSSessionStorageInterface.hpp"
29 
30 #include <MCDefs.hpp>
31 #include <MCLog.hpp>
32 
33 #include <qdatetime.h>
34 #include <qdir.h>
35 #include <qmap.h>
36 #include <qsqlerror.h>
37 #include <qsqlquery.h>
38 #include <qsqldatabase.h>
39 #include <qvariant.h>
40 #include <qvector.h>
41 
42 #include <boost/make_shared.hpp>
43 
44 static MSSessionStorage* SessionSingleton = nullptr;
45 
48 {
49  MSContext::RegisterTimer("StoragePlayback", "Storage playback time");
50  MSContext::RegisterTimer("StorageRecord", "Storage record time");
51 }
52 
53 
54 MSSessionStorage::~MSSessionStorage()
55 {
56  // Finalize and remove the storages
57  if (!SessionStorages.isEmpty())
58  {
60  }
61 }
62 
63 
65 {
66  if (!SessionSingleton)
67  {
68  SessionSingleton = new MSSessionStorage();
69  }
70  return SessionSingleton;
71 }
72 
73 
75 {
76  return PlaybackPath;
77 }
78 
79 
80 void MSSessionStorage::SetPlaybackPath(const QString& path)
81 {
82  if (PlaybackDatabase.get() || PlaybackStorage.get())
83  {
84  MC_WARNING("A database or storage has been opened for playback already.");
85  return;
86  }
87  PlaybackPath = path;
88  PlayedHeartBeats = 0;
89 }
90 
91 
93 {
94  return RecordPath;
95 }
96 
97 
98 void MSSessionStorage::SetRecordPath(const QString& record_path)
99 {
100  if (RecordDatabase.get() || RecordStorage.get())
101  {
102  MC_WARNING("A database or storage has been opened for record already.");
103  return;
104  }
105  RecordPath = record_path;
106 }
107 
108 
110 {
111  return !SessionStorages.isEmpty();
112 }
113 
114 
115 
117 {
118  return PlayedHeartBeats;
119 }
120 
121 
122 void MSSessionStorage::BeatEnd(int beat_id)
123 {
124  Q_UNUSED(beat_id);
125  if (PlaybackDatabase.get())
126  {
127  MSContext::StartTimer("StoragePlayback");
129  MSContext::StopTimer("StoragePlayback");
130  }
131  if (PlaybackStorage.get())
132  {
133  MSContext::StartTimer("StoragePlayback");
135  MSContext::StopTimer("StoragePlayback");
136  }
137  if (RecordDatabase.get())
138  {
139  MSContext::StartTimer("StorageRecord");
141  MSContext::StopTimer("StorageRecord");
142  }
143  if (RecordStorage.get())
144  {
145  MSContext::StartTimer("StorageRecord");
147  MSContext::StopTimer("StorageRecord");
148  }
149 }
150 
151 
153 {
154  if (SessionStorages.contains(&storage))
155  {
156  MC_WARNING("This storage is registered already!");
157  return;
158  }
159  SessionStorages.append(QPointer<MSSessionStorageInterface>(&storage));
160 }
161 
162 
164 {
165  if (SessionStorages.isEmpty())
166  {
167  MC_WARNING("No storage have been loaded yet!");
168  return;
169  }
170  Finalize();
171  // Clean up the storages
172  SessionStorages.clear();
173 }
174 
175 
177 {
178  MSContext::ResetTimer("StoragePlayback");
179  MSContext::ResetTimer("StorageRecord");
180 }
181 
182 
184 {
185  if (element.GetElementID() == -1)
186  {
187  MC_WARNING("Object does not have valid ID!");
188  return;
189  }
190  if (!RecordDatabase.get() && !RecordStorage.get() && !PlaybackDatabase.get() && !PlaybackStorage.get())
191  {
192  MC_WARNING("Nor a recorded neither a new database has not been opened yet.");
193  return;
194  }
195  if (RegElements.contains(&element))
196  {
197  MC_WARNING("The element instance has been registered already.");
198  return;
199  }
200  if (element.GetType() != MS::Source)
201  {
202  MC_WARNING("The class of the element is not a source class.");
203  return;
204  }
205  QString ClassName = element.GetClass();
206 
207  if (PlaybackDatabase.get())
208  {
209  QString SqlQuery1;
210  QString SqlQuery2;
211 
212  SqlQuery1 = "SELECT DISTINCT ID FROM "+ClassName.toUpper()+
213  " WHERE ID = "+QString::number(element.GetElementID())+';';
214  SqlQuery2 = "SELECT DISTINCT ID FROM "+ClassName.toUpper()+';';
215  if (ExecuteSqlQuery(*PlaybackDatabase, SqlQuery1).isEmpty())
216  {
217  QString Result = ExecuteSqlQuery(*PlaybackDatabase, SqlQuery2);
218  bool ConversionResult = true;
219  int CandidateID = Result.toInt(&ConversionResult);
220 
221  // Only one candidate ID can be re-mapped to one source object
222  if (Result.contains(','))
223  {
224  QString Message = "The source element ("+ClassName+") is not found in the database with the ID "+
225  QString::number(element.GetElementID())+" and there are more candidate IDs ("+
226  Result+"), thus re-mapping cannot be done reliably.";
227 
228  MC_ERROR(qPrintable(Message));
229  }
230  if (!ConversionResult)
231  {
232  QString Message = "The element class "+ClassName+" with ID "+QString::number(element.GetElementID())+
233  " is not found in the played session and no candidate ID available.";
234 
235  MC_ERROR(qPrintable(Message));
236  }
237  if (element.GetElementID() != CandidateID)
238  {
239  RemappedIDs.insert(element.GetElementID(), CandidateID);
240  QString Message = "The source element "+ClassName+" with ID "+QString::number(element.GetElementID())+
241  " has been re-mapped to the candidate ID "+Result+'.';
242 
243  MC_WARNING(qPrintable(Message));
244  }
245  }
246  if (!TableColumns.contains(ClassName))
247  {
248  QString ResultStr = ExecuteSqlQuery(*PlaybackDatabase, QString("PRAGMA table_info(%1);").arg(ClassName));
249  QStringList Columns;
250  bool DataEnd = false;
251  int i1 = 3;
252 
253  // Create the cache of the columns
254  while (!DataEnd)
255  {
256  QString Column = ResultStr.section(',', 1+i1*6, 1+i1*6);
257 
258  if (!Column.isEmpty())
259  {
260  Columns += Column;
261  } else {
262  DataEnd = true;
263  }
264  i1++;
265  }
266  // TODO: Race conditions? Empty database?
267  if (PlayedHeartBeats == 0)
268  {
269  QString QuesryStr = QString("SELECT BEATID FROM %1 ORDER BY BEATID DESC LIMIT 1;").arg(ClassName);
270  QString ResultStr = ExecuteSqlQuery(*PlaybackDatabase, QuesryStr);
271 
272  PlayedHeartBeats = ResultStr.toInt();
273  }
274  if (!Columns.isEmpty())
275  {
276  TableColumns[ClassName] = Columns;
277  }
278  }
279  }
280  if (PlaybackStorage.get())
281  {
282  MC::DataContainerSPtr ElementsContainer = PlaybackStorage->GetContainer("SourceElements");
283 
284  if (!ElementsContainer.get())
285  {
286  MC_ERROR("No SourceElements container in the session storage in playback mode");
287  }
288  MCDataItemBase* IDsPtr = ElementsContainer->GetData("IDs");
289  MCDataItemBase* ElementsPtr = ElementsContainer->GetData("Elements");
290 
291  if (!IDsPtr || !ElementsPtr)
292  {
293  MC_ERROR("No IDs or Elements items in SourceElements container in the session storage in playback mode");
294  }
295  MC::StringList* IDs = reinterpret_cast<MC::StringList*>(IDsPtr->DataPtr);
296  MC::StringList* Elements = reinterpret_cast<MC::StringList*>(ElementsPtr->DataPtr);
297  int ElementsInStorage = MCItemCountInContainer(*Elements, ClassName.toStdString());
298 
299  if (ElementsInStorage == 0)
300  {
301  MC_WARNING("Can't register %s element because it is not in the playback storage",
302  qPrintable(ClassName));
303  return;
304  }
305  // Only one candidate ID can be re-mapped to one source object
306  if (ElementsInStorage > 1)
307  {
308  QString Message = "The source element ("+ClassName+") is not found in the storage with the ID "+
309  QString::number(element.GetElementID())+" and there are more candidate IDs ("+
310  QString::number(ElementsInStorage)+"), thus re-mapping cannot be done reliably.";
311 
312  MC_ERROR(qPrintable(Message));
313  }
314  bool ConversionResult = true;
315  std::string Result = (*IDs)[MCItemIndicesInContainer(*Elements, ClassName.toStdString())[0]];
316  int CandidateID = QString::fromStdString(Result).toInt(&ConversionResult);
317 
318  if (!ConversionResult)
319  {
320  QString Message = "The element class "+ClassName+" with ID "+QString::number(element.GetElementID())+
321  " is not found in the played session storage and no candidate ID available.";
322 
323  MC_ERROR(qPrintable(Message));
324  }
325  // Use the candidate ID for remapping
326  if (element.GetElementID() != CandidateID)
327  {
328  RemappedIDs.insert(element.GetElementID(), CandidateID);
329  QString Message = "The source element "+ClassName+" with ID "+QString::number(element.GetElementID())+
330  " has been re-mapped to the candidate ID "+Result.c_str()+'.';
331 
332  MC_WARNING(qPrintable(Message));
333  }
334  if (PlayedHeartBeats == 0)
335  {
336  void* HeartBeatsPtr = ElementsContainer->GetData("HeartBeats")->DataPtr;
337 
338  if (HeartBeatsPtr)
339  PlayedHeartBeats = *reinterpret_cast<int*>(HeartBeatsPtr);
340  }
341  }
342  if (RecordDatabase.get())
343  {
344  QString QueryStr = QString("INSERT INTO SOURCE_ELEMENTS VALUES(%1, '%2');").
345  arg(QString::number(element.GetElementID()), ClassName);
346 
347  MC_LOG("New element into the db: %s", qPrintable(QueryStr));
348  ExecuteSqlQuery(*RecordDatabase, QueryStr);
349 
350  QString TempStr = RecordPath;
351 
352  TempStr += '/';
353  TempStr += QString::number(element.GetElementID());
354 
355  MCCreatePath(TempStr);
356 
357  // Keep the list of registered class names up-to-date
358  if (!ElementClassNames.contains(ClassName))
359  {
360  ElementClassNames += ClassName;
361  CreateTableForAClass(element);
362  }
363  }
364 
365  if (RecordStorage.get())
366  {
367  MC::DataContainerSPtr ElementContainer = RecordStorage->GetContainer("SourceElements");
368 
369  if (!ElementContainer.get())
370  {
371  MC::StringList TempList;
372  int TempInt = 0;
373 
374  ElementContainer = RecordStorage->CreateContainer("SourceElements");
375  ElementContainer->AddData(*new MCDataItem<MC::StringList>(TempList, "IDs", true));
376  ElementContainer->AddData(*new MCDataItem<MC::StringList>(TempList, "Elements", true));
377  ElementContainer->AddData(*new MCDataItem<int>(TempInt, "HeartBeats", true));
378  }
379  void* IDsPtr = ElementContainer->GetData("IDs")->DataPtr;
380  MC::StringList* IDs = reinterpret_cast<MC::StringList*>(IDsPtr);
381  void* ElementsPtr = ElementContainer->GetData("Elements")->DataPtr;
382  MC::StringList* Elements = reinterpret_cast<MC::StringList*>(ElementsPtr);
383 
384  IDs->push_back(MCToStr(element.GetElementID()));
385  Elements->push_back(ClassName.toStdString());
386  MC_LOG("New element into the storage: %s", qPrintable(ClassName));
387  QString TempStr = RecordPath;
388 
389  TempStr += '/';
390  TempStr += QString::number(element.GetElementID());
391  MCCreatePath(TempStr);
392  // Keep the list of registered class names up-to-date
393  if (!ElementClassNames.contains(ClassName))
394  {
395  ElementClassNames += ClassName;
397  }
398  }
399  // Register the element instance
400  RegElements.append(&element);
401 }
402 
403 
405 {
406  if (RecordDatabase.get())
407  {
408  MC_WARNING("A database has been opened for record already.");
409  return;
410  }
411  if (RecordPath.isEmpty())
412  {
413  MC_WARNING("Record path has not been specified.");
414  return;
415  }
417  // TODO: Remove Sqlite support?
418 // CreateRecordDatabase();
420 }
421 
422 
424 {
425  // Open a recorded session
426  QString StorageName = PlaybackPath+"/Session.bdb";
427 
428  if (QFile(StorageName).exists())
429  {
431  } else {
433  }
434 }
435 
436 
438 {
439  // Finalize playing the session
440  if (!PlaybackPath.isEmpty())
441  {
442  if (PlaybackDatabase.get())
443  {
445  }
446  if (PlaybackStorage.get())
447  {
449  }
450  SetPlaybackPath("");
451  PlayedHeartBeats = 0;
452  }
453  // Finalize recording the session
454  if (!RecordPath.isEmpty())
455  {
456  if (RecordDatabase.get())
457  {
459  }
460  if (RecordStorage.get())
461  {
463  }
464  SetRecordPath("");
465  }
466  // Clean up the storages
467  for (int i = 0; i < SessionStorages.size(); ++i)
468  {
469  if (!SessionStorages[i].isNull())
470  SessionStorages[i].data()->Finalize();
471  }
472  // Unregister the element instances
473  RegElements.clear();
474  RemappedIDs.clear();
475  // Delete the registered class names
476  ElementClassNames.clear();
477 }
478 
479 
480 QString MSSessionStorage::ConstructFileName(int element_id, int beat_id, const QString& base_path,
481  const QString& output_name,
482  const MSSessionStorageInterface::StorageDataType& storage_type) const
483 {
484  if (output_name.isEmpty())
485  {
486  MC_WARNING("Empty output name is not valid.");
487  return QString();
488  }
489  if (base_path.isEmpty())
490  {
491  MC_WARNING("Empty base path is not valid.");
492  return QString();
493  }
494  if (storage_type == MSSessionStorageInterface::sd_Unknown)
495  {
496  MC_WARNING("Unknown storage interface type is not valid.");
497  return QString();
498  }
499  QString BinaryFileName = base_path;
500 
501  BinaryFileName += QDir::separator();
502  BinaryFileName += QString::number(element_id);
503  BinaryFileName += QDir::separator();
504  BinaryFileName += output_name.toUpper();
505 
506  // Add suffix for the file name because it will be one file per heart beat.
507  // Note: Video and audio streams go to one file per element.
508  if (storage_type == MSSessionStorageInterface::sd_Binary)
509  {
510  BinaryFileName += '_';
511  BinaryFileName += QString::number(beat_id);
512  }
513  BinaryFileName += '.';
514  return BinaryFileName;
515 }
516 
517 
519 {
520  if (!QSqlDatabase::drivers().contains("QSQLITE"))
521  {
522  MC_ERROR("Please install or compile the Qt's SQL driver for SQLite, it is required for AiBO+.");
523  }
524  QString DatabaseName = RecordPath;
525 
526  DatabaseName += "/Session.db";
527  RecordDatabase.reset(new QSqlDatabase());
528  *RecordDatabase = QSqlDatabase::addDatabase("QSQLITE", "RecordSession");
529  RecordDatabase->setDatabaseName(DatabaseName);
530  if (!RecordDatabase->open())
531  {
532  MC_ERROR("Sqlite error: %s", qPrintable(RecordDatabase->lastError().text()));
533  }
534  // Switch of the journal file to avoid high performance overhead
535  ExecuteSqlQuery(*RecordDatabase, "PRAGMA journal_mode = OFF;");
536 #if defined(__MINGW32__) || defined(__MINGW64__)
537  // Start one big transaction to avoid the high performance overhead under MinGW/Windows
538  // On the other hand, the whole database can be lost in a nasty crash
539  RecordDatabase->transaction();
540 #endif
541  ExecuteSqlQuery(*RecordDatabase, "CREATE TABLE IF NOT EXISTS SOURCE_ELEMENTS(ID INTEGER, CLASSNAME TEXT);");
542 }
543 
544 
546 {
547  if (!RecordDatabase.get())
548  {
549  MC_WARNING("Database has not been created for record yet.");
550  return;
551  }
552 
553  QStringList OutputList = element.GetOutputList();
554  QString TableHeader;
555  bool TypeFound = false;
556  QString ClassName = element.GetClass();
557 
558  TableHeader = QString("CREATE TABLE IF NOT EXISTS %1 (ID INTEGER, BEATID INTEGER, OUTPUTS TEXT").
559  arg(ClassName.toUpper());
560 
561  // Do not create a table because there is no output data
562  if (OutputList.isEmpty())
563  return;
564 
565  // Process the possible output data to store in the database
566  for (int i = 0; i < OutputList.size(); ++i)
567  {
568  if (OutputList[i].isEmpty())
569  {
570  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
571  i, qPrintable(ClassName));
572  continue;
573  }
574  if (OutputList[i].contains(","))
575  {
576  MC_ERROR("The %d. parameter contains prohibited COMMA in the output list of the element class: %s",
577  i, qPrintable(ClassName));
578  }
579  QString OutputName = OutputList[i].section(':', 0, 0);
580  QString OutputType = OutputList[i].section(':', 1, 1);
581 
582  if (OutputName.isEmpty() || OutputType.isEmpty())
583  {
584  MC_ERROR("An output parameter has wrong syntax for class %s. "
585  "The string \'%s\' must follow the 'name:type' format.",
586  qPrintable(ClassName), qPrintable(OutputList[i]));
587  }
588  // Get the appropriate storage
589  MSSessionStorageInterface* Storage = GetStorage(OutputType);
590 
591  if (!Storage)
592  {
593  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
594  }
595 
596  MSData* OutputData = element.GetOutputData(OutputList[i]);
597 
598  if (!OutputData)
599  {
600  MC_ERROR("Output data %s cannot be fetched from %s.", qPrintable(OutputList[i]), qPrintable(ClassName));
601  }
602  if (OutputData->GetVariableMap().isEmpty())
603  {
604  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
605 
606  TypeFound = true;
607  if (StorageType == MSSessionStorageInterface::sd_Unknown)
608  {
609  MC_ERROR("Type %s cannot be translated to an Sql type.", qPrintable(OutputType));
610  }
611  if (StorageType == MSSessionStorageInterface::sd_Binary ||
613  {
614  continue;
615  }
616  TableHeader += ", ";
617  TableHeader += OutputName.toUpper();
618  TableHeader += ' ';
619 
620  if (StorageType == MSSessionStorageInterface::sd_Text)
621  {
622  TableHeader += "TEXT";
623  } else
624  if (StorageType == MSSessionStorageInterface::sd_Integer)
625  {
626  TableHeader += "INTEGER";
627  } else
628  if (StorageType == MSSessionStorageInterface::sd_Real)
629  {
630  TableHeader += "REAL";
631  }
632  } else {
633  TypeFound = true;
634  // Process the structure
635  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
636  {
637  if (iter.key().isEmpty())
638  {
639  MC_ERROR("A parameter is empty in the field list of the type: %s", qPrintable(OutputType));
640  }
641  QString FieldName = iter.key().section(':', 0, 0);
642  QString FieldType = iter.key().section(':', 1, 1);
643 
644  if (FieldName.isEmpty() || FieldType.isEmpty())
645  {
646  MC_ERROR("A field has wrong syntax for type %s. The string \'%s\' must follow the 'name:type' format.",
647  qPrintable(OutputType), qPrintable(iter.key()));
648  }
649 
650  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
651 
652  if (StorageType == MSSessionStorageInterface::sd_Unknown)
653  {
654  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type.",
655  qPrintable(FieldType), qPrintable(OutputType));
656  }
657  if (StorageType == MSSessionStorageInterface::sd_Binary ||
659  {
660  continue;
661  }
662  TableHeader += ", "+OutputName.toUpper()+'_'+FieldName.toUpper()+' ';
663 
664  if (StorageType == MSSessionStorageInterface::sd_Text)
665  {
666  TableHeader += "TEXT";
667  }
668  if (StorageType == MSSessionStorageInterface::sd_Integer)
669  {
670  TableHeader += "INTEGER";
671  }
672  if (StorageType == MSSessionStorageInterface::sd_Real)
673  {
674  TableHeader += "REAL";
675  }
676  } // End of iterating through a structure
677  } // End of processing a structure
678  } // End of output list processing
679 
680  // Do not create a table because there is no output data
681  if (!TypeFound)
682  {
683  MC_ERROR("No output type to store from a source element?!?");
684  }
685  TableHeader += ");";
686  ExecuteSqlQuery(*RecordDatabase, TableHeader);
687 }
688 
689 
691 {
692  if (!PlaybackDatabase.get())
693  {
694  MC_WARNING("Database has not been opened for play yet.");
695  return;
696  }
697  // Load the recorded states of the sources
698  for (int i = 0; i < RegElements.size(); ++i)
699  {
700  QStringList OutputList = RegElements[i]->GetOutputList();
701  QString ClassName = RegElements[i]->GetClass();
702  int ElementID = RegElements[i]->GetElementID();
703  int BeatID = RegElements[i]->GetBeatID();
704  QString QueryPart2;
705 
706  // Continue if there is no output data
707  if (OutputList.isEmpty())
708  {
709  continue;
710  }
711  // Re-mapping the source element ID to the candidate ID
712  if (RemappedIDs.contains(ElementID))
713  {
714  ElementID = RemappedIDs[ElementID];
715  }
716  // Construct the second part of the SQL queries
717  QueryPart2 = "FROM "+ClassName.toUpper()+" WHERE ID = "+QString::number(ElementID)+
718  " AND BEATID = "+QString::number(BeatID)+';';
719  // Extract the actual outputs from the database
720  RegElements[i]->CurrentOutputList = ExecuteSqlQuery(*PlaybackDatabase, "SELECT OUTPUTS "+QueryPart2).split(',');
721 
722  // Process the output data to load from the database
723  for (int i1 = 0; i1 < OutputList.size(); ++i1)
724  {
725  QString QueryPart1;
726 
727  if (OutputList[i1].isEmpty())
728  {
729  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
730  i1, qPrintable(ClassName));
731  continue;
732  }
733 
734  QString OutputName = OutputList[i1].section(':', 0, 0);
735  QString OutputType = OutputList[i1].section(':', 1, 1);
736 
737  // The output is not available in this heart beat
738  if (!RegElements[i]->CurrentOutputList.contains(OutputList[i1]))
739  continue;
740 
741  if (OutputName.isEmpty() || OutputType.isEmpty())
742  {
743  MC_ERROR("An output has wrong syntax for class %s. The string \'%s\' must follow the 'name:type' format.",
744  qPrintable(ClassName), qPrintable(OutputList[i1]));
745  }
746  // Get the appropriate storage
747  MSSessionStorageInterface* Storage = GetStorage(OutputType);
748 
749  if (!Storage)
750  {
751  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
752  }
753  // Extract the type fields
754  MSData* OutputData = RegElements[i]->GetOutputData(OutputList[i1]);
755 
756  if (!OutputData)
757  {
758  MC_ERROR("Output data %s cannot be fetched from %s.", qPrintable(OutputList[i1]), qPrintable(ClassName));
759  }
760  // It is a simple type
761  if (OutputData->GetVariableMap().isEmpty())
762  {
763  // Extract the output data
764  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
765 
766  if (StorageType == MSSessionStorageInterface::sd_Unknown)
767  {
768  MC_ERROR("Type %s cannot be translated to a storage type. Implementation is missing "
769  "in the registered storages.", qPrintable(OutputType));
770  }
771  if (StorageType == MSSessionStorageInterface::sd_Text ||
772  StorageType == MSSessionStorageInterface::sd_Integer ||
773  StorageType == MSSessionStorageInterface::sd_Real)
774  {
775  // Let's continue if the column does not exist in the database
776  if (!TableColumns[ClassName].contains(OutputName.toUpper()))
777  continue;
778 
779  QueryPart1 = "SELECT "+OutputName.toUpper()+' '+QueryPart2;
780  QString ResultStr = ExecuteSqlQuery(*PlaybackDatabase, QueryPart1);
781 
782  if (!Storage->ConvertSqlStringToData(OutputType, ResultStr, OutputData))
783  {
784  MC_ERROR("Type %s cannot be extracted from the Sql string. Implementation is missing "
785  "in the storages in MSSessionStorageInterface::ConvertSqlStringToData().",
786  qPrintable(OutputType));
787  }
788  continue;
789  }
790  if (StorageType == MSSessionStorageInterface::sd_Binary ||
792  {
793  QString FileName = ConstructFileName(ElementID, BeatID, PlaybackPath, OutputName, StorageType);
794 
795  if (!Storage->LoadBinaryDataFromFile(FileName, OutputType, StorageType, OutputData))
796  {
797  MC_ERROR("Unable to load type %s as binary. Implementation is missing in the storages in "
798  "MSSessionStorageInterface::LoadBinaryDataFromFile().", qPrintable(OutputType));
799  }
800  continue;
801  }
802  } else {
803  // Cache of the field types and data to improve the performance
804  QList<QString> FieldTypeList;
805  QList<void*> FieldDataList;
806 
807  QueryPart1 = "SELECT ";
808  // Process the structure
809  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
810  {
811  // Check the variable
812  if (iter.key().isEmpty())
813  {
814  MC_ERROR("A parameter is empty in the field list of the type: %s",
815  qPrintable(OutputType));
816  }
817  // Check the variable data
818  if (!iter.value())
819  {
820  MC_ERROR("Field %s in the type %s cannot be extracted.",
821  qPrintable(iter.key()), qPrintable(OutputType));
822  }
823  QString FieldName = iter.key().section(':', 0, 0);
824  QString FieldType = iter.key().section(':', 1, 1);
825 
826  if (FieldName.isEmpty() || FieldType.isEmpty())
827  {
828  MC_ERROR("A field has wrong syntax for type %s. "
829  "The string \'%s\' must follow the 'name:type' format.",
830  qPrintable(OutputType), qPrintable(iter.key()));
831  }
832 
833  // Get the storage type
834  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
835 
836  if (StorageType == MSSessionStorageInterface::sd_Unknown)
837  {
838  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type."
839  "Implementation is missing in the storages in MSSessionStorageInterface::FindStorageType().",
840  qPrintable(FieldType), qPrintable(OutputType));
841  }
842  if (StorageType == MSSessionStorageInterface::sd_Text ||
843  StorageType == MSSessionStorageInterface::sd_Integer ||
844  StorageType == MSSessionStorageInterface::sd_Real)
845  {
846  // Let's continue if the column does not exist in the database
847  if (!TableColumns[ClassName].contains(OutputName.toUpper()+'_'+FieldName.toUpper()))
848  continue;
849 
850  QueryPart1 += OutputName.toUpper()+'_'+FieldName.toUpper()+',';
851  FieldTypeList += FieldType;
852  FieldDataList += iter.value();
853  continue;
854  }
855  if (StorageType == MSSessionStorageInterface::sd_Binary ||
857  {
858  QString IDStr = OutputName.toUpper()+'_'+FieldName.toUpper();
859  QString FileName = ConstructFileName(ElementID, BeatID, PlaybackPath, IDStr, StorageType);
860 
861  if (!Storage->LoadBinaryDataFromFile(FileName, FieldType, StorageType, iter.value()))
862  {
863  MC_ERROR("Unable to load type %s as binary. Implementation is missing in the storages in "
864  "MSSessionStorageInterface::LoadBinaryDataFromFile().", qPrintable(FieldType));
865  }
866  continue;
867  } // End of storage if {}
868  } // End of structure processing for {}
869 
870  // Execute the query of the needed fields in a more efficient way at one query
871  QueryPart1.remove(QueryPart1.length()-1, 1);
872  QueryPart1 += ' '+QueryPart2;
873  QStringList Results = ExecuteSqlQuery(*PlaybackDatabase, QueryPart1).split(',');
874 
875  // Process the queried fields
876  for (int i3 = 0; i3 < FieldTypeList.size(); ++i3)
877  {
878  if (!Storage->ConvertSqlStringToData(FieldTypeList[i3], Results[i3], FieldDataList[i3]))
879  {
880  MC_ERROR("Type %s cannot be extracted from the Sql string. Implementation is missing in "
881  "the storages in MSSessionStorageInterface::ConvertSqlStringToData().",
882  qPrintable(FieldTypeList[i3]));
883  }
884  } // End of the processing the cache query {}
885  } // End of structure if {}
886  } // End of output list for {}
887  } // End of elements for {}
888 
889  // End of database signal
890  if (RegElements.size() > 0 && RegElements[0]->GetBeatID() == PlayedHeartBeats)
891  {
892  MC_LOG("End of played session database");
893  Q_EMIT(EndOfDatabase());
894  }
895 }
896 
897 
899 {
900  if (!RecordDatabase.get())
901  {
902  MC_WARNING("Database has not been created for record yet.");
903  return;
904  }
905 
906  for (int i = 0; i < RegElements.size(); ++i)
907  {
908  QStringList OutputList = RegElements[i]->GetOutputList();
909  QString InsertIDStr;
910  QString InsertValueStr;
911  QString ClassName = RegElements[i]->GetClass();
912  int ElementID = RegElements[i]->GetElementID();
913  int BeatID = RegElements[i]->GetBeatID();
914 
915  // Continue if there is no output data
916  if (OutputList.isEmpty())
917  {
918  continue;
919  }
920 
921  InsertIDStr = QString("INSERT INTO %1 (ID, BEATID, OUTPUTS").arg(ClassName.toUpper());
922  InsertValueStr = QString("VALUES (%1, %2, '%3'").arg(QString::number(ElementID), QString::number(BeatID),
923  RegElements[i]->CurrentOutputList.join(","));
924 
925  // Process the possible output data to store in the database
926  for (int i1 = 0; i1 < OutputList.size(); ++i1)
927  {
928  if (OutputList[i1].isEmpty())
929  {
930  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
931  i1, qPrintable(ClassName));
932  continue;
933  }
934  QString OutputName = OutputList[i1].section(':', 0, 0);
935  QString OutputType = OutputList[i1].section(':', 1, 1);
936 
937  // The output is not available in this heart beat
938  if (!RegElements[i]->CurrentOutputList.contains(OutputList[i1]))
939  {
940  continue;
941  }
942  if (OutputName.isEmpty() || OutputType.isEmpty())
943  {
944  MC_ERROR("An output has wrong syntax for class %s. The string \'%s\' must follow the 'name:type' format.",
945  qPrintable(ClassName), qPrintable(OutputList[i1]));
946  }
947 
948  // Get the appropriate storage
949  MSSessionStorageInterface* Storage = GetStorage(OutputType);
950 
951  if (!Storage)
952  {
953  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
954  }
955 
956  // Extract the output data
957  MSData* OutputData = RegElements[i]->GetOutputData(OutputList[i1]);
958 
959  // No output data, let's continue.
960  if (!OutputData)
961  {
962  continue;
963  }
964  // It is a simple type
965  if (OutputData->GetVariableMap().isEmpty())
966  {
967  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
968 
969  if (StorageType == MSSessionStorageInterface::sd_Unknown)
970  {
971  MC_ERROR("Type %s cannot be translated to a storage type. Implementation is missing "
972  "in the storages in MSSessionStorageInterface::FindStorageType().",
973  qPrintable(OutputType));
974  }
975  if (StorageType == MSSessionStorageInterface::sd_Text ||
976  StorageType == MSSessionStorageInterface::sd_Integer ||
977  StorageType == MSSessionStorageInterface::sd_Real)
978  {
979  QString SqlValue = Storage->ConvertDataToSqlString(OutputType, OutputData);
980 
981  if (SqlValue.isEmpty())
982  {
983  MC_ERROR("Type %s cannot be converted to an Sql type. Implementation is missing "
984  "in the storages in MSSessionStorageInterface::ConvertDataToSqlString().",
985  qPrintable(OutputType));
986  } else {
987  InsertIDStr += ", "+OutputName.toUpper();
988  InsertValueStr += ", \""+SqlValue+"\"";
989  }
990  continue;
991  }
992  if (StorageType == MSSessionStorageInterface::sd_Binary ||
994  {
995  QString FileName = ConstructFileName(ElementID, BeatID, RecordPath, OutputName, StorageType);
996 
997  if (!Storage->SaveBinaryDataToFile(FileName, OutputType, StorageType, OutputData))
998  {
999  MC_ERROR("Unable to save type %s as binary. Implementation is missing "
1000  "in the storages in MSSessionStorageInterface::SaveBinaryDataToFile().",
1001  qPrintable(OutputType));
1002  }
1003  continue;
1004  }
1005  } else {
1006  // Process the structure
1007  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
1008  {
1009  // Check the variable
1010  if (iter.key().isEmpty())
1011  {
1012  MC_ERROR("A parameter is empty in the field list of the type: %s",
1013  qPrintable(OutputType));
1014  }
1015  // Check the variable data
1016  if (!iter.value())
1017  {
1018  MC_ERROR("Field %s in the type %s cannot be extracted.",
1019  qPrintable(iter.key()), qPrintable(OutputType));
1020  }
1021 
1022  QString FieldName = iter.key().section(':', 0, 0);
1023  QString FieldType = iter.key().section(':', 1, 1);
1024 
1025  if (FieldName.isEmpty() || FieldType.isEmpty())
1026  {
1027  MC_ERROR("A field has wrong syntax for type %s. "
1028  "The string \'%s\' must follow the 'name:type' format.",
1029  qPrintable(OutputType), qPrintable(iter.key()));
1030  }
1031 
1032  // Process the output data
1033  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
1034 
1035  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1036  {
1037  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type."
1038  "Implementation is missing in the storages in MSSessionStorageInterface::FindStorageType().",
1039  qPrintable(FieldType), qPrintable(OutputType));
1040  }
1041  if (StorageType == MSSessionStorageInterface::sd_Text ||
1042  StorageType == MSSessionStorageInterface::sd_Integer ||
1043  StorageType == MSSessionStorageInterface::sd_Real)
1044  {
1045  QString SqlValue = Storage->ConvertDataToSqlString(FieldType, iter.value());
1046 
1047  if (SqlValue.isEmpty())
1048  {
1049  MC_ERROR("Type %s cannot be converted to an Sql type. Implementation is missing "
1050  "in the storages in MSSessionStorageInterface::ConvertDataToSqlString().",
1051  qPrintable(OutputType));
1052  } else {
1053  InsertIDStr += ", "+OutputName.toUpper()+'_'+FieldName.toUpper();
1054  InsertValueStr += ", "+SqlValue;
1055  }
1056  continue;
1057  }
1058  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1060  {
1061  QString IDStr = OutputName.toUpper()+'_'+FieldName.toUpper();
1062  QString FileName = ConstructFileName(ElementID, BeatID, RecordPath, IDStr, StorageType);
1063 
1064  if (!Storage->SaveBinaryDataToFile(FileName, FieldType, StorageType, iter.value()))
1065  {
1066  MC_ERROR("Unable to save type %s as binary. Implementation is missing "
1067  "in the storages in MSSessionStorageInterface::SaveBinaryDataToFile().",
1068  qPrintable(OutputType));
1069  }
1070  continue;
1071  } // End of storage if {}
1072  } // End of structure processing for {}
1073  } // End of structure if {}
1074  } // End of output list for {}
1075 
1076  // Finish the Sql insert
1077  InsertIDStr += ") "+InsertValueStr+");";
1078  ExecuteSqlQuery(*RecordDatabase, InsertIDStr);
1079  } // End of elements for {}
1080 }
1081 
1082 
1084 {
1085  if (type_name.isEmpty())
1086  {
1087  MC_WARNING("Empty type name is not valid.");
1088  return nullptr;
1089  }
1090  for (int i = 0; i < SessionStorages.size(); ++i)
1091  {
1092  if (SessionStorages[i]->GetSupportedTypes().contains(type_name))
1093  {
1094  return SessionStorages[i];
1095  }
1096  }
1097  return nullptr;
1098 }
1099 
1100 
1101 QString MSSessionStorage::ExecuteSqlQuery(const QSqlDatabase& database, const QString& query_str) const
1102 {
1103  if (query_str.isEmpty())
1104  {
1105  MC_WARNING("Empty query string is not valid.");
1106  return nullptr;
1107  }
1108  QSqlQuery Query(database);
1109  QStringList Results;
1110 
1111  if (!Query.exec(qPrintable(query_str)))
1112  {
1113  if (!Query.lastError().text().isEmpty())
1114  {
1115  MC_WARNING("Sqlite query: %s", qPrintable(query_str));
1116  MC_WARNING("Sqlite error in the query: %s", qPrintable(Query.lastError().text()));
1117  }
1118  if (!database.lastError().text().isEmpty())
1119  {
1120  MC_WARNING("Sqlite error in the database: %s", qPrintable(database.lastError().text()));
1121  }
1122  return QString();
1123  }
1124 
1125  while (Query.next())
1126  {
1127  for (int i = 0; Query.value(i) != QVariant::Invalid; ++i)
1128  Results += Query.value(i).toString();
1129  }
1130 
1131  if (!Results.isEmpty())
1132  {
1133  return Results.join(",");
1134  } else {
1135  return QString();
1136  }
1137 }
1138 
1139 
1141 {
1142  if (!RecordDatabase.get())
1143  {
1144  MC_WARNING("Database has not been created for record yet.");
1145  return;
1146  }
1147 #if defined(__MINGW32__) || defined(__MINGW64__)
1148  // Commit one big transaction to avoid the high performance overhead under MinGW/Windows
1149  // On the other hand, the whole database can be lost in a nasty crash
1150  RecordDatabase->commit();
1151 #endif
1152  RecordDatabase->close();
1153  RecordDatabase.reset();
1154  QSqlDatabase::removeDatabase("RecordSession");
1155 }
1156 
1157 
1159 {
1160  if (PlaybackPath.isEmpty())
1161  {
1162  MC_WARNING("Session playback location has not been set.");
1163  return;
1164  }
1165  if (PlaybackDatabase.get())
1166  {
1167  MC_WARNING("A database has been opened for play already.");
1168  return;
1169  }
1170  QString DatabaseName = PlaybackPath;
1171 
1172  DatabaseName += "/Session.db";
1173  if (!QSqlDatabase::drivers().contains("QSQLITE"))
1174  {
1175  MC_ERROR("Please install or compile the Qt's SQL driver for SQLite, it is required for AiBO+.");
1176  }
1177  PlaybackDatabase.reset(new QSqlDatabase());
1178  *PlaybackDatabase = QSqlDatabase::addDatabase("QSQLITE", "PlaybackSession");
1179  PlaybackDatabase->setDatabaseName(DatabaseName);
1180  if (!QFile(DatabaseName).exists())
1181  {
1182  MC_ERROR("Database file does not exist: %s", qPrintable(DatabaseName));
1183  }
1184  if (!PlaybackDatabase->open())
1185  {
1186  MC_ERROR("Sqlite error: %s", qPrintable(RecordDatabase->lastError().text()));
1187  }
1188  TableColumns.clear();
1189 }
1190 
1191 
1193 {
1194  if (!PlaybackDatabase.get())
1195  {
1196  MC_WARNING("Database has not been opened for play yet.");
1197  return;
1198  }
1199  PlaybackDatabase->close();
1200  PlaybackDatabase.reset();
1201  QSqlDatabase::removeDatabase("PlaybackSession");
1202 }
1203 
1204 
1206 {
1207  QString StorageName = RecordPath+"/Session.bdb";
1208 
1209  RecordStorage.reset(new MCDataStorage("RecordSession", true));
1210  if (!RecordStorage->SaveToFile(StorageName.toStdString()))
1211  {
1212  MC_ERROR("Can't create session storage (%s)", qPrintable(StorageName));
1213  }
1214 }
1215 
1216 
1218 {
1219  if (!RecordStorage.get())
1220  {
1221  MC_WARNING("Storage has not been created for record yet.");
1222  return;
1223  }
1224  QStringList OutputList = element.GetOutputList();
1225 
1226  // Do not create a table because there is no output data
1227  if (OutputList.isEmpty())
1228  return;
1229 
1230  bool TypeFound = false;
1231  std::string ClassName = element.GetClass().toStdString();
1232  MC::StringTable NewTable;
1233  MC::StringList TableHeader;
1234  MC::DataContainerSPtr ElementContainer = RecordStorage->GetContainer(ClassName);
1235 
1236  if (!ElementContainer.get())
1237  {
1238  ElementContainer = RecordStorage->CreateContainer(ClassName);
1239  }
1240  // First column
1241  TableHeader.push_back("ID");
1242  // Second column
1243  TableHeader.push_back("Outputs");
1244  // Process the possible output data to store in the database
1245  for (int i = 0; i < OutputList.size(); ++i)
1246  {
1247  if (OutputList[i].isEmpty())
1248  {
1249  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
1250  i, ClassName.c_str());
1251  continue;
1252  }
1253  QString OutputName = OutputList[i].section(':', 0, 0);
1254  QString OutputType = OutputList[i].section(':', 1, 1);
1255 
1256  if (OutputName.isEmpty() || OutputType.isEmpty())
1257  {
1258  MC_ERROR("An output parameter has wrong syntax for class %s. "
1259  "The string \'%s\' must follow the 'name:type' format.",
1260  ClassName.c_str(), qPrintable(OutputList[i]));
1261  }
1262  // Get the appropriate storage
1263  MSSessionStorageInterface* Storage = GetStorage(OutputType);
1264 
1265  if (!Storage)
1266  {
1267  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
1268  }
1269  MSData* OutputData = element.GetOutputData(OutputList[i]);
1270 
1271  if (!OutputData)
1272  {
1273  MC_ERROR("Output data %s cannot be fetched from %s.", qPrintable(OutputList[i]),
1274  ClassName.c_str());
1275  }
1276  if (OutputData->GetVariableMap().isEmpty())
1277  {
1278  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
1279 
1280  TypeFound = true;
1281  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1282  {
1283  MC_ERROR("Type %s cannot be translated to an Sql type.", qPrintable(OutputType));
1284  }
1285  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1287  {
1288  continue;
1289  }
1290  if (StorageType == MSSessionStorageInterface::sd_Text)
1291  {
1292  TableHeader.push_back(OutputName.toStdString());
1293  } else
1294  if (StorageType == MSSessionStorageInterface::sd_Integer)
1295  {
1296  TableHeader.push_back(OutputName.toStdString());
1297  } else
1298  if (StorageType == MSSessionStorageInterface::sd_Real)
1299  {
1300  TableHeader.push_back(OutputName.toStdString());
1301  }
1302  } else {
1303  TypeFound = true;
1304  // Process the structure
1305  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
1306  {
1307  if (iter.key().isEmpty())
1308  {
1309  MC_ERROR("A parameter is empty in the field list of the type: %s", qPrintable(OutputType));
1310  }
1311  QString FieldName = iter.key().section(':', 0, 0);
1312  QString FieldType = iter.key().section(':', 1, 1);
1313 
1314  if (FieldName.isEmpty() || FieldType.isEmpty())
1315  {
1316  MC_ERROR("A field has wrong syntax for type %s. The string \'%s\' must follow the 'name:type' format.",
1317  qPrintable(OutputType), qPrintable(iter.key()));
1318  }
1319  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
1320 
1321  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1322  {
1323  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type.",
1324  qPrintable(FieldType), qPrintable(OutputType));
1325  }
1326  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1328  {
1329  continue;
1330  }
1331  if (StorageType == MSSessionStorageInterface::sd_Text)
1332  {
1333  TableHeader.push_back(OutputName.toStdString()+'_'+FieldName.toStdString());
1334  }
1335  if (StorageType == MSSessionStorageInterface::sd_Integer)
1336  {
1337  TableHeader.push_back(OutputName.toStdString()+'_'+FieldName.toStdString());
1338  }
1339  if (StorageType == MSSessionStorageInterface::sd_Real)
1340  {
1341  TableHeader.push_back(OutputName.toStdString()+'_'+FieldName.toStdString());
1342  }
1343  } // End of iterating through a structure
1344  } // End of processing a structure
1345  } // End of output list processing
1346 
1347  // Do not create a table because there is no output data
1348  if (!TypeFound)
1349  {
1350  MC_ERROR("No output type to store from a source element?!?");
1351  }
1352  std::string ContainerIndexStr = MCToStr((element.GetBeatID() / RecordStoragePeriod)+1);
1353  MC::StringList BeatIDs;
1354 
1355  ElementContainer->AddData(*new MCDataItem<MC::StringList>(BeatIDs, "BeatIDs::"+ContainerIndexStr, true));
1356  NewTable.push_back(TableHeader);
1357  ElementContainer->AddData(*new MCDataItem<MC::StringTable>(NewTable, ClassName+"::"+ContainerIndexStr, true));
1358 }
1359 
1360 
1362 {
1363  if (!PlaybackStorage.get())
1364  {
1365  MC_WARNING("Storage has not been opened for playback yet.");
1366  return;
1367  }
1368  // Load the recorded states of the sources
1369  for (int i = 0; i < RegElements.size(); ++i)
1370  {
1371  QString ClassName = RegElements[i]->GetClass();
1372  int BeatID = RegElements[i]->GetBeatID();
1373  MC::DataContainerSPtr ElementContainer = PlaybackStorage->GetContainer(ClassName.toStdString());
1374 
1375  if (!ElementContainer.get() || !RegElements[i]->HasOutputs())
1376  continue;
1377 
1378  std::string ContainerIndexStr = MCToStr((BeatID / RecordStoragePeriod)+1);
1379  void* BeatIDsPtr = ElementContainer->GetData("BeatIDs::"+ContainerIndexStr)->DataPtr;
1380  MC::StringList* BeatIDs = reinterpret_cast<MC::StringList*>(BeatIDsPtr);
1381  std::string OutputsTableContainerName = ClassName.toStdString()+"::"+ContainerIndexStr;
1382  void* OutputsTablePtr = ElementContainer->GetData(OutputsTableContainerName)->DataPtr;
1383  MC::StringTable* OutputsTable = reinterpret_cast<MC::StringTable*>(OutputsTablePtr);
1384 
1385  if (!BeatIDs || !OutputsTable)
1386  continue;
1387 
1388  std::string BeatIDStr = MCToStr(BeatID);
1389  std::pair<int, std::string> Result = MCBinarySearch(*BeatIDs, BeatIDStr);
1390  QStringList OutputList = RegElements[i]->GetOutputList();
1391  int FinalIndex = 0;
1392 
1393  // Continue if the beat ID is not found
1394  if (Result.first == -1)
1395  continue;
1396 
1397  // Re-mapping source element ID to candidate ID
1398  int ElementID = RegElements[i]->GetElementID();
1399 
1400  if (RemappedIDs.contains(ElementID))
1401  {
1402  ElementID = RemappedIDs[ElementID];
1403  }
1404  std::string ElementIDStr = MCToStr(ElementID);
1405 
1406  // Determinate the exact line in the table with the outputs
1407  for (unsigned int i1 = Result.first; i1 < BeatIDs->size(); ++i1)
1408  {
1409  if ((*OutputsTable)[i1][0] == ElementIDStr)
1410  {
1411  FinalIndex = i1+1;
1412  break;
1413  }
1414  }
1415  // Extract the actual outputs from the database
1416  RegElements[i]->CurrentOutputList = QString::fromStdString((*OutputsTable)[FinalIndex][1]).split(',');
1417  // Remove empty output names
1418  for (int i1 = 0; i1 < RegElements[i]->CurrentOutputList.size(); ++i1)
1419  {
1420  if (RegElements[i]->CurrentOutputList[i1].isEmpty())
1421  {
1422  RegElements[i]->CurrentOutputList.removeAt(i1);
1423  --i1;
1424  }
1425  }
1426  // Process the output data to load from the database
1427  for (int i1 = 0; i1 < RegElements[i]->CurrentOutputList.size(); ++i1)
1428  {
1429  if (RegElements[i]->CurrentOutputList[i1].isEmpty())
1430  {
1431  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
1432  i1, qPrintable(ClassName));
1433  continue;
1434  }
1435  // The output is not available in this heart beat
1436  if (!OutputList.contains(RegElements[i]->CurrentOutputList[i1]))
1437  continue;
1438 
1439  QString OutputName = RegElements[i]->CurrentOutputList[i1].section(':', 0, 0);
1440  QString OutputType = RegElements[i]->CurrentOutputList[i1].section(':', 1, 1);
1441 
1442  if (OutputName.isEmpty() || OutputType.isEmpty())
1443  {
1444  MC_ERROR("An output has wrong syntax for class %s. The string \'%s\' must follow the 'name:type' format.",
1445  qPrintable(ClassName), qPrintable(RegElements[i]->CurrentOutputList[i1]));
1446  }
1447  // Get the appropriate storage
1448  MSSessionStorageInterface* Storage = GetStorage(OutputType);
1449 
1450  if (!Storage)
1451  {
1452  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
1453  }
1454  // Extract the type fields
1455  MSData* OutputData = RegElements[i]->GetOutputData(RegElements[i]->CurrentOutputList[i1]);
1456 
1457  if (!OutputData)
1458  {
1459  MC_ERROR("Output data %s cannot be fetched from %s.",
1460  qPrintable(RegElements[i]->CurrentOutputList[i1]), qPrintable(ClassName));
1461  }
1462  // It is a simple type
1463  if (OutputData->GetVariableMap().isEmpty())
1464  {
1465  // Extract the output data
1466  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
1467 
1468  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1469  {
1470  MC_ERROR("Type %s cannot be translated to a storage type. Implementation is missing "
1471  "in the registered storages.", qPrintable(OutputType));
1472  }
1473  if (StorageType == MSSessionStorageInterface::sd_Text ||
1474  StorageType == MSSessionStorageInterface::sd_Integer ||
1475  StorageType == MSSessionStorageInterface::sd_Real)
1476  {
1477  int DataIndex = -1;
1478 
1479  for (unsigned int i2 = 2; i2 < (*OutputsTable)[0].size(); ++i2)
1480  {
1481  if (QString::fromStdString((*OutputsTable)[0][i2]) == OutputName)
1482  DataIndex = i2;
1483  }
1484  // Let's continue if the column does not exist in the database
1485  if (DataIndex == -1)
1486  continue;
1487 
1488  QString ResultStr = QString::fromStdString((*OutputsTable)[FinalIndex][DataIndex]);
1489 
1490  if (!Storage->ConvertSqlStringToData(OutputType, ResultStr, OutputData))
1491  {
1492  MC_ERROR("Type %s cannot be extracted from the Sql string. Implementation is missing "
1493  "in the storages in MSSessionStorageInterface::ConvertSqlStringToData().",
1494  qPrintable(OutputType));
1495  }
1496  continue;
1497  }
1498  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1500  {
1501  QString FileName = ConstructFileName(ElementID, BeatID, PlaybackPath, OutputName, StorageType);
1502 
1503  if (!Storage->LoadBinaryDataFromFile(FileName, OutputType, StorageType, OutputData))
1504  {
1505  MC_ERROR("Unable to load type %s as binary. Implementation is missing in the storages in "
1506  "MSSessionStorageInterface::LoadBinaryDataFromFile().", qPrintable(OutputType));
1507  }
1508  continue;
1509  }
1510  } else {
1511  // Process the structure
1512  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
1513  {
1514  // Check the variable
1515  if (iter.key().isEmpty())
1516  {
1517  MC_ERROR("A parameter is empty in the field list of the type: %s", qPrintable(OutputType));
1518  }
1519  // Check the variable data
1520  if (!iter.value())
1521  {
1522  MC_ERROR("Field %s in the type %s cannot be extracted.", qPrintable(iter.key()),
1523  qPrintable(OutputType));
1524  }
1525  QString FieldName = iter.key().section(':', 0, 0);
1526  QString FieldType = iter.key().section(':', 1, 1);
1527 
1528  if (FieldName.isEmpty() || FieldType.isEmpty())
1529  {
1530  MC_ERROR("A field has wrong syntax for type %s. "
1531  "The string \'%s\' must follow the 'name:type' format.",
1532  qPrintable(OutputType), qPrintable(iter.key()));
1533  }
1534  // Get the storage type
1535  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
1536 
1537  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1538  {
1539  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type."
1540  "Implementation is missing in the storages in MSSessionStorageInterface::FindStorageType().",
1541  qPrintable(FieldType), qPrintable(OutputType));
1542  }
1543  if (StorageType == MSSessionStorageInterface::sd_Text ||
1544  StorageType == MSSessionStorageInterface::sd_Integer ||
1545  StorageType == MSSessionStorageInterface::sd_Real)
1546  {
1547  int DataIndex = -1;
1548 
1549  for (unsigned int i2 = 2; i2 < (*OutputsTable)[0].size(); ++i2)
1550  {
1551  if (QString::fromStdString((*OutputsTable)[0][i2]) == OutputName+'_'+FieldName)
1552  DataIndex = i2;
1553  }
1554  // Let's continue if the column does not exist in the database
1555  if (DataIndex == -1)
1556  continue;
1557 
1558  QString ResultStr = QString::fromStdString((*OutputsTable)[FinalIndex][DataIndex]);
1559  if (!Storage->ConvertSqlStringToData(FieldType, ResultStr, iter.value()))
1560  {
1561  MC_ERROR("Type %s cannot be extracted from the Sql string. Implementation is missing in "
1562  "the storages in MSSessionStorageInterface::ConvertSqlStringToData().",
1563  qPrintable(FieldType));
1564  }
1565  continue;
1566  }
1567  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1569  {
1570  QString IDStr = OutputName.toUpper()+'_'+FieldName.toUpper();
1571  QString FileName = ConstructFileName(ElementID, BeatID, PlaybackPath, IDStr, StorageType);
1572 
1573  if (!Storage->LoadBinaryDataFromFile(FileName, FieldType, StorageType, iter.value()))
1574  {
1575  MC_ERROR("Unable to load type %s as binary. Implementation is missing in the storages in "
1576  "MSSessionStorageInterface::LoadBinaryDataFromFile().", qPrintable(FieldType));
1577  }
1578  continue;
1579  } // End of storage if {}
1580  } // End of structure processing for {}
1581 
1582  } // End of structure if {}
1583  } // End of output list for {}
1584  } // End of elements for {}
1585 
1586  // End of database signal
1587  if (RegElements.size() > 0 && RegElements[0]->GetBeatID()+1 == PlayedHeartBeats)
1588  {
1589  MC_LOG("End of played session database");
1590  Q_EMIT(EndOfDatabase());
1591  }
1592 }
1593 
1594 
1596 {
1597  if (!RecordStorage.get())
1598  {
1599  MC_WARNING("Storage has not been created for record yet.");
1600  return;
1601  }
1602  for (int i = 0; i < RegElements.size(); ++i)
1603  {
1604  if (!RegElements[i]->HasOutputs())
1605  continue;
1606 
1607  QStringList OutputList = RegElements[i]->GetOutputList();
1608  QString ClassName = RegElements[i]->GetClass();
1609  int ElementID = RegElements[i]->GetElementID();
1610 
1611  // Continue if there is no output data
1612  if (OutputList.isEmpty())
1613  continue;
1614 
1615  MC::DataContainerSPtr ElementContainer = RecordStorage->GetContainer(ClassName.toStdString());
1616 
1617  if (!ElementContainer.get())
1618  continue;
1619 
1620  int BeatID = RegElements[i]->GetBeatID();
1621  std::string ContainerIndexStr = MCToStr((BeatID / RecordStoragePeriod)+1);
1622  MCDataItemBase* BeatIDsPtr = ElementContainer->GetData("BeatIDs::"+ContainerIndexStr);
1623  std::string OutputsTableContainerName = ClassName.toStdString()+"::"+ContainerIndexStr;
1624  MCDataItemBase* OutputsTablePtr = ElementContainer->GetData(OutputsTableContainerName);
1625 
1626  if (!BeatIDsPtr || !OutputsTablePtr)
1627  {
1629  BeatIDsPtr = ElementContainer->GetData("BeatIDs::"+ContainerIndexStr);
1630  OutputsTablePtr = ElementContainer->GetData(OutputsTableContainerName);
1631  }
1632  MC::StringList* BeatIDs = reinterpret_cast<MC::StringList*>(BeatIDsPtr->DataPtr);
1633  MC::StringTable* OutputsTable = reinterpret_cast<MC::StringTable*>(OutputsTablePtr->DataPtr);
1634  MC::StringList NewTableRow;
1635 
1636  for (unsigned int i1 = 0; i1 < (*OutputsTable)[0].size(); ++i1)
1637  NewTableRow.push_back("");
1638 
1639  BeatIDs->push_back(MCToStr(BeatID));
1640  NewTableRow[0] = MCToStr(ElementID);
1641  NewTableRow[1] = RegElements[i]->CurrentOutputList.join(",").toStdString();
1642 
1643  // Process the possible output data to store in the database
1644  for (int i1 = 0; i1 < OutputList.size(); ++i1)
1645  {
1646  if (OutputList[i1].isEmpty())
1647  {
1648  MC_WARNING("The %d. parameter is empty in the output list of the element class: %s",
1649  i1, qPrintable(ClassName));
1650  continue;
1651  }
1652  QString OutputName = OutputList[i1].section(':', 0, 0);
1653  QString OutputType = OutputList[i1].section(':', 1, 1);
1654 
1655  // The output is not available in this heart beat
1656  if (!RegElements[i]->CurrentOutputList.contains(OutputList[i1]))
1657  {
1658  continue;
1659  }
1660  if (OutputName.isEmpty() || OutputType.isEmpty())
1661  {
1662  MC_ERROR("An output has wrong syntax for class %s. The string \'%s\' must follow the 'name:type' format.",
1663  qPrintable(ClassName), qPrintable(OutputList[i1]));
1664  }
1665  // Get the appropriate storage
1666  MSSessionStorageInterface* Storage = GetStorage(OutputType);
1667 
1668  if (!Storage)
1669  {
1670  MC_ERROR("Unable to find a storage for type %s.", qPrintable(OutputType));
1671  }
1672 
1673  // Extract the output data
1674  MSData* OutputData = RegElements[i]->GetOutputData(OutputList[i1]);
1675 
1676  // No output data, let's continue.
1677  if (!OutputData)
1678  {
1679  continue;
1680  }
1681  // It is a simple type
1682  if (OutputData->GetVariableMap().isEmpty())
1683  {
1684  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(OutputType);
1685 
1686  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1687  {
1688  MC_ERROR("Type %s cannot be translated to a storage type. Implementation is missing "
1689  "in the storages in MSSessionStorageInterface::FindStorageType().",
1690  qPrintable(OutputType));
1691  }
1692  if (StorageType == MSSessionStorageInterface::sd_Text ||
1693  StorageType == MSSessionStorageInterface::sd_Integer ||
1694  StorageType == MSSessionStorageInterface::sd_Real)
1695  {
1696  QString SqlValue = Storage->ConvertDataToSqlString(OutputType, OutputData);
1697 
1698  if (SqlValue.isEmpty())
1699  {
1700  MC_ERROR("Type %s cannot be converted to an Sql type. Implementation is missing "
1701  "in the storages in MSSessionStorageInterface::ConvertDataToSqlString().",
1702  qPrintable(OutputType));
1703  } else {
1704  int DataIndex = -1;
1705 
1706  for (unsigned int i2 = 2; i2 < (*OutputsTable)[0].size(); ++i2)
1707  {
1708  if (QString::fromStdString((*OutputsTable)[0][i2]) == OutputName)
1709  DataIndex = i2;
1710  }
1711  // Let's continue if the column does not exist in the database
1712  if (DataIndex == -1)
1713  continue;
1714 
1715  NewTableRow[DataIndex] = SqlValue.toStdString();
1716  }
1717  continue;
1718  }
1719  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1721  {
1722  QString FileName = ConstructFileName(ElementID, BeatID, RecordPath, OutputName, StorageType);
1723 
1724  if (!Storage->SaveBinaryDataToFile(FileName, OutputType, StorageType, OutputData))
1725  {
1726  MC_ERROR("Unable to save type %s as binary. Implementation is missing "
1727  "in the storages in MSSessionStorageInterface::SaveBinaryDataToFile().",
1728  qPrintable(OutputType));
1729  }
1730  continue;
1731  }
1732  } else {
1733  // Process the structure
1734  for (auto iter = OutputData->GetVariableMap().constBegin(); iter != OutputData->GetVariableMap().constEnd(); ++iter)
1735  {
1736  // Check the variable
1737  if (iter.key().isEmpty())
1738  {
1739  MC_ERROR("A parameter is empty in the field list of the type: %s",
1740  qPrintable(OutputType));
1741  }
1742  // Check the variable data
1743  if (!iter.value())
1744  {
1745  MC_ERROR("Field %s in the type %s cannot be extracted.",
1746  qPrintable(iter.key()), qPrintable(OutputType));
1747  }
1748  QString FieldName = iter.key().section(':', 0, 0);
1749  QString FieldType = iter.key().section(':', 1, 1);
1750 
1751  if (FieldName.isEmpty() || FieldType.isEmpty())
1752  {
1753  MC_ERROR("A field has wrong syntax for type %s. "
1754  "The string \'%s\' must follow the 'name:type' format.",
1755  qPrintable(OutputType), qPrintable(iter.key()));
1756  }
1757 
1758  // Process the output data
1759  MSSessionStorageInterface::StorageDataType StorageType = Storage->FindStorageType(FieldType);
1760 
1761  if (StorageType == MSSessionStorageInterface::sd_Unknown)
1762  {
1763  MC_ERROR("Field %s in the type %s cannot be translated to an Sql type."
1764  "Implementation is missing in the storages in MSSessionStorageInterface::FindStorageType().",
1765  qPrintable(FieldType), qPrintable(OutputType));
1766  }
1767  if (StorageType == MSSessionStorageInterface::sd_Text ||
1768  StorageType == MSSessionStorageInterface::sd_Integer ||
1769  StorageType == MSSessionStorageInterface::sd_Real)
1770  {
1771  QString SqlValue = Storage->ConvertDataToSqlString(FieldType, iter.value());
1772 
1773  if (SqlValue.isEmpty())
1774  {
1775  MC_ERROR("Type %s cannot be converted to an Sql type. Implementation is missing "
1776  "in the storages in MSSessionStorageInterface::ConvertDataToSqlString().",
1777  qPrintable(OutputType));
1778  } else {
1779  int DataIndex = -1;
1780 
1781  for (unsigned int i2 = 2; i2 < (*OutputsTable)[0].size(); ++i2)
1782  {
1783  if (QString::fromStdString((*OutputsTable)[0][i2]) == OutputName+'_'+FieldName)
1784  DataIndex = i2;
1785  }
1786  // Let's continue if the column does not exist in the database
1787  if (DataIndex == -1)
1788  continue;
1789 
1790  NewTableRow[DataIndex] = SqlValue.toStdString();
1791  }
1792  continue;
1793  }
1794  if (StorageType == MSSessionStorageInterface::sd_Binary ||
1796  {
1797  QString IDStr = OutputName.toUpper()+'_'+FieldName.toUpper();
1798  QString FileName = ConstructFileName(ElementID, BeatID, RecordPath, IDStr, StorageType);
1799 
1800  if (!Storage->SaveBinaryDataToFile(FileName, FieldType, StorageType, iter.value()))
1801  {
1802  MC_ERROR("Unable to save type %s as binary. Implementation is missing "
1803  "in the storages in MSSessionStorageInterface::SaveBinaryDataToFile().",
1804  qPrintable(OutputType));
1805  }
1806  continue;
1807  } // End of storage if {}
1808  } // End of structure processing for {}
1809  } // End of structure if {}
1810  } // End of output list for {}
1811 
1812  // Add new row
1813  OutputsTable->push_back(NewTableRow);
1814  } // End of elements for {}
1815 
1816  MC::DataContainerSPtr ElementsContainer = RecordStorage->GetContainer("SourceElements");
1817 
1818  if (ElementsContainer.get() && RegElements.size() > 0)
1819  {
1820  void* HeartBeatsPtr = ElementsContainer->GetData("HeartBeats")->DataPtr;
1821 
1822  if (HeartBeatsPtr)
1823  *reinterpret_cast<int*>(HeartBeatsPtr) = RegElements[0]->GetBeatID();
1824  }
1825 
1826  // Save the record database content
1827  if (RegElements.size() > 0 && (RegElements[0]->GetBeatID() % 50 == 0))
1828  {
1829  QString StorageName = RecordPath+"/Session.bdb";
1830 
1831  if (!RecordStorage->SaveToFile(StorageName.toStdString()))
1832  {
1833  MC_ERROR("Can't save session storage (%s)", qPrintable(StorageName));
1834  }
1835  // Don't cache the previous container anymore
1836  if (RegElements[0]->GetBeatID() > 0 && RegElements[0]->GetBeatID() % RecordStoragePeriod == 0)
1837  {
1838  RecordStorage->OptimizeCachedContainers();
1839  }
1840  }
1841 }
1842 
1843 
1845 {
1846  if (!RecordStorage.get())
1847  {
1848  MC_WARNING("Storage has not been created for record yet.");
1849  return;
1850  }
1851  // Flush the content first
1852  QString StorageName = RecordPath+"/Session.bdb";
1853 
1854  if (!RecordStorage->SaveToFile(StorageName.toStdString()))
1855  {
1856  MC_ERROR("Can't save session storage (%s)", qPrintable(StorageName));
1857  }
1858  RecordStorage.reset();
1859 }
1860 
1861 
1863 {
1864  if (PlaybackPath.isEmpty())
1865  {
1866  MC_WARNING("Session playback location has not been set.");
1867  return;
1868  }
1869  if (PlaybackStorage.get())
1870  {
1871  MC_WARNING("A storage has been opened for playback already.");
1872  return;
1873  }
1874  QString StorageName = PlaybackPath+"/Session.bdb";
1875 
1876  if (!QFile(StorageName).exists())
1877  {
1878  MC_ERROR("Session storage file does not exist: %s", qPrintable(StorageName));
1879  }
1880  PlaybackStorage.reset(new MCDataStorage("PlaybackSession", true));
1881  if (!PlaybackStorage->LoadFromFile(StorageName.toStdString()))
1882  {
1883  MC_ERROR("Can't open session storage (%s)", qPrintable(StorageName));
1884  }
1885 }
1886 
1887 
1889 {
1890  if (!PlaybackStorage.get())
1891  {
1892  MC_WARNING("Storage has not been opened for playback yet.");
1893  return;
1894  }
1895  PlaybackStorage.reset();
1896 }
void CreateNewDataItemsForAClass(MSElement &object)
Create data container for an element class.
int GetPlayedHeartBeats() const
Get the number of the played heart beats.
MSSessionStorageInterface * GetStorage(const QString &type_name) const
Get the appropriate storage for a type.
Load or store the source elements&#39; output of a session.
QString ExecuteSqlQuery(const QSqlDatabase &database, const QString &query_str) const
Execute an SQL query.
bool MCCreatePath(const QString &path)
Create a new path if it does not exist.
Definition: MCDefs.cpp:344
void PreparePlay()
Prepare the play of a recorded session.
static void RegisterTimer(const QString &id, const QString &description)
Register a timer.
Definition: MSContext.cpp:644
void CreateRecordDatabase()
Create an empty record database.
QString GetClass() const
Get the class name.
Definition: MSElement.cpp:107
A helper class to fill the type info to the base structure.
void OpenPlaybackDatabase()
Open a database.
void CreateTableForAClass(MSElement &object)
Create table for an element class.
void EndOfDatabase()
End of the data in the database.
Interface class to handle new types in the storage.
int GetElementID() const
Get the element ID.
Definition: MSElement.cpp:83
void ResetTimers()
Reset the internal timers.
MSSessionStorage()
Class constructor.
void SetPlaybackPath(const QString &path)
Set the playback path.
boost::scoped_ptr< MCDataStorage > PlaybackStorage
Play data storage.
static MSSessionStorage * GetInstance()
Get a static instance of the class.
virtual bool SaveBinaryDataToFile(const QString &file_name, const QString &output_type, const StorageDataType &storage_type, void *output_data)=0
Save binary data to a file.
Basic ancestor class of the elements.
Definition: MSElement.hpp:65
#define MC_ERROR(...)
Error macro.
Definition: MCLog.hpp:45
QString PlaybackPath
Playback path.
static void StartTimer(const QString &id)
Start a timer.
Definition: MSContext.cpp:669
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
int GetBeatID() const
Get the current beat ID.
Definition: MSElement.cpp:95
Data container item base structure to store basic type info.
std::string MCToStr(const T value, bool hex_manipulator=false)
Convert an other type to string with std::stringstream.
Definition: MCDefs.hpp:360
virtual bool ConvertSqlStringToData(const QString &type_name, const QString &sql_str, void *output_data)=0
Convert an Sql string to a data.
QMap< QString, QStringList > TableColumns
Cached class outputs.
virtual StorageDataType FindStorageType(const QString &type_name)=0
Translate the type.
boost::scoped_ptr< QSqlDatabase > RecordDatabase
Record database.
QStringList ElementClassNames
Registered class names with an instance.
void RemoveStorages()
Remove the session storages.
virtual QString ConvertDataToSqlString(const QString &type_name, void *output_data)=0
Convert the data to a string for an Sql insert.
boost::scoped_ptr< QSqlDatabase > PlaybackDatabase
Playback database.
int PlayedHeartBeats
Played heart beats.
const QMap< QString, void * > & GetVariableMap() const
Get the variable map.
Definition: MSData.cpp:54
QList< QPointer< MSSessionStorageInterface > > SessionStorages
Storage implementations.
void CloseRecordStorage()
Close record storage.
void ClosePlaybackDatabase()
Close the database being played.
Data storage with file support.
static void ResetTimer(const QString &id)
Reset a timer.
Definition: MSContext.cpp:712
bool IsStorageAvailable() const
Check if any storage is available.
void LoadStatesFromDatabase()
Load the element states from the database.
QList< MSElement * > RegElements
Registered elements.
std::pair< int, T > MCBinarySearch(const U &container, const T &value, bool nearest=false)
Binary search for a value in a container.
QString GetPlaybackPath() const
Get the playback path.
Base class for the data exchange of the elements.
Definition: MSData.hpp:54
int MCItemCountInContainer(const U &container, const T item)
Calculate the number of a given item in a container.
void CloseRecordDatabase()
Close the record database.
void OpenPlaybackStorage()
Open playback storage.
QString ConstructFileName(int object_id, int beat_id, const QString &base_path, const QString &output_name, const MSSessionStorageInterface::StorageDataType &storage_type) const
Construct a file name in the storage without extension.
QString RecordPath
Path where the recording goes.
std::vector< int > MCItemIndicesInContainer(const U &container, const T item)
Get item indices in a container.
boost::scoped_ptr< MCDataStorage > RecordStorage
Record data storage.
void Finalize()
Finalize the storage operations.
MSData * GetOutputData(const QString &output_name) const
Get an output data of the element.
Definition: MSElement.cpp:231
void RegisterStorage(MSSessionStorageInterface &storage)
Register a session storage.
MS::ElementType GetType() const
Get the type of the element.
Definition: MSElement.cpp:101
void CreateRecordStorage()
Create a new record storage.
const int RecordStoragePeriod
Record storage period.
void RecordStatesToDatabase()
Record the element states to the database.
#define MC_LOG(...)
Debug macro.
Definition: MCLog.hpp:41
static void StopTimer(const QString &id)
Stop a timer.
Definition: MSContext.cpp:690
QStringList GetOutputList() const
Get the output list.
Definition: MSElement.cpp:194
QString GetRecordPath() const
Get the record path.
void RecordStatesToStorage()
Record element states to storage.
void RegisterElement(MSElement &object)
Register an element.
void SetRecordPath(const QString &record_path)
Set the record path.
void LoadStatesFromStorage()
Load element states from storage.
void PrepareRecord()
Prepare the record of a new session.
void BeatEnd(int beat_id)
This slot is being called by the end of a heart beat.
virtual bool LoadBinaryDataFromFile(const QString &file_name, const QString &output_type, const StorageDataType &storage_type, void *output_data)=0
Load binary data from a file.
void ClosePlaybackStorage()
Close playback storage.